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
5016 changed files with 109642 additions and 295066 deletions

View File

@@ -1,13 +1,12 @@
env:
CIRRUS_CLONE_DEPTH: 1
freebsd_12_task:
freebsd_instance:
image: freebsd-12-0-release-amd64
cpu: 8
memory: 8G
env:
CIRRUS_CLONE_DEPTH: 1
install_script: pkg install -y
bash bison curl cyrus-sasl git glib gmake gnutls gsed
bison curl cyrus-sasl git glib gmake gnutls
nettle perl5 pixman pkgconf png usbredir
script:
- mkdir build
@@ -15,24 +14,3 @@ freebsd_12_task:
- ../configure || { cat config.log; exit 1; }
- gmake -j8
- 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
script:
- ./configure --python=/usr/local/bin/python3 || { cat config.log; exit 1; }
- gmake -j$(sysctl -n hw.ncpu)
- gmake check -j$(sysctl -n hw.ncpu)
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
script:
- ./configure --cc=clang || { cat config.log; exit 1; }
- gmake -j$(sysctl -n hw.ncpu)
- gmake check -j$(sysctl -n hw.ncpu)

View File

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

8
.gitignore vendored
View File

@@ -1,4 +1,3 @@
/.doctrees
/config-devices.*
/config-all-devices.*
/config-all-disas.*
@@ -6,7 +5,6 @@
/config-target.*
/config.status
/config-temp
/elf2dmp
/trace-events-all
/trace/generated-events.h
/trace/generated-events.c
@@ -41,7 +39,6 @@
/qapi/qapi-types-*.[ch]
/qapi/qapi-types.[ch]
/qapi/qapi-visit-*.[ch]
!/qapi/qapi-visit-core.c
/qapi/qapi-visit.[ch]
/qapi/qapi-doc.texi
/qemu-doc.html
@@ -66,8 +63,6 @@
/scsi/qemu-pr-helper
/vhost-user-scsi
/vhost-user-blk
/vhost-user-gpu
/vhost-user-input
/fsdev/virtfs-proxy-helper
*.tmp
*.[1-9]
@@ -100,7 +95,6 @@
*.gcno
*.gcov
/pc-bios/bios-pq/status
/pc-bios/edk2-*.fd
/pc-bios/vgabios-pq/status
/pc-bios/optionrom/linuxboot.asm
/pc-bios/optionrom/linuxboot.bin
@@ -124,7 +118,6 @@
/pc-bios/optionrom/kvmvapic.img
/pc-bios/s390-ccw/s390-ccw.elf
/pc-bios/s390-ccw/s390-ccw.img
/docs/built
/docs/interop/qemu-ga-qapi.texi
/docs/interop/qemu-ga-ref.html
/docs/interop/qemu-ga-ref.info*
@@ -134,7 +127,6 @@
/docs/interop/qemu-qmp-ref.info*
/docs/interop/qemu-qmp-ref.txt
/docs/version.texi
/contrib/vhost-user-gpu/50-qemu-gpu.json
*.tps
.stgit-*
.git-submodule-status

View File

@@ -5,7 +5,7 @@ before_script:
build-system1:
script:
- apt-get install -y -qq libgtk-3-dev libvte-dev nettle-dev libcacard-dev
libusb-dev libvde-dev libspice-protocol-dev libgl1-mesa-dev libvdeplug-dev
libusb-dev libvde-dev libspice-protocol-dev libgl1-mesa-dev
- ./configure --enable-werror --target-list="aarch64-softmmu alpha-softmmu
cris-softmmu hppa-softmmu lm32-softmmu moxie-softmmu microblazeel-softmmu
mips64el-softmmu m68k-softmmu ppc-softmmu riscv64-softmmu sparc-softmmu"
@@ -45,10 +45,15 @@ build-tcg-disabled:
- ./check -raw 001 002 003 004 005 008 009 010 011 012 021 025 032 033 048
052 063 077 086 101 104 106 113 147 148 150 151 152 157 159 160
163 170 171 183 184 192 194 197 205 208 215 221 222 226 227 236
- ./check -qcow2 028 040 051 056 057 058 065 067 068 082 085 091 095 096 102
122 124 127 129 132 139 142 144 145 147 151 152 155 157 165 194
196 197 200 202 203 205 208 209 215 216 218 222 227 234 246 247
248 250 254 255 256
- ./check -qcow2 001 002 003 004 005 007 008 009 010 011 012 013 017 018 019
020 021 022 024 025 027 028 029 031 032 033 034 035 036 037 038
039 040 042 043 046 047 048 049 050 051 052 053 054 056 057 058
060 061 062 063 065 066 067 068 069 071 072 073 074 079 080 082
085 086 089 090 091 095 096 097 098 099 102 103 104 105 107 108
110 111 114 117 120 122 124 126 127 129 130 132 133 134 137 138
139 140 141 142 143 144 145 147 150 151 152 154 155 156 157 158
161 165 170 172 174 176 177 179 184 186 187 190 192 194 195 196
197 200 202 203 205 208 209 214 215 216 217 218 222 226 227 229 234
build-user:
script:
@@ -59,25 +64,10 @@ build-user:
build-clang:
script:
- apt-get install -y -qq clang libsdl2-dev libattr1-dev libcap-dev
- apt-get install -y -qq clang libsdl2-dev
xfslibs-dev libiscsi-dev libnfs-dev libseccomp-dev gnutls-dev librbd-dev
- ./configure --cc=clang --cxx=clang++ --enable-werror
--target-list="alpha-softmmu arm-softmmu m68k-softmmu mips64-softmmu
ppc-softmmu s390x-softmmu x86_64-softmmu arm-linux-user"
- make -j2
- make -j2 check
build-tci:
script:
- TARGETS="aarch64 alpha arm hppa m68k microblaze moxie ppc64 s390x x86_64"
- ./configure --enable-tcg-interpreter
--target-list="$(for tg in $TARGETS; do echo -n ${tg}'-softmmu '; done)"
- make -j2
- make tests/boot-serial-test tests/cdrom-test tests/pxe-test
- for tg in $TARGETS ; do
export QTEST_QEMU_BINARY="${tg}-softmmu/qemu-system-${tg}" ;
./tests/boot-serial-test || exit 1 ;
./tests/cdrom-test || exit 1 ;
done
- QTEST_QEMU_BINARY="x86_64-softmmu/qemu-system-x86_64" ./tests/pxe-test
- QTEST_QEMU_BINARY="s390x-softmmu/qemu-system-s390x" ./tests/pxe-test -m slow

18
.gitmodules vendored
View File

@@ -39,25 +39,13 @@
url = https://git.qemu.org/git/capstone.git
[submodule "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"]
path = roms/u-boot-sam460ex
url = https://git.qemu.org/git/u-boot-sam460ex.git
[submodule "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"]
path = tests/fp/berkeley-softfloat-3
url = https://git.qemu.org/git/berkeley-softfloat-3.git
[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
url = https://github.com/cota/berkeley-softfloat-3

138
.mailmap
View File

@@ -1,29 +1,23 @@
# This mailmap fixes up author names/addresses.
#
# If you are adding to this file consider if a similar change needs to
# be made to contrib/gitdm/aliases. They are not however completely
# analogous. .mailmap is concerned with fixing up damaged author
# fields where as the gitdm equivalent is more concerned with making
# sure multiple email addresses get mapped onto the same author.
#
# From man git-shortlog the forms are:
#
# Proper Name <commit@email.xx>
# <proper@email.xx> <commit@email.xx>
# Proper Name <proper@email.xx> <commit@email.xx>
# Proper Name <proper@email.xx> Commit Name <commit@email.xx>
#
# The first section translates weird addresses from the original git import
# into proper addresses so that they are counted properly by git shortlog.
Andrzej Zaborowski <balrogg@gmail.com> balrog <balrog@c046a42c-6fe2-441c-8c8c-71466251a162>
Anthony Liguori <anthony@codemonkey.ws> aliguori <aliguori@c046a42c-6fe2-441c-8c8c-71466251a162>
Anthony Liguori <anthony@codemonkey.ws> Anthony Liguori <aliguori@us.ibm.com>
Aurelien Jarno <aurelien@aurel32.net> aurel32 <aurel32@c046a42c-6fe2-441c-8c8c-71466251a162>
Blue Swirl <blauwirbel@gmail.com> blueswir1 <blueswir1@c046a42c-6fe2-441c-8c8c-71466251a162>
Edgar E. Iglesias <edgar.iglesias@gmail.com> edgar_igl <edgar_igl@c046a42c-6fe2-441c-8c8c-71466251a162>
Fabrice Bellard <fabrice@bellard.org> bellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162>
James Hogan <jhogan@kernel.org> <james.hogan@imgtec.com>
Jocelyn Mayer <l_indien@magic.fr> j_mayer <j_mayer@c046a42c-6fe2-441c-8c8c-71466251a162>
Paul Brook <paul@codesourcery.com> pbrook <pbrook@c046a42c-6fe2-441c-8c8c-71466251a162>
Yongbok Kim <yongbok.kim@mips.com> <yongbok.kim@imgtec.com>
Aleksandar Markovic <amarkovic@wavecomp.com> <aleksandar.markovic@mips.com>
Aleksandar Markovic <amarkovic@wavecomp.com> <aleksandar.markovic@imgtec.com>
Paul Burton <pburton@wavecomp.com> <paul.burton@mips.com>
Paul Burton <pburton@wavecomp.com> <paul.burton@imgtec.com>
Paul Burton <pburton@wavecomp.com> <paul@archlinuxmips.org>
Thiemo Seufer <ths@networkno.de> ths <ths@c046a42c-6fe2-441c-8c8c-71466251a162>
malc <av1474@comtv.ru> malc <malc@c046a42c-6fe2-441c-8c8c-71466251a162>
@@ -38,124 +32,8 @@ Ian McKellar <ianloic@google.com> Ian McKellar via Qemu-devel <qemu-devel@nongnu
Julia Suvorova <jusual@mail.ru> Julia Suvorova via Qemu-devel <qemu-devel@nongnu.org>
Justin Terry (VM) <juterry@microsoft.com> Justin Terry (VM) via Qemu-devel <qemu-devel@nongnu.org>
# Next, replace old addresses by a more recent one.
Aleksandar Markovic <amarkovic@wavecomp.com> <aleksandar.markovic@mips.com>
Aleksandar Markovic <amarkovic@wavecomp.com> <aleksandar.markovic@imgtec.com>
Aleksandar Rikalo <aleksandar.rikalo@rt-rk.com> <arikalo@wavecomp.com>
Anthony Liguori <anthony@codemonkey.ws> Anthony Liguori <aliguori@us.ibm.com>
James Hogan <jhogan@kernel.org> <james.hogan@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>
Philippe Mathieu-Daudé <philmd@redhat.com> <f4bug@amsat.org>
Yongbok Kim <yongbok.kim@mips.com> <yongbok.kim@imgtec.com>
# Also list preferred name forms where people have changed their
# git author config, or had utf8/latin1 encoding issues.
Aaron Lindsay <aaron@os.amperecomputing.com>
Alexey Gerasimenko <x1917x@gmail.com>
Alex Ivanov <void@aleksoft.net>
Andreas Färber <afaerber@suse.de>
Bandan Das <bsd@redhat.com>
Benjamin MARSILI <mlspirat42@gmail.com>
Benoît Canet <benoit.canet@gmail.com>
Benoît Canet <benoit.canet@irqsave.net>
Benoît Canet <benoit.canet@nodalink.com>
Boqun Feng <boqun.feng@gmail.com>
Boqun Feng <boqun.feng@intel.com>
Brad Smith <brad@comstyle.com>
Brijesh Singh <brijesh.singh@amd.com>
Brilly Wu <brillywu@viatech.com.cn>
Cédric Vincent <cedric.vincent@st.com>
CheneyLin <linzc@zju.edu.cn>
Chen Gang <chengang@emindsoft.com.cn>
Chen Gang <gang.chen.5i5j@gmail.com>
Chen Gang <gang.chen@sunrus.com.cn>
Chen Wei-Ren <chenwj@iis.sinica.edu.tw>
Christophe Lyon <christophe.lyon@st.com>
Collin L. Walling <walling@linux.ibm.com>
Daniel P. Berrangé <berrange@redhat.com>
Eduardo Otubo <otubo@redhat.com>
Fabrice Desclaux <fabrice.desclaux@cea.fr>
Fernando Luis Vázquez Cao <fernando_b1@lab.ntt.co.jp>
Fernando Luis Vázquez Cao <fernando@oss.ntt.co.jp>
Gautham R. Shenoy <ego@in.ibm.com>
Gautham R. Shenoy <ego@linux.vnet.ibm.com>
Gonglei (Arei) <arei.gonglei@huawei.com>
Guang Wang <wang.guang55@zte.com.cn>
Hailiang Zhang <zhang.zhanghailiang@huawei.com>
Hervé Poussineau <hpoussin@reactos.org>
Jakub Jermář <jakub@jermar.eu>
Jakub Jermář <jakub.jermar@kernkonzept.com>
Jean-Christophe Dubois <jcd@tribudubois.net>
Jindřich Makovička <makovick@gmail.com>
John Arbuckle <programmingkidx@gmail.com>
Juha Riihimäki <juha.riihimaki@nokia.com>
Juha Riihimäki <Juha.Riihimaki@nokia.com>
Jun Li <junmuzi@gmail.com>
Laurent Vivier <Laurent@lvivier.info>
Leandro Lupori <leandro.lupori@gmail.com>
Li Guang <lig.fnst@cn.fujitsu.com>
Liming Wang <walimisdev@gmail.com>
linzhecheng <linzc@zju.edu.cn>
Liran Schour <lirans@il.ibm.com>
Liu Yu <yu.liu@freescale.com>
Liu Yu <Yu.Liu@freescale.com>
Li Zhang <zhlcindy@gmail.com>
Li Zhang <zhlcindy@linux.vnet.ibm.com>
Lluís Vilanova <vilanova@ac.upc.edu>
Lluís Vilanova <xscript@gmx.net>
Longpeng (Mike) <longpeng2@huawei.com>
Luc Michel <luc.michel@git.antfield.fr>
Luc Michel <luc.michel@greensocs.com>
Marc Marí <marc.mari.barcelo@gmail.com>
Marc Marí <markmb@redhat.com>
Michael Avdienko <whitearchey@gmail.com>
Michael S. Tsirkin <mst@redhat.com>
Munkyu Im <munkyu.im@samsung.com>
Nicholas Bellinger <nab@linux-iscsi.org>
Nicholas Thomas <nick@bytemark.co.uk>
Nikunj A Dadhania <nikunj@linux.vnet.ibm.com>
Orit Wasserman <owasserm@redhat.com>
Paolo Bonzini <pbonzini@redhat.com>
Pavel Dovgaluk <dovgaluk@ispras.ru>
Pavel Dovgaluk <pavel.dovgaluk@gmail.com>
Pavel Dovgaluk <Pavel.Dovgaluk@ispras.ru>
Peter Crosthwaite <crosthwaite.peter@gmail.com>
Peter Crosthwaite <peter.crosthwaite@petalogix.com>
Peter Crosthwaite <peter.crosthwaite@xilinx.com>
Prasad J Pandit <pjp@fedoraproject.org>
Prasad J Pandit <ppandit@redhat.com>
Qiao Nuohan <qiaonuohan@cn.fujitsu.com>
Reimar Döffinger <Reimar.Doeffinger@gmx.de>
Remy Noel <remy.noel@blade-group.com>
Roger Pau Monné <roger.pau@citrix.com>
Shin'ichiro Kawasaki <kawasaki@juno.dti.ne.jp>
Shin'ichiro Kawasaki <shinichiro.kawasaki@wdc.com>
Sochin Jiang <sochin.jiang@huawei.com>
Takashi Yoshii <takasi-y@ops.dti.ne.jp>
Thomas Huth <thuth@redhat.com>
Thomas Knych <thomaswk@google.com>
Timothy Baldwin <T.E.Baldwin99@members.leeds.ac.uk>
Tony Nguyen <tony.nguyen@bt.com>
Venkateswararao Jujjuri <jvrao@linux.vnet.ibm.com>
Vibi Sreenivasan <vibi_sreenivasan@cms.com>
Vijaya Kumar K <vijayak@cavium.com>
Vijaya Kumar K <Vijaya.Kumar@cavium.com>
Vijay Kumar <vijaykumar@bravegnu.org>
Vijay Kumar <vijaykumar@zilogic.com>
Wang Guang <wang.guang55@zte.com.cn>
Wenchao Xia <xiawenc@linux.vnet.ibm.com>
Wenshuang Ma <kevinnma@tencent.com>
Xiaoqiang Zhao <zxq_yx_007@163.com>
Xinhua Cao <caoxinhua@huawei.com>
Xiong Zhang <xiong.y.zhang@intel.com>
Yin Yin <yin.yin@cs2c.com.cn>
yuchenlin <npes87184@gmail.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

@@ -7,10 +7,11 @@ env:
matrix:
- IMAGE=debian-amd64
TARGET_LIST=x86_64-softmmu,x86_64-linux-user
- IMAGE=debian-win32-cross
TARGET_LIST=arm-softmmu,i386-softmmu,lm32-softmmu
- IMAGE=debian-win64-cross
TARGET_LIST=aarch64-softmmu,sparc64-softmmu,x86_64-softmmu
# currently disabled as the mxe.cc repos are down
# - IMAGE=debian-win32-cross
# TARGET_LIST=arm-softmmu,i386-softmmu,lm32-softmmu
# - IMAGE=debian-win64-cross
# TARGET_LIST=aarch64-softmmu,sparc64-softmmu,x86_64-softmmu
- IMAGE=debian-armel-cross
TARGET_LIST=arm-softmmu,arm-linux-user,armeb-linux-user
- IMAGE=debian-armhf-cross

View File

@@ -5,18 +5,7 @@ dist: xenial
language: c
compiler:
- gcc
cache:
# There is one cache per branch and compiler version.
# characteristics of each job are used to identify the cache:
# - OS name (currently, linux, osx, or windows)
# - OS distribution (for Linux, xenial, trusty, or precise)
# - macOS image name (e.g., xcode7.2)
# - Names and values of visible environment variables set in .travis.yml or Settings panel
timeout: 1200
ccache: true
pip: true
directories:
- $HOME/avocado/data/cache
cache: ccache
addons:
@@ -26,10 +15,9 @@ addons:
- libaio-dev
- libattr1-dev
- libbrlapi-dev
- libcap-dev
- libcap-ng-dev
- libgcc-4.8-dev
- libgnutls28-dev
- libgnutls-dev
- libgtk-3-dev
- libiscsi-dev
- liblttng-ust-dev
@@ -37,28 +25,23 @@ addons:
- libnfs-dev
- libnss3-dev
- libpixman-1-dev
- libpng-dev
- libpng12-dev
- librados-dev
- libsdl2-dev
- libsdl2-image-dev
- libsdl1.2-dev
- libseccomp-dev
- libspice-protocol-dev
- libspice-server-dev
- libssh-dev
- libssh2-1-dev
- liburcu-dev
- libusb-1.0-0-dev
- libvdeplug-dev
- libvte-2.91-dev
- sparse
- uuid-dev
- gcovr
homebrew:
packages:
- ccache
- glib
- pixman
- gnu-sed
update: true
# The channel name "irc.oftc.net#qemu" is encrypted against qemu/qemu
@@ -77,11 +60,7 @@ env:
- SRC_DIR="."
- BUILD_DIR="."
- BASE_CONFIG="--disable-docs --disable-tools"
- 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,arm-softmmu,i386-softmmu,mips-softmmu,mips64-softmmu,ppc64-softmmu,riscv64-softmmu,s390x-softmmu,x86_64-softmmu"
- CCACHE_SLOPPINESS="include_file_ctime,include_file_mtime"
- CCACHE_MAXSIZE=1G
- TEST_CMD="make check -j3 V=1"
git:
@@ -90,62 +69,37 @@ git:
before_script:
- if [ "$TRAVIS_OS_NAME" == "osx" ] ; then export PATH="/usr/local/opt/ccache/libexec:$PATH" ; fi
- if command -v ccache ; then ccache --zero-stats ; fi
- mkdir -p ${BUILD_DIR} && cd ${BUILD_DIR}
- ${SRC_DIR}/configure ${BASE_CONFIG} ${CONFIG} || { cat config.log && exit 1; }
script:
- make -j3 && travis_retry ${TEST_CMD}
after_script:
- if command -v ccache ; then ccache --show-stats ; fi
- make -j3 && ${TEST_CMD}
matrix:
include:
- env:
- CONFIG="--disable-system --static"
- CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-default"
# we split the system builds as it takes a while to build them all
- env:
- CONFIG="--disable-user --target-list=${MAIN_SOFTMMU_TARGETS}"
- CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-default"
- CONFIG="--disable-system"
- env:
- CONFIG="--disable-user --target-list-exclude=${MAIN_SOFTMMU_TARGETS}"
- CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-default"
- CONFIG="--disable-user"
# Just build tools and run minimal unit and softfloat checks
- env:
- BASE_CONFIG="--enable-tools"
- CONFIG="--disable-user --disable-system"
- TEST_CMD="make check-unit check-softfloat -j3"
- CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-default"
- CONFIG="--enable-debug --enable-debug-tcg --disable-user"
# --enable-debug implies --enable-debug-tcg, also runs quite a bit slower
- 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
# TCG debug can be run just on it's own and is mostly agnostic to user/softmmu distinctions
- env:
- CONFIG="--enable-debug-tcg --disable-system"
- CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-debug-tcg"
- env:
- CONFIG="--disable-linux-aio --disable-cap-ng --disable-attr --disable-brlapi --disable-libusb --disable-replication --target-list=${MAIN_SOFTMMU_TARGETS}"
- CONFIG="--disable-linux-aio --disable-cap-ng --disable-attr --disable-brlapi --disable-libusb --disable-user --disable-replication"
# Module builds are mostly of interest to major distros
- env:
- CONFIG="--enable-modules --target-list=${MAIN_SOFTMMU_TARGETS}"
- CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-default"
- CONFIG="--enable-modules --disable-linux-user"
# Alternate coroutines implementations are only really of interest to KVM users
@@ -160,12 +114,10 @@ matrix:
- 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
- env:
- BUILD_DIR="out-of-tree/build/dir" SRC_DIR="../../.."
- BASE_CONFIG="--enable-tools --enable-docs"
- CONFIG="--target-list=x86_64-softmmu,aarch64-linux-user"
- CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-default"
addons:
apt:
packages:
@@ -173,48 +125,30 @@ matrix:
- texinfo
- 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)
- env:
- CONFIG="--disable-system"
- CACHE_NAME="${TRAVIS_BRANCH}-linux-clang-default"
compiler: clang
- env:
- CONFIG="--disable-user --target-list=${MAIN_SOFTMMU_TARGETS}"
- CACHE_NAME="${TRAVIS_BRANCH}-linux-clang-default"
compiler: clang
- env:
- CONFIG="--target-list=${MAIN_SOFTMMU_TARGETS} "
- CACHE_NAME="${TRAVIS_BRANCH}-linux-clang-sanitize"
compiler: clang
before_script:
- ./configure ${CONFIG} --extra-cflags="-fsanitize=undefined -Werror" || { cat config.log && exit 1; }
- env:
- CONFIG="--disable-user --target-list-exclude=${MAIN_SOFTMMU_TARGETS}"
- CACHE_NAME="${TRAVIS_BRANCH}-linux-clang-default"
- CONFIG="--disable-user"
compiler: clang
# gprof/gcov are GCC features
- env:
- CONFIG="--enable-gprof --enable-gcov --disable-pie --target-list=${MAIN_SOFTMMU_TARGETS}"
- 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"
after_success:
- ${SRC_DIR}/scripts/travis/coverage-summary.sh
# We manually include builds which we disable "make check" for
- env:
- CONFIG="--without-default-devices --disable-user"
- CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-default"
- TEST_CMD=""
# We manually include builds which we disable "make check" for
- env:
- CONFIG="--enable-debug --enable-tcg-interpreter"
@@ -239,23 +173,15 @@ matrix:
# MacOSX builds
- env:
- CONFIG="--target-list=${MAIN_SOFTMMU_TARGETS}"
- 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
- env:
- CONFIG="--target-list=i386-softmmu,ppc-softmmu,ppc64-softmmu,m68k-softmmu,x86_64-softmmu"
os: osx
osx_image: xcode10.3
compiler: clang
# Python builds
- env:
- CONFIG="--target-list=x86_64-softmmu"
- CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-default"
language: python
python:
- "3.4"
@@ -263,7 +189,6 @@ matrix:
- env:
- CONFIG="--target-list=x86_64-softmmu"
- CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-default"
language: python
python:
- "3.6"
@@ -271,20 +196,13 @@ matrix:
# Acceptance (Functional) tests
- env:
- CONFIG="--python=/usr/bin/python3 --target-list=x86_64-softmmu,mips-softmmu,mips64el-softmmu,aarch64-softmmu,arm-softmmu,s390x-softmmu,alpha-softmmu,ppc-softmmu,ppc64-softmmu,m68k-softmmu,sparc-softmmu"
- TEST_CMD="make check-acceptance"
after_failure:
- cat tests/results/latest/job.log
- CONFIG="--python=/usr/bin/python3 --target-list=x86_64-softmmu"
- TEST_CMD="make AVOCADO_SHOW=app check-acceptance"
addons:
apt:
packages:
- python3-pil
- python3-pip
- python3.5-venv
- tesseract-ocr
- tesseract-ocr-eng
# Using newer GCC with sanitizers
- addons:
apt:
@@ -294,8 +212,8 @@ matrix:
- ubuntu-toolchain-r-test
packages:
# Extra toolchains
- gcc-9
- g++-9
- gcc-7
- g++-7
# Build dependencies
- libaio-dev
- libattr1-dev
@@ -311,12 +229,11 @@ matrix:
- libpixman-1-dev
- libpng12-dev
- librados-dev
- libsdl2-dev
- libsdl2-image-dev
- libsdl1.2-dev
- libseccomp-dev
- libspice-protocol-dev
- libspice-server-dev
- libssh-dev
- libssh2-1-dev
- liburcu-dev
- libusb-1.0-0-dev
- libvte-2.91-dev
@@ -325,60 +242,13 @@ matrix:
language: generic
compiler: none
env:
- COMPILER_NAME=gcc CXX=g++-9 CC=gcc-9
- CONFIG="--cc=gcc-9 --cxx=g++-9 --disable-pie --disable-linux-user"
- COMPILER_NAME=gcc CXX=g++-7 CC=gcc-7
- CONFIG="--cc=gcc-7 --cxx=g++-7 --disable-pie --disable-linux-user"
- TEST_CMD=""
before_script:
- ./configure ${CONFIG} --extra-cflags="-g3 -O0 -Wno-error=stringop-truncation -fsanitize=thread -fuse-ld=gold" || { cat config.log && exit 1; }
- ./configure ${CONFIG} --extra-cflags="-g3 -O0 -fsanitize=thread -fuse-ld=gold" || { cat config.log && exit 1; }
# Run check-tcg against linux-user
- env:
- CONFIG="--disable-system --enable-debug-tcg"
- CONFIG="--disable-system"
- TEST_CMD="make -j3 check-tcg V=1"
- 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
- env:
- CONFIG="--disable-system --enable-plugins --enable-debug-tcg --target-list-exclude=sparc64-linux-user"
- TEST_CMD="make -j3 check-tcg V=1"
- CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-debug-tcg"
# Run check-tcg against softmmu targets
- env:
- CONFIG="--enable-debug-tcg --target-list=xtensa-softmmu,arm-softmmu,aarch64-softmmu,alpha-softmmu"
- TEST_CMD="make -j3 check-tcg V=1"
- CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-debug-tcg"
# Run check-tcg against softmmu targets (with plugins)
- env:
- CONFIG="--enable-plugins --enable-debug-tcg --target-list=xtensa-softmmu,arm-softmmu,aarch64-softmmu,alpha-softmmu"
- TEST_CMD="make -j3 check-tcg V=1"
- CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-debug-tcg"
# 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.
- 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 -j3"
- QEMU_VERSION="${TRAVIS_TAG:1}"
- CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-default"
before_script:
- command -v ccache && ccache --zero-stats
- mkdir -p ${BUILD_DIR} && cd ${BUILD_DIR}
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}
- ./configure ${BASE_CONFIG} ${CONFIG} || { cat config.log && exit 1; }
- make install

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,37 +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 KVM
bool
config LINUX
bool
config OPENGL
bool
config X11
bool
config SPICE
bool
config IVSHMEM
bool
config TPM
bool
config VHOST_USER
bool
config XEN
bool
select FSDEV_9P if VIRTFS
config VIRTFS
bool
config PVRDMA
bool

26
LICENSE
View File

@@ -1,26 +1,20 @@
The QEMU distribution includes both the QEMU emulator and
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 QEMU license:
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
Public License, version 2.
2) Parts of the QEMU emulator have specific licenses which are compatible
with the GNU General Public License, version 2. Hence each source file
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.
2) Parts of QEMU have specific licenses which are compatible with the
GNU General Public License, version 2. Hence each source file 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
License (and no later version) are only accepted for the following files
or directories: bsd-user/, linux-user/, hw/vfio/, hw/xen/xen_pt*.
3) The Tiny Code Generator (TCG) is mostly under the BSD or MIT licenses;
but some parts may be GPLv2 or other licenses. Again, see the
specific licensing information in each source file.
3) The Tiny Code Generator (TCG) is released under the BSD license
(see license headers in files).
4) QEMU is a trademark of Fabrice Bellard.

File diff suppressed because it is too large Load Diff

434
Makefile
View File

@@ -1,9 +1,5 @@
# Makefile for QEMU.
ifneq ($(words $(subst :, ,$(CURDIR))), 1)
$(error main directory cannot contain spaces nor colons)
endif
# Always point to the root of the build tree (needs GNU make).
BUILD_DIR=$(CURDIR)
@@ -13,7 +9,7 @@ SRC_PATH=.
UNCHECKED_GOALS := %clean TAGS cscope ctags dist \
html info pdf txt \
help check-help print-% \
docker docker-% vm-help vm-test vm-build-%
docker docker-% vm-test vm-build-%
print-%:
@echo '$*=$($*)'
@@ -73,13 +69,14 @@ CONFIG_ALL=y
config-host.mak: $(SRC_PATH)/configure $(SRC_PATH)/pc-bios $(SRC_PATH)/VERSION
@echo $@ is out-of-date, running configure
@./config.status
# Force configure to re-run if the API symbols are updated
ifeq ($(CONFIG_PLUGIN),y)
config-host.mak: $(SRC_PATH)/plugins/qemu-plugins.symbols
endif
@# TODO: The next lines include code which supports a smooth
@# transition from old configurations without config.status.
@# This code can be removed after QEMU 1.7.
@if test -x config.status; then \
./config.status; \
else \
sed -n "/.*Configured with/s/[^:]*: //p" $@ | sh; \
fi
else
config-host.mak:
ifneq ($(filter-out $(UNCHECKED_GOALS),$(MAKECMDGOALS)),$(if $(MAKECMDGOALS),,fail))
@@ -90,24 +87,7 @@ endif
include $(SRC_PATH)/rules.mak
# lor is defined in rules.mak
CONFIG_BLOCK := $(call lor,$(CONFIG_SOFTMMU),$(CONFIG_TOOLS))
# Create QEMU_PKGVERSION and FULL_VERSION strings
# If PKGVERSION is set, use that; otherwise get version and -dirty status from git
QEMU_PKGVERSION := $(if $(PKGVERSION),$(PKGVERSION),$(shell \
cd $(SRC_PATH); \
if test -e .git; then \
git describe --match 'v*' 2>/dev/null | tr -d '\n'; \
if ! git diff-index --quiet HEAD &>/dev/null; then \
echo "-dirty"; \
fi; \
fi))
# Either "version (pkgversion)", or just "version" if pkgversion not set
FULL_VERSION := $(if $(QEMU_PKGVERSION),$(VERSION) ($(QEMU_PKGVERSION)),$(VERSION))
generated-files-y = qemu-version.h config-host.h qemu-options.def
GENERATED_FILES = qemu-version.h config-host.h qemu-options.def
GENERATED_QAPI_FILES = qapi/qapi-builtin-types.h qapi/qapi-builtin-types.c
GENERATED_QAPI_FILES += qapi/qapi-types.h qapi/qapi-types.c
@@ -127,18 +107,20 @@ GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-events-%.c)
GENERATED_QAPI_FILES += qapi/qapi-introspect.c qapi/qapi-introspect.h
GENERATED_QAPI_FILES += qapi/qapi-doc.texi
generated-files-y += $(GENERATED_QAPI_FILES)
GENERATED_FILES += $(GENERATED_QAPI_FILES)
generated-files-y += trace/generated-tcg-tracers.h
GENERATED_FILES += trace/generated-tcg-tracers.h
generated-files-y += trace/generated-helpers-wrappers.h
generated-files-y += trace/generated-helpers.h
generated-files-y += trace/generated-helpers.c
GENERATED_FILES += trace/generated-helpers-wrappers.h
GENERATED_FILES += trace/generated-helpers.h
GENERATED_FILES += trace/generated-helpers.c
generated-files-$(CONFIG_TRACE_UST) += trace-ust-all.h
generated-files-$(CONFIG_TRACE_UST) += trace-ust-all.c
ifdef CONFIG_TRACE_UST
GENERATED_FILES += trace-ust-all.h
GENERATED_FILES += trace-ust-all.c
endif
generated-files-y += module_block.h
GENERATED_FILES += module_block.h
TRACE_HEADERS = trace-root.h $(trace-events-subdirs:%=%/trace.h)
TRACE_SOURCES = trace-root.c $(trace-events-subdirs:%=%/trace.c)
@@ -151,10 +133,10 @@ ifdef CONFIG_TRACE_UST
TRACE_HEADERS += trace-ust-root.h $(trace-events-subdirs:%=%/trace-ust.h)
endif
generated-files-y += $(TRACE_HEADERS)
generated-files-y += $(TRACE_SOURCES)
generated-files-y += $(BUILD_DIR)/trace-events-all
generated-files-y += .git-submodule-status
GENERATED_FILES += $(TRACE_HEADERS)
GENERATED_FILES += $(TRACE_SOURCES)
GENERATED_FILES += $(BUILD_DIR)/trace-events-all
GENERATED_FILES += .git-submodule-status
trace-group-name = $(shell dirname $1 | sed -e 's/[^a-zA-Z0-9]/_/g')
@@ -285,7 +267,7 @@ KEYCODEMAP_FILES = \
ui/input-keymap-osx-to-qcode.c \
$(NULL)
generated-files-$(CONFIG_SOFTMMU) += $(KEYCODEMAP_FILES)
GENERATED_FILES += $(KEYCODEMAP_FILES)
ui/input-keymap-%.c: $(KEYCODEMAP_GEN) $(KEYCODEMAP_CSV) $(SRC_PATH)/ui/Makefile.objs
$(call quiet-command,\
@@ -300,10 +282,6 @@ ui/input-keymap-%.c: $(KEYCODEMAP_GEN) $(KEYCODEMAP_CSV) $(SRC_PATH)/ui/Makefile
$(KEYCODEMAP_GEN): .git-submodule-status
$(KEYCODEMAP_CSV): .git-submodule-status
edk2-decompressed = $(basename $(wildcard pc-bios/edk2-*.fd.bz2))
pc-bios/edk2-%.fd: pc-bios/edk2-%.fd.bz2
$(call quiet-command,bzip2 -d -c $< > $@,"BUNZIP2",$<)
# Don't try to regenerate Makefile or configure
# We don't generate any of them
Makefile: ;
@@ -316,33 +294,10 @@ $(call set-vpath, $(SRC_PATH))
LIBS+=-lz $(LIBS_TOOLS)
vhost-user-json-y =
HELPERS-y =
HELPERS-$(call land,$(CONFIG_SOFTMMU),$(CONFIG_LINUX)) = qemu-bridge-helper$(EXESUF)
ifdef CONFIG_LINUX
ifdef CONFIG_VIRGL
ifdef CONFIG_GBM
HELPERS-y += vhost-user-gpu$(EXESUF)
vhost-user-json-y += contrib/vhost-user-gpu/50-qemu-gpu.json
endif
endif
endif
# Sphinx does not allow building manuals into the same directory as
# the source files, so if we're doing an in-tree QEMU build we must
# build the manuals into a subdirectory (and then install them from
# there for 'make install'). For an out-of-tree build we can just
# use the docs/ subdirectory in the build tree as normal.
ifeq ($(realpath $(SRC_PATH)),$(realpath .))
MANUAL_BUILDDIR := docs/built
else
MANUAL_BUILDDIR := docs
endif
ifdef BUILD_DOCS
DOCS=qemu-doc.html qemu-doc.txt qemu.1 qemu-img.1 qemu-nbd.8 $(MANUAL_BUILDDIR)/interop/qemu-ga.8
DOCS=qemu-doc.html qemu-doc.txt qemu.1 qemu-img.1 qemu-nbd.8 qemu-ga.8
DOCS+=docs/interop/qemu-qmp-ref.html docs/interop/qemu-qmp-ref.txt docs/interop/qemu-qmp-ref.7
DOCS+=docs/interop/qemu-ga-ref.html docs/interop/qemu-ga-ref.txt docs/interop/qemu-ga-ref.7
DOCS+=docs/qemu-block-drivers.7
@@ -358,14 +313,14 @@ DOCS=
endif
SUBDIR_MAKEFLAGS=$(if $(V),,--no-print-directory --quiet) BUILD_DIR=$(BUILD_DIR)
SUBDIR_DEVICES_MAK=$(patsubst %, %/config-devices.mak, $(filter %-softmmu, $(TARGET_DIRS)))
SUBDIR_DEVICES_MAK_DEP=$(patsubst %, %.d, $(SUBDIR_DEVICES_MAK))
SUBDIR_DEVICES_MAK=$(patsubst %, %/config-devices.mak, $(TARGET_DIRS))
SUBDIR_DEVICES_MAK_DEP=$(patsubst %, %-config-devices.mak.d, $(TARGET_DIRS))
ifeq ($(SUBDIR_DEVICES_MAK),)
config-all-devices.mak: config-host.mak
config-all-devices.mak:
$(call quiet-command,echo '# no devices' > $@,"GEN","$@")
else
config-all-devices.mak: $(SUBDIR_DEVICES_MAK) config-host.mak
config-all-devices.mak: $(SUBDIR_DEVICES_MAK)
$(call quiet-command, sed -n \
's|^\([^=]*\)=\(.*\)$$|\1:=$$(findstring y,$$(\1)\2)|p' \
$(SUBDIR_DEVICES_MAK) | sort -u > $@, \
@@ -374,28 +329,9 @@ endif
-include $(SUBDIR_DEVICES_MAK_DEP)
# This has to be kept in sync with Kconfig.host.
MINIKCONF_ARGS = \
$(CONFIG_MINIKCONF_MODE) \
$@ $*/config-devices.mak.d $< $(MINIKCONF_INPUTS) \
CONFIG_KVM=$(CONFIG_KVM) \
CONFIG_SPICE=$(CONFIG_SPICE) \
CONFIG_IVSHMEM=$(CONFIG_IVSHMEM) \
CONFIG_TPM=$(CONFIG_TPM) \
CONFIG_XEN=$(CONFIG_XEN) \
CONFIG_OPENGL=$(CONFIG_OPENGL) \
CONFIG_X11=$(CONFIG_X11) \
CONFIG_VHOST_USER=$(CONFIG_VHOST_USER) \
CONFIG_VIRTFS=$(CONFIG_VIRTFS) \
CONFIG_LINUX=$(CONFIG_LINUX) \
CONFIG_PVRDMA=$(CONFIG_PVRDMA)
MINIKCONF_INPUTS = $(SRC_PATH)/Kconfig.host $(SRC_PATH)/hw/Kconfig \
$(wildcard $(SRC_PATH)/hw/*/Kconfig)
MINIKCONF = $(PYTHON) $(SRC_PATH)/scripts/minikconf.py \
$(SUBDIR_DEVICES_MAK): %/config-devices.mak: default-configs/%.mak $(MINIKCONF_INPUTS) $(BUILD_DIR)/config-host.mak
$(call quiet-command, $(MINIKCONF) $(MINIKCONF_ARGS) > $@.tmp, "GEN", "$@.tmp")
%/config-devices.mak: default-configs/%.mak $(SRC_PATH)/scripts/make_device_config.sh
$(call quiet-command, \
$(SHELL) $(SRC_PATH)/scripts/make_device_config.sh $< $*-config-devices.mak.d $@ > $@.tmp,"GEN","$@.tmp")
$(call quiet-command, if test -f $@; then \
if cmp -s $@.old $@; then \
mv $@.tmp $@; \
@@ -434,13 +370,11 @@ dummy := $(call unnest-vars,, \
libvhost-user-obj-y \
vhost-user-scsi-obj-y \
vhost-user-blk-obj-y \
vhost-user-input-obj-y \
vhost-user-gpu-obj-y \
qga-vss-dll-obj-y \
block-obj-y \
block-obj-m \
crypto-obj-y \
crypto-user-obj-y \
crypto-aes-obj-y \
qom-obj-y \
io-obj-y \
common-obj-y \
@@ -449,16 +383,32 @@ dummy := $(call unnest-vars,, \
ui-obj-m \
audio-obj-y \
audio-obj-m \
trace-obj-y)
trace-obj-y \
slirp-obj-y)
include $(SRC_PATH)/tests/Makefile.include
all: $(DOCS) $(if $(BUILD_DOCS),sphinxdocs) $(TOOLS) $(HELPERS-y) recurse-all modules $(vhost-user-json-y)
all: $(DOCS) $(TOOLS) $(HELPERS-y) recurse-all modules
qemu-version.h: FORCE
$(call quiet-command, \
(printf '#define QEMU_PKGVERSION "$(QEMU_PKGVERSION)"\n'; \
printf '#define QEMU_FULL_VERSION "$(FULL_VERSION)"\n'; \
(cd $(SRC_PATH); \
if test -n "$(PKGVERSION)"; then \
pkgvers="$(PKGVERSION)"; \
else \
if test -d .git; then \
pkgvers=$$(git describe --match 'v*' 2>/dev/null | tr -d '\n');\
if ! git diff-index --quiet HEAD &>/dev/null; then \
pkgvers="$${pkgvers}-dirty"; \
fi; \
fi; \
fi; \
printf "#define QEMU_PKGVERSION \"$${pkgvers}\"\n"; \
if test -n "$${pkgvers}"; then \
printf '#define QEMU_FULL_VERSION QEMU_VERSION " (" QEMU_PKGVERSION ")"\n'; \
else \
printf '#define QEMU_FULL_VERSION QEMU_VERSION\n'; \
fi; \
) > $@.tmp)
$(call quiet-command, if ! cmp -s $@ $@.tmp; then \
mv $@.tmp $@; \
@@ -471,31 +421,23 @@ config-host.h-timestamp: config-host.mak
qemu-options.def: $(SRC_PATH)/qemu-options.hx $(SRC_PATH)/scripts/hxtool
$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@,"GEN","$@")
TARGET_DIRS_RULES := $(foreach t, all clean install, $(addsuffix /$(t), $(TARGET_DIRS)))
SUBDIR_RULES=$(patsubst %,subdir-%, $(TARGET_DIRS))
SOFTMMU_SUBDIR_RULES=$(filter %-softmmu,$(SUBDIR_RULES))
SOFTMMU_ALL_RULES=$(filter %-softmmu/all, $(TARGET_DIRS_RULES))
$(SOFTMMU_ALL_RULES): $(authz-obj-y)
$(SOFTMMU_ALL_RULES): $(block-obj-y)
$(SOFTMMU_ALL_RULES): $(chardev-obj-y)
$(SOFTMMU_ALL_RULES): $(crypto-obj-y)
$(SOFTMMU_ALL_RULES): $(io-obj-y)
$(SOFTMMU_ALL_RULES): config-all-devices.mak
ifdef DECOMPRESS_EDK2_BLOBS
$(SOFTMMU_ALL_RULES): $(edk2-decompressed)
endif
$(SOFTMMU_SUBDIR_RULES): $(authz-obj-y)
$(SOFTMMU_SUBDIR_RULES): $(block-obj-y)
$(SOFTMMU_SUBDIR_RULES): $(crypto-obj-y)
$(SOFTMMU_SUBDIR_RULES): $(io-obj-y)
$(SOFTMMU_SUBDIR_RULES): config-all-devices.mak
.PHONY: $(TARGET_DIRS_RULES)
# The $(TARGET_DIRS_RULES) are of the form SUBDIR/GOAL, so that
# $(dir $@) yields the sub-directory, and $(notdir $@) yields the sub-goal
$(TARGET_DIRS_RULES):
$(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C $(dir $@) V="$(V)" TARGET_DIR="$(dir $@)" $(notdir $@),)
subdir-%:
$(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C $* V="$(V)" TARGET_DIR="$*/" all,)
DTC_MAKE_ARGS=-I$(SRC_PATH)/dtc VPATH=$(SRC_PATH)/dtc -C dtc V="$(V)" LIBFDT_srcdir=$(SRC_PATH)/dtc/libfdt
DTC_CFLAGS=$(CFLAGS) $(QEMU_CFLAGS)
DTC_CPPFLAGS=-I$(BUILD_DIR)/dtc -I$(SRC_PATH)/dtc -I$(SRC_PATH)/dtc/libfdt
.PHONY: dtc/all
dtc/all: .git-submodule-status dtc/libfdt dtc/tests
subdir-dtc: .git-submodule-status dtc/libfdt dtc/tests
$(call quiet-command,$(MAKE) $(DTC_MAKE_ARGS) CPPFLAGS="$(DTC_CPPFLAGS)" CFLAGS="$(DTC_CFLAGS)" LDFLAGS="$(LDFLAGS)" ARFLAGS="$(ARFLAGS)" CC="$(CC)" AR="$(AR)" LD="$(LD)" $(SUBDIR_MAKEFLAGS) libfdt/libfdt.a,)
dtc/%: .git-submodule-status
@@ -513,39 +455,20 @@ CAP_CFLAGS += -DCAPSTONE_HAS_ARM64
CAP_CFLAGS += -DCAPSTONE_HAS_POWERPC
CAP_CFLAGS += -DCAPSTONE_HAS_X86
.PHONY: capstone/all
capstone/all: .git-submodule-status
subdir-capstone: .git-submodule-status
$(call quiet-command,$(MAKE) -C $(SRC_PATH)/capstone CAPSTONE_SHARED=no BUILDDIR="$(BUILD_DIR)/capstone" CC="$(CC)" AR="$(AR)" LD="$(LD)" RANLIB="$(RANLIB)" CFLAGS="$(CAP_CFLAGS)" $(SUBDIR_MAKEFLAGS) $(BUILD_DIR)/capstone/$(LIBCAPSTONE))
.PHONY: slirp/all
slirp/all: .git-submodule-status
$(call quiet-command,$(MAKE) -C $(SRC_PATH)/slirp \
BUILD_DIR="$(BUILD_DIR)/slirp" \
PKG_CONFIG="$(PKG_CONFIG)" \
CC="$(CC)" AR="$(AR)" LD="$(LD)" RANLIB="$(RANLIB)" \
CFLAGS="$(QEMU_CFLAGS) $(CFLAGS)" LDFLAGS="$(LDFLAGS)")
$(SUBDIR_RULES): libqemuutil.a $(common-obj-y) $(chardev-obj-y) $(slirp-obj-y) \
$(qom-obj-y) $(crypto-aes-obj-$(CONFIG_USER_ONLY))
# Compatibility gunk to keep make working across the rename of targets
# for recursion, to be removed some time after 4.1.
subdir-dtc: dtc/all
subdir-capstone: capstone/all
subdir-slirp: slirp/all
$(filter %/all, $(TARGET_DIRS_RULES)): libqemuutil.a $(common-obj-y) \
$(qom-obj-y) $(crypto-user-obj-$(CONFIG_USER_ONLY))
ROM_DIRS = $(addprefix pc-bios/, $(ROMS))
ROM_DIRS_RULES=$(foreach t, all clean, $(addsuffix /$(t), $(ROM_DIRS)))
ROMSUBDIR_RULES=$(patsubst %,romsubdir-%, $(ROMS))
# Only keep -O and -g cflags
.PHONY: $(ROM_DIRS_RULES)
$(ROM_DIRS_RULES):
$(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C $(dir $@) V="$(V)" TARGET_DIR="$(dir $@)" CFLAGS="$(filter -O% -g%,$(CFLAGS))" $(notdir $@),)
romsubdir-%:
$(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C pc-bios/$* V="$(V)" TARGET_DIR="$*/" CFLAGS="$(filter -O% -g%,$(CFLAGS))",)
.PHONY: recurse-all recurse-clean recurse-install
recurse-all: $(addsuffix /all, $(TARGET_DIRS) $(ROM_DIRS))
recurse-clean: $(addsuffix /clean, $(TARGET_DIRS) $(ROM_DIRS))
recurse-install: $(addsuffix /install, $(TARGET_DIRS))
$(addsuffix /install, $(TARGET_DIRS)): all
ALL_SUBDIRS=$(TARGET_DIRS) $(patsubst %,pc-bios/%, $(ROMS))
recurse-all: $(SUBDIR_RULES) $(ROMSUBDIR_RULES)
$(BUILD_DIR)/version.o: $(SRC_PATH)/version.rc config-host.h
$(call quiet-command,$(WINDRES) -I$(BUILD_DIR) -o $@ $<,"RC","version.o")
@@ -556,7 +479,7 @@ Makefile: $(version-obj-y)
# Build libraries
libqemuutil.a: $(util-obj-y) $(trace-obj-y) $(stub-obj-y)
libvhost-user.a: $(libvhost-user-obj-y) $(util-obj-y) $(stub-obj-y)
libvhost-user.a: $(libvhost-user-obj-y)
######################################################################
@@ -591,20 +514,13 @@ qemu-ga$(EXESUF): QEMU_CFLAGS += -I qga/qapi-generated
qemu-keymap$(EXESUF): LIBS += $(XKBCOMMON_LIBS)
qemu-keymap$(EXESUF): QEMU_CFLAGS += $(XKBCOMMON_CFLAGS)
qapi-py = $(SRC_PATH)/scripts/qapi/__init__.py \
$(SRC_PATH)/scripts/qapi/commands.py \
$(SRC_PATH)/scripts/qapi/common.py \
$(SRC_PATH)/scripts/qapi/doc.py \
$(SRC_PATH)/scripts/qapi/error.py \
qapi-py = $(SRC_PATH)/scripts/qapi/commands.py \
$(SRC_PATH)/scripts/qapi/events.py \
$(SRC_PATH)/scripts/qapi/expr.py \
$(SRC_PATH)/scripts/qapi/gen.py \
$(SRC_PATH)/scripts/qapi/introspect.py \
$(SRC_PATH)/scripts/qapi/parser.py \
$(SRC_PATH)/scripts/qapi/schema.py \
$(SRC_PATH)/scripts/qapi/source.py \
$(SRC_PATH)/scripts/qapi/types.py \
$(SRC_PATH)/scripts/qapi/visit.py \
$(SRC_PATH)/scripts/qapi/common.py \
$(SRC_PATH)/scripts/qapi/doc.py \
$(SRC_PATH)/scripts/qapi-gen.py
qga/qapi-generated/qga-qapi-types.c qga/qapi-generated/qga-qapi-types.h \
@@ -656,6 +572,7 @@ ifneq ($(EXESUF),)
qemu-ga: qemu-ga$(EXESUF) $(QGA_VSS_PROVIDER) $(QEMU_GA_MSI)
endif
elf2dmp$(EXESUF): LIBS += $(CURL_LIBS)
elf2dmp$(EXESUF): $(elf2dmp-obj-y)
$(call LINK, $^)
@@ -674,19 +591,6 @@ rdmacm-mux$(EXESUF): LIBS += "-libumad"
rdmacm-mux$(EXESUF): $(rdmacm-mux-obj-y) $(COMMON_LDADDS)
$(call LINK, $^)
vhost-user-gpu$(EXESUF): $(vhost-user-gpu-obj-y) $(libvhost-user-obj-y) libqemuutil.a libqemustub.a
$(call LINK, $^)
ifdef CONFIG_VHOST_USER_INPUT
ifdef CONFIG_LINUX
vhost-user-input$(EXESUF): $(vhost-user-input-obj-y) libvhost-user.a libqemuutil.a
$(call LINK, $^)
# build by default, do not install
all: vhost-user-input$(EXESUF)
endif
endif
module_block.h: $(SRC_PATH)/scripts/modules/module_block.py config-host.mak
$(call quiet-command,$(PYTHON) $< $@ \
$(addprefix $(SRC_PATH)/,$(patsubst %.mo,%.c,$(block-obj-m))), \
@@ -700,27 +604,26 @@ clean-coverage:
"CLEAN", "coverage files")
endif
clean: recurse-clean
clean:
# avoid old build problems by removing potentially incorrect old files
rm -f config.mak op-i386.h opc-i386.h gen-op-i386.h op-arm.h opc-arm.h gen-op-arm.h
rm -f qemu-options.def
rm -f *.msi
find . \( -name '*.so' -o -name '*.dll' -o -name '*.mo' -o -name '*.[oda]' \) -type f \
! -path ./roms/edk2/ArmPkg/Library/GccLto/liblto-aarch64.a \
! -path ./roms/edk2/ArmPkg/Library/GccLto/liblto-arm.a \
! -path ./roms/edk2/BaseTools/Source/Python/UPT/Dll/sqlite3.dll \
-exec rm {} +
rm -f $(edk2-decompressed)
rm -f $(filter-out %.tlb,$(TOOLS)) $(HELPERS-y) TAGS cscope.* *.pod *~ */*~
find . \( -name '*.so' -o -name '*.dll' -o -name '*.mo' -o -name '*.[oda]' \) -type f -exec rm {} +
rm -f $(filter-out %.tlb,$(TOOLS)) $(HELPERS-y) qemu-ga TAGS cscope.* *.pod *~ */*~
rm -f fsdev/*.pod scsi/*.pod
rm -f qemu-img-cmds.h
rm -f ui/shader/*-vert.h ui/shader/*-frag.h
@# May not be present in generated-files-y
@# May not be present in GENERATED_FILES
rm -f trace/generated-tracers-dtrace.dtrace*
rm -f trace/generated-tracers-dtrace.h*
rm -f $(foreach f,$(generated-files-y),$(f) $(f)-timestamp)
rm -f $(foreach f,$(GENERATED_FILES),$(f) $(f)-timestamp)
rm -f qapi-gen-timestamp
rm -rf qga/qapi-generated
for d in $(ALL_SUBDIRS); do \
if test -d $$d; then $(MAKE) -C $$d $@ || exit 1; fi; \
rm -f $$d/qemu-options.def; \
done
rm -f config-all-devices.mak
VERSION ?= $(shell cat VERSION)
@@ -730,14 +633,8 @@ dist: qemu-$(VERSION).tar.bz2
qemu-%.tar.bz2:
$(SRC_PATH)/scripts/make-release "$(SRC_PATH)" "$(patsubst qemu-%.tar.bz2,%,$@)"
define clean-manual =
rm -rf $(MANUAL_BUILDDIR)/$1/_static
rm -f $(MANUAL_BUILDDIR)/$1/objects.inv $(MANUAL_BUILDDIR)/$1/searchindex.js $(MANUAL_BUILDDIR)/$1/*.html
endef
distclean: clean
rm -f config-host.mak config-host.h* config-host.ld $(DOCS) qemu-options.texi qemu-img-cmds.texi qemu-monitor.texi qemu-monitor-info.texi
rm -f tests/tcg/config-*.mak
rm -f config-all-devices.mak config-all-disas.mak config.status
rm -f $(SUBDIR_DEVICES_MAK)
rm -f po/*.mo tests/qemu-iotests/common.env
@@ -746,7 +643,6 @@ distclean: clean
rm -f qemu-doc.fn qemu-doc.fns qemu-doc.info qemu-doc.ky qemu-doc.kys
rm -f qemu-doc.log qemu-doc.pdf qemu-doc.pg qemu-doc.toc qemu-doc.tp
rm -f qemu-doc.vr qemu-doc.txt
rm -f qemu-plugins-ld.symbols qemu-plugins-ld64.symbols
rm -f config.log
rm -f linux-headers/asm
rm -f docs/version.texi
@@ -757,10 +653,6 @@ distclean: clean
rm -f docs/interop/qemu-qmp-ref.html docs/interop/qemu-ga-ref.html
rm -f docs/qemu-block-drivers.7
rm -f docs/qemu-cpu-models.7
rm -rf .doctrees
$(call clean-manual,devel)
$(call clean-manual,interop)
$(call clean-manual,specs)
for d in $(TARGET_DIRS); do \
rm -rf $$d || exit 1 ; \
done
@@ -773,52 +665,28 @@ de-ch es fo fr-ca hu ja mk pt sl tr \
bepo cz
ifdef INSTALL_BLOBS
BLOBS=bios.bin bios-256k.bin bios-microvm.bin sgabios.bin vgabios.bin vgabios-cirrus.bin \
BLOBS=bios.bin bios-256k.bin sgabios.bin vgabios.bin vgabios-cirrus.bin \
vgabios-stdvga.bin vgabios-vmware.bin vgabios-qxl.bin vgabios-virtio.bin \
vgabios-ramfb.bin vgabios-bochs-display.bin vgabios-ati.bin \
vgabios-ramfb.bin vgabios-bochs-display.bin \
ppc_rom.bin openbios-sparc32 openbios-sparc64 openbios-ppc QEMU,tcx.bin QEMU,cgthree.bin \
pxe-e1000.rom pxe-eepro100.rom pxe-ne2k_pci.rom \
pxe-pcnet.rom pxe-rtl8139.rom pxe-virtio.rom \
efi-e1000.rom efi-eepro100.rom efi-ne2k_pci.rom \
efi-pcnet.rom efi-rtl8139.rom efi-virtio.rom \
efi-e1000e.rom efi-vmxnet3.rom \
qemu-nsis.bmp \
bamboo.dtb canyonlands.dtb petalogix-s3adsp1800.dtb petalogix-ml605.dtb \
multiboot.bin linuxboot.bin linuxboot_dma.bin kvmvapic.bin pvh.bin \
s390-ccw.img s390-netboot.img \
slof.bin skiboot.lid \
spapr-rtas.bin slof.bin skiboot.lid \
palcode-clipper \
u-boot.e500 u-boot-sam460-20100605.bin \
qemu_vga.ndrv \
edk2-licenses.txt \
hppa-firmware.img \
opensbi-riscv32-virt-fw_jump.bin \
opensbi-riscv64-sifive_u-fw_jump.bin opensbi-riscv64-virt-fw_jump.bin
DESCS=50-edk2-i386-secure.json 50-edk2-x86_64-secure.json \
60-edk2-aarch64.json 60-edk2-arm.json 60-edk2-i386.json 60-edk2-x86_64.json
hppa-firmware.img
else
BLOBS=
DESCS=
endif
# Note that we manually filter-out the non-Sphinx documentation which
# is currently built into the docs/interop directory in the build tree,
# and also any sphinx-built manpages.
define install-manual =
for d in $$(cd $(MANUAL_BUILDDIR) && find $1 -type d); do $(INSTALL_DIR) "$(DESTDIR)$(qemu_docdir)/$$d"; done
for f in $$(cd $(MANUAL_BUILDDIR) && find $1 -type f -a '!' '(' -name '*.[0-9]' -o -name 'qemu-*-qapi.*' -o -name 'qemu-*-ref.*' ')' ); do $(INSTALL_DATA) "$(MANUAL_BUILDDIR)/$$f" "$(DESTDIR)$(qemu_docdir)/$$f"; done
endef
# Note that we deliberately do not install the "devel" manual: it is
# for QEMU developers, and not interesting to our users.
.PHONY: install-sphinxdocs
install-sphinxdocs: sphinxdocs
$(call install-manual,interop)
$(call install-manual,specs)
install-doc: $(DOCS) install-sphinxdocs
install-doc: $(DOCS)
$(INSTALL_DIR) "$(DESTDIR)$(qemu_docdir)"
$(INSTALL_DATA) qemu-doc.html "$(DESTDIR)$(qemu_docdir)"
$(INSTALL_DATA) qemu-doc.txt "$(DESTDIR)$(qemu_docdir)"
@@ -831,7 +699,7 @@ ifdef CONFIG_POSIX
$(INSTALL_DATA) docs/interop/qemu-qmp-ref.7 "$(DESTDIR)$(mandir)/man7"
$(INSTALL_DATA) docs/qemu-block-drivers.7 "$(DESTDIR)$(mandir)/man7"
$(INSTALL_DATA) docs/qemu-cpu-models.7 "$(DESTDIR)$(mandir)/man7"
ifeq ($(CONFIG_TOOLS),y)
ifneq ($(TOOLS),)
$(INSTALL_DATA) qemu-img.1 "$(DESTDIR)$(mandir)/man1"
$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man8"
$(INSTALL_DATA) qemu-nbd.8 "$(DESTDIR)$(mandir)/man8"
@@ -840,7 +708,7 @@ ifdef CONFIG_TRACE_SYSTEMTAP
$(INSTALL_DATA) scripts/qemu-trace-stap.1 "$(DESTDIR)$(mandir)/man1"
endif
ifneq (,$(findstring qemu-ga,$(TOOLS)))
$(INSTALL_DATA) $(MANUAL_BUILDDIR)/interop/qemu-ga.8 "$(DESTDIR)$(mandir)/man8"
$(INSTALL_DATA) qemu-ga.8 "$(DESTDIR)$(mandir)/man8"
$(INSTALL_DATA) docs/interop/qemu-ga-ref.html "$(DESTDIR)$(qemu_docdir)"
$(INSTALL_DATA) docs/interop/qemu-ga-ref.txt "$(DESTDIR)$(qemu_docdir)"
$(INSTALL_DATA) docs/interop/qemu-ga-ref.7 "$(DESTDIR)$(mandir)/man7"
@@ -863,15 +731,9 @@ endif
ICON_SIZES=16x16 24x24 32x32 48x48 64x64 128x128 256x256 512x512
install-includedir:
$(INSTALL_DIR) "$(DESTDIR)$(includedir)"
install: all $(if $(BUILD_DOCS),install-doc) \
install-datadir install-localstatedir install-includedir \
$(if $(INSTALL_BLOBS),$(edk2-decompressed)) \
recurse-install
install: all $(if $(BUILD_DOCS),install-doc) install-datadir install-localstatedir
ifneq ($(TOOLS),)
$(call install-prog,$(TOOLS),$(DESTDIR)$(bindir))
$(call install-prog,$(subst qemu-ga,qemu-ga$(EXESUF),$(TOOLS)),$(DESTDIR)$(bindir))
endif
ifneq ($(CONFIG_MODULES),)
$(INSTALL_DIR) "$(DESTDIR)$(qemu_moddir)"
@@ -884,12 +746,6 @@ endif
ifneq ($(HELPERS-y),)
$(call install-prog,$(HELPERS-y),$(DESTDIR)$(libexecdir))
endif
ifneq ($(vhost-user-json-y),)
$(INSTALL_DIR) "$(DESTDIR)$(qemu_datadir)/vhost-user/"
for x in $(vhost-user-json-y); do \
$(INSTALL_DATA) $$x "$(DESTDIR)$(qemu_datadir)/vhost-user/"; \
done
endif
ifdef CONFIG_TRACE_SYSTEMTAP
$(INSTALL_PROG) "scripts/qemu-trace-stap" $(DESTDIR)$(bindir)
endif
@@ -897,47 +753,32 @@ ifneq ($(BLOBS),)
set -e; for x in $(BLOBS); do \
$(INSTALL_DATA) $(SRC_PATH)/pc-bios/$$x "$(DESTDIR)$(qemu_datadir)"; \
done
endif
ifdef INSTALL_BLOBS
set -e; for x in $(edk2-decompressed); do \
$(INSTALL_DATA) $$x "$(DESTDIR)$(qemu_datadir)"; \
done
endif
ifneq ($(DESCS),)
$(INSTALL_DIR) "$(DESTDIR)$(qemu_datadir)/firmware"
set -e; tmpf=$$(mktemp); trap 'rm -f -- "$$tmpf"' EXIT; \
for x in $(DESCS); do \
sed -e 's,@DATADIR@,$(qemu_datadir),' \
"$(SRC_PATH)/pc-bios/descriptors/$$x" > "$$tmpf"; \
$(INSTALL_DATA) "$$tmpf" \
"$(DESTDIR)$(qemu_datadir)/firmware/$$x"; \
done
endif
for s in $(ICON_SIZES); do \
mkdir -p "$(DESTDIR)$(qemu_icondir)/hicolor/$${s}/apps"; \
mkdir -p "$(DESTDIR)/$(qemu_icondir)/hicolor/$${s}/apps"; \
$(INSTALL_DATA) $(SRC_PATH)/ui/icons/qemu_$${s}.png \
"$(DESTDIR)$(qemu_icondir)/hicolor/$${s}/apps/qemu.png"; \
"$(DESTDIR)/$(qemu_icondir)/hicolor/$${s}/apps/qemu.png"; \
done; \
mkdir -p "$(DESTDIR)$(qemu_icondir)/hicolor/32x32/apps"; \
mkdir -p "$(DESTDIR)/$(qemu_icondir)/hicolor/32x32/apps"; \
$(INSTALL_DATA) $(SRC_PATH)/ui/icons/qemu_32x32.bmp \
"$(DESTDIR)$(qemu_icondir)/hicolor/32x32/apps/qemu.bmp"; \
mkdir -p "$(DESTDIR)$(qemu_icondir)/hicolor/scalable/apps"; \
"$(DESTDIR)/$(qemu_icondir)/hicolor/32x32/apps/qemu.bmp"; \
mkdir -p "$(DESTDIR)/$(qemu_icondir)/hicolor/scalable/apps"; \
$(INSTALL_DATA) $(SRC_PATH)/ui/icons/qemu.svg \
"$(DESTDIR)$(qemu_icondir)/hicolor/scalable/apps/qemu.svg"
mkdir -p "$(DESTDIR)$(qemu_desktopdir)"
"$(DESTDIR)/$(qemu_icondir)/hicolor/scalable/apps/qemu.svg"
mkdir -p "$(DESTDIR)/$(qemu_desktopdir)"
$(INSTALL_DATA) $(SRC_PATH)/ui/qemu.desktop \
"$(DESTDIR)$(qemu_desktopdir)/qemu.desktop"
"$(DESTDIR)/$(qemu_desktopdir)/qemu.desktop"
ifdef CONFIG_GTK
$(MAKE) -C po $@
endif
ifeq ($(CONFIG_PLUGIN),y)
$(INSTALL_DATA) $(SRC_PATH)/include/qemu/qemu-plugin.h "$(DESTDIR)$(includedir)/qemu-plugin.h"
endif
$(INSTALL_DIR) "$(DESTDIR)$(qemu_datadir)/keymaps"
set -e; for x in $(KEYMAPS); do \
$(INSTALL_DATA) $(SRC_PATH)/pc-bios/keymaps/$$x "$(DESTDIR)$(qemu_datadir)/keymaps"; \
done
$(INSTALL_DATA) $(BUILD_DIR)/trace-events-all "$(DESTDIR)$(qemu_datadir)/trace-events-all"
for d in $(TARGET_DIRS); do \
$(MAKE) $(SUBDIR_MAKEFLAGS) TARGET_DIR=$$d/ -C $$d $@ || exit 1 ; \
done
.PHONY: ctags
ctags:
@@ -976,14 +817,11 @@ ui/shader.o: $(SRC_PATH)/ui/shader.c \
MAKEINFO=makeinfo
MAKEINFOINCLUDES= -I docs -I $(<D) -I $(@D)
MAKEINFOFLAGS=--no-split --number-sections $(MAKEINFOINCLUDES)
TEXI2PODFLAGS=$(MAKEINFOINCLUDES) -DVERSION="$(VERSION)" -DCONFDIR="$(qemu_confdir)"
TEXI2PODFLAGS=$(MAKEINFOINCLUDES) "-DVERSION=$(VERSION)"
TEXI2PDFFLAGS=$(if $(V),,--quiet) -I $(SRC_PATH) $(MAKEINFOINCLUDES)
docs/version.texi: $(SRC_PATH)/VERSION config-host.mak
$(call quiet-command,(\
echo "@set VERSION $(VERSION)" && \
echo "@set CONFDIR $(qemu_confdir)" \
)> $@,"GEN","$@")
docs/version.texi: $(SRC_PATH)/VERSION
$(call quiet-command,echo "@set VERSION $(VERSION)" > $@,"GEN","$@")
%.html: %.texi docs/version.texi
$(call quiet-command,LC_ALL=C $(MAKEINFO) $(MAKEINFOFLAGS) --no-headers \
@@ -999,33 +837,6 @@ docs/version.texi: $(SRC_PATH)/VERSION config-host.mak
%.pdf: %.texi docs/version.texi
$(call quiet-command,texi2pdf $(TEXI2PDFFLAGS) $< -o $@,"GEN","$@")
# Sphinx builds all its documentation at once in one invocation
# and handles "don't rebuild things unless necessary" itself.
# The '.doctrees' files are cached information to speed this up.
.PHONY: sphinxdocs
sphinxdocs: $(MANUAL_BUILDDIR)/devel/index.html $(MANUAL_BUILDDIR)/interop/index.html $(MANUAL_BUILDDIR)/specs/index.html
# Canned command to build a single manual
# Arguments: $1 = manual name, $2 = Sphinx builder ('html' or 'man')
# Note the use of different doctree for each (manual, builder) tuple;
# this works around Sphinx not handling parallel invocation on
# a single doctree: https://github.com/sphinx-doc/sphinx/issues/2946
build-manual = $(call quiet-command,CONFDIR="$(qemu_confdir)" sphinx-build $(if $(V),,-q) -W -n -b $2 -D version=$(VERSION) -D release="$(FULL_VERSION)" -d .doctrees/$1-$2 $(SRC_PATH)/docs/$1 $(MANUAL_BUILDDIR)/$1 ,"SPHINX","$(MANUAL_BUILDDIR)/$1")
# We assume all RST files in the manual's directory are used in it
manual-deps = $(wildcard $(SRC_PATH)/docs/$1/*.rst) $(SRC_PATH)/docs/$1/conf.py $(SRC_PATH)/docs/conf.py
$(MANUAL_BUILDDIR)/devel/index.html: $(call manual-deps,devel)
$(call build-manual,devel,html)
$(MANUAL_BUILDDIR)/interop/index.html: $(call manual-deps,interop)
$(call build-manual,interop,html)
$(MANUAL_BUILDDIR)/specs/index.html: $(call manual-deps,specs)
$(call build-manual,specs,html)
$(MANUAL_BUILDDIR)/interop/qemu-ga.8: $(call manual-deps,interop)
$(call build-manual,interop,man)
qemu-options.texi: $(SRC_PATH)/qemu-options.hx $(SRC_PATH)/scripts/hxtool
$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@,"GEN","$@")
@@ -1049,21 +860,21 @@ qemu.1: qemu-option-trace.texi
qemu-img.1: qemu-img.texi qemu-option-trace.texi qemu-img-cmds.texi
fsdev/virtfs-proxy-helper.1: fsdev/virtfs-proxy-helper.texi
qemu-nbd.8: qemu-nbd.texi qemu-option-trace.texi
qemu-ga.8: qemu-ga.texi
docs/qemu-block-drivers.7: docs/qemu-block-drivers.texi
docs/qemu-cpu-models.7: docs/qemu-cpu-models.texi
scripts/qemu-trace-stap.1: scripts/qemu-trace-stap.texi
html: qemu-doc.html docs/interop/qemu-qmp-ref.html docs/interop/qemu-ga-ref.html sphinxdocs
html: qemu-doc.html docs/interop/qemu-qmp-ref.html docs/interop/qemu-ga-ref.html
info: qemu-doc.info docs/interop/qemu-qmp-ref.info docs/interop/qemu-ga-ref.info
pdf: qemu-doc.pdf docs/interop/qemu-qmp-ref.pdf docs/interop/qemu-ga-ref.pdf
txt: qemu-doc.txt docs/interop/qemu-qmp-ref.txt docs/interop/qemu-ga-ref.txt
qemu-doc.html qemu-doc.info qemu-doc.pdf qemu-doc.txt: \
qemu-img.texi qemu-nbd.texi qemu-options.texi \
qemu-tech.texi qemu-option-trace.texi \
qemu-deprecated.texi qemu-monitor.texi qemu-img-cmds.texi \
qemu-img.texi qemu-nbd.texi qemu-options.texi qemu-option-trace.texi \
qemu-deprecated.texi qemu-monitor.texi qemu-img-cmds.texi qemu-ga.texi \
qemu-monitor-info.texi docs/qemu-block-drivers.texi \
docs/qemu-cpu-models.texi docs/security.texi
docs/qemu-cpu-models.texi
docs/interop/qemu-ga-ref.dvi docs/interop/qemu-ga-ref.html \
docs/interop/qemu-ga-ref.info docs/interop/qemu-ga-ref.pdf \
@@ -1082,10 +893,7 @@ $(filter %.1 %.7 %.8,$(DOCS)): scripts/texi2pod.pl
%/coverage-report.html:
@mkdir -p $*
$(call quiet-command,\
gcovr -r $(SRC_PATH) \
$(foreach t, $(TARGET_DIRS), --object-directory $(BUILD_DIR)/$(t)) \
--object-directory $(BUILD_DIR) \
-p --html --html-details -o $@, \
gcovr -p --html --html-details -o $@, \
"GEN", "coverage-report.html")
.PHONY: coverage-report
@@ -1113,7 +921,7 @@ installer: $(INSTALLER)
INSTDIR=/tmp/qemu-nsis
$(INSTALLER): install-doc $(SRC_PATH)/qemu.nsi
$(INSTALLER): $(SRC_PATH)/qemu.nsi
$(MAKE) install prefix=${INSTDIR}
ifdef SIGNCODE
(cd ${INSTDIR}; \
@@ -1151,7 +959,7 @@ endif # CONFIG_WIN
# rebuilt before other object files
ifneq ($(wildcard config-host.mak),)
ifneq ($(filter-out $(UNCHECKED_GOALS),$(MAKECMDGOALS)),$(if $(MAKECMDGOALS),,fail))
Makefile: $(generated-files-y)
Makefile: $(GENERATED_FILES)
endif
endif
@@ -1181,7 +989,7 @@ endif
@$(if $(TARGET_DIRS), \
echo 'Architecture specific targets:'; \
$(foreach t, $(TARGET_DIRS), \
printf " %-30s - Build for %s\\n" $(t)/all $(t);) \
printf " %-30s - Build for %s\\n" $(patsubst %,subdir-%,$(t)) $(t);) \
echo '')
@echo 'Cleaning targets:'
@echo ' clean - Remove most generated files but keep the config'
@@ -1193,8 +1001,8 @@ endif
@echo ''
@echo 'Test targets:'
@echo ' check - Run all tests (check-help for details)'
@echo ' docker - Help about targets running tests inside containers'
@echo ' vm-help - Help about targets running tests inside VM'
@echo ' docker - Help about targets running tests inside Docker containers'
@echo ' vm-test - Help about targets running tests inside VM'
@echo ''
@echo 'Documentation targets:'
@echo ' html info pdf txt'

View File

@@ -4,6 +4,7 @@ stub-obj-y = stubs/ util/ crypto/
util-obj-y = util/ qobject/ qapi/
chardev-obj-y = chardev/
slirp-obj-$(CONFIG_SLIRP) = slirp/
#######################################################################
# authz-obj-y is code used by both qemu system emulation and qemu-img
@@ -13,7 +14,7 @@ authz-obj-y = authz/
#######################################################################
# block-obj-y is code used by both qemu system emulation and qemu-img
block-obj-y = nbd/
block-obj-y += nbd/
block-obj-y += block.o blockjob.o job.o
block-obj-y += block/ scsi/
block-obj-y += qemu-io-cmds.o
@@ -25,7 +26,7 @@ block-obj-m = block/
# crypto-obj-y is code used by both qemu system emulation and qemu-img
crypto-obj-y = crypto/
crypto-user-obj-y = crypto/
crypto-aes-obj-y = crypto/
#######################################################################
# qom-obj-y is code used by both qemu system emulation and qemu-img
@@ -45,9 +46,7 @@ io-obj-y = io/
ifeq ($(CONFIG_SOFTMMU),y)
common-obj-y = blockdev.o blockdev-nbd.o block/
common-obj-y += bootdevice.o iothread.o
common-obj-y += dump/
common-obj-y += job-qmp.o
common-obj-y += monitor/
common-obj-y += net/
common-obj-y += qdev-monitor.o device-hotplug.o
common-obj-$(CONFIG_WIN32) += os-win32.o
@@ -85,6 +84,7 @@ common-obj-$(CONFIG_FDT) += device_tree.o
######################################################################
# qapi
common-obj-y += qmp.o hmp.o
common-obj-y += qapi/
endif
@@ -102,6 +102,7 @@ version-obj-$(CONFIG_WIN32) += $(BUILD_DIR)/version.o
######################################################################
# tracing
util-obj-y += trace/
target-obj-y += trace/
######################################################################
# guest agent
@@ -123,28 +124,16 @@ vhost-user-scsi.o-libs := $(LIBISCSI_LIBS)
vhost-user-scsi-obj-y = contrib/vhost-user-scsi/
vhost-user-blk-obj-y = contrib/vhost-user-blk/
rdmacm-mux-obj-y = contrib/rdmacm-mux/
vhost-user-input-obj-y = contrib/vhost-user-input/
vhost-user-gpu-obj-y = contrib/vhost-user-gpu/
######################################################################
trace-events-subdirs =
trace-events-subdirs += accel/kvm
trace-events-subdirs += accel/tcg
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 += audio
trace-events-subdirs += authz
trace-events-subdirs += block
trace-events-subdirs += io
trace-events-subdirs += nbd
trace-events-subdirs += scsi
endif
ifeq ($(CONFIG_SOFTMMU),y)
trace-events-subdirs += chardev
trace-events-subdirs += audio
trace-events-subdirs += crypto
trace-events-subdirs += hw/9pfs
trace-events-subdirs += hw/acpi
trace-events-subdirs += hw/alpha
@@ -153,6 +142,7 @@ trace-events-subdirs += hw/audio
trace-events-subdirs += hw/block
trace-events-subdirs += hw/block/dataplane
trace-events-subdirs += hw/char
trace-events-subdirs += hw/display
trace-events-subdirs += hw/dma
trace-events-subdirs += hw/hppa
trace-events-subdirs += hw/i2c
@@ -163,7 +153,6 @@ trace-events-subdirs += hw/input
trace-events-subdirs += hw/intc
trace-events-subdirs += hw/isa
trace-events-subdirs += hw/mem
trace-events-subdirs += hw/mips
trace-events-subdirs += hw/misc
trace-events-subdirs += hw/misc/macio
trace-events-subdirs += hw/net
@@ -173,7 +162,6 @@ trace-events-subdirs += hw/pci-host
trace-events-subdirs += hw/ppc
trace-events-subdirs += hw/rdma
trace-events-subdirs += hw/rdma/vmw
trace-events-subdirs += hw/rtc
trace-events-subdirs += hw/s390x
trace-events-subdirs += hw/scsi
trace-events-subdirs += hw/sd
@@ -187,24 +175,22 @@ trace-events-subdirs += hw/virtio
trace-events-subdirs += hw/watchdog
trace-events-subdirs += hw/xen
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 += nbd
trace-events-subdirs += net
trace-events-subdirs += ui
endif
trace-events-subdirs += hw/display
trace-events-subdirs += qapi
trace-events-subdirs += qom
trace-events-subdirs += scsi
trace-events-subdirs += target/arm
trace-events-subdirs += target/hppa
trace-events-subdirs += target/i386
trace-events-subdirs += target/mips
trace-events-subdirs += target/ppc
trace-events-subdirs += target/riscv
trace-events-subdirs += target/s390x
trace-events-subdirs += target/sparc
trace-events-subdirs += ui
trace-events-subdirs += util
trace-events-subdirs += hw/core
trace-events-files = $(SRC_PATH)/trace-events $(trace-events-subdirs:%=$(SRC_PATH)/%/trace-events)

View File

@@ -4,11 +4,8 @@ BUILD_DIR?=$(CURDIR)/..
include ../config-host.mak
include config-target.mak
include $(SRC_PATH)/rules.mak
ifdef CONFIG_SOFTMMU
include config-devices.mak
endif
include $(SRC_PATH)/rules.mak
$(call set-vpath, $(SRC_PATH):$(BUILD_DIR))
ifdef CONFIG_LINUX
@@ -39,12 +36,14 @@ endif
PROGS=$(QEMU_PROG) $(QEMU_PROGW)
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-timestamp: config-target.mak
config-devices.h: config-devices.h-timestamp
config-devices.h-timestamp: config-devices.mak
ifdef CONFIG_TRACE_SYSTEMTAP
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
@true
obj-y += trace/
#########################################################
# cpu emulator library
obj-y += exec.o exec-vary.o
obj-y += exec.o
obj-y += accel/
obj-$(CONFIG_TCG) += tcg/tcg.o tcg/tcg-op.o tcg/tcg-op-vec.o tcg/tcg-op-gvec.o
obj-$(CONFIG_TCG) += tcg/tcg-common.o tcg/optimize.o
@@ -117,9 +114,6 @@ obj-$(CONFIG_TCG) += fpu/softfloat.o
obj-y += target/$(TARGET_BASE_ARCH)/
obj-y += disas.o
obj-$(call notempty,$(TARGET_XML_FILES)) += gdbstub-xml.o
LIBS := $(libs_cpu) $(LIBS)
obj-$(CONFIG_PLUGIN) += plugins/
#########################################################
# Linux user emulator target
@@ -151,14 +145,14 @@ endif #CONFIG_BSD_USER
#########################################################
# System emulator target
ifdef CONFIG_SOFTMMU
obj-y += arch_init.o cpus.o gdbstub.o balloon.o ioport.o
obj-y += arch_init.o cpus.o monitor.o gdbstub.o balloon.o ioport.o numa.o
obj-y += qtest.o
obj-y += dump/
obj-y += hw/
obj-y += monitor/
obj-y += qapi/
obj-y += memory.o
obj-y += memory_mapping.o
obj-y += dump.o
obj-$(TARGET_X86_64) += win_dump.o
obj-y += migration/ram.o
LIBS := $(libs_softmmu) $(LIBS)
@@ -169,37 +163,45 @@ else
obj-y += hw/$(TARGET_BASE_ARCH)/
endif
generated-files-y += hmp-commands.h hmp-commands-info.h
generated-files-y += config-devices.h
GENERATED_FILES += hmp-commands.h hmp-commands-info.h
endif # CONFIG_SOFTMMU
dummy := $(call unnest-vars,,obj-y)
all-obj-y := $(obj-y)
target-obj-y :=
block-obj-y :=
common-obj-y :=
chardev-obj-y :=
slirp-obj-y :=
include $(SRC_PATH)/Makefile.objs
dummy := $(call unnest-vars,,target-obj-y)
target-obj-y-save := $(target-obj-y)
dummy := $(call unnest-vars,.., \
authz-obj-y \
block-obj-y \
block-obj-m \
chardev-obj-y \
crypto-obj-y \
crypto-user-obj-y \
crypto-aes-obj-y \
qom-obj-y \
io-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 += $(target-obj-y)
all-obj-y += $(qom-obj-y)
all-obj-$(CONFIG_SOFTMMU) += $(authz-obj-y)
all-obj-$(CONFIG_SOFTMMU) += $(block-obj-y) $(chardev-obj-y)
all-obj-$(CONFIG_USER_ONLY) += $(crypto-user-obj-y)
all-obj-$(CONFIG_USER_ONLY) += $(crypto-aes-obj-y)
all-obj-$(CONFIG_SOFTMMU) += $(crypto-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
endif
COMMON_LDADDS = ../libqemuutil.a
@@ -224,7 +226,6 @@ clean: clean-target
rm -f *.a *~ $(PROGS)
rm -f $(shell find . -name '*.[od]')
rm -f hmp-commands.h gdbstub-xml.c
rm -f trace/generated-helpers.c trace/generated-helpers.c-timestamp
ifdef CONFIG_TRACE_SYSTEMTAP
rm -f *.stp
endif
@@ -240,21 +241,5 @@ ifdef CONFIG_TRACE_SYSTEMTAP
$(INSTALL_DATA) $(QEMU_PROG)-log.stp "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset/$(QEMU_PROG)-log.stp"
endif
generated-files-y += config-target.h
Makefile: $(generated-files-y)
# 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
GENERATED_FILES += config-target.h
Makefile: $(GENERATED_FILES)

View File

@@ -1,6 +1,5 @@
===========
QEMU README
===========
QEMU README
===========
QEMU is a generic and open source machine & userspace emulator and
virtualizer.
@@ -38,9 +37,6 @@ QEMU is multi-platform software intended to be buildable on all modern
Linux platforms, OS-X, Win32 (via the Mingw64 toolchain) and a variety
of other UNIX targets. The simple steps to build QEMU are:
.. code-block:: shell
mkdir build
cd build
../configure
@@ -48,9 +44,9 @@ of other UNIX targets. The simple steps to build QEMU are:
Additional information can also be found online via the QEMU website:
* `<https://qemu.org/Hosts/Linux>`_
* `<https://qemu.org/Hosts/Mac>`_
* `<https://qemu.org/Hosts/W32>`_
https://qemu.org/Hosts/Linux
https://qemu.org/Hosts/Mac
https://qemu.org/Hosts/W32
Submitting patches
@@ -58,29 +54,24 @@ Submitting patches
The QEMU source code is maintained under the GIT version control system.
.. code-block:: shell
git clone https://git.qemu.org/git/qemu.git
When submitting patches, one common approach is to use 'git
format-patch' and/or 'git send-email' to format & send the mail to the
qemu-devel@nongnu.org mailing list. All patches submitted must contain
a 'Signed-off-by' line from the author. Patches should follow the
guidelines set out in the CODING_STYLE.rst file.
guidelines set out in the HACKING and CODING_STYLE files.
Additional information on submitting patches can be found online via
the QEMU website
* `<https://qemu.org/Contribute/SubmitAPatch>`_
* `<https://qemu.org/Contribute/TrivialPatches>`_
https://qemu.org/Contribute/SubmitAPatch
https://qemu.org/Contribute/TrivialPatches
The QEMU website is also maintained under source control.
.. code-block:: shell
git clone https://git.qemu.org/git/qemu-web.git
* `<https://www.qemu.org/2017/02/04/the-new-qemu-website-is-up/>`_
https://www.qemu.org/2017/02/04/the-new-qemu-website-is-up/
A 'git-publish' utility was created to make above process less
cumbersome, and is highly recommended for making regular contributions,
@@ -91,12 +82,10 @@ manually for once.
For installation instructions, please go to
* `<https://github.com/stefanha/git-publish>`_
https://github.com/stefanha/git-publish
The workflow with 'git-publish' is:
.. code-block:: shell
$ git checkout master -b my-feature
$ # work on new commits, add your 'Signed-off-by' lines to each
$ git publish
@@ -106,8 +95,6 @@ back to it in the future.
Sending v2:
.. code-block:: shell
$ git checkout my-feature # same topic branch
$ # making changes to the commits (using 'git rebase', for example)
$ git publish
@@ -122,7 +109,7 @@ The QEMU project uses Launchpad as its primary upstream bug tracker. Bugs
found when running code built from QEMU git or upstream released sources
should be reported via:
* `<https://bugs.launchpad.net/qemu/>`_
https://bugs.launchpad.net/qemu/
If using QEMU via an operating system vendor pre-built binary package, it
is preferable to report bugs to the vendor's own bug tracker first. If
@@ -131,7 +118,7 @@ reported via launchpad.
For additional information on bug reporting consult:
* `<https://qemu.org/Contribute/ReportABug>`_
https://qemu.org/Contribute/ReportABug
Contact
@@ -140,11 +127,13 @@ Contact
The QEMU community can be contacted in a number of ways, with the two
main methods being email and IRC
* `<mailto:qemu-devel@nongnu.org>`_
* `<https://lists.nongnu.org/mailman/listinfo/qemu-devel>`_
* #qemu on irc.oftc.net
- qemu-devel@nongnu.org
https://lists.nongnu.org/mailman/listinfo/qemu-devel
- #qemu on irc.oftc.net
Information on additional methods of contacting the community can be
found online via the QEMU website:
* `<https://qemu.org/Contribute/StartHere>`_
https://qemu.org/Contribute/StartHere
-- End

View File

@@ -1 +1 @@
4.1.92
3.1.50

View File

@@ -1,5 +1,4 @@
obj-$(CONFIG_SOFTMMU) += accel.o
obj-$(call land,$(CONFIG_SOFTMMU),$(CONFIG_POSIX)) += qtest.o
obj-$(CONFIG_KVM) += kvm/
obj-$(CONFIG_TCG) += tcg/
obj-y += stubs/

View File

@@ -65,8 +65,6 @@ static int accel_init_machine(AccelClass *acc, MachineState *ms)
ms->accelerator = NULL;
*(acc->allowed) = false;
object_unref(OBJECT(accel));
} else {
object_set_accelerator_compat_props(acc->compat_props);
}
return ret;
}
@@ -93,9 +91,7 @@ void configure_accelerator(MachineState *ms, const char *progname)
#elif defined(CONFIG_KVM)
accel = "kvm";
#else
error_report("No accelerator selected and"
" no default accelerator available");
exit(1);
#error "No default accelerator available"
#endif
}
}
@@ -107,6 +103,11 @@ void configure_accelerator(MachineState *ms, const char *progname)
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;

View File

@@ -18,25 +18,24 @@
#include <linux/kvm.h>
#include "qemu-common.h"
#include "qemu/atomic.h"
#include "qemu/option.h"
#include "qemu/config-file.h"
#include "qemu/error-report.h"
#include "qapi/error.h"
#include "hw/hw.h"
#include "hw/pci/msi.h"
#include "hw/pci/msix.h"
#include "hw/s390x/adapter.h"
#include "exec/gdbstub.h"
#include "sysemu/kvm_int.h"
#include "sysemu/runstate.h"
#include "sysemu/cpus.h"
#include "sysemu/sysemu.h"
#include "qemu/bswap.h"
#include "exec/memory.h"
#include "exec/ram_addr.h"
#include "exec/address-spaces.h"
#include "qemu/event_notifier.h"
#include "qemu/main-loop.h"
#include "trace.h"
#include "hw/irq.h"
#include "sysemu/sev.h"
@@ -52,7 +51,7 @@
/* KVM uses PAGE_SIZE in its definition of KVM_COALESCED_MMIO_MAX. We
* need to use the real host PAGE_SIZE, as that's what KVM will use.
*/
#define PAGE_SIZE qemu_real_host_page_size
#define PAGE_SIZE getpagesize()
//#define DEBUG_KVM
@@ -89,11 +88,9 @@ struct KVMState
#ifdef KVM_CAP_SET_GUEST_DEBUG
QTAILQ_HEAD(, kvm_sw_breakpoint) kvm_sw_breakpoints;
#endif
int max_nested_state_len;
int many_ioeventfds;
int intx_set_mask;
bool sync_mmu;
bool manual_dirty_log_protect;
/* The man page (and posix) say ioctl numbers are signed int, but
* they're not. Linux, glibc and *BSD all treat ioctl numbers as
* unsigned, and treating them as signed here can break things */
@@ -113,13 +110,6 @@ struct KVMState
/* memory encryption */
void *memcrypt_handle;
int (*memcrypt_encrypt_data)(void *handle, uint8_t *ptr, uint64_t len);
/* For "info mtree -f" to tell if an MR is registered in KVM */
int nr_as;
struct KVMAs {
KVMMemoryListener *ml;
AddressSpace *as;
} *as;
};
KVMState *kvm_state;
@@ -140,7 +130,6 @@ bool kvm_direct_msi_allowed;
bool kvm_ioeventfd_any_length_allowed;
bool kvm_msi_use_devid;
static bool kvm_immediate_exit;
static hwaddr kvm_max_slot_size = ~0;
static const KVMCapabilityInfo kvm_required_capabilites[] = {
KVM_CAP_INFO(USER_MEMORY),
@@ -149,9 +138,6 @@ static const KVMCapabilityInfo kvm_required_capabilites[] = {
KVM_CAP_LAST_INFO
};
#define kvm_slots_lock(kml) qemu_mutex_lock(&(kml)->slots_lock)
#define kvm_slots_unlock(kml) qemu_mutex_unlock(&(kml)->slots_lock)
int kvm_get_max_memslots(void)
{
KVMState *s = KVM_STATE(current_machine->accelerator);
@@ -179,7 +165,6 @@ int kvm_memcrypt_encrypt_data(uint8_t *ptr, uint64_t len)
return 1;
}
/* Called with KVMMemoryListener.slots_lock held */
static KVMSlot *kvm_get_free_slot(KVMMemoryListener *kml)
{
KVMState *s = kvm_state;
@@ -197,17 +182,10 @@ static KVMSlot *kvm_get_free_slot(KVMMemoryListener *kml)
bool kvm_has_free_slot(MachineState *ms)
{
KVMState *s = KVM_STATE(ms->accelerator);
bool result;
KVMMemoryListener *kml = &s->memory_listener;
kvm_slots_lock(kml);
result = !!kvm_get_free_slot(kml);
kvm_slots_unlock(kml);
return result;
return kvm_get_free_slot(&s->memory_listener);
}
/* Called with KVMMemoryListener.slots_lock held */
static KVMSlot *kvm_alloc_slot(KVMMemoryListener *kml)
{
KVMSlot *slot = kvm_get_free_slot(kml);
@@ -266,21 +244,18 @@ int kvm_physical_memory_addr_from_host(KVMState *s, void *ram,
hwaddr *phys_addr)
{
KVMMemoryListener *kml = &s->memory_listener;
int i, ret = 0;
int i;
kvm_slots_lock(kml);
for (i = 0; i < s->nr_slots; i++) {
KVMSlot *mem = &kml->slots[i];
if (ram >= mem->ram && ram < mem->ram + mem->memory_size) {
*phys_addr = mem->start_addr + (ram - mem->ram);
ret = 1;
break;
return 1;
}
}
kvm_slots_unlock(kml);
return ret;
return 0;
}
static int kvm_set_user_memory_region(KVMMemoryListener *kml, KVMSlot *slot, bool new)
@@ -317,11 +292,6 @@ int kvm_destroy_vcpu(CPUState *cpu)
DPRINTF("kvm_destroy_vcpu\n");
ret = kvm_arch_destroy_vcpu(cpu);
if (ret < 0) {
goto err;
}
mmap_size = kvm_ioctl(s, KVM_GET_VCPU_MMAP_SIZE, 0);
if (mmap_size < 0) {
ret = mmap_size;
@@ -421,7 +391,6 @@ static int kvm_mem_flags(MemoryRegion *mr)
return flags;
}
/* Called with KVMMemoryListener.slots_lock held */
static int kvm_slot_update_flags(KVMMemoryListener *kml, KVMSlot *mem,
MemoryRegion *mr)
{
@@ -438,33 +407,21 @@ static int kvm_slot_update_flags(KVMMemoryListener *kml, KVMSlot *mem,
static int kvm_section_update_flags(KVMMemoryListener *kml,
MemoryRegionSection *section)
{
hwaddr start_addr, size, slot_size;
hwaddr start_addr, size;
KVMSlot *mem;
int ret = 0;
size = kvm_align_section(section, &start_addr);
if (!size) {
return 0;
}
kvm_slots_lock(kml);
while (size && !ret) {
slot_size = MIN(kvm_max_slot_size, size);
mem = kvm_lookup_matching_slot(kml, start_addr, slot_size);
if (!mem) {
/* We don't have a slot if we want to trap every access. */
goto out;
}
ret = kvm_slot_update_flags(kml, mem, section->mr);
start_addr += slot_size;
size -= slot_size;
mem = kvm_lookup_matching_slot(kml, start_addr, size);
if (!mem) {
/* We don't have a slot if we want to trap every access. */
return 0;
}
out:
kvm_slots_unlock(kml);
return ret;
return kvm_slot_update_flags(kml, mem, section->mr);
}
static void kvm_log_start(MemoryListener *listener,
@@ -507,7 +464,7 @@ static int kvm_get_dirty_pages_log_range(MemoryRegionSection *section,
{
ram_addr_t start = section->offset_within_region +
memory_region_get_ram_addr(section->mr);
ram_addr_t pages = int128_get64(section->size) / qemu_real_host_page_size;
ram_addr_t pages = int128_get64(section->size) / getpagesize();
cpu_physical_memory_set_dirty_lebitmap(bitmap, start, pages);
return 0;
@@ -516,15 +473,13 @@ static int kvm_get_dirty_pages_log_range(MemoryRegionSection *section,
#define ALIGN(x, y) (((x)+(y)-1) & ~((y)-1))
/**
* kvm_physical_sync_dirty_bitmap - Sync dirty bitmap from kernel space
* kvm_physical_sync_dirty_bitmap - Grab dirty bitmap from kernel space
* This function updates qemu's dirty bitmap using
* memory_region_set_dirty(). This means all bits are set
* to dirty.
*
* This function will first try to fetch dirty bitmap from the kernel,
* and then updates qemu's dirty bitmap.
*
* NOTE: caller must be with kml->slots_lock held.
*
* @kml: the KVM memory listener object
* @section: the memory section to sync the dirty bitmap with
* @start_add: start of logged region.
* @end_addr: end of logged region.
*/
static int kvm_physical_sync_dirty_bitmap(KVMMemoryListener *kml,
MemoryRegionSection *section)
@@ -533,18 +488,13 @@ static int kvm_physical_sync_dirty_bitmap(KVMMemoryListener *kml,
struct kvm_dirty_log d = {};
KVMSlot *mem;
hwaddr start_addr, size;
hwaddr slot_size, slot_offset = 0;
int ret = 0;
size = kvm_align_section(section, &start_addr);
while (size) {
MemoryRegionSection subsection = *section;
slot_size = MIN(kvm_max_slot_size, size);
mem = kvm_lookup_matching_slot(kml, start_addr, slot_size);
if (size) {
mem = kvm_lookup_matching_slot(kml, start_addr, size);
if (!mem) {
/* We don't have a slot if we want to trap every access. */
goto out;
return 0;
}
/* XXX bad kernel interface alert
@@ -559,203 +509,22 @@ static int kvm_physical_sync_dirty_bitmap(KVMMemoryListener *kml,
* So for now, let's align to 64 instead of HOST_LONG_BITS here, in
* a hope that sizeof(long) won't become >8 any time soon.
*/
if (!mem->dirty_bmap) {
hwaddr bitmap_size = ALIGN(((mem->memory_size) >> TARGET_PAGE_BITS),
/*HOST_LONG_BITS*/ 64) / 8;
/* Allocate on the first log_sync, once and for all */
mem->dirty_bmap = g_malloc0(bitmap_size);
}
size = ALIGN(((mem->memory_size) >> TARGET_PAGE_BITS),
/*HOST_LONG_BITS*/ 64) / 8;
d.dirty_bitmap = g_malloc0(size);
d.dirty_bitmap = mem->dirty_bmap;
d.slot = mem->slot | (kml->as_id << 16);
if (kvm_vm_ioctl(s, KVM_GET_DIRTY_LOG, &d) == -1) {
DPRINTF("ioctl failed %d\n", errno);
ret = -1;
goto out;
g_free(d.dirty_bitmap);
return -1;
}
subsection.offset_within_region += slot_offset;
subsection.size = int128_make64(slot_size);
kvm_get_dirty_pages_log_range(&subsection, d.dirty_bitmap);
slot_offset += slot_size;
start_addr += slot_size;
size -= slot_size;
}
out:
return ret;
}
/* Alignment requirement for KVM_CLEAR_DIRTY_LOG - 64 pages */
#define KVM_CLEAR_LOG_SHIFT 6
#define KVM_CLEAR_LOG_ALIGN (qemu_real_host_page_size << KVM_CLEAR_LOG_SHIFT)
#define KVM_CLEAR_LOG_MASK (-KVM_CLEAR_LOG_ALIGN)
static int kvm_log_clear_one_slot(KVMSlot *mem, int as_id, uint64_t start,
uint64_t size)
{
KVMState *s = kvm_state;
uint64_t end, bmap_start, start_delta, bmap_npages;
struct kvm_clear_dirty_log d;
unsigned long *bmap_clear = NULL, psize = qemu_real_host_page_size;
int ret;
/*
* We need to extend either the start or the size or both to
* satisfy the KVM interface requirement. Firstly, do the start
* page alignment on 64 host pages
*/
bmap_start = start & KVM_CLEAR_LOG_MASK;
start_delta = start - bmap_start;
bmap_start /= psize;
/*
* The kernel interface has restriction on the size too, that either:
*
* (1) the size is 64 host pages aligned (just like the start), or
* (2) the size fills up until the end of the KVM memslot.
*/
bmap_npages = DIV_ROUND_UP(size + start_delta, KVM_CLEAR_LOG_ALIGN)
<< KVM_CLEAR_LOG_SHIFT;
end = mem->memory_size / psize;
if (bmap_npages > end - bmap_start) {
bmap_npages = end - bmap_start;
}
start_delta /= psize;
/*
* Prepare the bitmap to clear dirty bits. Here we must guarantee
* that we won't clear any unknown dirty bits otherwise we might
* accidentally clear some set bits which are not yet synced from
* the kernel into QEMU's bitmap, then we'll lose track of the
* guest modifications upon those pages (which can directly lead
* to guest data loss or panic after migration).
*
* Layout of the KVMSlot.dirty_bmap:
*
* |<-------- bmap_npages -----------..>|
* [1]
* start_delta size
* |----------------|-------------|------------------|------------|
* ^ ^ ^ ^
* | | | |
* start bmap_start (start) end
* of memslot of memslot
*
* [1] bmap_npages can be aligned to either 64 pages or the end of slot
*/
assert(bmap_start % BITS_PER_LONG == 0);
/* We should never do log_clear before log_sync */
assert(mem->dirty_bmap);
if (start_delta) {
/* Slow path - we need to manipulate a temp bitmap */
bmap_clear = bitmap_new(bmap_npages);
bitmap_copy_with_src_offset(bmap_clear, mem->dirty_bmap,
bmap_start, start_delta + size / psize);
/*
* We need to fill the holes at start because that was not
* specified by the caller and we extended the bitmap only for
* 64 pages alignment
*/
bitmap_clear(bmap_clear, 0, start_delta);
d.dirty_bitmap = bmap_clear;
} else {
/* Fast path - start address aligns well with BITS_PER_LONG */
d.dirty_bitmap = mem->dirty_bmap + BIT_WORD(bmap_start);
kvm_get_dirty_pages_log_range(section, d.dirty_bitmap);
g_free(d.dirty_bitmap);
}
d.first_page = bmap_start;
/* It should never overflow. If it happens, say something */
assert(bmap_npages <= UINT32_MAX);
d.num_pages = bmap_npages;
d.slot = mem->slot | (as_id << 16);
if (kvm_vm_ioctl(s, KVM_CLEAR_DIRTY_LOG, &d) == -1) {
ret = -errno;
error_report("%s: KVM_CLEAR_DIRTY_LOG failed, slot=%d, "
"start=0x%"PRIx64", size=0x%"PRIx32", errno=%d",
__func__, d.slot, (uint64_t)d.first_page,
(uint32_t)d.num_pages, ret);
} else {
ret = 0;
trace_kvm_clear_dirty_log(d.slot, d.first_page, d.num_pages);
}
/*
* After we have updated the remote dirty bitmap, we update the
* cached bitmap as well for the memslot, then if another user
* clears the same region we know we shouldn't clear it again on
* the remote otherwise it's data loss as well.
*/
bitmap_clear(mem->dirty_bmap, bmap_start + start_delta,
size / psize);
/* This handles the NULL case well */
g_free(bmap_clear);
return ret;
}
/**
* kvm_physical_log_clear - Clear the kernel's dirty bitmap for range
*
* NOTE: this will be a no-op if we haven't enabled manual dirty log
* protection in the host kernel because in that case this operation
* will be done within log_sync().
*
* @kml: the kvm memory listener
* @section: the memory range to clear dirty bitmap
*/
static int kvm_physical_log_clear(KVMMemoryListener *kml,
MemoryRegionSection *section)
{
KVMState *s = kvm_state;
uint64_t start, size, offset, count;
KVMSlot *mem;
int ret = 0, i;
if (!s->manual_dirty_log_protect) {
/* No need to do explicit clear */
return ret;
}
start = section->offset_within_address_space;
size = int128_get64(section->size);
if (!size) {
/* Nothing more we can do... */
return ret;
}
kvm_slots_lock(kml);
for (i = 0; i < s->nr_slots; i++) {
mem = &kml->slots[i];
/* Discard slots that are empty or do not overlap the section */
if (!mem->memory_size ||
mem->start_addr > start + size - 1 ||
start > mem->start_addr + mem->memory_size - 1) {
continue;
}
if (start >= mem->start_addr) {
/* The slot starts before section or is aligned to it. */
offset = start - mem->start_addr;
count = MIN(mem->memory_size - offset, size);
} else {
/* The slot starts after section. */
offset = 0;
count = MIN(mem->memory_size, size - (mem->start_addr - start));
}
ret = kvm_log_clear_one_slot(mem, kml->as_id, offset, count);
if (ret < 0) {
break;
}
}
kvm_slots_unlock(kml);
return ret;
return 0;
}
static void kvm_coalesce_mmio_region(MemoryListener *listener,
@@ -988,14 +757,6 @@ kvm_check_extension_list(KVMState *s, const KVMCapabilityInfo *list)
return NULL;
}
void kvm_set_max_memslot_size(hwaddr max_slot_size)
{
g_assert(
ROUND_UP(max_slot_size, qemu_real_host_page_size) == max_slot_size
);
kvm_max_slot_size = max_slot_size;
}
static void kvm_set_phys_mem(KVMMemoryListener *kml,
MemoryRegionSection *section, bool add)
{
@@ -1003,7 +764,7 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml,
int err;
MemoryRegion *mr = section->mr;
bool writeable = !mr->readonly && !mr->rom_device;
hwaddr start_addr, size, slot_size;
hwaddr start_addr, size;
void *ram;
if (!memory_region_is_ram(mr)) {
@@ -1025,58 +786,40 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml,
ram = memory_region_get_ram_ptr(mr) + section->offset_within_region +
(start_addr - section->offset_within_address_space);
kvm_slots_lock(kml);
if (!add) {
do {
slot_size = MIN(kvm_max_slot_size, size);
mem = kvm_lookup_matching_slot(kml, start_addr, slot_size);
if (!mem) {
goto out;
}
if (mem->flags & KVM_MEM_LOG_DIRTY_PAGES) {
kvm_physical_sync_dirty_bitmap(kml, section);
}
mem = kvm_lookup_matching_slot(kml, start_addr, size);
if (!mem) {
return;
}
if (mem->flags & KVM_MEM_LOG_DIRTY_PAGES) {
kvm_physical_sync_dirty_bitmap(kml, section);
}
/* unregister the slot */
g_free(mem->dirty_bmap);
mem->dirty_bmap = NULL;
mem->memory_size = 0;
mem->flags = 0;
err = kvm_set_user_memory_region(kml, mem, false);
if (err) {
fprintf(stderr, "%s: error unregistering slot: %s\n",
__func__, strerror(-err));
abort();
}
start_addr += slot_size;
size -= slot_size;
} while (size);
goto out;
/* unregister the slot */
mem->memory_size = 0;
mem->flags = 0;
err = kvm_set_user_memory_region(kml, mem, false);
if (err) {
fprintf(stderr, "%s: error unregistering slot: %s\n",
__func__, strerror(-err));
abort();
}
return;
}
/* register the new slot */
do {
slot_size = MIN(kvm_max_slot_size, size);
mem = kvm_alloc_slot(kml);
mem->memory_size = slot_size;
mem->start_addr = start_addr;
mem->ram = ram;
mem->flags = kvm_mem_flags(mr);
mem = kvm_alloc_slot(kml);
mem->memory_size = size;
mem->start_addr = start_addr;
mem->ram = ram;
mem->flags = kvm_mem_flags(mr);
err = kvm_set_user_memory_region(kml, mem, true);
if (err) {
fprintf(stderr, "%s: error registering slot: %s\n", __func__,
strerror(-err));
abort();
}
start_addr += slot_size;
ram += slot_size;
size -= slot_size;
} while (size);
out:
kvm_slots_unlock(kml);
err = kvm_set_user_memory_region(kml, mem, true);
if (err) {
fprintf(stderr, "%s: error registering slot: %s\n", __func__,
strerror(-err));
abort();
}
}
static void kvm_region_add(MemoryListener *listener,
@@ -1103,30 +846,12 @@ static void kvm_log_sync(MemoryListener *listener,
KVMMemoryListener *kml = container_of(listener, KVMMemoryListener, listener);
int r;
kvm_slots_lock(kml);
r = kvm_physical_sync_dirty_bitmap(kml, section);
kvm_slots_unlock(kml);
if (r < 0) {
abort();
}
}
static void kvm_log_clear(MemoryListener *listener,
MemoryRegionSection *section)
{
KVMMemoryListener *kml = container_of(listener, KVMMemoryListener, listener);
int r;
r = kvm_physical_log_clear(kml, section);
if (r < 0) {
error_report_once("%s: kvm log clear failed: mr=%s "
"offset=%"HWADDR_PRIx" size=%"PRIx64, __func__,
section->mr->name, section->offset_within_region,
int128_get64(section->size));
abort();
}
}
static void kvm_mem_ioeventfd_add(MemoryListener *listener,
MemoryRegionSection *section,
bool match_data, uint64_t data,
@@ -1139,8 +864,8 @@ static void kvm_mem_ioeventfd_add(MemoryListener *listener,
data, true, int128_get64(section->size),
match_data);
if (r < 0) {
fprintf(stderr, "%s: error adding ioeventfd: %s (%d)\n",
__func__, strerror(-r), -r);
fprintf(stderr, "%s: error adding ioeventfd: %s\n",
__func__, strerror(-r));
abort();
}
}
@@ -1157,8 +882,6 @@ static void kvm_mem_ioeventfd_del(MemoryListener *listener,
data, false, int128_get64(section->size),
match_data);
if (r < 0) {
fprintf(stderr, "%s: error deleting ioeventfd: %s (%d)\n",
__func__, strerror(-r), -r);
abort();
}
}
@@ -1175,8 +898,8 @@ static void kvm_io_ioeventfd_add(MemoryListener *listener,
data, true, int128_get64(section->size),
match_data);
if (r < 0) {
fprintf(stderr, "%s: error adding ioeventfd: %s (%d)\n",
__func__, strerror(-r), -r);
fprintf(stderr, "%s: error adding ioeventfd: %s\n",
__func__, strerror(-r));
abort();
}
}
@@ -1194,8 +917,6 @@ static void kvm_io_ioeventfd_del(MemoryListener *listener,
data, false, int128_get64(section->size),
match_data);
if (r < 0) {
fprintf(stderr, "%s: error deleting ioeventfd: %s (%d)\n",
__func__, strerror(-r), -r);
abort();
}
}
@@ -1205,7 +926,6 @@ void kvm_memory_listener_register(KVMState *s, KVMMemoryListener *kml,
{
int i;
qemu_mutex_init(&kml->slots_lock);
kml->slots = g_malloc0(s->nr_slots * sizeof(KVMSlot));
kml->as_id = as_id;
@@ -1218,18 +938,9 @@ void kvm_memory_listener_register(KVMState *s, KVMMemoryListener *kml,
kml->listener.log_start = kvm_log_start;
kml->listener.log_stop = kvm_log_stop;
kml->listener.log_sync = kvm_log_sync;
kml->listener.log_clear = kvm_log_clear;
kml->listener.priority = 10;
memory_listener_register(&kml->listener, as);
for (i = 0; i < s->nr_as; ++i) {
if (!s->as[i].as) {
s->as[i].as = as;
s->as[i].ml = kml;
break;
}
}
}
static MemoryListener kvm_io_listener = {
@@ -1822,8 +1533,8 @@ static int kvm_init(MachineState *ms)
const char *name;
int num;
} num_cpus[] = {
{ "SMP", ms->smp.cpus },
{ "hotpluggable", ms->smp.max_cpus },
{ "SMP", smp_cpus },
{ "hotpluggable", max_cpus },
{ NULL, }
}, *nc = num_cpus;
int soft_vcpus_limit, hard_vcpus_limit;
@@ -1841,7 +1552,7 @@ static int kvm_init(MachineState *ms)
* even with KVM. TARGET_PAGE_SIZE is assumed to be the minimum
* page size for the system though.
*/
assert(TARGET_PAGE_SIZE <= qemu_real_host_page_size);
assert(TARGET_PAGE_SIZE <= getpagesize());
s->sigmask_len = 8;
@@ -1880,15 +1591,9 @@ static int kvm_init(MachineState *ms)
s->nr_slots = 32;
}
s->nr_as = kvm_check_extension(s, KVM_CAP_MULTI_ADDRESS_SPACE);
if (s->nr_as <= 1) {
s->nr_as = 1;
}
s->as = g_new0(struct KVMAs, s->nr_as);
kvm_type = qemu_opt_get(qemu_get_machine_opts(), "kvm-type");
if (mc->kvm_type) {
type = mc->kvm_type(ms, kvm_type);
type = mc->kvm_type(kvm_type);
} else if (kvm_type) {
ret = -EINVAL;
fprintf(stderr, "Invalid argument kvm-type=%s\n", kvm_type);
@@ -1957,17 +1662,6 @@ static int kvm_init(MachineState *ms)
s->coalesced_pio = s->coalesced_mmio &&
kvm_check_extension(s, KVM_CAP_COALESCED_PIO);
s->manual_dirty_log_protect =
kvm_check_extension(s, KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2);
if (s->manual_dirty_log_protect) {
ret = kvm_vm_enable_cap(s, KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2, 0, 1);
if (ret) {
warn_report("Trying to enable KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2 "
"but failed. Falling back to the legacy mode. ");
s->manual_dirty_log_protect = false;
}
}
#ifdef KVM_CAP_VCPU_EVENTS
s->vcpu_events = kvm_check_extension(s, KVM_CAP_VCPU_EVENTS);
#endif
@@ -1979,8 +1673,6 @@ static int kvm_init(MachineState *ms)
s->debugregs = kvm_check_extension(s, KVM_CAP_DEBUGREGS);
#endif
s->max_nested_state_len = kvm_check_extension(s, KVM_CAP_NESTED_STATE);
#ifdef KVM_CAP_IRQ_ROUTING
kvm_direct_msi_allowed = (kvm_check_extension(s, KVM_CAP_SIGNAL_MSI) > 0);
#endif
@@ -2106,7 +1798,7 @@ static int kvm_handle_internal_error(CPUState *cpu, struct kvm_run *run)
if (run->internal.suberror == KVM_INTERNAL_ERROR_EMULATION) {
fprintf(stderr, "emulation failure\n");
if (!kvm_arch_stop_on_emulation_error(cpu)) {
cpu_dump_state(cpu, stderr, CPU_DUMP_CODE);
cpu_dump_state(cpu, stderr, fprintf, CPU_DUMP_CODE);
return EXCP_INTERRUPT;
}
}
@@ -2397,7 +2089,7 @@ int kvm_cpu_exec(CPUState *cpu)
qemu_mutex_lock_iothread();
if (ret < 0) {
cpu_dump_state(cpu, stderr, CPU_DUMP_CODE);
cpu_dump_state(cpu, stderr, fprintf, CPU_DUMP_CODE);
vm_stop(RUN_STATE_INTERNAL_ERROR);
}
@@ -2548,11 +2240,6 @@ int kvm_has_debugregs(void)
return kvm_state->debugregs;
}
int kvm_max_nested_state_length(void)
{
return kvm_state->max_nested_state_len;
}
int kvm_has_many_ioeventfds(void)
{
if (!kvm_enabled()) {
@@ -2580,6 +2267,13 @@ bool kvm_arm_supports_user_irq(void)
return kvm_check_extension(kvm_state, KVM_CAP_ARM_USER_IRQ);
}
/* Whether the KVM_SET_GUEST_DEBUG ioctl supports single stepping */
int kvm_has_guestdbg_singlestep(void)
{
/* return kvm_check_extension(kvm_state, KVM_CAP_GUEST_DEBUG_SSTEP); */
return 0;
}
#ifdef KVM_CAP_SET_GUEST_DEBUG
struct kvm_sw_breakpoint *kvm_find_sw_breakpoint(CPUState *cpu,
target_ulong pc)
@@ -2629,6 +2323,15 @@ int kvm_update_guest_debug(CPUState *cpu, unsigned long reinject_trap)
return data.err;
}
void kvm_set_singlestep(CPUState *cs, int enabled)
{
if (kvm_has_guestdbg_singlestep()) {
kvm_update_guest_debug(cs, 0);
} else {
kvm_arch_set_singlestep(cs, enabled);
}
}
int kvm_insert_breakpoint(CPUState *cpu, target_ulong addr,
target_ulong len, int type)
{
@@ -2905,29 +2608,11 @@ int kvm_get_one_reg(CPUState *cs, uint64_t id, void *target)
return r;
}
static bool kvm_accel_has_memory(MachineState *ms, AddressSpace *as,
hwaddr start_addr, hwaddr size)
{
KVMState *kvm = KVM_STATE(ms->accelerator);
int i;
for (i = 0; i < kvm->nr_as; ++i) {
if (kvm->as[i].as == as && kvm->as[i].ml) {
size = MIN(kvm_max_slot_size, size);
return NULL != kvm_lookup_matching_slot(kvm->as[i].ml,
start_addr, size);
}
}
return false;
}
static void kvm_accel_class_init(ObjectClass *oc, void *data)
{
AccelClass *ac = ACCEL_CLASS(oc);
ac->name = "KVM";
ac->init_machine = kvm_init;
ac->has_memory = kvm_accel_has_memory;
ac->allowed = &kvm_allowed;
}

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_ioctl(int type, void *arg) "type 0x%x, arg %p"
@@ -15,5 +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_pio(int fd, uint16_t addr, uint32_t val, bool assign, uint32_t size, bool datamatch) "fd: %d @0x%x val=0x%x assign: %d size: %d match: %d"
kvm_set_user_memory(uint32_t slot, uint32_t flags, uint64_t guest_phys_addr, uint64_t memory_size, uint64_t userspace_addr, int ret) "Slot#%d flags=0x%x gpa=0x%"PRIx64 " size=0x%"PRIx64 " ua=0x%"PRIx64 " ret=%d"
kvm_clear_dirty_log(uint32_t slot, uint64_t start, uint32_t size) "slot#%"PRId32" start 0x%"PRIx64" size 0x%"PRIx32

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

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

View File

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

View File

@@ -11,6 +11,7 @@
*/
#include "qemu/osdep.h"
#include "qemu-common.h"
#include "cpu.h"
#include "sysemu/kvm.h"
@@ -78,6 +79,10 @@ int kvm_update_guest_debug(CPUState *cpu, unsigned long reinject_trap)
return -ENOSYS;
}
void kvm_set_singlestep(CPUState *cs, int enabled)
{
}
int kvm_insert_breakpoint(CPUState *cpu, target_ulong addr,
target_ulong len, int type)
{

View File

@@ -11,8 +11,10 @@
*/
#include "qemu/osdep.h"
#include "qemu-common.h"
#include "cpu.h"
#include "tcg/tcg.h"
#include "exec/cpu-common.h"
#include "exec/exec-all.h"
void tb_flush(CPUState *cpu)

View File

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

View File

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

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/>.
*/
#include "qemu/plugin.h"
#include "trace/mem.h"
#if DATA_SIZE == 16
@@ -60,6 +59,26 @@
# define ABI_TYPE uint32_t
#endif
#define ATOMIC_TRACE_RMW do { \
uint8_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, false); \
\
trace_guest_mem_before_exec(ENV_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
the ATOMIC_NAME macro, and redefined below. */
#if DATA_SIZE == 1
@@ -79,17 +98,14 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr,
ATOMIC_MMU_DECLS;
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
DATA_TYPE ret;
uint16_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, false,
ATOMIC_MMU_IDX);
atomic_trace_rmw_pre(env, addr, info);
ATOMIC_TRACE_RMW;
#if DATA_SIZE == 16
ret = atomic16_cmpxchg(haddr, cmpv, newv);
#else
ret = atomic_cmpxchg__nocheck(haddr, cmpv, newv);
#endif
ATOMIC_MMU_CLEANUP;
atomic_trace_rmw_post(env, addr, info);
return ret;
}
@@ -99,13 +115,10 @@ ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS)
{
ATOMIC_MMU_DECLS;
DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP;
uint16_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, false,
ATOMIC_MMU_IDX);
atomic_trace_ld_pre(env, addr, info);
ATOMIC_TRACE_LD;
val = atomic16_read(haddr);
ATOMIC_MMU_CLEANUP;
atomic_trace_ld_post(env, addr, info);
return val;
}
@@ -114,13 +127,10 @@ void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr,
{
ATOMIC_MMU_DECLS;
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
uint16_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, true,
ATOMIC_MMU_IDX);
atomic_trace_st_pre(env, addr, info);
ATOMIC_TRACE_ST;
atomic16_set(haddr, val);
ATOMIC_MMU_CLEANUP;
atomic_trace_st_post(env, addr, info);
}
#endif
#else
@@ -130,31 +140,24 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr,
ATOMIC_MMU_DECLS;
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
DATA_TYPE ret;
uint16_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, false,
ATOMIC_MMU_IDX);
atomic_trace_rmw_pre(env, addr, info);
ATOMIC_TRACE_RMW;
ret = atomic_xchg__nocheck(haddr, val);
ATOMIC_MMU_CLEANUP;
atomic_trace_rmw_post(env, addr, info);
return ret;
}
#define GEN_ATOMIC_HELPER(X) \
ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \
ABI_TYPE val EXTRA_ARGS) \
ABI_TYPE val EXTRA_ARGS) \
{ \
ATOMIC_MMU_DECLS; \
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \
DATA_TYPE ret; \
uint16_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, \
false, \
ATOMIC_MMU_IDX); \
\
atomic_trace_rmw_pre(env, addr, info); \
ATOMIC_TRACE_RMW; \
ret = atomic_##X(haddr, val); \
ATOMIC_MMU_CLEANUP; \
atomic_trace_rmw_post(env, addr, info); \
return ret; \
}
@@ -183,11 +186,8 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \
ATOMIC_MMU_DECLS; \
XDATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \
XDATA_TYPE cmp, old, new, val = xval; \
uint16_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, \
false, \
ATOMIC_MMU_IDX); \
\
atomic_trace_rmw_pre(env, addr, info); \
ATOMIC_TRACE_RMW; \
smp_mb(); \
cmp = atomic_read__nocheck(haddr); \
do { \
@@ -195,7 +195,6 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \
cmp = atomic_cmpxchg__nocheck(haddr, old, new); \
} while (cmp != old); \
ATOMIC_MMU_CLEANUP; \
atomic_trace_rmw_post(env, addr, info); \
return RET; \
}
@@ -233,18 +232,14 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr,
ATOMIC_MMU_DECLS;
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
DATA_TYPE ret;
uint16_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT,
false,
ATOMIC_MMU_IDX);
atomic_trace_rmw_pre(env, addr, info);
ATOMIC_TRACE_RMW;
#if DATA_SIZE == 16
ret = atomic16_cmpxchg(haddr, BSWAP(cmpv), BSWAP(newv));
#else
ret = atomic_cmpxchg__nocheck(haddr, BSWAP(cmpv), BSWAP(newv));
#endif
ATOMIC_MMU_CLEANUP;
atomic_trace_rmw_post(env, addr, info);
return BSWAP(ret);
}
@@ -254,14 +249,10 @@ ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS)
{
ATOMIC_MMU_DECLS;
DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP;
uint16_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT,
false,
ATOMIC_MMU_IDX);
atomic_trace_ld_pre(env, addr, info);
ATOMIC_TRACE_LD;
val = atomic16_read(haddr);
ATOMIC_MMU_CLEANUP;
atomic_trace_ld_post(env, addr, info);
return BSWAP(val);
}
@@ -270,16 +261,11 @@ void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr,
{
ATOMIC_MMU_DECLS;
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
uint16_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT,
true,
ATOMIC_MMU_IDX);
val = BSWAP(val);
atomic_trace_st_pre(env, addr, info);
ATOMIC_TRACE_ST;
val = BSWAP(val);
atomic16_set(haddr, val);
ATOMIC_MMU_CLEANUP;
atomic_trace_st_post(env, addr, info);
}
#endif
#else
@@ -289,32 +275,24 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr,
ATOMIC_MMU_DECLS;
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
ABI_TYPE ret;
uint16_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT,
false,
ATOMIC_MMU_IDX);
atomic_trace_rmw_pre(env, addr, info);
ATOMIC_TRACE_RMW;
ret = atomic_xchg__nocheck(haddr, BSWAP(val));
ATOMIC_MMU_CLEANUP;
atomic_trace_rmw_post(env, addr, info);
return BSWAP(ret);
}
#define GEN_ATOMIC_HELPER(X) \
ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \
ABI_TYPE val EXTRA_ARGS) \
ABI_TYPE val EXTRA_ARGS) \
{ \
ATOMIC_MMU_DECLS; \
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \
DATA_TYPE ret; \
uint16_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, \
false, \
ATOMIC_MMU_IDX); \
\
atomic_trace_rmw_pre(env, addr, info); \
ATOMIC_TRACE_RMW; \
ret = atomic_##X(haddr, BSWAP(val)); \
ATOMIC_MMU_CLEANUP; \
atomic_trace_rmw_post(env, addr, info); \
return BSWAP(ret); \
}
@@ -341,11 +319,8 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \
ATOMIC_MMU_DECLS; \
XDATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \
XDATA_TYPE ldo, ldn, old, new, val = xval; \
uint16_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, \
false, \
ATOMIC_MMU_IDX); \
\
atomic_trace_rmw_pre(env, addr, info); \
ATOMIC_TRACE_RMW; \
smp_mb(); \
ldn = atomic_read__nocheck(haddr); \
do { \
@@ -353,7 +328,6 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \
ldn = atomic_cmpxchg__nocheck(haddr, ldo, BSWAP(new)); \
} while (ldo != ldn); \
ATOMIC_MMU_CLEANUP; \
atomic_trace_rmw_post(env, addr, info); \
return RET; \
}
@@ -381,6 +355,10 @@ GEN_ATOMIC_HELPER_FN(add_fetch, ADD, DATA_TYPE, new)
#undef MEND
#endif /* DATA_SIZE > 1 */
#undef ATOMIC_TRACE_ST
#undef ATOMIC_TRACE_LD
#undef ATOMIC_TRACE_RMW
#undef BSWAP
#undef ABI_TYPE
#undef DATA_TYPE

View File

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

View File

@@ -16,9 +16,7 @@
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#include "qemu/osdep.h"
#include "qemu-common.h"
#include "cpu.h"
#include "trace.h"
#include "disas/disas.h"
@@ -56,7 +54,7 @@ typedef struct SyncClocks {
#define MAX_DELAY_PRINT_RATE 2000000000LL
#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;
@@ -64,7 +62,7 @@ static void align_clocks(SyncClocks *sc, CPUState *cpu)
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->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) {
return;
}
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->last_cpu_icount
= cpu->icount_extra + cpu_neg(cpu)->icount_decr.u16.low;
sc->last_cpu_icount = cpu->icount_extra + cpu->icount_decr.u16.low;
if (sc->diff_clk < max_delay) {
max_delay = sc->diff_clk;
}
@@ -169,6 +167,7 @@ static inline tcg_target_ulong cpu_tb_exec(CPUState *cpu, TranslationBlock *itb)
}
#endif /* DEBUG_DISAS */
cpu->can_do_io = !use_icount;
ret = tcg_qemu_tb_exec(env, tb_ptr);
cpu->can_do_io = 1;
last_tb = (TranslationBlock *)(ret & ~TB_EXIT_MASK);
@@ -238,6 +237,8 @@ void cpu_exec_step_atomic(CPUState *cpu)
uint32_t flags;
uint32_t cflags = 1;
uint32_t cf_mask = cflags & CF_HASH_MASK;
/* volatile because we modify it between setjmp and longjmp */
volatile bool in_exclusive_region = false;
if (sigsetjmp(cpu->jmp_env, 0) == 0) {
tb = tb_lookup__cpu_state(cpu, &pc, &cs_base, &flags, cf_mask);
@@ -251,6 +252,7 @@ void cpu_exec_step_atomic(CPUState *cpu)
/* Since we got here, we know that parallel_cpus must be true. */
parallel_cpus = false;
in_exclusive_region = true;
cc->cpu_exec_enter(cpu);
/* execute the generated code */
trace_exec_tb(tb, pc);
@@ -268,10 +270,9 @@ void cpu_exec_step_atomic(CPUState *cpu)
qemu_mutex_unlock_iothread();
}
assert_no_pages_locked();
qemu_plugin_disable_mem_helpers(cpu);
}
if (cpu_in_exclusive_context(cpu)) {
if (in_exclusive_region) {
/* We might longjump out of either the codegen or the
* execution, so must make sure we only end the exclusive
* region if we started it.
@@ -466,7 +467,7 @@ static inline bool cpu_handle_exception(CPUState *cpu, int *ret)
if (cpu->exception_index < 0) {
#ifndef CONFIG_USER_ONLY
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 */
cpu_exec_nocache(cpu, 1, tb_find(cpu, NULL, 0, curr_cflags()), true);
}
@@ -524,7 +525,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
* Ensure zeroing happens before reading cpu->exit_request or
* cpu->interrupt_request (see also smp_wmb in cpu_exit())
*/
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))) {
int interrupt_request;
@@ -595,9 +596,8 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
}
/* Finally, check if we need to exit to the main loop. */
if (unlikely(atomic_read(&cpu->exit_request))
|| (use_icount
&& cpu_neg(cpu)->icount_decr.u16.low + cpu->icount_extra == 0)) {
if (unlikely(atomic_read(&cpu->exit_request)
|| (use_icount && cpu->icount_decr.u16.low + cpu->icount_extra == 0))) {
atomic_set(&cpu->exit_request, 0);
if (cpu->exception_index == -1) {
cpu->exception_index = EXCP_INTERRUPT;
@@ -624,7 +624,7 @@ static inline void cpu_loop_exec_tb(CPUState *cpu, TranslationBlock *tb,
}
*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) {
/* Something asked us to stop executing chained TBs; just
* continue round the main loop. Whatever requested the exit
@@ -643,7 +643,7 @@ static inline void cpu_loop_exec_tb(CPUState *cpu, TranslationBlock *tb,
cpu_update_icount(cpu);
/* Refill decrementer and continue execution. */
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;
if (!cpu->icount_extra) {
/* Execute any remaining instructions, then let the main loop
@@ -702,8 +702,6 @@ int cpu_exec(CPUState *cpu)
if (qemu_mutex_iothread_locked()) {
qemu_mutex_unlock_iothread();
}
qemu_plugin_disable_mem_helpers(cpu);
assert_no_pages_locked();
}

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,14 +25,16 @@
#include "qemu/osdep.h"
#include "sysemu/accel.h"
#include "sysemu/tcg.h"
#include "sysemu/sysemu.h"
#include "qom/object.h"
#include "cpu.h"
#include "qemu-common.h"
#include "qom/cpu.h"
#include "sysemu/cpus.h"
#include "qemu/main-loop.h"
unsigned long tcg_tb_size;
#ifndef CONFIG_USER_ONLY
/* mask must never be zero, except for A20 change call */
static void tcg_handle_interrupt(CPUState *cpu, int mask)
{
@@ -49,7 +51,7 @@ static void tcg_handle_interrupt(CPUState *cpu, int mask)
if (!qemu_cpu_is_self(cpu)) {
qemu_cpu_kick(cpu);
} else {
atomic_set(&cpu_neg(cpu)->icount_decr.u16.high, -1);
atomic_set(&cpu->icount_decr.u16.high, -1);
if (use_icount &&
!cpu->can_do_io
&& (mask & ~old_mask) != 0) {
@@ -57,6 +59,7 @@ static void tcg_handle_interrupt(CPUState *cpu, int mask)
}
}
}
#endif
static int tcg_init(MachineState *ms)
{

View File

@@ -398,54 +398,6 @@ void HELPER(gvec_neg64)(void *d, void *a, uint32_t desc)
clear_high(d, oprsz, desc);
}
void HELPER(gvec_abs8)(void *d, void *a, uint32_t desc)
{
intptr_t oprsz = simd_oprsz(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(int8_t)) {
int8_t aa = *(int8_t *)(a + i);
*(int8_t *)(d + i) = aa < 0 ? -aa : aa;
}
clear_high(d, oprsz, desc);
}
void HELPER(gvec_abs16)(void *d, void *a, uint32_t desc)
{
intptr_t oprsz = simd_oprsz(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(int16_t)) {
int16_t aa = *(int16_t *)(a + i);
*(int16_t *)(d + i) = aa < 0 ? -aa : aa;
}
clear_high(d, oprsz, desc);
}
void HELPER(gvec_abs32)(void *d, void *a, uint32_t desc)
{
intptr_t oprsz = simd_oprsz(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(int32_t)) {
int32_t aa = *(int32_t *)(a + i);
*(int32_t *)(d + i) = aa < 0 ? -aa : aa;
}
clear_high(d, oprsz, desc);
}
void HELPER(gvec_abs64)(void *d, void *a, uint32_t desc)
{
intptr_t oprsz = simd_oprsz(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(int64_t)) {
int64_t aa = *(int64_t *)(a + i);
*(int64_t *)(d + i) = aa < 0 ? -aa : aa;
}
clear_high(d, oprsz, desc);
}
void HELPER(gvec_mov)(void *d, void *a, uint32_t desc)
{
intptr_t oprsz = simd_oprsz(desc);
@@ -773,150 +725,6 @@ void HELPER(gvec_sar64i)(void *d, void *a, uint32_t desc)
clear_high(d, oprsz, desc);
}
void HELPER(gvec_shl8v)(void *d, void *a, void *b, uint32_t desc)
{
intptr_t oprsz = simd_oprsz(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(uint8_t)) {
uint8_t sh = *(uint8_t *)(b + i) & 7;
*(uint8_t *)(d + i) = *(uint8_t *)(a + i) << sh;
}
clear_high(d, oprsz, desc);
}
void HELPER(gvec_shl16v)(void *d, void *a, void *b, uint32_t desc)
{
intptr_t oprsz = simd_oprsz(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(uint16_t)) {
uint8_t sh = *(uint16_t *)(b + i) & 15;
*(uint16_t *)(d + i) = *(uint16_t *)(a + i) << sh;
}
clear_high(d, oprsz, desc);
}
void HELPER(gvec_shl32v)(void *d, void *a, void *b, uint32_t desc)
{
intptr_t oprsz = simd_oprsz(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(uint32_t)) {
uint8_t sh = *(uint32_t *)(b + i) & 31;
*(uint32_t *)(d + i) = *(uint32_t *)(a + i) << sh;
}
clear_high(d, oprsz, desc);
}
void HELPER(gvec_shl64v)(void *d, void *a, void *b, uint32_t desc)
{
intptr_t oprsz = simd_oprsz(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
uint8_t sh = *(uint64_t *)(b + i) & 63;
*(uint64_t *)(d + i) = *(uint64_t *)(a + i) << sh;
}
clear_high(d, oprsz, desc);
}
void HELPER(gvec_shr8v)(void *d, void *a, void *b, uint32_t desc)
{
intptr_t oprsz = simd_oprsz(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(uint8_t)) {
uint8_t sh = *(uint8_t *)(b + i) & 7;
*(uint8_t *)(d + i) = *(uint8_t *)(a + i) >> sh;
}
clear_high(d, oprsz, desc);
}
void HELPER(gvec_shr16v)(void *d, void *a, void *b, uint32_t desc)
{
intptr_t oprsz = simd_oprsz(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(uint16_t)) {
uint8_t sh = *(uint16_t *)(b + i) & 15;
*(uint16_t *)(d + i) = *(uint16_t *)(a + i) >> sh;
}
clear_high(d, oprsz, desc);
}
void HELPER(gvec_shr32v)(void *d, void *a, void *b, uint32_t desc)
{
intptr_t oprsz = simd_oprsz(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(uint32_t)) {
uint8_t sh = *(uint32_t *)(b + i) & 31;
*(uint32_t *)(d + i) = *(uint32_t *)(a + i) >> sh;
}
clear_high(d, oprsz, desc);
}
void HELPER(gvec_shr64v)(void *d, void *a, void *b, uint32_t desc)
{
intptr_t oprsz = simd_oprsz(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
uint8_t sh = *(uint64_t *)(b + i) & 63;
*(uint64_t *)(d + i) = *(uint64_t *)(a + i) >> sh;
}
clear_high(d, oprsz, desc);
}
void HELPER(gvec_sar8v)(void *d, void *a, void *b, uint32_t desc)
{
intptr_t oprsz = simd_oprsz(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(int8_t)) {
uint8_t sh = *(uint8_t *)(b + i) & 7;
*(int8_t *)(d + i) = *(int8_t *)(a + i) >> sh;
}
clear_high(d, oprsz, desc);
}
void HELPER(gvec_sar16v)(void *d, void *a, void *b, uint32_t desc)
{
intptr_t oprsz = simd_oprsz(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(int16_t)) {
uint8_t sh = *(uint16_t *)(b + i) & 15;
*(int16_t *)(d + i) = *(int16_t *)(a + i) >> sh;
}
clear_high(d, oprsz, desc);
}
void HELPER(gvec_sar32v)(void *d, void *a, void *b, uint32_t desc)
{
intptr_t oprsz = simd_oprsz(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(int32_t)) {
uint8_t sh = *(uint32_t *)(b + i) & 31;
*(int32_t *)(d + i) = *(int32_t *)(a + i) >> sh;
}
clear_high(d, oprsz, desc);
}
void HELPER(gvec_sar64v)(void *d, void *a, void *b, uint32_t desc)
{
intptr_t oprsz = simd_oprsz(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(int64_t)) {
uint8_t sh = *(uint64_t *)(b + i) & 63;
*(int64_t *)(d + i) = *(int64_t *)(a + i) >> sh;
}
clear_high(d, oprsz, desc);
}
/* If vectors are enabled, the compiler fills in -1 for true.
Otherwise, we must take care of this by hand. */
#ifdef CONFIG_VECTOR16
@@ -1444,17 +1252,3 @@ void HELPER(gvec_umax64)(void *d, void *a, void *b, uint32_t desc)
}
clear_high(d, oprsz, desc);
}
void HELPER(gvec_bitsel)(void *d, void *a, void *b, void *c, uint32_t desc)
{
intptr_t oprsz = simd_oprsz(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(vec64)) {
vec64 aa = *(vec64 *)(a + i);
vec64 bb = *(vec64 *)(b + i);
vec64 cc = *(vec64 *)(c + i);
*(vec64 *)(d + i) = (bb & aa) | (cc & ~aa);
}
clear_high(d, oprsz, desc);
}

View File

@@ -146,7 +146,7 @@ uint64_t HELPER(ctpop_i64)(uint64_t arg)
void *HELPER(lookup_tb_ptr)(CPUArchState *env)
{
CPUState *cpu = env_cpu(env);
CPUState *cpu = ENV_GET_CPU(env);
TranslationBlock *tb;
target_ulong cs_base, pc;
uint32_t flags;
@@ -165,5 +165,5 @@ void *HELPER(lookup_tb_ptr)(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_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_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)
@@ -259,21 +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_sar64i, 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_eq8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_eq16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_eq32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
@@ -303,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_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_5(gvec_bitsel, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)

View File

@@ -1,4 +1,4 @@
# See docs/devel/tracing.txt for syntax documentation.
# Trace events for debugging and performance instrumentation
# TCG related tracing (mostly disabled by default)
# cpu-exec.c

View File

@@ -16,10 +16,9 @@
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#include "qemu/osdep.h"
#include "qemu-common.h"
#include "qemu-common.h"
#define NO_CPU_IO_DEFS
#include "cpu.h"
#include "trace.h"
@@ -51,12 +50,10 @@
#include "translate-all.h"
#include "qemu/bitmap.h"
#include "qemu/error-report.h"
#include "qemu/qemu-print.h"
#include "qemu/timer.h"
#include "qemu/main-loop.h"
#include "exec/log.h"
#include "sysemu/cpus.h"
#include "sysemu/tcg.h"
/* #define DEBUG_TB_INVALIDATE */
/* #define DEBUG_TB_FLUSH */
@@ -366,7 +363,7 @@ static int cpu_restore_state_from_tb(CPUState *cpu, TranslationBlock *tb,
assert(use_icount);
/* Reset the cycle counter to the start of the block
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);
@@ -1156,6 +1153,23 @@ void tcg_exec_init(unsigned long tb_size)
#endif
}
/*
* Allocate a new translation block. Flush the translation buffer if
* too many translation blocks or too much generated code.
*/
static TranslationBlock *tb_alloc(target_ulong pc)
{
TranslationBlock *tb;
assert_memory_lock();
tb = tcg_tb_alloc(tcg_ctx);
if (unlikely(tb == NULL)) {
return NULL;
}
return tb;
}
/* call with @p->lock held */
static inline void invalidate_page_bitmap(PageDesc *p)
{
@@ -1214,8 +1228,6 @@ static gboolean tb_host_size_iter(gpointer key, gpointer value, gpointer data)
/* flush all the translation blocks */
static void do_tb_flush(CPUState *cpu, run_on_cpu_data tb_flush_count)
{
bool did_flush = false;
mmap_lock();
/* If it is already been done on request of another CPU,
* just retry.
@@ -1223,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) {
goto done;
}
did_flush = true;
if (DEBUG_TB_FLUSH_GATE) {
size_t nb_tbs = tcg_nb_tbs();
@@ -1248,22 +1259,14 @@ static void do_tb_flush(CPUState *cpu, run_on_cpu_data tb_flush_count)
done:
mmap_unlock();
if (did_flush) {
qemu_plugin_flush_cb();
}
}
void tb_flush(CPUState *cpu)
{
if (tcg_enabled()) {
unsigned tb_flush_count = atomic_mb_read(&tb_ctx.tb_flush_count);
if (cpu_in_exclusive_context(cpu)) {
do_tb_flush(cpu, RUN_ON_CPU_HOST_INT(tb_flush_count));
} else {
async_safe_run_on_cpu(cpu, do_tb_flush,
RUN_ON_CPU_HOST_INT(tb_flush_count));
}
async_safe_run_on_cpu(cpu, do_tb_flush,
RUN_ON_CPU_HOST_INT(tb_flush_count));
}
}
@@ -1670,12 +1673,11 @@ TranslationBlock *tb_gen_code(CPUState *cpu,
tb_page_addr_t phys_pc, phys_page2;
target_ulong virt_page2;
tcg_insn_unit *gen_code_buf;
int gen_code_size, search_size, max_insns;
int gen_code_size, search_size;
#ifdef CONFIG_PROFILER
TCGProfile *prof = &tcg_ctx->prof;
int64_t ti;
#endif
assert_memory_lock();
phys_pc = get_page_addr_code(env, pc);
@@ -1689,19 +1691,8 @@ TranslationBlock *tb_gen_code(CPUState *cpu,
cflags &= ~CF_CLUSTER_MASK;
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:
tb = tcg_tb_alloc(tcg_ctx);
tb = tb_alloc(pc);
if (unlikely(!tb)) {
/* flush must be done */
tb_flush(cpu);
@@ -1717,10 +1708,8 @@ TranslationBlock *tb_gen_code(CPUState *cpu,
tb->cs_base = cs_base;
tb->flags = flags;
tb->cflags = cflags;
tb->orig_tb = NULL;
tb->trace_vcpu_dstate = *cpu->trace_dstate;
tcg_ctx->tb_cflags = cflags;
tb_overflow:
#ifdef CONFIG_PROFILER
/* includes aborted translations because of exceptions */
@@ -1730,8 +1719,8 @@ TranslationBlock *tb_gen_code(CPUState *cpu,
tcg_func_start(tcg_ctx);
tcg_ctx->cpu = env_cpu(env);
gen_intermediate_code(cpu, tb, max_insns);
tcg_ctx->cpu = ENV_GET_CPU(env);
gen_intermediate_code(cpu, tb);
tcg_ctx->cpu = NULL;
trace_translate_block(tb, tb->pc, tb->tc.ptr);
@@ -1754,39 +1743,14 @@ TranslationBlock *tb_gen_code(CPUState *cpu,
ti = profile_getclock();
#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);
if (unlikely(gen_code_size < 0)) {
switch (gen_code_size) {
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();
}
goto buffer_overflow;
}
search_size = encode_search(tb, (void *)gen_code_buf + gen_code_size);
if (unlikely(search_size < 0)) {
@@ -1885,7 +1849,7 @@ static void
tb_invalidate_phys_page_range__locked(struct page_collection *pages,
PageDesc *p, tb_page_addr_t start,
tb_page_addr_t end,
uintptr_t retaddr)
int is_cpu_write_access)
{
TranslationBlock *tb;
tb_page_addr_t tb_start, tb_end;
@@ -1893,9 +1857,9 @@ tb_invalidate_phys_page_range__locked(struct page_collection *pages,
#ifdef TARGET_HAS_PRECISE_SMC
CPUState *cpu = current_cpu;
CPUArchState *env = NULL;
bool current_tb_not_found = retaddr != 0;
bool current_tb_modified = false;
int current_tb_not_found = is_cpu_write_access;
TranslationBlock *current_tb = NULL;
int current_tb_modified = 0;
target_ulong current_pc = 0;
target_ulong current_cs_base = 0;
uint32_t current_flags = 0;
@@ -1927,21 +1891,24 @@ tb_invalidate_phys_page_range__locked(struct page_collection *pages,
if (!(tb_end <= start || tb_start >= end)) {
#ifdef TARGET_HAS_PRECISE_SMC
if (current_tb_not_found) {
current_tb_not_found = false;
/* now we have a real cpu fault */
current_tb = tcg_tb_lookup(retaddr);
current_tb_not_found = 0;
current_tb = NULL;
if (cpu->mem_io_pc) {
/* now we have a real cpu fault */
current_tb = tcg_tb_lookup(cpu->mem_io_pc);
}
}
if (current_tb == tb &&
(tb_cflags(current_tb) & CF_COUNT_MASK) != 1) {
/*
* If we are modifying the current TB, we must stop
* its execution. We could be more precise by checking
* that the modification is after the current PC, but it
* would require a specialized function to partially
* restore the CPU state.
*/
current_tb_modified = true;
cpu_restore_state_from_tb(cpu, current_tb, retaddr, true);
/* If we are modifying the current TB, we must stop
its execution. We could be more precise by checking
that the modification is after the current PC, but it
would require a specialized function to partially
restore the CPU state */
current_tb_modified = 1;
cpu_restore_state_from_tb(cpu, current_tb,
cpu->mem_io_pc, true);
cpu_get_tb_cpu_state(env, &current_pc, &current_cs_base,
&current_flags);
}
@@ -1976,7 +1943,8 @@ tb_invalidate_phys_page_range__locked(struct page_collection *pages,
*
* Called with mmap_lock held for user-mode emulation
*/
void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end)
void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end,
int is_cpu_write_access)
{
struct page_collection *pages;
PageDesc *p;
@@ -1988,7 +1956,8 @@ void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end)
return;
}
pages = page_collection_lock(start, end);
tb_invalidate_phys_page_range__locked(pages, p, start, end, 0);
tb_invalidate_phys_page_range__locked(pages, p, start, end,
is_cpu_write_access);
page_collection_unlock(pages);
}
@@ -2035,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.
*/
void tb_invalidate_phys_page_fast(struct page_collection *pages,
tb_page_addr_t start, int len,
uintptr_t retaddr)
tb_page_addr_t start, int len)
{
PageDesc *p;
@@ -2063,8 +2031,7 @@ void tb_invalidate_phys_page_fast(struct page_collection *pages,
}
} else {
do_invalidate:
tb_invalidate_phys_page_range__locked(pages, p, start, start + len,
retaddr);
tb_invalidate_phys_page_range__locked(pages, p, start, start + len, 1);
}
}
#else
@@ -2138,16 +2105,16 @@ static bool tb_invalidate_phys_page(tb_page_addr_t addr, uintptr_t pc)
#endif
/* user-mode: call with mmap_lock held */
void tb_check_watchpoint(CPUState *cpu, uintptr_t retaddr)
void tb_check_watchpoint(CPUState *cpu)
{
TranslationBlock *tb;
assert_memory_lock();
tb = tcg_tb_lookup(retaddr);
tb = tcg_tb_lookup(cpu->mem_io_pc);
if (tb) {
/* We can use retranslation to find the PC. */
cpu_restore_state_from_tb(cpu, tb, retaddr, true);
cpu_restore_state_from_tb(cpu, tb, cpu->mem_io_pc, true);
tb_phys_invalidate(tb, -1);
} else {
/* The exception probably happened in a helper. The CPU state should
@@ -2195,7 +2162,7 @@ void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr)
if ((env->hflags & MIPS_HFLAG_BMASK) != 0
&& env->active_tc.PC != tb->pc) {
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;
n = 2;
}
@@ -2203,7 +2170,7 @@ void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr)
if ((env->flags & ((DELAY_SLOT | DELAY_SLOT_CONDITIONAL))) != 0
&& env->pc != tb->pc) {
env->pc -= 2;
cpu_neg(cpu)->icount_decr.u16.low++;
cpu->icount_decr.u16.low++;
env->flags &= ~(DELAY_SLOT | DELAY_SLOT_CONDITIONAL);
n = 2;
}
@@ -2247,7 +2214,8 @@ void tb_flush_jmp_cache(CPUState *cpu, target_ulong 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;
size_t hgram_bins;
@@ -2256,7 +2224,7 @@ static void print_qht_statistics(struct qht_stats hst)
if (!hst.head_buckets) {
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,
(double)hst.used_head_buckets / hst.head_buckets * 100);
@@ -2266,7 +2234,7 @@ static void print_qht_statistics(struct qht_stats hst)
hgram_opts |= QDIST_PR_NODECIMAL;
}
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);
g_free(hgram);
@@ -2279,7 +2247,7 @@ static void print_qht_statistics(struct qht_stats hst)
hgram_opts |= QDIST_PR_NODECIMAL | QDIST_PR_NOBINRANGE;
}
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);
g_free(hgram);
}
@@ -2317,7 +2285,7 @@ static gboolean tb_tree_stats_iter(gpointer key, gpointer value, gpointer data)
return false;
}
void dump_exec_info(void)
void dump_exec_info(FILE *f, fprintf_function cpu_fprintf)
{
struct tb_tree_stats tst = {};
struct qht_stats hst;
@@ -2326,49 +2294,48 @@ void dump_exec_info(void)
tcg_tb_foreach(tb_tree_stats_iter, &tst);
nb_tbs = tst.nb_tbs;
/* 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;
* otherwise users might think "-tb-size" is not honoured.
* 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());
qemu_printf("TB count %zu\n", nb_tbs);
qemu_printf("TB avg target size %zu max=%zu bytes\n",
cpu_fprintf(f, "TB count %zu\n", nb_tbs);
cpu_fprintf(f, "TB avg target size %zu max=%zu bytes\n",
nb_tbs ? tst.target_size / nb_tbs : 0,
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,
tst.target_size ? (double)tst.host_size / tst.target_size : 0);
qemu_printf("cross page TB count %zu (%zu%%)\n", tst.cross_page,
nb_tbs ? (tst.cross_page * 100) / nb_tbs : 0);
qemu_printf("direct jump count %zu (%zu%%) (2 jumps=%zu %zu%%)\n",
cpu_fprintf(f, "cross page TB count %zu (%zu%%)\n", tst.cross_page,
nb_tbs ? (tst.cross_page * 100) / nb_tbs : 0);
cpu_fprintf(f, "direct jump count %zu (%zu%%) (2 jumps=%zu %zu%%)\n",
tst.direct_jmp_count,
nb_tbs ? (tst.direct_jmp_count * 100) / nb_tbs : 0,
tst.direct_jmp2_count,
nb_tbs ? (tst.direct_jmp2_count * 100) / nb_tbs : 0);
qht_statistics_init(&tb_ctx.htable, &hst);
print_qht_statistics(hst);
print_qht_statistics(f, cpu_fprintf, hst);
qht_statistics_destroy(&hst);
qemu_printf("\nStatistics:\n");
qemu_printf("TB flush count %u\n",
cpu_fprintf(f, "\nStatistics:\n");
cpu_fprintf(f, "TB flush count %u\n",
atomic_read(&tb_ctx.tb_flush_count));
qemu_printf("TB invalidate count %zu\n",
tcg_tb_phys_invalidate_count());
cpu_fprintf(f, "TB invalidate count %zu\n", tcg_tb_phys_invalidate_count());
tlb_flush_counts(&flush_full, &flush_part, &flush_elide);
qemu_printf("TLB full flushes %zu\n", flush_full);
qemu_printf("TLB partial flushes %zu\n", flush_part);
qemu_printf("TLB elided flushes %zu\n", flush_elide);
tcg_dump_info();
cpu_fprintf(f, "TLB full flushes %zu\n", flush_full);
cpu_fprintf(f, "TLB partial flushes %zu\n", flush_part);
cpu_fprintf(f, "TLB elided flushes %zu\n", flush_elide);
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 */
@@ -2377,7 +2344,7 @@ void cpu_interrupt(CPUState *cpu, int mask)
{
g_assert(qemu_mutex_iothread_locked());
cpu->interrupt_request |= mask;
atomic_set(&cpu_neg(cpu)->icount_decr.u16.high, -1);
atomic_set(&cpu->icount_decr.u16.high, -1);
}
/*

View File

@@ -27,10 +27,10 @@ struct page_collection *page_collection_lock(tb_page_addr_t start,
tb_page_addr_t end);
void page_collection_unlock(struct page_collection *set);
void tb_invalidate_phys_page_fast(struct page_collection *pages,
tb_page_addr_t start, int len,
uintptr_t retaddr);
void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end);
void tb_check_watchpoint(CPUState *cpu, uintptr_t retaddr);
tb_page_addr_t start, int len);
void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end,
int is_cpu_write_access);
void tb_check_watchpoint(CPUState *cpu);
#ifdef CONFIG_USER_ONLY
int page_unprotect(target_ulong address, uintptr_t pc);

View File

@@ -8,6 +8,7 @@
*/
#include "qemu/osdep.h"
#include "qemu-common.h"
#include "qemu/error-report.h"
#include "cpu.h"
#include "tcg/tcg.h"
@@ -16,7 +17,6 @@
#include "exec/gen-icount.h"
#include "exec/log.h"
#include "exec/translator.h"
#include "exec/plugin-gen.h"
/* Pairs with tcg_clear_temp_count.
To be called by #TranslatorOps.{translate_insn,tb_stop} if
@@ -32,10 +32,9 @@ void translator_loop_temp_check(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;
bool plugin_enabled;
/* Initialize DisasContext */
db->tb = tb;
@@ -43,9 +42,20 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
db->pc_next = db->pc_first;
db->is_jmp = DISAS_NEXT;
db->num_insns = 0;
db->max_insns = max_insns;
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);
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);
tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */
plugin_enabled = plugin_gen_tb_start(cpu, tb);
while (true) {
db->num_insns++;
ops->insn_start(db, cpu);
tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */
if (plugin_enabled) {
plugin_gen_insn_start(cpu, db);
}
/* Pass breakpoint hits to target for further processing */
if (!db->singlestep_enabled
&& unlikely(!QTAILQ_EMPTY(&cpu->breakpoints))) {
@@ -98,6 +102,7 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
/* Accept I/O on the last instruction. */
gen_io_start();
ops->translate_insn(db, cpu);
gen_io_end();
} else {
ops->translate_insn(db, cpu);
}
@@ -107,14 +112,6 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
break;
}
/*
* We can't instrument after instructions that change control
* flow although this only really affects post-load operations.
*/
if (plugin_enabled) {
plugin_gen_insn_end();
}
/* Stop translation if the output buffer is full,
or we have executed all of the allowed instructions. */
if (tcg_op_buf_full() || db->num_insns >= db->max_insns) {
@@ -127,10 +124,6 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
ops->tb_stop(db, cpu);
gen_tb_end(db->tb, db->num_insns - bp_insn);
if (plugin_enabled) {
plugin_gen_tb_end(cpu);
}
/* The disas_log hook may use these values rather than recompute. */
db->tb->size = db->pc_next - db->pc_first;
db->tb->icount = db->num_insns;

View File

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

View File

@@ -63,57 +63,28 @@ static inline int handle_cpu_signal(uintptr_t pc, siginfo_t *info,
{
CPUState *cpu = current_cpu;
CPUClass *cc;
int ret;
unsigned long address = (unsigned long)info->si_addr;
MMUAccessType access_type = is_write ? MMU_DATA_STORE : MMU_DATA_LOAD;
switch (helper_retaddr) {
default:
/*
* Fault during host memory operation within a helper function.
* The helper's host return address, saved here, gives us a
* pointer into the generated code that will unwind to the
* correct guest pc.
*/
/* We must handle PC addresses from two different sources:
* a call return address and a signal frame address.
*
* Within cpu_restore_state_from_tb we assume the former and adjust
* the address by -GETPC_ADJ so that the address is within the call
* insn so that addr does not accidentally match the beginning of the
* 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;
break;
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.
*/
} else {
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
@@ -163,7 +134,7 @@ static inline int handle_cpu_signal(uintptr_t pc, siginfo_t *info,
* currently executing TB was modified and must be exited
* immediately. Clear helper_retaddr for next execution.
*/
clear_helper_retaddr();
helper_retaddr = 0;
cpu_exit_tb_from_sighandler(cpu, old_set);
/* NORETURN */
@@ -176,48 +147,35 @@ static inline int handle_cpu_signal(uintptr_t pc, siginfo_t *info,
are still valid segv ones */
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->tlb_fill(cpu, address, 0, access_type, MMU_USER_IDX, false, pc);
g_assert_not_reached();
}
/* see if it is an MMU fault */
g_assert(cc->handle_mmu_fault);
ret = cc->handle_mmu_fault(cpu, address, 0, is_write, MMU_USER_IDX);
void *probe_access(CPUArchState *env, target_ulong addr, int size,
MMUAccessType access_type, int mmu_idx, uintptr_t retaddr)
{
int flags;
g_assert(-(addr | TARGET_PAGE_MASK) >= size);
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 (ret == 0) {
/* The MMU fault was handled without causing real CPU fault.
* Retain helper_retaddr for a possible second fault.
*/
return 1;
}
if (!guest_addr_valid(addr) || page_check_range(addr, size, flags) < 0) {
CPUState *cpu = env_cpu(env);
CPUClass *cc = CPU_GET_CLASS(cpu);
cc->tlb_fill(cpu, addr, size, access_type, MMU_USER_IDX, false,
retaddr);
g_assert_not_reached();
/* All other paths lead to cpu_exit; clear helper_retaddr
* for next execution.
*/
helper_retaddr = 0;
if (ret < 0) {
return 0; /* not an MMU fault */
}
return size ? g2h(addr) : NULL;
/* Now we have a real cpu fault. */
cpu_restore_state(cpu, pc, true);
sigprocmask(SIG_SETMASK, old_set, NULL);
cpu_loop_exit(cpu);
/* never comes here */
return 1;
}
#if defined(__i386__)
@@ -740,24 +698,20 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr,
{
/* Enforce qemu required alignment. */
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);
set_helper_retaddr(retaddr);
return ret;
helper_retaddr = retaddr;
return g2h(addr);
}
/* Macro to call the above, with local variables from the use context. */
#define ATOMIC_MMU_DECLS do {} while (0)
#define ATOMIC_MMU_LOOKUP atomic_mmu_lookup(env, addr, DATA_SIZE, GETPC())
#define ATOMIC_MMU_CLEANUP do { clear_helper_retaddr(); } while (0)
#define ATOMIC_MMU_IDX MMU_USER_IDX
#define ATOMIC_MMU_CLEANUP do { helper_retaddr = 0; } while (0)
#define ATOMIC_NAME(X) HELPER(glue(glue(atomic_ ## X, SUFFIX), END))
#define EXTRA_ARGS
#include "atomic_common.inc.c"
#define DATA_SIZE 1
#include "atomic_template.h"

View File

@@ -22,6 +22,7 @@
* THE SOFTWARE.
*/
#include "qemu/osdep.h"
#include "qemu-common.h"
#include "cpu.h"
#include "sysemu/sysemu.h"
#include "sysemu/arch_init.h"
@@ -38,10 +39,6 @@
int graphic_width = 1024;
int graphic_height = 768;
int graphic_depth = 8;
#elif defined(TARGET_M68K)
int graphic_width = 800;
int graphic_height = 600;
int graphic_depth = 8;
#else
int graphic_width = 800;
int graphic_height = 600;
@@ -110,3 +107,14 @@ int xen_available(void)
return 0;
#endif
}
TargetInfo *qmp_query_target(Error **errp)
{
TargetInfo *info = g_malloc0(sizeof(*info));
info->arch = qapi_enum_parse(&SysEmuTarget_lookup, TARGET_NAME, -1,
&error_abort);
return info;
}

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_AUDIO_COREAUDIO) += coreaudio.o
common-obj-$(CONFIG_AUDIO_DSOUND) += dsoundaudio.o
common-obj-$(CONFIG_AUDIO_PT_INT) += audio_pt_int.o
common-obj-$(CONFIG_AUDIO_WIN_INT) += audio_win_int.o
common-obj-y += wavcapture.o

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
#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 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
#define AUDIO_HOST_ENDIANNESS 1
#else
#define AUDIO_HOST_ENDIANNESS 0
#endif
typedef struct audsettings {
struct audsettings {
int freq;
int nchannels;
AudioFormat fmt;
audfmt_e fmt;
int endianness;
} audsettings;
audsettings audiodev_to_audsettings(AudiodevPerDirectionOptions *pdo);
int audioformat_bytes_per_sample(AudioFormat fmt);
int audio_buffer_frames(AudiodevPerDirectionOptions *pdo,
audsettings *as, int def_usecs);
int audio_buffer_samples(AudiodevPerDirectionOptions *pdo,
audsettings *as, int def_usecs);
int audio_buffer_bytes(AudiodevPerDirectionOptions *pdo,
audsettings *as, int def_usecs);
};
typedef enum {
AUD_CNOTIFY_ENABLE,
@@ -79,10 +77,8 @@ typedef struct SWVoiceOut SWVoiceOut;
typedef struct CaptureVoiceOut CaptureVoiceOut;
typedef struct SWVoiceIn SWVoiceIn;
typedef struct AudioState AudioState;
typedef struct QEMUSoundCard {
char *name;
AudioState *state;
QLIST_ENTRY (QEMUSoundCard) entries;
} QEMUSoundCard;
@@ -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_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_remove_card (QEMUSoundCard *card);
CaptureVoiceOut *AUD_add_capture(
AudioState *s,
CaptureVoiceOut *AUD_add_capture (
struct audsettings *as,
struct audio_capture_ops *ops,
void *opaque
@@ -113,7 +109,7 @@ SWVoiceOut *AUD_open_out (
);
void AUD_close_out (QEMUSoundCard *card, SWVoiceOut *sw);
size_t AUD_write (SWVoiceOut *sw, void *pcm_buf, size_t size);
int AUD_write (SWVoiceOut *sw, void *pcm_buf, int size);
int AUD_get_buffer_size_out (SWVoiceOut *sw);
void AUD_set_active_out (SWVoiceOut *sw, int on);
int AUD_is_active_out (SWVoiceOut *sw);
@@ -124,16 +120,6 @@ uint64_t AUD_get_elapsed_usec_out (SWVoiceOut *sw, QEMUAudioTimeStamp *ts);
void AUD_set_volume_out (SWVoiceOut *sw, int mute, uint8_t lvol, uint8_t rvol);
void AUD_set_volume_in (SWVoiceIn *sw, int mute, uint8_t lvol, uint8_t rvol);
#define AUDIO_MAX_CHANNELS 16
typedef struct Volume {
bool mute;
int channels;
uint8_t vol[AUDIO_MAX_CHANNELS];
} Volume;
void audio_set_volume_out(SWVoiceOut *sw, Volume *vol);
void audio_set_volume_in(SWVoiceIn *sw, Volume *vol);
SWVoiceIn *AUD_open_in (
QEMUSoundCard *card,
SWVoiceIn *sw,
@@ -144,7 +130,7 @@ SWVoiceIn *AUD_open_in (
);
void AUD_close_in (QEMUSoundCard *card, SWVoiceIn *sw);
size_t AUD_read (SWVoiceIn *sw, void *pcm_buf, size_t size);
int AUD_read (SWVoiceIn *sw, void *pcm_buf, int size);
void AUD_set_active_in (SWVoiceIn *sw, int on);
int AUD_is_active_in (SWVoiceIn *sw);
@@ -157,8 +143,25 @@ static inline void *advance (void *p, int incr)
return (d + incr);
}
int wav_start_capture(AudioState *state, CaptureState *s, const char *path,
int freq, int bits, int nchannels);
#ifdef __GNUC__
#define audio_MIN(a, b) ( __extension__ ({ \
__typeof (a) ta = a; \
__typeof (b) tb = b; \
((ta)>(tb)?(tb):(ta)); \
}))
#define audio_MAX(a, b) ( __extension__ ({ \
__typeof (a) ta = a; \
__typeof (b) tb = b; \
((ta)<(tb)?(tb):(ta)); \
}))
#else
#define audio_MIN(a, b) ((a)>(b)?(b):(a))
#define audio_MAX(a, b) ((a)<(b)?(b):(a))
#endif
int wav_start_capture (CaptureState *s, const char *path, int freq,
int bits, int nchannels);
bool audio_is_cleaning_up(void);
void audio_cleanup(void);
@@ -168,14 +171,4 @@ void audio_sample_to_uint64(void *samples, int pos,
void audio_sample_from_uint64(void *samples, int pos,
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 */

View File

@@ -33,6 +33,22 @@
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 {
void *opaque;
audio_callback_fn fn;
@@ -43,70 +59,63 @@ struct audio_pcm_info {
int sign;
int freq;
int nchannels;
int bytes_per_frame;
int align;
int shift;
int bytes_per_second;
int swap_endianness;
};
typedef struct AudioState AudioState;
typedef struct SWVoiceCap SWVoiceCap;
typedef struct STSampleBuffer {
size_t pos, size;
st_sample samples[];
} STSampleBuffer;
typedef struct HWVoiceOut {
AudioState *s;
int enabled;
int poll_mode;
int pending_disable;
struct audio_pcm_info info;
f_sample *clip;
int rpos;
uint64_t ts_helper;
STSampleBuffer *mix_buf;
void *buf_emul;
size_t pos_emul, pending_emul, size_emul;
struct st_sample *mix_buf;
size_t samples;
int samples;
QLIST_HEAD (sw_out_listhead, SWVoiceOut) sw_head;
QLIST_HEAD (sw_cap_listhead, SWVoiceCap) cap_head;
int ctl_caps;
struct audio_pcm_ops *pcm_ops;
QLIST_ENTRY (HWVoiceOut) entries;
} HWVoiceOut;
typedef struct HWVoiceIn {
AudioState *s;
int enabled;
int poll_mode;
struct audio_pcm_info info;
t_sample *conv;
size_t total_samples_captured;
int wpos;
int total_samples_captured;
uint64_t ts_helper;
STSampleBuffer *conv_buf;
void *buf_emul;
size_t pos_emul, pending_emul, size_emul;
struct st_sample *conv_buf;
size_t samples;
int samples;
QLIST_HEAD (sw_in_listhead, SWVoiceIn) sw_head;
int ctl_caps;
struct audio_pcm_ops *pcm_ops;
QLIST_ENTRY (HWVoiceIn) entries;
} HWVoiceIn;
struct SWVoiceOut {
QEMUSoundCard *card;
AudioState *s;
struct audio_pcm_info info;
t_sample *conv;
int64_t ratio;
struct st_sample *buf;
void *rate;
size_t total_hw_samples_mixed;
int total_hw_samples_mixed;
int active;
int empty;
HWVoiceOut *hw;
@@ -118,12 +127,11 @@ struct SWVoiceOut {
struct SWVoiceIn {
QEMUSoundCard *card;
AudioState *s;
int active;
struct audio_pcm_info info;
int64_t ratio;
void *rate;
size_t total_hw_samples_acquired;
int total_hw_samples_acquired;
struct st_sample *buf;
f_sample *clip;
HWVoiceIn *hw;
@@ -137,7 +145,8 @@ typedef struct audio_driver audio_driver;
struct audio_driver {
const char *name;
const char *descr;
void *(*init) (Audiodev *);
struct audio_option *options;
void *(*init) (void);
void (*fini) (void *);
struct audio_pcm_ops *pcm_ops;
int can_be_default;
@@ -145,46 +154,24 @@ struct audio_driver {
int max_voices_in;
int voice_size_out;
int voice_size_in;
int ctl_caps;
QLIST_ENTRY(audio_driver) next;
};
struct audio_pcm_ops {
int (*init_out)(HWVoiceOut *hw, audsettings *as, void *drv_opaque);
void (*fini_out)(HWVoiceOut *hw);
size_t (*write) (HWVoiceOut *hw, void *buf, size_t size);
/*
* get a buffer that after later can be passed to put_buffer_out; optional
* returns the buffer, and writes it's size to size (in bytes)
* this is unrelated to the above buffer_size_out function
*/
void *(*get_buffer_out)(HWVoiceOut *hw, size_t *size);
/*
* put back the buffer returned by get_buffer_out; optional
* buf must be equal the pointer returned by get_buffer_out,
* size may be smaller
*/
size_t (*put_buffer_out)(HWVoiceOut *hw, void *buf, size_t size);
void (*enable_out)(HWVoiceOut *hw, bool enable);
void (*volume_out)(HWVoiceOut *hw, Volume *vol);
int (*init_out)(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque);
void (*fini_out)(HWVoiceOut *hw);
int (*run_out) (HWVoiceOut *hw, int live);
int (*write) (SWVoiceOut *sw, void *buf, int size);
int (*ctl_out) (HWVoiceOut *hw, int cmd, ...);
int (*init_in) (HWVoiceIn *hw, audsettings *as, void *drv_opaque);
void (*fini_in) (HWVoiceIn *hw);
size_t (*read) (HWVoiceIn *hw, void *buf, size_t size);
void *(*get_buffer_in)(HWVoiceIn *hw, size_t *size);
void (*put_buffer_in)(HWVoiceIn *hw, void *buf, size_t size);
void (*enable_in)(HWVoiceIn *hw, bool enable);
void (*volume_in)(HWVoiceIn *hw, Volume *vol);
int (*init_in) (HWVoiceIn *hw, struct audsettings *as, void *drv_opaque);
void (*fini_in) (HWVoiceIn *hw);
int (*run_in) (HWVoiceIn *hw);
int (*read) (SWVoiceIn *sw, void *buf, int size);
int (*ctl_in) (HWVoiceIn *hw, int cmd, ...);
};
void *audio_generic_get_buffer_in(HWVoiceIn *hw, size_t *size);
void audio_generic_put_buffer_in(HWVoiceIn *hw, void *buf, size_t size);
void *audio_generic_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_put_buffer_out_nowrite(HWVoiceOut *hw, void *buf,
size_t size);
size_t audio_generic_write(HWVoiceOut *hw, void *buf, size_t size);
size_t audio_generic_read(HWVoiceIn *hw, void *buf, size_t size);
struct capture_callback {
struct audio_capture_ops ops;
void *opaque;
@@ -206,7 +193,6 @@ struct SWVoiceCap {
typedef struct AudioState {
struct audio_driver *drv;
Audiodev *dev;
void *drv_opaque;
QEMUTimer *ts;
@@ -217,39 +203,36 @@ typedef struct AudioState {
int nb_hw_voices_out;
int nb_hw_voices_in;
int vm_running;
int64_t period_ticks;
bool timer_running;
uint64_t timer_last;
QTAILQ_ENTRY(AudioState) list;
} AudioState;
extern const struct mixeng_volume nominal_volume;
extern const char *audio_prio_list[];
void audio_driver_register(audio_driver *drv);
audio_driver *audio_driver_lookup(const char *name);
void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as);
void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len);
int audio_pcm_sw_write (SWVoiceOut *sw, void *buf, int len);
int audio_pcm_hw_get_live_in (HWVoiceIn *hw);
int audio_pcm_sw_read (SWVoiceIn *sw, void *buf, int len);
int audio_pcm_hw_clip_out (HWVoiceOut *hw, void *pcm_buf,
int live, int pending);
int audio_bug (const char *funcname, int cond);
void *audio_calloc (const char *funcname, int nmemb, size_t size);
void audio_run(AudioState *s, const char *msg);
void audio_run (const char *msg);
typedef struct RateCtl {
int64_t start_ticks;
int64_t bytes_sent;
} RateCtl;
#define VOICE_ENABLE 1
#define VOICE_DISABLE 2
#define VOICE_VOLUME 3
void audio_rate_start(RateCtl *rate);
size_t audio_rate_get_bytes(struct audio_pcm_info *info, RateCtl *rate,
size_t bytes_avail);
#define VOICE_VOLUME_CAP (1 << VOICE_VOLUME)
static inline size_t audio_ring_dist(size_t dst, size_t src, size_t len)
static inline int audio_ring_dist (int dst, int src, int len)
{
return (dst >= src) ? (dst - src) : (len - src + dst);
}
@@ -265,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) 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 */

View File

@@ -1,549 +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 void lv_start_struct(Visitor *v, const char *name, void **obj,
size_t size, Error **errp)
{
LegacyPrintVisitor *lv = (LegacyPrintVisitor *) v;
lv->path = g_list_append(lv->path, g_strdup(name));
}
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 void lv_type_int64(Visitor *v, const char *name, int64_t *obj,
Error **errp)
{
lv_print_key(v, name);
printf("%" PRIi64, *obj);
}
static void lv_type_uint64(Visitor *v, const char *name, uint64_t *obj,
Error **errp)
{
lv_print_key(v, name);
printf("%" PRIu64, *obj);
}
static void lv_type_bool(Visitor *v, const char *name, bool *obj, Error **errp)
{
lv_print_key(v, name);
printf("%s", *obj ? "on" : "off");
}
static void 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++);
}
}
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
#endif
static void glue(audio_init_nb_voices_, TYPE)(AudioState *s,
struct audio_driver *drv)
static void glue (audio_init_nb_voices_, TYPE) (struct audio_driver *drv)
{
AudioState *s = &glob_audio_state;
int max_voices = glue (drv->max_voices_, TYPE);
int voice_size = glue (drv->voice_size_, TYPE);
@@ -71,24 +71,20 @@ static void glue(audio_init_nb_voices_, TYPE)(AudioState *s,
static void glue (audio_pcm_hw_free_resources_, TYPE) (HW *hw)
{
g_free(hw->buf_emul);
g_free (HWBUF);
HWBUF = NULL;
}
static void glue(audio_pcm_hw_alloc_resources_, TYPE)(HW *hw)
static int glue (audio_pcm_hw_alloc_resources_, TYPE) (HW *hw)
{
if (glue(audio_get_pdo_, TYPE)(hw->s->dev)->mixing_engine) {
size_t samples = hw->samples;
if (audio_bug(__func__, samples == 0)) {
dolog("Attempted to allocate empty buffer\n");
}
HWBUF = g_malloc0(sizeof(STSampleBuffer) + sizeof(st_sample) * samples);
HWBUF->size = samples;
} else {
HWBUF = NULL;
HWBUF = audio_calloc(__func__, hw->samples, sizeof(struct st_sample));
if (!HWBUF) {
dolog ("Could not allocate " NAME " buffer (%d samples)\n",
hw->samples);
return -1;
}
return 0;
}
static void glue (audio_pcm_sw_free_resources_, TYPE) (SW *sw)
@@ -107,11 +103,7 @@ static int glue (audio_pcm_sw_alloc_resources_, TYPE) (SW *sw)
{
int samples;
if (!glue(audio_get_pdo_, TYPE)(sw->s->dev)->mixing_engine) {
return 0;
}
samples = ((int64_t) sw->HWBUF->size << 32) / sw->ratio;
samples = ((int64_t) sw->hw->samples << 32) / sw->ratio;
sw->buf = audio_calloc(__func__, samples, sizeof(struct st_sample));
if (!sw->buf) {
@@ -191,8 +183,8 @@ static void glue (audio_pcm_hw_del_sw_, TYPE) (SW *sw)
static void glue (audio_pcm_hw_gc_, TYPE) (HW **hwp)
{
AudioState *s = &glob_audio_state;
HW *hw = *hwp;
AudioState *s = hw->s;
if (!hw->sw_head.lh_first) {
#ifdef DAC
@@ -207,14 +199,15 @@ static void glue (audio_pcm_hw_gc_, TYPE) (HW **hwp)
}
}
static HW *glue(audio_pcm_hw_find_any_, TYPE)(AudioState *s, HW *hw)
static HW *glue (audio_pcm_hw_find_any_, TYPE) (HW *hw)
{
AudioState *s = &glob_audio_state;
return hw ? hw->entries.le_next : glue (s->hw_head_, TYPE).lh_first;
}
static HW *glue(audio_pcm_hw_find_any_enabled_, TYPE)(AudioState *s, HW *hw)
static HW *glue (audio_pcm_hw_find_any_enabled_, TYPE) (HW *hw)
{
while ((hw = glue(audio_pcm_hw_find_any_, TYPE)(s, hw))) {
while ((hw = glue (audio_pcm_hw_find_any_, TYPE) (hw))) {
if (hw->enabled) {
return hw;
}
@@ -222,10 +215,12 @@ static HW *glue(audio_pcm_hw_find_any_enabled_, TYPE)(AudioState *s, HW *hw)
return NULL;
}
static HW *glue(audio_pcm_hw_find_specific_, TYPE)(AudioState *s, HW *hw,
struct audsettings *as)
static HW *glue (audio_pcm_hw_find_specific_, TYPE) (
HW *hw,
struct audsettings *as
)
{
while ((hw = glue(audio_pcm_hw_find_any_, TYPE)(s, hw))) {
while ((hw = glue (audio_pcm_hw_find_any_, TYPE) (hw))) {
if (audio_pcm_info_eq (&hw->info, as)) {
return hw;
}
@@ -233,10 +228,10 @@ static HW *glue(audio_pcm_hw_find_specific_, TYPE)(AudioState *s, HW *hw,
return NULL;
}
static HW *glue(audio_pcm_hw_add_new_, TYPE)(AudioState *s,
struct audsettings *as)
static HW *glue (audio_pcm_hw_add_new_, TYPE) (struct audsettings *as)
{
HW *hw;
AudioState *s = &glob_audio_state;
struct audio_driver *drv = s->drv;
if (!glue (s->nb_hw_voices_, TYPE)) {
@@ -260,8 +255,8 @@ static HW *glue(audio_pcm_hw_add_new_, TYPE)(AudioState *s,
return NULL;
}
hw->s = s;
hw->pcm_ops = drv->pcm_ops;
hw->ctl_caps = drv->ctl_caps;
QLIST_INIT (&hw->sw_head);
#ifdef DAC
@@ -272,7 +267,7 @@ static HW *glue(audio_pcm_hw_add_new_, TYPE)(AudioState *s,
}
if (audio_bug(__func__, hw->samples <= 0)) {
dolog("hw->samples=%zd\n", hw->samples);
dolog ("hw->samples=%d\n", hw->samples);
goto err1;
}
@@ -286,7 +281,9 @@ static HW *glue(audio_pcm_hw_add_new_, TYPE)(AudioState *s,
[hw->info.swap_endianness]
[audio_bits_to_index (hw->info.bits)];
glue(audio_pcm_hw_alloc_resources_, TYPE)(hw);
if (glue (audio_pcm_hw_alloc_resources_, TYPE) (hw)) {
goto err1;
}
QLIST_INSERT_HEAD (&s->glue (hw_head_, TYPE), hw, entries);
glue (s->nb_hw_voices_, TYPE) -= 1;
@@ -302,62 +299,31 @@ static HW *glue(audio_pcm_hw_add_new_, TYPE)(AudioState *s,
return NULL;
}
AudiodevPerDirectionOptions *glue(audio_get_pdo_, TYPE)(Audiodev *dev)
{
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_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)
static HW *glue (audio_pcm_hw_add_, TYPE) (struct audsettings *as)
{
HW *hw;
AudiodevPerDirectionOptions *pdo = glue(audio_get_pdo_, TYPE)(s->dev);
if (!pdo->mixing_engine || pdo->fixed_settings) {
hw = glue(audio_pcm_hw_add_new_, TYPE)(s, as);
if (!pdo->mixing_engine || hw) {
if (glue (conf.fixed_, TYPE).enabled && glue (conf.fixed_, TYPE).greedy) {
hw = glue (audio_pcm_hw_add_new_, TYPE) (as);
if (hw) {
return hw;
}
}
hw = glue(audio_pcm_hw_find_specific_, TYPE)(s, NULL, as);
hw = glue (audio_pcm_hw_find_specific_, TYPE) (NULL, as);
if (hw) {
return hw;
}
hw = glue(audio_pcm_hw_add_new_, TYPE)(s, as);
hw = glue (audio_pcm_hw_add_new_, TYPE) (as);
if (hw) {
return hw;
}
return glue(audio_pcm_hw_find_any_, TYPE)(s, NULL);
return glue (audio_pcm_hw_find_any_, TYPE) (NULL);
}
static SW *glue(audio_pcm_create_voice_pair_, TYPE)(
AudioState *s,
static SW *glue (audio_pcm_create_voice_pair_, TYPE) (
const char *sw_name,
struct audsettings *as
)
@@ -365,10 +331,9 @@ static SW *glue(audio_pcm_create_voice_pair_, TYPE)(
SW *sw;
HW *hw;
struct audsettings hw_as;
AudiodevPerDirectionOptions *pdo = glue(audio_get_pdo_, TYPE)(s->dev);
if (pdo->fixed_settings) {
hw_as = audiodev_to_audsettings(pdo);
if (glue (conf.fixed_, TYPE).enabled) {
hw_as = glue (conf.fixed_, TYPE).settings;
}
else {
hw_as = *as;
@@ -380,9 +345,8 @@ static SW *glue(audio_pcm_create_voice_pair_, TYPE)(
sw_name ? sw_name : "unknown", sizeof (*sw));
goto err1;
}
sw->s = s;
hw = glue(audio_pcm_hw_add_, TYPE)(s, &hw_as);
hw = glue (audio_pcm_hw_add_, TYPE) (&hw_as);
if (!hw) {
goto err2;
}
@@ -433,8 +397,7 @@ SW *glue (AUD_open_, TYPE) (
struct audsettings *as
)
{
AudioState *s;
AudiodevPerDirectionOptions *pdo;
AudioState *s = &glob_audio_state;
if (audio_bug(__func__, !card || !name || !callback_fn || !as)) {
dolog ("card=%p name=%p callback_fn=%p as=%p\n",
@@ -442,9 +405,6 @@ SW *glue (AUD_open_, TYPE) (
goto fail;
}
s = card->state;
pdo = glue(audio_get_pdo_, TYPE)(s->dev);
ldebug ("open %s, freq %d, nchannels %d, fmt %d\n",
name, as->freq, as->nchannels, as->fmt);
@@ -462,7 +422,7 @@ SW *glue (AUD_open_, TYPE) (
return sw;
}
if (!pdo->fixed_settings && sw) {
if (!glue (conf.fixed_, TYPE).enabled && sw) {
glue (AUD_close_, TYPE) (card, sw);
sw = NULL;
}
@@ -482,7 +442,7 @@ SW *glue (AUD_open_, TYPE) (
}
}
else {
sw = glue(audio_pcm_create_voice_pair_, TYPE)(s, name, as);
sw = glue (audio_pcm_create_voice_pair_, TYPE) (name, as);
if (!sw) {
dolog ("Failed to create voice `%s'\n", name);
return NULL;

View File

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

View File

@@ -26,7 +26,7 @@
#include <CoreAudio/CoreAudio.h>
#include <pthread.h> /* pthread_X */
#include "qemu/module.h"
#include "qemu-common.h"
#include "audio.h"
#define AUDIO_CAP "coreaudio"
@@ -36,6 +36,11 @@
#define MAC_OS_X_VERSION_10_6 1060
#endif
typedef struct {
int buffer_frames;
int nbuffers;
} CoreaudioConf;
typedef struct coreaudioVoiceOut {
HWVoiceOut hw;
pthread_mutex_t mutex;
@@ -43,6 +48,9 @@ typedef struct coreaudioVoiceOut {
UInt32 audioDevicePropertyBufferFrameSize;
AudioStreamBasicDescription outputStreamBasicDescription;
AudioDeviceIOProcID ioprocid;
int live;
int decr;
int rpos;
} coreaudioVoiceOut;
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
@@ -394,29 +402,31 @@ static int coreaudio_unlock (coreaudioVoiceOut *core, const char *fn_name)
return 0;
}
#define COREAUDIO_WRAPPER_FUNC(name, ret_type, args_decl, args) \
static ret_type glue(coreaudio_, name)args_decl \
{ \
coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; \
ret_type ret; \
\
if (coreaudio_lock(core, "coreaudio_" #name)) { \
return 0; \
} \
\
ret = glue(audio_generic_, name)args; \
\
coreaudio_unlock(core, "coreaudio_" #name); \
return ret; \
static int coreaudio_run_out (HWVoiceOut *hw, int live)
{
int decr;
coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
if (coreaudio_lock (core, "coreaudio_run_out")) {
return 0;
}
COREAUDIO_WRAPPER_FUNC(get_buffer_out, void *, (HWVoiceOut *hw, size_t *size),
(hw, size))
COREAUDIO_WRAPPER_FUNC(put_buffer_out_nowrite, size_t,
(HWVoiceOut *hw, void *buf, size_t size),
(hw, buf, size))
COREAUDIO_WRAPPER_FUNC(write, size_t, (HWVoiceOut *hw, void *buf, size_t size),
(hw, buf, size))
#undef COREAUDIO_WRAPPER_FUNC
if (core->decr > live) {
ldebug ("core->decr %d live %d core->live %d\n",
core->decr,
live,
core->live);
}
decr = audio_MIN (core->decr, live);
core->decr -= decr;
core->live = live - decr;
hw->rpos = core->rpos;
coreaudio_unlock (core, "coreaudio_run_out");
return decr;
}
/* callback to feed audiooutput buffer */
static OSStatus audioDeviceIOProc(
@@ -428,11 +438,19 @@ static OSStatus audioDeviceIOProc(
const AudioTimeStamp* inOutputTime,
void* hwptr)
{
UInt32 frameCount, pending_frames;
void *out = outOutputData->mBuffers[0].mData;
UInt32 frame, frameCount;
float *out = outOutputData->mBuffers[0].mData;
HWVoiceOut *hw = hwptr;
coreaudioVoiceOut *core = (coreaudioVoiceOut *) hwptr;
size_t len;
int rpos, live;
struct st_sample *src;
#ifndef FLOAT_MIXENG
#ifdef RECIPROCAL
const float scale = 1.f / UINT_MAX;
#else
const float scale = UINT_MAX;
#endif
#endif
if (coreaudio_lock (core, "audioDeviceIOProc")) {
inInputTime = 0;
@@ -440,49 +458,45 @@ static OSStatus audioDeviceIOProc(
}
frameCount = core->audioDevicePropertyBufferFrameSize;
pending_frames = hw->pending_emul / hw->info.bytes_per_frame;
live = core->live;
/* if there are not enough samples, set signal and return */
if (pending_frames < frameCount) {
if (live < frameCount) {
inInputTime = 0;
coreaudio_unlock (core, "audioDeviceIOProc(empty)");
return 0;
}
len = frameCount * hw->info.bytes_per_frame;
while (len) {
size_t write_len;
ssize_t start = ((ssize_t) hw->pos_emul) - hw->pending_emul;
if (start < 0) {
start += hw->size_emul;
}
assert(start >= 0 && start < hw->size_emul);
rpos = core->rpos;
src = hw->mix_buf + rpos;
write_len = MIN(MIN(hw->pending_emul, len),
hw->size_emul - start);
memcpy(out, hw->buf_emul + start, write_len);
hw->pending_emul -= write_len;
len -= write_len;
out += write_len;
/* fill buffer */
for (frame = 0; frame < frameCount; frame++) {
#ifdef FLOAT_MIXENG
*out++ = src[frame].l; /* left channel */
*out++ = src[frame].r; /* right channel */
#else
#ifdef RECIPROCAL
*out++ = src[frame].l * scale; /* left channel */
*out++ = src[frame].r * scale; /* right channel */
#else
*out++ = src[frame].l / scale; /* left channel */
*out++ = src[frame].r / scale; /* right channel */
#endif
#endif
}
rpos = (rpos + frameCount) % hw->samples;
core->decr += frameCount;
core->rpos = rpos;
coreaudio_unlock (core, "audioDeviceIOProc");
return 0;
}
static UInt32 coreaudio_get_flags(struct audio_pcm_info *info,
struct audsettings *as)
static int coreaudio_write (SWVoiceOut *sw, void *buf, int len)
{
UInt32 flags = info->sign ? kAudioFormatFlagIsSignedInteger : 0;
if (as->endianness) { /* 0 = little, 1 = big */
flags |= kAudioFormatFlagIsBigEndian;
}
if (flags == 0) { /* must not be 0 */
flags = kAudioFormatFlagsAreAllClear;
}
return flags;
return audio_pcm_sw_write (sw, buf, len);
}
static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
@@ -493,9 +507,7 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
int err;
const char *typ = "playback";
AudioValueRange frameRange;
Audiodev *dev = drv_opaque;
AudiodevCoreaudioPerDirectionOptions *cpdo = dev->u.coreaudio.out;
int frames;
CoreaudioConf *conf = drv_opaque;
/* create mutex */
err = pthread_mutex_init(&core->mutex, NULL);
@@ -526,17 +538,16 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
return -1;
}
frames = audio_buffer_frames(
qapi_AudiodevCoreaudioPerDirectionOptions_base(cpdo), as, 11610);
if (frameRange.mMinimum > frames) {
if (frameRange.mMinimum > conf->buffer_frames) {
core->audioDevicePropertyBufferFrameSize = (UInt32) 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;
dolog ("warning: Downsizing Buffer Frames to %f\n", frameRange.mMaximum);
}
else {
core->audioDevicePropertyBufferFrameSize = frames;
core->audioDevicePropertyBufferFrameSize = conf->buffer_frames;
}
/* set Buffer Frame Size */
@@ -557,8 +568,7 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
"Could not get device buffer frame size\n");
return -1;
}
hw->samples = (cpdo->has_buffer_count ? cpdo->buffer_count : 4) *
core->audioDevicePropertyBufferFrameSize;
hw->samples = conf->nbuffers * core->audioDevicePropertyBufferFrameSize;
/* get StreamFormat */
status = coreaudio_get_streamformat(core->outputDeviceID,
@@ -572,16 +582,6 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
/* set Samplerate */
core->outputStreamBasicDescription.mSampleRate = (Float64) as->freq;
core->outputStreamBasicDescription.mFormatID = kAudioFormatLinearPCM;
core->outputStreamBasicDescription.mFormatFlags =
coreaudio_get_flags(&hw->info, as);
core->outputStreamBasicDescription.mBytesPerPacket =
core->outputStreamBasicDescription.mBytesPerFrame =
hw->info.nchannels * hw->info.bits / 8;
core->outputStreamBasicDescription.mFramesPerPacket = 1;
core->outputStreamBasicDescription.mChannelsPerFrame = hw->info.nchannels;
core->outputStreamBasicDescription.mBitsPerChannel = hw->info.bits;
status = coreaudio_set_streamformat(core->outputDeviceID,
&core->outputStreamBasicDescription);
if (status != kAudioHardwareNoError) {
@@ -648,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;
coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
if (enable) {
switch (cmd) {
case VOICE_ENABLE:
/* start playback */
if (!isPlaying(core->outputDeviceID)) {
status = AudioDeviceStart(core->outputDeviceID, core->ioprocid);
@@ -661,7 +662,9 @@ static void coreaudio_enable_out(HWVoiceOut *hw, bool enable)
coreaudio_logerr (status, "Could not resume playback\n");
}
}
} else {
break;
case VOICE_DISABLE:
/* stop playback */
if (!audio_is_cleaning_up()) {
if (isPlaying(core->outputDeviceID)) {
@@ -672,30 +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)
{
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 = {
.init_out = coreaudio_init_out,
.fini_out = coreaudio_fini_out,
.run_out = coreaudio_run_out,
.write = coreaudio_write,
.get_buffer_out = coreaudio_get_buffer_out,
.put_buffer_out = coreaudio_put_buffer_out_nowrite,
.enable_out = coreaudio_enable_out
.ctl_out = coreaudio_ctl_out
};
static struct audio_driver coreaudio_audio_driver = {
.name = "coreaudio",
.descr = "CoreAudio http://developer.apple.com/audio/coreaudio.html",
.options = coreaudio_options,
.init = coreaudio_audio_init,
.fini = coreaudio_audio_fini,
.pcm_ops = &coreaudio_pcm_ops,

View File

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

View File

@@ -27,12 +27,11 @@
*/
#include "qemu/osdep.h"
#include "qemu-common.h"
#include "audio.h"
#define AUDIO_CAP "dsound"
#include "audio_int.h"
#include "qemu/host-utils.h"
#include "qemu/module.h"
#include <windows.h>
#include <mmsystem.h>
@@ -43,21 +42,35 @@
/* #define DEBUG_DSOUND */
typedef struct {
int bufsize_in;
int bufsize_out;
int latency_millis;
} DSoundConf;
typedef struct {
LPDIRECTSOUND dsound;
LPDIRECTSOUNDCAPTURE dsound_capture;
struct audsettings settings;
Audiodev *dev;
DSoundConf conf;
} dsound;
typedef struct {
HWVoiceOut hw;
LPDIRECTSOUNDBUFFER dsound_buffer;
DWORD old_pos;
int first_time;
dsound *s;
#ifdef DEBUG_DSOUND
DWORD old_ppos;
DWORD played;
DWORD mixed;
#endif
} DSoundVoiceOut;
typedef struct {
HWVoiceIn hw;
int first_time;
LPDIRECTSOUNDCAPTUREBUFFER dsound_capture_buffer;
dsound *s;
} DSoundVoiceIn;
@@ -235,6 +248,11 @@ static void GCC_FMT_ATTR (3, 4) dsound_logerr2 (
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
static void print_wave_format (WAVEFORMATEX *wfx)
{
@@ -299,6 +317,33 @@ static int dsound_get_status_in (LPDIRECTSOUNDCAPTUREBUFFER dscb,
return 0;
}
static void dsound_write_sample (HWVoiceOut *hw, uint8_t *dst, int dst_len)
{
int src_len1 = dst_len;
int src_len2 = 0;
int pos = hw->rpos + dst_len;
struct st_sample *src1 = hw->mix_buf + hw->rpos;
struct st_sample *src2 = NULL;
if (pos > hw->samples) {
src_len1 = hw->samples - hw->rpos;
src2 = hw->mix_buf;
src_len2 = dst_len - src_len1;
pos = src_len2;
}
if (src_len1) {
hw->clip (dst, src1, src_len1);
}
if (src_len2) {
dst = advance (dst, src_len1 << hw->info.shift);
hw->clip (dst, src2, src_len2);
}
hw->rpos = pos % hw->samples;
}
static void dsound_clear_sample (HWVoiceOut *hw, LPDIRECTSOUNDBUFFER dsb,
dsound *s)
{
@@ -310,7 +355,7 @@ static void dsound_clear_sample (HWVoiceOut *hw, LPDIRECTSOUNDBUFFER dsb,
dsb,
&hw->info,
0,
hw->size_emul,
hw->samples << hw->info.shift,
&p1, &p2,
&blen1, &blen2,
1,
@@ -320,8 +365,8 @@ static void dsound_clear_sample (HWVoiceOut *hw, LPDIRECTSOUNDBUFFER dsb,
return;
}
len1 = blen1 / hw->info.bytes_per_frame;
len2 = blen2 / hw->info.bytes_per_frame;
len1 = blen1 >> hw->info.shift;
len2 = blen2 >> hw->info.shift;
#ifdef DEBUG_DSOUND
dolog ("clear %p,%ld,%ld %p,%ld,%ld\n",
@@ -361,7 +406,7 @@ static int dsound_open (dsound *s)
return 0;
}
static void dsound_enable_out(HWVoiceOut *hw, bool enable)
static int dsound_ctl_out (HWVoiceOut *hw, int cmd, ...)
{
HRESULT hr;
DWORD status;
@@ -371,17 +416,18 @@ static void dsound_enable_out(HWVoiceOut *hw, bool enable)
if (!dsb) {
dolog ("Attempt to control voice without a buffer\n");
return;
return 0;
}
if (enable) {
switch (cmd) {
case VOICE_ENABLE:
if (dsound_get_status_out (dsb, &status, s)) {
return;
return -1;
}
if (status & DSBSTATUS_PLAYING) {
dolog ("warning: Voice is already playing\n");
return;
return 0;
}
dsound_clear_sample (hw, dsb, s);
@@ -389,74 +435,170 @@ static void dsound_enable_out(HWVoiceOut *hw, bool enable)
hr = IDirectSoundBuffer_Play (dsb, 0, 0, DSBPLAY_LOOPING);
if (FAILED (hr)) {
dsound_logerr (hr, "Could not start playing buffer\n");
return;
return -1;
}
} else {
break;
case VOICE_DISABLE:
if (dsound_get_status_out (dsb, &status, s)) {
return;
return -1;
}
if (status & DSBSTATUS_PLAYING) {
hr = IDirectSoundBuffer_Stop (dsb);
if (FAILED (hr)) {
dsound_logerr (hr, "Could not stop playing buffer\n");
return;
return -1;
}
}
else {
dolog ("warning: Voice is not playing\n");
}
break;
}
return 0;
}
static void *dsound_get_buffer_out(HWVoiceOut *hw, size_t *size)
static int dsound_write (SWVoiceOut *sw, void *buf, int len)
{
return audio_pcm_sw_write (sw, buf, len);
}
static int dsound_run_out (HWVoiceOut *hw, int live)
{
DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer;
HRESULT hr;
DWORD ppos, act_size;
size_t req_size;
int err;
void *ret;
hr = IDirectSoundBuffer_GetCurrentPosition(dsb, &ppos, NULL);
if (FAILED(hr)) {
dsound_logerr(hr, "Could not get playback buffer position\n");
*size = 0;
return NULL;
}
req_size = audio_ring_dist(ppos, hw->pos_emul, hw->size_emul);
req_size = MIN(req_size, hw->size_emul - hw->pos_emul);
err = dsound_lock_out(dsb, &hw->info, hw->pos_emul, req_size, &ret, NULL,
&act_size, NULL, false, ds->s);
if (err) {
dolog("Failed to lock buffer\n");
*size = 0;
return NULL;
}
*size = act_size;
return ret;
}
static size_t dsound_put_buffer_out(HWVoiceOut *hw, void *buf, size_t len)
{
HRESULT hr;
DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer;
int err = dsound_unlock_out(dsb, buf, NULL, len, 0);
int len, hwshift;
DWORD blen1, blen2;
DWORD len1, len2;
DWORD decr;
DWORD wpos, ppos, old_pos;
LPVOID p1, p2;
int bufsize;
dsound *s = ds->s;
DSoundConf *conf = &s->conf;
if (err) {
dolog("Failed to unlock buffer!!\n");
if (!dsb) {
dolog ("Attempt to run empty with playback buffer\n");
return 0;
}
hw->pos_emul = (hw->pos_emul + len) % hw->size_emul;
return len;
hwshift = hw->info.shift;
bufsize = hw->samples << hwshift;
hr = IDirectSoundBuffer_GetCurrentPosition (
dsb,
&ppos,
ds->first_time ? &wpos : NULL
);
if (FAILED (hr)) {
dsound_logerr (hr, "Could not get playback buffer position\n");
return 0;
}
len = live << hwshift;
if (ds->first_time) {
if (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;
DWORD status;
@@ -465,17 +607,18 @@ static void dsound_enable_in(HWVoiceIn *hw, bool enable)
if (!dscb) {
dolog ("Attempt to control capture voice without a buffer\n");
return;
return -1;
}
if (enable) {
switch (cmd) {
case VOICE_ENABLE:
if (dsound_get_status_in (dscb, &status)) {
return;
return -1;
}
if (status & DSCBSTATUS_CAPTURING) {
dolog ("warning: Voice is already capturing\n");
return;
return 0;
}
/* clear ?? */
@@ -483,71 +626,133 @@ static void dsound_enable_in(HWVoiceIn *hw, bool enable)
hr = IDirectSoundCaptureBuffer_Start (dscb, DSCBSTART_LOOPING);
if (FAILED (hr)) {
dsound_logerr (hr, "Could not start capturing\n");
return;
return -1;
}
} else {
break;
case VOICE_DISABLE:
if (dsound_get_status_in (dscb, &status)) {
return;
return -1;
}
if (status & DSCBSTATUS_CAPTURING) {
hr = IDirectSoundCaptureBuffer_Stop (dscb);
if (FAILED (hr)) {
dsound_logerr (hr, "Could not stop capturing\n");
return;
return -1;
}
}
else {
dolog ("warning: Voice is not capturing\n");
}
break;
}
return 0;
}
static void *dsound_get_buffer_in(HWVoiceIn *hw, size_t *size)
static int dsound_read (SWVoiceIn *sw, void *buf, int len)
{
return audio_pcm_sw_read (sw, buf, len);
}
static int dsound_run_in (HWVoiceIn *hw)
{
DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer;
HRESULT hr;
DWORD cpos, act_size;
size_t req_size;
int err;
void *ret;
hr = IDirectSoundCaptureBuffer_GetCurrentPosition(dscb, &cpos, NULL);
if (FAILED(hr)) {
dsound_logerr(hr, "Could not get capture buffer position\n");
*size = 0;
return NULL;
}
req_size = audio_ring_dist(cpos, hw->pos_emul, hw->size_emul);
req_size = MIN(req_size, hw->size_emul - hw->pos_emul);
err = dsound_lock_in(dscb, &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 void dsound_put_buffer_in(HWVoiceIn *hw, void *buf, size_t len)
{
HRESULT hr;
DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer;
int err = dsound_unlock_in(dscb, buf, NULL, len, 0);
int live, len, dead;
DWORD blen1, blen2;
DWORD len1, len2;
DWORD decr;
DWORD cpos, rpos;
LPVOID p1, p2;
int hwshift;
dsound *s = ds->s;
if (err) {
dolog("Failed to unlock buffer!!\n");
return;
if (!dscb) {
dolog ("Attempt to run without capture buffer\n");
return 0;
}
hw->pos_emul = (hw->pos_emul + len) % hw->size_emul;
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) {
ds->first_time = 0;
if (rpos & hw->info.align) {
ldebug ("warning: Misaligned capture read position %ld(%d)\n",
rpos, hw->info.align);
}
hw->wpos = rpos >> hwshift;
}
if (cpos & hw->info.align) {
ldebug ("warning: Misaligned capture position %ld(%d)\n",
cpos, hw->info.align);
}
cpos >>= hwshift;
len = audio_ring_dist (cpos, hw->wpos, hw->samples);
if (!len) {
return 0;
}
len = audio_MIN (len, dead);
err = dsound_lock_in (
dscb,
&hw->info,
hw->wpos << hwshift,
len << hwshift,
&p1,
&p2,
&blen1,
&blen2,
0,
s
);
if (err) {
return 0;
}
len1 = blen1 >> hwshift;
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 DSoundConf glob_conf = {
.bufsize_in = 16384,
.bufsize_out = 16384,
.latency_millis = 10
};
static void dsound_audio_fini (void *opaque)
{
HRESULT hr;
@@ -578,22 +783,13 @@ static void dsound_audio_fini (void *opaque)
g_free(s);
}
static void *dsound_audio_init(Audiodev *dev)
static void *dsound_audio_init (void)
{
int err;
HRESULT hr;
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);
if (FAILED (hr)) {
dsound_logerr (hr, "Could not initialize COM\n");
@@ -658,25 +854,46 @@ static void *dsound_audio_init(Audiodev *dev)
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 = {
.init_out = dsound_init_out,
.fini_out = dsound_fini_out,
.write = audio_generic_write,
.get_buffer_out = dsound_get_buffer_out,
.put_buffer_out = dsound_put_buffer_out,
.enable_out = dsound_enable_out,
.run_out = dsound_run_out,
.write = dsound_write,
.ctl_out = dsound_ctl_out,
.init_in = dsound_init_in,
.fini_in = dsound_fini_in,
.read = audio_generic_read,
.get_buffer_in = dsound_get_buffer_in,
.put_buffer_in = dsound_put_buffer_in,
.enable_in = dsound_enable_in,
.run_in = dsound_run_in,
.read = dsound_read,
.ctl_in = dsound_ctl_in
};
static struct audio_driver dsound_audio_driver = {
.name = "dsound",
.descr = "DirectSound http://wikipedia.org/wiki/DirectSound",
.options = dsound_options,
.init = dsound_audio_init,
.fini = dsound_audio_fini,
.pcm_ops = &dsound_pcm_ops,

View File

@@ -23,6 +23,7 @@
* THE SOFTWARE.
*/
#include "qemu/osdep.h"
#include "qemu-common.h"
#include "qemu/bswap.h"
#include "qemu/error-report.h"
#include "audio.h"

View File

@@ -33,7 +33,6 @@ struct st_sample { mixeng_real l; mixeng_real r; };
struct mixeng_volume { int mute; int64_t r; int64_t l; };
struct st_sample { int64_t l; int64_t r; };
#endif
typedef struct st_sample st_sample;
typedef void (t_sample) (struct st_sample *dst, const void *src, int samples);
typedef void (f_sample) (void *dst, const struct st_sample *src, int samples);
@@ -42,10 +41,10 @@ extern t_sample *mixeng_conv[2][2][2][3];
extern f_sample *mixeng_clip[2][2][2][3];
void *st_rate_start (int inrate, int outrate);
void st_rate_flow(void *opaque, st_sample *ibuf, st_sample *obuf,
size_t *isamp, size_t *osamp);
void st_rate_flow_mix(void *opaque, st_sample *ibuf, st_sample *obuf,
size_t *isamp, size_t *osamp);
void st_rate_flow (void *opaque, struct st_sample *ibuf, struct st_sample *obuf,
int *isamp, int *osamp);
void st_rate_flow_mix (void *opaque, struct st_sample *ibuf, struct st_sample *obuf,
int *isamp, int *osamp);
void st_rate_stop (void *opaque);
void mixeng_clear (struct st_sample *buf, int len);
void mixeng_volume (struct st_sample *buf, int len, struct mixeng_volume *vol);

View File

@@ -21,10 +21,9 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "qemu/osdep.h"
#include "qemu-common.h"
#include "qemu/host-utils.h"
#include "qemu/module.h"
#include "audio.h"
#include "qemu/timer.h"
@@ -33,27 +32,43 @@
typedef struct NoVoiceOut {
HWVoiceOut hw;
RateCtl rate;
int64_t old_ticks;
} NoVoiceOut;
typedef struct NoVoiceIn {
HWVoiceIn hw;
RateCtl rate;
int64_t old_ticks;
} NoVoiceIn;
static size_t no_write(HWVoiceOut *hw, void *buf, size_t len)
static int no_run_out (HWVoiceOut *hw, int live)
{
NoVoiceOut *no = (NoVoiceOut *) hw;
return audio_rate_get_bytes(&hw->info, &no->rate, len);
int decr, samples;
int64_t now;
int64_t ticks;
int64_t bytes;
now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
ticks = now - no->old_ticks;
bytes = muldiv64(ticks, hw->info.bytes_per_second, NANOSECONDS_PER_SECOND);
bytes = audio_MIN(bytes, INT_MAX);
samples = bytes >> hw->info.shift;
no->old_ticks = now;
decr = audio_MIN (live, samples);
hw->rpos = (hw->rpos + decr) % hw->samples;
return decr;
}
static int no_write (SWVoiceOut *sw, void *buf, int len)
{
return audio_pcm_sw_write(sw, buf, len);
}
static int no_init_out(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque)
{
NoVoiceOut *no = (NoVoiceOut *) hw;
audio_pcm_init_info (&hw->info, as);
hw->samples = 1024;
audio_rate_start(&no->rate);
return 0;
}
@@ -62,22 +77,17 @@ static void no_fini_out (HWVoiceOut *hw)
(void) hw;
}
static void no_enable_out(HWVoiceOut *hw, bool enable)
static int no_ctl_out (HWVoiceOut *hw, int cmd, ...)
{
NoVoiceOut *no = (NoVoiceOut *) hw;
if (enable) {
audio_rate_start(&no->rate);
}
(void) hw;
(void) cmd;
return 0;
}
static int no_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
{
NoVoiceIn *no = (NoVoiceIn *) hw;
audio_pcm_init_info (&hw->info, as);
hw->samples = 1024;
audio_rate_start(&no->rate);
return 0;
}
@@ -86,25 +96,47 @@ static void no_fini_in (HWVoiceIn *hw)
(void) hw;
}
static size_t no_read(HWVoiceIn *hw, void *buf, size_t size)
static int no_run_in (HWVoiceIn *hw)
{
NoVoiceIn *no = (NoVoiceIn *) hw;
int64_t bytes = audio_rate_get_bytes(&hw->info, &no->rate, size);
int live = audio_pcm_hw_get_live_in (hw);
int dead = hw->samples - live;
int samples = 0;
audio_pcm_info_clear_buf(&hw->info, buf, bytes / hw->info.bytes_per_frame);
return bytes;
}
if (dead) {
int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
int64_t ticks = now - no->old_ticks;
int64_t bytes =
muldiv64(ticks, hw->info.bytes_per_second, NANOSECONDS_PER_SECOND);
static void no_enable_in(HWVoiceIn *hw, bool enable)
{
NoVoiceIn *no = (NoVoiceIn *) hw;
if (enable) {
audio_rate_start(&no->rate);
no->old_ticks = now;
bytes = audio_MIN (bytes, INT_MAX);
samples = bytes >> hw->info.shift;
samples = audio_MIN (samples, dead);
}
return samples;
}
static void *no_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;
}
@@ -117,18 +149,21 @@ static void no_audio_fini (void *opaque)
static struct audio_pcm_ops no_pcm_ops = {
.init_out = no_init_out,
.fini_out = no_fini_out,
.run_out = no_run_out,
.write = no_write,
.enable_out = no_enable_out,
.ctl_out = no_ctl_out,
.init_in = no_init_in,
.fini_in = no_fini_in,
.run_in = no_run_in,
.read = no_read,
.enable_in = no_enable_in
.ctl_in = no_ctl_in
};
static struct audio_driver no_audio_driver = {
.name = "none",
.descr = "Timer based audio emulation",
.options = NULL,
.init = no_audio_init,
.fini = no_audio_fini,
.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
* THE SOFTWARE.
*/
#include "qemu/osdep.h"
#include <sys/ioctl.h>
#include <sys/soundcard.h>
#include "qemu-common.h"
#include "qemu/main-loop.h"
#include "qemu/module.h"
#include "qemu/host-utils.h"
#include "audio.h"
#include "trace.h"
@@ -38,26 +37,40 @@
#define USE_DSP_POLICY
#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 {
HWVoiceOut hw;
void *pcm_buf;
int fd;
int wpos;
int nfrags;
int fragsize;
int mmapped;
Audiodev *dev;
int pending;
OSSConf *conf;
} OSSVoiceOut;
typedef struct OSSVoiceIn {
HWVoiceIn hw;
void *pcm_buf;
int fd;
int nfrags;
int fragsize;
Audiodev *dev;
OSSConf *conf;
} OSSVoiceIn;
struct oss_params {
int freq;
int fmt;
audfmt_e fmt;
int nchannels;
int nfrags;
int fragsize;
@@ -106,40 +119,45 @@ static void oss_anal_close (int *fdp)
static void oss_helper_poll_out (void *opaque)
{
AudioState *s = opaque;
audio_run(s, "oss_poll_out");
(void) opaque;
audio_run ("oss_poll_out");
}
static void oss_helper_poll_in (void *opaque)
{
AudioState *s = opaque;
audio_run(s, "oss_poll_in");
(void) opaque;
audio_run ("oss_poll_in");
}
static void oss_poll_out (HWVoiceOut *hw)
{
OSSVoiceOut *oss = (OSSVoiceOut *) hw;
qemu_set_fd_handler(oss->fd, NULL, oss_helper_poll_out, hw->s);
qemu_set_fd_handler (oss->fd, NULL, oss_helper_poll_out, NULL);
}
static void oss_poll_in (HWVoiceIn *hw)
{
OSSVoiceIn *oss = (OSSVoiceIn *) hw;
qemu_set_fd_handler(oss->fd, oss_helper_poll_in, NULL, hw->s);
qemu_set_fd_handler (oss->fd, oss_helper_poll_in, NULL, NULL);
}
static int 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) {
case AUDIO_FORMAT_S8:
case AUD_FMT_S8:
return AFMT_S8;
case AUDIO_FORMAT_U8:
case AUD_FMT_U8:
return AFMT_U8;
case AUDIO_FORMAT_S16:
case AUD_FMT_S16:
if (endianness) {
return AFMT_S16_BE;
}
@@ -147,7 +165,7 @@ static int aud_to_ossfmt (AudioFormat fmt, int endianness)
return AFMT_S16_LE;
}
case AUDIO_FORMAT_U16:
case AUD_FMT_U16:
if (endianness) {
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) {
case AFMT_S8:
*endianness = 0;
*fmt = AUDIO_FORMAT_S8;
*fmt = AUD_FMT_S8;
break;
case AFMT_U8:
*endianness = 0;
*fmt = AUDIO_FORMAT_U8;
*fmt = AUD_FMT_U8;
break;
case AFMT_S16_LE:
*endianness = 0;
*fmt = AUDIO_FORMAT_S16;
*fmt = AUD_FMT_S16;
break;
case AFMT_U16_LE:
*endianness = 0;
*fmt = AUDIO_FORMAT_U16;
*fmt = AUD_FMT_U16;
break;
case AFMT_S16_BE:
*endianness = 1;
*fmt = AUDIO_FORMAT_S16;
*fmt = AUD_FMT_S16;
break;
case AFMT_U16_BE:
*endianness = 1;
*fmt = AUDIO_FORMAT_U16;
*fmt = AUD_FMT_U16;
break;
default:
@@ -244,25 +262,19 @@ static int oss_get_version (int fd, int *version, const char *typ)
}
#endif
static int oss_open(int in, struct oss_params *req, audsettings *as,
struct oss_params *obt, int *pfd, Audiodev *dev)
static int oss_open (int in, struct oss_params *req,
struct oss_params *obt, int *pfd, OSSConf* conf)
{
AudiodevOssOptions *oopts = &dev->u.oss;
AudiodevOssPerDirectionOptions *opdo = in ? oopts->in : oopts->out;
int fd;
int oflags = (oopts->has_exclusive && oopts->exclusive) ? O_EXCL : 0;
int oflags = conf->exclusive ? O_EXCL : 0;
audio_buf_info abinfo;
int fmt, freq, nchannels;
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";
#ifdef USE_DSP_POLICY
int policy = oopts->has_dsp_policy ? oopts->dsp_policy : 5;
#endif
/* Kludge needed to have working mmap on Linux */
oflags |= (oopts->has_try_mmap && oopts->try_mmap) ?
O_RDWR : (in ? O_RDONLY : O_WRONLY);
oflags |= conf->try_mmap ? O_RDWR : (in ? O_RDONLY : O_WRONLY);
fd = open (dspname, oflags | O_NONBLOCK);
if (-1 == fd) {
@@ -273,9 +285,6 @@ static int oss_open(int in, struct oss_params *req, audsettings *as,
freq = req->freq;
nchannels = req->nchannels;
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)) {
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
if (policy >= 0) {
if (conf->policy >= 0) {
int version;
if (!oss_get_version (fd, &version, typ)) {
trace_oss_version(version);
if (version >= 0x040000) {
int policy2 = policy;
if (ioctl(fd, SNDCTL_DSP_POLICY, &policy2)) {
int policy = conf->policy;
if (ioctl (fd, SNDCTL_DSP_POLICY, &policy)) {
oss_logerr2 (errno, typ,
"Failed to set timing policy to %d\n",
policy);
conf->policy);
goto err;
}
setfragment = 0;
@@ -367,87 +376,97 @@ static int oss_open(int in, struct oss_params *req, audsettings *as,
return -1;
}
static size_t oss_get_available_bytes(OSSVoiceOut *oss)
static void oss_write_pending (OSSVoiceOut *oss)
{
int err;
struct count_info cntinfo;
assert(oss->mmapped);
err = ioctl(oss->fd, SNDCTL_DSP_GETOPTR, &cntinfo);
if (err < 0) {
oss_logerr(errno, "SNDCTL_DSP_GETOPTR failed\n");
return 0;
}
return audio_ring_dist(cntinfo.ptr, oss->hw.pos_emul, oss->hw.size_emul);
}
static void *oss_get_buffer_out(HWVoiceOut *hw, size_t *size)
{
OSSVoiceOut *oss = (OSSVoiceOut *) hw;
if (oss->mmapped) {
*size = MIN(oss_get_available_bytes(oss), hw->size_emul - hw->pos_emul);
return hw->buf_emul + hw->pos_emul;
} else {
return audio_generic_get_buffer_out(hw, size);
}
}
static size_t oss_put_buffer_out(HWVoiceOut *hw, void *buf, size_t size)
{
OSSVoiceOut *oss = (OSSVoiceOut *) hw;
if (oss->mmapped) {
assert(buf == hw->buf_emul + hw->pos_emul && size < hw->size_emul);
hw->pos_emul = (hw->pos_emul + size) % hw->size_emul;
return size;
} else {
return audio_generic_put_buffer_out(hw, buf, size);
}
}
static size_t oss_write(HWVoiceOut *hw, void *buf, size_t len)
{
OSSVoiceOut *oss = (OSSVoiceOut *) hw;
size_t pos;
HWVoiceOut *hw = &oss->hw;
if (oss->mmapped) {
size_t total_len;
len = MIN(len, oss_get_available_bytes(oss));
total_len = len;
while (len) {
size_t to_copy = MIN(len, hw->size_emul - hw->pos_emul);
memcpy(hw->buf_emul + hw->pos_emul, buf, to_copy);
hw->pos_emul = (hw->pos_emul + to_copy) % hw->pos_emul;
buf += to_copy;
len -= to_copy;
}
return total_len;
return;
}
pos = 0;
while (len) {
while (oss->pending) {
int samples_written;
ssize_t bytes_written;
void *pcm = advance(buf, pos);
int samples_till_end = hw->samples - oss->wpos;
int samples_to_write = audio_MIN (oss->pending, samples_till_end);
int bytes_to_write = samples_to_write << hw->info.shift;
void *pcm = advance (oss->pcm_buf, oss->wpos << hw->info.shift);
bytes_written = write(oss->fd, pcm, len);
bytes_written = write (oss->fd, pcm, bytes_to_write);
if (bytes_written < 0) {
if (errno != EAGAIN) {
oss_logerr(errno, "failed to write %zu bytes\n",
len);
oss_logerr (errno, "failed to write %d bytes\n",
bytes_to_write);
}
return pos;
}
pos += bytes_written;
if (bytes_written < len) {
break;
}
len -= bytes_written;
if (bytes_written & hw->info.align) {
dolog ("misaligned write asked for %d, but got %zd\n",
bytes_to_write, bytes_written);
return;
}
samples_written = bytes_written >> hw->info.shift;
oss->pending -= samples_written;
oss->wpos = (oss->wpos + samples_written) % hw->samples;
if (bytes_written - bytes_to_write) {
break;
}
}
return pos;
}
static int oss_run_out (HWVoiceOut *hw, int live)
{
OSSVoiceOut *oss = (OSSVoiceOut *) hw;
int err, decr;
struct audio_buf_info abinfo;
struct count_info cntinfo;
int bufsize;
bufsize = hw->samples << hw->info.shift;
if (oss->mmapped) {
int bytes, pos;
err = ioctl (oss->fd, SNDCTL_DSP_GETOPTR, &cntinfo);
if (err < 0) {
oss_logerr (errno, "SNDCTL_DSP_GETOPTR failed\n");
return 0;
}
pos = hw->rpos << hw->info.shift;
bytes = audio_ring_dist (cntinfo.ptr, pos, bufsize);
decr = audio_MIN (bytes >> hw->info.shift, live);
}
else {
err = ioctl (oss->fd, SNDCTL_DSP_GETOSPACE, &abinfo);
if (err < 0) {
oss_logerr (errno, "SNDCTL_DSP_GETOPTR failed\n");
return 0;
}
if (abinfo.bytes > bufsize) {
trace_oss_invalid_available_size(abinfo.bytes, bufsize);
abinfo.bytes = bufsize;
}
if (abinfo.bytes < 0) {
trace_oss_invalid_available_size(abinfo.bytes, bufsize);
return 0;
}
decr = audio_MIN (abinfo.bytes >> hw->info.shift, live);
if (!decr) {
return 0;
}
}
decr = audio_pcm_hw_clip_out (hw, oss->pcm_buf, decr, oss->pending);
oss->pending += decr;
oss_write_pending (oss);
return decr;
}
static void oss_fini_out (HWVoiceOut *hw)
@@ -458,13 +477,18 @@ static void oss_fini_out (HWVoiceOut *hw)
ldebug ("oss_fini\n");
oss_anal_close (&oss->fd);
if (oss->mmapped && hw->buf_emul) {
err = munmap(hw->buf_emul, hw->size_emul);
if (err) {
oss_logerr(errno, "Failed to unmap buffer %p, size %zu\n",
hw->buf_emul, hw->size_emul);
if (oss->pcm_buf) {
if (oss->mmapped) {
err = munmap (oss->pcm_buf, hw->samples << hw->info.shift);
if (err) {
oss_logerr (errno, "Failed to unmap buffer %p, size %d\n",
oss->pcm_buf, hw->samples << hw->info.shift);
}
}
hw->buf_emul = NULL;
else {
g_free (oss->pcm_buf);
}
oss->pcm_buf = NULL;
}
}
@@ -476,18 +500,19 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as,
int endianness;
int err;
int fd;
AudioFormat effective_fmt;
audfmt_e effective_fmt;
struct audsettings obt_as;
Audiodev *dev = drv_opaque;
AudiodevOssOptions *oopts = &dev->u.oss;
OSSConf *conf = drv_opaque;
oss->fd = -1;
req.fmt = aud_to_ossfmt (as->fmt, as->endianness);
req.freq = as->freq;
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;
}
@@ -506,29 +531,28 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as,
oss->nfrags = obt.nfrags;
oss->fragsize = obt.fragsize;
if (obt.nfrags * obt.fragsize % hw->info.bytes_per_frame) {
if (obt.nfrags * obt.fragsize & hw->info.align) {
dolog ("warning: Misaligned DAC buffer, size %d, alignment %d\n",
obt.nfrags * obt.fragsize, hw->info.bytes_per_frame);
obt.nfrags * obt.fragsize, hw->info.align + 1);
}
hw->samples = (obt.nfrags * obt.fragsize) / hw->info.bytes_per_frame;
hw->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift;
oss->mmapped = 0;
if (oopts->has_try_mmap && oopts->try_mmap) {
hw->size_emul = hw->samples * hw->info.bytes_per_frame;
hw->buf_emul = mmap(
if (conf->try_mmap) {
oss->pcm_buf = mmap (
NULL,
hw->size_emul,
hw->samples << hw->info.shift,
PROT_READ | PROT_WRITE,
MAP_SHARED,
fd,
0
);
if (hw->buf_emul == MAP_FAILED) {
oss_logerr(errno, "Failed to map %zu bytes of DAC\n",
hw->size_emul);
hw->buf_emul = NULL;
} else {
if (oss->pcm_buf == MAP_FAILED) {
oss_logerr (errno, "Failed to map %d bytes of DAC\n",
hw->samples << hw->info.shift);
}
else {
int err;
int trig = 0;
if (ioctl (fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
@@ -548,65 +572,92 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as,
}
if (!oss->mmapped) {
err = munmap(hw->buf_emul, hw->size_emul);
err = munmap (oss->pcm_buf, hw->samples << hw->info.shift);
if (err) {
oss_logerr(errno, "Failed to unmap buffer %p size %zu\n",
hw->buf_emul, hw->size_emul);
oss_logerr (errno, "Failed to unmap buffer %p size %d\n",
oss->pcm_buf, hw->samples << hw->info.shift);
}
hw->buf_emul = NULL;
}
}
}
if (!oss->mmapped) {
oss->pcm_buf = audio_calloc(__func__,
hw->samples,
1 << hw->info.shift);
if (!oss->pcm_buf) {
dolog (
"Could not allocate DAC buffer (%d samples, each %d bytes)\n",
hw->samples,
1 << hw->info.shift
);
oss_anal_close (&fd);
return -1;
}
}
oss->fd = fd;
oss->dev = dev;
oss->conf = conf;
return 0;
}
static void oss_enable_out(HWVoiceOut *hw, bool enable)
static int oss_ctl_out (HWVoiceOut *hw, int cmd, ...)
{
int trig;
OSSVoiceOut *oss = (OSSVoiceOut *) hw;
AudiodevOssPerDirectionOptions *opdo = oss->dev->u.oss.out;
if (enable) {
bool poll_mode = opdo->try_poll;
switch (cmd) {
case VOICE_ENABLE:
{
va_list ap;
int poll_mode;
ldebug("enabling voice\n");
if (poll_mode) {
oss_poll_out(hw);
poll_mode = 0;
va_start (ap, cmd);
poll_mode = va_arg (ap, int);
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;
}
}
hw->poll_mode = poll_mode;
break;
if (!oss->mmapped) {
return;
}
audio_pcm_info_clear_buf(&hw->info, hw->buf_emul, hw->mix_buf->size);
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 {
case VOICE_DISABLE:
if (hw->poll_mode) {
qemu_set_fd_handler (oss->fd, NULL, NULL, NULL);
hw->poll_mode = 0;
}
if (!oss->mmapped) {
return;
return 0;
}
ldebug ("disabling voice\n");
trig = 0;
if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
oss_logerr (errno, "SNDCTL_DSP_SETTRIGGER 0 failed\n");
return;
return -1;
}
break;
}
return 0;
}
static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
@@ -616,16 +667,18 @@ static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
int endianness;
int err;
int fd;
AudioFormat effective_fmt;
audfmt_e effective_fmt;
struct audsettings obt_as;
Audiodev *dev = drv_opaque;
OSSConf *conf = drv_opaque;
oss->fd = -1;
req.fmt = aud_to_ossfmt (as->fmt, as->endianness);
req.freq = as->freq;
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;
}
@@ -644,15 +697,22 @@ static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
oss->nfrags = obt.nfrags;
oss->fragsize = obt.fragsize;
if (obt.nfrags * obt.fragsize % hw->info.bytes_per_frame) {
if (obt.nfrags * obt.fragsize & hw->info.align) {
dolog ("warning: Misaligned ADC buffer, size %d, alignment %d\n",
obt.nfrags * obt.fragsize, hw->info.bytes_per_frame);
obt.nfrags * obt.fragsize, hw->info.align + 1);
}
hw->samples = (obt.nfrags * obt.fragsize) / hw->info.bytes_per_frame;
hw->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift;
oss->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift);
if (!oss->pcm_buf) {
dolog ("Could not allocate ADC buffer (%d samples, each %d bytes)\n",
hw->samples, 1 << hw->info.shift);
oss_anal_close (&fd);
return -1;
}
oss->fd = fd;
oss->dev = dev;
oss->conf = conf;
return 0;
}
@@ -661,106 +721,211 @@ static void oss_fini_in (HWVoiceIn *hw)
OSSVoiceIn *oss = (OSSVoiceIn *) hw;
oss_anal_close (&oss->fd);
g_free(oss->pcm_buf);
oss->pcm_buf = NULL;
}
static size_t oss_read(HWVoiceIn *hw, void *buf, size_t len)
static int oss_run_in (HWVoiceIn *hw)
{
OSSVoiceIn *oss = (OSSVoiceIn *) hw;
size_t pos = 0;
int hwshift = hw->info.shift;
int i;
int live = audio_pcm_hw_get_live_in (hw);
int dead = hw->samples - live;
size_t read_samples = 0;
struct {
int add;
int len;
} bufs[2] = {
{ .add = hw->wpos, .len = 0 },
{ .add = 0, .len = 0 }
};
while (len) {
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;
void *dst = advance(buf, pos);
nread = read(oss->fd, dst, len);
if (bufs[i].len) {
void *p = advance (oss->pcm_buf, bufs[i].add << hwshift);
nread = read (oss->fd, p, bufs[i].len);
if (nread == -1) {
switch (errno) {
case EINTR:
case EAGAIN:
break;
default:
oss_logerr(errno, "Failed to read %zu bytes of audio (to %p)\n",
len, dst);
if (nread > 0) {
if (nread & hw->info.align) {
dolog ("warning: Misaligned read %zd (requested %d), "
"alignment %d\n", nread, bufs[i].add << hwshift,
hw->info.align + 1);
}
read_samples += nread >> hwshift;
hw->conv (hw->conv_buf + bufs[i].add, p, nread >> hwshift);
}
if (bufs[i].len - nread) {
if (nread == -1) {
switch (errno) {
case EINTR:
case EAGAIN:
break;
default:
oss_logerr (
errno,
"Failed to read %d bytes of audio (to %p)\n",
bufs[i].len, p
);
break;
}
}
break;
}
}
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;
AudiodevOssPerDirectionOptions *opdo = oss->dev->u.oss.out;
if (enable) {
bool poll_mode = opdo->try_poll;
switch (cmd) {
case VOICE_ENABLE:
{
va_list ap;
int poll_mode;
if (poll_mode) {
oss_poll_in(hw);
poll_mode = 0;
va_start (ap, cmd);
poll_mode = va_arg (ap, int);
va_end (ap);
if (poll_mode) {
oss_poll_in (hw);
poll_mode = 0;
}
hw->poll_mode = poll_mode;
}
hw->poll_mode = poll_mode;
} else {
break;
case VOICE_DISABLE:
if (hw->poll_mode) {
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) {
opdo->try_poll = true;
opdo->has_try_poll = true;
}
}
OSSConf *conf = g_malloc(sizeof(OSSConf));
*conf = glob_conf;
static void *oss_audio_init(Audiodev *dev)
{
AudiodevOssOptions *oopts;
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) {
if (access(conf->devpath_in, R_OK | W_OK) < 0 ||
access(conf->devpath_out, R_OK | W_OK) < 0) {
g_free(conf);
return NULL;
}
return dev;
return conf;
}
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 = {
.init_out = oss_init_out,
.fini_out = oss_fini_out,
.run_out = oss_run_out,
.write = oss_write,
.get_buffer_out = oss_get_buffer_out,
.put_buffer_out = oss_put_buffer_out,
.enable_out = oss_enable_out,
.ctl_out = oss_ctl_out,
.init_in = oss_init_in,
.fini_in = oss_fini_in,
.run_in = oss_run_in,
.read = oss_read,
.enable_in = oss_enable_in
.ctl_in = oss_ctl_in
};
static struct audio_driver oss_audio_driver = {
.name = "oss",
.descr = "OSS http://www.opensound.com",
.options = oss_options,
.init = oss_audio_init,
.fini = oss_audio_fini,
.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.
*/
void NAME (void *opaque, struct st_sample *ibuf, struct st_sample *obuf,
size_t *isamp, size_t *osamp)
int *isamp, int *osamp)
{
struct rate *rate = opaque;
struct st_sample *istart, *iend;

View File

@@ -21,11 +21,10 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "qemu/osdep.h"
#include <SDL.h>
#include <SDL_thread.h>
#include "qemu/module.h"
#include "qemu-common.h"
#include "audio.h"
#ifndef _WIN32
@@ -39,15 +38,31 @@
#define AUDIO_CAP "sdl"
#include "audio_int.h"
#define USE_SEMAPHORE (SDL_MAJOR_VERSION < 2)
typedef struct SDLVoiceOut {
HWVoiceOut hw;
int live;
#if USE_SEMAPHORE
int rpos;
#endif
int decr;
} SDLVoiceOut;
static struct {
int nb_samples;
} conf = {
.nb_samples = 1024
};
static struct SDLAudioState {
int exit;
#if USE_SEMAPHORE
SDL_mutex *mutex;
SDL_sem *sem;
#endif
int initialized;
bool driver_created;
Audiodev *dev;
} glob_sdl;
typedef struct SDLAudioState SDLAudioState;
@@ -62,19 +77,79 @@ static void GCC_FMT_ATTR (1, 2) sdl_logerr (const char *fmt, ...)
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) {
case AUDIO_FORMAT_S8:
case AUD_FMT_S8:
return AUDIO_S8;
case AUDIO_FORMAT_U8:
case AUD_FMT_U8:
return AUDIO_U8;
case AUDIO_FORMAT_S16:
case AUD_FMT_S16:
return AUDIO_S16LSB;
case AUDIO_FORMAT_U16:
case AUD_FMT_U16:
return AUDIO_U16LSB;
default:
@@ -86,37 +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) {
case AUDIO_S8:
*endianness = 0;
*fmt = AUDIO_FORMAT_S8;
*fmt = AUD_FMT_S8;
break;
case AUDIO_U8:
*endianness = 0;
*fmt = AUDIO_FORMAT_U8;
*fmt = AUD_FMT_U8;
break;
case AUDIO_S16LSB:
*endianness = 0;
*fmt = AUDIO_FORMAT_S16;
*fmt = AUD_FMT_S16;
break;
case AUDIO_U16LSB:
*endianness = 0;
*fmt = AUDIO_FORMAT_U16;
*fmt = AUD_FMT_U16;
break;
case AUDIO_S16MSB:
*endianness = 1;
*fmt = AUDIO_FORMAT_S16;
*fmt = AUD_FMT_S16;
break;
case AUDIO_U16MSB:
*endianness = 1;
*fmt = AUDIO_FORMAT_U16;
*fmt = AUD_FMT_U16;
break;
default:
@@ -168,9 +243,9 @@ static int sdl_open (SDL_AudioSpec *req, SDL_AudioSpec *obt)
static void sdl_close (SDLAudioState *s)
{
if (s->initialized) {
SDL_LockAudio();
sdl_lock (s, "sdl_close");
s->exit = 1;
SDL_UnlockAudio();
sdl_unlock_and_post (s, "sdl_close");
SDL_PauseAudio (1);
SDL_CloseAudio ();
s->initialized = 0;
@@ -182,59 +257,119 @@ static void sdl_callback (void *opaque, Uint8 *buf, int len)
SDLVoiceOut *sdl = opaque;
SDLAudioState *s = &glob_sdl;
HWVoiceOut *hw = &sdl->hw;
int samples = len >> hw->info.shift;
if (s->exit) {
return;
}
/* dolog ("in callback samples=%zu live=%zu\n", samples, sdl->live); */
while (samples) {
int to_mix, decr;
while (hw->pending_emul && len) {
size_t write_len;
ssize_t start = ((ssize_t) hw->pos_emul) - hw->pending_emul;
if (start < 0) {
start += hw->size_emul;
/* dolog ("in callback samples=%d\n", samples); */
#if USE_SEMAPHORE
sdl_wait (s, "sdl_callback");
if (s->exit) {
return;
}
assert(start >= 0 && start < hw->size_emul);
write_len = MIN(MIN(hw->pending_emul, len),
hw->size_emul - start);
if (sdl_lock (s, "sdl_callback")) {
return;
}
memcpy(buf, hw->buf_emul + start, write_len);
hw->pending_emul -= write_len;
len -= write_len;
buf += write_len;
if (audio_bug(__func__, sdl->live < 0 || sdl->live > hw->samples)) {
dolog ("sdl->live=%d hw->samples=%d\n",
sdl->live, hw->samples);
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 (len) {
memset(buf, 0, len);
#if (SDL_MAJOR_VERSION >= 2)
/* SDL2 does not clear the remaining buffer for us, so do it on our own */
if (samples) {
memset(buf, 0, samples << hw->info.shift);
}
#endif
}
#define SDL_WRAPPER_FUNC(name, ret_type, args_decl, args, fail, unlock) \
static ret_type glue(sdl_, name)args_decl \
{ \
ret_type ret; \
\
SDL_LockAudio(); \
\
ret = glue(audio_generic_, name)args; \
\
SDL_UnlockAudio(); \
return ret; \
static int sdl_write_out (SWVoiceOut *sw, void *buf, int len)
{
return audio_pcm_sw_write (sw, buf, len);
}
static int sdl_run_out (HWVoiceOut *hw, int live)
{
int decr;
SDLVoiceOut *sdl = (SDLVoiceOut *) hw;
SDLAudioState *s = &glob_sdl;
if (sdl_lock (s, "sdl_run_out")) {
return 0;
}
SDL_WRAPPER_FUNC(get_buffer_out, void *, (HWVoiceOut *hw, size_t *size),
(hw, size), *size = 0, sdl_unlock)
SDL_WRAPPER_FUNC(put_buffer_out_nowrite, size_t,
(HWVoiceOut *hw, void *buf, size_t size), (hw, buf, size),
/*nothing*/, sdl_unlock_and_post)
SDL_WRAPPER_FUNC(write, size_t,
(HWVoiceOut *hw, void *buf, size_t size), (hw, buf, size),
/*nothing*/, sdl_unlock_and_post)
if (sdl->decr > live) {
ldebug ("sdl->decr %d live %d sdl->live %d\n",
sdl->decr,
live,
sdl->live);
}
#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)
{
@@ -251,13 +386,13 @@ static int sdl_init_out(HWVoiceOut *hw, struct audsettings *as,
SDL_AudioSpec req, obt;
int endianness;
int err;
AudioFormat effective_fmt;
audfmt_e effective_fmt;
struct audsettings obt_as;
req.freq = as->freq;
req.format = aud_to_sdlfmt (as->fmt);
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.userdata = sdl;
@@ -285,12 +420,23 @@ static int sdl_init_out(HWVoiceOut *hw, struct audsettings *as,
return 0;
}
static void sdl_enable_out(HWVoiceOut *hw, bool enable)
static int sdl_ctl_out (HWVoiceOut *hw, int cmd, ...)
{
SDL_PauseAudio(!enable);
(void) hw;
switch (cmd) {
case VOICE_ENABLE:
SDL_PauseAudio (0);
break;
case VOICE_DISABLE:
SDL_PauseAudio (1);
break;
}
return 0;
}
static void *sdl_audio_init(Audiodev *dev)
static void *sdl_audio_init (void)
{
SDLAudioState *s = &glob_sdl;
if (s->driver_created) {
@@ -303,8 +449,24 @@ static void *sdl_audio_init(Audiodev *dev)
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->dev = dev;
return s;
}
@@ -312,23 +474,36 @@ static void sdl_audio_fini (void *opaque)
{
SDLAudioState *s = opaque;
sdl_close (s);
#if USE_SEMAPHORE
SDL_DestroySemaphore (s->sem);
SDL_DestroyMutex (s->mutex);
#endif
SDL_QuitSubSystem (SDL_INIT_AUDIO);
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 = {
.init_out = sdl_init_out,
.fini_out = sdl_fini_out,
.write = sdl_write,
.get_buffer_out = sdl_get_buffer_out,
.put_buffer_out = sdl_put_buffer_out_nowrite,
.enable_out = sdl_enable_out,
.run_out = sdl_run_out,
.write = sdl_write_out,
.ctl_out = sdl_ctl_out,
};
static struct audio_driver sdl_audio_driver = {
.name = "sdl",
.descr = "SDL http://www.libsdl.org",
.options = sdl_options,
.init = sdl_audio_init,
.fini = sdl_audio_fini,
.pcm_ops = &sdl_pcm_ops,

View File

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

View File

@@ -1,6 +1,6 @@
# See docs/devel/tracing.txt for syntax documentation.
# alsaaudio.c
# audio/alsaaudio.c
alsa_revents(int revents) "revents = %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"
@@ -12,11 +12,11 @@ alsa_resume_out(void) "Resuming suspended output stream"
alsa_resume_in(void) "Resuming suspended input stream"
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_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_stop(void) ""
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
* THE SOFTWARE.
*/
#include "qemu/osdep.h"
#include "qemu/host-utils.h"
#include "qemu/module.h"
#include "qemu/timer.h"
#include "qapi/opts-visitor.h"
#include "audio.h"
#define AUDIO_CAP "wav"
@@ -35,23 +32,63 @@
typedef struct WAVVoiceOut {
HWVoiceOut hw;
FILE *f;
RateCtl rate;
int64_t old_ticks;
void *pcm_buf;
int total_samples;
} WAVVoiceOut;
static size_t wav_write_out(HWVoiceOut *hw, void *buf, size_t len)
typedef struct {
struct audsettings settings;
const char *wav_path;
} WAVConf;
static int wav_run_out (HWVoiceOut *hw, int live)
{
WAVVoiceOut *wav = (WAVVoiceOut *) hw;
int64_t bytes = audio_rate_get_bytes(&hw->info, &wav->rate, len);
assert(bytes % hw->info.bytes_per_frame == 0);
int rpos, decr, samples;
uint8_t *dst;
struct st_sample *src;
int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
int64_t ticks = now - wav->old_ticks;
int64_t bytes =
muldiv64(ticks, hw->info.bytes_per_second, NANOSECONDS_PER_SECOND);
if (bytes && fwrite(buf, bytes, 1, wav->f) != 1) {
dolog("wav_write_out: fwrite of %" PRId64 " bytes failed\nReason: %s\n",
bytes, strerror(errno));
if (bytes > INT_MAX) {
samples = INT_MAX >> hw->info.shift;
}
else {
samples = bytes >> hw->info.shift;
}
wav->total_samples += bytes / hw->info.bytes_per_frame;
return bytes;
wav->old_ticks = now;
decr = audio_MIN (live, samples);
samples = decr;
rpos = hw->rpos;
while (samples) {
int left_till_end_samples = hw->samples - rpos;
int convert_samples = audio_MIN (samples, left_till_end_samples);
src = hw->mix_buf + rpos;
dst = advance (wav->pcm_buf, rpos << hw->info.shift);
hw->clip (dst, src, convert_samples);
if (fwrite (dst, convert_samples << hw->info.shift, 1, wav->f) != 1) {
dolog ("wav_run_out: fwrite of %d bytes failed\nReaons: %s\n",
convert_samples << hw->info.shift, strerror (errno));
}
rpos = (rpos + convert_samples) % hw->samples;
samples -= convert_samples;
wav->total_samples += convert_samples;
}
hw->rpos = rpos;
return decr;
}
static int wav_write_out (SWVoiceOut *sw, void *buf, int len)
{
return audio_pcm_sw_write (sw, buf, len);
}
/* VICE code: Store number as little endian. */
@@ -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,
0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00
};
Audiodev *dev = drv_opaque;
AudiodevWavOptions *wopts = &dev->u.wav;
struct audsettings wav_as = audiodev_to_audsettings(dev->u.wav.out);
const char *wav_path = wopts->has_path ? wopts->path : "qemu.wav";
WAVConf *conf = drv_opaque;
struct audsettings wav_as = conf->settings;
stereo = wav_as.nchannels == 2;
switch (wav_as.fmt) {
case AUDIO_FORMAT_S8:
case AUDIO_FORMAT_U8:
case AUD_FMT_S8:
case AUD_FMT_U8:
bits16 = 0;
break;
case AUDIO_FORMAT_S16:
case AUDIO_FORMAT_U16:
case AUD_FMT_S16:
case AUD_FMT_U16:
bits16 = 1;
break;
case AUDIO_FORMAT_S32:
case AUDIO_FORMAT_U32:
case AUD_FMT_S32:
case AUD_FMT_U32:
dolog ("WAVE files can not handle 32bit formats\n");
return -1;
default:
abort();
}
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);
hw->samples = 1024;
wav->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift);
if (!wav->pcm_buf) {
dolog ("Could not allocate buffer (%d bytes)\n",
hw->samples << hw->info.shift);
return -1;
}
le_store (hdr + 22, hw->info.nchannels, 2);
le_store (hdr + 24, hw->info.freq, 4);
le_store (hdr + 28, hw->info.freq << (bits16 + stereo), 4);
le_store (hdr + 32, 1 << (bits16 + stereo), 2);
wav->f = fopen(wav_path, "wb");
wav->f = fopen (conf->wav_path, "wb");
if (!wav->f) {
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;
}
@@ -124,8 +165,6 @@ static int wav_init_out(HWVoiceOut *hw, struct audsettings *as,
strerror(errno));
return -1;
}
audio_rate_start(&wav->rate);
return 0;
}
@@ -134,7 +173,7 @@ static void wav_fini_out (HWVoiceOut *hw)
WAVVoiceOut *wav = (WAVVoiceOut *) hw;
uint8_t rlen[4];
uint8_t dlen[4];
uint32_t datalen = wav->total_samples * hw->info.bytes_per_frame;
uint32_t datalen = wav->total_samples << hw->info.shift;
uint32_t rifflen = datalen + 36;
if (!wav->f) {
@@ -171,38 +210,78 @@ static void wav_fini_out (HWVoiceOut *hw)
wav->f, strerror (errno));
}
wav->f = NULL;
g_free (wav->pcm_buf);
wav->pcm_buf = NULL;
}
static void wav_enable_out(HWVoiceOut *hw, bool enable)
static int wav_ctl_out (HWVoiceOut *hw, int cmd, ...)
{
WAVVoiceOut *wav = (WAVVoiceOut *) hw;
if (enable) {
audio_rate_start(&wav->rate);
}
(void) hw;
(void) cmd;
return 0;
}
static void *wav_audio_init(Audiodev *dev)
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);
return dev;
WAVConf *conf = g_malloc(sizeof(WAVConf));
*conf = glob_conf;
return conf;
}
static void wav_audio_fini (void *opaque)
{
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 = {
.init_out = wav_init_out,
.fini_out = wav_fini_out,
.run_out = wav_run_out,
.write = wav_write_out,
.enable_out = wav_enable_out,
.ctl_out = wav_ctl_out,
};
static struct audio_driver wav_audio_driver = {
.name = "wav",
.descr = "WAV renderer http://wikipedia.org/wiki/WAV",
.options = wav_options,
.init = wav_audio_init,
.fini = wav_audio_fini,
.pcm_ops = &wav_pcm_ops,

View File

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

View File

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

View File

@@ -20,10 +20,9 @@
#include "qemu/osdep.h"
#include "authz/list.h"
#include "trace.h"
#include "authz/trace.h"
#include "qom/object_interfaces.h"
#include "qapi/qapi-visit-authz.h"
#include "qemu/module.h"
static bool qauthz_list_is_allowed(QAuthZ *authz,
const char *identity,

View File

@@ -20,10 +20,9 @@
#include "qemu/osdep.h"
#include "authz/listfile.h"
#include "trace.h"
#include "authz/trace.h"
#include "qemu/error-report.h"
#include "qemu/main-loop.h"
#include "qemu/module.h"
#include "qemu/sockets.h"
#include "qemu/filemonitor.h"
#include "qom/object_interfaces.h"
@@ -94,7 +93,7 @@ qauthz_list_file_load(QAuthZListFile *fauthz, Error **errp)
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,
const char *name G_GNUC_UNUSED,
void *opaque)

View File

@@ -20,8 +20,7 @@
#include "qemu/osdep.h"
#include "authz/pamacct.h"
#include "trace.h"
#include "qemu/module.h"
#include "authz/trace.h"
#include "qom/object_interfaces.h"
#include <security/pam_appl.h>

View File

@@ -20,8 +20,7 @@
#include "qemu/osdep.h"
#include "authz/simple.h"
#include "trace.h"
#include "qemu/module.h"
#include "authz/trace.h"
#include "qom/object_interfaces.h"
static bool qauthz_simple_is_allowed(QAuthZ *authz,

View File

@@ -1,18 +1,18 @@
# 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"
# 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"
# 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_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_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"

View File

@@ -1,4 +1,4 @@
common-obj-y += rng.o rng-egd.o rng-builtin.o
common-obj-y += rng.o rng-egd.o
common-obj-$(CONFIG_POSIX) += rng-random.o
common-obj-$(CONFIG_TPM) += tpm.o
@@ -9,11 +9,10 @@ common-obj-$(CONFIG_POSIX) += hostmem-file.o
common-obj-y += cryptodev.o
common-obj-y += cryptodev-builtin.o
ifeq ($(CONFIG_VIRTIO_CRYPTO),y)
ifeq ($(CONFIG_VIRTIO),y)
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
common-obj-$(call land,$(CONFIG_VHOST_USER),$(CONFIG_VIRTIO)) += vhost-user.o
common-obj-$(CONFIG_LINUX) += hostmem-memfd.o

View File

@@ -23,6 +23,7 @@
#include "qemu/osdep.h"
#include "sysemu/cryptodev.h"
#include "hw/boards.h"
#include "qapi/error.h"
#include "standard-headers/linux/virtio_crypto.h"
#include "crypto/cipher.h"

View File

@@ -22,6 +22,7 @@
*/
#include "qemu/osdep.h"
#include "hw/boards.h"
#include "qapi/error.h"
#include "qapi/qmp/qerror.h"
#include "qemu/error-report.h"
@@ -46,7 +47,7 @@
typedef struct CryptoDevBackendVhostUser {
CryptoDevBackend parent_obj;
VhostUserState vhost_user;
VhostUserState *vhost_user;
CharBackend chr;
char *chr_name;
bool opened;
@@ -103,7 +104,7 @@ cryptodev_vhost_user_start(int queues,
continue;
}
options.opaque = &s->vhost_user;
options.opaque = s->vhost_user;
options.backend_type = VHOST_BACKEND_TYPE_USER;
options.cc = b->conf.peers.ccs[i];
s->vhost_crypto[i] = cryptodev_vhost_init(&options);
@@ -181,6 +182,7 @@ static void cryptodev_vhost_user_init(
size_t i;
Error *local_err = NULL;
Chardev *chr;
VhostUserState *user;
CryptoDevBackendClient *cc;
CryptoDevBackendVhostUser *s =
CRYPTODEV_BACKEND_VHOST_USER(backend);
@@ -211,10 +213,15 @@ static void cryptodev_vhost_user_init(
}
}
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;
}
user->chr = &s->chr;
s->vhost_user = user;
qemu_chr_fe_set_handlers(&s->chr, NULL, NULL,
cryptodev_vhost_user_event, NULL, s, NULL, true);
@@ -300,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,

View File

@@ -23,6 +23,7 @@
#include "qemu/osdep.h"
#include "sysemu/cryptodev.h"
#include "hw/boards.h"
#include "qapi/error.h"
#include "qapi/visitor.h"
#include "qemu/config-file.h"

View File

@@ -9,11 +9,10 @@
* 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-common.h"
#include "qemu/error-report.h"
#include "qemu/module.h"
#include "sysemu/hostmem.h"
#include "sysemu/sysemu.h"
#include "qom/object_interfaces.h"
@@ -42,12 +41,10 @@ struct HostMemoryBackendFile {
static void
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);
#ifdef CONFIG_POSIX
gchar *name;
#endif
if (!backend->size) {
error_setg(errp, "can't create backend with size 0");
@@ -57,7 +54,9 @@ file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
error_setg(errp, "mem-path property not set");
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);
memory_region_init_ram_from_file(&backend->mr, OBJECT(backend),

View File

@@ -9,13 +9,12 @@
* 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-common.h"
#include "sysemu/hostmem.h"
#include "sysemu/sysemu.h"
#include "qom/object_interfaces.h"
#include "qemu/memfd.h"
#include "qemu/module.h"
#include "qapi/error.h"
#define TYPE_MEMORY_BACKEND_MEMFD "memory-backend-memfd"
@@ -155,13 +154,15 @@ memfd_backend_class_init(ObjectClass *oc, void *data)
"Huge pages size (ex: 2M, 1G)",
&error_abort);
}
object_class_property_add_bool(oc, "seal",
memfd_backend_get_seal,
memfd_backend_set_seal,
&error_abort);
object_class_property_set_description(oc, "seal",
"Seal growing & shrinking",
&error_abort);
if (qemu_memfd_check(MFD_ALLOW_SEALING)) {
object_class_property_add_bool(oc, "seal",
memfd_backend_get_seal,
memfd_backend_set_seal,
&error_abort);
object_class_property_set_description(oc, "seal",
"Seal growing & shrinking",
&error_abort);
}
}
static const TypeInfo memfd_backend_info = {
@@ -174,7 +175,7 @@ static const TypeInfo memfd_backend_info = {
static void register_types(void)
{
if (qemu_memfd_check(MFD_ALLOW_SEALING)) {
if (qemu_memfd_check(0)) {
type_register_static(&memfd_backend_info);
}
}

View File

@@ -9,11 +9,9 @@
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
#include "qemu/osdep.h"
#include "sysemu/hostmem.h"
#include "qapi/error.h"
#include "qemu/module.h"
#include "qom/object_interfaces.h"
#define TYPE_MEMORY_BACKEND_RAM "memory-backend-ram"

View File

@@ -12,7 +12,6 @@
#include "qemu/osdep.h"
#include "sysemu/hostmem.h"
#include "sysemu/sysemu.h"
#include "hw/boards.h"
#include "qapi/error.h"
#include "qapi/qapi-builtin-visit.h"
@@ -89,7 +88,7 @@ host_memory_backend_get_host_nodes(Object *obj, Visitor *v, const char *name,
value = find_first_bit(backend->host_nodes, MAX_NODES);
if (value == MAX_NODES) {
goto ret;
return;
}
*node = g_malloc0(sizeof(**node));
@@ -107,7 +106,6 @@ host_memory_backend_get_host_nodes(Object *obj, Visitor *v, const char *name,
node = &(*node)->next;
} while (true);
ret:
visit_type_uint16List(v, name, &host_nodes, errp);
}
@@ -223,7 +221,6 @@ static void host_memory_backend_set_prealloc(Object *obj, bool value,
{
Error *local_err = NULL;
HostMemoryBackend *backend = MEMORY_BACKEND(obj);
MachineState *ms = MACHINE(qdev_get_machine());
if (backend->force_prealloc) {
if (value) {
@@ -243,7 +240,7 @@ static void host_memory_backend_set_prealloc(Object *obj, bool value,
void *ptr = memory_region_get_ram_ptr(&backend->mr);
uint64_t sz = memory_region_size(&backend->mr);
os_mem_prealloc(fd, ptr, sz, ms->smp.cpus, &local_err);
os_mem_prealloc(fd, ptr, sz, smp_cpus, &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
@@ -304,7 +301,7 @@ size_t host_memory_backend_pagesize(HostMemoryBackend *memdev)
#else
size_t host_memory_backend_pagesize(HostMemoryBackend *memdev)
{
return qemu_real_host_page_size;
return getpagesize();
}
#endif
@@ -313,7 +310,6 @@ host_memory_backend_memory_complete(UserCreatable *uc, Error **errp)
{
HostMemoryBackend *backend = MEMORY_BACKEND(uc);
HostMemoryBackendClass *bc = MEMORY_BACKEND_GET_CLASS(uc);
MachineState *ms = MACHINE(qdev_get_machine());
Error *local_err = NULL;
void *ptr;
uint64_t sz;
@@ -378,7 +374,7 @@ host_memory_backend_memory_complete(UserCreatable *uc, Error **errp)
*/
if (backend->prealloc) {
os_mem_prealloc(memory_region_get_fd(&backend->mr), ptr, sz,
ms->smp.cpus, &local_err);
smp_cpus, &local_err);
if (local_err) {
goto out;
}

View File

@@ -1,77 +0,0 @@
/*
* QEMU Builtin Random Number Generator Backend
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
#include "qemu/osdep.h"
#include "sysemu/rng.h"
#include "qemu/main-loop.h"
#include "qemu/guest-random.h"
#define RNG_BUILTIN(obj) OBJECT_CHECK(RngBuiltin, (obj), TYPE_RNG_BUILTIN)
typedef struct RngBuiltin {
RngBackend parent;
QEMUBH *bh;
} RngBuiltin;
static void rng_builtin_receive_entropy_bh(void *opaque)
{
RngBuiltin *s = opaque;
while (!QSIMPLEQ_EMPTY(&s->parent.requests)) {
RngRequest *req = QSIMPLEQ_FIRST(&s->parent.requests);
qemu_guest_getrandom_nofail(req->data, req->size);
req->receive_entropy(req->opaque, req->data, req->size);
rng_backend_finalize_request(&s->parent, req);
}
}
static void rng_builtin_request_entropy(RngBackend *b, RngRequest *req)
{
RngBuiltin *s = RNG_BUILTIN(b);
qemu_bh_schedule(s->bh);
}
static void rng_builtin_init(Object *obj)
{
RngBuiltin *s = RNG_BUILTIN(obj);
s->bh = qemu_bh_new(rng_builtin_receive_entropy_bh, s);
}
static void rng_builtin_finalize(Object *obj)
{
RngBuiltin *s = RNG_BUILTIN(obj);
qemu_bh_delete(s->bh);
}
static void rng_builtin_class_init(ObjectClass *klass, void *data)
{
RngBackendClass *rbc = RNG_BACKEND_CLASS(klass);
rbc->request_entropy = rng_builtin_request_entropy;
}
static const TypeInfo rng_builtin_info = {
.name = TYPE_RNG_BUILTIN,
.parent = TYPE_RNG_BACKEND,
.instance_size = sizeof(RngBuiltin),
.instance_init = rng_builtin_init,
.instance_finalize = rng_builtin_finalize,
.class_init = rng_builtin_class_init,
};
static void register_types(void)
{
type_register_static(&rng_builtin_info);
}
type_init(register_types);

View File

@@ -15,7 +15,6 @@
#include "chardev/char-fe.h"
#include "qapi/error.h"
#include "qapi/qmp/qerror.h"
#include "qemu/module.h"
#define TYPE_RNG_EGD "rng-egd"
#define RNG_EGD(obj) OBJECT_CHECK(RngEgd, (obj), TYPE_RNG_EGD)

View File

@@ -16,7 +16,6 @@
#include "qapi/error.h"
#include "qapi/qmp/qerror.h"
#include "qemu/main-loop.h"
#include "qemu/module.h"
struct RngRandom
{
@@ -113,7 +112,7 @@ static void rng_random_init(Object *obj)
rng_random_set_filename,
NULL);
s->filename = g_strdup("/dev/urandom");
s->filename = g_strdup("/dev/random");
s->fd = -1;
}

View File

@@ -14,7 +14,6 @@
#include "sysemu/rng.h"
#include "qapi/error.h"
#include "qapi/qmp/qerror.h"
#include "qemu/module.h"
#include "qom/object_interfaces.h"
void rng_backend_request_entropy(RngBackend *s, size_t size,

View File

@@ -18,7 +18,6 @@
#include "sysemu/tpm.h"
#include "qemu/thread.h"
#include "qemu/main-loop.h"
#include "qemu/module.h"
#include "block/thread-pool.h"
#include "qemu/error-report.h"

View File

@@ -1,208 +0,0 @@
/*
* QEMU vhost-user backend
*
* Copyright (C) 2018 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 "qapi/error.h"
#include "qapi/qmp/qerror.h"
#include "qemu/error-report.h"
#include "qom/object_interfaces.h"
#include "sysemu/vhost-user-backend.h"
#include "sysemu/kvm.h"
#include "io/channel-command.h"
#include "hw/virtio/virtio-bus.h"
static bool
ioeventfd_enabled(void)
{
return kvm_enabled() && kvm_eventfds_enabled();
}
int
vhost_user_backend_dev_init(VhostUserBackend *b, VirtIODevice *vdev,
unsigned nvqs, Error **errp)
{
int ret;
assert(!b->vdev && vdev);
if (!ioeventfd_enabled()) {
error_setg(errp, "vhost initialization failed: requires kvm");
return -1;
}
if (!vhost_user_init(&b->vhost_user, &b->chr, errp)) {
return -1;
}
b->vdev = vdev;
b->dev.nvqs = nvqs;
b->dev.vqs = g_new0(struct vhost_virtqueue, nvqs);
ret = vhost_dev_init(&b->dev, &b->vhost_user, VHOST_BACKEND_TYPE_USER, 0);
if (ret < 0) {
error_setg_errno(errp, -ret, "vhost initialization failed");
return -1;
}
return 0;
}
void
vhost_user_backend_start(VhostUserBackend *b)
{
BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(b->vdev)));
VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
int ret, i ;
if (b->started) {
return;
}
if (!k->set_guest_notifiers) {
error_report("binding does not support guest notifiers");
return;
}
ret = vhost_dev_enable_notifiers(&b->dev, b->vdev);
if (ret < 0) {
return;
}
ret = k->set_guest_notifiers(qbus->parent, b->dev.nvqs, true);
if (ret < 0) {
error_report("Error binding guest notifier");
goto err_host_notifiers;
}
b->dev.acked_features = b->vdev->guest_features;
ret = vhost_dev_start(&b->dev, b->vdev);
if (ret < 0) {
error_report("Error start vhost dev");
goto err_guest_notifiers;
}
/* guest_notifier_mask/pending not used yet, so just unmask
* everything here. virtio-pci will do the right thing by
* enabling/disabling irqfd.
*/
for (i = 0; i < b->dev.nvqs; i++) {
vhost_virtqueue_mask(&b->dev, b->vdev,
b->dev.vq_index + i, false);
}
b->started = true;
return;
err_guest_notifiers:
k->set_guest_notifiers(qbus->parent, b->dev.nvqs, false);
err_host_notifiers:
vhost_dev_disable_notifiers(&b->dev, b->vdev);
}
void
vhost_user_backend_stop(VhostUserBackend *b)
{
BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(b->vdev)));
VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
int ret = 0;
if (!b->started) {
return;
}
vhost_dev_stop(&b->dev, b->vdev);
if (k->set_guest_notifiers) {
ret = k->set_guest_notifiers(qbus->parent,
b->dev.nvqs, false);
if (ret < 0) {
error_report("vhost guest notifier cleanup failed: %d", ret);
}
}
assert(ret >= 0);
vhost_dev_disable_notifiers(&b->dev, b->vdev);
b->started = false;
}
static void set_chardev(Object *obj, const char *value, Error **errp)
{
VhostUserBackend *b = VHOST_USER_BACKEND(obj);
Chardev *chr;
if (b->completed) {
error_setg(errp, QERR_PERMISSION_DENIED);
return;
}
g_free(b->chr_name);
b->chr_name = g_strdup(value);
chr = qemu_chr_find(b->chr_name);
if (chr == NULL) {
error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
"Chardev '%s' not found", b->chr_name);
return;
}
if (!qemu_chr_fe_init(&b->chr, chr, errp)) {
return;
}
b->completed = true;
/* could call vhost_dev_init() so early message can be exchanged */
}
static char *get_chardev(Object *obj, Error **errp)
{
VhostUserBackend *b = VHOST_USER_BACKEND(obj);
Chardev *chr = qemu_chr_fe_get_driver(&b->chr);
if (chr && chr->label) {
return g_strdup(chr->label);
}
return NULL;
}
static void vhost_user_backend_init(Object *obj)
{
object_property_add_str(obj, "chardev", get_chardev, set_chardev, NULL);
}
static void vhost_user_backend_finalize(Object *obj)
{
VhostUserBackend *b = VHOST_USER_BACKEND(obj);
g_free(b->dev.vqs);
g_free(b->chr_name);
vhost_user_cleanup(&b->vhost_user);
qemu_chr_fe_deinit(&b->chr, true);
}
static const TypeInfo vhost_user_backend_info = {
.name = TYPE_VHOST_USER_BACKEND,
.parent = TYPE_OBJECT,
.instance_size = sizeof(VhostUserBackend),
.instance_init = vhost_user_backend_init,
.instance_finalize = vhost_user_backend_finalize,
.class_size = sizeof(VhostUserBackendClass),
};
static void register_types(void)
{
type_register_static(&vhost_user_backend_info);
}
type_init(register_types);

View File

@@ -25,7 +25,9 @@
*/
#include "qemu/osdep.h"
#include "qemu-common.h"
#include "qemu/atomic.h"
#include "exec/cpu-common.h"
#include "sysemu/kvm.h"
#include "sysemu/balloon.h"
#include "trace-root.h"

1230
block.c

File diff suppressed because it is too large Load Diff

View File

@@ -6,7 +6,7 @@ block-obj-$(CONFIG_BOCHS) += bochs.o
block-obj-$(CONFIG_VVFAT) += vvfat.o
block-obj-$(CONFIG_DMG) += dmg.o
block-obj-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o qcow2-cache.o qcow2-bitmap.o qcow2-threads.o
block-obj-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o qcow2-cache.o qcow2-bitmap.o
block-obj-$(CONFIG_QED) += qed.o qed-l2-cache.o qed-table.o qed-cluster.o
block-obj-$(CONFIG_QED) += qed-check.o
block-obj-y += vhdx.o vhdx-endian.o vhdx-log.o
@@ -22,7 +22,7 @@ block-obj-y += null.o mirror.o commit.o io.o create.o
block-obj-y += throttle-groups.o
block-obj-$(CONFIG_LINUX) += nvme.o
block-obj-y += nbd.o
block-obj-y += nbd.o nbd-client.o
block-obj-$(CONFIG_SHEEPDOG) += sheepdog.o
block-obj-$(CONFIG_LIBISCSI) += iscsi.o
block-obj-$(if $(CONFIG_LIBISCSI),y,n) += iscsi-opts.o
@@ -31,19 +31,15 @@ block-obj-$(CONFIG_CURL) += curl.o
block-obj-$(CONFIG_RBD) += rbd.o
block-obj-$(CONFIG_GLUSTERFS) += gluster.o
block-obj-$(CONFIG_VXHS) += vxhs.o
block-obj-$(CONFIG_LIBSSH) += ssh.o
block-obj-$(CONFIG_LIBSSH2) += ssh.o
block-obj-y += accounting.o dirty-bitmap.o
block-obj-y += write-threshold.o
block-obj-y += backup.o
block-obj-$(CONFIG_REPLICATION) += replication.o
block-obj-y += throttle.o copy-on-read.o
block-obj-y += block-copy.o
block-obj-y += crypto.o
block-obj-y += aio_task.o
block-obj-y += backup-top.o
common-obj-y += stream.o
nfs.o-libs := $(LIBNFS_LIBS)
@@ -56,8 +52,8 @@ rbd.o-libs := $(RBD_LIBS)
gluster.o-cflags := $(GLUSTERFS_CFLAGS)
gluster.o-libs := $(GLUSTERFS_LIBS)
vxhs.o-libs := $(VXHS_LIBS)
ssh.o-cflags := $(LIBSSH_CFLAGS)
ssh.o-libs := $(LIBSSH_LIBS)
ssh.o-cflags := $(LIBSSH2_CFLAGS)
ssh.o-libs := $(LIBSSH2_LIBS)
block-obj-dmg-bz2-$(CONFIG_BZIP2) += dmg-bz2.o
block-obj-$(if $(CONFIG_DMG),m,n) += $(block-obj-dmg-bz2-y)
dmg-bz2.o-libs := $(BZIP2_LIBS)

View File

@@ -195,10 +195,6 @@ static void block_account_one_io(BlockAcctStats *stats, BlockAcctCookie *cookie,
assert(cookie->type < BLOCK_MAX_IOTYPE);
if (cookie->type == BLOCK_ACCT_NONE) {
return;
}
qemu_mutex_lock(&stats->lock);
if (failed) {
@@ -221,8 +217,6 @@ static void block_account_one_io(BlockAcctStats *stats, BlockAcctCookie *cookie,
}
qemu_mutex_unlock(&stats->lock);
cookie->type = BLOCK_ACCT_NONE;
}
void block_acct_done(BlockAcctStats *stats, BlockAcctCookie *cookie)

View File

@@ -1,124 +0,0 @@
/*
* Aio tasks loops
*
* Copyright (c) 2019 Virtuozzo International GmbH.
*
* 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 "block/aio.h"
#include "block/aio_task.h"
struct AioTaskPool {
Coroutine *main_co;
int status;
int max_busy_tasks;
int busy_tasks;
bool waiting;
};
static void coroutine_fn aio_task_co(void *opaque)
{
AioTask *task = opaque;
AioTaskPool *pool = task->pool;
assert(pool->busy_tasks < pool->max_busy_tasks);
pool->busy_tasks++;
task->ret = task->func(task);
pool->busy_tasks--;
if (task->ret < 0 && pool->status == 0) {
pool->status = task->ret;
}
g_free(task);
if (pool->waiting) {
pool->waiting = false;
aio_co_wake(pool->main_co);
}
}
void coroutine_fn aio_task_pool_wait_one(AioTaskPool *pool)
{
assert(pool->busy_tasks > 0);
assert(qemu_coroutine_self() == pool->main_co);
pool->waiting = true;
qemu_coroutine_yield();
assert(!pool->waiting);
assert(pool->busy_tasks < pool->max_busy_tasks);
}
void coroutine_fn aio_task_pool_wait_slot(AioTaskPool *pool)
{
if (pool->busy_tasks < pool->max_busy_tasks) {
return;
}
aio_task_pool_wait_one(pool);
}
void coroutine_fn aio_task_pool_wait_all(AioTaskPool *pool)
{
while (pool->busy_tasks > 0) {
aio_task_pool_wait_one(pool);
}
}
void coroutine_fn aio_task_pool_start_task(AioTaskPool *pool, AioTask *task)
{
aio_task_pool_wait_slot(pool);
task->pool = pool;
qemu_coroutine_enter(qemu_coroutine_create(aio_task_co, task));
}
AioTaskPool *coroutine_fn aio_task_pool_new(int max_busy_tasks)
{
AioTaskPool *pool = g_new0(AioTaskPool, 1);
pool->main_co = qemu_coroutine_self();
pool->max_busy_tasks = max_busy_tasks;
return pool;
}
void aio_task_pool_free(AioTaskPool *pool)
{
g_free(pool);
}
int aio_task_pool_status(AioTaskPool *pool)
{
if (!pool) {
return 0; /* Sugar for lazy allocation of aio pool */
}
return pool->status;
}
bool aio_task_pool_empty(AioTaskPool *pool)
{
return pool->busy_tasks == 0;
}

View File

@@ -1,276 +0,0 @@
/*
* backup-top filter driver
*
* The driver performs Copy-Before-Write (CBW) operation: it is injected above
* some node, and before each write it copies _old_ data to the target node.
*
* Copyright (c) 2018-2019 Virtuozzo International GmbH.
*
* Author:
* Sementsov-Ogievskiy Vladimir <vsementsov@virtuozzo.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "qemu/osdep.h"
#include "sysemu/block-backend.h"
#include "qemu/cutils.h"
#include "qapi/error.h"
#include "block/block_int.h"
#include "block/qdict.h"
#include "block/block-copy.h"
#include "block/backup-top.h"
typedef struct BDRVBackupTopState {
BlockCopyState *bcs;
BdrvChild *target;
bool active;
} BDRVBackupTopState;
static coroutine_fn int backup_top_co_preadv(
BlockDriverState *bs, uint64_t offset, uint64_t bytes,
QEMUIOVector *qiov, int flags)
{
return bdrv_co_preadv(bs->backing, offset, bytes, qiov, flags);
}
static coroutine_fn int backup_top_cbw(BlockDriverState *bs, uint64_t offset,
uint64_t bytes)
{
BDRVBackupTopState *s = bs->opaque;
uint64_t end = QEMU_ALIGN_UP(offset + bytes, s->bcs->cluster_size);
uint64_t off = QEMU_ALIGN_DOWN(offset, s->bcs->cluster_size);
return block_copy(s->bcs, off, end - off, NULL);
}
static int coroutine_fn backup_top_co_pdiscard(BlockDriverState *bs,
int64_t offset, int bytes)
{
int ret = backup_top_cbw(bs, offset, bytes);
if (ret < 0) {
return ret;
}
return bdrv_co_pdiscard(bs->backing, offset, bytes);
}
static int coroutine_fn backup_top_co_pwrite_zeroes(BlockDriverState *bs,
int64_t offset, int bytes, BdrvRequestFlags flags)
{
int ret = backup_top_cbw(bs, offset, bytes);
if (ret < 0) {
return ret;
}
return bdrv_co_pwrite_zeroes(bs->backing, offset, bytes, flags);
}
static coroutine_fn int backup_top_co_pwritev(BlockDriverState *bs,
uint64_t offset,
uint64_t bytes,
QEMUIOVector *qiov, int flags)
{
if (!(flags & BDRV_REQ_WRITE_UNCHANGED)) {
int ret = backup_top_cbw(bs, offset, bytes);
if (ret < 0) {
return ret;
}
}
return bdrv_co_pwritev(bs->backing, offset, bytes, qiov, flags);
}
static int coroutine_fn backup_top_co_flush(BlockDriverState *bs)
{
if (!bs->backing) {
return 0;
}
return bdrv_co_flush(bs->backing->bs);
}
static void backup_top_refresh_filename(BlockDriverState *bs)
{
if (bs->backing == NULL) {
/*
* we can be here after failed bdrv_attach_child in
* bdrv_set_backing_hd
*/
return;
}
pstrcpy(bs->exact_filename, sizeof(bs->exact_filename),
bs->backing->bs->filename);
}
static void backup_top_child_perm(BlockDriverState *bs, BdrvChild *c,
const BdrvChildRole *role,
BlockReopenQueue *reopen_queue,
uint64_t perm, uint64_t shared,
uint64_t *nperm, uint64_t *nshared)
{
BDRVBackupTopState *s = bs->opaque;
if (!s->active) {
/*
* The filter node may be in process of bdrv_append(), which firstly do
* bdrv_set_backing_hd() and then bdrv_replace_node(). This means that
* we can't unshare BLK_PERM_WRITE during bdrv_append() operation. So,
* let's require nothing during bdrv_append() and refresh permissions
* after it (see bdrv_backup_top_append()).
*/
*nperm = 0;
*nshared = BLK_PERM_ALL;
return;
}
if (role == &child_file) {
/*
* Target child
*
* Share write to target (child_file), to not interfere
* with guest writes to its disk which may be in target backing chain.
*/
*nshared = BLK_PERM_ALL;
*nperm = BLK_PERM_WRITE;
} else {
/* Source child */
bdrv_filter_default_perms(bs, c, role, reopen_queue, perm, shared,
nperm, nshared);
if (perm & BLK_PERM_WRITE) {
*nperm = *nperm | BLK_PERM_CONSISTENT_READ;
}
*nshared &= ~BLK_PERM_WRITE;
}
}
BlockDriver bdrv_backup_top_filter = {
.format_name = "backup-top",
.instance_size = sizeof(BDRVBackupTopState),
.bdrv_co_preadv = backup_top_co_preadv,
.bdrv_co_pwritev = backup_top_co_pwritev,
.bdrv_co_pwrite_zeroes = backup_top_co_pwrite_zeroes,
.bdrv_co_pdiscard = backup_top_co_pdiscard,
.bdrv_co_flush = backup_top_co_flush,
.bdrv_co_block_status = bdrv_co_block_status_from_backing,
.bdrv_refresh_filename = backup_top_refresh_filename,
.bdrv_child_perm = backup_top_child_perm,
.is_filter = true,
};
BlockDriverState *bdrv_backup_top_append(BlockDriverState *source,
BlockDriverState *target,
const char *filter_node_name,
uint64_t cluster_size,
BdrvRequestFlags write_flags,
BlockCopyState **bcs,
Error **errp)
{
Error *local_err = NULL;
BDRVBackupTopState *state;
BlockDriverState *top = bdrv_new_open_driver(&bdrv_backup_top_filter,
filter_node_name,
BDRV_O_RDWR, errp);
if (!top) {
return NULL;
}
top->total_sectors = source->total_sectors;
top->opaque = state = g_new0(BDRVBackupTopState, 1);
bdrv_ref(target);
state->target = bdrv_attach_child(top, target, "target", &child_file, errp);
if (!state->target) {
bdrv_unref(target);
bdrv_unref(top);
return NULL;
}
bdrv_drained_begin(source);
bdrv_ref(top);
bdrv_append(top, source, &local_err);
if (local_err) {
error_prepend(&local_err, "Cannot append backup-top filter: ");
goto append_failed;
}
/*
* bdrv_append() finished successfully, now we can require permissions
* we want.
*/
state->active = true;
bdrv_child_refresh_perms(top, top->backing, &local_err);
if (local_err) {
error_prepend(&local_err,
"Cannot set permissions for backup-top filter: ");
goto failed_after_append;
}
state->bcs = block_copy_state_new(top->backing, state->target,
cluster_size, write_flags, &local_err);
if (local_err) {
error_prepend(&local_err, "Cannot create block-copy-state: ");
goto failed_after_append;
}
*bcs = state->bcs;
bdrv_drained_end(source);
return top;
failed_after_append:
state->active = false;
bdrv_backup_top_drop(top);
append_failed:
bdrv_drained_end(source);
bdrv_unref_child(top, state->target);
bdrv_unref(top);
error_propagate(errp, local_err);
return NULL;
}
void bdrv_backup_top_drop(BlockDriverState *bs)
{
BDRVBackupTopState *s = bs->opaque;
AioContext *aio_context = bdrv_get_aio_context(bs);
block_copy_state_free(s->bcs);
aio_context_acquire(aio_context);
bdrv_drained_begin(bs);
s->active = false;
bdrv_child_refresh_perms(bs, bs->backing, &error_abort);
bdrv_replace_node(bs, backing_bs(bs), &error_abort);
bdrv_set_backing_hd(bs, NULL, &error_abort);
bdrv_drained_end(bs);
bdrv_unref(bs);
aio_context_release(aio_context);
}

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