Compare commits
	
		
			5 Commits
		
	
	
		
			v4.2.0-rc1
			...
			rm-protoco
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 6321d78dc5 | ||
|  | a0974f99aa | ||
|  | 42883c01bf | ||
|  | 7966c2b312 | ||
|  | 5888011244 | 
							
								
								
									
										38
									
								
								.cirrus.yml
									
									
									
									
									
								
							
							
						
						
									
										38
									
								
								.cirrus.yml
									
									
									
									
									
								
							| @@ -1,38 +0,0 @@ | |||||||
| env: |  | ||||||
|   CIRRUS_CLONE_DEPTH: 1 |  | ||||||
|  |  | ||||||
| freebsd_12_task: |  | ||||||
|   freebsd_instance: |  | ||||||
|     image: freebsd-12-0-release-amd64 |  | ||||||
|     cpu: 8 |  | ||||||
|     memory: 8G |  | ||||||
|   install_script: pkg install -y |  | ||||||
|     bash bison curl cyrus-sasl git glib gmake gnutls gsed |  | ||||||
|     nettle perl5 pixman pkgconf png usbredir |  | ||||||
|   script: |  | ||||||
|     - mkdir build |  | ||||||
|     - cd build |  | ||||||
|     - ../configure || { cat config.log; exit 1; } |  | ||||||
|     - gmake -j8 |  | ||||||
|     - gmake -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) |  | ||||||
| @@ -1,10 +1,4 @@ | |||||||
| # EditorConfig is a file format and collection of text editor plugins | # http://editorconfig.org | ||||||
| # for maintaining consistent coding styles between different editors |  | ||||||
| # and IDEs. Most popular editors support this either natively or via |  | ||||||
| # plugin. |  | ||||||
| # |  | ||||||
| # Check https://editorconfig.org for details. |  | ||||||
|  |  | ||||||
| root = true | root = true | ||||||
|  |  | ||||||
| [*] | [*] | ||||||
| @@ -12,32 +6,10 @@ end_of_line = lf | |||||||
| insert_final_newline = true | insert_final_newline = true | ||||||
| charset = utf-8 | charset = utf-8 | ||||||
|  |  | ||||||
| [*.mak] |  | ||||||
| indent_style = tab |  | ||||||
| indent_size = 8 |  | ||||||
| file_type_emacs = makefile |  | ||||||
|  |  | ||||||
| [Makefile*] | [Makefile*] | ||||||
| indent_style = tab | indent_style = tab | ||||||
| indent_size = 8 | indent_size = 8 | ||||||
| file_type_emacs = makefile |  | ||||||
|  |  | ||||||
| [*.{c,h}] | [*.{c,h}] | ||||||
| indent_style = space | indent_style = space | ||||||
| indent_size = 4 | indent_size = 4 | ||||||
|  |  | ||||||
| [*.sh] |  | ||||||
| indent_style = space |  | ||||||
| indent_size = 4 |  | ||||||
|  |  | ||||||
| [*.{s,S}] |  | ||||||
| indent_style = tab |  | ||||||
| indent_size = 8 |  | ||||||
| file_type_emacs = asm |  | ||||||
|  |  | ||||||
| [*.{vert,frag}] |  | ||||||
| file_type_emacs = glsl |  | ||||||
|  |  | ||||||
| [*.json] |  | ||||||
| indent_style = space |  | ||||||
| file_type_emacs = python |  | ||||||
|   | |||||||
							
								
								
									
										84
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										84
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1,4 +1,3 @@ | |||||||
| /.doctrees |  | ||||||
| /config-devices.* | /config-devices.* | ||||||
| /config-all-devices.* | /config-all-devices.* | ||||||
| /config-all-disas.* | /config-all-disas.* | ||||||
| @@ -6,7 +5,6 @@ | |||||||
| /config-target.* | /config-target.* | ||||||
| /config.status | /config.status | ||||||
| /config-temp | /config-temp | ||||||
| /elf2dmp |  | ||||||
| /trace-events-all | /trace-events-all | ||||||
| /trace/generated-events.h | /trace/generated-events.h | ||||||
| /trace/generated-events.c | /trace/generated-events.c | ||||||
| @@ -32,22 +30,79 @@ | |||||||
| /qapi-gen-timestamp | /qapi-gen-timestamp | ||||||
| /qapi/qapi-builtin-types.[ch] | /qapi/qapi-builtin-types.[ch] | ||||||
| /qapi/qapi-builtin-visit.[ch] | /qapi/qapi-builtin-visit.[ch] | ||||||
| /qapi/qapi-commands-*.[ch] | /qapi/qapi-commands-block-core.[ch] | ||||||
|  | /qapi/qapi-commands-block.[ch] | ||||||
|  | /qapi/qapi-commands-char.[ch] | ||||||
|  | /qapi/qapi-commands-common.[ch] | ||||||
|  | /qapi/qapi-commands-crypto.[ch] | ||||||
|  | /qapi/qapi-commands-introspect.[ch] | ||||||
|  | /qapi/qapi-commands-migration.[ch] | ||||||
|  | /qapi/qapi-commands-misc.[ch] | ||||||
|  | /qapi/qapi-commands-net.[ch] | ||||||
|  | /qapi/qapi-commands-rocker.[ch] | ||||||
|  | /qapi/qapi-commands-run-state.[ch] | ||||||
|  | /qapi/qapi-commands-sockets.[ch] | ||||||
|  | /qapi/qapi-commands-tpm.[ch] | ||||||
|  | /qapi/qapi-commands-trace.[ch] | ||||||
|  | /qapi/qapi-commands-transaction.[ch] | ||||||
|  | /qapi/qapi-commands-ui.[ch] | ||||||
| /qapi/qapi-commands.[ch] | /qapi/qapi-commands.[ch] | ||||||
| /qapi/qapi-emit-events.[ch] | /qapi/qapi-events-block-core.[ch] | ||||||
| /qapi/qapi-events-*.[ch] | /qapi/qapi-events-block.[ch] | ||||||
|  | /qapi/qapi-events-char.[ch] | ||||||
|  | /qapi/qapi-events-common.[ch] | ||||||
|  | /qapi/qapi-events-crypto.[ch] | ||||||
|  | /qapi/qapi-events-introspect.[ch] | ||||||
|  | /qapi/qapi-events-migration.[ch] | ||||||
|  | /qapi/qapi-events-misc.[ch] | ||||||
|  | /qapi/qapi-events-net.[ch] | ||||||
|  | /qapi/qapi-events-rocker.[ch] | ||||||
|  | /qapi/qapi-events-run-state.[ch] | ||||||
|  | /qapi/qapi-events-sockets.[ch] | ||||||
|  | /qapi/qapi-events-tpm.[ch] | ||||||
|  | /qapi/qapi-events-trace.[ch] | ||||||
|  | /qapi/qapi-events-transaction.[ch] | ||||||
|  | /qapi/qapi-events-ui.[ch] | ||||||
| /qapi/qapi-events.[ch] | /qapi/qapi-events.[ch] | ||||||
| /qapi/qapi-introspect.[ch] | /qapi/qapi-introspect.[ch] | ||||||
| /qapi/qapi-types-*.[ch] | /qapi/qapi-types-block-core.[ch] | ||||||
|  | /qapi/qapi-types-block.[ch] | ||||||
|  | /qapi/qapi-types-char.[ch] | ||||||
|  | /qapi/qapi-types-common.[ch] | ||||||
|  | /qapi/qapi-types-crypto.[ch] | ||||||
|  | /qapi/qapi-types-introspect.[ch] | ||||||
|  | /qapi/qapi-types-migration.[ch] | ||||||
|  | /qapi/qapi-types-misc.[ch] | ||||||
|  | /qapi/qapi-types-net.[ch] | ||||||
|  | /qapi/qapi-types-rocker.[ch] | ||||||
|  | /qapi/qapi-types-run-state.[ch] | ||||||
|  | /qapi/qapi-types-sockets.[ch] | ||||||
|  | /qapi/qapi-types-tpm.[ch] | ||||||
|  | /qapi/qapi-types-trace.[ch] | ||||||
|  | /qapi/qapi-types-transaction.[ch] | ||||||
|  | /qapi/qapi-types-ui.[ch] | ||||||
| /qapi/qapi-types.[ch] | /qapi/qapi-types.[ch] | ||||||
| /qapi/qapi-visit-*.[ch] | /qapi/qapi-visit-block-core.[ch] | ||||||
| !/qapi/qapi-visit-core.c | /qapi/qapi-visit-block.[ch] | ||||||
|  | /qapi/qapi-visit-char.[ch] | ||||||
|  | /qapi/qapi-visit-common.[ch] | ||||||
|  | /qapi/qapi-visit-crypto.[ch] | ||||||
|  | /qapi/qapi-visit-introspect.[ch] | ||||||
|  | /qapi/qapi-visit-migration.[ch] | ||||||
|  | /qapi/qapi-visit-misc.[ch] | ||||||
|  | /qapi/qapi-visit-net.[ch] | ||||||
|  | /qapi/qapi-visit-rocker.[ch] | ||||||
|  | /qapi/qapi-visit-run-state.[ch] | ||||||
|  | /qapi/qapi-visit-sockets.[ch] | ||||||
|  | /qapi/qapi-visit-tpm.[ch] | ||||||
|  | /qapi/qapi-visit-trace.[ch] | ||||||
|  | /qapi/qapi-visit-transaction.[ch] | ||||||
|  | /qapi/qapi-visit-ui.[ch] | ||||||
| /qapi/qapi-visit.[ch] | /qapi/qapi-visit.[ch] | ||||||
| /qapi/qapi-doc.texi | /qapi/qapi-doc.texi | ||||||
| /qemu-doc.html | /qemu-doc.html | ||||||
| /qemu-doc.info | /qemu-doc.info | ||||||
| /qemu-doc.txt | /qemu-doc.txt | ||||||
| /qemu-edid |  | ||||||
| /qemu-img | /qemu-img | ||||||
| /qemu-nbd | /qemu-nbd | ||||||
| /qemu-options.def | /qemu-options.def | ||||||
| @@ -66,8 +121,6 @@ | |||||||
| /scsi/qemu-pr-helper | /scsi/qemu-pr-helper | ||||||
| /vhost-user-scsi | /vhost-user-scsi | ||||||
| /vhost-user-blk | /vhost-user-blk | ||||||
| /vhost-user-gpu |  | ||||||
| /vhost-user-input |  | ||||||
| /fsdev/virtfs-proxy-helper | /fsdev/virtfs-proxy-helper | ||||||
| *.tmp | *.tmp | ||||||
| *.[1-9] | *.[1-9] | ||||||
| @@ -98,9 +151,7 @@ | |||||||
| .sdk | .sdk | ||||||
| *.gcda | *.gcda | ||||||
| *.gcno | *.gcno | ||||||
| *.gcov |  | ||||||
| /pc-bios/bios-pq/status | /pc-bios/bios-pq/status | ||||||
| /pc-bios/edk2-*.fd |  | ||||||
| /pc-bios/vgabios-pq/status | /pc-bios/vgabios-pq/status | ||||||
| /pc-bios/optionrom/linuxboot.asm | /pc-bios/optionrom/linuxboot.asm | ||||||
| /pc-bios/optionrom/linuxboot.bin | /pc-bios/optionrom/linuxboot.bin | ||||||
| @@ -110,10 +161,6 @@ | |||||||
| /pc-bios/optionrom/linuxboot_dma.bin | /pc-bios/optionrom/linuxboot_dma.bin | ||||||
| /pc-bios/optionrom/linuxboot_dma.raw | /pc-bios/optionrom/linuxboot_dma.raw | ||||||
| /pc-bios/optionrom/linuxboot_dma.img | /pc-bios/optionrom/linuxboot_dma.img | ||||||
| /pc-bios/optionrom/pvh.asm |  | ||||||
| /pc-bios/optionrom/pvh.bin |  | ||||||
| /pc-bios/optionrom/pvh.raw |  | ||||||
| /pc-bios/optionrom/pvh.img |  | ||||||
| /pc-bios/optionrom/multiboot.asm | /pc-bios/optionrom/multiboot.asm | ||||||
| /pc-bios/optionrom/multiboot.bin | /pc-bios/optionrom/multiboot.bin | ||||||
| /pc-bios/optionrom/multiboot.raw | /pc-bios/optionrom/multiboot.raw | ||||||
| @@ -124,7 +171,6 @@ | |||||||
| /pc-bios/optionrom/kvmvapic.img | /pc-bios/optionrom/kvmvapic.img | ||||||
| /pc-bios/s390-ccw/s390-ccw.elf | /pc-bios/s390-ccw/s390-ccw.elf | ||||||
| /pc-bios/s390-ccw/s390-ccw.img | /pc-bios/s390-ccw/s390-ccw.img | ||||||
| /docs/built |  | ||||||
| /docs/interop/qemu-ga-qapi.texi | /docs/interop/qemu-ga-qapi.texi | ||||||
| /docs/interop/qemu-ga-ref.html | /docs/interop/qemu-ga-ref.html | ||||||
| /docs/interop/qemu-ga-ref.info* | /docs/interop/qemu-ga-ref.info* | ||||||
| @@ -134,7 +180,6 @@ | |||||||
| /docs/interop/qemu-qmp-ref.info* | /docs/interop/qemu-qmp-ref.info* | ||||||
| /docs/interop/qemu-qmp-ref.txt | /docs/interop/qemu-qmp-ref.txt | ||||||
| /docs/version.texi | /docs/version.texi | ||||||
| /contrib/vhost-user-gpu/50-qemu-gpu.json |  | ||||||
| *.tps | *.tps | ||||||
| .stgit-* | .stgit-* | ||||||
| .git-submodule-status | .git-submodule-status | ||||||
| @@ -161,4 +206,3 @@ trace-dtrace-root.h | |||||||
| trace-dtrace-root.dtrace | trace-dtrace-root.dtrace | ||||||
| trace-ust-all.h | trace-ust-all.h | ||||||
| trace-ust-all.c | trace-ust-all.c | ||||||
| /target/arm/decode-sve.inc.c |  | ||||||
|   | |||||||
| @@ -1,83 +0,0 @@ | |||||||
| before_script: |  | ||||||
|  - apt-get update -qq |  | ||||||
|  - apt-get install -y -qq flex bison libglib2.0-dev libpixman-1-dev genisoimage |  | ||||||
|  |  | ||||||
| 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 |  | ||||||
|  - ./configure --enable-werror --target-list="aarch64-softmmu alpha-softmmu |  | ||||||
|       cris-softmmu hppa-softmmu lm32-softmmu moxie-softmmu microblazeel-softmmu |  | ||||||
|       mips64el-softmmu m68k-softmmu ppc-softmmu riscv64-softmmu sparc-softmmu" |  | ||||||
|  - make -j2 |  | ||||||
|  - make -j2 check |  | ||||||
|  |  | ||||||
| build-system2: |  | ||||||
|  script: |  | ||||||
|  - apt-get install -y -qq libsdl2-dev libgcrypt-dev libbrlapi-dev libaio-dev |  | ||||||
|       libfdt-dev liblzo2-dev librdmacm-dev libibverbs-dev libibumad-dev |  | ||||||
|  - ./configure --enable-werror --target-list="tricore-softmmu unicore32-softmmu |  | ||||||
|       microblaze-softmmu mips-softmmu riscv32-softmmu s390x-softmmu sh4-softmmu |  | ||||||
|       sparc64-softmmu x86_64-softmmu xtensa-softmmu nios2-softmmu or1k-softmmu" |  | ||||||
|  - make -j2 |  | ||||||
|  - make -j2 check |  | ||||||
|  |  | ||||||
| build-disabled: |  | ||||||
|  script: |  | ||||||
|  - ./configure --enable-werror --disable-rdma --disable-slirp --disable-curl |  | ||||||
|       --disable-capstone --disable-live-block-migration --disable-glusterfs |  | ||||||
|       --disable-replication --disable-coroutine-pool --disable-smartcard |  | ||||||
|       --disable-guest-agent --disable-curses --disable-libxml2 --disable-tpm |  | ||||||
|       --disable-qom-cast-debug --disable-spice --disable-vhost-vsock |  | ||||||
|       --disable-vhost-net --disable-vhost-crypto --disable-vhost-user |  | ||||||
|       --target-list="i386-softmmu ppc64-softmmu mips64-softmmu i386-linux-user" |  | ||||||
|  - make -j2 |  | ||||||
|  - make -j2 check-qtest SPEED=slow |  | ||||||
|  |  | ||||||
| build-tcg-disabled: |  | ||||||
|  script: |  | ||||||
|  - apt-get install -y -qq clang libgtk-3-dev libbluetooth-dev libusb-dev |  | ||||||
|  - ./configure --cc=clang --enable-werror --disable-tcg --audio-drv-list="" |  | ||||||
|  - make -j2 |  | ||||||
|  - make check-unit |  | ||||||
|  - make check-qapi-schema |  | ||||||
|  - cd tests/qemu-iotests/ |  | ||||||
|  - ./check -raw 001 002 003 004 005 008 009 010 011 012 021 025 032 033 048 |  | ||||||
|             052 063 077 086 101 104 106 113 147 148 150 151 152 157 159 160 |  | ||||||
|             163 170 171 183 184 192 194 197 205 208 215 221 222 226 227 236 |  | ||||||
|  - ./check -qcow2 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 |  | ||||||
|  |  | ||||||
| build-user: |  | ||||||
|  script: |  | ||||||
|  - ./configure --enable-werror --disable-system --disable-guest-agent |  | ||||||
|                --disable-capstone --disable-slirp --disable-fdt |  | ||||||
|  - make -j2 |  | ||||||
|  - make run-tcg-tests-i386-linux-user run-tcg-tests-x86_64-linux-user |  | ||||||
|  |  | ||||||
| build-clang: |  | ||||||
|  script: |  | ||||||
|  - apt-get install -y -qq clang libsdl2-dev libattr1-dev libcap-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 |  | ||||||
							
								
								
									
										51
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										51
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							| @@ -1,63 +1,48 @@ | |||||||
|  | [submodule "roms/vgabios"] | ||||||
|  | 	path = roms/vgabios | ||||||
|  | 	url = git://git.qemu-project.org/vgabios.git/ | ||||||
| [submodule "roms/seabios"] | [submodule "roms/seabios"] | ||||||
| 	path = roms/seabios | 	path = roms/seabios | ||||||
| 	url = https://git.qemu.org/git/seabios.git/ | 	url = git://git.qemu-project.org/seabios.git/ | ||||||
| [submodule "roms/SLOF"] | [submodule "roms/SLOF"] | ||||||
| 	path = roms/SLOF | 	path = roms/SLOF | ||||||
| 	url = https://git.qemu.org/git/SLOF.git | 	url = git://git.qemu-project.org/SLOF.git | ||||||
| [submodule "roms/ipxe"] | [submodule "roms/ipxe"] | ||||||
| 	path = roms/ipxe | 	path = roms/ipxe | ||||||
| 	url = https://git.qemu.org/git/ipxe.git | 	url = git://git.qemu-project.org/ipxe.git | ||||||
| [submodule "roms/openbios"] | [submodule "roms/openbios"] | ||||||
| 	path = roms/openbios | 	path = roms/openbios | ||||||
| 	url = https://git.qemu.org/git/openbios.git | 	url = git://git.qemu-project.org/openbios.git | ||||||
| [submodule "roms/openhackware"] | [submodule "roms/openhackware"] | ||||||
| 	path = roms/openhackware | 	path = roms/openhackware | ||||||
| 	url = https://git.qemu.org/git/openhackware.git | 	url = git://git.qemu-project.org/openhackware.git | ||||||
| [submodule "roms/qemu-palcode"] | [submodule "roms/qemu-palcode"] | ||||||
| 	path = roms/qemu-palcode | 	path = roms/qemu-palcode | ||||||
| 	url = https://git.qemu.org/git/qemu-palcode.git | 	url = git://github.com/rth7680/qemu-palcode.git | ||||||
| [submodule "roms/sgabios"] | [submodule "roms/sgabios"] | ||||||
| 	path = roms/sgabios | 	path = roms/sgabios | ||||||
| 	url = https://git.qemu.org/git/sgabios.git | 	url = git://git.qemu-project.org/sgabios.git | ||||||
| [submodule "dtc"] | [submodule "dtc"] | ||||||
| 	path = dtc | 	path = dtc | ||||||
| 	url = https://git.qemu.org/git/dtc.git | 	url = git://git.qemu-project.org/dtc.git | ||||||
| [submodule "roms/u-boot"] | [submodule "roms/u-boot"] | ||||||
| 	path = roms/u-boot | 	path = roms/u-boot | ||||||
| 	url = https://git.qemu.org/git/u-boot.git | 	url = git://git.qemu-project.org/u-boot.git | ||||||
| [submodule "roms/skiboot"] | [submodule "roms/skiboot"] | ||||||
| 	path = roms/skiboot | 	path = roms/skiboot | ||||||
| 	url = https://git.qemu.org/git/skiboot.git | 	url = git://git.qemu.org/skiboot.git | ||||||
| [submodule "roms/QemuMacDrivers"] | [submodule "roms/QemuMacDrivers"] | ||||||
| 	path = roms/QemuMacDrivers | 	path = roms/QemuMacDrivers | ||||||
| 	url = https://git.qemu.org/git/QemuMacDrivers.git | 	url = git://git.qemu.org/QemuMacDrivers.git | ||||||
| [submodule "ui/keycodemapdb"] | [submodule "ui/keycodemapdb"] | ||||||
| 	path = ui/keycodemapdb | 	path = ui/keycodemapdb | ||||||
| 	url = https://git.qemu.org/git/keycodemapdb.git | 	url = git://git.qemu.org/keycodemapdb.git | ||||||
| [submodule "capstone"] | [submodule "capstone"] | ||||||
| 	path = capstone | 	path = capstone | ||||||
| 	url = https://git.qemu.org/git/capstone.git | 	url = git://git.qemu.org/capstone.git | ||||||
| [submodule "roms/seabios-hppa"] | [submodule "roms/seabios-hppa"] | ||||||
| 	path = roms/seabios-hppa | 	path = roms/seabios-hppa | ||||||
| 	url = https://git.qemu.org/git/seabios-hppa.git | 	url = git://github.com/hdeller/seabios-hppa.git | ||||||
| [submodule "roms/u-boot-sam460ex"] | [submodule "roms/u-boot-sam460ex"] | ||||||
| 	path = roms/u-boot-sam460ex | 	path = roms/u-boot-sam460ex | ||||||
| 	url = https://git.qemu.org/git/u-boot-sam460ex.git | 	url = git://github.com/zbalaton/u-boot-sam460ex | ||||||
| [submodule "tests/fp/berkeley-testfloat-3"] |  | ||||||
| 	path = tests/fp/berkeley-testfloat-3 |  | ||||||
| 	url = https://git.qemu.org/git/berkeley-testfloat-3.git |  | ||||||
| [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 |  | ||||||
|   | |||||||
							
								
								
									
										153
									
								
								.mailmap
									
									
									
									
									
								
							
							
						
						
									
										153
									
								
								.mailmap
									
									
									
									
									
								
							| @@ -1,161 +1,24 @@ | |||||||
| # This mailmap fixes up author names/addresses. | # This mailmap just translates the weird addresses from the original import into git | ||||||
|  | # into proper addresses so that they are counted properly in git shortlog output. | ||||||
| # | # | ||||||
| # 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> | Andrzej Zaborowski <balrogg@gmail.com> balrog <balrog@c046a42c-6fe2-441c-8c8c-71466251a162> | ||||||
| Anthony Liguori <anthony@codemonkey.ws> aliguori <aliguori@c046a42c-6fe2-441c-8c8c-71466251a162> | Anthony Liguori <anthony@codemonkey.ws> aliguori <aliguori@c046a42c-6fe2-441c-8c8c-71466251a162> | ||||||
|  | Anthony Liguori <anthony@codemonkey.ws> Anthony Liguori <aliguori@us.ibm.com> | ||||||
| Aurelien Jarno <aurelien@aurel32.net> aurel32 <aurel32@c046a42c-6fe2-441c-8c8c-71466251a162> | Aurelien Jarno <aurelien@aurel32.net> aurel32 <aurel32@c046a42c-6fe2-441c-8c8c-71466251a162> | ||||||
| Blue Swirl <blauwirbel@gmail.com> blueswir1 <blueswir1@c046a42c-6fe2-441c-8c8c-71466251a162> | Blue Swirl <blauwirbel@gmail.com> blueswir1 <blueswir1@c046a42c-6fe2-441c-8c8c-71466251a162> | ||||||
| Edgar E. Iglesias <edgar.iglesias@gmail.com> edgar_igl <edgar_igl@c046a42c-6fe2-441c-8c8c-71466251a162> | Edgar E. Iglesias <edgar.iglesias@gmail.com> edgar_igl <edgar_igl@c046a42c-6fe2-441c-8c8c-71466251a162> | ||||||
| Fabrice Bellard <fabrice@bellard.org> bellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162> | Fabrice Bellard <fabrice@bellard.org> bellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162> | ||||||
|  | James Hogan <jhogan@kernel.org> <james.hogan@imgtec.com> | ||||||
| Jocelyn Mayer <l_indien@magic.fr> j_mayer <j_mayer@c046a42c-6fe2-441c-8c8c-71466251a162> | Jocelyn Mayer <l_indien@magic.fr> j_mayer <j_mayer@c046a42c-6fe2-441c-8c8c-71466251a162> | ||||||
| Paul Brook <paul@codesourcery.com> pbrook <pbrook@c046a42c-6fe2-441c-8c8c-71466251a162> | Paul Brook <paul@codesourcery.com> pbrook <pbrook@c046a42c-6fe2-441c-8c8c-71466251a162> | ||||||
|  | Paul Burton <paul.burton@mips.com> <paul.burton@imgtec.com> | ||||||
|  | Paul Burton <paul.burton@mips.com> <paul@archlinuxmips.org> | ||||||
| Thiemo Seufer <ths@networkno.de> ths <ths@c046a42c-6fe2-441c-8c8c-71466251a162> | Thiemo Seufer <ths@networkno.de> ths <ths@c046a42c-6fe2-441c-8c8c-71466251a162> | ||||||
| malc <av1474@comtv.ru> malc <malc@c046a42c-6fe2-441c-8c8c-71466251a162> | malc <av1474@comtv.ru> malc <malc@c046a42c-6fe2-441c-8c8c-71466251a162> | ||||||
|  |  | ||||||
| # There is also a: | # There is also a: | ||||||
| #    (no author) <(no author)@c046a42c-6fe2-441c-8c8c-71466251a162> | #    (no author) <(no author)@c046a42c-6fe2-441c-8c8c-71466251a162> | ||||||
| # for the cvs2svn initialization commit e63c3dc74bf. | # for the cvs2svn initialization commit e63c3dc74bf. | ||||||
|  | # | ||||||
| # Next, translate a few commits where mailman rewrote the From: line due |  | ||||||
| # to strict SPF, although we prefer to avoid adding more entries like that. |  | ||||||
| Ed Swierk <eswierk@skyportsystems.com> Ed Swierk via Qemu-devel <qemu-devel@nongnu.org> |  | ||||||
| Ian McKellar <ianloic@google.com> Ian McKellar via Qemu-devel <qemu-devel@nongnu.org> |  | ||||||
| Julia Suvorova <jusual@mail.ru> Julia Suvorova via Qemu-devel <qemu-devel@nongnu.org> |  | ||||||
| Justin Terry (VM) <juterry@microsoft.com> Justin Terry (VM) via Qemu-devel <qemu-devel@nongnu.org> |  | ||||||
|  |  | ||||||
| # 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 | # Also list preferred name forms where people have changed their | ||||||
| # git author config, or had utf8/latin1 encoding issues. | # git author config | ||||||
| Aaron Lindsay <aaron@os.amperecomputing.com> |  | ||||||
| Alexey Gerasimenko <x1917x@gmail.com> |  | ||||||
| Alex Ivanov <void@aleksoft.net> |  | ||||||
| Andreas Färber <afaerber@suse.de> |  | ||||||
| Bandan Das <bsd@redhat.com> |  | ||||||
| Benjamin MARSILI <mlspirat42@gmail.com> |  | ||||||
| Benoît Canet <benoit.canet@gmail.com> |  | ||||||
| Benoît Canet <benoit.canet@irqsave.net> |  | ||||||
| Benoît Canet <benoit.canet@nodalink.com> |  | ||||||
| Boqun Feng <boqun.feng@gmail.com> |  | ||||||
| Boqun Feng <boqun.feng@intel.com> |  | ||||||
| Brad Smith <brad@comstyle.com> |  | ||||||
| Brijesh Singh <brijesh.singh@amd.com> |  | ||||||
| Brilly Wu <brillywu@viatech.com.cn> |  | ||||||
| Cédric Vincent <cedric.vincent@st.com> |  | ||||||
| CheneyLin <linzc@zju.edu.cn> |  | ||||||
| Chen Gang <chengang@emindsoft.com.cn> |  | ||||||
| Chen Gang <gang.chen.5i5j@gmail.com> |  | ||||||
| Chen Gang <gang.chen@sunrus.com.cn> |  | ||||||
| Chen Wei-Ren <chenwj@iis.sinica.edu.tw> |  | ||||||
| Christophe Lyon <christophe.lyon@st.com> |  | ||||||
| Collin L. Walling <walling@linux.ibm.com> |  | ||||||
| Daniel P. Berrangé <berrange@redhat.com> | Daniel P. Berrangé <berrange@redhat.com> | ||||||
| Eduardo Otubo <otubo@redhat.com> |  | ||||||
| Fabrice Desclaux <fabrice.desclaux@cea.fr> |  | ||||||
| Fernando Luis Vázquez Cao <fernando_b1@lab.ntt.co.jp> |  | ||||||
| Fernando Luis Vázquez Cao <fernando@oss.ntt.co.jp> |  | ||||||
| Gautham R. Shenoy <ego@in.ibm.com> |  | ||||||
| Gautham R. Shenoy <ego@linux.vnet.ibm.com> |  | ||||||
| Gonglei (Arei) <arei.gonglei@huawei.com> |  | ||||||
| Guang Wang <wang.guang55@zte.com.cn> |  | ||||||
| Hailiang Zhang <zhang.zhanghailiang@huawei.com> |  | ||||||
| Hervé Poussineau <hpoussin@reactos.org> |  | ||||||
| Jakub Jermář <jakub@jermar.eu> |  | ||||||
| Jakub Jermář <jakub.jermar@kernkonzept.com> |  | ||||||
| Jean-Christophe Dubois <jcd@tribudubois.net> |  | ||||||
| Jindřich Makovička <makovick@gmail.com> |  | ||||||
| John Arbuckle <programmingkidx@gmail.com> |  | ||||||
| Juha Riihimäki <juha.riihimaki@nokia.com> |  | ||||||
| Juha Riihimäki <Juha.Riihimaki@nokia.com> |  | ||||||
| Jun Li <junmuzi@gmail.com> |  | ||||||
| Laurent Vivier <Laurent@lvivier.info> |  | ||||||
| Leandro Lupori <leandro.lupori@gmail.com> |  | ||||||
| Li Guang <lig.fnst@cn.fujitsu.com> |  | ||||||
| Liming Wang <walimisdev@gmail.com> |  | ||||||
| linzhecheng <linzc@zju.edu.cn> |  | ||||||
| Liran Schour <lirans@il.ibm.com> |  | ||||||
| Liu Yu <yu.liu@freescale.com> |  | ||||||
| Liu Yu <Yu.Liu@freescale.com> |  | ||||||
| Li Zhang <zhlcindy@gmail.com> |  | ||||||
| Li Zhang <zhlcindy@linux.vnet.ibm.com> |  | ||||||
| Lluís Vilanova <vilanova@ac.upc.edu> |  | ||||||
| Lluís Vilanova <xscript@gmx.net> |  | ||||||
| Longpeng (Mike) <longpeng2@huawei.com> |  | ||||||
| Luc Michel <luc.michel@git.antfield.fr> |  | ||||||
| Luc Michel <luc.michel@greensocs.com> |  | ||||||
| Marc Marí <marc.mari.barcelo@gmail.com> |  | ||||||
| Marc Marí <markmb@redhat.com> |  | ||||||
| Michael Avdienko <whitearchey@gmail.com> |  | ||||||
| Michael S. Tsirkin <mst@redhat.com> |  | ||||||
| Munkyu Im <munkyu.im@samsung.com> |  | ||||||
| Nicholas Bellinger <nab@linux-iscsi.org> |  | ||||||
| Nicholas Thomas <nick@bytemark.co.uk> |  | ||||||
| Nikunj A Dadhania <nikunj@linux.vnet.ibm.com> |  | ||||||
| Orit Wasserman <owasserm@redhat.com> |  | ||||||
| Paolo Bonzini <pbonzini@redhat.com> |  | ||||||
| Pavel Dovgaluk <dovgaluk@ispras.ru> |  | ||||||
| Pavel Dovgaluk <pavel.dovgaluk@gmail.com> |  | ||||||
| Pavel Dovgaluk <Pavel.Dovgaluk@ispras.ru> |  | ||||||
| Peter Crosthwaite <crosthwaite.peter@gmail.com> |  | ||||||
| Peter Crosthwaite <peter.crosthwaite@petalogix.com> |  | ||||||
| Peter Crosthwaite <peter.crosthwaite@xilinx.com> |  | ||||||
| Prasad J Pandit <pjp@fedoraproject.org> |  | ||||||
| Prasad J Pandit <ppandit@redhat.com> |  | ||||||
| Qiao Nuohan <qiaonuohan@cn.fujitsu.com> |  | ||||||
| Reimar Döffinger <Reimar.Doeffinger@gmx.de> |  | ||||||
| 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> |  | ||||||
|   | |||||||
							
								
								
									
										302
									
								
								.patchew.yml
									
									
									
									
									
								
							
							
						
						
									
										302
									
								
								.patchew.yml
									
									
									
									
									
								
							| @@ -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 |  | ||||||
| @@ -35,5 +35,13 @@ build: | |||||||
|     options: "-e HOME=/root" |     options: "-e HOME=/root" | ||||||
|   ci: |   ci: | ||||||
|     - unset CC |     - unset CC | ||||||
|  |     # some targets require newer up to date packages, for example TARGET_LIST matching | ||||||
|  |     # aarch64*-softmmu|arm*-softmmu|ppc*-softmmu|microblaze*-softmmu|mips64el-softmmu) | ||||||
|  |     # see the configure script: | ||||||
|  |     #    error_exit "DTC (libfdt) version >= 1.4.2 not present. Your options:" | ||||||
|  |     #    "  (1) Preferred: Install the DTC (libfdt) devel package" | ||||||
|  |     #    "  (2) Fetch the DTC submodule, using:" | ||||||
|  |     #    "      git submodule update --init dtc" | ||||||
|  |     - dpkg --compare-versions `dpkg-query --showformat='${Version}' --show libfdt-dev` ge 1.4.2 || git submodule update --init dtc | ||||||
|     - ./configure ${QEMU_CONFIGURE_OPTS} --target-list=${TARGET_LIST} |     - ./configure ${QEMU_CONFIGURE_OPTS} --target-list=${TARGET_LIST} | ||||||
|     - make -j$(($(getconf _NPROCESSORS_ONLN) + 1)) |     - make -j$(($(getconf _NPROCESSORS_ONLN) + 1)) | ||||||
|   | |||||||
							
								
								
									
										409
									
								
								.travis.yml
									
									
									
									
									
								
							
							
						
						
									
										409
									
								
								.travis.yml
									
									
									
									
									
								
							| @@ -1,24 +1,10 @@ | |||||||
| # The current Travis default is a VM based 16.04 Xenial on GCE | sudo: false | ||||||
| # Additional builds with specific requirements for a full VM need to |  | ||||||
| # be added as additional matrix: entries later on |  | ||||||
| dist: xenial |  | ||||||
| language: c | language: c | ||||||
|  | python: | ||||||
|  |   - "2.6" | ||||||
| compiler: | compiler: | ||||||
|   - gcc |   - gcc | ||||||
| cache: | cache: ccache | ||||||
|   # There is one cache per branch and compiler version. |  | ||||||
|   # characteristics of each job are used to identify the cache: |  | ||||||
|   # - OS name (currently, 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 |  | ||||||
|  |  | ||||||
|  |  | ||||||
| addons: | addons: | ||||||
|   apt: |   apt: | ||||||
|     packages: |     packages: | ||||||
| @@ -26,10 +12,9 @@ addons: | |||||||
|       - libaio-dev |       - libaio-dev | ||||||
|       - libattr1-dev |       - libattr1-dev | ||||||
|       - libbrlapi-dev |       - libbrlapi-dev | ||||||
|       - libcap-dev |  | ||||||
|       - libcap-ng-dev |       - libcap-ng-dev | ||||||
|       - libgcc-4.8-dev |       - libgcc-4.8-dev | ||||||
|       - libgnutls28-dev |       - libgnutls-dev | ||||||
|       - libgtk-3-dev |       - libgtk-3-dev | ||||||
|       - libiscsi-dev |       - libiscsi-dev | ||||||
|       - liblttng-ust-dev |       - liblttng-ust-dev | ||||||
| @@ -37,29 +22,18 @@ addons: | |||||||
|       - libnfs-dev |       - libnfs-dev | ||||||
|       - libnss3-dev |       - libnss3-dev | ||||||
|       - libpixman-1-dev |       - libpixman-1-dev | ||||||
|       - libpng-dev |       - libpng12-dev | ||||||
|       - librados-dev |       - librados-dev | ||||||
|       - libsdl2-dev |       - libsdl1.2-dev | ||||||
|       - libsdl2-image-dev |  | ||||||
|       - libseccomp-dev |       - libseccomp-dev | ||||||
|       - libspice-protocol-dev |       - libspice-protocol-dev | ||||||
|       - libspice-server-dev |       - libspice-server-dev | ||||||
|       - libssh-dev |       - libssh2-1-dev | ||||||
|       - liburcu-dev |       - liburcu-dev | ||||||
|       - libusb-1.0-0-dev |       - libusb-1.0-0-dev | ||||||
|       - libvdeplug-dev |       - libvte-2.90-dev | ||||||
|       - libvte-2.91-dev |  | ||||||
|       - sparse |       - sparse | ||||||
|       - uuid-dev |       - uuid-dev | ||||||
|       - gcovr |  | ||||||
|   homebrew: |  | ||||||
|     packages: |  | ||||||
|       - ccache |  | ||||||
|       - glib |  | ||||||
|       - pixman |  | ||||||
|       - gnu-sed |  | ||||||
|     update: true |  | ||||||
|  |  | ||||||
|  |  | ||||||
| # The channel name "irc.oftc.net#qemu" is encrypted against qemu/qemu | # The channel name "irc.oftc.net#qemu" is encrypted against qemu/qemu | ||||||
| # to prevent IRC notifications from forks. This was created using: | # to prevent IRC notifications from forks. This was created using: | ||||||
| @@ -70,232 +44,130 @@ notifications: | |||||||
|       - secure: "F7GDRgjuOo5IUyRLqSkmDL7kvdU4UcH3Lm/W2db2JnDHTGCqgEdaYEYKciyCLZ57vOTsTsOgesN8iUT7hNHBd1KWKjZe9KDTZWppWRYVwAwQMzVeSOsbbU4tRoJ6Pp+3qhH1Z0eGYR9ZgKYAoTumDFgSAYRp4IscKS8jkoedOqM=" |       - secure: "F7GDRgjuOo5IUyRLqSkmDL7kvdU4UcH3Lm/W2db2JnDHTGCqgEdaYEYKciyCLZ57vOTsTsOgesN8iUT7hNHBd1KWKjZe9KDTZWppWRYVwAwQMzVeSOsbbU4tRoJ6Pp+3qhH1Z0eGYR9ZgKYAoTumDFgSAYRp4IscKS8jkoedOqM=" | ||||||
|     on_success: change |     on_success: change | ||||||
|     on_failure: always |     on_failure: always | ||||||
|  |  | ||||||
|  |  | ||||||
| env: | env: | ||||||
|   global: |   global: | ||||||
|     - SRC_DIR="." |     - TEST_CMD="make check" | ||||||
|     - BUILD_DIR="." |     - MAKEFLAGS="-j3" | ||||||
|     - BASE_CONFIG="--disable-docs --disable-tools" |   matrix: | ||||||
|     - TEST_CMD="make check V=1" |     - CONFIG="" | ||||||
|     # This is broadly a list of "mainline" softmmu targets which have support across the major distros |     - CONFIG="--enable-debug --enable-debug-tcg --enable-trace-backends=log" | ||||||
|     - MAIN_SOFTMMU_TARGETS="aarch64-softmmu,arm-softmmu,i386-softmmu,mips-softmmu,mips64-softmmu,ppc64-softmmu,riscv64-softmmu,s390x-softmmu,x86_64-softmmu" |     - CONFIG="--disable-linux-aio --disable-cap-ng --disable-attr --disable-brlapi --disable-uuid --disable-libusb" | ||||||
|     - CCACHE_SLOPPINESS="include_file_ctime,include_file_mtime" |     - CONFIG="--enable-modules --disable-linux-user" | ||||||
|     - CCACHE_MAXSIZE=1G |     - CONFIG="--with-coroutine=ucontext --disable-linux-user" | ||||||
|  |     - CONFIG="--with-coroutine=sigaltstack --disable-linux-user" | ||||||
|  |  | ||||||
| git: | git: | ||||||
|   # we want to do this ourselves |   # we want to do this ourselves | ||||||
|   submodules: false |   submodules: false | ||||||
|  | before_install: | ||||||
|  |   - if [ "$TRAVIS_OS_NAME" == "osx" ]; then brew update ; fi | ||||||
|  |   - if [ "$TRAVIS_OS_NAME" == "osx" ]; then brew install libffi gettext glib pixman ; fi | ||||||
|  |   - wget -O - http://people.linaro.org/~alex.bennee/qemu-submodule-git-seed.tar.xz | tar -xvJ | ||||||
|  |   - git submodule update --init --recursive | ||||||
| before_script: | before_script: | ||||||
|   - if [ "$TRAVIS_OS_NAME" == "osx" ] ; then export PATH="/usr/local/opt/ccache/libexec:$PATH" ; fi |   - ./configure ${CONFIG} | ||||||
|   - 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: | script: | ||||||
|   - make -j3 && travis_retry ${TEST_CMD} |   - make ${MAKEFLAGS} && ${TEST_CMD} | ||||||
| after_script: |  | ||||||
|   - if command -v ccache ; then ccache --show-stats ; fi |  | ||||||
|  |  | ||||||
|  |  | ||||||
| matrix: | matrix: | ||||||
|   include: |   include: | ||||||
|     - env: |     # Test with CLang for compile portability | ||||||
|         - CONFIG="--disable-system --static" |     - env: CONFIG="" | ||||||
|         - 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" |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     - env: |  | ||||||
|         - CONFIG="--disable-user --target-list-exclude=${MAIN_SOFTMMU_TARGETS}" |  | ||||||
|         - CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-default" |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     # Just build tools and run minimal unit and softfloat checks |  | ||||||
|     - 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" |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     # --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 |  | ||||||
|     - 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}" |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     # 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" |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     # Alternate coroutines implementations are only really of interest to KVM users |  | ||||||
|     # However we can't test against KVM on Travis so we can only run unit tests |  | ||||||
|     - env: |  | ||||||
|         - CONFIG="--with-coroutine=ucontext --disable-tcg" |  | ||||||
|         - TEST_CMD="make check-unit -j3 V=1" |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     - env: |  | ||||||
|         - CONFIG="--with-coroutine=sigaltstack --disable-tcg" |  | ||||||
|         - TEST_CMD="make check-unit -j3 V=1" |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     # Check we can build docs and tools (out of tree) |  | ||||||
|     - 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: |  | ||||||
|             - python-sphinx |  | ||||||
|             - texinfo |  | ||||||
|             - perl |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     # Test with Clang for compile portability (Travis uses clang-5.0) |  | ||||||
|     - env: |  | ||||||
|         - CONFIG="--disable-system" |  | ||||||
|         - CACHE_NAME="${TRAVIS_BRANCH}-linux-clang-default" |  | ||||||
|       compiler: clang |       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" |  | ||||||
|       compiler: clang |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     # gprof/gcov are GCC features |     # gprof/gcov are GCC features | ||||||
|     - env: |     - env: CONFIG="--enable-gprof --enable-gcov --disable-pie" | ||||||
|         - CONFIG="--enable-gprof --enable-gcov --disable-pie --target-list=${MAIN_SOFTMMU_TARGETS}" |       compiler: gcc | ||||||
|       after_success: |  | ||||||
|         - ${SRC_DIR}/scripts/travis/coverage-summary.sh |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     # We manually include builds which we disable "make check" for |     # We manually include builds which we disable "make check" for | ||||||
|     - env: |     - env: CONFIG="--enable-debug --enable-tcg-interpreter" | ||||||
|         - CONFIG="--without-default-devices --disable-user" |            TEST_CMD="" | ||||||
|         - CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-default" |       compiler: gcc | ||||||
|         - TEST_CMD="" |     - env: CONFIG="--enable-trace-backends=simple" | ||||||
|  |            TEST_CMD="" | ||||||
|  |       compiler: gcc | ||||||
|     # We manually include builds which we disable "make check" for |     - env: CONFIG="--enable-trace-backends=ftrace" | ||||||
|     - env: |            TEST_CMD="" | ||||||
|         - CONFIG="--enable-debug --enable-tcg-interpreter" |       compiler: gcc | ||||||
|         - TEST_CMD="" |     - env: CONFIG="--enable-trace-backends=ust" | ||||||
|  |            TEST_CMD="" | ||||||
|  |       compiler: gcc | ||||||
|     # We don't need to exercise every backend with every front-end |     - env: CONFIG="--disable-tcg" | ||||||
|     - env: |            TEST_CMD="" | ||||||
|         - CONFIG="--enable-trace-backends=log,simple,syslog --disable-system" |       compiler: gcc | ||||||
|         - TEST_CMD="" |     - env: CONFIG="" | ||||||
|  |  | ||||||
|  |  | ||||||
|     - env: |  | ||||||
|         - CONFIG="--enable-trace-backends=ftrace --target-list=x86_64-softmmu" |  | ||||||
|         - TEST_CMD="" |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     - env: |  | ||||||
|         - CONFIG="--enable-trace-backends=ust --target-list=x86_64-softmmu" |  | ||||||
|         - TEST_CMD="" |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     # MacOSX builds |  | ||||||
|     - env: |  | ||||||
|         - CONFIG="--target-list=${MAIN_SOFTMMU_TARGETS}" |  | ||||||
|       os: osx |       os: osx | ||||||
|       osx_image: xcode9.4 |  | ||||||
|       compiler: clang |       compiler: clang | ||||||
|  |     # Plain Trusty System Build | ||||||
|  |     - env: CONFIG="--disable-linux-user" | ||||||
|     - env: |       sudo: required | ||||||
|         - CONFIG="--target-list=i386-softmmu,ppc-softmmu,ppc64-softmmu,m68k-softmmu,x86_64-softmmu" |       addons: | ||||||
|       os: osx |       dist: trusty | ||||||
|       osx_image: xcode10.3 |       compiler: gcc | ||||||
|       compiler: clang |       before_install: | ||||||
|  |         - sudo apt-get update -qq | ||||||
|  |         - sudo apt-get build-dep -qq qemu | ||||||
|     # Python builds |         - wget -O - http://people.linaro.org/~alex.bennee/qemu-submodule-git-seed.tar.xz | tar -xvJ | ||||||
|     - env: |         - git submodule update --init --recursive | ||||||
|         - CONFIG="--target-list=x86_64-softmmu" |     # Plain Trusty Linux User Build | ||||||
|         - CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-default" |     - env: CONFIG="--disable-system" | ||||||
|       language: python |       sudo: required | ||||||
|  |       addons: | ||||||
|  |       dist: trusty | ||||||
|  |       compiler: gcc | ||||||
|  |       before_install: | ||||||
|  |         - sudo apt-get update -qq | ||||||
|  |         - sudo apt-get build-dep -qq qemu | ||||||
|  |         - wget -O - http://people.linaro.org/~alex.bennee/qemu-submodule-git-seed.tar.xz | tar -xvJ | ||||||
|  |         - git submodule update --init --recursive | ||||||
|  |     # Trusty System build with latest stable clang & python 3.0 | ||||||
|  |     - sudo: required | ||||||
|  |       addons: | ||||||
|  |       dist: trusty | ||||||
|  |       language: generic | ||||||
|  |       compiler: none | ||||||
|       python: |       python: | ||||||
|         - "3.4" |         - "3.0" | ||||||
|  |       env: | ||||||
|  |         - COMPILER_NAME=clang CXX=clang++-3.9 CC=clang-3.9 | ||||||
|     - env: |         - CONFIG="--disable-linux-user --cc=clang-3.9 --cxx=clang++-3.9 --python=/usr/bin/python3" | ||||||
|         - CONFIG="--target-list=x86_64-softmmu" |       before_install: | ||||||
|         - CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-default" |         - wget -nv -O - http://llvm.org/apt/llvm-snapshot.gpg.key | sudo apt-key add - | ||||||
|       language: python |         - sudo apt-add-repository -y 'deb http://llvm.org/apt/trusty llvm-toolchain-trusty-3.9 main' | ||||||
|  |         - sudo apt-get update -qq | ||||||
|  |         - sudo apt-get install -qq -y clang-3.9 | ||||||
|  |         - sudo apt-get build-dep -qq qemu | ||||||
|  |         - wget -O - http://people.linaro.org/~alex.bennee/qemu-submodule-git-seed.tar.xz | tar -xvJ | ||||||
|  |         - git submodule update --init --recursive | ||||||
|  |       before_script: | ||||||
|  |         - ./configure ${CONFIG} || cat config.log | ||||||
|  |     # Trusty Linux User build with latest stable clang & python 3.6 | ||||||
|  |     - sudo: required | ||||||
|  |       addons: | ||||||
|  |       dist: trusty | ||||||
|  |       language: generic | ||||||
|  |       compiler: none | ||||||
|       python: |       python: | ||||||
|         - "3.6" |         - "3.6" | ||||||
|  |       env: | ||||||
|  |         - COMPILER_NAME=clang CXX=clang++-3.9 CC=clang-3.9 | ||||||
|     # Acceptance (Functional) tests |         - CONFIG="--disable-system --cc=clang-3.9 --cxx=clang++-3.9 --python=/usr/bin/python3" | ||||||
|     - env: |       before_install: | ||||||
|         - 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" |         - wget -nv -O - http://llvm.org/apt/llvm-snapshot.gpg.key | sudo apt-key add - | ||||||
|         - TEST_CMD="make check-acceptance" |         - sudo apt-add-repository -y 'deb http://llvm.org/apt/trusty llvm-toolchain-trusty-3.9 main' | ||||||
|       after_failure: |         - sudo apt-get update -qq | ||||||
|         - cat tests/results/latest/job.log |         - sudo apt-get install -qq -y clang-3.9 | ||||||
|       addons: |         - sudo apt-get build-dep -qq qemu | ||||||
|         apt: |         - wget -O - http://people.linaro.org/~alex.bennee/qemu-submodule-git-seed.tar.xz | tar -xvJ | ||||||
|           packages: |         - git submodule update --init --recursive | ||||||
|             - python3-pil |       before_script: | ||||||
|             - python3-pip |         - ./configure ${CONFIG} || cat config.log | ||||||
|             - python3.5-venv |  | ||||||
|             - tesseract-ocr |  | ||||||
|             - tesseract-ocr-eng |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     # Using newer GCC with sanitizers |     # Using newer GCC with sanitizers | ||||||
|     - addons: |     - addons: | ||||||
|         apt: |         apt: | ||||||
|           update: true |  | ||||||
|           sources: |           sources: | ||||||
|             # PPAs for newer toolchains |             # PPAs for newer toolchains | ||||||
|             - ubuntu-toolchain-r-test |             - ubuntu-toolchain-r-test | ||||||
|           packages: |           packages: | ||||||
|             # Extra toolchains |             # Extra toolchains | ||||||
|             - gcc-9 |             - gcc-5 | ||||||
|             - g++-9 |             - g++-5 | ||||||
|             # Build dependencies |             # Build dependencies | ||||||
|             - libaio-dev |             - libaio-dev | ||||||
|             - libattr1-dev |             - libattr1-dev | ||||||
| @@ -311,74 +183,21 @@ matrix: | |||||||
|             - libpixman-1-dev |             - libpixman-1-dev | ||||||
|             - libpng12-dev |             - libpng12-dev | ||||||
|             - librados-dev |             - librados-dev | ||||||
|             - libsdl2-dev |             - libsdl1.2-dev | ||||||
|             - libsdl2-image-dev |  | ||||||
|             - libseccomp-dev |             - libseccomp-dev | ||||||
|             - libspice-protocol-dev |             - libspice-protocol-dev | ||||||
|             - libspice-server-dev |             - libspice-server-dev | ||||||
|             - libssh-dev |             - libssh2-1-dev | ||||||
|             - liburcu-dev |             - liburcu-dev | ||||||
|             - libusb-1.0-0-dev |             - libusb-1.0-0-dev | ||||||
|             - libvte-2.91-dev |             - libvte-2.90-dev | ||||||
|             - sparse |             - sparse | ||||||
|             - uuid-dev |             - uuid-dev | ||||||
|       language: generic |       language: generic | ||||||
|       compiler: none |       compiler: none | ||||||
|       env: |       env: | ||||||
|         - COMPILER_NAME=gcc CXX=g++-9 CC=gcc-9 |         - COMPILER_NAME=gcc CXX=g++-5 CC=gcc-5 | ||||||
|         - CONFIG="--cc=gcc-9 --cxx=g++-9 --disable-pie --disable-linux-user" |         - CONFIG="--cc=gcc-5 --cxx=g++-5 --disable-pie --disable-linux-user" | ||||||
|         - TEST_CMD="" |         - TEST_CMD="" | ||||||
|       before_script: |       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 | ||||||
|  |  | ||||||
|  |  | ||||||
|     # Run check-tcg against linux-user |  | ||||||
|     - env: |  | ||||||
|         - CONFIG="--disable-system --enable-debug-tcg" |  | ||||||
|         - 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 |  | ||||||
|   | |||||||
							
								
								
									
										160
									
								
								CODING_STYLE
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										160
									
								
								CODING_STYLE
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,160 @@ | |||||||
|  | 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. | ||||||
|  |  | ||||||
|  | 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 | ||||||
							
								
								
									
										641
									
								
								CODING_STYLE.rst
									
									
									
									
									
								
							
							
						
						
									
										641
									
								
								CODING_STYLE.rst
									
									
									
									
									
								
							| @@ -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 |  | ||||||
							
								
								
									
										22
									
								
								COPYING.LIB
									
									
									
									
									
								
							
							
						
						
									
										22
									
								
								COPYING.LIB
									
									
									
									
									
								
							| @@ -1,8 +1,8 @@ | |||||||
|                   GNU LESSER GENERAL PUBLIC LICENSE | 		  GNU LESSER GENERAL PUBLIC LICENSE | ||||||
|                        Version 2.1, February 1999 | 		       Version 2.1, February 1999 | ||||||
|  |  | ||||||
|  Copyright (C) 1991, 1999 Free Software Foundation, Inc. |  Copyright (C) 1991, 1999 Free Software Foundation, Inc. | ||||||
|  51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA | 	51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA | ||||||
|  Everyone is permitted to copy and distribute verbatim copies |  Everyone is permitted to copy and distribute verbatim copies | ||||||
|  of this license document, but changing it is not allowed. |  of this license document, but changing it is not allowed. | ||||||
|  |  | ||||||
| @@ -10,7 +10,7 @@ | |||||||
|  as the successor of the GNU Library Public License, version 2, hence |  as the successor of the GNU Library Public License, version 2, hence | ||||||
|  the version number 2.1.] |  the version number 2.1.] | ||||||
|  |  | ||||||
|                             Preamble | 			    Preamble | ||||||
|  |  | ||||||
|   The licenses for most software are designed to take away your |   The licenses for most software are designed to take away your | ||||||
| freedom to share and change it.  By contrast, the GNU General Public | freedom to share and change it.  By contrast, the GNU General Public | ||||||
| @@ -112,7 +112,7 @@ modification follow.  Pay close attention to the difference between a | |||||||
| former contains code derived from the library, whereas the latter must | former contains code derived from the library, whereas the latter must | ||||||
| be combined with the library in order to run. | be combined with the library in order to run. | ||||||
|  |  | ||||||
|                   GNU LESSER GENERAL PUBLIC LICENSE | 		  GNU LESSER GENERAL PUBLIC LICENSE | ||||||
|    TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION |    TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | ||||||
|  |  | ||||||
|   0. This License Agreement applies to any software library or other |   0. This License Agreement applies to any software library or other | ||||||
| @@ -146,7 +146,7 @@ such a program is covered only if its contents constitute a work based | |||||||
| on the Library (independent of the use of the Library in a tool for | on the Library (independent of the use of the Library in a tool for | ||||||
| writing it).  Whether that is true depends on what the Library does | writing it).  Whether that is true depends on what the Library does | ||||||
| and what the program that uses the Library does. | and what the program that uses the Library does. | ||||||
|  |    | ||||||
|   1. You may copy and distribute verbatim copies of the Library's |   1. You may copy and distribute verbatim copies of the Library's | ||||||
| complete source code as you receive it, in any medium, provided that | complete source code as you receive it, in any medium, provided that | ||||||
| you conspicuously and appropriately publish on each copy an | you conspicuously and appropriately publish on each copy an | ||||||
| @@ -432,7 +432,7 @@ decision will be guided by the two goals of preserving the free status | |||||||
| of all derivatives of our free software and of promoting the sharing | of all derivatives of our free software and of promoting the sharing | ||||||
| and reuse of software generally. | and reuse of software generally. | ||||||
|  |  | ||||||
|                             NO WARRANTY | 			    NO WARRANTY | ||||||
|  |  | ||||||
|   15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO |   15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO | ||||||
| WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. | WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. | ||||||
| @@ -455,7 +455,7 @@ FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF | |||||||
| SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH | SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH | ||||||
| DAMAGES. | DAMAGES. | ||||||
|  |  | ||||||
|                      END OF TERMS AND CONDITIONS | 		     END OF TERMS AND CONDITIONS | ||||||
|  |  | ||||||
|            How to Apply These Terms to Your New Libraries |            How to Apply These Terms to Your New Libraries | ||||||
|  |  | ||||||
| @@ -476,7 +476,7 @@ convey the exclusion of warranty; and each file should have at least the | |||||||
|     This library is free software; you can redistribute it and/or |     This library is free software; you can redistribute it and/or | ||||||
|     modify it under the terms of the GNU Lesser General Public |     modify it under the terms of the GNU Lesser General Public | ||||||
|     License as published by the Free Software Foundation; either |     License as published by the Free Software Foundation; either | ||||||
|     version 2.1 of the License, or (at your option) any later version. |     version 2 of the License, or (at your option) any later version. | ||||||
|  |  | ||||||
|     This library is distributed in the hope that it will be useful, |     This library is distributed in the hope that it will be useful, | ||||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of |     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
| @@ -485,7 +485,7 @@ convey the exclusion of warranty; and each file should have at least the | |||||||
|  |  | ||||||
|     You should have received a copy of the GNU Lesser General Public |     You should have received a copy of the GNU Lesser General Public | ||||||
|     License along with this library; if not, write to the Free Software |     License along with this library; if not, write to the Free Software | ||||||
|     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA |     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA | ||||||
|  |  | ||||||
| Also add information on how to contact you by electronic and paper mail. | Also add information on how to contact you by electronic and paper mail. | ||||||
|  |  | ||||||
| @@ -500,3 +500,5 @@ necessary.  Here is a sample; alter the names: | |||||||
|   Ty Coon, President of Vice |   Ty Coon, President of Vice | ||||||
|  |  | ||||||
| That's all there is to it! | That's all there is to it! | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										270
									
								
								COPYING.PYTHON
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										270
									
								
								COPYING.PYTHON
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,270 @@ | |||||||
|  | A. HISTORY OF THE SOFTWARE | ||||||
|  | ========================== | ||||||
|  |  | ||||||
|  | Python was created in the early 1990s by Guido van Rossum at Stichting | ||||||
|  | Mathematisch Centrum (CWI, see http://www.cwi.nl) in the Netherlands | ||||||
|  | as a successor of a language called ABC.  Guido remains Python's | ||||||
|  | principal author, although it includes many contributions from others. | ||||||
|  |  | ||||||
|  | In 1995, Guido continued his work on Python at the Corporation for | ||||||
|  | National Research Initiatives (CNRI, see http://www.cnri.reston.va.us) | ||||||
|  | in Reston, Virginia where he released several versions of the | ||||||
|  | software. | ||||||
|  |  | ||||||
|  | In May 2000, Guido and the Python core development team moved to | ||||||
|  | BeOpen.com to form the BeOpen PythonLabs team.  In October of the same | ||||||
|  | year, the PythonLabs team moved to Digital Creations (now Zope | ||||||
|  | Corporation, see http://www.zope.com).  In 2001, the Python Software | ||||||
|  | Foundation (PSF, see http://www.python.org/psf/) was formed, a | ||||||
|  | non-profit organization created specifically to own Python-related | ||||||
|  | Intellectual Property.  Zope Corporation is a sponsoring member of | ||||||
|  | the PSF. | ||||||
|  |  | ||||||
|  | All Python releases are Open Source (see http://www.opensource.org for | ||||||
|  | the Open Source Definition).  Historically, most, but not all, Python | ||||||
|  | releases have also been GPL-compatible; the table below summarizes | ||||||
|  | the various releases. | ||||||
|  |  | ||||||
|  |     Release         Derived     Year        Owner       GPL- | ||||||
|  |                     from                                compatible? (1) | ||||||
|  |  | ||||||
|  |     0.9.0 thru 1.2              1991-1995   CWI         yes | ||||||
|  |     1.3 thru 1.5.2  1.2         1995-1999   CNRI        yes | ||||||
|  |     1.6             1.5.2       2000        CNRI        no | ||||||
|  |     2.0             1.6         2000        BeOpen.com  no | ||||||
|  |     1.6.1           1.6         2001        CNRI        yes (2) | ||||||
|  |     2.1             2.0+1.6.1   2001        PSF         no | ||||||
|  |     2.0.1           2.0+1.6.1   2001        PSF         yes | ||||||
|  |     2.1.1           2.1+2.0.1   2001        PSF         yes | ||||||
|  |     2.2             2.1.1       2001        PSF         yes | ||||||
|  |     2.1.2           2.1.1       2002        PSF         yes | ||||||
|  |     2.1.3           2.1.2       2002        PSF         yes | ||||||
|  |     2.2.1           2.2         2002        PSF         yes | ||||||
|  |     2.2.2           2.2.1       2002        PSF         yes | ||||||
|  |     2.2.3           2.2.2       2003        PSF         yes | ||||||
|  |     2.3             2.2.2       2002-2003   PSF         yes | ||||||
|  |     2.3.1           2.3         2002-2003   PSF         yes | ||||||
|  |     2.3.2           2.3.1       2002-2003   PSF         yes | ||||||
|  |     2.3.3           2.3.2       2002-2003   PSF         yes | ||||||
|  |     2.3.4           2.3.3       2004        PSF         yes | ||||||
|  |     2.3.5           2.3.4       2005        PSF         yes | ||||||
|  |     2.4             2.3         2004        PSF         yes | ||||||
|  |     2.4.1           2.4         2005        PSF         yes | ||||||
|  |     2.4.2           2.4.1       2005        PSF         yes | ||||||
|  |     2.4.3           2.4.2       2006        PSF         yes | ||||||
|  |     2.5             2.4         2006        PSF         yes | ||||||
|  |     2.7             2.6         2010        PSF         yes | ||||||
|  |  | ||||||
|  | Footnotes: | ||||||
|  |  | ||||||
|  | (1) GPL-compatible doesn't mean that we're distributing Python under | ||||||
|  |     the GPL.  All Python licenses, unlike the GPL, let you distribute | ||||||
|  |     a modified version without making your changes open source.  The | ||||||
|  |     GPL-compatible licenses make it possible to combine Python with | ||||||
|  |     other software that is released under the GPL; the others don't. | ||||||
|  |  | ||||||
|  | (2) According to Richard Stallman, 1.6.1 is not GPL-compatible, | ||||||
|  |     because its license has a choice of law clause.  According to | ||||||
|  |     CNRI, however, Stallman's lawyer has told CNRI's lawyer that 1.6.1 | ||||||
|  |     is "not incompatible" with the GPL. | ||||||
|  |  | ||||||
|  | Thanks to the many outside volunteers who have worked under Guido's | ||||||
|  | direction to make these releases possible. | ||||||
|  |  | ||||||
|  |  | ||||||
|  | B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON | ||||||
|  | =============================================================== | ||||||
|  |  | ||||||
|  | PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 | ||||||
|  | -------------------------------------------- | ||||||
|  |  | ||||||
|  | 1. This LICENSE AGREEMENT is between the Python Software Foundation | ||||||
|  | ("PSF"), and the Individual or Organization ("Licensee") accessing and | ||||||
|  | otherwise using this software ("Python") in source or binary form and | ||||||
|  | its associated documentation. | ||||||
|  |  | ||||||
|  | 2. Subject to the terms and conditions of this License Agreement, PSF | ||||||
|  | hereby grants Licensee a nonexclusive, royalty-free, world-wide | ||||||
|  | license to reproduce, analyze, test, perform and/or display publicly, | ||||||
|  | prepare derivative works, distribute, and otherwise use Python | ||||||
|  | alone or in any derivative version, provided, however, that PSF's | ||||||
|  | License Agreement and PSF's notice of copyright, i.e., "Copyright (c) | ||||||
|  | 2001, 2002, 2003, 2004, 2005, 2006 Python Software Foundation; All Rights | ||||||
|  | Reserved" are retained in Python alone or in any derivative version  | ||||||
|  | prepared by Licensee. | ||||||
|  |  | ||||||
|  | 3. In the event Licensee prepares a derivative work that is based on | ||||||
|  | or incorporates Python or any part thereof, and wants to make | ||||||
|  | the derivative work available to others as provided herein, then | ||||||
|  | Licensee hereby agrees to include in any such work a brief summary of | ||||||
|  | the changes made to Python. | ||||||
|  |  | ||||||
|  | 4. PSF is making Python available to Licensee on an "AS IS" | ||||||
|  | basis.  PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR | ||||||
|  | IMPLIED.  BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND | ||||||
|  | DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS | ||||||
|  | FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT | ||||||
|  | INFRINGE ANY THIRD PARTY RIGHTS. | ||||||
|  |  | ||||||
|  | 5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON | ||||||
|  | FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS | ||||||
|  | A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON, | ||||||
|  | OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. | ||||||
|  |  | ||||||
|  | 6. This License Agreement will automatically terminate upon a material | ||||||
|  | breach of its terms and conditions. | ||||||
|  |  | ||||||
|  | 7. Nothing in this License Agreement shall be deemed to create any | ||||||
|  | relationship of agency, partnership, or joint venture between PSF and | ||||||
|  | Licensee.  This License Agreement does not grant permission to use PSF | ||||||
|  | trademarks or trade name in a trademark sense to endorse or promote | ||||||
|  | products or services of Licensee, or any third party. | ||||||
|  |  | ||||||
|  | 8. By copying, installing or otherwise using Python, Licensee | ||||||
|  | agrees to be bound by the terms and conditions of this License | ||||||
|  | Agreement. | ||||||
|  |  | ||||||
|  |  | ||||||
|  | BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0 | ||||||
|  | ------------------------------------------- | ||||||
|  |  | ||||||
|  | BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1 | ||||||
|  |  | ||||||
|  | 1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an | ||||||
|  | office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the | ||||||
|  | Individual or Organization ("Licensee") accessing and otherwise using | ||||||
|  | this software in source or binary form and its associated | ||||||
|  | documentation ("the Software"). | ||||||
|  |  | ||||||
|  | 2. Subject to the terms and conditions of this BeOpen Python License | ||||||
|  | Agreement, BeOpen hereby grants Licensee a non-exclusive, | ||||||
|  | royalty-free, world-wide license to reproduce, analyze, test, perform | ||||||
|  | and/or display publicly, prepare derivative works, distribute, and | ||||||
|  | otherwise use the Software alone or in any derivative version, | ||||||
|  | provided, however, that the BeOpen Python License is retained in the | ||||||
|  | Software, alone or in any derivative version prepared by Licensee. | ||||||
|  |  | ||||||
|  | 3. BeOpen is making the Software available to Licensee on an "AS IS" | ||||||
|  | basis.  BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR | ||||||
|  | IMPLIED.  BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND | ||||||
|  | DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS | ||||||
|  | FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT | ||||||
|  | INFRINGE ANY THIRD PARTY RIGHTS. | ||||||
|  |  | ||||||
|  | 4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE | ||||||
|  | SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS | ||||||
|  | AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY | ||||||
|  | DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. | ||||||
|  |  | ||||||
|  | 5. This License Agreement will automatically terminate upon a material | ||||||
|  | breach of its terms and conditions. | ||||||
|  |  | ||||||
|  | 6. This License Agreement shall be governed by and interpreted in all | ||||||
|  | respects by the law of the State of California, excluding conflict of | ||||||
|  | law provisions.  Nothing in this License Agreement shall be deemed to | ||||||
|  | create any relationship of agency, partnership, or joint venture | ||||||
|  | between BeOpen and Licensee.  This License Agreement does not grant | ||||||
|  | permission to use BeOpen trademarks or trade names in a trademark | ||||||
|  | sense to endorse or promote products or services of Licensee, or any | ||||||
|  | third party.  As an exception, the "BeOpen Python" logos available at | ||||||
|  | http://www.pythonlabs.com/logos.html may be used according to the | ||||||
|  | permissions granted on that web page. | ||||||
|  |  | ||||||
|  | 7. By copying, installing or otherwise using the software, Licensee | ||||||
|  | agrees to be bound by the terms and conditions of this License | ||||||
|  | Agreement. | ||||||
|  |  | ||||||
|  |  | ||||||
|  | CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1 | ||||||
|  | --------------------------------------- | ||||||
|  |  | ||||||
|  | 1. This LICENSE AGREEMENT is between the Corporation for National | ||||||
|  | Research Initiatives, having an office at 1895 Preston White Drive, | ||||||
|  | Reston, VA 20191 ("CNRI"), and the Individual or Organization | ||||||
|  | ("Licensee") accessing and otherwise using Python 1.6.1 software in | ||||||
|  | source or binary form and its associated documentation. | ||||||
|  |  | ||||||
|  | 2. Subject to the terms and conditions of this License Agreement, CNRI | ||||||
|  | hereby grants Licensee a nonexclusive, royalty-free, world-wide | ||||||
|  | license to reproduce, analyze, test, perform and/or display publicly, | ||||||
|  | prepare derivative works, distribute, and otherwise use Python 1.6.1 | ||||||
|  | alone or in any derivative version, provided, however, that CNRI's | ||||||
|  | License Agreement and CNRI's notice of copyright, i.e., "Copyright (c) | ||||||
|  | 1995-2001 Corporation for National Research Initiatives; All Rights | ||||||
|  | Reserved" are retained in Python 1.6.1 alone or in any derivative | ||||||
|  | version prepared by Licensee.  Alternately, in lieu of CNRI's License | ||||||
|  | Agreement, Licensee may substitute the following text (omitting the | ||||||
|  | quotes): "Python 1.6.1 is made available subject to the terms and | ||||||
|  | conditions in CNRI's License Agreement.  This Agreement together with | ||||||
|  | Python 1.6.1 may be located on the Internet using the following | ||||||
|  | unique, persistent identifier (known as a handle): 1895.22/1013.  This | ||||||
|  | Agreement may also be obtained from a proxy server on the Internet | ||||||
|  | using the following URL: http://hdl.handle.net/1895.22/1013". | ||||||
|  |  | ||||||
|  | 3. In the event Licensee prepares a derivative work that is based on | ||||||
|  | or incorporates Python 1.6.1 or any part thereof, and wants to make | ||||||
|  | the derivative work available to others as provided herein, then | ||||||
|  | Licensee hereby agrees to include in any such work a brief summary of | ||||||
|  | the changes made to Python 1.6.1. | ||||||
|  |  | ||||||
|  | 4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS" | ||||||
|  | basis.  CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR | ||||||
|  | IMPLIED.  BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND | ||||||
|  | DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS | ||||||
|  | FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT | ||||||
|  | INFRINGE ANY THIRD PARTY RIGHTS. | ||||||
|  |  | ||||||
|  | 5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON | ||||||
|  | 1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS | ||||||
|  | A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1, | ||||||
|  | OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. | ||||||
|  |  | ||||||
|  | 6. This License Agreement will automatically terminate upon a material | ||||||
|  | breach of its terms and conditions. | ||||||
|  |  | ||||||
|  | 7. This License Agreement shall be governed by the federal | ||||||
|  | intellectual property law of the United States, including without | ||||||
|  | limitation the federal copyright law, and, to the extent such | ||||||
|  | U.S. federal law does not apply, by the law of the Commonwealth of | ||||||
|  | Virginia, excluding Virginia's conflict of law provisions. | ||||||
|  | Notwithstanding the foregoing, with regard to derivative works based | ||||||
|  | on Python 1.6.1 that incorporate non-separable material that was | ||||||
|  | previously distributed under the GNU General Public License (GPL), the | ||||||
|  | law of the Commonwealth of Virginia shall govern this License | ||||||
|  | Agreement only as to issues arising under or with respect to | ||||||
|  | Paragraphs 4, 5, and 7 of this License Agreement.  Nothing in this | ||||||
|  | License Agreement shall be deemed to create any relationship of | ||||||
|  | agency, partnership, or joint venture between CNRI and Licensee.  This | ||||||
|  | License Agreement does not grant permission to use CNRI trademarks or | ||||||
|  | trade name in a trademark sense to endorse or promote products or | ||||||
|  | services of Licensee, or any third party. | ||||||
|  |  | ||||||
|  | 8. By clicking on the "ACCEPT" button where indicated, or by copying, | ||||||
|  | installing or otherwise using Python 1.6.1, Licensee agrees to be | ||||||
|  | bound by the terms and conditions of this License Agreement. | ||||||
|  |  | ||||||
|  |         ACCEPT | ||||||
|  |  | ||||||
|  |  | ||||||
|  | CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2 | ||||||
|  | -------------------------------------------------- | ||||||
|  |  | ||||||
|  | Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam, | ||||||
|  | The Netherlands.  All rights reserved. | ||||||
|  |  | ||||||
|  | Permission to use, copy, modify, and distribute this software and its | ||||||
|  | documentation for any purpose and without fee is hereby granted, | ||||||
|  | provided that the above copyright notice appear in all copies and that | ||||||
|  | both that copyright notice and this permission notice appear in | ||||||
|  | supporting documentation, and that the name of Stichting Mathematisch | ||||||
|  | Centrum or CWI not be used in advertising or publicity pertaining to | ||||||
|  | distribution of the software without specific, written prior | ||||||
|  | permission. | ||||||
|  |  | ||||||
|  | STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO | ||||||
|  | THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND | ||||||
|  | FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE | ||||||
|  | FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||||||
|  | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||||||
|  | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT | ||||||
|  | OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||||||
							
								
								
									
										236
									
								
								HACKING
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										236
									
								
								HACKING
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,236 @@ | |||||||
|  | 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. | ||||||
|  |  | ||||||
|  | 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(). | ||||||
							
								
								
									
										37
									
								
								Kconfig.host
									
									
									
									
									
								
							
							
						
						
									
										37
									
								
								Kconfig.host
									
									
									
									
									
								
							| @@ -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
									
									
									
									
									
								
							
							
						
						
									
										26
									
								
								LICENSE
									
									
									
									
									
								
							| @@ -1,26 +1,20 @@ | |||||||
| The QEMU distribution includes both the QEMU emulator and | The following points clarify the QEMU license: | ||||||
| various firmware files.  These are separate programs that are |  | ||||||
| distributed together for our users' convenience, and they have |  | ||||||
| separate licenses. |  | ||||||
|  |  | ||||||
| The following points clarify the license of the QEMU emulator: | 1) QEMU as a whole is released under the GNU General Public License, | ||||||
|  | version 2. | ||||||
|  |  | ||||||
| 1) The QEMU emulator as a whole is released under the GNU General | 2) Parts of QEMU have specific licenses which are compatible with the | ||||||
| Public License, version 2. | GNU General Public License, version 2. Hence each source file contains | ||||||
|  | its own licensing information.  Source files with no licensing information | ||||||
| 2) Parts of the QEMU emulator have specific licenses which are compatible | are released under the GNU General Public License, version 2 or (at your | ||||||
| with the GNU General Public License, version 2. Hence each source file | option) any later version. | ||||||
| contains its own licensing information.  Source files with no licensing |  | ||||||
| information are released under the GNU General Public License, version |  | ||||||
| 2 or (at your option) any later version. |  | ||||||
|  |  | ||||||
| As of July 2013, contributions under version 2 of the GNU General Public | As of July 2013, contributions under version 2 of the GNU General Public | ||||||
| License (and no later version) are only accepted for the following files | License (and no later version) are only accepted for the following files | ||||||
| or directories: bsd-user/, linux-user/, hw/vfio/, hw/xen/xen_pt*. | or directories: bsd-user/, linux-user/, hw/vfio/, hw/xen/xen_pt*. | ||||||
|  |  | ||||||
| 3) The Tiny Code Generator (TCG) is mostly under the BSD or MIT licenses; | 3) The Tiny Code Generator (TCG) is released under the BSD license | ||||||
|    but some parts may be GPLv2 or other licenses. Again, see the |    (see license headers in files). | ||||||
|    specific licensing information in each source file. |  | ||||||
|  |  | ||||||
| 4) QEMU is a trademark of Fabrice Bellard. | 4) QEMU is a trademark of Fabrice Bellard. | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										1166
									
								
								MAINTAINERS
									
									
									
									
									
								
							
							
						
						
									
										1166
									
								
								MAINTAINERS
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										194
									
								
								Makefile.objs
									
									
									
									
									
								
							
							
						
						
									
										194
									
								
								Makefile.objs
									
									
									
									
									
								
							| @@ -1,20 +1,69 @@ | |||||||
| ####################################################################### | ####################################################################### | ||||||
| # Common libraries for tools and emulators | # Common libraries for tools and emulators | ||||||
| stub-obj-y = stubs/ util/ crypto/ | stub-obj-y = stubs/ crypto/ | ||||||
| util-obj-y = util/ qobject/ qapi/ | util-obj-y = util/ qobject/ qapi/ | ||||||
|  | util-obj-y += qapi/qapi-builtin-types.o | ||||||
|  | util-obj-y += qapi/qapi-types.o | ||||||
|  | util-obj-y += qapi/qapi-types-block-core.o | ||||||
|  | util-obj-y += qapi/qapi-types-block.o | ||||||
|  | util-obj-y += qapi/qapi-types-char.o | ||||||
|  | util-obj-y += qapi/qapi-types-common.o | ||||||
|  | util-obj-y += qapi/qapi-types-crypto.o | ||||||
|  | util-obj-y += qapi/qapi-types-introspect.o | ||||||
|  | util-obj-y += qapi/qapi-types-migration.o | ||||||
|  | util-obj-y += qapi/qapi-types-misc.o | ||||||
|  | util-obj-y += qapi/qapi-types-net.o | ||||||
|  | util-obj-y += qapi/qapi-types-rocker.o | ||||||
|  | util-obj-y += qapi/qapi-types-run-state.o | ||||||
|  | util-obj-y += qapi/qapi-types-sockets.o | ||||||
|  | util-obj-y += qapi/qapi-types-tpm.o | ||||||
|  | util-obj-y += qapi/qapi-types-trace.o | ||||||
|  | util-obj-y += qapi/qapi-types-transaction.o | ||||||
|  | util-obj-y += qapi/qapi-types-ui.o | ||||||
|  | util-obj-y += qapi/qapi-builtin-visit.o | ||||||
|  | util-obj-y += qapi/qapi-visit.o | ||||||
|  | util-obj-y += qapi/qapi-visit-block-core.o | ||||||
|  | util-obj-y += qapi/qapi-visit-block.o | ||||||
|  | util-obj-y += qapi/qapi-visit-char.o | ||||||
|  | util-obj-y += qapi/qapi-visit-common.o | ||||||
|  | util-obj-y += qapi/qapi-visit-crypto.o | ||||||
|  | util-obj-y += qapi/qapi-visit-introspect.o | ||||||
|  | util-obj-y += qapi/qapi-visit-migration.o | ||||||
|  | util-obj-y += qapi/qapi-visit-misc.o | ||||||
|  | util-obj-y += qapi/qapi-visit-net.o | ||||||
|  | util-obj-y += qapi/qapi-visit-rocker.o | ||||||
|  | util-obj-y += qapi/qapi-visit-run-state.o | ||||||
|  | util-obj-y += qapi/qapi-visit-sockets.o | ||||||
|  | util-obj-y += qapi/qapi-visit-tpm.o | ||||||
|  | util-obj-y += qapi/qapi-visit-trace.o | ||||||
|  | util-obj-y += qapi/qapi-visit-transaction.o | ||||||
|  | util-obj-y += qapi/qapi-visit-ui.o | ||||||
|  | util-obj-y += qapi/qapi-events.o | ||||||
|  | util-obj-y += qapi/qapi-events-block-core.o | ||||||
|  | util-obj-y += qapi/qapi-events-block.o | ||||||
|  | util-obj-y += qapi/qapi-events-char.o | ||||||
|  | util-obj-y += qapi/qapi-events-common.o | ||||||
|  | util-obj-y += qapi/qapi-events-crypto.o | ||||||
|  | util-obj-y += qapi/qapi-events-introspect.o | ||||||
|  | util-obj-y += qapi/qapi-events-migration.o | ||||||
|  | util-obj-y += qapi/qapi-events-misc.o | ||||||
|  | util-obj-y += qapi/qapi-events-net.o | ||||||
|  | util-obj-y += qapi/qapi-events-rocker.o | ||||||
|  | util-obj-y += qapi/qapi-events-run-state.o | ||||||
|  | util-obj-y += qapi/qapi-events-sockets.o | ||||||
|  | util-obj-y += qapi/qapi-events-tpm.o | ||||||
|  | util-obj-y += qapi/qapi-events-trace.o | ||||||
|  | util-obj-y += qapi/qapi-events-transaction.o | ||||||
|  | util-obj-y += qapi/qapi-events-ui.o | ||||||
|  | util-obj-y += qapi/qapi-introspect.o | ||||||
|  |  | ||||||
| chardev-obj-y = chardev/ | chardev-obj-y = chardev/ | ||||||
|  |  | ||||||
| ####################################################################### |  | ||||||
| # authz-obj-y is code used by both qemu system emulation and qemu-img |  | ||||||
|  |  | ||||||
| authz-obj-y = authz/ |  | ||||||
|  |  | ||||||
| ####################################################################### | ####################################################################### | ||||||
| # block-obj-y is code used by both qemu system emulation and qemu-img | # 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.o blockjob.o | ||||||
| block-obj-y += block/ scsi/ | block-obj-y += block/ scsi/ | ||||||
| block-obj-y += qemu-io-cmds.o | block-obj-y += qemu-io-cmds.o | ||||||
| block-obj-$(CONFIG_REPLICATION) += replication.o | block-obj-$(CONFIG_REPLICATION) += replication.o | ||||||
| @@ -25,7 +74,7 @@ block-obj-m = block/ | |||||||
| # crypto-obj-y is code used by both qemu system emulation and qemu-img | # crypto-obj-y is code used by both qemu system emulation and qemu-img | ||||||
|  |  | ||||||
| crypto-obj-y = crypto/ | 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 | # qom-obj-y is code used by both qemu system emulation and qemu-img | ||||||
| @@ -45,9 +94,6 @@ io-obj-y = io/ | |||||||
| ifeq ($(CONFIG_SOFTMMU),y) | ifeq ($(CONFIG_SOFTMMU),y) | ||||||
| common-obj-y = blockdev.o blockdev-nbd.o block/ | common-obj-y = blockdev.o blockdev-nbd.o block/ | ||||||
| common-obj-y += bootdevice.o iothread.o | common-obj-y += bootdevice.o iothread.o | ||||||
| common-obj-y += dump/ |  | ||||||
| common-obj-y += job-qmp.o |  | ||||||
| common-obj-y += monitor/ |  | ||||||
| common-obj-y += net/ | common-obj-y += net/ | ||||||
| common-obj-y += qdev-monitor.o device-hotplug.o | common-obj-y += qdev-monitor.o device-hotplug.o | ||||||
| common-obj-$(CONFIG_WIN32) += os-win32.o | common-obj-$(CONFIG_WIN32) += os-win32.o | ||||||
| @@ -73,6 +119,8 @@ common-obj-y += vl.o | |||||||
| vl.o-cflags := $(GPROF_CFLAGS) $(SDL_CFLAGS) | vl.o-cflags := $(GPROF_CFLAGS) $(SDL_CFLAGS) | ||||||
| common-obj-$(CONFIG_TPM) += tpm.o | common-obj-$(CONFIG_TPM) += tpm.o | ||||||
|  |  | ||||||
|  | common-obj-$(CONFIG_SLIRP) += slirp/ | ||||||
|  |  | ||||||
| common-obj-y += backends/ | common-obj-y += backends/ | ||||||
| common-obj-y += chardev/ | common-obj-y += chardev/ | ||||||
|  |  | ||||||
| @@ -85,7 +133,25 @@ common-obj-$(CONFIG_FDT) += device_tree.o | |||||||
| ###################################################################### | ###################################################################### | ||||||
| # qapi | # qapi | ||||||
|  |  | ||||||
| common-obj-y += qapi/ | common-obj-y += qapi/qapi-commands.o | ||||||
|  | common-obj-y += qapi/qapi-commands-block-core.o | ||||||
|  | common-obj-y += qapi/qapi-commands-block.o | ||||||
|  | common-obj-y += qapi/qapi-commands-char.o | ||||||
|  | common-obj-y += qapi/qapi-commands-common.o | ||||||
|  | common-obj-y += qapi/qapi-commands-crypto.o | ||||||
|  | common-obj-y += qapi/qapi-commands-introspect.o | ||||||
|  | common-obj-y += qapi/qapi-commands-migration.o | ||||||
|  | common-obj-y += qapi/qapi-commands-misc.o | ||||||
|  | common-obj-y += qapi/qapi-commands-net.o | ||||||
|  | common-obj-y += qapi/qapi-commands-rocker.o | ||||||
|  | common-obj-y += qapi/qapi-commands-run-state.o | ||||||
|  | common-obj-y += qapi/qapi-commands-sockets.o | ||||||
|  | common-obj-y += qapi/qapi-commands-tpm.o | ||||||
|  | common-obj-y += qapi/qapi-commands-trace.o | ||||||
|  | common-obj-y += qapi/qapi-commands-transaction.o | ||||||
|  | common-obj-y += qapi/qapi-commands-ui.o | ||||||
|  | common-obj-y += qapi/qapi-introspect.o | ||||||
|  | common-obj-y += qmp.o hmp.o | ||||||
| endif | endif | ||||||
|  |  | ||||||
| ####################################################################### | ####################################################################### | ||||||
| @@ -102,6 +168,7 @@ version-obj-$(CONFIG_WIN32) += $(BUILD_DIR)/version.o | |||||||
| ###################################################################### | ###################################################################### | ||||||
| # tracing | # tracing | ||||||
| util-obj-y +=  trace/ | util-obj-y +=  trace/ | ||||||
|  | target-obj-y += trace/ | ||||||
|  |  | ||||||
| ###################################################################### | ###################################################################### | ||||||
| # guest agent | # guest agent | ||||||
| @@ -114,7 +181,6 @@ qga-vss-dll-obj-y = qga/ | |||||||
|  |  | ||||||
| ###################################################################### | ###################################################################### | ||||||
| # contrib | # contrib | ||||||
| elf2dmp-obj-y = contrib/elf2dmp/ |  | ||||||
| ivshmem-client-obj-$(CONFIG_IVSHMEM) = contrib/ivshmem-client/ | ivshmem-client-obj-$(CONFIG_IVSHMEM) = contrib/ivshmem-client/ | ||||||
| ivshmem-server-obj-$(CONFIG_IVSHMEM) = contrib/ivshmem-server/ | ivshmem-server-obj-$(CONFIG_IVSHMEM) = contrib/ivshmem-server/ | ||||||
| libvhost-user-obj-y = contrib/libvhost-user/ | libvhost-user-obj-y = contrib/libvhost-user/ | ||||||
| @@ -122,89 +188,69 @@ vhost-user-scsi.o-cflags := $(LIBISCSI_CFLAGS) | |||||||
| vhost-user-scsi.o-libs := $(LIBISCSI_LIBS) | vhost-user-scsi.o-libs := $(LIBISCSI_LIBS) | ||||||
| vhost-user-scsi-obj-y = contrib/vhost-user-scsi/ | vhost-user-scsi-obj-y = contrib/vhost-user-scsi/ | ||||||
| vhost-user-blk-obj-y = contrib/vhost-user-blk/ | vhost-user-blk-obj-y = contrib/vhost-user-blk/ | ||||||
| rdmacm-mux-obj-y = contrib/rdmacm-mux/ |  | ||||||
| vhost-user-input-obj-y = contrib/vhost-user-input/ |  | ||||||
| vhost-user-gpu-obj-y = contrib/vhost-user-gpu/ |  | ||||||
|  |  | ||||||
| ###################################################################### | ###################################################################### | ||||||
| trace-events-subdirs = | trace-events-subdirs = | ||||||
| trace-events-subdirs += accel/kvm | trace-events-subdirs += util | ||||||
| trace-events-subdirs += accel/tcg |  | ||||||
| trace-events-subdirs += crypto | trace-events-subdirs += crypto | ||||||
| trace-events-subdirs += monitor |  | ||||||
| ifeq ($(CONFIG_USER_ONLY),y) |  | ||||||
| trace-events-subdirs += linux-user |  | ||||||
| endif |  | ||||||
| ifeq ($(CONFIG_BLOCK),y) |  | ||||||
| trace-events-subdirs += authz |  | ||||||
| trace-events-subdirs += block |  | ||||||
| trace-events-subdirs += io | trace-events-subdirs += io | ||||||
| trace-events-subdirs += nbd | trace-events-subdirs += migration | ||||||
| trace-events-subdirs += scsi | trace-events-subdirs += block | ||||||
| endif |  | ||||||
| ifeq ($(CONFIG_SOFTMMU),y) |  | ||||||
| trace-events-subdirs += chardev | trace-events-subdirs += chardev | ||||||
| trace-events-subdirs += audio |  | ||||||
| trace-events-subdirs += hw/9pfs |  | ||||||
| trace-events-subdirs += hw/acpi |  | ||||||
| trace-events-subdirs += hw/alpha |  | ||||||
| trace-events-subdirs += hw/arm |  | ||||||
| trace-events-subdirs += hw/audio |  | ||||||
| trace-events-subdirs += hw/block | trace-events-subdirs += hw/block | ||||||
| trace-events-subdirs += hw/block/dataplane | trace-events-subdirs += hw/block/dataplane | ||||||
| trace-events-subdirs += hw/char | trace-events-subdirs += hw/char | ||||||
| trace-events-subdirs += hw/dma |  | ||||||
| trace-events-subdirs += hw/hppa |  | ||||||
| trace-events-subdirs += hw/i2c |  | ||||||
| trace-events-subdirs += hw/i386 |  | ||||||
| trace-events-subdirs += hw/i386/xen |  | ||||||
| trace-events-subdirs += hw/ide |  | ||||||
| trace-events-subdirs += hw/input |  | ||||||
| trace-events-subdirs += hw/intc | 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 | trace-events-subdirs += hw/net | ||||||
| trace-events-subdirs += hw/nvram |  | ||||||
| trace-events-subdirs += hw/pci |  | ||||||
| trace-events-subdirs += hw/pci-host |  | ||||||
| trace-events-subdirs += hw/ppc |  | ||||||
| trace-events-subdirs += hw/rdma | trace-events-subdirs += hw/rdma | ||||||
| trace-events-subdirs += hw/rdma/vmw | trace-events-subdirs += hw/rdma/vmw | ||||||
| trace-events-subdirs += hw/rtc | trace-events-subdirs += hw/virtio | ||||||
| trace-events-subdirs += hw/s390x | trace-events-subdirs += hw/audio | ||||||
|  | trace-events-subdirs += hw/misc | ||||||
|  | trace-events-subdirs += hw/misc/macio | ||||||
|  | trace-events-subdirs += hw/usb | ||||||
| trace-events-subdirs += hw/scsi | trace-events-subdirs += hw/scsi | ||||||
| trace-events-subdirs += hw/sd | trace-events-subdirs += hw/nvram | ||||||
|  | trace-events-subdirs += hw/display | ||||||
|  | trace-events-subdirs += hw/input | ||||||
|  | trace-events-subdirs += hw/timer | ||||||
|  | trace-events-subdirs += hw/dma | ||||||
| trace-events-subdirs += hw/sparc | trace-events-subdirs += hw/sparc | ||||||
| trace-events-subdirs += hw/sparc64 | trace-events-subdirs += hw/sparc64 | ||||||
| trace-events-subdirs += hw/timer | trace-events-subdirs += hw/sd | ||||||
| trace-events-subdirs += hw/tpm | trace-events-subdirs += hw/isa | ||||||
| trace-events-subdirs += hw/usb | trace-events-subdirs += hw/mem | ||||||
|  | trace-events-subdirs += hw/i386 | ||||||
|  | trace-events-subdirs += hw/i386/xen | ||||||
|  | trace-events-subdirs += hw/9pfs | ||||||
|  | trace-events-subdirs += hw/ppc | ||||||
|  | trace-events-subdirs += hw/pci | ||||||
|  | trace-events-subdirs += hw/pci-host | ||||||
|  | trace-events-subdirs += hw/s390x | ||||||
| trace-events-subdirs += hw/vfio | trace-events-subdirs += hw/vfio | ||||||
| trace-events-subdirs += hw/virtio | trace-events-subdirs += hw/acpi | ||||||
| trace-events-subdirs += hw/watchdog | trace-events-subdirs += hw/arm | ||||||
|  | trace-events-subdirs += hw/alpha | ||||||
|  | trace-events-subdirs += hw/hppa | ||||||
| trace-events-subdirs += hw/xen | trace-events-subdirs += hw/xen | ||||||
| trace-events-subdirs += hw/gpio | trace-events-subdirs += hw/ide | ||||||
| trace-events-subdirs += hw/riscv | trace-events-subdirs += hw/tpm | ||||||
| trace-events-subdirs += migration |  | ||||||
| trace-events-subdirs += net |  | ||||||
| trace-events-subdirs += ui | trace-events-subdirs += ui | ||||||
| endif | trace-events-subdirs += audio | ||||||
| trace-events-subdirs += hw/display | trace-events-subdirs += net | ||||||
| trace-events-subdirs += qapi |  | ||||||
| trace-events-subdirs += qom |  | ||||||
| trace-events-subdirs += target/arm | trace-events-subdirs += target/arm | ||||||
| trace-events-subdirs += target/hppa |  | ||||||
| trace-events-subdirs += target/i386 | trace-events-subdirs += target/i386 | ||||||
| trace-events-subdirs += target/mips | trace-events-subdirs += target/mips | ||||||
| trace-events-subdirs += target/ppc |  | ||||||
| trace-events-subdirs += target/riscv |  | ||||||
| trace-events-subdirs += target/s390x |  | ||||||
| trace-events-subdirs += target/sparc | trace-events-subdirs += target/sparc | ||||||
| trace-events-subdirs += util | trace-events-subdirs += target/s390x | ||||||
| trace-events-subdirs += hw/core | trace-events-subdirs += target/ppc | ||||||
|  | trace-events-subdirs += qom | ||||||
|  | trace-events-subdirs += linux-user | ||||||
|  | trace-events-subdirs += qapi | ||||||
|  | trace-events-subdirs += accel/tcg | ||||||
|  | trace-events-subdirs += accel/kvm | ||||||
|  | trace-events-subdirs += nbd | ||||||
|  | trace-events-subdirs += scsi | ||||||
|  |  | ||||||
| trace-events-files = $(SRC_PATH)/trace-events $(trace-events-subdirs:%=$(SRC_PATH)/%/trace-events) | trace-events-files = $(SRC_PATH)/trace-events $(trace-events-subdirs:%=$(SRC_PATH)/%/trace-events) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -4,19 +4,16 @@ BUILD_DIR?=$(CURDIR)/.. | |||||||
|  |  | ||||||
| include ../config-host.mak | include ../config-host.mak | ||||||
| include config-target.mak | include config-target.mak | ||||||
| include $(SRC_PATH)/rules.mak |  | ||||||
|  |  | ||||||
| ifdef CONFIG_SOFTMMU |  | ||||||
| include config-devices.mak | include config-devices.mak | ||||||
| endif | include $(SRC_PATH)/rules.mak | ||||||
|  |  | ||||||
| $(call set-vpath, $(SRC_PATH):$(BUILD_DIR)) | $(call set-vpath, $(SRC_PATH):$(BUILD_DIR)) | ||||||
| ifdef CONFIG_LINUX | ifdef CONFIG_LINUX | ||||||
| QEMU_CFLAGS += -I../linux-headers | QEMU_CFLAGS += -I../linux-headers | ||||||
| endif | endif | ||||||
| QEMU_CFLAGS += -iquote .. -iquote $(SRC_PATH)/target/$(TARGET_BASE_ARCH) -DNEED_CPU_H | QEMU_CFLAGS += -I.. -I$(SRC_PATH)/target/$(TARGET_BASE_ARCH) -DNEED_CPU_H | ||||||
|  |  | ||||||
| QEMU_CFLAGS+=-iquote $(SRC_PATH)/include | QEMU_CFLAGS+=-I$(SRC_PATH)/include | ||||||
|  |  | ||||||
| ifdef CONFIG_USER_ONLY | ifdef CONFIG_USER_ONLY | ||||||
| # user emulator name | # user emulator name | ||||||
| @@ -42,11 +39,8 @@ STPFILES= | |||||||
| config-target.h: config-target.h-timestamp | config-target.h: config-target.h-timestamp | ||||||
| config-target.h-timestamp: config-target.mak | config-target.h-timestamp: config-target.mak | ||||||
|  |  | ||||||
| config-devices.h: config-devices.h-timestamp |  | ||||||
| config-devices.h-timestamp: config-devices.mak |  | ||||||
|  |  | ||||||
| ifdef CONFIG_TRACE_SYSTEMTAP | ifdef CONFIG_TRACE_SYSTEMTAP | ||||||
| stap: $(QEMU_PROG).stp-installed $(QEMU_PROG).stp $(QEMU_PROG)-simpletrace.stp $(QEMU_PROG)-log.stp | stap: $(QEMU_PROG).stp-installed $(QEMU_PROG).stp $(QEMU_PROG)-simpletrace.stp | ||||||
|  |  | ||||||
| ifdef CONFIG_USER_ONLY | ifdef CONFIG_USER_ONLY | ||||||
| TARGET_TYPE=user | TARGET_TYPE=user | ||||||
| @@ -85,14 +79,6 @@ $(QEMU_PROG)-simpletrace.stp: $(BUILD_DIR)/trace-events-all $(tracetool-y) | |||||||
| 		--probe-prefix=qemu.$(TARGET_TYPE).$(TARGET_NAME) \ | 		--probe-prefix=qemu.$(TARGET_TYPE).$(TARGET_NAME) \ | ||||||
| 		$< > $@,"GEN","$(TARGET_DIR)$(QEMU_PROG)-simpletrace.stp") | 		$< > $@,"GEN","$(TARGET_DIR)$(QEMU_PROG)-simpletrace.stp") | ||||||
|  |  | ||||||
| $(QEMU_PROG)-log.stp: $(BUILD_DIR)/trace-events-all $(tracetool-y) |  | ||||||
| 	$(call quiet-command,$(TRACETOOL) \ |  | ||||||
| 		--group=all \ |  | ||||||
| 		--format=log-stap \ |  | ||||||
| 		--backends=$(TRACE_BACKENDS) \ |  | ||||||
| 		--probe-prefix=qemu.$(TARGET_TYPE).$(TARGET_NAME) \ |  | ||||||
| 		$< > $@,"GEN","$(TARGET_DIR)$(QEMU_PROG)-log.stp") |  | ||||||
|  |  | ||||||
| else | else | ||||||
| stap: | stap: | ||||||
| endif | endif | ||||||
| @@ -103,23 +89,18 @@ all: $(PROGS) stap | |||||||
| # Dummy command so that make thinks it has done something | # Dummy command so that make thinks it has done something | ||||||
| 	@true | 	@true | ||||||
|  |  | ||||||
| obj-y += trace/ |  | ||||||
|  |  | ||||||
| ######################################################### | ######################################################### | ||||||
| # cpu emulator library | # cpu emulator library | ||||||
| obj-y += exec.o exec-vary.o | obj-y += exec.o | ||||||
| obj-y += accel/ | obj-y += accel/ | ||||||
| obj-$(CONFIG_TCG) += tcg/tcg.o tcg/tcg-op.o tcg/tcg-op-vec.o tcg/tcg-op-gvec.o | obj-$(CONFIG_TCG) += tcg/tcg.o tcg/tcg-op.o tcg/tcg-op-vec.o tcg/tcg-op-gvec.o | ||||||
| obj-$(CONFIG_TCG) += tcg/tcg-common.o tcg/optimize.o | obj-$(CONFIG_TCG) += tcg/tcg-common.o tcg/optimize.o | ||||||
| obj-$(CONFIG_TCG_INTERPRETER) += tcg/tci.o | obj-$(CONFIG_TCG_INTERPRETER) += tcg/tci.o | ||||||
| obj-$(CONFIG_TCG_INTERPRETER) += disas/tci.o | obj-$(CONFIG_TCG_INTERPRETER) += disas/tci.o | ||||||
| obj-$(CONFIG_TCG) += fpu/softfloat.o | obj-y += fpu/softfloat.o | ||||||
| obj-y += target/$(TARGET_BASE_ARCH)/ | obj-y += target/$(TARGET_BASE_ARCH)/ | ||||||
| obj-y += disas.o | obj-y += disas.o | ||||||
| obj-$(call notempty,$(TARGET_XML_FILES)) += gdbstub-xml.o | obj-$(call notempty,$(TARGET_XML_FILES)) += gdbstub-xml.o | ||||||
| LIBS := $(libs_cpu) $(LIBS) |  | ||||||
|  |  | ||||||
| obj-$(CONFIG_PLUGIN) += plugins/ |  | ||||||
|  |  | ||||||
| ######################################################### | ######################################################### | ||||||
| # Linux user emulator target | # Linux user emulator target | ||||||
| @@ -151,14 +132,12 @@ endif #CONFIG_BSD_USER | |||||||
| ######################################################### | ######################################################### | ||||||
| # System emulator target | # System emulator target | ||||||
| ifdef CONFIG_SOFTMMU | 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 += qtest.o | ||||||
| obj-y += dump/ |  | ||||||
| obj-y += hw/ | obj-y += hw/ | ||||||
| obj-y += monitor/ |  | ||||||
| obj-y += qapi/ |  | ||||||
| obj-y += memory.o | obj-y += memory.o | ||||||
| obj-y += memory_mapping.o | obj-y += memory_mapping.o | ||||||
|  | obj-y += dump.o | ||||||
| obj-y += migration/ram.o | obj-y += migration/ram.o | ||||||
| LIBS := $(libs_softmmu) $(LIBS) | LIBS := $(libs_softmmu) $(LIBS) | ||||||
|  |  | ||||||
| @@ -169,37 +148,43 @@ else | |||||||
| obj-y += hw/$(TARGET_BASE_ARCH)/ | obj-y += hw/$(TARGET_BASE_ARCH)/ | ||||||
| endif | endif | ||||||
|  |  | ||||||
| generated-files-y += hmp-commands.h hmp-commands-info.h | GENERATED_FILES += hmp-commands.h hmp-commands-info.h | ||||||
| generated-files-y += config-devices.h |  | ||||||
|  |  | ||||||
| endif # CONFIG_SOFTMMU | endif # CONFIG_SOFTMMU | ||||||
|  |  | ||||||
|  | # Workaround for http://gcc.gnu.org/PR55489, see configure. | ||||||
|  | %/translate.o: QEMU_CFLAGS += $(TRANSLATE_OPT_CFLAGS) | ||||||
|  |  | ||||||
| dummy := $(call unnest-vars,,obj-y) | dummy := $(call unnest-vars,,obj-y) | ||||||
| all-obj-y := $(obj-y) | all-obj-y := $(obj-y) | ||||||
|  |  | ||||||
|  | target-obj-y := | ||||||
|  | block-obj-y := | ||||||
|  | common-obj-y := | ||||||
|  | chardev-obj-y := | ||||||
| include $(SRC_PATH)/Makefile.objs | include $(SRC_PATH)/Makefile.objs | ||||||
|  | dummy := $(call unnest-vars,,target-obj-y) | ||||||
|  | target-obj-y-save := $(target-obj-y) | ||||||
| dummy := $(call unnest-vars,.., \ | dummy := $(call unnest-vars,.., \ | ||||||
|                authz-obj-y \ |  | ||||||
|                block-obj-y \ |                block-obj-y \ | ||||||
|                block-obj-m \ |                block-obj-m \ | ||||||
|                chardev-obj-y \ |                chardev-obj-y \ | ||||||
|                crypto-obj-y \ |                crypto-obj-y \ | ||||||
|                crypto-user-obj-y \ |                crypto-aes-obj-y \ | ||||||
|                qom-obj-y \ |                qom-obj-y \ | ||||||
|                io-obj-y \ |                io-obj-y \ | ||||||
|                common-obj-y \ |                common-obj-y \ | ||||||
|                common-obj-m) |                common-obj-m) | ||||||
|  | target-obj-y := $(target-obj-y-save) | ||||||
| all-obj-y += $(common-obj-y) | all-obj-y += $(common-obj-y) | ||||||
|  | all-obj-y += $(target-obj-y) | ||||||
| all-obj-y += $(qom-obj-y) | all-obj-y += $(qom-obj-y) | ||||||
| all-obj-$(CONFIG_SOFTMMU) += $(authz-obj-y) |  | ||||||
| all-obj-$(CONFIG_SOFTMMU) += $(block-obj-y) $(chardev-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) += $(crypto-obj-y) | ||||||
| all-obj-$(CONFIG_SOFTMMU) += $(io-obj-y) | all-obj-$(CONFIG_SOFTMMU) += $(io-obj-y) | ||||||
|  |  | ||||||
| ifdef CONFIG_SOFTMMU |  | ||||||
| $(QEMU_PROG_BUILD): config-devices.mak | $(QEMU_PROG_BUILD): config-devices.mak | ||||||
| endif |  | ||||||
|  |  | ||||||
| COMMON_LDADDS = ../libqemuutil.a | COMMON_LDADDS = ../libqemuutil.a | ||||||
|  |  | ||||||
| @@ -224,7 +209,6 @@ clean: clean-target | |||||||
| 	rm -f *.a *~ $(PROGS) | 	rm -f *.a *~ $(PROGS) | ||||||
| 	rm -f $(shell find . -name '*.[od]') | 	rm -f $(shell find . -name '*.[od]') | ||||||
| 	rm -f hmp-commands.h gdbstub-xml.c | 	rm -f hmp-commands.h gdbstub-xml.c | ||||||
| 	rm -f trace/generated-helpers.c trace/generated-helpers.c-timestamp |  | ||||||
| ifdef CONFIG_TRACE_SYSTEMTAP | ifdef CONFIG_TRACE_SYSTEMTAP | ||||||
| 	rm -f *.stp | 	rm -f *.stp | ||||||
| endif | endif | ||||||
| @@ -237,24 +221,7 @@ ifdef CONFIG_TRACE_SYSTEMTAP | |||||||
| 	$(INSTALL_DIR) "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset" | 	$(INSTALL_DIR) "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset" | ||||||
| 	$(INSTALL_DATA) $(QEMU_PROG).stp-installed "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset/$(QEMU_PROG).stp" | 	$(INSTALL_DATA) $(QEMU_PROG).stp-installed "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset/$(QEMU_PROG).stp" | ||||||
| 	$(INSTALL_DATA) $(QEMU_PROG)-simpletrace.stp "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset/$(QEMU_PROG)-simpletrace.stp" | 	$(INSTALL_DATA) $(QEMU_PROG)-simpletrace.stp "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset/$(QEMU_PROG)-simpletrace.stp" | ||||||
| 	$(INSTALL_DATA) $(QEMU_PROG)-log.stp "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset/$(QEMU_PROG)-log.stp" |  | ||||||
| endif | endif | ||||||
|  |  | ||||||
| generated-files-y += config-target.h | GENERATED_FILES += config-target.h | ||||||
| Makefile: $(generated-files-y) | Makefile: $(GENERATED_FILES) | ||||||
|  |  | ||||||
| # Reports/Analysis |  | ||||||
| # |  | ||||||
| # The target specific coverage report only cares about target specific |  | ||||||
| # blobs and not the shared code. |  | ||||||
| # |  | ||||||
|  |  | ||||||
| %/coverage-report.html: |  | ||||||
| 	@mkdir -p $* |  | ||||||
| 	$(call quiet-command,\ |  | ||||||
| 		gcovr -r $(SRC_PATH) --object-directory $(CURDIR) \ |  | ||||||
| 		-p --html --html-details -o $@, \ |  | ||||||
| 		"GEN", "coverage-report.html") |  | ||||||
|  |  | ||||||
| .PHONY: coverage-report |  | ||||||
| coverage-report: $(CURDIR)/reports/coverage/coverage-report.html |  | ||||||
|   | |||||||
							
								
								
									
										139
									
								
								README
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										139
									
								
								README
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,139 @@ | |||||||
|  |          QEMU README | ||||||
|  |          =========== | ||||||
|  |  | ||||||
|  | QEMU is a generic and open source machine & userspace emulator and | ||||||
|  | virtualizer. | ||||||
|  |  | ||||||
|  | QEMU is capable of emulating a complete machine in software without any | ||||||
|  | need for hardware virtualization support. By using dynamic translation, | ||||||
|  | it achieves very good performance. QEMU can also integrate with the Xen | ||||||
|  | and KVM hypervisors to provide emulated hardware while allowing the | ||||||
|  | hypervisor to manage the CPU. With hypervisor support, QEMU can achieve | ||||||
|  | near native performance for CPUs. When QEMU emulates CPUs directly it is | ||||||
|  | capable of running operating systems made for one machine (e.g. an ARMv7 | ||||||
|  | board) on a different machine (e.g. an x86_64 PC board). | ||||||
|  |  | ||||||
|  | QEMU is also capable of providing userspace API virtualization for Linux | ||||||
|  | and BSD kernel interfaces. This allows binaries compiled against one | ||||||
|  | architecture ABI (e.g. the Linux PPC64 ABI) to be run on a host using a | ||||||
|  | different architecture ABI (e.g. the Linux x86_64 ABI). This does not | ||||||
|  | involve any hardware emulation, simply CPU and syscall emulation. | ||||||
|  |  | ||||||
|  | QEMU aims to fit into a variety of use cases. It can be invoked directly | ||||||
|  | by users wishing to have full control over its behaviour and settings. | ||||||
|  | It also aims to facilitate integration into higher level management | ||||||
|  | layers, by providing a stable command line interface and monitor API. | ||||||
|  | It is commonly invoked indirectly via the libvirt library when using | ||||||
|  | open source applications such as oVirt, OpenStack and virt-manager. | ||||||
|  |  | ||||||
|  | QEMU as a whole is released under the GNU General Public License, | ||||||
|  | version 2. For full licensing details, consult the LICENSE file. | ||||||
|  |  | ||||||
|  |  | ||||||
|  | Building | ||||||
|  | ======== | ||||||
|  |  | ||||||
|  | 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: | ||||||
|  |  | ||||||
|  |   mkdir build | ||||||
|  |   cd build | ||||||
|  |   ../configure | ||||||
|  |   make | ||||||
|  |  | ||||||
|  | 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 | ||||||
|  |  | ||||||
|  |  | ||||||
|  | Submitting patches | ||||||
|  | ================== | ||||||
|  |  | ||||||
|  | The QEMU source code is maintained under the GIT version control system. | ||||||
|  |  | ||||||
|  |    git clone git://git.qemu.org/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 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 | ||||||
|  |  | ||||||
|  | The QEMU website is also maintained under source control. | ||||||
|  |  | ||||||
|  |   git clone git://git.qemu.org/qemu-web.git | ||||||
|  |   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, | ||||||
|  | or even just for sending consecutive patch series revisions. It also | ||||||
|  | requires a working 'git send-email' setup, and by default doesn't | ||||||
|  | automate everything, so you may want to go through the above steps | ||||||
|  | manually for once. | ||||||
|  |  | ||||||
|  | For installation instructions, please go to | ||||||
|  |  | ||||||
|  |   https://github.com/stefanha/git-publish | ||||||
|  |  | ||||||
|  | The workflow with 'git-publish' is: | ||||||
|  |  | ||||||
|  |   $ git checkout master -b my-feature | ||||||
|  |   $ # work on new commits, add your 'Signed-off-by' lines to each | ||||||
|  |   $ git publish | ||||||
|  |  | ||||||
|  | Your patch series will be sent and tagged as my-feature-v1 if you need to refer | ||||||
|  | back to it in the future. | ||||||
|  |  | ||||||
|  | Sending v2: | ||||||
|  |  | ||||||
|  |   $ git checkout my-feature # same topic branch | ||||||
|  |   $ # making changes to the commits (using 'git rebase', for example) | ||||||
|  |   $ git publish | ||||||
|  |  | ||||||
|  | Your patch series will be sent with 'v2' tag in the subject and the git tip | ||||||
|  | will be tagged as my-feature-v2. | ||||||
|  |  | ||||||
|  | Bug reporting | ||||||
|  | ============= | ||||||
|  |  | ||||||
|  | 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/ | ||||||
|  |  | ||||||
|  | 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 | ||||||
|  | the bug is also known to affect latest upstream code, it can also be | ||||||
|  | reported via launchpad. | ||||||
|  |  | ||||||
|  | For additional information on bug reporting consult: | ||||||
|  |  | ||||||
|  |   https://qemu.org/Contribute/ReportABug | ||||||
|  |  | ||||||
|  |  | ||||||
|  | Contact | ||||||
|  | ======= | ||||||
|  |  | ||||||
|  | The QEMU community can be contacted in a number of ways, with the two | ||||||
|  | main methods being email and IRC | ||||||
|  |  | ||||||
|  |  - 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 | ||||||
|  |  | ||||||
|  | -- End | ||||||
							
								
								
									
										150
									
								
								README.rst
									
									
									
									
									
								
							
							
						
						
									
										150
									
								
								README.rst
									
									
									
									
									
								
							| @@ -1,150 +0,0 @@ | |||||||
| =========== |  | ||||||
| QEMU README |  | ||||||
| =========== |  | ||||||
|  |  | ||||||
| QEMU is a generic and open source machine & userspace emulator and |  | ||||||
| virtualizer. |  | ||||||
|  |  | ||||||
| QEMU is capable of emulating a complete machine in software without any |  | ||||||
| need for hardware virtualization support. By using dynamic translation, |  | ||||||
| it achieves very good performance. QEMU can also integrate with the Xen |  | ||||||
| and KVM hypervisors to provide emulated hardware while allowing the |  | ||||||
| hypervisor to manage the CPU. With hypervisor support, QEMU can achieve |  | ||||||
| near native performance for CPUs. When QEMU emulates CPUs directly it is |  | ||||||
| capable of running operating systems made for one machine (e.g. an ARMv7 |  | ||||||
| board) on a different machine (e.g. an x86_64 PC board). |  | ||||||
|  |  | ||||||
| QEMU is also capable of providing userspace API virtualization for Linux |  | ||||||
| and BSD kernel interfaces. This allows binaries compiled against one |  | ||||||
| architecture ABI (e.g. the Linux PPC64 ABI) to be run on a host using a |  | ||||||
| different architecture ABI (e.g. the Linux x86_64 ABI). This does not |  | ||||||
| involve any hardware emulation, simply CPU and syscall emulation. |  | ||||||
|  |  | ||||||
| QEMU aims to fit into a variety of use cases. It can be invoked directly |  | ||||||
| by users wishing to have full control over its behaviour and settings. |  | ||||||
| It also aims to facilitate integration into higher level management |  | ||||||
| layers, by providing a stable command line interface and monitor API. |  | ||||||
| It is commonly invoked indirectly via the libvirt library when using |  | ||||||
| open source applications such as oVirt, OpenStack and virt-manager. |  | ||||||
|  |  | ||||||
| QEMU as a whole is released under the GNU General Public License, |  | ||||||
| version 2. For full licensing details, consult the LICENSE file. |  | ||||||
|  |  | ||||||
|  |  | ||||||
| Building |  | ||||||
| ======== |  | ||||||
|  |  | ||||||
| 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 |  | ||||||
|   make |  | ||||||
|  |  | ||||||
| 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>`_ |  | ||||||
|  |  | ||||||
|  |  | ||||||
| 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. |  | ||||||
|  |  | ||||||
| Additional information on submitting patches can be found online via |  | ||||||
| the QEMU website |  | ||||||
|  |  | ||||||
| * `<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/>`_ |  | ||||||
|  |  | ||||||
| A 'git-publish' utility was created to make above process less |  | ||||||
| cumbersome, and is highly recommended for making regular contributions, |  | ||||||
| or even just for sending consecutive patch series revisions. It also |  | ||||||
| requires a working 'git send-email' setup, and by default doesn't |  | ||||||
| automate everything, so you may want to go through the above steps |  | ||||||
| manually for once. |  | ||||||
|  |  | ||||||
| For installation instructions, please go to |  | ||||||
|  |  | ||||||
| *  `<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 |  | ||||||
|  |  | ||||||
| Your patch series will be sent and tagged as my-feature-v1 if you need to refer |  | ||||||
| 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 |  | ||||||
|  |  | ||||||
| Your patch series will be sent with 'v2' tag in the subject and the git tip |  | ||||||
| will be tagged as my-feature-v2. |  | ||||||
|  |  | ||||||
| Bug reporting |  | ||||||
| ============= |  | ||||||
|  |  | ||||||
| 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/>`_ |  | ||||||
|  |  | ||||||
| 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 |  | ||||||
| the bug is also known to affect latest upstream code, it can also be |  | ||||||
| reported via launchpad. |  | ||||||
|  |  | ||||||
| For additional information on bug reporting consult: |  | ||||||
|  |  | ||||||
| * `<https://qemu.org/Contribute/ReportABug>`_ |  | ||||||
|  |  | ||||||
|  |  | ||||||
| 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 |  | ||||||
|  |  | ||||||
| Information on additional methods of contacting the community can be |  | ||||||
| found online via the QEMU website: |  | ||||||
|  |  | ||||||
| * `<https://qemu.org/Contribute/StartHere>`_ |  | ||||||
| @@ -1,5 +1,4 @@ | |||||||
| obj-$(CONFIG_SOFTMMU) += accel.o | obj-$(CONFIG_SOFTMMU) += accel.o | ||||||
| obj-$(call land,$(CONFIG_SOFTMMU),$(CONFIG_POSIX)) += qtest.o | obj-y += kvm/ | ||||||
| obj-$(CONFIG_KVM) += kvm/ |  | ||||||
| obj-$(CONFIG_TCG) += tcg/ | obj-$(CONFIG_TCG) += tcg/ | ||||||
| obj-y += stubs/ | obj-y += stubs/ | ||||||
|   | |||||||
| @@ -34,7 +34,6 @@ | |||||||
| #include "qom/object.h" | #include "qom/object.h" | ||||||
| #include "qemu/error-report.h" | #include "qemu/error-report.h" | ||||||
| #include "qemu/option.h" | #include "qemu/option.h" | ||||||
| #include "qapi/error.h" |  | ||||||
|  |  | ||||||
| static const TypeInfo accel_type = { | static const TypeInfo accel_type = { | ||||||
|     .name = TYPE_ACCEL, |     .name = TYPE_ACCEL, | ||||||
| @@ -65,16 +64,14 @@ static int accel_init_machine(AccelClass *acc, MachineState *ms) | |||||||
|         ms->accelerator = NULL; |         ms->accelerator = NULL; | ||||||
|         *(acc->allowed) = false; |         *(acc->allowed) = false; | ||||||
|         object_unref(OBJECT(accel)); |         object_unref(OBJECT(accel)); | ||||||
|     } else { |  | ||||||
|         object_set_accelerator_compat_props(acc->compat_props); |  | ||||||
|     } |     } | ||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
|  |  | ||||||
| void configure_accelerator(MachineState *ms, const char *progname) | void configure_accelerator(MachineState *ms) | ||||||
| { | { | ||||||
|     const char *accel; |     const char *accel, *p; | ||||||
|     char **accel_list, **tmp; |     char buf[10]; | ||||||
|     int ret; |     int ret; | ||||||
|     bool accel_initialised = false; |     bool accel_initialised = false; | ||||||
|     bool init_failed = false; |     bool init_failed = false; | ||||||
| @@ -82,31 +79,25 @@ void configure_accelerator(MachineState *ms, const char *progname) | |||||||
|  |  | ||||||
|     accel = qemu_opt_get(qemu_get_machine_opts(), "accel"); |     accel = qemu_opt_get(qemu_get_machine_opts(), "accel"); | ||||||
|     if (accel == NULL) { |     if (accel == NULL) { | ||||||
|         /* Select the default accelerator */ |         /* Use the default "accelerator", tcg */ | ||||||
|         int pnlen = strlen(progname); |         accel = "tcg"; | ||||||
|         if (pnlen >= 3 && g_str_equal(&progname[pnlen - 3], "kvm")) { |  | ||||||
|             /* If the program name ends with "kvm", we prefer KVM */ |  | ||||||
|             accel = "kvm:tcg"; |  | ||||||
|         } else { |  | ||||||
| #if defined(CONFIG_TCG) |  | ||||||
|             accel = "tcg"; |  | ||||||
| #elif defined(CONFIG_KVM) |  | ||||||
|             accel = "kvm"; |  | ||||||
| #else |  | ||||||
|             error_report("No accelerator selected and" |  | ||||||
|                          " no default accelerator available"); |  | ||||||
|             exit(1); |  | ||||||
| #endif |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     accel_list = g_strsplit(accel, ":", 0); |     p = accel; | ||||||
|  |     while (!accel_initialised && *p != '\0') { | ||||||
|     for (tmp = accel_list; !accel_initialised && tmp && *tmp; tmp++) { |         if (*p == ':') { | ||||||
|         acc = accel_find(*tmp); |             p++; | ||||||
|  |         } | ||||||
|  |         p = get_opt_name(buf, sizeof(buf), p, ':'); | ||||||
|  |         acc = accel_find(buf); | ||||||
|         if (!acc) { |         if (!acc) { | ||||||
|             continue; |             continue; | ||||||
|         } |         } | ||||||
|  |         if (acc->available && !acc->available()) { | ||||||
|  |             printf("%s not supported for this target\n", | ||||||
|  |                    acc->name); | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|         ret = accel_init_machine(acc, ms); |         ret = accel_init_machine(acc, ms); | ||||||
|         if (ret < 0) { |         if (ret < 0) { | ||||||
|             init_failed = true; |             init_failed = true; | ||||||
| @@ -116,7 +107,6 @@ void configure_accelerator(MachineState *ms, const char *progname) | |||||||
|             accel_initialised = true; |             accel_initialised = true; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     g_strfreev(accel_list); |  | ||||||
|  |  | ||||||
|     if (!accel_initialised) { |     if (!accel_initialised) { | ||||||
|         if (!init_failed) { |         if (!init_failed) { | ||||||
| @@ -130,13 +120,10 @@ void configure_accelerator(MachineState *ms, const char *progname) | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| void accel_setup_post(MachineState *ms) | void accel_register_compat_props(AccelState *accel) | ||||||
| { | { | ||||||
|     AccelState *accel = ms->accelerator; |     AccelClass *class = ACCEL_GET_CLASS(accel); | ||||||
|     AccelClass *acc = ACCEL_GET_CLASS(accel); |     register_compat_props_array(class->global_props); | ||||||
|     if (acc->setup_post) { |  | ||||||
|         acc->setup_post(ms, accel); |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static void register_accel_types(void) | static void register_accel_types(void) | ||||||
|   | |||||||
| @@ -1,2 +1 @@ | |||||||
| obj-y += kvm-all.o | obj-$(CONFIG_KVM) += kvm-all.o | ||||||
| obj-$(call lnot,$(CONFIG_SEV)) += sev-stub.o |  | ||||||
|   | |||||||
| @@ -18,29 +18,26 @@ | |||||||
|  |  | ||||||
| #include <linux/kvm.h> | #include <linux/kvm.h> | ||||||
|  |  | ||||||
|  | #include "qemu-common.h" | ||||||
| #include "qemu/atomic.h" | #include "qemu/atomic.h" | ||||||
| #include "qemu/option.h" | #include "qemu/option.h" | ||||||
| #include "qemu/config-file.h" | #include "qemu/config-file.h" | ||||||
| #include "qemu/error-report.h" | #include "qemu/error-report.h" | ||||||
| #include "qapi/error.h" | #include "qapi/error.h" | ||||||
|  | #include "hw/hw.h" | ||||||
| #include "hw/pci/msi.h" | #include "hw/pci/msi.h" | ||||||
| #include "hw/pci/msix.h" | #include "hw/pci/msix.h" | ||||||
| #include "hw/s390x/adapter.h" | #include "hw/s390x/adapter.h" | ||||||
| #include "exec/gdbstub.h" | #include "exec/gdbstub.h" | ||||||
| #include "sysemu/kvm_int.h" | #include "sysemu/kvm_int.h" | ||||||
| #include "sysemu/runstate.h" |  | ||||||
| #include "sysemu/cpus.h" | #include "sysemu/cpus.h" | ||||||
| #include "sysemu/sysemu.h" |  | ||||||
| #include "qemu/bswap.h" | #include "qemu/bswap.h" | ||||||
| #include "exec/memory.h" | #include "exec/memory.h" | ||||||
| #include "exec/ram_addr.h" | #include "exec/ram_addr.h" | ||||||
| #include "exec/address-spaces.h" | #include "exec/address-spaces.h" | ||||||
| #include "qemu/event_notifier.h" | #include "qemu/event_notifier.h" | ||||||
| #include "qemu/main-loop.h" |  | ||||||
| #include "trace.h" | #include "trace.h" | ||||||
| #include "hw/irq.h" | #include "hw/irq.h" | ||||||
| #include "sysemu/sev.h" |  | ||||||
| #include "sysemu/balloon.h" |  | ||||||
|  |  | ||||||
| #include "hw/boards.h" | #include "hw/boards.h" | ||||||
|  |  | ||||||
| @@ -52,7 +49,7 @@ | |||||||
| /* KVM uses PAGE_SIZE in its definition of KVM_COALESCED_MMIO_MAX. We | /* 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. |  * 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 | //#define DEBUG_KVM | ||||||
|  |  | ||||||
| @@ -80,20 +77,17 @@ struct KVMState | |||||||
|     int fd; |     int fd; | ||||||
|     int vmfd; |     int vmfd; | ||||||
|     int coalesced_mmio; |     int coalesced_mmio; | ||||||
|     int coalesced_pio; |  | ||||||
|     struct kvm_coalesced_mmio_ring *coalesced_mmio_ring; |     struct kvm_coalesced_mmio_ring *coalesced_mmio_ring; | ||||||
|     bool coalesced_flush_in_progress; |     bool coalesced_flush_in_progress; | ||||||
|     int vcpu_events; |     int vcpu_events; | ||||||
|     int robust_singlestep; |     int robust_singlestep; | ||||||
|     int debugregs; |     int debugregs; | ||||||
| #ifdef KVM_CAP_SET_GUEST_DEBUG | #ifdef KVM_CAP_SET_GUEST_DEBUG | ||||||
|     QTAILQ_HEAD(, kvm_sw_breakpoint) kvm_sw_breakpoints; |     struct kvm_sw_breakpoint_head kvm_sw_breakpoints; | ||||||
| #endif | #endif | ||||||
|     int max_nested_state_len; |  | ||||||
|     int many_ioeventfds; |     int many_ioeventfds; | ||||||
|     int intx_set_mask; |     int intx_set_mask; | ||||||
|     bool sync_mmu; |     bool sync_mmu; | ||||||
|     bool manual_dirty_log_protect; |  | ||||||
|     /* The man page (and posix) say ioctl numbers are signed int, but |     /* The man page (and posix) say ioctl numbers are signed int, but | ||||||
|      * they're not.  Linux, glibc and *BSD all treat ioctl numbers as |      * they're not.  Linux, glibc and *BSD all treat ioctl numbers as | ||||||
|      * unsigned, and treating them as signed here can break things */ |      * unsigned, and treating them as signed here can break things */ | ||||||
| @@ -105,21 +99,10 @@ struct KVMState | |||||||
|     int nr_allocated_irq_routes; |     int nr_allocated_irq_routes; | ||||||
|     unsigned long *used_gsi_bitmap; |     unsigned long *used_gsi_bitmap; | ||||||
|     unsigned int gsi_count; |     unsigned int gsi_count; | ||||||
|     QTAILQ_HEAD(, KVMMSIRoute) msi_hashtab[KVM_MSI_HASHTAB_SIZE]; |     QTAILQ_HEAD(msi_hashtab, KVMMSIRoute) msi_hashtab[KVM_MSI_HASHTAB_SIZE]; | ||||||
| #endif | #endif | ||||||
|     KVMMemoryListener memory_listener; |     KVMMemoryListener memory_listener; | ||||||
|     QLIST_HEAD(, KVMParkedVcpu) kvm_parked_vcpus; |     QLIST_HEAD(, KVMParkedVcpu) kvm_parked_vcpus; | ||||||
|  |  | ||||||
|     /* 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; | KVMState *kvm_state; | ||||||
| @@ -140,7 +123,6 @@ bool kvm_direct_msi_allowed; | |||||||
| bool kvm_ioeventfd_any_length_allowed; | bool kvm_ioeventfd_any_length_allowed; | ||||||
| bool kvm_msi_use_devid; | bool kvm_msi_use_devid; | ||||||
| static bool kvm_immediate_exit; | static bool kvm_immediate_exit; | ||||||
| static hwaddr kvm_max_slot_size = ~0; |  | ||||||
|  |  | ||||||
| static const KVMCapabilityInfo kvm_required_capabilites[] = { | static const KVMCapabilityInfo kvm_required_capabilites[] = { | ||||||
|     KVM_CAP_INFO(USER_MEMORY), |     KVM_CAP_INFO(USER_MEMORY), | ||||||
| @@ -149,9 +131,6 @@ static const KVMCapabilityInfo kvm_required_capabilites[] = { | |||||||
|     KVM_CAP_LAST_INFO |     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) | int kvm_get_max_memslots(void) | ||||||
| { | { | ||||||
|     KVMState *s = KVM_STATE(current_machine->accelerator); |     KVMState *s = KVM_STATE(current_machine->accelerator); | ||||||
| @@ -159,27 +138,6 @@ int kvm_get_max_memslots(void) | |||||||
|     return s->nr_slots; |     return s->nr_slots; | ||||||
| } | } | ||||||
|  |  | ||||||
| bool kvm_memcrypt_enabled(void) |  | ||||||
| { |  | ||||||
|     if (kvm_state && kvm_state->memcrypt_handle) { |  | ||||||
|         return true; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return false; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int kvm_memcrypt_encrypt_data(uint8_t *ptr, uint64_t len) |  | ||||||
| { |  | ||||||
|     if (kvm_state->memcrypt_handle && |  | ||||||
|         kvm_state->memcrypt_encrypt_data) { |  | ||||||
|         return kvm_state->memcrypt_encrypt_data(kvm_state->memcrypt_handle, |  | ||||||
|                                               ptr, len); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return 1; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* Called with KVMMemoryListener.slots_lock held */ |  | ||||||
| static KVMSlot *kvm_get_free_slot(KVMMemoryListener *kml) | static KVMSlot *kvm_get_free_slot(KVMMemoryListener *kml) | ||||||
| { | { | ||||||
|     KVMState *s = kvm_state; |     KVMState *s = kvm_state; | ||||||
| @@ -197,17 +155,10 @@ static KVMSlot *kvm_get_free_slot(KVMMemoryListener *kml) | |||||||
| bool kvm_has_free_slot(MachineState *ms) | bool kvm_has_free_slot(MachineState *ms) | ||||||
| { | { | ||||||
|     KVMState *s = KVM_STATE(ms->accelerator); |     KVMState *s = KVM_STATE(ms->accelerator); | ||||||
|     bool result; |  | ||||||
|     KVMMemoryListener *kml = &s->memory_listener; |  | ||||||
|  |  | ||||||
|     kvm_slots_lock(kml); |     return kvm_get_free_slot(&s->memory_listener); | ||||||
|     result = !!kvm_get_free_slot(kml); |  | ||||||
|     kvm_slots_unlock(kml); |  | ||||||
|  |  | ||||||
|     return result; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Called with KVMMemoryListener.slots_lock held */ |  | ||||||
| static KVMSlot *kvm_alloc_slot(KVMMemoryListener *kml) | static KVMSlot *kvm_alloc_slot(KVMMemoryListener *kml) | ||||||
| { | { | ||||||
|     KVMSlot *slot = kvm_get_free_slot(kml); |     KVMSlot *slot = kvm_get_free_slot(kml); | ||||||
| @@ -266,24 +217,21 @@ int kvm_physical_memory_addr_from_host(KVMState *s, void *ram, | |||||||
|                                        hwaddr *phys_addr) |                                        hwaddr *phys_addr) | ||||||
| { | { | ||||||
|     KVMMemoryListener *kml = &s->memory_listener; |     KVMMemoryListener *kml = &s->memory_listener; | ||||||
|     int i, ret = 0; |     int i; | ||||||
|  |  | ||||||
|     kvm_slots_lock(kml); |  | ||||||
|     for (i = 0; i < s->nr_slots; i++) { |     for (i = 0; i < s->nr_slots; i++) { | ||||||
|         KVMSlot *mem = &kml->slots[i]; |         KVMSlot *mem = &kml->slots[i]; | ||||||
|  |  | ||||||
|         if (ram >= mem->ram && ram < mem->ram + mem->memory_size) { |         if (ram >= mem->ram && ram < mem->ram + mem->memory_size) { | ||||||
|             *phys_addr = mem->start_addr + (ram - mem->ram); |             *phys_addr = mem->start_addr + (ram - mem->ram); | ||||||
|             ret = 1; |             return 1; | ||||||
|             break; |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     kvm_slots_unlock(kml); |  | ||||||
|  |  | ||||||
|     return ret; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| static int kvm_set_user_memory_region(KVMMemoryListener *kml, KVMSlot *slot, bool new) | static int kvm_set_user_memory_region(KVMMemoryListener *kml, KVMSlot *slot) | ||||||
| { | { | ||||||
|     KVMState *s = kvm_state; |     KVMState *s = kvm_state; | ||||||
|     struct kvm_userspace_memory_region mem; |     struct kvm_userspace_memory_region mem; | ||||||
| @@ -294,7 +242,7 @@ static int kvm_set_user_memory_region(KVMMemoryListener *kml, KVMSlot *slot, boo | |||||||
|     mem.userspace_addr = (unsigned long)slot->ram; |     mem.userspace_addr = (unsigned long)slot->ram; | ||||||
|     mem.flags = slot->flags; |     mem.flags = slot->flags; | ||||||
|  |  | ||||||
|     if (slot->memory_size && !new && (mem.flags ^ slot->old_flags) & KVM_MEM_READONLY) { |     if (slot->memory_size && mem.flags & KVM_MEM_READONLY) { | ||||||
|         /* Set the slot size to 0 before setting the slot to the desired |         /* Set the slot size to 0 before setting the slot to the desired | ||||||
|          * value. This is needed based on KVM commit 75d61fbc. */ |          * value. This is needed based on KVM commit 75d61fbc. */ | ||||||
|         mem.memory_size = 0; |         mem.memory_size = 0; | ||||||
| @@ -302,7 +250,6 @@ static int kvm_set_user_memory_region(KVMMemoryListener *kml, KVMSlot *slot, boo | |||||||
|     } |     } | ||||||
|     mem.memory_size = slot->memory_size; |     mem.memory_size = slot->memory_size; | ||||||
|     ret = kvm_vm_ioctl(s, KVM_SET_USER_MEMORY_REGION, &mem); |     ret = kvm_vm_ioctl(s, KVM_SET_USER_MEMORY_REGION, &mem); | ||||||
|     slot->old_flags = mem.flags; |  | ||||||
|     trace_kvm_set_user_memory(mem.slot, mem.flags, mem.guest_phys_addr, |     trace_kvm_set_user_memory(mem.slot, mem.flags, mem.guest_phys_addr, | ||||||
|                               mem.memory_size, mem.userspace_addr, ret); |                               mem.memory_size, mem.userspace_addr, ret); | ||||||
|     return ret; |     return ret; | ||||||
| @@ -317,11 +264,6 @@ int kvm_destroy_vcpu(CPUState *cpu) | |||||||
|  |  | ||||||
|     DPRINTF("kvm_destroy_vcpu\n"); |     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); |     mmap_size = kvm_ioctl(s, KVM_GET_VCPU_MMAP_SIZE, 0); | ||||||
|     if (mmap_size < 0) { |     if (mmap_size < 0) { | ||||||
|         ret = mmap_size; |         ret = mmap_size; | ||||||
| @@ -421,50 +363,40 @@ static int kvm_mem_flags(MemoryRegion *mr) | |||||||
|     return flags; |     return flags; | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Called with KVMMemoryListener.slots_lock held */ |  | ||||||
| static int kvm_slot_update_flags(KVMMemoryListener *kml, KVMSlot *mem, | static int kvm_slot_update_flags(KVMMemoryListener *kml, KVMSlot *mem, | ||||||
|                                  MemoryRegion *mr) |                                  MemoryRegion *mr) | ||||||
| { | { | ||||||
|  |     int old_flags; | ||||||
|  |  | ||||||
|  |     old_flags = mem->flags; | ||||||
|     mem->flags = kvm_mem_flags(mr); |     mem->flags = kvm_mem_flags(mr); | ||||||
|  |  | ||||||
|     /* If nothing changed effectively, no need to issue ioctl */ |     /* If nothing changed effectively, no need to issue ioctl */ | ||||||
|     if (mem->flags == mem->old_flags) { |     if (mem->flags == old_flags) { | ||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return kvm_set_user_memory_region(kml, mem, false); |     return kvm_set_user_memory_region(kml, mem); | ||||||
| } | } | ||||||
|  |  | ||||||
| static int kvm_section_update_flags(KVMMemoryListener *kml, | static int kvm_section_update_flags(KVMMemoryListener *kml, | ||||||
|                                     MemoryRegionSection *section) |                                     MemoryRegionSection *section) | ||||||
| { | { | ||||||
|     hwaddr start_addr, size, slot_size; |     hwaddr start_addr, size; | ||||||
|     KVMSlot *mem; |     KVMSlot *mem; | ||||||
|     int ret = 0; |  | ||||||
|  |  | ||||||
|     size = kvm_align_section(section, &start_addr); |     size = kvm_align_section(section, &start_addr); | ||||||
|     if (!size) { |     if (!size) { | ||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     kvm_slots_lock(kml); |     mem = kvm_lookup_matching_slot(kml, start_addr, size); | ||||||
|  |     if (!mem) { | ||||||
|     while (size && !ret) { |         /* We don't have a slot if we want to trap every access. */ | ||||||
|         slot_size = MIN(kvm_max_slot_size, size); |         return 0; | ||||||
|         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; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
| out: |     return kvm_slot_update_flags(kml, mem, section->mr); | ||||||
|     kvm_slots_unlock(kml); |  | ||||||
|     return ret; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static void kvm_log_start(MemoryListener *listener, | static void kvm_log_start(MemoryListener *listener, | ||||||
| @@ -507,7 +439,7 @@ static int kvm_get_dirty_pages_log_range(MemoryRegionSection *section, | |||||||
| { | { | ||||||
|     ram_addr_t start = section->offset_within_region + |     ram_addr_t start = section->offset_within_region + | ||||||
|                        memory_region_get_ram_addr(section->mr); |                        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); |     cpu_physical_memory_set_dirty_lebitmap(bitmap, start, pages); | ||||||
|     return 0; |     return 0; | ||||||
| @@ -516,15 +448,13 @@ static int kvm_get_dirty_pages_log_range(MemoryRegionSection *section, | |||||||
| #define ALIGN(x, y)  (((x)+(y)-1) & ~((y)-1)) | #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, |  * @start_add: start of logged region. | ||||||
|  * and then updates qemu's dirty bitmap. |  * @end_addr: end of logged region. | ||||||
|  * |  | ||||||
|  * 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 |  | ||||||
|  */ |  */ | ||||||
| static int kvm_physical_sync_dirty_bitmap(KVMMemoryListener *kml, | static int kvm_physical_sync_dirty_bitmap(KVMMemoryListener *kml, | ||||||
|                                           MemoryRegionSection *section) |                                           MemoryRegionSection *section) | ||||||
| @@ -533,18 +463,13 @@ static int kvm_physical_sync_dirty_bitmap(KVMMemoryListener *kml, | |||||||
|     struct kvm_dirty_log d = {}; |     struct kvm_dirty_log d = {}; | ||||||
|     KVMSlot *mem; |     KVMSlot *mem; | ||||||
|     hwaddr start_addr, size; |     hwaddr start_addr, size; | ||||||
|     hwaddr slot_size, slot_offset = 0; |  | ||||||
|     int ret = 0; |  | ||||||
|  |  | ||||||
|     size = kvm_align_section(section, &start_addr); |     size = kvm_align_section(section, &start_addr); | ||||||
|     while (size) { |     if (size) { | ||||||
|         MemoryRegionSection subsection = *section; |         mem = kvm_lookup_matching_slot(kml, start_addr, size); | ||||||
|  |  | ||||||
|         slot_size = MIN(kvm_max_slot_size, size); |  | ||||||
|         mem = kvm_lookup_matching_slot(kml, start_addr, slot_size); |  | ||||||
|         if (!mem) { |         if (!mem) { | ||||||
|             /* We don't have a slot if we want to trap every access. */ |             /* We don't have a slot if we want to trap every access. */ | ||||||
|             goto out; |             return 0; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         /* XXX bad kernel interface alert |         /* XXX bad kernel interface alert | ||||||
| @@ -559,203 +484,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 |          * 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. |          * a hope that sizeof(long) won't become >8 any time soon. | ||||||
|          */ |          */ | ||||||
|         if (!mem->dirty_bmap) { |         size = ALIGN(((mem->memory_size) >> TARGET_PAGE_BITS), | ||||||
|             hwaddr bitmap_size = ALIGN(((mem->memory_size) >> TARGET_PAGE_BITS), |                      /*HOST_LONG_BITS*/ 64) / 8; | ||||||
|                                         /*HOST_LONG_BITS*/ 64) / 8; |         d.dirty_bitmap = g_malloc0(size); | ||||||
|             /* Allocate on the first log_sync, once and for all */ |  | ||||||
|             mem->dirty_bmap = g_malloc0(bitmap_size); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         d.dirty_bitmap = mem->dirty_bmap; |  | ||||||
|         d.slot = mem->slot | (kml->as_id << 16); |         d.slot = mem->slot | (kml->as_id << 16); | ||||||
|         if (kvm_vm_ioctl(s, KVM_GET_DIRTY_LOG, &d) == -1) { |         if (kvm_vm_ioctl(s, KVM_GET_DIRTY_LOG, &d) == -1) { | ||||||
|             DPRINTF("ioctl failed %d\n", errno); |             DPRINTF("ioctl failed %d\n", errno); | ||||||
|             ret = -1; |             g_free(d.dirty_bitmap); | ||||||
|             goto out; |             return -1; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         subsection.offset_within_region += slot_offset; |         kvm_get_dirty_pages_log_range(section, d.dirty_bitmap); | ||||||
|         subsection.size = int128_make64(slot_size); |         g_free(d.dirty_bitmap); | ||||||
|         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); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     d.first_page = bmap_start; |     return 0; | ||||||
|     /* 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; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static void kvm_coalesce_mmio_region(MemoryListener *listener, | static void kvm_coalesce_mmio_region(MemoryListener *listener, | ||||||
| @@ -792,45 +536,6 @@ static void kvm_uncoalesce_mmio_region(MemoryListener *listener, | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| static void kvm_coalesce_pio_add(MemoryListener *listener, |  | ||||||
|                                 MemoryRegionSection *section, |  | ||||||
|                                 hwaddr start, hwaddr size) |  | ||||||
| { |  | ||||||
|     KVMState *s = kvm_state; |  | ||||||
|  |  | ||||||
|     if (s->coalesced_pio) { |  | ||||||
|         struct kvm_coalesced_mmio_zone zone; |  | ||||||
|  |  | ||||||
|         zone.addr = start; |  | ||||||
|         zone.size = size; |  | ||||||
|         zone.pio = 1; |  | ||||||
|  |  | ||||||
|         (void)kvm_vm_ioctl(s, KVM_REGISTER_COALESCED_MMIO, &zone); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void kvm_coalesce_pio_del(MemoryListener *listener, |  | ||||||
|                                 MemoryRegionSection *section, |  | ||||||
|                                 hwaddr start, hwaddr size) |  | ||||||
| { |  | ||||||
|     KVMState *s = kvm_state; |  | ||||||
|  |  | ||||||
|     if (s->coalesced_pio) { |  | ||||||
|         struct kvm_coalesced_mmio_zone zone; |  | ||||||
|  |  | ||||||
|         zone.addr = start; |  | ||||||
|         zone.size = size; |  | ||||||
|         zone.pio = 1; |  | ||||||
|  |  | ||||||
|         (void)kvm_vm_ioctl(s, KVM_UNREGISTER_COALESCED_MMIO, &zone); |  | ||||||
|      } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static MemoryListener kvm_coalesced_pio_listener = { |  | ||||||
|     .coalesced_io_add = kvm_coalesce_pio_add, |  | ||||||
|     .coalesced_io_del = kvm_coalesce_pio_del, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| int kvm_check_extension(KVMState *s, unsigned int extension) | int kvm_check_extension(KVMState *s, unsigned int extension) | ||||||
| { | { | ||||||
|     int ret; |     int ret; | ||||||
| @@ -888,8 +593,6 @@ static int kvm_set_ioeventfd_mmio(int fd, hwaddr addr, uint32_t val, | |||||||
|         .fd = fd, |         .fd = fd, | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     trace_kvm_set_ioeventfd_mmio(fd, (uint64_t)addr, val, assign, size, |  | ||||||
|                                  datamatch); |  | ||||||
|     if (!kvm_enabled()) { |     if (!kvm_enabled()) { | ||||||
|         return -ENOSYS; |         return -ENOSYS; | ||||||
|     } |     } | ||||||
| @@ -921,7 +624,6 @@ static int kvm_set_ioeventfd_pio(int fd, uint16_t addr, uint16_t val, | |||||||
|         .fd = fd, |         .fd = fd, | ||||||
|     }; |     }; | ||||||
|     int r; |     int r; | ||||||
|     trace_kvm_set_ioeventfd_pio(fd, addr, val, assign, size, datamatch); |  | ||||||
|     if (!kvm_enabled()) { |     if (!kvm_enabled()) { | ||||||
|         return -ENOSYS; |         return -ENOSYS; | ||||||
|     } |     } | ||||||
| @@ -988,14 +690,6 @@ kvm_check_extension_list(KVMState *s, const KVMCapabilityInfo *list) | |||||||
|     return NULL; |     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, | static void kvm_set_phys_mem(KVMMemoryListener *kml, | ||||||
|                              MemoryRegionSection *section, bool add) |                              MemoryRegionSection *section, bool add) | ||||||
| { | { | ||||||
| @@ -1003,7 +697,7 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml, | |||||||
|     int err; |     int err; | ||||||
|     MemoryRegion *mr = section->mr; |     MemoryRegion *mr = section->mr; | ||||||
|     bool writeable = !mr->readonly && !mr->rom_device; |     bool writeable = !mr->readonly && !mr->rom_device; | ||||||
|     hwaddr start_addr, size, slot_size; |     hwaddr start_addr, size; | ||||||
|     void *ram; |     void *ram; | ||||||
|  |  | ||||||
|     if (!memory_region_is_ram(mr)) { |     if (!memory_region_is_ram(mr)) { | ||||||
| @@ -1025,58 +719,39 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml, | |||||||
|     ram = memory_region_get_ram_ptr(mr) + section->offset_within_region + |     ram = memory_region_get_ram_ptr(mr) + section->offset_within_region + | ||||||
|           (start_addr - section->offset_within_address_space); |           (start_addr - section->offset_within_address_space); | ||||||
|  |  | ||||||
|     kvm_slots_lock(kml); |  | ||||||
|  |  | ||||||
|     if (!add) { |     if (!add) { | ||||||
|         do { |         mem = kvm_lookup_matching_slot(kml, start_addr, size); | ||||||
|             slot_size = MIN(kvm_max_slot_size, size); |         if (!mem) { | ||||||
|             mem = kvm_lookup_matching_slot(kml, start_addr, slot_size); |             return; | ||||||
|             if (!mem) { |         } | ||||||
|                 goto out; |         if (mem->flags & KVM_MEM_LOG_DIRTY_PAGES) { | ||||||
|             } |             kvm_physical_sync_dirty_bitmap(kml, section); | ||||||
|             if (mem->flags & KVM_MEM_LOG_DIRTY_PAGES) { |         } | ||||||
|                 kvm_physical_sync_dirty_bitmap(kml, section); |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             /* unregister the slot */ |         /* unregister the slot */ | ||||||
|             g_free(mem->dirty_bmap); |         mem->memory_size = 0; | ||||||
|             mem->dirty_bmap = NULL; |         err = kvm_set_user_memory_region(kml, mem); | ||||||
|             mem->memory_size = 0; |         if (err) { | ||||||
|             mem->flags = 0; |             fprintf(stderr, "%s: error unregistering slot: %s\n", | ||||||
|             err = kvm_set_user_memory_region(kml, mem, false); |                     __func__, strerror(-err)); | ||||||
|             if (err) { |             abort(); | ||||||
|                 fprintf(stderr, "%s: error unregistering slot: %s\n", |         } | ||||||
|                         __func__, strerror(-err)); |         return; | ||||||
|                 abort(); |  | ||||||
|             } |  | ||||||
|             start_addr += slot_size; |  | ||||||
|             size -= slot_size; |  | ||||||
|         } while (size); |  | ||||||
|         goto out; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* register the new slot */ |     /* register the new slot */ | ||||||
|     do { |     mem = kvm_alloc_slot(kml); | ||||||
|         slot_size = MIN(kvm_max_slot_size, size); |     mem->memory_size = size; | ||||||
|         mem = kvm_alloc_slot(kml); |     mem->start_addr = start_addr; | ||||||
|         mem->memory_size = slot_size; |     mem->ram = ram; | ||||||
|         mem->start_addr = start_addr; |     mem->flags = kvm_mem_flags(mr); | ||||||
|         mem->ram = ram; |  | ||||||
|         mem->flags = kvm_mem_flags(mr); |  | ||||||
|  |  | ||||||
|         err = kvm_set_user_memory_region(kml, mem, true); |     err = kvm_set_user_memory_region(kml, mem); | ||||||
|         if (err) { |     if (err) { | ||||||
|             fprintf(stderr, "%s: error registering slot: %s\n", __func__, |         fprintf(stderr, "%s: error registering slot: %s\n", __func__, | ||||||
|                     strerror(-err)); |                 strerror(-err)); | ||||||
|             abort(); |         abort(); | ||||||
|         } |     } | ||||||
|         start_addr += slot_size; |  | ||||||
|         ram += slot_size; |  | ||||||
|         size -= slot_size; |  | ||||||
|     } while (size); |  | ||||||
|  |  | ||||||
| out: |  | ||||||
|     kvm_slots_unlock(kml); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static void kvm_region_add(MemoryListener *listener, | static void kvm_region_add(MemoryListener *listener, | ||||||
| @@ -1103,30 +778,12 @@ static void kvm_log_sync(MemoryListener *listener, | |||||||
|     KVMMemoryListener *kml = container_of(listener, KVMMemoryListener, listener); |     KVMMemoryListener *kml = container_of(listener, KVMMemoryListener, listener); | ||||||
|     int r; |     int r; | ||||||
|  |  | ||||||
|     kvm_slots_lock(kml); |  | ||||||
|     r = kvm_physical_sync_dirty_bitmap(kml, section); |     r = kvm_physical_sync_dirty_bitmap(kml, section); | ||||||
|     kvm_slots_unlock(kml); |  | ||||||
|     if (r < 0) { |     if (r < 0) { | ||||||
|         abort(); |         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, | static void kvm_mem_ioeventfd_add(MemoryListener *listener, | ||||||
|                                   MemoryRegionSection *section, |                                   MemoryRegionSection *section, | ||||||
|                                   bool match_data, uint64_t data, |                                   bool match_data, uint64_t data, | ||||||
| @@ -1139,8 +796,8 @@ static void kvm_mem_ioeventfd_add(MemoryListener *listener, | |||||||
|                                data, true, int128_get64(section->size), |                                data, true, int128_get64(section->size), | ||||||
|                                match_data); |                                match_data); | ||||||
|     if (r < 0) { |     if (r < 0) { | ||||||
|         fprintf(stderr, "%s: error adding ioeventfd: %s (%d)\n", |         fprintf(stderr, "%s: error adding ioeventfd: %s\n", | ||||||
|                 __func__, strerror(-r), -r); |                 __func__, strerror(-r)); | ||||||
|         abort(); |         abort(); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -1157,8 +814,6 @@ static void kvm_mem_ioeventfd_del(MemoryListener *listener, | |||||||
|                                data, false, int128_get64(section->size), |                                data, false, int128_get64(section->size), | ||||||
|                                match_data); |                                match_data); | ||||||
|     if (r < 0) { |     if (r < 0) { | ||||||
|         fprintf(stderr, "%s: error deleting ioeventfd: %s (%d)\n", |  | ||||||
|                 __func__, strerror(-r), -r); |  | ||||||
|         abort(); |         abort(); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -1175,8 +830,8 @@ static void kvm_io_ioeventfd_add(MemoryListener *listener, | |||||||
|                               data, true, int128_get64(section->size), |                               data, true, int128_get64(section->size), | ||||||
|                               match_data); |                               match_data); | ||||||
|     if (r < 0) { |     if (r < 0) { | ||||||
|         fprintf(stderr, "%s: error adding ioeventfd: %s (%d)\n", |         fprintf(stderr, "%s: error adding ioeventfd: %s\n", | ||||||
|                 __func__, strerror(-r), -r); |                 __func__, strerror(-r)); | ||||||
|         abort(); |         abort(); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -1194,8 +849,6 @@ static void kvm_io_ioeventfd_del(MemoryListener *listener, | |||||||
|                               data, false, int128_get64(section->size), |                               data, false, int128_get64(section->size), | ||||||
|                               match_data); |                               match_data); | ||||||
|     if (r < 0) { |     if (r < 0) { | ||||||
|         fprintf(stderr, "%s: error deleting ioeventfd: %s (%d)\n", |  | ||||||
|                 __func__, strerror(-r), -r); |  | ||||||
|         abort(); |         abort(); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -1205,7 +858,6 @@ void kvm_memory_listener_register(KVMState *s, KVMMemoryListener *kml, | |||||||
| { | { | ||||||
|     int i; |     int i; | ||||||
|  |  | ||||||
|     qemu_mutex_init(&kml->slots_lock); |  | ||||||
|     kml->slots = g_malloc0(s->nr_slots * sizeof(KVMSlot)); |     kml->slots = g_malloc0(s->nr_slots * sizeof(KVMSlot)); | ||||||
|     kml->as_id = as_id; |     kml->as_id = as_id; | ||||||
|  |  | ||||||
| @@ -1218,18 +870,9 @@ void kvm_memory_listener_register(KVMState *s, KVMMemoryListener *kml, | |||||||
|     kml->listener.log_start = kvm_log_start; |     kml->listener.log_start = kvm_log_start; | ||||||
|     kml->listener.log_stop = kvm_log_stop; |     kml->listener.log_stop = kvm_log_stop; | ||||||
|     kml->listener.log_sync = kvm_log_sync; |     kml->listener.log_sync = kvm_log_sync; | ||||||
|     kml->listener.log_clear = kvm_log_clear; |  | ||||||
|     kml->listener.priority = 10; |     kml->listener.priority = 10; | ||||||
|  |  | ||||||
|     memory_listener_register(&kml->listener, as); |     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 = { | static MemoryListener kvm_io_listener = { | ||||||
| @@ -1822,8 +1465,8 @@ static int kvm_init(MachineState *ms) | |||||||
|         const char *name; |         const char *name; | ||||||
|         int num; |         int num; | ||||||
|     } num_cpus[] = { |     } num_cpus[] = { | ||||||
|         { "SMP",          ms->smp.cpus }, |         { "SMP",          smp_cpus }, | ||||||
|         { "hotpluggable", ms->smp.max_cpus }, |         { "hotpluggable", max_cpus }, | ||||||
|         { NULL, } |         { NULL, } | ||||||
|     }, *nc = num_cpus; |     }, *nc = num_cpus; | ||||||
|     int soft_vcpus_limit, hard_vcpus_limit; |     int soft_vcpus_limit, hard_vcpus_limit; | ||||||
| @@ -1841,7 +1484,7 @@ static int kvm_init(MachineState *ms) | |||||||
|      * even with KVM.  TARGET_PAGE_SIZE is assumed to be the minimum |      * even with KVM.  TARGET_PAGE_SIZE is assumed to be the minimum | ||||||
|      * page size for the system though. |      * page size for the system though. | ||||||
|      */ |      */ | ||||||
|     assert(TARGET_PAGE_SIZE <= qemu_real_host_page_size); |     assert(TARGET_PAGE_SIZE <= getpagesize()); | ||||||
|  |  | ||||||
|     s->sigmask_len = 8; |     s->sigmask_len = 8; | ||||||
|  |  | ||||||
| @@ -1880,15 +1523,9 @@ static int kvm_init(MachineState *ms) | |||||||
|         s->nr_slots = 32; |         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"); |     kvm_type = qemu_opt_get(qemu_get_machine_opts(), "kvm-type"); | ||||||
|     if (mc->kvm_type) { |     if (mc->kvm_type) { | ||||||
|         type = mc->kvm_type(ms, kvm_type); |         type = mc->kvm_type(kvm_type); | ||||||
|     } else if (kvm_type) { |     } else if (kvm_type) { | ||||||
|         ret = -EINVAL; |         ret = -EINVAL; | ||||||
|         fprintf(stderr, "Invalid argument kvm-type=%s\n", kvm_type); |         fprintf(stderr, "Invalid argument kvm-type=%s\n", kvm_type); | ||||||
| @@ -1954,19 +1591,6 @@ static int kvm_init(MachineState *ms) | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     s->coalesced_mmio = kvm_check_extension(s, KVM_CAP_COALESCED_MMIO); |     s->coalesced_mmio = kvm_check_extension(s, KVM_CAP_COALESCED_MMIO); | ||||||
|     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 | #ifdef KVM_CAP_VCPU_EVENTS | ||||||
|     s->vcpu_events = kvm_check_extension(s, KVM_CAP_VCPU_EVENTS); |     s->vcpu_events = kvm_check_extension(s, KVM_CAP_VCPU_EVENTS); | ||||||
| @@ -1979,8 +1603,6 @@ static int kvm_init(MachineState *ms) | |||||||
|     s->debugregs = kvm_check_extension(s, KVM_CAP_DEBUGREGS); |     s->debugregs = kvm_check_extension(s, KVM_CAP_DEBUGREGS); | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|     s->max_nested_state_len = kvm_check_extension(s, KVM_CAP_NESTED_STATE); |  | ||||||
|  |  | ||||||
| #ifdef KVM_CAP_IRQ_ROUTING | #ifdef KVM_CAP_IRQ_ROUTING | ||||||
|     kvm_direct_msi_allowed = (kvm_check_extension(s, KVM_CAP_SIGNAL_MSI) > 0); |     kvm_direct_msi_allowed = (kvm_check_extension(s, KVM_CAP_SIGNAL_MSI) > 0); | ||||||
| #endif | #endif | ||||||
| @@ -1992,8 +1614,10 @@ static int kvm_init(MachineState *ms) | |||||||
|         s->irq_set_ioctl = KVM_IRQ_LINE_STATUS; |         s->irq_set_ioctl = KVM_IRQ_LINE_STATUS; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | #ifdef KVM_CAP_READONLY_MEM | ||||||
|     kvm_readonly_mem_allowed = |     kvm_readonly_mem_allowed = | ||||||
|         (kvm_check_extension(s, KVM_CAP_READONLY_MEM) > 0); |         (kvm_check_extension(s, KVM_CAP_READONLY_MEM) > 0); | ||||||
|  | #endif | ||||||
|  |  | ||||||
|     kvm_eventfds_allowed = |     kvm_eventfds_allowed = | ||||||
|         (kvm_check_extension(s, KVM_CAP_IOEVENTFD) > 0); |         (kvm_check_extension(s, KVM_CAP_IOEVENTFD) > 0); | ||||||
| @@ -2012,20 +1636,6 @@ static int kvm_init(MachineState *ms) | |||||||
|  |  | ||||||
|     kvm_state = s; |     kvm_state = s; | ||||||
|  |  | ||||||
|     /* |  | ||||||
|      * if memory encryption object is specified then initialize the memory |  | ||||||
|      * encryption context. |  | ||||||
|      */ |  | ||||||
|     if (ms->memory_encryption) { |  | ||||||
|         kvm_state->memcrypt_handle = sev_guest_init(ms->memory_encryption); |  | ||||||
|         if (!kvm_state->memcrypt_handle) { |  | ||||||
|             ret = -1; |  | ||||||
|             goto err; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         kvm_state->memcrypt_encrypt_data = sev_encrypt_data; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     ret = kvm_arch_init(ms, s); |     ret = kvm_arch_init(ms, s); | ||||||
|     if (ret < 0) { |     if (ret < 0) { | ||||||
|         goto err; |         goto err; | ||||||
| @@ -2039,22 +1649,17 @@ static int kvm_init(MachineState *ms) | |||||||
|         s->memory_listener.listener.eventfd_add = kvm_mem_ioeventfd_add; |         s->memory_listener.listener.eventfd_add = kvm_mem_ioeventfd_add; | ||||||
|         s->memory_listener.listener.eventfd_del = kvm_mem_ioeventfd_del; |         s->memory_listener.listener.eventfd_del = kvm_mem_ioeventfd_del; | ||||||
|     } |     } | ||||||
|     s->memory_listener.listener.coalesced_io_add = kvm_coalesce_mmio_region; |     s->memory_listener.listener.coalesced_mmio_add = kvm_coalesce_mmio_region; | ||||||
|     s->memory_listener.listener.coalesced_io_del = kvm_uncoalesce_mmio_region; |     s->memory_listener.listener.coalesced_mmio_del = kvm_uncoalesce_mmio_region; | ||||||
|  |  | ||||||
|     kvm_memory_listener_register(s, &s->memory_listener, |     kvm_memory_listener_register(s, &s->memory_listener, | ||||||
|                                  &address_space_memory, 0); |                                  &address_space_memory, 0); | ||||||
|     memory_listener_register(&kvm_io_listener, |     memory_listener_register(&kvm_io_listener, | ||||||
|                              &address_space_io); |                              &address_space_io); | ||||||
|     memory_listener_register(&kvm_coalesced_pio_listener, |  | ||||||
|                              &address_space_io); |  | ||||||
|  |  | ||||||
|     s->many_ioeventfds = kvm_check_many_ioeventfds(); |     s->many_ioeventfds = kvm_check_many_ioeventfds(); | ||||||
|  |  | ||||||
|     s->sync_mmu = !!kvm_vm_check_extension(kvm_state, KVM_CAP_SYNC_MMU); |     s->sync_mmu = !!kvm_vm_check_extension(kvm_state, KVM_CAP_SYNC_MMU); | ||||||
|     if (!s->sync_mmu) { |  | ||||||
|         qemu_balloon_inhibit(true); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
|  |  | ||||||
| @@ -2106,7 +1711,7 @@ static int kvm_handle_internal_error(CPUState *cpu, struct kvm_run *run) | |||||||
|     if (run->internal.suberror == KVM_INTERNAL_ERROR_EMULATION) { |     if (run->internal.suberror == KVM_INTERNAL_ERROR_EMULATION) { | ||||||
|         fprintf(stderr, "emulation failure\n"); |         fprintf(stderr, "emulation failure\n"); | ||||||
|         if (!kvm_arch_stop_on_emulation_error(cpu)) { |         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; |             return EXCP_INTERRUPT; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -2133,13 +1738,7 @@ void kvm_flush_coalesced_mmio_buffer(void) | |||||||
|  |  | ||||||
|             ent = &ring->coalesced_mmio[ring->first]; |             ent = &ring->coalesced_mmio[ring->first]; | ||||||
|  |  | ||||||
|             if (ent->pio == 1) { |             cpu_physical_memory_write(ent->phys_addr, ent->data, ent->len); | ||||||
|                 address_space_rw(&address_space_io, ent->phys_addr, |  | ||||||
|                                  MEMTXATTRS_UNSPECIFIED, ent->data, |  | ||||||
|                                  ent->len, true); |  | ||||||
|             } else { |  | ||||||
|                 cpu_physical_memory_write(ent->phys_addr, ent->data, ent->len); |  | ||||||
|             } |  | ||||||
|             smp_wmb(); |             smp_wmb(); | ||||||
|             ring->first = (ring->first + 1) % KVM_COALESCED_MMIO_MAX; |             ring->first = (ring->first + 1) % KVM_COALESCED_MMIO_MAX; | ||||||
|         } |         } | ||||||
| @@ -2397,7 +1996,7 @@ int kvm_cpu_exec(CPUState *cpu) | |||||||
|     qemu_mutex_lock_iothread(); |     qemu_mutex_lock_iothread(); | ||||||
|  |  | ||||||
|     if (ret < 0) { |     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); |         vm_stop(RUN_STATE_INTERNAL_ERROR); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -2548,11 +2147,6 @@ int kvm_has_debugregs(void) | |||||||
|     return kvm_state->debugregs; |     return kvm_state->debugregs; | ||||||
| } | } | ||||||
|  |  | ||||||
| int kvm_max_nested_state_length(void) |  | ||||||
| { |  | ||||||
|     return kvm_state->max_nested_state_len; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int kvm_has_many_ioeventfds(void) | int kvm_has_many_ioeventfds(void) | ||||||
| { | { | ||||||
|     if (!kvm_enabled()) { |     if (!kvm_enabled()) { | ||||||
| @@ -2905,29 +2499,11 @@ int kvm_get_one_reg(CPUState *cs, uint64_t id, void *target) | |||||||
|     return r; |     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) | static void kvm_accel_class_init(ObjectClass *oc, void *data) | ||||||
| { | { | ||||||
|     AccelClass *ac = ACCEL_CLASS(oc); |     AccelClass *ac = ACCEL_CLASS(oc); | ||||||
|     ac->name = "KVM"; |     ac->name = "KVM"; | ||||||
|     ac->init_machine = kvm_init; |     ac->init_machine = kvm_init; | ||||||
|     ac->has_memory = kvm_accel_has_memory; |  | ||||||
|     ac->allowed = &kvm_allowed; |     ac->allowed = &kvm_allowed; | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,26 +0,0 @@ | |||||||
| /* |  | ||||||
|  * QEMU SEV stub |  | ||||||
|  * |  | ||||||
|  * Copyright Advanced Micro Devices 2018 |  | ||||||
|  * |  | ||||||
|  * Authors: |  | ||||||
|  *      Brijesh Singh <brijesh.singh@amd.com> |  | ||||||
|  * |  | ||||||
|  * This work is licensed under the terms of the GNU GPL, version 2 or later. |  | ||||||
|  * See the COPYING file in the top-level directory. |  | ||||||
|  * |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #include "qemu/osdep.h" |  | ||||||
| #include "qemu-common.h" |  | ||||||
| #include "sysemu/sev.h" |  | ||||||
|  |  | ||||||
| int sev_encrypt_data(void *handle, uint8_t *ptr, uint64_t len) |  | ||||||
| { |  | ||||||
|     abort(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void *sev_guest_init(const char *id) |  | ||||||
| { |  | ||||||
|     return NULL; |  | ||||||
| } |  | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| # See docs/devel/tracing.txt for syntax documentation. | # Trace events for debugging and performance instrumentation | ||||||
|  |  | ||||||
| # kvm-all.c | # kvm-all.c | ||||||
| kvm_ioctl(int type, void *arg) "type 0x%x, arg %p" | kvm_ioctl(int type, void *arg) "type 0x%x, arg %p" | ||||||
| @@ -12,8 +12,5 @@ kvm_irqchip_commit_routes(void) "" | |||||||
| kvm_irqchip_add_msi_route(char *name, int vector, int virq) "dev %s vector %d virq %d" | kvm_irqchip_add_msi_route(char *name, int vector, int virq) "dev %s vector %d virq %d" | ||||||
| kvm_irqchip_update_msi_route(int virq) "Updating MSI route virq=%d" | kvm_irqchip_update_msi_route(int virq) "Updating MSI route virq=%d" | ||||||
| kvm_irqchip_release_virq(int virq) "virq %d" | 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_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 |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -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); |  | ||||||
| @@ -14,6 +14,7 @@ | |||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #include "qemu/osdep.h" | #include "qemu/osdep.h" | ||||||
|  | #include "qemu-common.h" | ||||||
| #include "cpu.h" | #include "cpu.h" | ||||||
| #include "sysemu/hax.h" | #include "sysemu/hax.h" | ||||||
|  |  | ||||||
|   | |||||||
| @@ -12,6 +12,7 @@ | |||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #include "qemu/osdep.h" | #include "qemu/osdep.h" | ||||||
|  | #include "qemu-common.h" | ||||||
| #include "cpu.h" | #include "cpu.h" | ||||||
| #include "sysemu/hvf.h" | #include "sysemu/hvf.h" | ||||||
|  |  | ||||||
|   | |||||||
| @@ -11,6 +11,7 @@ | |||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #include "qemu/osdep.h" | #include "qemu/osdep.h" | ||||||
|  | #include "qemu-common.h" | ||||||
| #include "cpu.h" | #include "cpu.h" | ||||||
| #include "sysemu/kvm.h" | #include "sysemu/kvm.h" | ||||||
|  |  | ||||||
| @@ -104,16 +105,6 @@ int kvm_on_sigbus(int code, void *addr) | |||||||
|     return 1; |     return 1; | ||||||
| } | } | ||||||
|  |  | ||||||
| bool kvm_memcrypt_enabled(void) |  | ||||||
| { |  | ||||||
|     return false; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int kvm_memcrypt_encrypt_data(uint8_t *ptr, uint64_t len) |  | ||||||
| { |  | ||||||
|   return 1; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #ifndef CONFIG_USER_ONLY | #ifndef CONFIG_USER_ONLY | ||||||
| int kvm_irqchip_add_msi_route(KVMState *s, int vector, PCIDevice *dev) | int kvm_irqchip_add_msi_route(KVMState *s, int vector, PCIDevice *dev) | ||||||
| { | { | ||||||
|   | |||||||
| @@ -11,14 +11,20 @@ | |||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #include "qemu/osdep.h" | #include "qemu/osdep.h" | ||||||
|  | #include "qemu-common.h" | ||||||
| #include "cpu.h" | #include "cpu.h" | ||||||
| #include "tcg/tcg.h" | #include "tcg/tcg.h" | ||||||
|  | #include "exec/cpu-common.h" | ||||||
| #include "exec/exec-all.h" | #include "exec/exec-all.h" | ||||||
|  |  | ||||||
| void tb_flush(CPUState *cpu) | void tb_flush(CPUState *cpu) | ||||||
| { | { | ||||||
| } | } | ||||||
|  |  | ||||||
|  | void tb_unlock(void) | ||||||
|  | { | ||||||
|  | } | ||||||
|  |  | ||||||
| void tlb_set_dirty(CPUState *cpu, target_ulong vaddr) | void tlb_set_dirty(CPUState *cpu, target_ulong vaddr) | ||||||
| { | { | ||||||
| } | } | ||||||
|   | |||||||
| @@ -9,6 +9,7 @@ | |||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #include "qemu/osdep.h" | #include "qemu/osdep.h" | ||||||
|  | #include "qemu-common.h" | ||||||
| #include "cpu.h" | #include "cpu.h" | ||||||
| #include "sysemu/whpx.h" | #include "sysemu/whpx.h" | ||||||
|  |  | ||||||
|   | |||||||
| @@ -6,4 +6,3 @@ obj-y += translator.o | |||||||
|  |  | ||||||
| obj-$(CONFIG_USER_ONLY) += user-exec.o | obj-$(CONFIG_USER_ONLY) += user-exec.o | ||||||
| obj-$(call lnot,$(CONFIG_SOFTMMU)) += user-exec-stub.o | obj-$(call lnot,$(CONFIG_SOFTMMU)) += user-exec-stub.o | ||||||
| obj-$(CONFIG_PLUGIN) += plugin-gen.o |  | ||||||
|   | |||||||
| @@ -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); |  | ||||||
| } |  | ||||||
| @@ -7,7 +7,7 @@ | |||||||
|  * This library is free software; you can redistribute it and/or |  * This library is free software; you can redistribute it and/or | ||||||
|  * modify it under the terms of the GNU Lesser General Public |  * modify it under the terms of the GNU Lesser General Public | ||||||
|  * License as published by the Free Software Foundation; either |  * License as published by the Free Software Foundation; either | ||||||
|  * version 2.1 of the License, or (at your option) any later version. |  * version 2 of the License, or (at your option) any later version. | ||||||
|  * |  * | ||||||
|  * This library is distributed in the hope that it will be useful, |  * This library is distributed in the hope that it will be useful, | ||||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
| @@ -18,38 +18,26 @@ | |||||||
|  * License along with this library; if not, see <http://www.gnu.org/licenses/>. |  * License along with this library; if not, see <http://www.gnu.org/licenses/>. | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #include "qemu/plugin.h" |  | ||||||
| #include "trace/mem.h" |  | ||||||
|  |  | ||||||
| #if DATA_SIZE == 16 | #if DATA_SIZE == 16 | ||||||
| # define SUFFIX     o | # define SUFFIX     o | ||||||
| # define DATA_TYPE  Int128 | # define DATA_TYPE  Int128 | ||||||
| # define BSWAP      bswap128 | # define BSWAP      bswap128 | ||||||
| # define SHIFT      4 |  | ||||||
| #elif DATA_SIZE == 8 | #elif DATA_SIZE == 8 | ||||||
| # define SUFFIX     q | # define SUFFIX     q | ||||||
| # define DATA_TYPE  uint64_t | # define DATA_TYPE  uint64_t | ||||||
| # define SDATA_TYPE int64_t |  | ||||||
| # define BSWAP      bswap64 | # define BSWAP      bswap64 | ||||||
| # define SHIFT      3 |  | ||||||
| #elif DATA_SIZE == 4 | #elif DATA_SIZE == 4 | ||||||
| # define SUFFIX     l | # define SUFFIX     l | ||||||
| # define DATA_TYPE  uint32_t | # define DATA_TYPE  uint32_t | ||||||
| # define SDATA_TYPE int32_t |  | ||||||
| # define BSWAP      bswap32 | # define BSWAP      bswap32 | ||||||
| # define SHIFT      2 |  | ||||||
| #elif DATA_SIZE == 2 | #elif DATA_SIZE == 2 | ||||||
| # define SUFFIX     w | # define SUFFIX     w | ||||||
| # define DATA_TYPE  uint16_t | # define DATA_TYPE  uint16_t | ||||||
| # define SDATA_TYPE int16_t |  | ||||||
| # define BSWAP      bswap16 | # define BSWAP      bswap16 | ||||||
| # define SHIFT      1 |  | ||||||
| #elif DATA_SIZE == 1 | #elif DATA_SIZE == 1 | ||||||
| # define SUFFIX     b | # define SUFFIX     b | ||||||
| # define DATA_TYPE  uint8_t | # define DATA_TYPE  uint8_t | ||||||
| # define SDATA_TYPE int8_t |  | ||||||
| # define BSWAP | # define BSWAP | ||||||
| # define SHIFT      0 |  | ||||||
| #else | #else | ||||||
| # error unsupported data size | # error unsupported data size | ||||||
| #endif | #endif | ||||||
| @@ -64,13 +52,10 @@ | |||||||
|    the ATOMIC_NAME macro, and redefined below.  */ |    the ATOMIC_NAME macro, and redefined below.  */ | ||||||
| #if DATA_SIZE == 1 | #if DATA_SIZE == 1 | ||||||
| # define END | # define END | ||||||
| # define MEND _be /* either le or be would be fine */ |  | ||||||
| #elif defined(HOST_WORDS_BIGENDIAN) | #elif defined(HOST_WORDS_BIGENDIAN) | ||||||
| # define END  _be | # define END  _be | ||||||
| # define MEND _be |  | ||||||
| #else | #else | ||||||
| # define END  _le | # define END  _le | ||||||
| # define MEND _le |  | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr, | ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr, | ||||||
| @@ -78,34 +63,18 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr, | |||||||
| { | { | ||||||
|     ATOMIC_MMU_DECLS; |     ATOMIC_MMU_DECLS; | ||||||
|     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; |     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; | ||||||
|     DATA_TYPE ret; |     DATA_TYPE ret = atomic_cmpxchg__nocheck(haddr, cmpv, newv); | ||||||
|     uint16_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, false, |  | ||||||
|                                                            ATOMIC_MMU_IDX); |  | ||||||
|  |  | ||||||
|     atomic_trace_rmw_pre(env, addr, info); |  | ||||||
| #if DATA_SIZE == 16 |  | ||||||
|     ret = atomic16_cmpxchg(haddr, cmpv, newv); |  | ||||||
| #else |  | ||||||
|     ret = atomic_cmpxchg__nocheck(haddr, cmpv, newv); |  | ||||||
| #endif |  | ||||||
|     ATOMIC_MMU_CLEANUP; |     ATOMIC_MMU_CLEANUP; | ||||||
|     atomic_trace_rmw_post(env, addr, info); |  | ||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
|  |  | ||||||
| #if DATA_SIZE >= 16 | #if DATA_SIZE >= 16 | ||||||
| #if HAVE_ATOMIC128 |  | ||||||
| ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS) | ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS) | ||||||
| { | { | ||||||
|     ATOMIC_MMU_DECLS; |     ATOMIC_MMU_DECLS; | ||||||
|     DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP; |     DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP; | ||||||
|     uint16_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, false, |     __atomic_load(haddr, &val, __ATOMIC_RELAXED); | ||||||
|                                                            ATOMIC_MMU_IDX); |  | ||||||
|  |  | ||||||
|     atomic_trace_ld_pre(env, addr, info); |  | ||||||
|     val = atomic16_read(haddr); |  | ||||||
|     ATOMIC_MMU_CLEANUP; |     ATOMIC_MMU_CLEANUP; | ||||||
|     atomic_trace_ld_post(env, addr, info); |  | ||||||
|     return val; |     return val; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -114,47 +83,28 @@ void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr, | |||||||
| { | { | ||||||
|     ATOMIC_MMU_DECLS; |     ATOMIC_MMU_DECLS; | ||||||
|     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; |     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; | ||||||
|     uint16_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, true, |     __atomic_store(haddr, &val, __ATOMIC_RELAXED); | ||||||
|                                                           ATOMIC_MMU_IDX); |  | ||||||
|  |  | ||||||
|     atomic_trace_st_pre(env, addr, info); |  | ||||||
|     atomic16_set(haddr, val); |  | ||||||
|     ATOMIC_MMU_CLEANUP; |     ATOMIC_MMU_CLEANUP; | ||||||
|     atomic_trace_st_post(env, addr, info); |  | ||||||
| } | } | ||||||
| #endif |  | ||||||
| #else | #else | ||||||
| ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr, | ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr, | ||||||
|                            ABI_TYPE val EXTRA_ARGS) |                            ABI_TYPE val EXTRA_ARGS) | ||||||
| { | { | ||||||
|     ATOMIC_MMU_DECLS; |     ATOMIC_MMU_DECLS; | ||||||
|     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; |     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; | ||||||
|     DATA_TYPE ret; |     DATA_TYPE ret = atomic_xchg__nocheck(haddr, val); | ||||||
|     uint16_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, false, |  | ||||||
|                                                           ATOMIC_MMU_IDX); |  | ||||||
|  |  | ||||||
|     atomic_trace_rmw_pre(env, addr, info); |  | ||||||
|     ret = atomic_xchg__nocheck(haddr, val); |  | ||||||
|     ATOMIC_MMU_CLEANUP; |     ATOMIC_MMU_CLEANUP; | ||||||
|     atomic_trace_rmw_post(env, addr, info); |  | ||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
|  |  | ||||||
| #define GEN_ATOMIC_HELPER(X)                                        \ | #define GEN_ATOMIC_HELPER(X)                                        \ | ||||||
| ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr,       \ | ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr,       \ | ||||||
|                         ABI_TYPE val EXTRA_ARGS)                    \ |                  ABI_TYPE val EXTRA_ARGS)                           \ | ||||||
| {                                                                   \ | {                                                                   \ | ||||||
|     ATOMIC_MMU_DECLS;                                               \ |     ATOMIC_MMU_DECLS;                                               \ | ||||||
|     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;                           \ |     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;                           \ | ||||||
|     DATA_TYPE ret;                                                  \ |     DATA_TYPE ret = atomic_##X(haddr, val);                         \ | ||||||
|     uint16_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT,   \ |  | ||||||
|                                                            false,   \ |  | ||||||
|                                                            ATOMIC_MMU_IDX); \ |  | ||||||
|                                                                     \ |  | ||||||
|     atomic_trace_rmw_pre(env, addr, info);                          \ |  | ||||||
|     ret = atomic_##X(haddr, val);                                   \ |  | ||||||
|     ATOMIC_MMU_CLEANUP;                                             \ |     ATOMIC_MMU_CLEANUP;                                             \ | ||||||
|     atomic_trace_rmw_post(env, addr, info);                         \ |  | ||||||
|     return ret;                                                     \ |     return ret;                                                     \ | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -168,52 +118,9 @@ GEN_ATOMIC_HELPER(or_fetch) | |||||||
| GEN_ATOMIC_HELPER(xor_fetch) | GEN_ATOMIC_HELPER(xor_fetch) | ||||||
|  |  | ||||||
| #undef GEN_ATOMIC_HELPER | #undef GEN_ATOMIC_HELPER | ||||||
|  |  | ||||||
| /* These helpers are, as a whole, full barriers.  Within the helper, |  | ||||||
|  * the leading barrier is explicit and the trailing barrier is within |  | ||||||
|  * cmpxchg primitive. |  | ||||||
|  * |  | ||||||
|  * Trace this load + RMW loop as a single RMW op. This way, regardless |  | ||||||
|  * of CF_PARALLEL's value, we'll trace just a read and a write. |  | ||||||
|  */ |  | ||||||
| #define GEN_ATOMIC_HELPER_FN(X, FN, XDATA_TYPE, RET)                \ |  | ||||||
| ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr,       \ |  | ||||||
|                         ABI_TYPE xval EXTRA_ARGS)                   \ |  | ||||||
| {                                                                   \ |  | ||||||
|     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);                          \ |  | ||||||
|     smp_mb();                                                       \ |  | ||||||
|     cmp = atomic_read__nocheck(haddr);                              \ |  | ||||||
|     do {                                                            \ |  | ||||||
|         old = cmp; new = FN(old, val);                              \ |  | ||||||
|         cmp = atomic_cmpxchg__nocheck(haddr, old, new);             \ |  | ||||||
|     } while (cmp != old);                                           \ |  | ||||||
|     ATOMIC_MMU_CLEANUP;                                             \ |  | ||||||
|     atomic_trace_rmw_post(env, addr, info);                         \ |  | ||||||
|     return RET;                                                     \ |  | ||||||
| } |  | ||||||
|  |  | ||||||
| GEN_ATOMIC_HELPER_FN(fetch_smin, MIN, SDATA_TYPE, old) |  | ||||||
| GEN_ATOMIC_HELPER_FN(fetch_umin, MIN,  DATA_TYPE, old) |  | ||||||
| GEN_ATOMIC_HELPER_FN(fetch_smax, MAX, SDATA_TYPE, old) |  | ||||||
| GEN_ATOMIC_HELPER_FN(fetch_umax, MAX,  DATA_TYPE, old) |  | ||||||
|  |  | ||||||
| GEN_ATOMIC_HELPER_FN(smin_fetch, MIN, SDATA_TYPE, new) |  | ||||||
| GEN_ATOMIC_HELPER_FN(umin_fetch, MIN,  DATA_TYPE, new) |  | ||||||
| GEN_ATOMIC_HELPER_FN(smax_fetch, MAX, SDATA_TYPE, new) |  | ||||||
| GEN_ATOMIC_HELPER_FN(umax_fetch, MAX,  DATA_TYPE, new) |  | ||||||
|  |  | ||||||
| #undef GEN_ATOMIC_HELPER_FN |  | ||||||
| #endif /* DATA SIZE >= 16 */ | #endif /* DATA SIZE >= 16 */ | ||||||
|  |  | ||||||
| #undef END | #undef END | ||||||
| #undef MEND |  | ||||||
|  |  | ||||||
| #if DATA_SIZE > 1 | #if DATA_SIZE > 1 | ||||||
|  |  | ||||||
| @@ -221,10 +128,8 @@ GEN_ATOMIC_HELPER_FN(umax_fetch, MAX,  DATA_TYPE, new) | |||||||
|    within the ATOMIC_NAME macro.  */ |    within the ATOMIC_NAME macro.  */ | ||||||
| #ifdef HOST_WORDS_BIGENDIAN | #ifdef HOST_WORDS_BIGENDIAN | ||||||
| # define END  _le | # define END  _le | ||||||
| # define MEND _le |  | ||||||
| #else | #else | ||||||
| # define END  _be | # define END  _be | ||||||
| # define MEND _be |  | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr, | ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr, | ||||||
| @@ -232,36 +137,18 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr, | |||||||
| { | { | ||||||
|     ATOMIC_MMU_DECLS; |     ATOMIC_MMU_DECLS; | ||||||
|     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; |     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; | ||||||
|     DATA_TYPE ret; |     DATA_TYPE ret = atomic_cmpxchg__nocheck(haddr, BSWAP(cmpv), BSWAP(newv)); | ||||||
|     uint16_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, |  | ||||||
|                                                            false, |  | ||||||
|                                                            ATOMIC_MMU_IDX); |  | ||||||
|  |  | ||||||
|     atomic_trace_rmw_pre(env, addr, info); |  | ||||||
| #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_MMU_CLEANUP; | ||||||
|     atomic_trace_rmw_post(env, addr, info); |  | ||||||
|     return BSWAP(ret); |     return BSWAP(ret); | ||||||
| } | } | ||||||
|  |  | ||||||
| #if DATA_SIZE >= 16 | #if DATA_SIZE >= 16 | ||||||
| #if HAVE_ATOMIC128 |  | ||||||
| ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS) | ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS) | ||||||
| { | { | ||||||
|     ATOMIC_MMU_DECLS; |     ATOMIC_MMU_DECLS; | ||||||
|     DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP; |     DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP; | ||||||
|     uint16_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, |     __atomic_load(haddr, &val, __ATOMIC_RELAXED); | ||||||
|                                                            false, |  | ||||||
|                                                            ATOMIC_MMU_IDX); |  | ||||||
|  |  | ||||||
|     atomic_trace_ld_pre(env, addr, info); |  | ||||||
|     val = atomic16_read(haddr); |  | ||||||
|     ATOMIC_MMU_CLEANUP; |     ATOMIC_MMU_CLEANUP; | ||||||
|     atomic_trace_ld_post(env, addr, info); |  | ||||||
|     return BSWAP(val); |     return BSWAP(val); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -270,51 +157,29 @@ void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr, | |||||||
| { | { | ||||||
|     ATOMIC_MMU_DECLS; |     ATOMIC_MMU_DECLS; | ||||||
|     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; |     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; | ||||||
|     uint16_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, |  | ||||||
|                                                            true, |  | ||||||
|                                                            ATOMIC_MMU_IDX); |  | ||||||
|  |  | ||||||
|     val = BSWAP(val); |     val = BSWAP(val); | ||||||
|     atomic_trace_st_pre(env, addr, info); |     __atomic_store(haddr, &val, __ATOMIC_RELAXED); | ||||||
|     val = BSWAP(val); |  | ||||||
|     atomic16_set(haddr, val); |  | ||||||
|     ATOMIC_MMU_CLEANUP; |     ATOMIC_MMU_CLEANUP; | ||||||
|     atomic_trace_st_post(env, addr, info); |  | ||||||
| } | } | ||||||
| #endif |  | ||||||
| #else | #else | ||||||
| ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr, | ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr, | ||||||
|                            ABI_TYPE val EXTRA_ARGS) |                            ABI_TYPE val EXTRA_ARGS) | ||||||
| { | { | ||||||
|     ATOMIC_MMU_DECLS; |     ATOMIC_MMU_DECLS; | ||||||
|     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; |     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; | ||||||
|     ABI_TYPE ret; |     ABI_TYPE ret = atomic_xchg__nocheck(haddr, BSWAP(val)); | ||||||
|     uint16_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, |  | ||||||
|                                                            false, |  | ||||||
|                                                            ATOMIC_MMU_IDX); |  | ||||||
|  |  | ||||||
|     atomic_trace_rmw_pre(env, addr, info); |  | ||||||
|     ret = atomic_xchg__nocheck(haddr, BSWAP(val)); |  | ||||||
|     ATOMIC_MMU_CLEANUP; |     ATOMIC_MMU_CLEANUP; | ||||||
|     atomic_trace_rmw_post(env, addr, info); |  | ||||||
|     return BSWAP(ret); |     return BSWAP(ret); | ||||||
| } | } | ||||||
|  |  | ||||||
| #define GEN_ATOMIC_HELPER(X)                                        \ | #define GEN_ATOMIC_HELPER(X)                                        \ | ||||||
| ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr,       \ | ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr,       \ | ||||||
|                         ABI_TYPE val EXTRA_ARGS)                    \ |                  ABI_TYPE val EXTRA_ARGS)                           \ | ||||||
| {                                                                   \ | {                                                                   \ | ||||||
|     ATOMIC_MMU_DECLS;                                               \ |     ATOMIC_MMU_DECLS;                                               \ | ||||||
|     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;                           \ |     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;                           \ | ||||||
|     DATA_TYPE ret;                                                  \ |     DATA_TYPE ret = atomic_##X(haddr, BSWAP(val));                  \ | ||||||
|     uint16_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT,   \ |  | ||||||
|                                                            false,   \ |  | ||||||
|                                                            ATOMIC_MMU_IDX); \ |  | ||||||
|                                                                     \ |  | ||||||
|     atomic_trace_rmw_pre(env, addr, info);                          \ |  | ||||||
|     ret = atomic_##X(haddr, BSWAP(val));                            \ |  | ||||||
|     ATOMIC_MMU_CLEANUP;                                             \ |     ATOMIC_MMU_CLEANUP;                                             \ | ||||||
|     atomic_trace_rmw_post(env, addr, info);                         \ |  | ||||||
|     return BSWAP(ret);                                              \ |     return BSWAP(ret);                                              \ | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -327,64 +192,54 @@ GEN_ATOMIC_HELPER(xor_fetch) | |||||||
|  |  | ||||||
| #undef GEN_ATOMIC_HELPER | #undef GEN_ATOMIC_HELPER | ||||||
|  |  | ||||||
| /* These helpers are, as a whole, full barriers.  Within the helper, |  | ||||||
|  * the leading barrier is explicit and the trailing barrier is within |  | ||||||
|  * cmpxchg primitive. |  | ||||||
|  * |  | ||||||
|  * Trace this load + RMW loop as a single RMW op. This way, regardless |  | ||||||
|  * of CF_PARALLEL's value, we'll trace just a read and a write. |  | ||||||
|  */ |  | ||||||
| #define GEN_ATOMIC_HELPER_FN(X, FN, XDATA_TYPE, RET)                \ |  | ||||||
| ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr,       \ |  | ||||||
|                         ABI_TYPE xval EXTRA_ARGS)                   \ |  | ||||||
| {                                                                   \ |  | ||||||
|     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);                          \ |  | ||||||
|     smp_mb();                                                       \ |  | ||||||
|     ldn = atomic_read__nocheck(haddr);                              \ |  | ||||||
|     do {                                                            \ |  | ||||||
|         ldo = ldn; old = BSWAP(ldo); new = FN(old, val);            \ |  | ||||||
|         ldn = atomic_cmpxchg__nocheck(haddr, ldo, BSWAP(new));      \ |  | ||||||
|     } while (ldo != ldn);                                           \ |  | ||||||
|     ATOMIC_MMU_CLEANUP;                                             \ |  | ||||||
|     atomic_trace_rmw_post(env, addr, info);                         \ |  | ||||||
|     return RET;                                                     \ |  | ||||||
| } |  | ||||||
|  |  | ||||||
| GEN_ATOMIC_HELPER_FN(fetch_smin, MIN, SDATA_TYPE, old) |  | ||||||
| GEN_ATOMIC_HELPER_FN(fetch_umin, MIN,  DATA_TYPE, old) |  | ||||||
| GEN_ATOMIC_HELPER_FN(fetch_smax, MAX, SDATA_TYPE, old) |  | ||||||
| GEN_ATOMIC_HELPER_FN(fetch_umax, MAX,  DATA_TYPE, old) |  | ||||||
|  |  | ||||||
| GEN_ATOMIC_HELPER_FN(smin_fetch, MIN, SDATA_TYPE, new) |  | ||||||
| GEN_ATOMIC_HELPER_FN(umin_fetch, MIN,  DATA_TYPE, new) |  | ||||||
| GEN_ATOMIC_HELPER_FN(smax_fetch, MAX, SDATA_TYPE, new) |  | ||||||
| GEN_ATOMIC_HELPER_FN(umax_fetch, MAX,  DATA_TYPE, new) |  | ||||||
|  |  | ||||||
| /* Note that for addition, we need to use a separate cmpxchg loop instead | /* Note that for addition, we need to use a separate cmpxchg loop instead | ||||||
|    of bswaps for the reverse-host-endian helpers.  */ |    of bswaps for the reverse-host-endian helpers.  */ | ||||||
| #define ADD(X, Y)   (X + Y) | ABI_TYPE ATOMIC_NAME(fetch_add)(CPUArchState *env, target_ulong addr, | ||||||
| GEN_ATOMIC_HELPER_FN(fetch_add, ADD, DATA_TYPE, old) |                          ABI_TYPE val EXTRA_ARGS) | ||||||
| GEN_ATOMIC_HELPER_FN(add_fetch, ADD, DATA_TYPE, new) | { | ||||||
| #undef ADD |     ATOMIC_MMU_DECLS; | ||||||
|  |     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; | ||||||
|  |     DATA_TYPE ldo, ldn, ret, sto; | ||||||
|  |  | ||||||
| #undef GEN_ATOMIC_HELPER_FN |     ldo = atomic_read__nocheck(haddr); | ||||||
|  |     while (1) { | ||||||
|  |         ret = BSWAP(ldo); | ||||||
|  |         sto = BSWAP(ret + val); | ||||||
|  |         ldn = atomic_cmpxchg__nocheck(haddr, ldo, sto); | ||||||
|  |         if (ldn == ldo) { | ||||||
|  |             ATOMIC_MMU_CLEANUP; | ||||||
|  |             return ret; | ||||||
|  |         } | ||||||
|  |         ldo = ldn; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | ABI_TYPE ATOMIC_NAME(add_fetch)(CPUArchState *env, target_ulong addr, | ||||||
|  |                          ABI_TYPE val EXTRA_ARGS) | ||||||
|  | { | ||||||
|  |     ATOMIC_MMU_DECLS; | ||||||
|  |     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; | ||||||
|  |     DATA_TYPE ldo, ldn, ret, sto; | ||||||
|  |  | ||||||
|  |     ldo = atomic_read__nocheck(haddr); | ||||||
|  |     while (1) { | ||||||
|  |         ret = BSWAP(ldo) + val; | ||||||
|  |         sto = BSWAP(ret); | ||||||
|  |         ldn = atomic_cmpxchg__nocheck(haddr, ldo, sto); | ||||||
|  |         if (ldn == ldo) { | ||||||
|  |             ATOMIC_MMU_CLEANUP; | ||||||
|  |             return ret; | ||||||
|  |         } | ||||||
|  |         ldo = ldn; | ||||||
|  |     } | ||||||
|  | } | ||||||
| #endif /* DATA_SIZE >= 16 */ | #endif /* DATA_SIZE >= 16 */ | ||||||
|  |  | ||||||
| #undef END | #undef END | ||||||
| #undef MEND |  | ||||||
| #endif /* DATA_SIZE > 1 */ | #endif /* DATA_SIZE > 1 */ | ||||||
|  |  | ||||||
| #undef BSWAP | #undef BSWAP | ||||||
| #undef ABI_TYPE | #undef ABI_TYPE | ||||||
| #undef DATA_TYPE | #undef DATA_TYPE | ||||||
| #undef SDATA_TYPE |  | ||||||
| #undef SUFFIX | #undef SUFFIX | ||||||
| #undef DATA_SIZE | #undef DATA_SIZE | ||||||
| #undef SHIFT |  | ||||||
|   | |||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * This library is free software; you can redistribute it and/or |  * This library is free software; you can redistribute it and/or | ||||||
|  * modify it under the terms of the GNU Lesser General Public |  * modify it under the terms of the GNU Lesser General Public | ||||||
|  * License as published by the Free Software Foundation; either |  * License as published by the Free Software Foundation; either | ||||||
|  * version 2.1 of the License, or (at your option) any later version. |  * version 2 of the License, or (at your option) any later version. | ||||||
|  * |  * | ||||||
|  * This library is distributed in the hope that it will be useful, |  * This library is distributed in the hope that it will be useful, | ||||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
| @@ -20,7 +20,6 @@ | |||||||
| #include "qemu/osdep.h" | #include "qemu/osdep.h" | ||||||
| #include "cpu.h" | #include "cpu.h" | ||||||
| #include "sysemu/cpus.h" | #include "sysemu/cpus.h" | ||||||
| #include "sysemu/tcg.h" |  | ||||||
| #include "exec/exec-all.h" | #include "exec/exec-all.h" | ||||||
|  |  | ||||||
| bool tcg_allowed; | bool tcg_allowed; | ||||||
| @@ -28,8 +27,10 @@ bool tcg_allowed; | |||||||
| /* exit the current TB, but without causing any exception to be raised */ | /* exit the current TB, but without causing any exception to be raised */ | ||||||
| void cpu_loop_exit_noexc(CPUState *cpu) | void cpu_loop_exit_noexc(CPUState *cpu) | ||||||
| { | { | ||||||
|  |     /* XXX: restore cpu registers saved in host registers */ | ||||||
|  |  | ||||||
|     cpu->exception_index = -1; |     cpu->exception_index = -1; | ||||||
|     cpu_loop_exit(cpu); |     siglongjmp(cpu->jmp_env, 1); | ||||||
| } | } | ||||||
|  |  | ||||||
| #if defined(CONFIG_SOFTMMU) | #if defined(CONFIG_SOFTMMU) | ||||||
| @@ -64,17 +65,15 @@ void cpu_reloading_memory_map(void) | |||||||
|  |  | ||||||
| void cpu_loop_exit(CPUState *cpu) | void cpu_loop_exit(CPUState *cpu) | ||||||
| { | { | ||||||
|     /* Undo the setting in cpu_tb_exec.  */ |  | ||||||
|     cpu->can_do_io = 1; |  | ||||||
|     siglongjmp(cpu->jmp_env, 1); |     siglongjmp(cpu->jmp_env, 1); | ||||||
| } | } | ||||||
|  |  | ||||||
| void cpu_loop_exit_restore(CPUState *cpu, uintptr_t pc) | void cpu_loop_exit_restore(CPUState *cpu, uintptr_t pc) | ||||||
| { | { | ||||||
|     if (pc) { |     if (pc) { | ||||||
|         cpu_restore_state(cpu, pc, true); |         cpu_restore_state(cpu, pc); | ||||||
|     } |     } | ||||||
|     cpu_loop_exit(cpu); |     siglongjmp(cpu->jmp_env, 1); | ||||||
| } | } | ||||||
|  |  | ||||||
| void cpu_loop_exit_atomic(CPUState *cpu, uintptr_t pc) | void cpu_loop_exit_atomic(CPUState *cpu, uintptr_t pc) | ||||||
|   | |||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * This library is free software; you can redistribute it and/or |  * This library is free software; you can redistribute it and/or | ||||||
|  * modify it under the terms of the GNU Lesser General Public |  * modify it under the terms of the GNU Lesser General Public | ||||||
|  * License as published by the Free Software Foundation; either |  * License as published by the Free Software Foundation; either | ||||||
|  * version 2.1 of the License, or (at your option) any later version. |  * version 2 of the License, or (at your option) any later version. | ||||||
|  * |  * | ||||||
|  * This library is distributed in the hope that it will be useful, |  * This library is distributed in the hope that it will be useful, | ||||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
| @@ -16,9 +16,7 @@ | |||||||
|  * You should have received a copy of the GNU Lesser General Public |  * You should have received a copy of the GNU Lesser General Public | ||||||
|  * License along with this library; if not, see <http://www.gnu.org/licenses/>. |  * License along with this library; if not, see <http://www.gnu.org/licenses/>. | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #include "qemu/osdep.h" | #include "qemu/osdep.h" | ||||||
| #include "qemu-common.h" |  | ||||||
| #include "cpu.h" | #include "cpu.h" | ||||||
| #include "trace.h" | #include "trace.h" | ||||||
| #include "disas/disas.h" | #include "disas/disas.h" | ||||||
| @@ -27,6 +25,7 @@ | |||||||
| #include "qemu/atomic.h" | #include "qemu/atomic.h" | ||||||
| #include "sysemu/qtest.h" | #include "sysemu/qtest.h" | ||||||
| #include "qemu/timer.h" | #include "qemu/timer.h" | ||||||
|  | #include "exec/address-spaces.h" | ||||||
| #include "qemu/rcu.h" | #include "qemu/rcu.h" | ||||||
| #include "exec/tb-hash.h" | #include "exec/tb-hash.h" | ||||||
| #include "exec/tb-lookup.h" | #include "exec/tb-lookup.h" | ||||||
| @@ -56,7 +55,7 @@ typedef struct SyncClocks { | |||||||
| #define MAX_DELAY_PRINT_RATE 2000000000LL | #define MAX_DELAY_PRINT_RATE 2000000000LL | ||||||
| #define MAX_NB_PRINTS 100 | #define MAX_NB_PRINTS 100 | ||||||
|  |  | ||||||
| static void align_clocks(SyncClocks *sc, CPUState *cpu) | static void align_clocks(SyncClocks *sc, const CPUState *cpu) | ||||||
| { | { | ||||||
|     int64_t cpu_icount; |     int64_t cpu_icount; | ||||||
|  |  | ||||||
| @@ -64,7 +63,7 @@ static void align_clocks(SyncClocks *sc, CPUState *cpu) | |||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     cpu_icount = cpu->icount_extra + cpu_neg(cpu)->icount_decr.u16.low; |     cpu_icount = cpu->icount_extra + cpu->icount_decr.u16.low; | ||||||
|     sc->diff_clk += cpu_icount_to_ns(sc->last_cpu_icount - cpu_icount); |     sc->diff_clk += cpu_icount_to_ns(sc->last_cpu_icount - cpu_icount); | ||||||
|     sc->last_cpu_icount = cpu_icount; |     sc->last_cpu_icount = cpu_icount; | ||||||
|  |  | ||||||
| @@ -107,15 +106,15 @@ static void print_delay(const SyncClocks *sc) | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| static void init_delay_params(SyncClocks *sc, CPUState *cpu) | static void init_delay_params(SyncClocks *sc, | ||||||
|  |                               const CPUState *cpu) | ||||||
| { | { | ||||||
|     if (!icount_align_option) { |     if (!icount_align_option) { | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|     sc->realtime_clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL_RT); |     sc->realtime_clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL_RT); | ||||||
|     sc->diff_clk = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - sc->realtime_clock; |     sc->diff_clk = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - sc->realtime_clock; | ||||||
|     sc->last_cpu_icount |     sc->last_cpu_icount = cpu->icount_extra + cpu->icount_decr.u16.low; | ||||||
|         = cpu->icount_extra + cpu_neg(cpu)->icount_decr.u16.low; |  | ||||||
|     if (sc->diff_clk < max_delay) { |     if (sc->diff_clk < max_delay) { | ||||||
|         max_delay = sc->diff_clk; |         max_delay = sc->diff_clk; | ||||||
|     } |     } | ||||||
| @@ -157,18 +156,16 @@ static inline tcg_target_ulong cpu_tb_exec(CPUState *cpu, TranslationBlock *itb) | |||||||
|     if (qemu_loglevel_mask(CPU_LOG_TB_CPU) |     if (qemu_loglevel_mask(CPU_LOG_TB_CPU) | ||||||
|         && qemu_log_in_addr_range(itb->pc)) { |         && qemu_log_in_addr_range(itb->pc)) { | ||||||
|         qemu_log_lock(); |         qemu_log_lock(); | ||||||
|         int flags = 0; |  | ||||||
|         if (qemu_loglevel_mask(CPU_LOG_TB_FPU)) { |  | ||||||
|             flags |= CPU_DUMP_FPU; |  | ||||||
|         } |  | ||||||
| #if defined(TARGET_I386) | #if defined(TARGET_I386) | ||||||
|         flags |= CPU_DUMP_CCOP; |         log_cpu_state(cpu, CPU_DUMP_CCOP); | ||||||
|  | #else | ||||||
|  |         log_cpu_state(cpu, 0); | ||||||
| #endif | #endif | ||||||
|         log_cpu_state(cpu, flags); |  | ||||||
|         qemu_log_unlock(); |         qemu_log_unlock(); | ||||||
|     } |     } | ||||||
| #endif /* DEBUG_DISAS */ | #endif /* DEBUG_DISAS */ | ||||||
|  |  | ||||||
|  |     cpu->can_do_io = !use_icount; | ||||||
|     ret = tcg_qemu_tb_exec(env, tb_ptr); |     ret = tcg_qemu_tb_exec(env, tb_ptr); | ||||||
|     cpu->can_do_io = 1; |     cpu->can_do_io = 1; | ||||||
|     last_tb = (TranslationBlock *)(ret & ~TB_EXIT_MASK); |     last_tb = (TranslationBlock *)(ret & ~TB_EXIT_MASK); | ||||||
| @@ -213,20 +210,20 @@ static void cpu_exec_nocache(CPUState *cpu, int max_cycles, | |||||||
|        We only end up here when an existing TB is too long.  */ |        We only end up here when an existing TB is too long.  */ | ||||||
|     cflags |= MIN(max_cycles, CF_COUNT_MASK); |     cflags |= MIN(max_cycles, CF_COUNT_MASK); | ||||||
|  |  | ||||||
|     mmap_lock(); |     tb_lock(); | ||||||
|     tb = tb_gen_code(cpu, orig_tb->pc, orig_tb->cs_base, |     tb = tb_gen_code(cpu, orig_tb->pc, orig_tb->cs_base, | ||||||
|                      orig_tb->flags, cflags); |                      orig_tb->flags, cflags); | ||||||
|     tb->orig_tb = orig_tb; |     tb->orig_tb = orig_tb; | ||||||
|     mmap_unlock(); |     tb_unlock(); | ||||||
|  |  | ||||||
|     /* execute the generated code */ |     /* execute the generated code */ | ||||||
|     trace_exec_tb_nocache(tb, tb->pc); |     trace_exec_tb_nocache(tb, tb->pc); | ||||||
|     cpu_tb_exec(cpu, tb); |     cpu_tb_exec(cpu, tb); | ||||||
|  |  | ||||||
|     mmap_lock(); |     tb_lock(); | ||||||
|     tb_phys_invalidate(tb, -1); |     tb_phys_invalidate(tb, -1); | ||||||
|     mmap_unlock(); |     tb_remove(tb); | ||||||
|     tcg_tb_remove(tb); |     tb_unlock(); | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| @@ -238,12 +235,19 @@ void cpu_exec_step_atomic(CPUState *cpu) | |||||||
|     uint32_t flags; |     uint32_t flags; | ||||||
|     uint32_t cflags = 1; |     uint32_t cflags = 1; | ||||||
|     uint32_t cf_mask = cflags & CF_HASH_MASK; |     uint32_t cf_mask = cflags & CF_HASH_MASK; | ||||||
|  |     /* volatile because we modify it between setjmp and longjmp */ | ||||||
|  |     volatile bool in_exclusive_region = false; | ||||||
|  |  | ||||||
|     if (sigsetjmp(cpu->jmp_env, 0) == 0) { |     if (sigsetjmp(cpu->jmp_env, 0) == 0) { | ||||||
|         tb = tb_lookup__cpu_state(cpu, &pc, &cs_base, &flags, cf_mask); |         tb = tb_lookup__cpu_state(cpu, &pc, &cs_base, &flags, cf_mask); | ||||||
|         if (tb == NULL) { |         if (tb == NULL) { | ||||||
|             mmap_lock(); |             mmap_lock(); | ||||||
|             tb = tb_gen_code(cpu, pc, cs_base, flags, cflags); |             tb_lock(); | ||||||
|  |             tb = tb_htable_lookup(cpu, pc, cs_base, flags, cf_mask); | ||||||
|  |             if (likely(tb == NULL)) { | ||||||
|  |                 tb = tb_gen_code(cpu, pc, cs_base, flags, cflags); | ||||||
|  |             } | ||||||
|  |             tb_unlock(); | ||||||
|             mmap_unlock(); |             mmap_unlock(); | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -251,27 +255,25 @@ void cpu_exec_step_atomic(CPUState *cpu) | |||||||
|  |  | ||||||
|         /* Since we got here, we know that parallel_cpus must be true.  */ |         /* Since we got here, we know that parallel_cpus must be true.  */ | ||||||
|         parallel_cpus = false; |         parallel_cpus = false; | ||||||
|  |         in_exclusive_region = true; | ||||||
|         cc->cpu_exec_enter(cpu); |         cc->cpu_exec_enter(cpu); | ||||||
|         /* execute the generated code */ |         /* execute the generated code */ | ||||||
|         trace_exec_tb(tb, pc); |         trace_exec_tb(tb, pc); | ||||||
|         cpu_tb_exec(cpu, tb); |         cpu_tb_exec(cpu, tb); | ||||||
|         cc->cpu_exec_exit(cpu); |         cc->cpu_exec_exit(cpu); | ||||||
|     } else { |     } else { | ||||||
|         /* |         /* We may have exited due to another problem here, so we need | ||||||
|  |          * to reset any tb_locks we may have taken but didn't release. | ||||||
|          * The mmap_lock is dropped by tb_gen_code if it runs out of |          * The mmap_lock is dropped by tb_gen_code if it runs out of | ||||||
|          * memory. |          * memory. | ||||||
|          */ |          */ | ||||||
| #ifndef CONFIG_SOFTMMU | #ifndef CONFIG_SOFTMMU | ||||||
|         tcg_debug_assert(!have_mmap_lock()); |         tcg_debug_assert(!have_mmap_lock()); | ||||||
| #endif | #endif | ||||||
|         if (qemu_mutex_iothread_locked()) { |         tb_lock_reset(); | ||||||
|             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 |         /* We might longjump out of either the codegen or the | ||||||
|          * execution, so must make sure we only end the exclusive |          * execution, so must make sure we only end the exclusive | ||||||
|          * region if we started it. |          * region if we started it. | ||||||
| @@ -291,7 +293,7 @@ struct tb_desc { | |||||||
|     uint32_t trace_vcpu_dstate; |     uint32_t trace_vcpu_dstate; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| static bool tb_lookup_cmp(const void *p, const void *d) | static bool tb_cmp(const void *p, const void *d) | ||||||
| { | { | ||||||
|     const TranslationBlock *tb = p; |     const TranslationBlock *tb = p; | ||||||
|     const struct tb_desc *desc = d; |     const struct tb_desc *desc = d; | ||||||
| @@ -334,12 +336,9 @@ TranslationBlock *tb_htable_lookup(CPUState *cpu, target_ulong pc, | |||||||
|     desc.trace_vcpu_dstate = *cpu->trace_dstate; |     desc.trace_vcpu_dstate = *cpu->trace_dstate; | ||||||
|     desc.pc = pc; |     desc.pc = pc; | ||||||
|     phys_pc = get_page_addr_code(desc.env, pc); |     phys_pc = get_page_addr_code(desc.env, pc); | ||||||
|     if (phys_pc == -1) { |  | ||||||
|         return NULL; |  | ||||||
|     } |  | ||||||
|     desc.phys_page1 = phys_pc & TARGET_PAGE_MASK; |     desc.phys_page1 = phys_pc & TARGET_PAGE_MASK; | ||||||
|     h = tb_hash_func(phys_pc, pc, flags, cf_mask, *cpu->trace_dstate); |     h = tb_hash_func(phys_pc, pc, flags, cf_mask, *cpu->trace_dstate); | ||||||
|     return qht_lookup_custom(&tb_ctx.htable, &desc, h, tb_lookup_cmp); |     return qht_lookup(&tb_ctx.htable, tb_cmp, &desc, h); | ||||||
| } | } | ||||||
|  |  | ||||||
| void tb_set_jmp_target(TranslationBlock *tb, int n, uintptr_t addr) | void tb_set_jmp_target(TranslationBlock *tb, int n, uintptr_t addr) | ||||||
| @@ -353,43 +352,28 @@ void tb_set_jmp_target(TranslationBlock *tb, int n, uintptr_t addr) | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /* Called with tb_lock held.  */ | ||||||
| static inline void tb_add_jump(TranslationBlock *tb, int n, | static inline void tb_add_jump(TranslationBlock *tb, int n, | ||||||
|                                TranslationBlock *tb_next) |                                TranslationBlock *tb_next) | ||||||
| { | { | ||||||
|     uintptr_t old; |  | ||||||
|  |  | ||||||
|     assert(n < ARRAY_SIZE(tb->jmp_list_next)); |     assert(n < ARRAY_SIZE(tb->jmp_list_next)); | ||||||
|     qemu_spin_lock(&tb_next->jmp_lock); |     if (tb->jmp_list_next[n]) { | ||||||
|  |         /* Another thread has already done this while we were | ||||||
|     /* make sure the destination TB is valid */ |          * outside of the lock; nothing to do in this case */ | ||||||
|     if (tb_next->cflags & CF_INVALID) { |         return; | ||||||
|         goto out_unlock_next; |  | ||||||
|     } |     } | ||||||
|     /* Atomically claim the jump destination slot only if it was NULL */ |  | ||||||
|     old = atomic_cmpxchg(&tb->jmp_dest[n], (uintptr_t)NULL, (uintptr_t)tb_next); |  | ||||||
|     if (old) { |  | ||||||
|         goto out_unlock_next; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /* patch the native jump address */ |  | ||||||
|     tb_set_jmp_target(tb, n, (uintptr_t)tb_next->tc.ptr); |  | ||||||
|  |  | ||||||
|     /* add in TB jmp list */ |  | ||||||
|     tb->jmp_list_next[n] = tb_next->jmp_list_head; |  | ||||||
|     tb_next->jmp_list_head = (uintptr_t)tb | n; |  | ||||||
|  |  | ||||||
|     qemu_spin_unlock(&tb_next->jmp_lock); |  | ||||||
|  |  | ||||||
|     qemu_log_mask_and_addr(CPU_LOG_EXEC, tb->pc, |     qemu_log_mask_and_addr(CPU_LOG_EXEC, tb->pc, | ||||||
|                            "Linking TBs %p [" TARGET_FMT_lx |                            "Linking TBs %p [" TARGET_FMT_lx | ||||||
|                            "] index %d -> %p [" TARGET_FMT_lx "]\n", |                            "] index %d -> %p [" TARGET_FMT_lx "]\n", | ||||||
|                            tb->tc.ptr, tb->pc, n, |                            tb->tc.ptr, tb->pc, n, | ||||||
|                            tb_next->tc.ptr, tb_next->pc); |                            tb_next->tc.ptr, tb_next->pc); | ||||||
|     return; |  | ||||||
|  |  | ||||||
|  out_unlock_next: |     /* patch the native jump address */ | ||||||
|     qemu_spin_unlock(&tb_next->jmp_lock); |     tb_set_jmp_target(tb, n, (uintptr_t)tb_next->tc.ptr); | ||||||
|     return; |  | ||||||
|  |     /* add in TB jmp circular list */ | ||||||
|  |     tb->jmp_list_next[n] = tb_next->jmp_list_first; | ||||||
|  |     tb_next->jmp_list_first = (uintptr_t)tb | n; | ||||||
| } | } | ||||||
|  |  | ||||||
| static inline TranslationBlock *tb_find(CPUState *cpu, | static inline TranslationBlock *tb_find(CPUState *cpu, | ||||||
| @@ -399,11 +383,27 @@ static inline TranslationBlock *tb_find(CPUState *cpu, | |||||||
|     TranslationBlock *tb; |     TranslationBlock *tb; | ||||||
|     target_ulong cs_base, pc; |     target_ulong cs_base, pc; | ||||||
|     uint32_t flags; |     uint32_t flags; | ||||||
|  |     bool acquired_tb_lock = false; | ||||||
|  |  | ||||||
|     tb = tb_lookup__cpu_state(cpu, &pc, &cs_base, &flags, cf_mask); |     tb = tb_lookup__cpu_state(cpu, &pc, &cs_base, &flags, cf_mask); | ||||||
|     if (tb == NULL) { |     if (tb == NULL) { | ||||||
|  |         /* mmap_lock is needed by tb_gen_code, and mmap_lock must be | ||||||
|  |          * taken outside tb_lock. As system emulation is currently | ||||||
|  |          * single threaded the locks are NOPs. | ||||||
|  |          */ | ||||||
|         mmap_lock(); |         mmap_lock(); | ||||||
|         tb = tb_gen_code(cpu, pc, cs_base, flags, cf_mask); |         tb_lock(); | ||||||
|  |         acquired_tb_lock = true; | ||||||
|  |  | ||||||
|  |         /* There's a chance that our desired tb has been translated while | ||||||
|  |          * taking the locks so we check again inside the lock. | ||||||
|  |          */ | ||||||
|  |         tb = tb_htable_lookup(cpu, pc, cs_base, flags, cf_mask); | ||||||
|  |         if (likely(tb == NULL)) { | ||||||
|  |             /* if no translated code available, then translate it now */ | ||||||
|  |             tb = tb_gen_code(cpu, pc, cs_base, flags, cf_mask); | ||||||
|  |         } | ||||||
|  |  | ||||||
|         mmap_unlock(); |         mmap_unlock(); | ||||||
|         /* We add the TB in the virtual pc hash table for the fast lookup */ |         /* We add the TB in the virtual pc hash table for the fast lookup */ | ||||||
|         atomic_set(&cpu->tb_jmp_cache[tb_jmp_cache_hash_func(pc)], tb); |         atomic_set(&cpu->tb_jmp_cache[tb_jmp_cache_hash_func(pc)], tb); | ||||||
| @@ -418,8 +418,17 @@ static inline TranslationBlock *tb_find(CPUState *cpu, | |||||||
|     } |     } | ||||||
| #endif | #endif | ||||||
|     /* See if we can patch the calling TB. */ |     /* See if we can patch the calling TB. */ | ||||||
|     if (last_tb) { |     if (last_tb && !qemu_loglevel_mask(CPU_LOG_TB_NOCHAIN)) { | ||||||
|         tb_add_jump(last_tb, tb_exit, tb); |         if (!acquired_tb_lock) { | ||||||
|  |             tb_lock(); | ||||||
|  |             acquired_tb_lock = true; | ||||||
|  |         } | ||||||
|  |         if (!(tb->cflags & CF_INVALID)) { | ||||||
|  |             tb_add_jump(last_tb, tb_exit, tb); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     if (acquired_tb_lock) { | ||||||
|  |         tb_unlock(); | ||||||
|     } |     } | ||||||
|     return tb; |     return tb; | ||||||
| } | } | ||||||
| @@ -466,7 +475,7 @@ static inline bool cpu_handle_exception(CPUState *cpu, int *ret) | |||||||
|     if (cpu->exception_index < 0) { |     if (cpu->exception_index < 0) { | ||||||
| #ifndef CONFIG_USER_ONLY | #ifndef CONFIG_USER_ONLY | ||||||
|         if (replay_has_exception() |         if (replay_has_exception() | ||||||
|             && cpu_neg(cpu)->icount_decr.u16.low + cpu->icount_extra == 0) { |                && cpu->icount_decr.u16.low + cpu->icount_extra == 0) { | ||||||
|             /* try to cause an exception pending in the log */ |             /* try to cause an exception pending in the log */ | ||||||
|             cpu_exec_nocache(cpu, 1, tb_find(cpu, NULL, 0, curr_cflags()), true); |             cpu_exec_nocache(cpu, 1, tb_find(cpu, NULL, 0, curr_cflags()), true); | ||||||
|         } |         } | ||||||
| @@ -524,7 +533,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, | |||||||
|      * Ensure zeroing happens before reading cpu->exit_request or |      * Ensure zeroing happens before reading cpu->exit_request or | ||||||
|      * cpu->interrupt_request (see also smp_wmb in cpu_exit()) |      * cpu->interrupt_request (see also smp_wmb in cpu_exit()) | ||||||
|      */ |      */ | ||||||
|     atomic_mb_set(&cpu_neg(cpu)->icount_decr.u16.high, 0); |     atomic_mb_set(&cpu->icount_decr.u16.high, 0); | ||||||
|  |  | ||||||
|     if (unlikely(atomic_read(&cpu->interrupt_request))) { |     if (unlikely(atomic_read(&cpu->interrupt_request))) { | ||||||
|         int interrupt_request; |         int interrupt_request; | ||||||
| @@ -576,7 +585,6 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, | |||||||
|         else { |         else { | ||||||
|             if (cc->cpu_exec_interrupt(cpu, interrupt_request)) { |             if (cc->cpu_exec_interrupt(cpu, interrupt_request)) { | ||||||
|                 replay_interrupt(); |                 replay_interrupt(); | ||||||
|                 cpu->exception_index = -1; |  | ||||||
|                 *last_tb = NULL; |                 *last_tb = NULL; | ||||||
|             } |             } | ||||||
|             /* The target hook may have updated the 'cpu->interrupt_request'; |             /* The target hook may have updated the 'cpu->interrupt_request'; | ||||||
| @@ -595,13 +603,10 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* Finally, check if we need to exit to the main loop.  */ |     /* Finally, check if we need to exit to the main loop.  */ | ||||||
|     if (unlikely(atomic_read(&cpu->exit_request)) |     if (unlikely(atomic_read(&cpu->exit_request) | ||||||
|         || (use_icount |         || (use_icount && cpu->icount_decr.u16.low + cpu->icount_extra == 0))) { | ||||||
|             && cpu_neg(cpu)->icount_decr.u16.low + cpu->icount_extra == 0)) { |  | ||||||
|         atomic_set(&cpu->exit_request, 0); |         atomic_set(&cpu->exit_request, 0); | ||||||
|         if (cpu->exception_index == -1) { |         cpu->exception_index = EXCP_INTERRUPT; | ||||||
|             cpu->exception_index = EXCP_INTERRUPT; |  | ||||||
|         } |  | ||||||
|         return true; |         return true; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -624,7 +629,7 @@ static inline void cpu_loop_exec_tb(CPUState *cpu, TranslationBlock *tb, | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     *last_tb = NULL; |     *last_tb = NULL; | ||||||
|     insns_left = atomic_read(&cpu_neg(cpu)->icount_decr.u32); |     insns_left = atomic_read(&cpu->icount_decr.u32); | ||||||
|     if (insns_left < 0) { |     if (insns_left < 0) { | ||||||
|         /* Something asked us to stop executing chained TBs; just |         /* Something asked us to stop executing chained TBs; just | ||||||
|          * continue round the main loop. Whatever requested the exit |          * continue round the main loop. Whatever requested the exit | ||||||
| @@ -643,7 +648,7 @@ static inline void cpu_loop_exec_tb(CPUState *cpu, TranslationBlock *tb, | |||||||
|     cpu_update_icount(cpu); |     cpu_update_icount(cpu); | ||||||
|     /* Refill decrementer and continue execution.  */ |     /* Refill decrementer and continue execution.  */ | ||||||
|     insns_left = MIN(0xffff, cpu->icount_budget); |     insns_left = MIN(0xffff, cpu->icount_budget); | ||||||
|     cpu_neg(cpu)->icount_decr.u16.low = insns_left; |     cpu->icount_decr.u16.low = insns_left; | ||||||
|     cpu->icount_extra = cpu->icount_budget - insns_left; |     cpu->icount_extra = cpu->icount_budget - insns_left; | ||||||
|     if (!cpu->icount_extra) { |     if (!cpu->icount_extra) { | ||||||
|         /* Execute any remaining instructions, then let the main loop |         /* Execute any remaining instructions, then let the main loop | ||||||
| @@ -696,15 +701,11 @@ int cpu_exec(CPUState *cpu) | |||||||
|         g_assert(cpu == current_cpu); |         g_assert(cpu == current_cpu); | ||||||
|         g_assert(cc == CPU_GET_CLASS(cpu)); |         g_assert(cc == CPU_GET_CLASS(cpu)); | ||||||
| #endif /* buggy compiler */ | #endif /* buggy compiler */ | ||||||
| #ifndef CONFIG_SOFTMMU |         cpu->can_do_io = 1; | ||||||
|         tcg_debug_assert(!have_mmap_lock()); |         tb_lock_reset(); | ||||||
| #endif |  | ||||||
|         if (qemu_mutex_iothread_locked()) { |         if (qemu_mutex_iothread_locked()) { | ||||||
|             qemu_mutex_unlock_iothread(); |             qemu_mutex_unlock_iothread(); | ||||||
|         } |         } | ||||||
|         qemu_plugin_disable_mem_helpers(cpu); |  | ||||||
|  |  | ||||||
|         assert_no_pages_locked(); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* if an exception is pending, we execute it here */ |     /* if an exception is pending, we execute it here */ | ||||||
|   | |||||||
							
								
								
									
										1904
									
								
								accel/tcg/cputlb.c
									
									
									
									
									
								
							
							
						
						
									
										1904
									
								
								accel/tcg/cputlb.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,932 +0,0 @@ | |||||||
| /* |  | ||||||
|  * plugin-gen.c - TCG-related bits of plugin infrastructure |  | ||||||
|  * |  | ||||||
|  * Copyright (C) 2018, Emilio G. Cota <cota@braap.org> |  | ||||||
|  * License: GNU GPL, version 2 or later. |  | ||||||
|  *   See the COPYING file in the top-level directory. |  | ||||||
|  * |  | ||||||
|  * We support instrumentation at an instruction granularity. That is, |  | ||||||
|  * if a plugin wants to instrument the memory accesses performed by a |  | ||||||
|  * particular instruction, it can just do that instead of instrumenting |  | ||||||
|  * all memory accesses. Thus, in order to do this we first have to |  | ||||||
|  * translate a TB, so that plugins can decide what/where to instrument. |  | ||||||
|  * |  | ||||||
|  * Injecting the desired instrumentation could be done with a second |  | ||||||
|  * translation pass that combined the instrumentation requests, but that |  | ||||||
|  * would be ugly and inefficient since we would decode the guest code twice. |  | ||||||
|  * Instead, during TB translation we add "empty" instrumentation calls for all |  | ||||||
|  * possible instrumentation events, and then once we collect the instrumentation |  | ||||||
|  * requests from plugins, we either "fill in" those empty events or remove them |  | ||||||
|  * if they have no requests. |  | ||||||
|  * |  | ||||||
|  * When "filling in" an event we first copy the empty callback's TCG ops. This |  | ||||||
|  * might seem unnecessary, but it is done to support an arbitrary number |  | ||||||
|  * of callbacks per event. Take for example a regular instruction callback. |  | ||||||
|  * We first generate a callback to an empty helper function. Then, if two |  | ||||||
|  * plugins register one callback each for this instruction, we make two copies |  | ||||||
|  * of the TCG ops generated for the empty callback, substituting the function |  | ||||||
|  * pointer that points to the empty helper function with the plugins' desired |  | ||||||
|  * callback functions. After that we remove the empty callback's ops. |  | ||||||
|  * |  | ||||||
|  * Note that the location in TCGOp.args[] of the pointer to a helper function |  | ||||||
|  * varies across different guest and host architectures. Instead of duplicating |  | ||||||
|  * the logic that figures this out, we rely on the fact that the empty |  | ||||||
|  * callbacks point to empty functions that are unique pointers in the program. |  | ||||||
|  * Thus, to find the right location we just have to look for a match in |  | ||||||
|  * TCGOp.args[]. This is the main reason why we first copy an empty callback's |  | ||||||
|  * TCG ops and then fill them in; regardless of whether we have one or many |  | ||||||
|  * callbacks for that event, the logic to add all of them is the same. |  | ||||||
|  * |  | ||||||
|  * When generating more than one callback per event, we make a small |  | ||||||
|  * optimization to avoid generating redundant operations. For instance, for the |  | ||||||
|  * second and all subsequent callbacks of an event, we do not need to reload the |  | ||||||
|  * CPU's index into a TCG temp, since the first callback did it already. |  | ||||||
|  */ |  | ||||||
| #include "qemu/osdep.h" |  | ||||||
| #include "cpu.h" |  | ||||||
| #include "tcg/tcg.h" |  | ||||||
| #include "tcg/tcg-op.h" |  | ||||||
| #include "trace/mem.h" |  | ||||||
| #include "exec/exec-all.h" |  | ||||||
| #include "exec/plugin-gen.h" |  | ||||||
| #include "exec/translator.h" |  | ||||||
|  |  | ||||||
| #ifdef CONFIG_SOFTMMU |  | ||||||
| # define CONFIG_SOFTMMU_GATE 1 |  | ||||||
| #else |  | ||||||
| # define CONFIG_SOFTMMU_GATE 0 |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| /* |  | ||||||
|  * plugin_cb_start TCG op args[]: |  | ||||||
|  * 0: enum plugin_gen_from |  | ||||||
|  * 1: enum plugin_gen_cb |  | ||||||
|  * 2: set to 1 for mem callback that is a write, 0 otherwise. |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| enum plugin_gen_from { |  | ||||||
|     PLUGIN_GEN_FROM_TB, |  | ||||||
|     PLUGIN_GEN_FROM_INSN, |  | ||||||
|     PLUGIN_GEN_FROM_MEM, |  | ||||||
|     PLUGIN_GEN_AFTER_INSN, |  | ||||||
|     PLUGIN_GEN_N_FROMS, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| enum plugin_gen_cb { |  | ||||||
|     PLUGIN_GEN_CB_UDATA, |  | ||||||
|     PLUGIN_GEN_CB_INLINE, |  | ||||||
|     PLUGIN_GEN_CB_MEM, |  | ||||||
|     PLUGIN_GEN_ENABLE_MEM_HELPER, |  | ||||||
|     PLUGIN_GEN_DISABLE_MEM_HELPER, |  | ||||||
|     PLUGIN_GEN_N_CBS, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| /* |  | ||||||
|  * These helpers are stubs that get dynamically switched out for calls |  | ||||||
|  * direct to the plugin if they are subscribed to. |  | ||||||
|  */ |  | ||||||
| void HELPER(plugin_vcpu_udata_cb)(uint32_t cpu_index, void *udata) |  | ||||||
| { } |  | ||||||
|  |  | ||||||
| void HELPER(plugin_vcpu_mem_cb)(unsigned int vcpu_index, |  | ||||||
|                                 qemu_plugin_meminfo_t info, uint64_t vaddr, |  | ||||||
|                                 void *userdata) |  | ||||||
| { } |  | ||||||
|  |  | ||||||
| static void do_gen_mem_cb(TCGv vaddr, uint32_t info) |  | ||||||
| { |  | ||||||
|     TCGv_i32 cpu_index = tcg_temp_new_i32(); |  | ||||||
|     TCGv_i32 meminfo = tcg_const_i32(info); |  | ||||||
|     TCGv_i64 vaddr64 = tcg_temp_new_i64(); |  | ||||||
|     TCGv_ptr udata = tcg_const_ptr(NULL); |  | ||||||
|  |  | ||||||
|     tcg_gen_ld_i32(cpu_index, cpu_env, |  | ||||||
|                    -offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index)); |  | ||||||
|     tcg_gen_extu_tl_i64(vaddr64, vaddr); |  | ||||||
|  |  | ||||||
|     gen_helper_plugin_vcpu_mem_cb(cpu_index, meminfo, vaddr64, udata); |  | ||||||
|  |  | ||||||
|     tcg_temp_free_ptr(udata); |  | ||||||
|     tcg_temp_free_i64(vaddr64); |  | ||||||
|     tcg_temp_free_i32(meminfo); |  | ||||||
|     tcg_temp_free_i32(cpu_index); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void gen_empty_udata_cb(void) |  | ||||||
| { |  | ||||||
|     TCGv_i32 cpu_index = tcg_temp_new_i32(); |  | ||||||
|     TCGv_ptr udata = tcg_const_ptr(NULL); /* will be overwritten later */ |  | ||||||
|  |  | ||||||
|     tcg_gen_ld_i32(cpu_index, cpu_env, |  | ||||||
|                    -offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index)); |  | ||||||
|     gen_helper_plugin_vcpu_udata_cb(cpu_index, udata); |  | ||||||
|  |  | ||||||
|     tcg_temp_free_ptr(udata); |  | ||||||
|     tcg_temp_free_i32(cpu_index); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* |  | ||||||
|  * For now we only support addi_i64. |  | ||||||
|  * When we support more ops, we can generate one empty inline cb for each. |  | ||||||
|  */ |  | ||||||
| static void gen_empty_inline_cb(void) |  | ||||||
| { |  | ||||||
|     TCGv_i64 val = tcg_temp_new_i64(); |  | ||||||
|     TCGv_ptr ptr = tcg_const_ptr(NULL); /* overwritten later */ |  | ||||||
|  |  | ||||||
|     tcg_gen_ld_i64(val, ptr, 0); |  | ||||||
|     /* pass an immediate != 0 so that it doesn't get optimized away */ |  | ||||||
|     tcg_gen_addi_i64(val, val, 0xdeadface); |  | ||||||
|     tcg_gen_st_i64(val, ptr, 0); |  | ||||||
|     tcg_temp_free_ptr(ptr); |  | ||||||
|     tcg_temp_free_i64(val); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void gen_empty_mem_cb(TCGv addr, uint32_t info) |  | ||||||
| { |  | ||||||
|     do_gen_mem_cb(addr, info); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* |  | ||||||
|  * Share the same function for enable/disable. When enabling, the NULL |  | ||||||
|  * pointer will be overwritten later. |  | ||||||
|  */ |  | ||||||
| static void gen_empty_mem_helper(void) |  | ||||||
| { |  | ||||||
|     TCGv_ptr ptr; |  | ||||||
|  |  | ||||||
|     ptr = tcg_const_ptr(NULL); |  | ||||||
|     tcg_gen_st_ptr(ptr, cpu_env, offsetof(CPUState, plugin_mem_cbs) - |  | ||||||
|                                  offsetof(ArchCPU, env)); |  | ||||||
|     tcg_temp_free_ptr(ptr); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static inline |  | ||||||
| void gen_plugin_cb_start(enum plugin_gen_from from, |  | ||||||
|                          enum plugin_gen_cb type, unsigned wr) |  | ||||||
| { |  | ||||||
|     TCGOp *op; |  | ||||||
|  |  | ||||||
|     tcg_gen_plugin_cb_start(from, type, wr); |  | ||||||
|     op = tcg_last_op(); |  | ||||||
|     QSIMPLEQ_INSERT_TAIL(&tcg_ctx->plugin_ops, op, plugin_link); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void gen_wrapped(enum plugin_gen_from from, |  | ||||||
|                         enum plugin_gen_cb type, void (*func)(void)) |  | ||||||
| { |  | ||||||
|     gen_plugin_cb_start(from, type, 0); |  | ||||||
|     func(); |  | ||||||
|     tcg_gen_plugin_cb_end(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static inline void plugin_gen_empty_callback(enum plugin_gen_from from) |  | ||||||
| { |  | ||||||
|     switch (from) { |  | ||||||
|     case PLUGIN_GEN_AFTER_INSN: |  | ||||||
|         gen_wrapped(from, PLUGIN_GEN_DISABLE_MEM_HELPER, |  | ||||||
|                     gen_empty_mem_helper); |  | ||||||
|         break; |  | ||||||
|     case PLUGIN_GEN_FROM_INSN: |  | ||||||
|         /* |  | ||||||
|          * Note: plugin_gen_inject() relies on ENABLE_MEM_HELPER being |  | ||||||
|          * the first callback of an instruction |  | ||||||
|          */ |  | ||||||
|         gen_wrapped(from, PLUGIN_GEN_ENABLE_MEM_HELPER, |  | ||||||
|                     gen_empty_mem_helper); |  | ||||||
|         /* fall through */ |  | ||||||
|     case PLUGIN_GEN_FROM_TB: |  | ||||||
|         gen_wrapped(from, PLUGIN_GEN_CB_UDATA, gen_empty_udata_cb); |  | ||||||
|         gen_wrapped(from, PLUGIN_GEN_CB_INLINE, gen_empty_inline_cb); |  | ||||||
|         break; |  | ||||||
|     default: |  | ||||||
|         g_assert_not_reached(); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| union mem_gen_fn { |  | ||||||
|     void (*mem_fn)(TCGv, uint32_t); |  | ||||||
|     void (*inline_fn)(void); |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static void gen_mem_wrapped(enum plugin_gen_cb type, |  | ||||||
|                             const union mem_gen_fn *f, TCGv addr, |  | ||||||
|                             uint32_t info, bool is_mem) |  | ||||||
| { |  | ||||||
|     int wr = !!(info & TRACE_MEM_ST); |  | ||||||
|  |  | ||||||
|     gen_plugin_cb_start(PLUGIN_GEN_FROM_MEM, type, wr); |  | ||||||
|     if (is_mem) { |  | ||||||
|         f->mem_fn(addr, info); |  | ||||||
|     } else { |  | ||||||
|         f->inline_fn(); |  | ||||||
|     } |  | ||||||
|     tcg_gen_plugin_cb_end(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void plugin_gen_empty_mem_callback(TCGv addr, uint32_t info) |  | ||||||
| { |  | ||||||
|     union mem_gen_fn fn; |  | ||||||
|  |  | ||||||
|     fn.mem_fn = gen_empty_mem_cb; |  | ||||||
|     gen_mem_wrapped(PLUGIN_GEN_CB_MEM, &fn, addr, info, true); |  | ||||||
|  |  | ||||||
|     fn.inline_fn = gen_empty_inline_cb; |  | ||||||
|     gen_mem_wrapped(PLUGIN_GEN_CB_INLINE, &fn, 0, info, false); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static TCGOp *find_op(TCGOp *op, TCGOpcode opc) |  | ||||||
| { |  | ||||||
|     while (op) { |  | ||||||
|         if (op->opc == opc) { |  | ||||||
|             return op; |  | ||||||
|         } |  | ||||||
|         op = QTAILQ_NEXT(op, link); |  | ||||||
|     } |  | ||||||
|     return NULL; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static TCGOp *rm_ops_range(TCGOp *begin, TCGOp *end) |  | ||||||
| { |  | ||||||
|     TCGOp *ret = QTAILQ_NEXT(end, link); |  | ||||||
|  |  | ||||||
|     QTAILQ_REMOVE_SEVERAL(&tcg_ctx->ops, begin, end, link); |  | ||||||
|     return ret; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* remove all ops until (and including) plugin_cb_end */ |  | ||||||
| static TCGOp *rm_ops(TCGOp *op) |  | ||||||
| { |  | ||||||
|     TCGOp *end_op = find_op(op, INDEX_op_plugin_cb_end); |  | ||||||
|  |  | ||||||
|     tcg_debug_assert(end_op); |  | ||||||
|     return rm_ops_range(op, end_op); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static TCGOp *copy_op_nocheck(TCGOp **begin_op, TCGOp *op) |  | ||||||
| { |  | ||||||
|     *begin_op = QTAILQ_NEXT(*begin_op, link); |  | ||||||
|     tcg_debug_assert(*begin_op); |  | ||||||
|     op = tcg_op_insert_after(tcg_ctx, op, (*begin_op)->opc); |  | ||||||
|     memcpy(op->args, (*begin_op)->args, sizeof(op->args)); |  | ||||||
|     return op; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static TCGOp *copy_op(TCGOp **begin_op, TCGOp *op, TCGOpcode opc) |  | ||||||
| { |  | ||||||
|     op = copy_op_nocheck(begin_op, op); |  | ||||||
|     tcg_debug_assert((*begin_op)->opc == opc); |  | ||||||
|     return op; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static TCGOp *copy_extu_i32_i64(TCGOp **begin_op, TCGOp *op) |  | ||||||
| { |  | ||||||
|     if (TCG_TARGET_REG_BITS == 32) { |  | ||||||
|         /* mov_i32 */ |  | ||||||
|         op = copy_op(begin_op, op, INDEX_op_mov_i32); |  | ||||||
|         /* movi_i32 */ |  | ||||||
|         op = copy_op(begin_op, op, INDEX_op_movi_i32); |  | ||||||
|     } else { |  | ||||||
|         /* extu_i32_i64 */ |  | ||||||
|         op = copy_op(begin_op, op, INDEX_op_extu_i32_i64); |  | ||||||
|     } |  | ||||||
|     return op; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static TCGOp *copy_mov_i64(TCGOp **begin_op, TCGOp *op) |  | ||||||
| { |  | ||||||
|     if (TCG_TARGET_REG_BITS == 32) { |  | ||||||
|         /* 2x mov_i32 */ |  | ||||||
|         op = copy_op(begin_op, op, INDEX_op_mov_i32); |  | ||||||
|         op = copy_op(begin_op, op, INDEX_op_mov_i32); |  | ||||||
|     } else { |  | ||||||
|         /* mov_i64 */ |  | ||||||
|         op = copy_op(begin_op, op, INDEX_op_mov_i64); |  | ||||||
|     } |  | ||||||
|     return op; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static TCGOp *copy_movi_i64(TCGOp **begin_op, TCGOp *op, uint64_t v) |  | ||||||
| { |  | ||||||
|     if (TCG_TARGET_REG_BITS == 32) { |  | ||||||
|         /* 2x movi_i32 */ |  | ||||||
|         op = copy_op(begin_op, op, INDEX_op_movi_i32); |  | ||||||
|         op->args[1] = v; |  | ||||||
|  |  | ||||||
|         op = copy_op(begin_op, op, INDEX_op_movi_i32); |  | ||||||
|         op->args[1] = v >> 32; |  | ||||||
|     } else { |  | ||||||
|         /* movi_i64 */ |  | ||||||
|         op = copy_op(begin_op, op, INDEX_op_movi_i64); |  | ||||||
|         op->args[1] = v; |  | ||||||
|     } |  | ||||||
|     return op; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static TCGOp *copy_const_ptr(TCGOp **begin_op, TCGOp *op, void *ptr) |  | ||||||
| { |  | ||||||
|     if (UINTPTR_MAX == UINT32_MAX) { |  | ||||||
|         /* movi_i32 */ |  | ||||||
|         op = copy_op(begin_op, op, INDEX_op_movi_i32); |  | ||||||
|         op->args[1] = (uintptr_t)ptr; |  | ||||||
|     } else { |  | ||||||
|         /* movi_i64 */ |  | ||||||
|         op = copy_movi_i64(begin_op, op, (uint64_t)(uintptr_t)ptr); |  | ||||||
|     } |  | ||||||
|     return op; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static TCGOp *copy_const_i64(TCGOp **begin_op, TCGOp *op, uint64_t v) |  | ||||||
| { |  | ||||||
|     return copy_movi_i64(begin_op, op, v); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static TCGOp *copy_extu_tl_i64(TCGOp **begin_op, TCGOp *op) |  | ||||||
| { |  | ||||||
|     if (TARGET_LONG_BITS == 32) { |  | ||||||
|         /* extu_i32_i64 */ |  | ||||||
|         op = copy_extu_i32_i64(begin_op, op); |  | ||||||
|     } else { |  | ||||||
|         /* mov_i64 */ |  | ||||||
|         op = copy_mov_i64(begin_op, op); |  | ||||||
|     } |  | ||||||
|     return op; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static TCGOp *copy_ld_i64(TCGOp **begin_op, TCGOp *op) |  | ||||||
| { |  | ||||||
|     if (TCG_TARGET_REG_BITS == 32) { |  | ||||||
|         /* 2x ld_i32 */ |  | ||||||
|         op = copy_op(begin_op, op, INDEX_op_ld_i32); |  | ||||||
|         op = copy_op(begin_op, op, INDEX_op_ld_i32); |  | ||||||
|     } else { |  | ||||||
|         /* ld_i64 */ |  | ||||||
|         op = copy_op(begin_op, op, INDEX_op_ld_i64); |  | ||||||
|     } |  | ||||||
|     return op; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static TCGOp *copy_st_i64(TCGOp **begin_op, TCGOp *op) |  | ||||||
| { |  | ||||||
|     if (TCG_TARGET_REG_BITS == 32) { |  | ||||||
|         /* 2x st_i32 */ |  | ||||||
|         op = copy_op(begin_op, op, INDEX_op_st_i32); |  | ||||||
|         op = copy_op(begin_op, op, INDEX_op_st_i32); |  | ||||||
|     } else { |  | ||||||
|         /* st_i64 */ |  | ||||||
|         op = copy_op(begin_op, op, INDEX_op_st_i64); |  | ||||||
|     } |  | ||||||
|     return op; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static TCGOp *copy_add_i64(TCGOp **begin_op, TCGOp *op) |  | ||||||
| { |  | ||||||
|     if (TCG_TARGET_REG_BITS == 32) { |  | ||||||
|         /* all 32-bit backends must implement add2_i32 */ |  | ||||||
|         g_assert(TCG_TARGET_HAS_add2_i32); |  | ||||||
|         op = copy_op(begin_op, op, INDEX_op_add2_i32); |  | ||||||
|     } else { |  | ||||||
|         op = copy_op(begin_op, op, INDEX_op_add_i64); |  | ||||||
|     } |  | ||||||
|     return op; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static TCGOp *copy_st_ptr(TCGOp **begin_op, TCGOp *op) |  | ||||||
| { |  | ||||||
|     if (UINTPTR_MAX == UINT32_MAX) { |  | ||||||
|         /* st_i32 */ |  | ||||||
|         op = copy_op(begin_op, op, INDEX_op_st_i32); |  | ||||||
|     } else { |  | ||||||
|         /* st_i64 */ |  | ||||||
|         op = copy_st_i64(begin_op, op); |  | ||||||
|     } |  | ||||||
|     return op; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static TCGOp *copy_call(TCGOp **begin_op, TCGOp *op, void *empty_func, |  | ||||||
|                         void *func, unsigned tcg_flags, int *cb_idx) |  | ||||||
| { |  | ||||||
|     /* copy all ops until the call */ |  | ||||||
|     do { |  | ||||||
|         op = copy_op_nocheck(begin_op, op); |  | ||||||
|     } while (op->opc != INDEX_op_call); |  | ||||||
|  |  | ||||||
|     /* fill in the op call */ |  | ||||||
|     op->param1 = (*begin_op)->param1; |  | ||||||
|     op->param2 = (*begin_op)->param2; |  | ||||||
|     tcg_debug_assert(op->life == 0); |  | ||||||
|     if (*cb_idx == -1) { |  | ||||||
|         int i; |  | ||||||
|  |  | ||||||
|         /* |  | ||||||
|          * Instead of working out the position of the callback in args[], just |  | ||||||
|          * look for @empty_func, since it should be a unique pointer. |  | ||||||
|          */ |  | ||||||
|         for (i = 0; i < MAX_OPC_PARAM_ARGS; i++) { |  | ||||||
|             if ((uintptr_t)(*begin_op)->args[i] == (uintptr_t)empty_func) { |  | ||||||
|                 *cb_idx = i; |  | ||||||
|                 break; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         tcg_debug_assert(i < MAX_OPC_PARAM_ARGS); |  | ||||||
|     } |  | ||||||
|     op->args[*cb_idx] = (uintptr_t)func; |  | ||||||
|     op->args[*cb_idx + 1] = tcg_flags; |  | ||||||
|  |  | ||||||
|     return op; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static TCGOp *append_udata_cb(const struct qemu_plugin_dyn_cb *cb, |  | ||||||
|                               TCGOp *begin_op, TCGOp *op, int *cb_idx) |  | ||||||
| { |  | ||||||
|     /* const_ptr */ |  | ||||||
|     op = copy_const_ptr(&begin_op, op, cb->userp); |  | ||||||
|  |  | ||||||
|     /* copy the ld_i32, but note that we only have to copy it once */ |  | ||||||
|     begin_op = QTAILQ_NEXT(begin_op, link); |  | ||||||
|     tcg_debug_assert(begin_op && begin_op->opc == INDEX_op_ld_i32); |  | ||||||
|     if (*cb_idx == -1) { |  | ||||||
|         op = tcg_op_insert_after(tcg_ctx, op, INDEX_op_ld_i32); |  | ||||||
|         memcpy(op->args, begin_op->args, sizeof(op->args)); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /* call */ |  | ||||||
|     op = copy_call(&begin_op, op, HELPER(plugin_vcpu_udata_cb), |  | ||||||
|                    cb->f.vcpu_udata, cb->tcg_flags, cb_idx); |  | ||||||
|  |  | ||||||
|     return op; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static TCGOp *append_inline_cb(const struct qemu_plugin_dyn_cb *cb, |  | ||||||
|                                TCGOp *begin_op, TCGOp *op, |  | ||||||
|                                int *unused) |  | ||||||
| { |  | ||||||
|     /* const_ptr */ |  | ||||||
|     op = copy_const_ptr(&begin_op, op, cb->userp); |  | ||||||
|  |  | ||||||
|     /* ld_i64 */ |  | ||||||
|     op = copy_ld_i64(&begin_op, op); |  | ||||||
|  |  | ||||||
|     /* const_i64 */ |  | ||||||
|     op = copy_const_i64(&begin_op, op, cb->inline_insn.imm); |  | ||||||
|  |  | ||||||
|     /* add_i64 */ |  | ||||||
|     op = copy_add_i64(&begin_op, op); |  | ||||||
|  |  | ||||||
|     /* st_i64 */ |  | ||||||
|     op = copy_st_i64(&begin_op, op); |  | ||||||
|  |  | ||||||
|     return op; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static TCGOp *append_mem_cb(const struct qemu_plugin_dyn_cb *cb, |  | ||||||
|                             TCGOp *begin_op, TCGOp *op, int *cb_idx) |  | ||||||
| { |  | ||||||
|     enum plugin_gen_cb type = begin_op->args[1]; |  | ||||||
|  |  | ||||||
|     tcg_debug_assert(type == PLUGIN_GEN_CB_MEM); |  | ||||||
|  |  | ||||||
|     /* const_i32 == movi_i32 ("info", so it remains as is) */ |  | ||||||
|     op = copy_op(&begin_op, op, INDEX_op_movi_i32); |  | ||||||
|  |  | ||||||
|     /* const_ptr */ |  | ||||||
|     op = copy_const_ptr(&begin_op, op, cb->userp); |  | ||||||
|  |  | ||||||
|     /* copy the ld_i32, but note that we only have to copy it once */ |  | ||||||
|     begin_op = QTAILQ_NEXT(begin_op, link); |  | ||||||
|     tcg_debug_assert(begin_op && begin_op->opc == INDEX_op_ld_i32); |  | ||||||
|     if (*cb_idx == -1) { |  | ||||||
|         op = tcg_op_insert_after(tcg_ctx, op, INDEX_op_ld_i32); |  | ||||||
|         memcpy(op->args, begin_op->args, sizeof(op->args)); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /* extu_tl_i64 */ |  | ||||||
|     op = copy_extu_tl_i64(&begin_op, op); |  | ||||||
|  |  | ||||||
|     if (type == PLUGIN_GEN_CB_MEM) { |  | ||||||
|         /* call */ |  | ||||||
|         op = copy_call(&begin_op, op, HELPER(plugin_vcpu_mem_cb), |  | ||||||
|                        cb->f.vcpu_udata, cb->tcg_flags, cb_idx); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return op; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| typedef TCGOp *(*inject_fn)(const struct qemu_plugin_dyn_cb *cb, |  | ||||||
|                             TCGOp *begin_op, TCGOp *op, int *intp); |  | ||||||
| typedef bool (*op_ok_fn)(const TCGOp *op, const struct qemu_plugin_dyn_cb *cb); |  | ||||||
|  |  | ||||||
| static bool op_ok(const TCGOp *op, const struct qemu_plugin_dyn_cb *cb) |  | ||||||
| { |  | ||||||
|     return true; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static bool op_rw(const TCGOp *op, const struct qemu_plugin_dyn_cb *cb) |  | ||||||
| { |  | ||||||
|     int w; |  | ||||||
|  |  | ||||||
|     w = op->args[2]; |  | ||||||
|     return !!(cb->rw & (w + 1)); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static inline |  | ||||||
| void inject_cb_type(const GArray *cbs, TCGOp *begin_op, inject_fn inject, |  | ||||||
|                     op_ok_fn ok) |  | ||||||
| { |  | ||||||
|     TCGOp *end_op; |  | ||||||
|     TCGOp *op; |  | ||||||
|     int cb_idx = -1; |  | ||||||
|     int i; |  | ||||||
|  |  | ||||||
|     if (!cbs || cbs->len == 0) { |  | ||||||
|         rm_ops(begin_op); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     end_op = find_op(begin_op, INDEX_op_plugin_cb_end); |  | ||||||
|     tcg_debug_assert(end_op); |  | ||||||
|  |  | ||||||
|     op = end_op; |  | ||||||
|     for (i = 0; i < cbs->len; i++) { |  | ||||||
|         struct qemu_plugin_dyn_cb *cb = |  | ||||||
|             &g_array_index(cbs, struct qemu_plugin_dyn_cb, i); |  | ||||||
|  |  | ||||||
|         if (!ok(begin_op, cb)) { |  | ||||||
|             continue; |  | ||||||
|         } |  | ||||||
|         op = inject(cb, begin_op, op, &cb_idx); |  | ||||||
|     } |  | ||||||
|     rm_ops_range(begin_op, end_op); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void |  | ||||||
| inject_udata_cb(const GArray *cbs, TCGOp *begin_op) |  | ||||||
| { |  | ||||||
|     inject_cb_type(cbs, begin_op, append_udata_cb, op_ok); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void |  | ||||||
| inject_inline_cb(const GArray *cbs, TCGOp *begin_op, op_ok_fn ok) |  | ||||||
| { |  | ||||||
|     inject_cb_type(cbs, begin_op, append_inline_cb, ok); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void |  | ||||||
| inject_mem_cb(const GArray *cbs, TCGOp *begin_op) |  | ||||||
| { |  | ||||||
|     inject_cb_type(cbs, begin_op, append_mem_cb, op_rw); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* we could change the ops in place, but we can reuse more code by copying */ |  | ||||||
| static void inject_mem_helper(TCGOp *begin_op, GArray *arr) |  | ||||||
| { |  | ||||||
|     TCGOp *orig_op = begin_op; |  | ||||||
|     TCGOp *end_op; |  | ||||||
|     TCGOp *op; |  | ||||||
|  |  | ||||||
|     end_op = find_op(begin_op, INDEX_op_plugin_cb_end); |  | ||||||
|     tcg_debug_assert(end_op); |  | ||||||
|  |  | ||||||
|     /* const ptr */ |  | ||||||
|     op = copy_const_ptr(&begin_op, end_op, arr); |  | ||||||
|  |  | ||||||
|     /* st_ptr */ |  | ||||||
|     op = copy_st_ptr(&begin_op, op); |  | ||||||
|  |  | ||||||
|     rm_ops_range(orig_op, end_op); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* |  | ||||||
|  * Tracking memory accesses performed from helpers requires extra work. |  | ||||||
|  * If an instruction is emulated with helpers, we do two things: |  | ||||||
|  * (1) copy the CB descriptors, and keep track of it so that they can be |  | ||||||
|  * freed later on, and (2) point CPUState.plugin_mem_cbs to the descriptors, so |  | ||||||
|  * that we can read them at run-time (i.e. when the helper executes). |  | ||||||
|  * This run-time access is performed from qemu_plugin_vcpu_mem_cb. |  | ||||||
|  * |  | ||||||
|  * Note that plugin_gen_disable_mem_helpers undoes (2). Since it |  | ||||||
|  * is possible that the code we generate after the instruction is |  | ||||||
|  * dead, we also add checks before generating tb_exit etc. |  | ||||||
|  */ |  | ||||||
| static void inject_mem_enable_helper(struct qemu_plugin_insn *plugin_insn, |  | ||||||
|                                      TCGOp *begin_op) |  | ||||||
| { |  | ||||||
|     GArray *cbs[2]; |  | ||||||
|     GArray *arr; |  | ||||||
|     size_t n_cbs, i; |  | ||||||
|  |  | ||||||
|     cbs[0] = plugin_insn->cbs[PLUGIN_CB_MEM][PLUGIN_CB_REGULAR]; |  | ||||||
|     cbs[1] = plugin_insn->cbs[PLUGIN_CB_MEM][PLUGIN_CB_INLINE]; |  | ||||||
|  |  | ||||||
|     n_cbs = 0; |  | ||||||
|     for (i = 0; i < ARRAY_SIZE(cbs); i++) { |  | ||||||
|         n_cbs += cbs[i]->len; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     plugin_insn->mem_helper = plugin_insn->calls_helpers && n_cbs; |  | ||||||
|     if (likely(!plugin_insn->mem_helper)) { |  | ||||||
|         rm_ops(begin_op); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     arr = g_array_sized_new(false, false, |  | ||||||
|                             sizeof(struct qemu_plugin_dyn_cb), n_cbs); |  | ||||||
|  |  | ||||||
|     for (i = 0; i < ARRAY_SIZE(cbs); i++) { |  | ||||||
|         g_array_append_vals(arr, cbs[i]->data, cbs[i]->len); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     qemu_plugin_add_dyn_cb_arr(arr); |  | ||||||
|     inject_mem_helper(begin_op, arr); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void inject_mem_disable_helper(struct qemu_plugin_insn *plugin_insn, |  | ||||||
|                                       TCGOp *begin_op) |  | ||||||
| { |  | ||||||
|     if (likely(!plugin_insn->mem_helper)) { |  | ||||||
|         rm_ops(begin_op); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|     inject_mem_helper(begin_op, NULL); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* called before finishing a TB with exit_tb, goto_tb or goto_ptr */ |  | ||||||
| void plugin_gen_disable_mem_helpers(void) |  | ||||||
| { |  | ||||||
|     TCGv_ptr ptr; |  | ||||||
|  |  | ||||||
|     if (likely(tcg_ctx->plugin_insn == NULL || |  | ||||||
|                !tcg_ctx->plugin_insn->mem_helper)) { |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|     ptr = tcg_const_ptr(NULL); |  | ||||||
|     tcg_gen_st_ptr(ptr, cpu_env, offsetof(CPUState, plugin_mem_cbs) - |  | ||||||
|                                  offsetof(ArchCPU, env)); |  | ||||||
|     tcg_temp_free_ptr(ptr); |  | ||||||
|     tcg_ctx->plugin_insn->mem_helper = false; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void plugin_gen_tb_udata(const struct qemu_plugin_tb *ptb, |  | ||||||
|                                 TCGOp *begin_op) |  | ||||||
| { |  | ||||||
|     inject_udata_cb(ptb->cbs[PLUGIN_CB_REGULAR], begin_op); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void plugin_gen_tb_inline(const struct qemu_plugin_tb *ptb, |  | ||||||
|                                  TCGOp *begin_op) |  | ||||||
| { |  | ||||||
|     inject_inline_cb(ptb->cbs[PLUGIN_CB_INLINE], begin_op, op_ok); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void plugin_gen_insn_udata(const struct qemu_plugin_tb *ptb, |  | ||||||
|                                   TCGOp *begin_op, int insn_idx) |  | ||||||
| { |  | ||||||
|     struct qemu_plugin_insn *insn = g_ptr_array_index(ptb->insns, insn_idx); |  | ||||||
|  |  | ||||||
|     inject_udata_cb(insn->cbs[PLUGIN_CB_INSN][PLUGIN_CB_REGULAR], begin_op); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void plugin_gen_insn_inline(const struct qemu_plugin_tb *ptb, |  | ||||||
|                                    TCGOp *begin_op, int insn_idx) |  | ||||||
| { |  | ||||||
|     struct qemu_plugin_insn *insn = g_ptr_array_index(ptb->insns, insn_idx); |  | ||||||
|     inject_inline_cb(insn->cbs[PLUGIN_CB_INSN][PLUGIN_CB_INLINE], |  | ||||||
|                      begin_op, op_ok); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void plugin_gen_mem_regular(const struct qemu_plugin_tb *ptb, |  | ||||||
|                                    TCGOp *begin_op, int insn_idx) |  | ||||||
| { |  | ||||||
|     struct qemu_plugin_insn *insn = g_ptr_array_index(ptb->insns, insn_idx); |  | ||||||
|     inject_mem_cb(insn->cbs[PLUGIN_CB_MEM][PLUGIN_CB_REGULAR], begin_op); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void plugin_gen_mem_inline(const struct qemu_plugin_tb *ptb, |  | ||||||
|                                   TCGOp *begin_op, int insn_idx) |  | ||||||
| { |  | ||||||
|     const GArray *cbs; |  | ||||||
|     struct qemu_plugin_insn *insn = g_ptr_array_index(ptb->insns, insn_idx); |  | ||||||
|  |  | ||||||
|     cbs = insn->cbs[PLUGIN_CB_MEM][PLUGIN_CB_INLINE]; |  | ||||||
|     inject_inline_cb(cbs, begin_op, op_rw); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void plugin_gen_enable_mem_helper(const struct qemu_plugin_tb *ptb, |  | ||||||
|                                          TCGOp *begin_op, int insn_idx) |  | ||||||
| { |  | ||||||
|     struct qemu_plugin_insn *insn = g_ptr_array_index(ptb->insns, insn_idx); |  | ||||||
|     inject_mem_enable_helper(insn, begin_op); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void plugin_gen_disable_mem_helper(const struct qemu_plugin_tb *ptb, |  | ||||||
|                                           TCGOp *begin_op, int insn_idx) |  | ||||||
| { |  | ||||||
|     struct qemu_plugin_insn *insn = g_ptr_array_index(ptb->insns, insn_idx); |  | ||||||
|     inject_mem_disable_helper(insn, begin_op); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void plugin_inject_cb(const struct qemu_plugin_tb *ptb, TCGOp *begin_op, |  | ||||||
|                              int insn_idx) |  | ||||||
| { |  | ||||||
|     enum plugin_gen_from from = begin_op->args[0]; |  | ||||||
|     enum plugin_gen_cb type = begin_op->args[1]; |  | ||||||
|  |  | ||||||
|     switch (from) { |  | ||||||
|     case PLUGIN_GEN_FROM_TB: |  | ||||||
|         switch (type) { |  | ||||||
|         case PLUGIN_GEN_CB_UDATA: |  | ||||||
|             plugin_gen_tb_udata(ptb, begin_op); |  | ||||||
|             return; |  | ||||||
|         case PLUGIN_GEN_CB_INLINE: |  | ||||||
|             plugin_gen_tb_inline(ptb, begin_op); |  | ||||||
|             return; |  | ||||||
|         default: |  | ||||||
|             g_assert_not_reached(); |  | ||||||
|         } |  | ||||||
|     case PLUGIN_GEN_FROM_INSN: |  | ||||||
|         switch (type) { |  | ||||||
|         case PLUGIN_GEN_CB_UDATA: |  | ||||||
|             plugin_gen_insn_udata(ptb, begin_op, insn_idx); |  | ||||||
|             return; |  | ||||||
|         case PLUGIN_GEN_CB_INLINE: |  | ||||||
|             plugin_gen_insn_inline(ptb, begin_op, insn_idx); |  | ||||||
|             return; |  | ||||||
|         case PLUGIN_GEN_ENABLE_MEM_HELPER: |  | ||||||
|             plugin_gen_enable_mem_helper(ptb, begin_op, insn_idx); |  | ||||||
|             return; |  | ||||||
|         default: |  | ||||||
|             g_assert_not_reached(); |  | ||||||
|         } |  | ||||||
|     case PLUGIN_GEN_FROM_MEM: |  | ||||||
|         switch (type) { |  | ||||||
|         case PLUGIN_GEN_CB_MEM: |  | ||||||
|             plugin_gen_mem_regular(ptb, begin_op, insn_idx); |  | ||||||
|             return; |  | ||||||
|         case PLUGIN_GEN_CB_INLINE: |  | ||||||
|             plugin_gen_mem_inline(ptb, begin_op, insn_idx); |  | ||||||
|             return; |  | ||||||
|         default: |  | ||||||
|             g_assert_not_reached(); |  | ||||||
|         } |  | ||||||
|     case PLUGIN_GEN_AFTER_INSN: |  | ||||||
|         switch (type) { |  | ||||||
|         case PLUGIN_GEN_DISABLE_MEM_HELPER: |  | ||||||
|             plugin_gen_disable_mem_helper(ptb, begin_op, insn_idx); |  | ||||||
|             return; |  | ||||||
|         default: |  | ||||||
|             g_assert_not_reached(); |  | ||||||
|         } |  | ||||||
|     default: |  | ||||||
|         g_assert_not_reached(); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* #define DEBUG_PLUGIN_GEN_OPS */ |  | ||||||
| static void pr_ops(void) |  | ||||||
| { |  | ||||||
| #ifdef DEBUG_PLUGIN_GEN_OPS |  | ||||||
|     TCGOp *op; |  | ||||||
|     int i = 0; |  | ||||||
|  |  | ||||||
|     QTAILQ_FOREACH(op, &tcg_ctx->ops, link) { |  | ||||||
|         const char *name = ""; |  | ||||||
|         const char *type = ""; |  | ||||||
|  |  | ||||||
|         if (op->opc == INDEX_op_plugin_cb_start) { |  | ||||||
|             switch (op->args[0]) { |  | ||||||
|             case PLUGIN_GEN_FROM_TB: |  | ||||||
|                 name = "tb"; |  | ||||||
|                 break; |  | ||||||
|             case PLUGIN_GEN_FROM_INSN: |  | ||||||
|                 name = "insn"; |  | ||||||
|                 break; |  | ||||||
|             case PLUGIN_GEN_FROM_MEM: |  | ||||||
|                 name = "mem"; |  | ||||||
|                 break; |  | ||||||
|             case PLUGIN_GEN_AFTER_INSN: |  | ||||||
|                 name = "after insn"; |  | ||||||
|                 break; |  | ||||||
|             default: |  | ||||||
|                 break; |  | ||||||
|             } |  | ||||||
|             switch (op->args[1]) { |  | ||||||
|             case PLUGIN_GEN_CB_UDATA: |  | ||||||
|                 type = "udata"; |  | ||||||
|                 break; |  | ||||||
|             case PLUGIN_GEN_CB_INLINE: |  | ||||||
|                 type = "inline"; |  | ||||||
|                 break; |  | ||||||
|             case PLUGIN_GEN_CB_MEM: |  | ||||||
|                 type = "mem"; |  | ||||||
|                 break; |  | ||||||
|             case PLUGIN_GEN_ENABLE_MEM_HELPER: |  | ||||||
|                 type = "enable mem helper"; |  | ||||||
|                 break; |  | ||||||
|             case PLUGIN_GEN_DISABLE_MEM_HELPER: |  | ||||||
|                 type = "disable mem helper"; |  | ||||||
|                 break; |  | ||||||
|             default: |  | ||||||
|                 break; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         printf("op[%2i]: %s %s %s\n", i, tcg_op_defs[op->opc].name, name, type); |  | ||||||
|         i++; |  | ||||||
|     } |  | ||||||
| #endif |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void plugin_gen_inject(const struct qemu_plugin_tb *plugin_tb) |  | ||||||
| { |  | ||||||
|     TCGOp *op; |  | ||||||
|     int insn_idx; |  | ||||||
|  |  | ||||||
|     pr_ops(); |  | ||||||
|     insn_idx = -1; |  | ||||||
|     QSIMPLEQ_FOREACH(op, &tcg_ctx->plugin_ops, plugin_link) { |  | ||||||
|         enum plugin_gen_from from = op->args[0]; |  | ||||||
|         enum plugin_gen_cb type = op->args[1]; |  | ||||||
|  |  | ||||||
|         tcg_debug_assert(op->opc == INDEX_op_plugin_cb_start); |  | ||||||
|         /* ENABLE_MEM_HELPER is the first callback of an instruction */ |  | ||||||
|         if (from == PLUGIN_GEN_FROM_INSN && |  | ||||||
|             type == PLUGIN_GEN_ENABLE_MEM_HELPER) { |  | ||||||
|             insn_idx++; |  | ||||||
|         } |  | ||||||
|         plugin_inject_cb(plugin_tb, op, insn_idx); |  | ||||||
|     } |  | ||||||
|     pr_ops(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| bool plugin_gen_tb_start(CPUState *cpu, const TranslationBlock *tb) |  | ||||||
| { |  | ||||||
|     struct qemu_plugin_tb *ptb = tcg_ctx->plugin_tb; |  | ||||||
|     bool ret = false; |  | ||||||
|  |  | ||||||
|     if (test_bit(QEMU_PLUGIN_EV_VCPU_TB_TRANS, cpu->plugin_mask)) { |  | ||||||
|         ret = true; |  | ||||||
|  |  | ||||||
|         QSIMPLEQ_INIT(&tcg_ctx->plugin_ops); |  | ||||||
|         ptb->vaddr = tb->pc; |  | ||||||
|         ptb->vaddr2 = -1; |  | ||||||
|         get_page_addr_code_hostp(cpu->env_ptr, tb->pc, &ptb->haddr1); |  | ||||||
|         ptb->haddr2 = NULL; |  | ||||||
|  |  | ||||||
|         plugin_gen_empty_callback(PLUGIN_GEN_FROM_TB); |  | ||||||
|     } |  | ||||||
|     return ret; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void plugin_gen_insn_start(CPUState *cpu, const DisasContextBase *db) |  | ||||||
| { |  | ||||||
|     struct qemu_plugin_tb *ptb = tcg_ctx->plugin_tb; |  | ||||||
|     struct qemu_plugin_insn *pinsn; |  | ||||||
|  |  | ||||||
|     pinsn = qemu_plugin_tb_insn_get(ptb); |  | ||||||
|     tcg_ctx->plugin_insn = pinsn; |  | ||||||
|     pinsn->vaddr = db->pc_next; |  | ||||||
|     plugin_gen_empty_callback(PLUGIN_GEN_FROM_INSN); |  | ||||||
|  |  | ||||||
|     /* |  | ||||||
|      * Detect page crossing to get the new host address. |  | ||||||
|      * Note that we skip this when haddr1 == NULL, e.g. when we're |  | ||||||
|      * fetching instructions from a region not backed by RAM. |  | ||||||
|      */ |  | ||||||
|     if (likely(ptb->haddr1 != NULL && ptb->vaddr2 == -1) && |  | ||||||
|         unlikely((db->pc_next & TARGET_PAGE_MASK) != |  | ||||||
|                  (db->pc_first & TARGET_PAGE_MASK))) { |  | ||||||
|         get_page_addr_code_hostp(cpu->env_ptr, db->pc_next, |  | ||||||
|                                  &ptb->haddr2); |  | ||||||
|         ptb->vaddr2 = db->pc_next; |  | ||||||
|     } |  | ||||||
|     if (likely(ptb->vaddr2 == -1)) { |  | ||||||
|         pinsn->haddr = ptb->haddr1 + pinsn->vaddr - ptb->vaddr; |  | ||||||
|     } else { |  | ||||||
|         pinsn->haddr = ptb->haddr2 + pinsn->vaddr - ptb->vaddr2; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void plugin_gen_insn_end(void) |  | ||||||
| { |  | ||||||
|     plugin_gen_empty_callback(PLUGIN_GEN_AFTER_INSN); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void plugin_gen_tb_end(CPUState *cpu) |  | ||||||
| { |  | ||||||
|     struct qemu_plugin_tb *ptb = tcg_ctx->plugin_tb; |  | ||||||
|     int i; |  | ||||||
|  |  | ||||||
|     /* collect instrumentation requests */ |  | ||||||
|     qemu_plugin_tb_trans_cb(cpu, ptb); |  | ||||||
|  |  | ||||||
|     /* inject the instrumentation at the appropriate places */ |  | ||||||
|     plugin_gen_inject(ptb); |  | ||||||
|  |  | ||||||
|     /* clean up */ |  | ||||||
|     for (i = 0; i < PLUGIN_N_CB_SUBTYPES; i++) { |  | ||||||
|         if (ptb->cbs[i]) { |  | ||||||
|             g_array_set_size(ptb->cbs[i], 0); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     ptb->n = 0; |  | ||||||
|     tcg_ctx->plugin_insn = NULL; |  | ||||||
| } |  | ||||||
| @@ -1,5 +0,0 @@ | |||||||
| #ifdef CONFIG_PLUGIN |  | ||||||
| /* Note: no TCG flags because those are overwritten later */ |  | ||||||
| DEF_HELPER_2(plugin_vcpu_udata_cb, void, i32, ptr) |  | ||||||
| DEF_HELPER_4(plugin_vcpu_mem_cb, void, i32, i32, i64, ptr) |  | ||||||
| #endif |  | ||||||
							
								
								
									
										435
									
								
								accel/tcg/softmmu_template.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										435
									
								
								accel/tcg/softmmu_template.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,435 @@ | |||||||
|  | /* | ||||||
|  |  *  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 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) | ||||||
|  | { | ||||||
|  |     CPUIOTLBEntry *iotlbentry = &env->iotlb[mmu_idx][index]; | ||||||
|  |     return io_readx(env, iotlbentry, mmu_idx, addr, retaddr, DATA_SIZE); | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, | ||||||
|  |                             TCGMemOpIdx oi, uintptr_t retaddr) | ||||||
|  | { | ||||||
|  |     unsigned mmu_idx = get_mmuidx(oi); | ||||||
|  |     int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); | ||||||
|  |     target_ulong tlb_addr = env->tlb_table[mmu_idx][index].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 ((addr & TARGET_PAGE_MASK) | ||||||
|  |          != (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) { | ||||||
|  |         if (!VICTIM_TLB_HIT(ADDR_READ, addr)) { | ||||||
|  |             tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, READ_ACCESS_TYPE, | ||||||
|  |                      mmu_idx, retaddr); | ||||||
|  |         } | ||||||
|  |         tlb_addr = env->tlb_table[mmu_idx][index].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); | ||||||
|  |         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 + env->tlb_table[mmu_idx][index].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) | ||||||
|  | { | ||||||
|  |     unsigned mmu_idx = get_mmuidx(oi); | ||||||
|  |     int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); | ||||||
|  |     target_ulong tlb_addr = env->tlb_table[mmu_idx][index].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 ((addr & TARGET_PAGE_MASK) | ||||||
|  |          != (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) { | ||||||
|  |         if (!VICTIM_TLB_HIT(ADDR_READ, addr)) { | ||||||
|  |             tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, READ_ACCESS_TYPE, | ||||||
|  |                      mmu_idx, retaddr); | ||||||
|  |         } | ||||||
|  |         tlb_addr = env->tlb_table[mmu_idx][index].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); | ||||||
|  |         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 + env->tlb_table[mmu_idx][index].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) | ||||||
|  | { | ||||||
|  |     CPUIOTLBEntry *iotlbentry = &env->iotlb[mmu_idx][index]; | ||||||
|  |     return io_writex(env, iotlbentry, mmu_idx, val, addr, retaddr, DATA_SIZE); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, | ||||||
|  |                        TCGMemOpIdx oi, uintptr_t retaddr) | ||||||
|  | { | ||||||
|  |     unsigned mmu_idx = get_mmuidx(oi); | ||||||
|  |     int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); | ||||||
|  |     target_ulong tlb_addr = env->tlb_table[mmu_idx][index].addr_write; | ||||||
|  |     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 ((addr & TARGET_PAGE_MASK) | ||||||
|  |         != (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) { | ||||||
|  |         if (!VICTIM_TLB_HIT(addr_write, addr)) { | ||||||
|  |             tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, MMU_DATA_STORE, | ||||||
|  |                      mmu_idx, retaddr); | ||||||
|  |         } | ||||||
|  |         tlb_addr = env->tlb_table[mmu_idx][index].addr_write & ~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); | ||||||
|  |         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, index2; | ||||||
|  |         target_ulong page2, tlb_addr2; | ||||||
|  |     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; | ||||||
|  |         index2 = (page2 >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); | ||||||
|  |         tlb_addr2 = env->tlb_table[mmu_idx][index2].addr_write; | ||||||
|  |         if (page2 != (tlb_addr2 & (TARGET_PAGE_MASK | TLB_INVALID_MASK)) | ||||||
|  |             && !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 + env->tlb_table[mmu_idx][index].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) | ||||||
|  | { | ||||||
|  |     unsigned mmu_idx = get_mmuidx(oi); | ||||||
|  |     int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); | ||||||
|  |     target_ulong tlb_addr = env->tlb_table[mmu_idx][index].addr_write; | ||||||
|  |     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 ((addr & TARGET_PAGE_MASK) | ||||||
|  |         != (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) { | ||||||
|  |         if (!VICTIM_TLB_HIT(addr_write, addr)) { | ||||||
|  |             tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, MMU_DATA_STORE, | ||||||
|  |                      mmu_idx, retaddr); | ||||||
|  |         } | ||||||
|  |         tlb_addr = env->tlb_table[mmu_idx][index].addr_write & ~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); | ||||||
|  |         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, index2; | ||||||
|  |         target_ulong page2, tlb_addr2; | ||||||
|  |     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; | ||||||
|  |         index2 = (page2 >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); | ||||||
|  |         tlb_addr2 = env->tlb_table[mmu_idx][index2].addr_write; | ||||||
|  |         if (page2 != (tlb_addr2 & (TARGET_PAGE_MASK | TLB_INVALID_MASK)) | ||||||
|  |             && !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 + env->tlb_table[mmu_idx][index].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 | ||||||
| @@ -25,14 +25,16 @@ | |||||||
|  |  | ||||||
| #include "qemu/osdep.h" | #include "qemu/osdep.h" | ||||||
| #include "sysemu/accel.h" | #include "sysemu/accel.h" | ||||||
| #include "sysemu/tcg.h" | #include "sysemu/sysemu.h" | ||||||
| #include "qom/object.h" | #include "qom/object.h" | ||||||
| #include "cpu.h" | #include "qemu-common.h" | ||||||
|  | #include "qom/cpu.h" | ||||||
| #include "sysemu/cpus.h" | #include "sysemu/cpus.h" | ||||||
| #include "qemu/main-loop.h" | #include "qemu/main-loop.h" | ||||||
|  |  | ||||||
| unsigned long tcg_tb_size; | unsigned long tcg_tb_size; | ||||||
|  |  | ||||||
|  | #ifndef CONFIG_USER_ONLY | ||||||
| /* mask must never be zero, except for A20 change call */ | /* mask must never be zero, except for A20 change call */ | ||||||
| static void tcg_handle_interrupt(CPUState *cpu, int mask) | static void tcg_handle_interrupt(CPUState *cpu, int mask) | ||||||
| { | { | ||||||
| @@ -49,7 +51,7 @@ static void tcg_handle_interrupt(CPUState *cpu, int mask) | |||||||
|     if (!qemu_cpu_is_self(cpu)) { |     if (!qemu_cpu_is_self(cpu)) { | ||||||
|         qemu_cpu_kick(cpu); |         qemu_cpu_kick(cpu); | ||||||
|     } else { |     } else { | ||||||
|         atomic_set(&cpu_neg(cpu)->icount_decr.u16.high, -1); |         cpu->icount_decr.u16.high = -1; | ||||||
|         if (use_icount && |         if (use_icount && | ||||||
|             !cpu->can_do_io |             !cpu->can_do_io | ||||||
|             && (mask & ~old_mask) != 0) { |             && (mask & ~old_mask) != 0) { | ||||||
| @@ -57,6 +59,7 @@ static void tcg_handle_interrupt(CPUState *cpu, int mask) | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | #endif | ||||||
|  |  | ||||||
| static int tcg_init(MachineState *ms) | static int tcg_init(MachineState *ms) | ||||||
| { | { | ||||||
|   | |||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * This library is free software; you can redistribute it and/or |  * This library is free software; you can redistribute it and/or | ||||||
|  * modify it under the terms of the GNU Lesser General Public |  * modify it under the terms of the GNU Lesser General Public | ||||||
|  * License as published by the Free Software Foundation; either |  * License as published by the Free Software Foundation; either | ||||||
|  * version 2.1 of the License, or (at your option) any later version. |  * version 2 of the License, or (at your option) any later version. | ||||||
|  * |  * | ||||||
|  * This library is distributed in the hope that it will be useful, |  * This library is distributed in the hope that it will be useful, | ||||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
| @@ -398,54 +398,6 @@ void HELPER(gvec_neg64)(void *d, void *a, uint32_t desc) | |||||||
|     clear_high(d, oprsz, 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) | void HELPER(gvec_mov)(void *d, void *a, uint32_t desc) | ||||||
| { | { | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |     intptr_t oprsz = simd_oprsz(desc); | ||||||
| @@ -560,39 +512,6 @@ void HELPER(gvec_orc)(void *d, void *a, void *b, uint32_t desc) | |||||||
|     clear_high(d, oprsz, desc); |     clear_high(d, oprsz, desc); | ||||||
| } | } | ||||||
|  |  | ||||||
| void HELPER(gvec_nand)(void *d, void *a, void *b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(vec64)) { |  | ||||||
|         *(vec64 *)(d + i) = ~(*(vec64 *)(a + i) & *(vec64 *)(b + i)); |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_nor)(void *d, void *a, void *b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(vec64)) { |  | ||||||
|         *(vec64 *)(d + i) = ~(*(vec64 *)(a + i) | *(vec64 *)(b + i)); |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_eqv)(void *d, void *a, void *b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(vec64)) { |  | ||||||
|         *(vec64 *)(d + i) = ~(*(vec64 *)(a + i) ^ *(vec64 *)(b + i)); |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_ands)(void *d, void *a, uint64_t b, uint32_t desc) | void HELPER(gvec_ands)(void *d, void *a, uint64_t b, uint32_t desc) | ||||||
| { | { | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |     intptr_t oprsz = simd_oprsz(desc); | ||||||
| @@ -773,150 +692,6 @@ void HELPER(gvec_sar64i)(void *d, void *a, uint32_t desc) | |||||||
|     clear_high(d, oprsz, 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. | /* If vectors are enabled, the compiler fills in -1 for true. | ||||||
|    Otherwise, we must take care of this by hand.  */ |    Otherwise, we must take care of this by hand.  */ | ||||||
| #ifdef CONFIG_VECTOR16 | #ifdef CONFIG_VECTOR16 | ||||||
| @@ -930,7 +705,7 @@ void HELPER(NAME)(void *d, void *a, void *b, uint32_t desc)                \ | |||||||
| {                                                                          \ | {                                                                          \ | ||||||
|     intptr_t oprsz = simd_oprsz(desc);                                     \ |     intptr_t oprsz = simd_oprsz(desc);                                     \ | ||||||
|     intptr_t i;                                                            \ |     intptr_t i;                                                            \ | ||||||
|     for (i = 0; i < oprsz; i += sizeof(TYPE)) {                            \ |     for (i = 0; i < oprsz; i += sizeof(vec64)) {                           \ | ||||||
|         *(TYPE *)(d + i) = DO_CMP0(*(TYPE *)(a + i) OP *(TYPE *)(b + i));  \ |         *(TYPE *)(d + i) = DO_CMP0(*(TYPE *)(a + i) OP *(TYPE *)(b + i));  \ | ||||||
|     }                                                                      \ |     }                                                                      \ | ||||||
|     clear_high(d, oprsz, desc);                                            \ |     clear_high(d, oprsz, desc);                                            \ | ||||||
| @@ -1220,241 +995,3 @@ void HELPER(gvec_ussub64)(void *d, void *a, void *b, uint32_t desc) | |||||||
|     } |     } | ||||||
|     clear_high(d, oprsz, desc); |     clear_high(d, oprsz, desc); | ||||||
| } | } | ||||||
|  |  | ||||||
| void HELPER(gvec_smin8)(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)) { |  | ||||||
|         int8_t aa = *(int8_t *)(a + i); |  | ||||||
|         int8_t bb = *(int8_t *)(b + i); |  | ||||||
|         int8_t dd = aa < bb ? aa : bb; |  | ||||||
|         *(int8_t *)(d + i) = dd; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_smin16)(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)) { |  | ||||||
|         int16_t aa = *(int16_t *)(a + i); |  | ||||||
|         int16_t bb = *(int16_t *)(b + i); |  | ||||||
|         int16_t dd = aa < bb ? aa : bb; |  | ||||||
|         *(int16_t *)(d + i) = dd; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_smin32)(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)) { |  | ||||||
|         int32_t aa = *(int32_t *)(a + i); |  | ||||||
|         int32_t bb = *(int32_t *)(b + i); |  | ||||||
|         int32_t dd = aa < bb ? aa : bb; |  | ||||||
|         *(int32_t *)(d + i) = dd; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_smin64)(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)) { |  | ||||||
|         int64_t aa = *(int64_t *)(a + i); |  | ||||||
|         int64_t bb = *(int64_t *)(b + i); |  | ||||||
|         int64_t dd = aa < bb ? aa : bb; |  | ||||||
|         *(int64_t *)(d + i) = dd; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_smax8)(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)) { |  | ||||||
|         int8_t aa = *(int8_t *)(a + i); |  | ||||||
|         int8_t bb = *(int8_t *)(b + i); |  | ||||||
|         int8_t dd = aa > bb ? aa : bb; |  | ||||||
|         *(int8_t *)(d + i) = dd; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_smax16)(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)) { |  | ||||||
|         int16_t aa = *(int16_t *)(a + i); |  | ||||||
|         int16_t bb = *(int16_t *)(b + i); |  | ||||||
|         int16_t dd = aa > bb ? aa : bb; |  | ||||||
|         *(int16_t *)(d + i) = dd; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_smax32)(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)) { |  | ||||||
|         int32_t aa = *(int32_t *)(a + i); |  | ||||||
|         int32_t bb = *(int32_t *)(b + i); |  | ||||||
|         int32_t dd = aa > bb ? aa : bb; |  | ||||||
|         *(int32_t *)(d + i) = dd; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_smax64)(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)) { |  | ||||||
|         int64_t aa = *(int64_t *)(a + i); |  | ||||||
|         int64_t bb = *(int64_t *)(b + i); |  | ||||||
|         int64_t dd = aa > bb ? aa : bb; |  | ||||||
|         *(int64_t *)(d + i) = dd; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_umin8)(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 aa = *(uint8_t *)(a + i); |  | ||||||
|         uint8_t bb = *(uint8_t *)(b + i); |  | ||||||
|         uint8_t dd = aa < bb ? aa : bb; |  | ||||||
|         *(uint8_t *)(d + i) = dd; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_umin16)(void *d, void *a, void *b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(uint16_t)) { |  | ||||||
|         uint16_t aa = *(uint16_t *)(a + i); |  | ||||||
|         uint16_t bb = *(uint16_t *)(b + i); |  | ||||||
|         uint16_t dd = aa < bb ? aa : bb; |  | ||||||
|         *(uint16_t *)(d + i) = dd; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_umin32)(void *d, void *a, void *b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(uint32_t)) { |  | ||||||
|         uint32_t aa = *(uint32_t *)(a + i); |  | ||||||
|         uint32_t bb = *(uint32_t *)(b + i); |  | ||||||
|         uint32_t dd = aa < bb ? aa : bb; |  | ||||||
|         *(uint32_t *)(d + i) = dd; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_umin64)(void *d, void *a, void *b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(uint64_t)) { |  | ||||||
|         uint64_t aa = *(uint64_t *)(a + i); |  | ||||||
|         uint64_t bb = *(uint64_t *)(b + i); |  | ||||||
|         uint64_t dd = aa < bb ? aa : bb; |  | ||||||
|         *(uint64_t *)(d + i) = dd; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_umax8)(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 aa = *(uint8_t *)(a + i); |  | ||||||
|         uint8_t bb = *(uint8_t *)(b + i); |  | ||||||
|         uint8_t dd = aa > bb ? aa : bb; |  | ||||||
|         *(uint8_t *)(d + i) = dd; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_umax16)(void *d, void *a, void *b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(uint16_t)) { |  | ||||||
|         uint16_t aa = *(uint16_t *)(a + i); |  | ||||||
|         uint16_t bb = *(uint16_t *)(b + i); |  | ||||||
|         uint16_t dd = aa > bb ? aa : bb; |  | ||||||
|         *(uint16_t *)(d + i) = dd; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_umax32)(void *d, void *a, void *b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(uint32_t)) { |  | ||||||
|         uint32_t aa = *(uint32_t *)(a + i); |  | ||||||
|         uint32_t bb = *(uint32_t *)(b + i); |  | ||||||
|         uint32_t dd = aa > bb ? aa : bb; |  | ||||||
|         *(uint32_t *)(d + i) = dd; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_umax64)(void *d, void *a, void *b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(uint64_t)) { |  | ||||||
|         uint64_t aa = *(uint64_t *)(a + i); |  | ||||||
|         uint64_t bb = *(uint64_t *)(b + i); |  | ||||||
|         uint64_t dd = aa > bb ? aa : bb; |  | ||||||
|         *(uint64_t *)(d + i) = dd; |  | ||||||
|     } |  | ||||||
|     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); |  | ||||||
| } |  | ||||||
|   | |||||||
| @@ -146,7 +146,7 @@ uint64_t HELPER(ctpop_i64)(uint64_t arg) | |||||||
|  |  | ||||||
| void *HELPER(lookup_tb_ptr)(CPUArchState *env) | void *HELPER(lookup_tb_ptr)(CPUArchState *env) | ||||||
| { | { | ||||||
|     CPUState *cpu = env_cpu(env); |     CPUState *cpu = ENV_GET_CPU(env); | ||||||
|     TranslationBlock *tb; |     TranslationBlock *tb; | ||||||
|     target_ulong cs_base, pc; |     target_ulong cs_base, pc; | ||||||
|     uint32_t flags; |     uint32_t flags; | ||||||
| @@ -165,5 +165,5 @@ void *HELPER(lookup_tb_ptr)(CPUArchState *env) | |||||||
|  |  | ||||||
| void HELPER(exit_atomic)(CPUArchState *env) | void HELPER(exit_atomic)(CPUArchState *env) | ||||||
| { | { | ||||||
|     cpu_loop_exit_atomic(env_cpu(env), GETPC()); |     cpu_loop_exit_atomic(ENV_GET_CPU(env), GETPC()); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -125,19 +125,11 @@ GEN_ATOMIC_HELPERS(fetch_add) | |||||||
| GEN_ATOMIC_HELPERS(fetch_and) | GEN_ATOMIC_HELPERS(fetch_and) | ||||||
| GEN_ATOMIC_HELPERS(fetch_or) | GEN_ATOMIC_HELPERS(fetch_or) | ||||||
| GEN_ATOMIC_HELPERS(fetch_xor) | GEN_ATOMIC_HELPERS(fetch_xor) | ||||||
| GEN_ATOMIC_HELPERS(fetch_smin) |  | ||||||
| GEN_ATOMIC_HELPERS(fetch_umin) |  | ||||||
| GEN_ATOMIC_HELPERS(fetch_smax) |  | ||||||
| GEN_ATOMIC_HELPERS(fetch_umax) |  | ||||||
|  |  | ||||||
| GEN_ATOMIC_HELPERS(add_fetch) | GEN_ATOMIC_HELPERS(add_fetch) | ||||||
| GEN_ATOMIC_HELPERS(and_fetch) | GEN_ATOMIC_HELPERS(and_fetch) | ||||||
| GEN_ATOMIC_HELPERS(or_fetch) | GEN_ATOMIC_HELPERS(or_fetch) | ||||||
| GEN_ATOMIC_HELPERS(xor_fetch) | GEN_ATOMIC_HELPERS(xor_fetch) | ||||||
| GEN_ATOMIC_HELPERS(smin_fetch) |  | ||||||
| GEN_ATOMIC_HELPERS(umin_fetch) |  | ||||||
| GEN_ATOMIC_HELPERS(smax_fetch) |  | ||||||
| GEN_ATOMIC_HELPERS(umax_fetch) |  | ||||||
|  |  | ||||||
| GEN_ATOMIC_HELPERS(xchg) | GEN_ATOMIC_HELPERS(xchg) | ||||||
|  |  | ||||||
| @@ -200,45 +192,17 @@ DEF_HELPER_FLAGS_4(gvec_ussub16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | |||||||
| DEF_HELPER_FLAGS_4(gvec_ussub32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | DEF_HELPER_FLAGS_4(gvec_ussub32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||||
| DEF_HELPER_FLAGS_4(gvec_ussub64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | DEF_HELPER_FLAGS_4(gvec_ussub64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||||
|  |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_smin8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_smin16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_smin32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_smin64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
|  |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_smax8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_smax16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_smax32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_smax64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
|  |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_umin8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_umin16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_umin32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_umin64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
|  |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_umax8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_umax16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_umax32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_umax64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
|  |  | ||||||
| DEF_HELPER_FLAGS_3(gvec_neg8, TCG_CALL_NO_RWG, void, ptr, ptr, i32) | DEF_HELPER_FLAGS_3(gvec_neg8, TCG_CALL_NO_RWG, void, ptr, ptr, i32) | ||||||
| DEF_HELPER_FLAGS_3(gvec_neg16, TCG_CALL_NO_RWG, void, ptr, ptr, i32) | DEF_HELPER_FLAGS_3(gvec_neg16, TCG_CALL_NO_RWG, void, ptr, ptr, i32) | ||||||
| DEF_HELPER_FLAGS_3(gvec_neg32, TCG_CALL_NO_RWG, void, ptr, ptr, i32) | DEF_HELPER_FLAGS_3(gvec_neg32, TCG_CALL_NO_RWG, void, ptr, ptr, i32) | ||||||
| DEF_HELPER_FLAGS_3(gvec_neg64, TCG_CALL_NO_RWG, void, ptr, ptr, i32) | DEF_HELPER_FLAGS_3(gvec_neg64, TCG_CALL_NO_RWG, void, ptr, ptr, i32) | ||||||
|  |  | ||||||
| DEF_HELPER_FLAGS_3(gvec_abs8, TCG_CALL_NO_RWG, void, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_3(gvec_abs16, TCG_CALL_NO_RWG, void, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_3(gvec_abs32, TCG_CALL_NO_RWG, void, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_3(gvec_abs64, TCG_CALL_NO_RWG, void, ptr, ptr, i32) |  | ||||||
|  |  | ||||||
| DEF_HELPER_FLAGS_3(gvec_not, TCG_CALL_NO_RWG, void, ptr, ptr, i32) | DEF_HELPER_FLAGS_3(gvec_not, TCG_CALL_NO_RWG, void, ptr, ptr, i32) | ||||||
| DEF_HELPER_FLAGS_4(gvec_and, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | DEF_HELPER_FLAGS_4(gvec_and, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||||
| DEF_HELPER_FLAGS_4(gvec_or, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | DEF_HELPER_FLAGS_4(gvec_or, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||||
| DEF_HELPER_FLAGS_4(gvec_xor, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | DEF_HELPER_FLAGS_4(gvec_xor, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||||
| DEF_HELPER_FLAGS_4(gvec_andc, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | DEF_HELPER_FLAGS_4(gvec_andc, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||||
| DEF_HELPER_FLAGS_4(gvec_orc, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | DEF_HELPER_FLAGS_4(gvec_orc, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||||
| DEF_HELPER_FLAGS_4(gvec_nand, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_nor, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_eqv, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
|  |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_ands, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) | DEF_HELPER_FLAGS_4(gvec_ands, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) | ||||||
| DEF_HELPER_FLAGS_4(gvec_xors, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) | DEF_HELPER_FLAGS_4(gvec_xors, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) | ||||||
| @@ -259,21 +223,6 @@ DEF_HELPER_FLAGS_3(gvec_sar16i, TCG_CALL_NO_RWG, void, ptr, ptr, i32) | |||||||
| DEF_HELPER_FLAGS_3(gvec_sar32i, TCG_CALL_NO_RWG, void, ptr, ptr, i32) | DEF_HELPER_FLAGS_3(gvec_sar32i, TCG_CALL_NO_RWG, void, ptr, ptr, i32) | ||||||
| DEF_HELPER_FLAGS_3(gvec_sar64i, TCG_CALL_NO_RWG, void, ptr, ptr, i32) | DEF_HELPER_FLAGS_3(gvec_sar64i, TCG_CALL_NO_RWG, void, ptr, ptr, i32) | ||||||
|  |  | ||||||
| DEF_HELPER_FLAGS_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_eq8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||||
| DEF_HELPER_FLAGS_4(gvec_eq16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | DEF_HELPER_FLAGS_4(gvec_eq16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||||
| DEF_HELPER_FLAGS_4(gvec_eq32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | DEF_HELPER_FLAGS_4(gvec_eq32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||||
| @@ -303,5 +252,3 @@ DEF_HELPER_FLAGS_4(gvec_leu8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | |||||||
| DEF_HELPER_FLAGS_4(gvec_leu16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | DEF_HELPER_FLAGS_4(gvec_leu16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||||
| DEF_HELPER_FLAGS_4(gvec_leu32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | DEF_HELPER_FLAGS_4(gvec_leu32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||||
| DEF_HELPER_FLAGS_4(gvec_leu64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | DEF_HELPER_FLAGS_4(gvec_leu64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||||
|  |  | ||||||
| DEF_HELPER_FLAGS_5(gvec_bitsel, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) |  | ||||||
|   | |||||||
| @@ -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) | # TCG related tracing (mostly disabled by default) | ||||||
| # cpu-exec.c | # cpu-exec.c | ||||||
|   | |||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -6,7 +6,7 @@ | |||||||
|  * This library is free software; you can redistribute it and/or |  * This library is free software; you can redistribute it and/or | ||||||
|  * modify it under the terms of the GNU Lesser General Public |  * modify it under the terms of the GNU Lesser General Public | ||||||
|  * License as published by the Free Software Foundation; either |  * License as published by the Free Software Foundation; either | ||||||
|  * version 2.1 of the License, or (at your option) any later version. |  * version 2 of the License, or (at your option) any later version. | ||||||
|  * |  * | ||||||
|  * This library is distributed in the hope that it will be useful, |  * This library is distributed in the hope that it will be useful, | ||||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
| @@ -23,14 +23,11 @@ | |||||||
|  |  | ||||||
|  |  | ||||||
| /* translate-all.c */ | /* translate-all.c */ | ||||||
| struct page_collection *page_collection_lock(tb_page_addr_t start, | void tb_invalidate_phys_page_fast(tb_page_addr_t start, int len); | ||||||
|                                              tb_page_addr_t end); | void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end, | ||||||
| void page_collection_unlock(struct page_collection *set); |                                    int is_cpu_write_access); | ||||||
| void tb_invalidate_phys_page_fast(struct page_collection *pages, | void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t end); | ||||||
|                                   tb_page_addr_t start, int len, | void tb_check_watchpoint(CPUState *cpu); | ||||||
|                                   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); |  | ||||||
|  |  | ||||||
| #ifdef CONFIG_USER_ONLY | #ifdef CONFIG_USER_ONLY | ||||||
| int page_unprotect(target_ulong address, uintptr_t pc); | int page_unprotect(target_ulong address, uintptr_t pc); | ||||||
|   | |||||||
| @@ -8,6 +8,7 @@ | |||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #include "qemu/osdep.h" | #include "qemu/osdep.h" | ||||||
|  | #include "qemu-common.h" | ||||||
| #include "qemu/error-report.h" | #include "qemu/error-report.h" | ||||||
| #include "cpu.h" | #include "cpu.h" | ||||||
| #include "tcg/tcg.h" | #include "tcg/tcg.h" | ||||||
| @@ -16,7 +17,6 @@ | |||||||
| #include "exec/gen-icount.h" | #include "exec/gen-icount.h" | ||||||
| #include "exec/log.h" | #include "exec/log.h" | ||||||
| #include "exec/translator.h" | #include "exec/translator.h" | ||||||
| #include "exec/plugin-gen.h" |  | ||||||
|  |  | ||||||
| /* Pairs with tcg_clear_temp_count. | /* Pairs with tcg_clear_temp_count. | ||||||
|    To be called by #TranslatorOps.{translate_insn,tb_stop} if |    To be called by #TranslatorOps.{translate_insn,tb_stop} if | ||||||
| @@ -32,10 +32,9 @@ void translator_loop_temp_check(DisasContextBase *db) | |||||||
| } | } | ||||||
|  |  | ||||||
| void translator_loop(const TranslatorOps *ops, DisasContextBase *db, | void translator_loop(const TranslatorOps *ops, DisasContextBase *db, | ||||||
|                      CPUState *cpu, TranslationBlock *tb, int max_insns) |                      CPUState *cpu, TranslationBlock *tb) | ||||||
| { | { | ||||||
|     int bp_insn = 0; |     int max_insns; | ||||||
|     bool plugin_enabled; |  | ||||||
|  |  | ||||||
|     /* Initialize DisasContext */ |     /* Initialize DisasContext */ | ||||||
|     db->tb = tb; |     db->tb = tb; | ||||||
| @@ -43,10 +42,21 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db, | |||||||
|     db->pc_next = db->pc_first; |     db->pc_next = db->pc_first; | ||||||
|     db->is_jmp = DISAS_NEXT; |     db->is_jmp = DISAS_NEXT; | ||||||
|     db->num_insns = 0; |     db->num_insns = 0; | ||||||
|     db->max_insns = max_insns; |  | ||||||
|     db->singlestep_enabled = cpu->singlestep_enabled; |     db->singlestep_enabled = cpu->singlestep_enabled; | ||||||
|  |  | ||||||
|     ops->init_disas_context(db, cpu); |     /* Instruction counting */ | ||||||
|  |     max_insns = tb_cflags(db->tb) & CF_COUNT_MASK; | ||||||
|  |     if (max_insns == 0) { | ||||||
|  |         max_insns = CF_COUNT_MASK; | ||||||
|  |     } | ||||||
|  |     if (max_insns > TCG_MAX_INSNS) { | ||||||
|  |         max_insns = TCG_MAX_INSNS; | ||||||
|  |     } | ||||||
|  |     if (db->singlestep_enabled || singlestep) { | ||||||
|  |         max_insns = 1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     max_insns = ops->init_disas_context(db, cpu, max_insns); | ||||||
|     tcg_debug_assert(db->is_jmp == DISAS_NEXT);  /* no early exit */ |     tcg_debug_assert(db->is_jmp == DISAS_NEXT);  /* no early exit */ | ||||||
|  |  | ||||||
|     /* Reset the temp count so that we can identify leaks */ |     /* Reset the temp count so that we can identify leaks */ | ||||||
| @@ -57,25 +67,17 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db, | |||||||
|     ops->tb_start(db, cpu); |     ops->tb_start(db, cpu); | ||||||
|     tcg_debug_assert(db->is_jmp == DISAS_NEXT);  /* no early exit */ |     tcg_debug_assert(db->is_jmp == DISAS_NEXT);  /* no early exit */ | ||||||
|  |  | ||||||
|     plugin_enabled = plugin_gen_tb_start(cpu, tb); |  | ||||||
|  |  | ||||||
|     while (true) { |     while (true) { | ||||||
|         db->num_insns++; |         db->num_insns++; | ||||||
|         ops->insn_start(db, cpu); |         ops->insn_start(db, cpu); | ||||||
|         tcg_debug_assert(db->is_jmp == DISAS_NEXT);  /* no early exit */ |         tcg_debug_assert(db->is_jmp == DISAS_NEXT);  /* no early exit */ | ||||||
|  |  | ||||||
|         if (plugin_enabled) { |  | ||||||
|             plugin_gen_insn_start(cpu, db); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         /* Pass breakpoint hits to target for further processing */ |         /* Pass breakpoint hits to target for further processing */ | ||||||
|         if (!db->singlestep_enabled |         if (unlikely(!QTAILQ_EMPTY(&cpu->breakpoints))) { | ||||||
|             && unlikely(!QTAILQ_EMPTY(&cpu->breakpoints))) { |  | ||||||
|             CPUBreakpoint *bp; |             CPUBreakpoint *bp; | ||||||
|             QTAILQ_FOREACH(bp, &cpu->breakpoints, entry) { |             QTAILQ_FOREACH(bp, &cpu->breakpoints, entry) { | ||||||
|                 if (bp->pc == db->pc_next) { |                 if (bp->pc == db->pc_next) { | ||||||
|                     if (ops->breakpoint_check(db, cpu, bp)) { |                     if (ops->breakpoint_check(db, cpu, bp)) { | ||||||
|                         bp_insn = 1; |  | ||||||
|                         break; |                         break; | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
| @@ -93,11 +95,11 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db, | |||||||
|            update db->pc_next and db->is_jmp to indicate what should be |            update db->pc_next and db->is_jmp to indicate what should be | ||||||
|            done next -- either exiting this loop or locate the start of |            done next -- either exiting this loop or locate the start of | ||||||
|            the next instruction.  */ |            the next instruction.  */ | ||||||
|         if (db->num_insns == db->max_insns |         if (db->num_insns == max_insns && (tb_cflags(db->tb) & CF_LAST_IO)) { | ||||||
|             && (tb_cflags(db->tb) & CF_LAST_IO)) { |  | ||||||
|             /* Accept I/O on the last instruction.  */ |             /* Accept I/O on the last instruction.  */ | ||||||
|             gen_io_start(); |             gen_io_start(); | ||||||
|             ops->translate_insn(db, cpu); |             ops->translate_insn(db, cpu); | ||||||
|  |             gen_io_end(); | ||||||
|         } else { |         } else { | ||||||
|             ops->translate_insn(db, cpu); |             ops->translate_insn(db, cpu); | ||||||
|         } |         } | ||||||
| @@ -107,17 +109,9 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db, | |||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         /* |  | ||||||
|          * We can't instrument after instructions that change control |  | ||||||
|          * flow although this only really affects post-load operations. |  | ||||||
|          */ |  | ||||||
|         if (plugin_enabled) { |  | ||||||
|             plugin_gen_insn_end(); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         /* Stop translation if the output buffer is full, |         /* Stop translation if the output buffer is full, | ||||||
|            or we have executed all of the allowed instructions.  */ |            or we have executed all of the allowed instructions.  */ | ||||||
|         if (tcg_op_buf_full() || db->num_insns >= db->max_insns) { |         if (tcg_op_buf_full() || db->num_insns >= max_insns) { | ||||||
|             db->is_jmp = DISAS_TOO_MANY; |             db->is_jmp = DISAS_TOO_MANY; | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
| @@ -125,11 +119,7 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db, | |||||||
|  |  | ||||||
|     /* Emit code to exit the TB, as indicated by db->is_jmp.  */ |     /* Emit code to exit the TB, as indicated by db->is_jmp.  */ | ||||||
|     ops->tb_stop(db, cpu); |     ops->tb_stop(db, cpu); | ||||||
|     gen_tb_end(db->tb, db->num_insns - bp_insn); |     gen_tb_end(db->tb, db->num_insns); | ||||||
|  |  | ||||||
|     if (plugin_enabled) { |  | ||||||
|         plugin_gen_tb_end(cpu); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /* The disas_log hook may use these values rather than recompute.  */ |     /* The disas_log hook may use these values rather than recompute.  */ | ||||||
|     db->tb->size = db->pc_next - db->pc_first; |     db->tb->size = db->pc_next - db->pc_first; | ||||||
|   | |||||||
| @@ -1,9 +1,7 @@ | |||||||
| #include "qemu/osdep.h" | #include "qemu/osdep.h" | ||||||
| #include "hw/core/cpu.h" | #include "qemu-common.h" | ||||||
|  | #include "qom/cpu.h" | ||||||
| #include "sysemu/replay.h" | #include "sysemu/replay.h" | ||||||
| #include "sysemu/sysemu.h" |  | ||||||
|  |  | ||||||
| bool enable_cpu_pm = false; |  | ||||||
|  |  | ||||||
| void cpu_resume(CPUState *cpu) | void cpu_resume(CPUState *cpu) | ||||||
| { | { | ||||||
|   | |||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * This library is free software; you can redistribute it and/or |  * This library is free software; you can redistribute it and/or | ||||||
|  * modify it under the terms of the GNU Lesser General Public |  * modify it under the terms of the GNU Lesser General Public | ||||||
|  * License as published by the Free Software Foundation; either |  * License as published by the Free Software Foundation; either | ||||||
|  * version 2.1 of the License, or (at your option) any later version. |  * version 2 of the License, or (at your option) any later version. | ||||||
|  * |  * | ||||||
|  * This library is distributed in the hope that it will be useful, |  * This library is distributed in the hope that it will be useful, | ||||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
| @@ -25,7 +25,6 @@ | |||||||
| #include "exec/cpu_ldst.h" | #include "exec/cpu_ldst.h" | ||||||
| #include "translate-all.h" | #include "translate-all.h" | ||||||
| #include "exec/helper-proto.h" | #include "exec/helper-proto.h" | ||||||
| #include "qemu/atomic128.h" |  | ||||||
|  |  | ||||||
| #undef EAX | #undef EAX | ||||||
| #undef ECX | #undef ECX | ||||||
| @@ -63,57 +62,28 @@ static inline int handle_cpu_signal(uintptr_t pc, siginfo_t *info, | |||||||
| { | { | ||||||
|     CPUState *cpu = current_cpu; |     CPUState *cpu = current_cpu; | ||||||
|     CPUClass *cc; |     CPUClass *cc; | ||||||
|  |     int ret; | ||||||
|     unsigned long address = (unsigned long)info->si_addr; |     unsigned long address = (unsigned long)info->si_addr; | ||||||
|     MMUAccessType access_type = is_write ? MMU_DATA_STORE : MMU_DATA_LOAD; |  | ||||||
|  |  | ||||||
|     switch (helper_retaddr) { |     /* We must handle PC addresses from two different sources: | ||||||
|     default: |      * a call return address and a signal frame address. | ||||||
|         /* |      * | ||||||
|          * Fault during host memory operation within a helper function. |      * Within cpu_restore_state_from_tb we assume the former and adjust | ||||||
|          * The helper's host return address, saved here, gives us a |      * the address by -GETPC_ADJ so that the address is within the call | ||||||
|          * pointer into the generated code that will unwind to the |      * insn so that addr does not accidentally match the beginning of the | ||||||
|          * correct guest pc. |      * next guest insn. | ||||||
|          */ |      * | ||||||
|  |      * However, when the PC comes from the signal frame, it points to | ||||||
|  |      * the actual faulting host insn and not a call insn.  Subtracting | ||||||
|  |      * GETPC_ADJ in that case may accidentally match the previous guest insn. | ||||||
|  |      * | ||||||
|  |      * So for the later case, adjust forward to compensate for what | ||||||
|  |      * will be done later by cpu_restore_state_from_tb. | ||||||
|  |      */ | ||||||
|  |     if (helper_retaddr) { | ||||||
|         pc = helper_retaddr; |         pc = helper_retaddr; | ||||||
|         break; |     } else { | ||||||
|  |  | ||||||
|     case 0: |  | ||||||
|         /* |  | ||||||
|          * Fault during host memory operation within generated code. |  | ||||||
|          * (Or, a unrelated bug within qemu, but we can't tell from here). |  | ||||||
|          * |  | ||||||
|          * We take the host pc from the signal frame.  However, we cannot |  | ||||||
|          * use that value directly.  Within cpu_restore_state_from_tb, we |  | ||||||
|          * assume PC comes from GETPC(), as used by the helper functions, |  | ||||||
|          * so we adjust the address by -GETPC_ADJ to form an address that |  | ||||||
|          * is within the call insn, so that the address does not accidentially |  | ||||||
|          * match the beginning of the next guest insn.  However, when the |  | ||||||
|          * pc comes from the signal frame it points to the actual faulting |  | ||||||
|          * host memory insn and not the return from a call insn. |  | ||||||
|          * |  | ||||||
|          * Therefore, adjust to compensate for what will be done later |  | ||||||
|          * by cpu_restore_state_from_tb. |  | ||||||
|          */ |  | ||||||
|         pc += GETPC_ADJ; |         pc += GETPC_ADJ; | ||||||
|         break; |  | ||||||
|  |  | ||||||
|     case 1: |  | ||||||
|         /* |  | ||||||
|          * Fault during host read for translation, or loosely, "execution". |  | ||||||
|          * |  | ||||||
|          * The guest pc is already pointing to the start of the TB for which |  | ||||||
|          * code is being generated.  If the guest translator manages the |  | ||||||
|          * page crossings correctly, this is exactly the correct address |  | ||||||
|          * (and if the translator doesn't handle page boundaries correctly |  | ||||||
|          * there's little we can do about that here).  Therefore, do not |  | ||||||
|          * trigger the unwinder. |  | ||||||
|          * |  | ||||||
|          * Like tb_gen_code, release the memory lock before cpu_loop_exit. |  | ||||||
|          */ |  | ||||||
|         pc = 0; |  | ||||||
|         access_type = MMU_INST_FETCH; |  | ||||||
|         mmap_unlock(); |  | ||||||
|         break; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* For synchronous signals we expect to be coming from the vCPU |     /* For synchronous signals we expect to be coming from the vCPU | ||||||
| @@ -163,7 +133,7 @@ static inline int handle_cpu_signal(uintptr_t pc, siginfo_t *info, | |||||||
|              * currently executing TB was modified and must be exited |              * currently executing TB was modified and must be exited | ||||||
|              * immediately.  Clear helper_retaddr for next execution. |              * immediately.  Clear helper_retaddr for next execution. | ||||||
|              */ |              */ | ||||||
|             clear_helper_retaddr(); |             helper_retaddr = 0; | ||||||
|             cpu_exit_tb_from_sighandler(cpu, old_set); |             cpu_exit_tb_from_sighandler(cpu, old_set); | ||||||
|             /* NORETURN */ |             /* NORETURN */ | ||||||
|  |  | ||||||
| @@ -176,48 +146,35 @@ static inline int handle_cpu_signal(uintptr_t pc, siginfo_t *info, | |||||||
|        are still valid segv ones */ |        are still valid segv ones */ | ||||||
|     address = h2g_nocheck(address); |     address = h2g_nocheck(address); | ||||||
|  |  | ||||||
|     /* |  | ||||||
|      * There is no way the target can handle this other than raising |  | ||||||
|      * an exception.  Undo signal and retaddr state prior to longjmp. |  | ||||||
|      */ |  | ||||||
|     sigprocmask(SIG_SETMASK, old_set, NULL); |  | ||||||
|     clear_helper_retaddr(); |  | ||||||
|  |  | ||||||
|     cc = CPU_GET_CLASS(cpu); |     cc = CPU_GET_CLASS(cpu); | ||||||
|     cc->tlb_fill(cpu, address, 0, access_type, MMU_USER_IDX, false, pc); |     /* see if it is an MMU fault */ | ||||||
|     g_assert_not_reached(); |     g_assert(cc->handle_mmu_fault); | ||||||
| } |     ret = cc->handle_mmu_fault(cpu, address, 0, is_write, MMU_USER_IDX); | ||||||
|  |  | ||||||
| void *probe_access(CPUArchState *env, target_ulong addr, int size, |     if (ret == 0) { | ||||||
|                    MMUAccessType access_type, int mmu_idx, uintptr_t retaddr) |         /* The MMU fault was handled without causing real CPU fault. | ||||||
| { |          *  Retain helper_retaddr for a possible second fault. | ||||||
|     int flags; |          */ | ||||||
|  |         return 1; | ||||||
|     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 (!guest_addr_valid(addr) || page_check_range(addr, size, flags) < 0) { |     /* All other paths lead to cpu_exit; clear helper_retaddr | ||||||
|         CPUState *cpu = env_cpu(env); |      * for next execution. | ||||||
|         CPUClass *cc = CPU_GET_CLASS(cpu); |      */ | ||||||
|         cc->tlb_fill(cpu, addr, size, access_type, MMU_USER_IDX, false, |     helper_retaddr = 0; | ||||||
|                      retaddr); |  | ||||||
|         g_assert_not_reached(); |     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); | ||||||
|  |  | ||||||
|  |     sigprocmask(SIG_SETMASK, old_set, NULL); | ||||||
|  |     cpu_loop_exit(cpu); | ||||||
|  |  | ||||||
|  |     /* never comes here */ | ||||||
|  |     return 1; | ||||||
| } | } | ||||||
|  |  | ||||||
| #if defined(__i386__) | #if defined(__i386__) | ||||||
| @@ -521,66 +478,28 @@ int cpu_signal_handler(int host_signum, void *pinfo, | |||||||
|  |  | ||||||
| #elif defined(__aarch64__) | #elif defined(__aarch64__) | ||||||
|  |  | ||||||
| #ifndef ESR_MAGIC |  | ||||||
| /* Pre-3.16 kernel headers don't have these, so provide fallback definitions */ |  | ||||||
| #define ESR_MAGIC 0x45535201 |  | ||||||
| struct esr_context { |  | ||||||
|     struct _aarch64_ctx head; |  | ||||||
|     uint64_t esr; |  | ||||||
| }; |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| static inline struct _aarch64_ctx *first_ctx(ucontext_t *uc) |  | ||||||
| { |  | ||||||
|     return (struct _aarch64_ctx *)&uc->uc_mcontext.__reserved; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static inline struct _aarch64_ctx *next_ctx(struct _aarch64_ctx *hdr) |  | ||||||
| { |  | ||||||
|     return (struct _aarch64_ctx *)((char *)hdr + hdr->size); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int cpu_signal_handler(int host_signum, void *pinfo, void *puc) | int cpu_signal_handler(int host_signum, void *pinfo, void *puc) | ||||||
| { | { | ||||||
|     siginfo_t *info = pinfo; |     siginfo_t *info = pinfo; | ||||||
|     ucontext_t *uc = puc; |     ucontext_t *uc = puc; | ||||||
|     uintptr_t pc = uc->uc_mcontext.pc; |     uintptr_t pc = uc->uc_mcontext.pc; | ||||||
|  |     uint32_t insn = *(uint32_t *)pc; | ||||||
|     bool is_write; |     bool is_write; | ||||||
|     struct _aarch64_ctx *hdr; |  | ||||||
|     struct esr_context const *esrctx = NULL; |  | ||||||
|  |  | ||||||
|     /* Find the esr_context, which has the WnR bit in it */ |     /* XXX: need kernel patch to get write flag faster.  */ | ||||||
|     for (hdr = first_ctx(uc); hdr->magic; hdr = next_ctx(hdr)) { |     is_write = (   (insn & 0xbfff0000) == 0x0c000000   /* C3.3.1 */ | ||||||
|         if (hdr->magic == ESR_MAGIC) { |                 || (insn & 0xbfe00000) == 0x0c800000   /* C3.3.2 */ | ||||||
|             esrctx = (struct esr_context const *)hdr; |                 || (insn & 0xbfdf0000) == 0x0d000000   /* C3.3.3 */ | ||||||
|             break; |                 || (insn & 0xbfc00000) == 0x0d800000   /* C3.3.4 */ | ||||||
|         } |                 || (insn & 0x3f400000) == 0x08000000   /* C3.3.6 */ | ||||||
|     } |                 || (insn & 0x3bc00000) == 0x39000000   /* C3.3.13 */ | ||||||
|  |                 || (insn & 0x3fc00000) == 0x3d800000   /* ... 128bit */ | ||||||
|  |                 /* Ingore bits 10, 11 & 21, controlling indexing.  */ | ||||||
|  |                 || (insn & 0x3bc00000) == 0x38000000   /* C3.3.8-12 */ | ||||||
|  |                 || (insn & 0x3fe00000) == 0x3c800000   /* ... 128bit */ | ||||||
|  |                 /* Ignore bits 23 & 24, controlling indexing.  */ | ||||||
|  |                 || (insn & 0x3a400000) == 0x28000000); /* C3.3.7,14-16 */ | ||||||
|  |  | ||||||
|     if (esrctx) { |  | ||||||
|         /* For data aborts ESR.EC is 0b10010x: then bit 6 is the WnR bit */ |  | ||||||
|         uint64_t esr = esrctx->esr; |  | ||||||
|         is_write = extract32(esr, 27, 5) == 0x12 && extract32(esr, 6, 1) == 1; |  | ||||||
|     } else { |  | ||||||
|         /* |  | ||||||
|          * Fall back to parsing instructions; will only be needed |  | ||||||
|          * for really ancient (pre-3.16) kernels. |  | ||||||
|          */ |  | ||||||
|         uint32_t insn = *(uint32_t *)pc; |  | ||||||
|  |  | ||||||
|         is_write = ((insn & 0xbfff0000) == 0x0c000000   /* C3.3.1 */ |  | ||||||
|                     || (insn & 0xbfe00000) == 0x0c800000   /* C3.3.2 */ |  | ||||||
|                     || (insn & 0xbfdf0000) == 0x0d000000   /* C3.3.3 */ |  | ||||||
|                     || (insn & 0xbfc00000) == 0x0d800000   /* C3.3.4 */ |  | ||||||
|                     || (insn & 0x3f400000) == 0x08000000   /* C3.3.6 */ |  | ||||||
|                     || (insn & 0x3bc00000) == 0x39000000   /* C3.3.13 */ |  | ||||||
|                     || (insn & 0x3fc00000) == 0x3d800000   /* ... 128bit */ |  | ||||||
|                     /* Ignore bits 10, 11 & 21, controlling indexing.  */ |  | ||||||
|                     || (insn & 0x3bc00000) == 0x38000000   /* C3.3.8-12 */ |  | ||||||
|                     || (insn & 0x3fe00000) == 0x3c800000   /* ... 128bit */ |  | ||||||
|                     /* Ignore bits 23 & 24, controlling indexing.  */ |  | ||||||
|                     || (insn & 0x3a400000) == 0x28000000); /* C3.3.7,14-16 */ |  | ||||||
|     } |  | ||||||
|     return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask); |     return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -651,81 +570,6 @@ int cpu_signal_handler(int host_signum, void *pinfo, | |||||||
|     return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask); |     return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask); | ||||||
| } | } | ||||||
|  |  | ||||||
| #elif defined(__riscv) |  | ||||||
|  |  | ||||||
| int cpu_signal_handler(int host_signum, void *pinfo, |  | ||||||
|                        void *puc) |  | ||||||
| { |  | ||||||
|     siginfo_t *info = pinfo; |  | ||||||
|     ucontext_t *uc = puc; |  | ||||||
|     greg_t pc = uc->uc_mcontext.__gregs[REG_PC]; |  | ||||||
|     uint32_t insn = *(uint32_t *)pc; |  | ||||||
|     int is_write = 0; |  | ||||||
|  |  | ||||||
|     /* Detect store by reading the instruction at the program |  | ||||||
|        counter. Note: we currently only generate 32-bit |  | ||||||
|        instructions so we thus only detect 32-bit stores */ |  | ||||||
|     switch (((insn >> 0) & 0b11)) { |  | ||||||
|     case 3: |  | ||||||
|         switch (((insn >> 2) & 0b11111)) { |  | ||||||
|         case 8: |  | ||||||
|             switch (((insn >> 12) & 0b111)) { |  | ||||||
|             case 0: /* sb */ |  | ||||||
|             case 1: /* sh */ |  | ||||||
|             case 2: /* sw */ |  | ||||||
|             case 3: /* sd */ |  | ||||||
|             case 4: /* sq */ |  | ||||||
|                 is_write = 1; |  | ||||||
|                 break; |  | ||||||
|             default: |  | ||||||
|                 break; |  | ||||||
|             } |  | ||||||
|             break; |  | ||||||
|         case 9: |  | ||||||
|             switch (((insn >> 12) & 0b111)) { |  | ||||||
|             case 2: /* fsw */ |  | ||||||
|             case 3: /* fsd */ |  | ||||||
|             case 4: /* fsq */ |  | ||||||
|                 is_write = 1; |  | ||||||
|                 break; |  | ||||||
|             default: |  | ||||||
|                 break; |  | ||||||
|             } |  | ||||||
|             break; |  | ||||||
|         default: |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /* Check for compressed instructions */ |  | ||||||
|     switch (((insn >> 13) & 0b111)) { |  | ||||||
|     case 7: |  | ||||||
|         switch (insn & 0b11) { |  | ||||||
|         case 0: /*c.sd */ |  | ||||||
|         case 2: /* c.sdsp */ |  | ||||||
|             is_write = 1; |  | ||||||
|             break; |  | ||||||
|         default: |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|         break; |  | ||||||
|     case 6: |  | ||||||
|         switch (insn & 0b11) { |  | ||||||
|         case 0: /* c.sw */ |  | ||||||
|         case 3: /* c.swsp */ |  | ||||||
|             is_write = 1; |  | ||||||
|             break; |  | ||||||
|         default: |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|         break; |  | ||||||
|     default: |  | ||||||
|         break; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #else | #else | ||||||
|  |  | ||||||
| #error host CPU specific signal handler needed | #error host CPU specific signal handler needed | ||||||
| @@ -740,24 +584,20 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr, | |||||||
| { | { | ||||||
|     /* Enforce qemu required alignment.  */ |     /* Enforce qemu required alignment.  */ | ||||||
|     if (unlikely(addr & (size - 1))) { |     if (unlikely(addr & (size - 1))) { | ||||||
|         cpu_loop_exit_atomic(env_cpu(env), retaddr); |         cpu_loop_exit_atomic(ENV_GET_CPU(env), retaddr); | ||||||
|     } |     } | ||||||
|     void *ret = g2h(addr); |     helper_retaddr = retaddr; | ||||||
|     set_helper_retaddr(retaddr); |     return g2h(addr); | ||||||
|     return ret; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Macro to call the above, with local variables from the use context.  */ | /* Macro to call the above, with local variables from the use context.  */ | ||||||
| #define ATOMIC_MMU_DECLS do {} while (0) | #define ATOMIC_MMU_DECLS do {} while (0) | ||||||
| #define ATOMIC_MMU_LOOKUP  atomic_mmu_lookup(env, addr, DATA_SIZE, GETPC()) | #define ATOMIC_MMU_LOOKUP  atomic_mmu_lookup(env, addr, DATA_SIZE, GETPC()) | ||||||
| #define ATOMIC_MMU_CLEANUP do { clear_helper_retaddr(); } while (0) | #define ATOMIC_MMU_CLEANUP do { helper_retaddr = 0; } while (0) | ||||||
| #define ATOMIC_MMU_IDX MMU_USER_IDX |  | ||||||
|  |  | ||||||
| #define ATOMIC_NAME(X)   HELPER(glue(glue(atomic_ ## X, SUFFIX), END)) | #define ATOMIC_NAME(X)   HELPER(glue(glue(atomic_ ## X, SUFFIX), END)) | ||||||
| #define EXTRA_ARGS | #define EXTRA_ARGS | ||||||
|  |  | ||||||
| #include "atomic_common.inc.c" |  | ||||||
|  |  | ||||||
| #define DATA_SIZE 1 | #define DATA_SIZE 1 | ||||||
| #include "atomic_template.h" | #include "atomic_template.h" | ||||||
|  |  | ||||||
| @@ -775,7 +615,7 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr, | |||||||
| /* The following is only callable from other helpers, and matches up | /* The following is only callable from other helpers, and matches up | ||||||
|    with the softmmu version.  */ |    with the softmmu version.  */ | ||||||
|  |  | ||||||
| #if HAVE_ATOMIC128 || HAVE_CMPXCHG128 | #ifdef CONFIG_ATOMIC128 | ||||||
|  |  | ||||||
| #undef EXTRA_ARGS | #undef EXTRA_ARGS | ||||||
| #undef ATOMIC_NAME | #undef ATOMIC_NAME | ||||||
| @@ -788,4 +628,4 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr, | |||||||
|  |  | ||||||
| #define DATA_SIZE 16 | #define DATA_SIZE 16 | ||||||
| #include "atomic_template.h" | #include "atomic_template.h" | ||||||
| #endif | #endif /* CONFIG_ATOMIC128 */ | ||||||
|   | |||||||
							
								
								
									
										32
									
								
								arch_init.c
									
									
									
									
									
								
							
							
						
						
									
										32
									
								
								arch_init.c
									
									
									
									
									
								
							| @@ -22,13 +22,13 @@ | |||||||
|  * THE SOFTWARE. |  * THE SOFTWARE. | ||||||
|  */ |  */ | ||||||
| #include "qemu/osdep.h" | #include "qemu/osdep.h" | ||||||
|  | #include "qemu-common.h" | ||||||
| #include "cpu.h" | #include "cpu.h" | ||||||
| #include "sysemu/sysemu.h" | #include "sysemu/sysemu.h" | ||||||
| #include "sysemu/arch_init.h" | #include "sysemu/arch_init.h" | ||||||
| #include "hw/pci/pci.h" | #include "hw/pci/pci.h" | ||||||
| #include "hw/audio/soundhw.h" | #include "hw/audio/soundhw.h" | ||||||
| #include "qapi/qapi-commands-misc.h" | #include "qapi/qapi-commands-misc.h" | ||||||
| #include "qapi/error.h" |  | ||||||
| #include "qemu/config-file.h" | #include "qemu/config-file.h" | ||||||
| #include "qemu/error-report.h" | #include "qemu/error-report.h" | ||||||
| #include "hw/acpi/acpi.h" | #include "hw/acpi/acpi.h" | ||||||
| @@ -38,10 +38,6 @@ | |||||||
| int graphic_width = 1024; | int graphic_width = 1024; | ||||||
| int graphic_height = 768; | int graphic_height = 768; | ||||||
| int graphic_depth = 8; | int graphic_depth = 8; | ||||||
| #elif defined(TARGET_M68K) |  | ||||||
| int graphic_width = 800; |  | ||||||
| int graphic_height = 600; |  | ||||||
| int graphic_depth = 8; |  | ||||||
| #else | #else | ||||||
| int graphic_width = 800; | int graphic_width = 800; | ||||||
| int graphic_height = 600; | int graphic_height = 600; | ||||||
| @@ -55,14 +51,14 @@ int graphic_depth = 32; | |||||||
| #define QEMU_ARCH QEMU_ARCH_ARM | #define QEMU_ARCH QEMU_ARCH_ARM | ||||||
| #elif defined(TARGET_CRIS) | #elif defined(TARGET_CRIS) | ||||||
| #define QEMU_ARCH QEMU_ARCH_CRIS | #define QEMU_ARCH QEMU_ARCH_CRIS | ||||||
| #elif defined(TARGET_HPPA) |  | ||||||
| #define QEMU_ARCH QEMU_ARCH_HPPA |  | ||||||
| #elif defined(TARGET_I386) | #elif defined(TARGET_I386) | ||||||
| #define QEMU_ARCH QEMU_ARCH_I386 | #define QEMU_ARCH QEMU_ARCH_I386 | ||||||
| #elif defined(TARGET_LM32) | #elif defined(TARGET_HPPA) | ||||||
| #define QEMU_ARCH QEMU_ARCH_LM32 | #define QEMU_ARCH QEMU_ARCH_HPPA | ||||||
| #elif defined(TARGET_M68K) | #elif defined(TARGET_M68K) | ||||||
| #define QEMU_ARCH QEMU_ARCH_M68K | #define QEMU_ARCH QEMU_ARCH_M68K | ||||||
|  | #elif defined(TARGET_LM32) | ||||||
|  | #define QEMU_ARCH QEMU_ARCH_LM32 | ||||||
| #elif defined(TARGET_MICROBLAZE) | #elif defined(TARGET_MICROBLAZE) | ||||||
| #define QEMU_ARCH QEMU_ARCH_MICROBLAZE | #define QEMU_ARCH QEMU_ARCH_MICROBLAZE | ||||||
| #elif defined(TARGET_MIPS) | #elif defined(TARGET_MIPS) | ||||||
| @@ -83,12 +79,12 @@ int graphic_depth = 32; | |||||||
| #define QEMU_ARCH QEMU_ARCH_SH4 | #define QEMU_ARCH QEMU_ARCH_SH4 | ||||||
| #elif defined(TARGET_SPARC) | #elif defined(TARGET_SPARC) | ||||||
| #define QEMU_ARCH QEMU_ARCH_SPARC | #define QEMU_ARCH QEMU_ARCH_SPARC | ||||||
| #elif defined(TARGET_TRICORE) |  | ||||||
| #define QEMU_ARCH QEMU_ARCH_TRICORE |  | ||||||
| #elif defined(TARGET_UNICORE32) |  | ||||||
| #define QEMU_ARCH QEMU_ARCH_UNICORE32 |  | ||||||
| #elif defined(TARGET_XTENSA) | #elif defined(TARGET_XTENSA) | ||||||
| #define QEMU_ARCH QEMU_ARCH_XTENSA | #define QEMU_ARCH QEMU_ARCH_XTENSA | ||||||
|  | #elif defined(TARGET_UNICORE32) | ||||||
|  | #define QEMU_ARCH QEMU_ARCH_UNICORE32 | ||||||
|  | #elif defined(TARGET_TRICORE) | ||||||
|  | #define QEMU_ARCH QEMU_ARCH_TRICORE | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| const uint32_t arch_type = QEMU_ARCH; | const uint32_t arch_type = QEMU_ARCH; | ||||||
| @@ -110,3 +106,13 @@ int xen_available(void) | |||||||
|     return 0; |     return 0; | ||||||
| #endif | #endif | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | TargetInfo *qmp_query_target(Error **errp) | ||||||
|  | { | ||||||
|  |     TargetInfo *info = g_malloc0(sizeof(*info)); | ||||||
|  |  | ||||||
|  |     info->arch = g_strdup(TARGET_NAME); | ||||||
|  |  | ||||||
|  |     return info; | ||||||
|  | } | ||||||
|   | |||||||
| @@ -1,7 +1,8 @@ | |||||||
| common-obj-y = audio.o audio_legacy.o noaudio.o wavaudio.o mixeng.o | common-obj-y = audio.o noaudio.o wavaudio.o mixeng.o | ||||||
| common-obj-$(CONFIG_SPICE) += spiceaudio.o | common-obj-$(CONFIG_SPICE) += spiceaudio.o | ||||||
| common-obj-$(CONFIG_AUDIO_COREAUDIO) += coreaudio.o | common-obj-$(CONFIG_AUDIO_COREAUDIO) += coreaudio.o | ||||||
| common-obj-$(CONFIG_AUDIO_DSOUND) += dsoundaudio.o | common-obj-$(CONFIG_AUDIO_DSOUND) += dsoundaudio.o | ||||||
|  | common-obj-$(CONFIG_AUDIO_PT_INT) += audio_pt_int.o | ||||||
| common-obj-$(CONFIG_AUDIO_WIN_INT) += audio_win_int.o | common-obj-$(CONFIG_AUDIO_WIN_INT) += audio_win_int.o | ||||||
| common-obj-y += wavcapture.o | common-obj-y += wavcapture.o | ||||||
|  |  | ||||||
|   | |||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										1669
									
								
								audio/audio.c
									
									
									
									
									
								
							
							
						
						
									
										1669
									
								
								audio/audio.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -26,32 +26,30 @@ | |||||||
| #define QEMU_AUDIO_H | #define QEMU_AUDIO_H | ||||||
|  |  | ||||||
| #include "qemu/queue.h" | #include "qemu/queue.h" | ||||||
| #include "qapi/qapi-types-audio.h" |  | ||||||
| #include "hw/qdev-properties.h" |  | ||||||
|  |  | ||||||
| typedef void (*audio_callback_fn) (void *opaque, int avail); | typedef void (*audio_callback_fn) (void *opaque, int avail); | ||||||
|  |  | ||||||
|  | typedef enum { | ||||||
|  |     AUD_FMT_U8, | ||||||
|  |     AUD_FMT_S8, | ||||||
|  |     AUD_FMT_U16, | ||||||
|  |     AUD_FMT_S16, | ||||||
|  |     AUD_FMT_U32, | ||||||
|  |     AUD_FMT_S32 | ||||||
|  | } audfmt_e; | ||||||
|  |  | ||||||
| #ifdef HOST_WORDS_BIGENDIAN | #ifdef HOST_WORDS_BIGENDIAN | ||||||
| #define AUDIO_HOST_ENDIANNESS 1 | #define AUDIO_HOST_ENDIANNESS 1 | ||||||
| #else | #else | ||||||
| #define AUDIO_HOST_ENDIANNESS 0 | #define AUDIO_HOST_ENDIANNESS 0 | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| typedef struct audsettings { | struct audsettings { | ||||||
|     int freq; |     int freq; | ||||||
|     int nchannels; |     int nchannels; | ||||||
|     AudioFormat fmt; |     audfmt_e fmt; | ||||||
|     int endianness; |     int endianness; | ||||||
| } audsettings; | }; | ||||||
|  |  | ||||||
| audsettings audiodev_to_audsettings(AudiodevPerDirectionOptions *pdo); |  | ||||||
| int audioformat_bytes_per_sample(AudioFormat fmt); |  | ||||||
| int audio_buffer_frames(AudiodevPerDirectionOptions *pdo, |  | ||||||
|                         audsettings *as, int def_usecs); |  | ||||||
| int audio_buffer_samples(AudiodevPerDirectionOptions *pdo, |  | ||||||
|                          audsettings *as, int def_usecs); |  | ||||||
| int audio_buffer_bytes(AudiodevPerDirectionOptions *pdo, |  | ||||||
|                        audsettings *as, int def_usecs); |  | ||||||
|  |  | ||||||
| typedef enum { | typedef enum { | ||||||
|     AUD_CNOTIFY_ENABLE, |     AUD_CNOTIFY_ENABLE, | ||||||
| @@ -79,10 +77,8 @@ typedef struct SWVoiceOut SWVoiceOut; | |||||||
| typedef struct CaptureVoiceOut CaptureVoiceOut; | typedef struct CaptureVoiceOut CaptureVoiceOut; | ||||||
| typedef struct SWVoiceIn SWVoiceIn; | typedef struct SWVoiceIn SWVoiceIn; | ||||||
|  |  | ||||||
| typedef struct AudioState AudioState; |  | ||||||
| typedef struct QEMUSoundCard { | typedef struct QEMUSoundCard { | ||||||
|     char *name; |     char *name; | ||||||
|     AudioState *state; |  | ||||||
|     QLIST_ENTRY (QEMUSoundCard) entries; |     QLIST_ENTRY (QEMUSoundCard) entries; | ||||||
| } QEMUSoundCard; | } QEMUSoundCard; | ||||||
|  |  | ||||||
| @@ -93,10 +89,10 @@ typedef struct QEMUAudioTimeStamp { | |||||||
| void AUD_vlog (const char *cap, const char *fmt, va_list ap) GCC_FMT_ATTR(2, 0); | void AUD_vlog (const char *cap, const char *fmt, va_list ap) GCC_FMT_ATTR(2, 0); | ||||||
| void AUD_log (const char *cap, const char *fmt, ...) GCC_FMT_ATTR(2, 3); | void AUD_log (const char *cap, const char *fmt, ...) GCC_FMT_ATTR(2, 3); | ||||||
|  |  | ||||||
|  | void AUD_help (void); | ||||||
| void AUD_register_card (const char *name, QEMUSoundCard *card); | void AUD_register_card (const char *name, QEMUSoundCard *card); | ||||||
| void AUD_remove_card (QEMUSoundCard *card); | void AUD_remove_card (QEMUSoundCard *card); | ||||||
| CaptureVoiceOut *AUD_add_capture( | CaptureVoiceOut *AUD_add_capture ( | ||||||
|     AudioState *s, |  | ||||||
|     struct audsettings *as, |     struct audsettings *as, | ||||||
|     struct audio_capture_ops *ops, |     struct audio_capture_ops *ops, | ||||||
|     void *opaque |     void *opaque | ||||||
| @@ -113,7 +109,7 @@ SWVoiceOut *AUD_open_out ( | |||||||
|     ); |     ); | ||||||
|  |  | ||||||
| void AUD_close_out (QEMUSoundCard *card, SWVoiceOut *sw); | void AUD_close_out (QEMUSoundCard *card, SWVoiceOut *sw); | ||||||
| size_t AUD_write (SWVoiceOut *sw, void *pcm_buf, size_t size); | int  AUD_write (SWVoiceOut *sw, void *pcm_buf, int size); | ||||||
| int  AUD_get_buffer_size_out (SWVoiceOut *sw); | int  AUD_get_buffer_size_out (SWVoiceOut *sw); | ||||||
| void AUD_set_active_out (SWVoiceOut *sw, int on); | void AUD_set_active_out (SWVoiceOut *sw, int on); | ||||||
| int  AUD_is_active_out (SWVoiceOut *sw); | int  AUD_is_active_out (SWVoiceOut *sw); | ||||||
| @@ -124,16 +120,6 @@ uint64_t AUD_get_elapsed_usec_out (SWVoiceOut *sw, QEMUAudioTimeStamp *ts); | |||||||
| void AUD_set_volume_out (SWVoiceOut *sw, int mute, uint8_t lvol, uint8_t rvol); | void AUD_set_volume_out (SWVoiceOut *sw, int mute, uint8_t lvol, uint8_t rvol); | ||||||
| void AUD_set_volume_in (SWVoiceIn *sw, int mute, uint8_t lvol, uint8_t rvol); | void AUD_set_volume_in (SWVoiceIn *sw, int mute, uint8_t lvol, uint8_t rvol); | ||||||
|  |  | ||||||
| #define AUDIO_MAX_CHANNELS 16 |  | ||||||
| typedef struct Volume { |  | ||||||
|     bool mute; |  | ||||||
|     int channels; |  | ||||||
|     uint8_t vol[AUDIO_MAX_CHANNELS]; |  | ||||||
| } Volume; |  | ||||||
|  |  | ||||||
| void audio_set_volume_out(SWVoiceOut *sw, Volume *vol); |  | ||||||
| void audio_set_volume_in(SWVoiceIn *sw, Volume *vol); |  | ||||||
|  |  | ||||||
| SWVoiceIn *AUD_open_in ( | SWVoiceIn *AUD_open_in ( | ||||||
|     QEMUSoundCard *card, |     QEMUSoundCard *card, | ||||||
|     SWVoiceIn *sw, |     SWVoiceIn *sw, | ||||||
| @@ -144,7 +130,7 @@ SWVoiceIn *AUD_open_in ( | |||||||
|     ); |     ); | ||||||
|  |  | ||||||
| void AUD_close_in (QEMUSoundCard *card, SWVoiceIn *sw); | void AUD_close_in (QEMUSoundCard *card, SWVoiceIn *sw); | ||||||
| size_t AUD_read (SWVoiceIn *sw, void *pcm_buf, size_t size); | int  AUD_read (SWVoiceIn *sw, void *pcm_buf, int size); | ||||||
| void AUD_set_active_in (SWVoiceIn *sw, int on); | void AUD_set_active_in (SWVoiceIn *sw, int on); | ||||||
| int  AUD_is_active_in (SWVoiceIn *sw); | int  AUD_is_active_in (SWVoiceIn *sw); | ||||||
|  |  | ||||||
| @@ -157,8 +143,25 @@ static inline void *advance (void *p, int incr) | |||||||
|     return (d + incr); |     return (d + incr); | ||||||
| } | } | ||||||
|  |  | ||||||
| int wav_start_capture(AudioState *state, CaptureState *s, const char *path, | #ifdef __GNUC__ | ||||||
|                       int freq, int bits, int nchannels); | #define audio_MIN(a, b) ( __extension__ ({      \ | ||||||
|  |     __typeof (a) ta = a;                        \ | ||||||
|  |     __typeof (b) tb = b;                        \ | ||||||
|  |     ((ta)>(tb)?(tb):(ta));                      \ | ||||||
|  | })) | ||||||
|  |  | ||||||
|  | #define audio_MAX(a, b) ( __extension__ ({      \ | ||||||
|  |     __typeof (a) ta = a;                        \ | ||||||
|  |     __typeof (b) tb = b;                        \ | ||||||
|  |     ((ta)<(tb)?(tb):(ta));                      \ | ||||||
|  | })) | ||||||
|  | #else | ||||||
|  | #define audio_MIN(a, b) ((a)>(b)?(b):(a)) | ||||||
|  | #define audio_MAX(a, b) ((a)<(b)?(b):(a)) | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | int wav_start_capture (CaptureState *s, const char *path, int freq, | ||||||
|  |                        int bits, int nchannels); | ||||||
|  |  | ||||||
| bool audio_is_cleaning_up(void); | bool audio_is_cleaning_up(void); | ||||||
| void audio_cleanup(void); | void audio_cleanup(void); | ||||||
| @@ -168,14 +171,4 @@ void audio_sample_to_uint64(void *samples, int pos, | |||||||
| void audio_sample_from_uint64(void *samples, int pos, | void audio_sample_from_uint64(void *samples, int pos, | ||||||
|                             uint64_t left, uint64_t right); |                             uint64_t left, uint64_t right); | ||||||
|  |  | ||||||
| void audio_parse_option(const char *opt); |  | ||||||
| void audio_init_audiodevs(void); |  | ||||||
| void audio_legacy_help(void); |  | ||||||
|  |  | ||||||
| AudioState *audio_state_by_name(const char *name); |  | ||||||
| const char *audio_get_id(QEMUSoundCard *card); |  | ||||||
|  |  | ||||||
| #define DEFINE_AUDIO_PROPERTIES(_s, _f)         \ |  | ||||||
|     DEFINE_PROP_AUDIODEV("audiodev", _s, _f) |  | ||||||
|  |  | ||||||
| #endif /* QEMU_AUDIO_H */ | #endif /* QEMU_AUDIO_H */ | ||||||
|   | |||||||
| @@ -33,6 +33,22 @@ | |||||||
|  |  | ||||||
| struct audio_pcm_ops; | struct audio_pcm_ops; | ||||||
|  |  | ||||||
|  | typedef enum { | ||||||
|  |     AUD_OPT_INT, | ||||||
|  |     AUD_OPT_FMT, | ||||||
|  |     AUD_OPT_STR, | ||||||
|  |     AUD_OPT_BOOL | ||||||
|  | } audio_option_tag_e; | ||||||
|  |  | ||||||
|  | struct audio_option { | ||||||
|  |     const char *name; | ||||||
|  |     audio_option_tag_e tag; | ||||||
|  |     void *valp; | ||||||
|  |     const char *descr; | ||||||
|  |     int *overriddenp; | ||||||
|  |     int overridden; | ||||||
|  | }; | ||||||
|  |  | ||||||
| struct audio_callback { | struct audio_callback { | ||||||
|     void *opaque; |     void *opaque; | ||||||
|     audio_callback_fn fn; |     audio_callback_fn fn; | ||||||
| @@ -43,70 +59,63 @@ struct audio_pcm_info { | |||||||
|     int sign; |     int sign; | ||||||
|     int freq; |     int freq; | ||||||
|     int nchannels; |     int nchannels; | ||||||
|     int bytes_per_frame; |     int align; | ||||||
|  |     int shift; | ||||||
|     int bytes_per_second; |     int bytes_per_second; | ||||||
|     int swap_endianness; |     int swap_endianness; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| typedef struct AudioState AudioState; |  | ||||||
| typedef struct SWVoiceCap SWVoiceCap; | typedef struct SWVoiceCap SWVoiceCap; | ||||||
|  |  | ||||||
| typedef struct STSampleBuffer { |  | ||||||
|     size_t pos, size; |  | ||||||
|     st_sample samples[]; |  | ||||||
| } STSampleBuffer; |  | ||||||
|  |  | ||||||
| typedef struct HWVoiceOut { | typedef struct HWVoiceOut { | ||||||
|     AudioState *s; |  | ||||||
|     int enabled; |     int enabled; | ||||||
|     int poll_mode; |     int poll_mode; | ||||||
|     int pending_disable; |     int pending_disable; | ||||||
|     struct audio_pcm_info info; |     struct audio_pcm_info info; | ||||||
|  |  | ||||||
|     f_sample *clip; |     f_sample *clip; | ||||||
|  |  | ||||||
|  |     int rpos; | ||||||
|     uint64_t ts_helper; |     uint64_t ts_helper; | ||||||
|  |  | ||||||
|     STSampleBuffer *mix_buf; |     struct st_sample *mix_buf; | ||||||
|     void *buf_emul; |  | ||||||
|     size_t pos_emul, pending_emul, size_emul; |  | ||||||
|  |  | ||||||
|     size_t samples; |     int samples; | ||||||
|     QLIST_HEAD (sw_out_listhead, SWVoiceOut) sw_head; |     QLIST_HEAD (sw_out_listhead, SWVoiceOut) sw_head; | ||||||
|     QLIST_HEAD (sw_cap_listhead, SWVoiceCap) cap_head; |     QLIST_HEAD (sw_cap_listhead, SWVoiceCap) cap_head; | ||||||
|  |     int ctl_caps; | ||||||
|     struct audio_pcm_ops *pcm_ops; |     struct audio_pcm_ops *pcm_ops; | ||||||
|     QLIST_ENTRY (HWVoiceOut) entries; |     QLIST_ENTRY (HWVoiceOut) entries; | ||||||
| } HWVoiceOut; | } HWVoiceOut; | ||||||
|  |  | ||||||
| typedef struct HWVoiceIn { | typedef struct HWVoiceIn { | ||||||
|     AudioState *s; |  | ||||||
|     int enabled; |     int enabled; | ||||||
|     int poll_mode; |     int poll_mode; | ||||||
|     struct audio_pcm_info info; |     struct audio_pcm_info info; | ||||||
|  |  | ||||||
|     t_sample *conv; |     t_sample *conv; | ||||||
|  |  | ||||||
|     size_t total_samples_captured; |     int wpos; | ||||||
|  |     int total_samples_captured; | ||||||
|     uint64_t ts_helper; |     uint64_t ts_helper; | ||||||
|  |  | ||||||
|     STSampleBuffer *conv_buf; |     struct st_sample *conv_buf; | ||||||
|     void *buf_emul; |  | ||||||
|     size_t pos_emul, pending_emul, size_emul; |  | ||||||
|  |  | ||||||
|     size_t samples; |     int samples; | ||||||
|     QLIST_HEAD (sw_in_listhead, SWVoiceIn) sw_head; |     QLIST_HEAD (sw_in_listhead, SWVoiceIn) sw_head; | ||||||
|  |     int ctl_caps; | ||||||
|     struct audio_pcm_ops *pcm_ops; |     struct audio_pcm_ops *pcm_ops; | ||||||
|     QLIST_ENTRY (HWVoiceIn) entries; |     QLIST_ENTRY (HWVoiceIn) entries; | ||||||
| } HWVoiceIn; | } HWVoiceIn; | ||||||
|  |  | ||||||
| struct SWVoiceOut { | struct SWVoiceOut { | ||||||
|     QEMUSoundCard *card; |     QEMUSoundCard *card; | ||||||
|     AudioState *s; |  | ||||||
|     struct audio_pcm_info info; |     struct audio_pcm_info info; | ||||||
|     t_sample *conv; |     t_sample *conv; | ||||||
|     int64_t ratio; |     int64_t ratio; | ||||||
|     struct st_sample *buf; |     struct st_sample *buf; | ||||||
|     void *rate; |     void *rate; | ||||||
|     size_t total_hw_samples_mixed; |     int total_hw_samples_mixed; | ||||||
|     int active; |     int active; | ||||||
|     int empty; |     int empty; | ||||||
|     HWVoiceOut *hw; |     HWVoiceOut *hw; | ||||||
| @@ -118,12 +127,11 @@ struct SWVoiceOut { | |||||||
|  |  | ||||||
| struct SWVoiceIn { | struct SWVoiceIn { | ||||||
|     QEMUSoundCard *card; |     QEMUSoundCard *card; | ||||||
|     AudioState *s; |  | ||||||
|     int active; |     int active; | ||||||
|     struct audio_pcm_info info; |     struct audio_pcm_info info; | ||||||
|     int64_t ratio; |     int64_t ratio; | ||||||
|     void *rate; |     void *rate; | ||||||
|     size_t total_hw_samples_acquired; |     int total_hw_samples_acquired; | ||||||
|     struct st_sample *buf; |     struct st_sample *buf; | ||||||
|     f_sample *clip; |     f_sample *clip; | ||||||
|     HWVoiceIn *hw; |     HWVoiceIn *hw; | ||||||
| @@ -137,7 +145,8 @@ typedef struct audio_driver audio_driver; | |||||||
| struct audio_driver { | struct audio_driver { | ||||||
|     const char *name; |     const char *name; | ||||||
|     const char *descr; |     const char *descr; | ||||||
|     void *(*init) (Audiodev *); |     struct audio_option *options; | ||||||
|  |     void *(*init) (void); | ||||||
|     void (*fini) (void *); |     void (*fini) (void *); | ||||||
|     struct audio_pcm_ops *pcm_ops; |     struct audio_pcm_ops *pcm_ops; | ||||||
|     int can_be_default; |     int can_be_default; | ||||||
| @@ -145,46 +154,24 @@ struct audio_driver { | |||||||
|     int max_voices_in; |     int max_voices_in; | ||||||
|     int voice_size_out; |     int voice_size_out; | ||||||
|     int voice_size_in; |     int voice_size_in; | ||||||
|  |     int ctl_caps; | ||||||
|     QLIST_ENTRY(audio_driver) next; |     QLIST_ENTRY(audio_driver) next; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| struct audio_pcm_ops { | struct audio_pcm_ops { | ||||||
|     int    (*init_out)(HWVoiceOut *hw, audsettings *as, void *drv_opaque); |     int  (*init_out)(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque); | ||||||
|     void   (*fini_out)(HWVoiceOut *hw); |     void (*fini_out)(HWVoiceOut *hw); | ||||||
|     size_t (*write)   (HWVoiceOut *hw, void *buf, size_t size); |     int  (*run_out) (HWVoiceOut *hw, int live); | ||||||
|     /* |     int  (*write)   (SWVoiceOut *sw, void *buf, int size); | ||||||
|      * get a buffer that after later can be passed to put_buffer_out; optional |     int  (*ctl_out) (HWVoiceOut *hw, int cmd, ...); | ||||||
|      * returns the buffer, and writes it's size to size (in bytes) |  | ||||||
|      * this is unrelated to the above buffer_size_out function |  | ||||||
|      */ |  | ||||||
|     void  *(*get_buffer_out)(HWVoiceOut *hw, size_t *size); |  | ||||||
|     /* |  | ||||||
|      * put back the buffer returned by get_buffer_out; optional |  | ||||||
|      * buf must be equal the pointer returned by get_buffer_out, |  | ||||||
|      * size may be smaller |  | ||||||
|      */ |  | ||||||
|     size_t (*put_buffer_out)(HWVoiceOut *hw, void *buf, size_t size); |  | ||||||
|     void   (*enable_out)(HWVoiceOut *hw, bool enable); |  | ||||||
|     void   (*volume_out)(HWVoiceOut *hw, Volume *vol); |  | ||||||
|  |  | ||||||
|     int    (*init_in) (HWVoiceIn *hw, audsettings *as, void *drv_opaque); |     int  (*init_in) (HWVoiceIn *hw, struct audsettings *as, void *drv_opaque); | ||||||
|     void   (*fini_in) (HWVoiceIn *hw); |     void (*fini_in) (HWVoiceIn *hw); | ||||||
|     size_t (*read)    (HWVoiceIn *hw, void *buf, size_t size); |     int  (*run_in)  (HWVoiceIn *hw); | ||||||
|     void  *(*get_buffer_in)(HWVoiceIn *hw, size_t *size); |     int  (*read)    (SWVoiceIn *sw, void *buf, int size); | ||||||
|     void   (*put_buffer_in)(HWVoiceIn *hw, void *buf, size_t size); |     int  (*ctl_in)  (HWVoiceIn *hw, int cmd, ...); | ||||||
|     void   (*enable_in)(HWVoiceIn *hw, bool enable); |  | ||||||
|     void   (*volume_in)(HWVoiceIn *hw, Volume *vol); |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| void *audio_generic_get_buffer_in(HWVoiceIn *hw, size_t *size); |  | ||||||
| void audio_generic_put_buffer_in(HWVoiceIn *hw, void *buf, size_t size); |  | ||||||
| void *audio_generic_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 capture_callback { | ||||||
|     struct audio_capture_ops ops; |     struct audio_capture_ops ops; | ||||||
|     void *opaque; |     void *opaque; | ||||||
| @@ -204,9 +191,8 @@ struct SWVoiceCap { | |||||||
|     QLIST_ENTRY (SWVoiceCap) entries; |     QLIST_ENTRY (SWVoiceCap) entries; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| typedef struct AudioState { | struct AudioState { | ||||||
|     struct audio_driver *drv; |     struct audio_driver *drv; | ||||||
|     Audiodev *dev; |  | ||||||
|     void *drv_opaque; |     void *drv_opaque; | ||||||
|  |  | ||||||
|     QEMUTimer *ts; |     QEMUTimer *ts; | ||||||
| @@ -217,39 +203,36 @@ typedef struct AudioState { | |||||||
|     int nb_hw_voices_out; |     int nb_hw_voices_out; | ||||||
|     int nb_hw_voices_in; |     int nb_hw_voices_in; | ||||||
|     int vm_running; |     int vm_running; | ||||||
|     int64_t period_ticks; | }; | ||||||
|  |  | ||||||
|     bool timer_running; |  | ||||||
|     uint64_t timer_last; |  | ||||||
|  |  | ||||||
|     QTAILQ_ENTRY(AudioState) list; |  | ||||||
| } AudioState; |  | ||||||
|  |  | ||||||
| extern const struct mixeng_volume nominal_volume; | extern const struct mixeng_volume nominal_volume; | ||||||
|  |  | ||||||
| extern const char *audio_prio_list[]; |  | ||||||
|  |  | ||||||
| void audio_driver_register(audio_driver *drv); | void audio_driver_register(audio_driver *drv); | ||||||
| audio_driver *audio_driver_lookup(const char *name); | audio_driver *audio_driver_lookup(const char *name); | ||||||
|  |  | ||||||
| void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as); | void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as); | ||||||
| void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len); | void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len); | ||||||
|  |  | ||||||
|  | int  audio_pcm_sw_write (SWVoiceOut *sw, void *buf, int len); | ||||||
|  | int  audio_pcm_hw_get_live_in (HWVoiceIn *hw); | ||||||
|  |  | ||||||
|  | int  audio_pcm_sw_read (SWVoiceIn *sw, void *buf, int len); | ||||||
|  |  | ||||||
|  | int audio_pcm_hw_clip_out (HWVoiceOut *hw, void *pcm_buf, | ||||||
|  |                            int live, int pending); | ||||||
|  |  | ||||||
| int audio_bug (const char *funcname, int cond); | int audio_bug (const char *funcname, int cond); | ||||||
| void *audio_calloc (const char *funcname, int nmemb, size_t size); | void *audio_calloc (const char *funcname, int nmemb, size_t size); | ||||||
|  |  | ||||||
| void audio_run(AudioState *s, const char *msg); | void audio_run (const char *msg); | ||||||
|  |  | ||||||
| typedef struct RateCtl { | #define VOICE_ENABLE 1 | ||||||
|     int64_t start_ticks; | #define VOICE_DISABLE 2 | ||||||
|     int64_t bytes_sent; | #define VOICE_VOLUME 3 | ||||||
| } RateCtl; |  | ||||||
|  |  | ||||||
| void audio_rate_start(RateCtl *rate); | #define VOICE_VOLUME_CAP (1 << VOICE_VOLUME) | ||||||
| size_t audio_rate_get_bytes(struct audio_pcm_info *info, RateCtl *rate, |  | ||||||
|                             size_t bytes_avail); |  | ||||||
|  |  | ||||||
| static inline size_t audio_ring_dist(size_t dst, size_t src, size_t len) | static inline int audio_ring_dist (int dst, int src, int len) | ||||||
| { | { | ||||||
|     return (dst >= src) ? (dst - src) : (len - src + dst); |     return (dst >= src) ? (dst - src) : (len - src + dst); | ||||||
| } | } | ||||||
| @@ -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) #n | ||||||
| #define AUDIO_STRINGIFY(n) AUDIO_STRINGIFY_(n) | #define AUDIO_STRINGIFY(n) AUDIO_STRINGIFY_(n) | ||||||
|  |  | ||||||
| typedef struct AudiodevListEntry { |  | ||||||
|     Audiodev *dev; |  | ||||||
|     QSIMPLEQ_ENTRY(AudiodevListEntry) next; |  | ||||||
| } AudiodevListEntry; |  | ||||||
|  |  | ||||||
| typedef QSIMPLEQ_HEAD(, AudiodevListEntry) AudiodevListHead; |  | ||||||
| AudiodevListHead audio_handle_legacy_opts(void); |  | ||||||
|  |  | ||||||
| void audio_free_audiodev_list(AudiodevListHead *head); |  | ||||||
|  |  | ||||||
| void audio_create_pdos(Audiodev *dev); |  | ||||||
| AudiodevPerDirectionOptions *audio_get_pdo_in(Audiodev *dev); |  | ||||||
| AudiodevPerDirectionOptions *audio_get_pdo_out(Audiodev *dev); |  | ||||||
|  |  | ||||||
| #endif /* QEMU_AUDIO_INT_H */ | #endif /* QEMU_AUDIO_INT_H */ | ||||||
|   | |||||||
| @@ -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
									
								
							
							
						
						
									
										174
									
								
								audio/audio_pt_int.c
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										22
									
								
								audio/audio_pt_int.h
									
									
									
									
									
										Normal 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 */ | ||||||
| @@ -36,9 +36,9 @@ | |||||||
| #define HWBUF hw->conv_buf | #define HWBUF hw->conv_buf | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| static void glue(audio_init_nb_voices_, TYPE)(AudioState *s, | static void glue (audio_init_nb_voices_, TYPE) (struct audio_driver *drv) | ||||||
|                                               struct audio_driver *drv) |  | ||||||
| { | { | ||||||
|  |     AudioState *s = &glob_audio_state; | ||||||
|     int max_voices = glue (drv->max_voices_, TYPE); |     int max_voices = glue (drv->max_voices_, TYPE); | ||||||
|     int voice_size = glue (drv->voice_size_, TYPE); |     int voice_size = glue (drv->voice_size_, TYPE); | ||||||
|  |  | ||||||
| @@ -71,24 +71,20 @@ static void glue(audio_init_nb_voices_, TYPE)(AudioState *s, | |||||||
|  |  | ||||||
| static void glue (audio_pcm_hw_free_resources_, TYPE) (HW *hw) | static void glue (audio_pcm_hw_free_resources_, TYPE) (HW *hw) | ||||||
| { | { | ||||||
|     g_free(hw->buf_emul); |  | ||||||
|     g_free (HWBUF); |     g_free (HWBUF); | ||||||
|     HWBUF = NULL; |     HWBUF = NULL; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void glue(audio_pcm_hw_alloc_resources_, TYPE)(HW *hw) | static int glue (audio_pcm_hw_alloc_resources_, TYPE) (HW *hw) | ||||||
| { | { | ||||||
|     if (glue(audio_get_pdo_, TYPE)(hw->s->dev)->mixing_engine) { |     HWBUF = audio_calloc(__func__, hw->samples, sizeof(struct st_sample)); | ||||||
|         size_t samples = hw->samples; |     if (!HWBUF) { | ||||||
|         if (audio_bug(__func__, samples == 0)) { |         dolog ("Could not allocate " NAME " buffer (%d samples)\n", | ||||||
|             dolog("Attempted to allocate empty buffer\n"); |                hw->samples); | ||||||
|         } |         return -1; | ||||||
|  |  | ||||||
|         HWBUF = g_malloc0(sizeof(STSampleBuffer) + sizeof(st_sample) * samples); |  | ||||||
|         HWBUF->size = samples; |  | ||||||
|     } else { |  | ||||||
|         HWBUF = NULL; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void glue (audio_pcm_sw_free_resources_, TYPE) (SW *sw) | static void glue (audio_pcm_sw_free_resources_, TYPE) (SW *sw) | ||||||
| @@ -107,11 +103,7 @@ static int glue (audio_pcm_sw_alloc_resources_, TYPE) (SW *sw) | |||||||
| { | { | ||||||
|     int samples; |     int samples; | ||||||
|  |  | ||||||
|     if (!glue(audio_get_pdo_, TYPE)(sw->s->dev)->mixing_engine) { |     samples = ((int64_t) sw->hw->samples << 32) / sw->ratio; | ||||||
|         return 0; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     samples = ((int64_t) sw->HWBUF->size << 32) / sw->ratio; |  | ||||||
|  |  | ||||||
|     sw->buf = audio_calloc(__func__, samples, sizeof(struct st_sample)); |     sw->buf = audio_calloc(__func__, samples, sizeof(struct st_sample)); | ||||||
|     if (!sw->buf) { |     if (!sw->buf) { | ||||||
| @@ -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) | static void glue (audio_pcm_hw_gc_, TYPE) (HW **hwp) | ||||||
| { | { | ||||||
|  |     AudioState *s = &glob_audio_state; | ||||||
|     HW *hw = *hwp; |     HW *hw = *hwp; | ||||||
|     AudioState *s = hw->s; |  | ||||||
|  |  | ||||||
|     if (!hw->sw_head.lh_first) { |     if (!hw->sw_head.lh_first) { | ||||||
| #ifdef DAC | #ifdef DAC | ||||||
| @@ -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; |     return hw ? hw->entries.le_next : glue (s->hw_head_, TYPE).lh_first; | ||||||
| } | } | ||||||
|  |  | ||||||
| static HW *glue(audio_pcm_hw_find_any_enabled_, TYPE)(AudioState *s, HW *hw) | static HW *glue (audio_pcm_hw_find_any_enabled_, TYPE) (HW *hw) | ||||||
| { | { | ||||||
|     while ((hw = glue(audio_pcm_hw_find_any_, TYPE)(s, hw))) { |     while ((hw = glue (audio_pcm_hw_find_any_, TYPE) (hw))) { | ||||||
|         if (hw->enabled) { |         if (hw->enabled) { | ||||||
|             return hw; |             return hw; | ||||||
|         } |         } | ||||||
| @@ -222,10 +215,12 @@ static HW *glue(audio_pcm_hw_find_any_enabled_, TYPE)(AudioState *s, HW *hw) | |||||||
|     return NULL; |     return NULL; | ||||||
| } | } | ||||||
|  |  | ||||||
| static HW *glue(audio_pcm_hw_find_specific_, TYPE)(AudioState *s, HW *hw, | static HW *glue (audio_pcm_hw_find_specific_, TYPE) ( | ||||||
|                                                    struct audsettings *as) |     HW *hw, | ||||||
|  |     struct audsettings *as | ||||||
|  |     ) | ||||||
| { | { | ||||||
|     while ((hw = glue(audio_pcm_hw_find_any_, TYPE)(s, hw))) { |     while ((hw = glue (audio_pcm_hw_find_any_, TYPE) (hw))) { | ||||||
|         if (audio_pcm_info_eq (&hw->info, as)) { |         if (audio_pcm_info_eq (&hw->info, as)) { | ||||||
|             return hw; |             return hw; | ||||||
|         } |         } | ||||||
| @@ -233,10 +228,10 @@ static HW *glue(audio_pcm_hw_find_specific_, TYPE)(AudioState *s, HW *hw, | |||||||
|     return NULL; |     return NULL; | ||||||
| } | } | ||||||
|  |  | ||||||
| static HW *glue(audio_pcm_hw_add_new_, TYPE)(AudioState *s, | static HW *glue (audio_pcm_hw_add_new_, TYPE) (struct audsettings *as) | ||||||
|                                              struct audsettings *as) |  | ||||||
| { | { | ||||||
|     HW *hw; |     HW *hw; | ||||||
|  |     AudioState *s = &glob_audio_state; | ||||||
|     struct audio_driver *drv = s->drv; |     struct audio_driver *drv = s->drv; | ||||||
|  |  | ||||||
|     if (!glue (s->nb_hw_voices_, TYPE)) { |     if (!glue (s->nb_hw_voices_, TYPE)) { | ||||||
| @@ -260,8 +255,8 @@ static HW *glue(audio_pcm_hw_add_new_, TYPE)(AudioState *s, | |||||||
|         return NULL; |         return NULL; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     hw->s = s; |  | ||||||
|     hw->pcm_ops = drv->pcm_ops; |     hw->pcm_ops = drv->pcm_ops; | ||||||
|  |     hw->ctl_caps = drv->ctl_caps; | ||||||
|  |  | ||||||
|     QLIST_INIT (&hw->sw_head); |     QLIST_INIT (&hw->sw_head); | ||||||
| #ifdef DAC | #ifdef DAC | ||||||
| @@ -272,7 +267,7 @@ static HW *glue(audio_pcm_hw_add_new_, TYPE)(AudioState *s, | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (audio_bug(__func__, hw->samples <= 0)) { |     if (audio_bug(__func__, hw->samples <= 0)) { | ||||||
|         dolog("hw->samples=%zd\n", hw->samples); |         dolog ("hw->samples=%d\n", hw->samples); | ||||||
|         goto err1; |         goto err1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -286,7 +281,9 @@ static HW *glue(audio_pcm_hw_add_new_, TYPE)(AudioState *s, | |||||||
|         [hw->info.swap_endianness] |         [hw->info.swap_endianness] | ||||||
|         [audio_bits_to_index (hw->info.bits)]; |         [audio_bits_to_index (hw->info.bits)]; | ||||||
|  |  | ||||||
|     glue(audio_pcm_hw_alloc_resources_, TYPE)(hw); |     if (glue (audio_pcm_hw_alloc_resources_, TYPE) (hw)) { | ||||||
|  |         goto err1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     QLIST_INSERT_HEAD (&s->glue (hw_head_, TYPE), hw, entries); |     QLIST_INSERT_HEAD (&s->glue (hw_head_, TYPE), hw, entries); | ||||||
|     glue (s->nb_hw_voices_, TYPE) -= 1; |     glue (s->nb_hw_voices_, TYPE) -= 1; | ||||||
| @@ -302,62 +299,31 @@ static HW *glue(audio_pcm_hw_add_new_, TYPE)(AudioState *s, | |||||||
|     return NULL; |     return NULL; | ||||||
| } | } | ||||||
|  |  | ||||||
| AudiodevPerDirectionOptions *glue(audio_get_pdo_, TYPE)(Audiodev *dev) | static HW *glue (audio_pcm_hw_add_, TYPE) (struct audsettings *as) | ||||||
| { |  | ||||||
|     switch (dev->driver) { |  | ||||||
|     case AUDIODEV_DRIVER_NONE: |  | ||||||
|         return dev->u.none.TYPE; |  | ||||||
|     case AUDIODEV_DRIVER_ALSA: |  | ||||||
|         return qapi_AudiodevAlsaPerDirectionOptions_base(dev->u.alsa.TYPE); |  | ||||||
|     case AUDIODEV_DRIVER_COREAUDIO: |  | ||||||
|         return qapi_AudiodevCoreaudioPerDirectionOptions_base( |  | ||||||
|             dev->u.coreaudio.TYPE); |  | ||||||
|     case AUDIODEV_DRIVER_DSOUND: |  | ||||||
|         return dev->u.dsound.TYPE; |  | ||||||
|     case AUDIODEV_DRIVER_OSS: |  | ||||||
|         return qapi_AudiodevOssPerDirectionOptions_base(dev->u.oss.TYPE); |  | ||||||
|     case AUDIODEV_DRIVER_PA: |  | ||||||
|         return qapi_AudiodevPaPerDirectionOptions_base(dev->u.pa.TYPE); |  | ||||||
|     case AUDIODEV_DRIVER_SDL: |  | ||||||
|         return dev->u.sdl.TYPE; |  | ||||||
|     case AUDIODEV_DRIVER_SPICE: |  | ||||||
|         return dev->u.spice.TYPE; |  | ||||||
|     case AUDIODEV_DRIVER_WAV: |  | ||||||
|         return dev->u.wav.TYPE; |  | ||||||
|  |  | ||||||
|     case AUDIODEV_DRIVER__MAX: |  | ||||||
|         break; |  | ||||||
|     } |  | ||||||
|     abort(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static HW *glue(audio_pcm_hw_add_, TYPE)(AudioState *s, struct audsettings *as) |  | ||||||
| { | { | ||||||
|     HW *hw; |     HW *hw; | ||||||
|     AudiodevPerDirectionOptions *pdo = glue(audio_get_pdo_, TYPE)(s->dev); |  | ||||||
|  |  | ||||||
|     if (!pdo->mixing_engine || pdo->fixed_settings) { |     if (glue (conf.fixed_, TYPE).enabled && glue (conf.fixed_, TYPE).greedy) { | ||||||
|         hw = glue(audio_pcm_hw_add_new_, TYPE)(s, as); |         hw = glue (audio_pcm_hw_add_new_, TYPE) (as); | ||||||
|         if (!pdo->mixing_engine || hw) { |         if (hw) { | ||||||
|             return hw; |             return hw; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     hw = glue(audio_pcm_hw_find_specific_, TYPE)(s, NULL, as); |     hw = glue (audio_pcm_hw_find_specific_, TYPE) (NULL, as); | ||||||
|     if (hw) { |     if (hw) { | ||||||
|         return hw; |         return hw; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     hw = glue(audio_pcm_hw_add_new_, TYPE)(s, as); |     hw = glue (audio_pcm_hw_add_new_, TYPE) (as); | ||||||
|     if (hw) { |     if (hw) { | ||||||
|         return hw; |         return hw; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return glue(audio_pcm_hw_find_any_, TYPE)(s, NULL); |     return glue (audio_pcm_hw_find_any_, TYPE) (NULL); | ||||||
| } | } | ||||||
|  |  | ||||||
| static SW *glue(audio_pcm_create_voice_pair_, TYPE)( | static SW *glue (audio_pcm_create_voice_pair_, TYPE) ( | ||||||
|     AudioState *s, |  | ||||||
|     const char *sw_name, |     const char *sw_name, | ||||||
|     struct audsettings *as |     struct audsettings *as | ||||||
|     ) |     ) | ||||||
| @@ -365,10 +331,9 @@ static SW *glue(audio_pcm_create_voice_pair_, TYPE)( | |||||||
|     SW *sw; |     SW *sw; | ||||||
|     HW *hw; |     HW *hw; | ||||||
|     struct audsettings hw_as; |     struct audsettings hw_as; | ||||||
|     AudiodevPerDirectionOptions *pdo = glue(audio_get_pdo_, TYPE)(s->dev); |  | ||||||
|  |  | ||||||
|     if (pdo->fixed_settings) { |     if (glue (conf.fixed_, TYPE).enabled) { | ||||||
|         hw_as = audiodev_to_audsettings(pdo); |         hw_as = glue (conf.fixed_, TYPE).settings; | ||||||
|     } |     } | ||||||
|     else { |     else { | ||||||
|         hw_as = *as; |         hw_as = *as; | ||||||
| @@ -380,9 +345,8 @@ static SW *glue(audio_pcm_create_voice_pair_, TYPE)( | |||||||
|                sw_name ? sw_name : "unknown", sizeof (*sw)); |                sw_name ? sw_name : "unknown", sizeof (*sw)); | ||||||
|         goto err1; |         goto err1; | ||||||
|     } |     } | ||||||
|     sw->s = s; |  | ||||||
|  |  | ||||||
|     hw = glue(audio_pcm_hw_add_, TYPE)(s, &hw_as); |     hw = glue (audio_pcm_hw_add_, TYPE) (&hw_as); | ||||||
|     if (!hw) { |     if (!hw) { | ||||||
|         goto err2; |         goto err2; | ||||||
|     } |     } | ||||||
| @@ -433,8 +397,7 @@ SW *glue (AUD_open_, TYPE) ( | |||||||
|     struct audsettings *as |     struct audsettings *as | ||||||
|     ) |     ) | ||||||
| { | { | ||||||
|     AudioState *s; |     AudioState *s = &glob_audio_state; | ||||||
|     AudiodevPerDirectionOptions *pdo; |  | ||||||
|  |  | ||||||
|     if (audio_bug(__func__, !card || !name || !callback_fn || !as)) { |     if (audio_bug(__func__, !card || !name || !callback_fn || !as)) { | ||||||
|         dolog ("card=%p name=%p callback_fn=%p as=%p\n", |         dolog ("card=%p name=%p callback_fn=%p as=%p\n", | ||||||
| @@ -442,9 +405,6 @@ SW *glue (AUD_open_, TYPE) ( | |||||||
|         goto fail; |         goto fail; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     s = card->state; |  | ||||||
|     pdo = glue(audio_get_pdo_, TYPE)(s->dev); |  | ||||||
|  |  | ||||||
|     ldebug ("open %s, freq %d, nchannels %d, fmt %d\n", |     ldebug ("open %s, freq %d, nchannels %d, fmt %d\n", | ||||||
|             name, as->freq, as->nchannels, as->fmt); |             name, as->freq, as->nchannels, as->fmt); | ||||||
|  |  | ||||||
| @@ -462,7 +422,7 @@ SW *glue (AUD_open_, TYPE) ( | |||||||
|         return sw; |         return sw; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (!pdo->fixed_settings && sw) { |     if (!glue (conf.fixed_, TYPE).enabled && sw) { | ||||||
|         glue (AUD_close_, TYPE) (card, sw); |         glue (AUD_close_, TYPE) (card, sw); | ||||||
|         sw = NULL; |         sw = NULL; | ||||||
|     } |     } | ||||||
| @@ -482,7 +442,7 @@ SW *glue (AUD_open_, TYPE) ( | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     else { |     else { | ||||||
|         sw = glue(audio_pcm_create_voice_pair_, TYPE)(s, name, as); |         sw = glue (audio_pcm_create_voice_pair_, TYPE) (name, as); | ||||||
|         if (!sw) { |         if (!sw) { | ||||||
|             dolog ("Failed to create voice `%s'\n", name); |             dolog ("Failed to create voice `%s'\n", name); | ||||||
|             return NULL; |             return NULL; | ||||||
|   | |||||||
| @@ -24,20 +24,20 @@ int waveformat_from_audio_settings (WAVEFORMATEX *wfx, | |||||||
|     wfx->cbSize = 0; |     wfx->cbSize = 0; | ||||||
|  |  | ||||||
|     switch (as->fmt) { |     switch (as->fmt) { | ||||||
|     case AUDIO_FORMAT_S8: |     case AUD_FMT_S8: | ||||||
|     case AUDIO_FORMAT_U8: |     case AUD_FMT_U8: | ||||||
|         wfx->wBitsPerSample = 8; |         wfx->wBitsPerSample = 8; | ||||||
|         break; |         break; | ||||||
|  |  | ||||||
|     case AUDIO_FORMAT_S16: |     case AUD_FMT_S16: | ||||||
|     case AUDIO_FORMAT_U16: |     case AUD_FMT_U16: | ||||||
|         wfx->wBitsPerSample = 16; |         wfx->wBitsPerSample = 16; | ||||||
|         wfx->nAvgBytesPerSec <<= 1; |         wfx->nAvgBytesPerSec <<= 1; | ||||||
|         wfx->nBlockAlign <<= 1; |         wfx->nBlockAlign <<= 1; | ||||||
|         break; |         break; | ||||||
|  |  | ||||||
|     case AUDIO_FORMAT_S32: |     case AUD_FMT_S32: | ||||||
|     case AUDIO_FORMAT_U32: |     case AUD_FMT_U32: | ||||||
|         wfx->wBitsPerSample = 32; |         wfx->wBitsPerSample = 32; | ||||||
|         wfx->nAvgBytesPerSec <<= 2; |         wfx->nAvgBytesPerSec <<= 2; | ||||||
|         wfx->nBlockAlign <<= 2; |         wfx->nBlockAlign <<= 2; | ||||||
| @@ -85,15 +85,15 @@ int waveformat_to_audio_settings (WAVEFORMATEX *wfx, | |||||||
|  |  | ||||||
|     switch (wfx->wBitsPerSample) { |     switch (wfx->wBitsPerSample) { | ||||||
|     case 8: |     case 8: | ||||||
|         as->fmt = AUDIO_FORMAT_U8; |         as->fmt = AUD_FMT_U8; | ||||||
|         break; |         break; | ||||||
|  |  | ||||||
|     case 16: |     case 16: | ||||||
|         as->fmt = AUDIO_FORMAT_S16; |         as->fmt = AUD_FMT_S16; | ||||||
|         break; |         break; | ||||||
|  |  | ||||||
|     case 32: |     case 32: | ||||||
|         as->fmt = AUDIO_FORMAT_S32; |         as->fmt = AUD_FMT_S32; | ||||||
|         break; |         break; | ||||||
|  |  | ||||||
|     default: |     default: | ||||||
|   | |||||||
| @@ -26,7 +26,7 @@ | |||||||
| #include <CoreAudio/CoreAudio.h> | #include <CoreAudio/CoreAudio.h> | ||||||
| #include <pthread.h>            /* pthread_X */ | #include <pthread.h>            /* pthread_X */ | ||||||
|  |  | ||||||
| #include "qemu/module.h" | #include "qemu-common.h" | ||||||
| #include "audio.h" | #include "audio.h" | ||||||
|  |  | ||||||
| #define AUDIO_CAP "coreaudio" | #define AUDIO_CAP "coreaudio" | ||||||
| @@ -36,6 +36,11 @@ | |||||||
| #define MAC_OS_X_VERSION_10_6 1060 | #define MAC_OS_X_VERSION_10_6 1060 | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | typedef struct { | ||||||
|  |     int buffer_frames; | ||||||
|  |     int nbuffers; | ||||||
|  | } CoreaudioConf; | ||||||
|  |  | ||||||
| typedef struct coreaudioVoiceOut { | typedef struct coreaudioVoiceOut { | ||||||
|     HWVoiceOut hw; |     HWVoiceOut hw; | ||||||
|     pthread_mutex_t mutex; |     pthread_mutex_t mutex; | ||||||
| @@ -43,6 +48,9 @@ typedef struct coreaudioVoiceOut { | |||||||
|     UInt32 audioDevicePropertyBufferFrameSize; |     UInt32 audioDevicePropertyBufferFrameSize; | ||||||
|     AudioStreamBasicDescription outputStreamBasicDescription; |     AudioStreamBasicDescription outputStreamBasicDescription; | ||||||
|     AudioDeviceIOProcID ioprocid; |     AudioDeviceIOProcID ioprocid; | ||||||
|  |     int live; | ||||||
|  |     int decr; | ||||||
|  |     int rpos; | ||||||
| } coreaudioVoiceOut; | } coreaudioVoiceOut; | ||||||
|  |  | ||||||
| #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 | #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 | ||||||
| @@ -394,29 +402,31 @@ static int coreaudio_unlock (coreaudioVoiceOut *core, const char *fn_name) | |||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| #define COREAUDIO_WRAPPER_FUNC(name, ret_type, args_decl, args) \ | static int coreaudio_run_out (HWVoiceOut *hw, int live) | ||||||
|     static ret_type glue(coreaudio_, name)args_decl             \ | { | ||||||
|     {                                                           \ |     int decr; | ||||||
|         coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;     \ |     coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; | ||||||
|         ret_type ret;                                           \ |  | ||||||
|                                                                 \ |     if (coreaudio_lock (core, "coreaudio_run_out")) { | ||||||
|         if (coreaudio_lock(core, "coreaudio_" #name)) {         \ |         return 0; | ||||||
|             return 0;                                           \ |  | ||||||
|         }                                                       \ |  | ||||||
|                                                                 \ |  | ||||||
|         ret = glue(audio_generic_, name)args;                   \ |  | ||||||
|                                                                 \ |  | ||||||
|         coreaudio_unlock(core, "coreaudio_" #name);             \ |  | ||||||
|         return ret;                                             \ |  | ||||||
|     } |     } | ||||||
| COREAUDIO_WRAPPER_FUNC(get_buffer_out, void *, (HWVoiceOut *hw, size_t *size), |  | ||||||
|                        (hw, size)) |     if (core->decr > live) { | ||||||
| COREAUDIO_WRAPPER_FUNC(put_buffer_out_nowrite, size_t, |         ldebug ("core->decr %d live %d core->live %d\n", | ||||||
|                        (HWVoiceOut *hw, void *buf, size_t size), |                 core->decr, | ||||||
|                        (hw, buf, size)) |                 live, | ||||||
| COREAUDIO_WRAPPER_FUNC(write, size_t, (HWVoiceOut *hw, void *buf, size_t size), |                 core->live); | ||||||
|                        (hw, buf, size)) |     } | ||||||
| #undef COREAUDIO_WRAPPER_FUNC |  | ||||||
|  |     decr = audio_MIN (core->decr, live); | ||||||
|  |     core->decr -= decr; | ||||||
|  |  | ||||||
|  |     core->live = live - decr; | ||||||
|  |     hw->rpos = core->rpos; | ||||||
|  |  | ||||||
|  |     coreaudio_unlock (core, "coreaudio_run_out"); | ||||||
|  |     return decr; | ||||||
|  | } | ||||||
|  |  | ||||||
| /* callback to feed audiooutput buffer */ | /* callback to feed audiooutput buffer */ | ||||||
| static OSStatus audioDeviceIOProc( | static OSStatus audioDeviceIOProc( | ||||||
| @@ -428,11 +438,19 @@ static OSStatus audioDeviceIOProc( | |||||||
|     const AudioTimeStamp* inOutputTime, |     const AudioTimeStamp* inOutputTime, | ||||||
|     void* hwptr) |     void* hwptr) | ||||||
| { | { | ||||||
|     UInt32 frameCount, pending_frames; |     UInt32 frame, frameCount; | ||||||
|     void *out = outOutputData->mBuffers[0].mData; |     float *out = outOutputData->mBuffers[0].mData; | ||||||
|     HWVoiceOut *hw = hwptr; |     HWVoiceOut *hw = hwptr; | ||||||
|     coreaudioVoiceOut *core = (coreaudioVoiceOut *) hwptr; |     coreaudioVoiceOut *core = (coreaudioVoiceOut *) hwptr; | ||||||
|     size_t len; |     int rpos, live; | ||||||
|  |     struct st_sample *src; | ||||||
|  | #ifndef FLOAT_MIXENG | ||||||
|  | #ifdef RECIPROCAL | ||||||
|  |     const float scale = 1.f / UINT_MAX; | ||||||
|  | #else | ||||||
|  |     const float scale = UINT_MAX; | ||||||
|  | #endif | ||||||
|  | #endif | ||||||
|  |  | ||||||
|     if (coreaudio_lock (core, "audioDeviceIOProc")) { |     if (coreaudio_lock (core, "audioDeviceIOProc")) { | ||||||
|         inInputTime = 0; |         inInputTime = 0; | ||||||
| @@ -440,49 +458,45 @@ static OSStatus audioDeviceIOProc( | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     frameCount = core->audioDevicePropertyBufferFrameSize; |     frameCount = core->audioDevicePropertyBufferFrameSize; | ||||||
|     pending_frames = hw->pending_emul / hw->info.bytes_per_frame; |     live = core->live; | ||||||
|  |  | ||||||
|     /* if there are not enough samples, set signal and return */ |     /* if there are not enough samples, set signal and return */ | ||||||
|     if (pending_frames < frameCount) { |     if (live < frameCount) { | ||||||
|         inInputTime = 0; |         inInputTime = 0; | ||||||
|         coreaudio_unlock (core, "audioDeviceIOProc(empty)"); |         coreaudio_unlock (core, "audioDeviceIOProc(empty)"); | ||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     len = frameCount * hw->info.bytes_per_frame; |     rpos = core->rpos; | ||||||
|     while (len) { |     src = hw->mix_buf + rpos; | ||||||
|         size_t write_len; |  | ||||||
|         ssize_t start = ((ssize_t) hw->pos_emul) - hw->pending_emul; |  | ||||||
|         if (start < 0) { |  | ||||||
|             start += hw->size_emul; |  | ||||||
|         } |  | ||||||
|         assert(start >= 0 && start < hw->size_emul); |  | ||||||
|  |  | ||||||
|         write_len = MIN(MIN(hw->pending_emul, len), |     /* fill buffer */ | ||||||
|                         hw->size_emul - start); |     for (frame = 0; frame < frameCount; frame++) { | ||||||
|  | #ifdef FLOAT_MIXENG | ||||||
|         memcpy(out, hw->buf_emul + start, write_len); |         *out++ = src[frame].l; /* left channel */ | ||||||
|         hw->pending_emul -= write_len; |         *out++ = src[frame].r; /* right channel */ | ||||||
|         len -= write_len; | #else | ||||||
|         out += write_len; | #ifdef RECIPROCAL | ||||||
|  |         *out++ = src[frame].l * scale; /* left channel */ | ||||||
|  |         *out++ = src[frame].r * scale; /* right channel */ | ||||||
|  | #else | ||||||
|  |         *out++ = src[frame].l / scale; /* left channel */ | ||||||
|  |         *out++ = src[frame].r / scale; /* right channel */ | ||||||
|  | #endif | ||||||
|  | #endif | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     rpos = (rpos + frameCount) % hw->samples; | ||||||
|  |     core->decr += frameCount; | ||||||
|  |     core->rpos = rpos; | ||||||
|  |  | ||||||
|     coreaudio_unlock (core, "audioDeviceIOProc"); |     coreaudio_unlock (core, "audioDeviceIOProc"); | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| static UInt32 coreaudio_get_flags(struct audio_pcm_info *info, | static int coreaudio_write (SWVoiceOut *sw, void *buf, int len) | ||||||
|                                   struct audsettings *as) |  | ||||||
| { | { | ||||||
|     UInt32 flags = info->sign ? kAudioFormatFlagIsSignedInteger : 0; |     return audio_pcm_sw_write (sw, buf, len); | ||||||
|     if (as->endianness) { /* 0 = little, 1 = big */ |  | ||||||
|         flags |= kAudioFormatFlagIsBigEndian; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (flags == 0) { /* must not be 0 */ |  | ||||||
|         flags = kAudioFormatFlagsAreAllClear; |  | ||||||
|     } |  | ||||||
|     return flags; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as, | 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; |     int err; | ||||||
|     const char *typ = "playback"; |     const char *typ = "playback"; | ||||||
|     AudioValueRange frameRange; |     AudioValueRange frameRange; | ||||||
|     Audiodev *dev = drv_opaque; |     CoreaudioConf *conf = drv_opaque; | ||||||
|     AudiodevCoreaudioPerDirectionOptions *cpdo = dev->u.coreaudio.out; |  | ||||||
|     int frames; |  | ||||||
|  |  | ||||||
|     /* create mutex */ |     /* create mutex */ | ||||||
|     err = pthread_mutex_init(&core->mutex, NULL); |     err = pthread_mutex_init(&core->mutex, NULL); | ||||||
| @@ -526,17 +538,16 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as, | |||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     frames = audio_buffer_frames( |     if (frameRange.mMinimum > conf->buffer_frames) { | ||||||
|         qapi_AudiodevCoreaudioPerDirectionOptions_base(cpdo), as, 11610); |  | ||||||
|     if (frameRange.mMinimum > frames) { |  | ||||||
|         core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMinimum; |         core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMinimum; | ||||||
|         dolog ("warning: Upsizing Buffer Frames to %f\n", frameRange.mMinimum); |         dolog ("warning: Upsizing Buffer Frames to %f\n", frameRange.mMinimum); | ||||||
|     } else if (frameRange.mMaximum < frames) { |     } | ||||||
|  |     else if (frameRange.mMaximum < conf->buffer_frames) { | ||||||
|         core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMaximum; |         core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMaximum; | ||||||
|         dolog ("warning: Downsizing Buffer Frames to %f\n", frameRange.mMaximum); |         dolog ("warning: Downsizing Buffer Frames to %f\n", frameRange.mMaximum); | ||||||
|     } |     } | ||||||
|     else { |     else { | ||||||
|         core->audioDevicePropertyBufferFrameSize = frames; |         core->audioDevicePropertyBufferFrameSize = conf->buffer_frames; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* set Buffer Frame Size */ |     /* set Buffer Frame Size */ | ||||||
| @@ -557,8 +568,7 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as, | |||||||
|                            "Could not get device buffer frame size\n"); |                            "Could not get device buffer frame size\n"); | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|     hw->samples = (cpdo->has_buffer_count ? cpdo->buffer_count : 4) * |     hw->samples = conf->nbuffers * core->audioDevicePropertyBufferFrameSize; | ||||||
|         core->audioDevicePropertyBufferFrameSize; |  | ||||||
|  |  | ||||||
|     /* get StreamFormat */ |     /* get StreamFormat */ | ||||||
|     status = coreaudio_get_streamformat(core->outputDeviceID, |     status = coreaudio_get_streamformat(core->outputDeviceID, | ||||||
| @@ -572,16 +582,6 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as, | |||||||
|  |  | ||||||
|     /* set Samplerate */ |     /* set Samplerate */ | ||||||
|     core->outputStreamBasicDescription.mSampleRate = (Float64) as->freq; |     core->outputStreamBasicDescription.mSampleRate = (Float64) as->freq; | ||||||
|     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, |     status = coreaudio_set_streamformat(core->outputDeviceID, | ||||||
|                                         &core->outputStreamBasicDescription); |                                         &core->outputStreamBasicDescription); | ||||||
|     if (status != kAudioHardwareNoError) { |     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; |     OSStatus status; | ||||||
|     coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; |     coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; | ||||||
|  |  | ||||||
|     if (enable) { |     switch (cmd) { | ||||||
|  |     case VOICE_ENABLE: | ||||||
|         /* start playback */ |         /* start playback */ | ||||||
|         if (!isPlaying(core->outputDeviceID)) { |         if (!isPlaying(core->outputDeviceID)) { | ||||||
|             status = AudioDeviceStart(core->outputDeviceID, core->ioprocid); |             status = AudioDeviceStart(core->outputDeviceID, core->ioprocid); | ||||||
| @@ -661,7 +662,9 @@ static void coreaudio_enable_out(HWVoiceOut *hw, bool enable) | |||||||
|                 coreaudio_logerr (status, "Could not resume playback\n"); |                 coreaudio_logerr (status, "Could not resume playback\n"); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } else { |         break; | ||||||
|  |  | ||||||
|  |     case VOICE_DISABLE: | ||||||
|         /* stop playback */ |         /* stop playback */ | ||||||
|         if (!audio_is_cleaning_up()) { |         if (!audio_is_cleaning_up()) { | ||||||
|             if (isPlaying(core->outputDeviceID)) { |             if (isPlaying(core->outputDeviceID)) { | ||||||
| @@ -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) | static void coreaudio_audio_fini (void *opaque) | ||||||
| { | { | ||||||
|  |     g_free(opaque); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static struct audio_option coreaudio_options[] = { | ||||||
|  |     { | ||||||
|  |         .name  = "BUFFER_SIZE", | ||||||
|  |         .tag   = AUD_OPT_INT, | ||||||
|  |         .valp  = &glob_conf.buffer_frames, | ||||||
|  |         .descr = "Size of the buffer in frames" | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |         .name  = "BUFFER_COUNT", | ||||||
|  |         .tag   = AUD_OPT_INT, | ||||||
|  |         .valp  = &glob_conf.nbuffers, | ||||||
|  |         .descr = "Number of buffers" | ||||||
|  |     }, | ||||||
|  |     { /* End of list */ } | ||||||
|  | }; | ||||||
|  |  | ||||||
| static struct audio_pcm_ops coreaudio_pcm_ops = { | static struct audio_pcm_ops coreaudio_pcm_ops = { | ||||||
|     .init_out = coreaudio_init_out, |     .init_out = coreaudio_init_out, | ||||||
|     .fini_out = coreaudio_fini_out, |     .fini_out = coreaudio_fini_out, | ||||||
|  |     .run_out  = coreaudio_run_out, | ||||||
|     .write    = coreaudio_write, |     .write    = coreaudio_write, | ||||||
|     .get_buffer_out = coreaudio_get_buffer_out, |     .ctl_out  = coreaudio_ctl_out | ||||||
|     .put_buffer_out = coreaudio_put_buffer_out_nowrite, |  | ||||||
|     .enable_out = coreaudio_enable_out |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| static struct audio_driver coreaudio_audio_driver = { | static struct audio_driver coreaudio_audio_driver = { | ||||||
|     .name           = "coreaudio", |     .name           = "coreaudio", | ||||||
|     .descr          = "CoreAudio http://developer.apple.com/audio/coreaudio.html", |     .descr          = "CoreAudio http://developer.apple.com/audio/coreaudio.html", | ||||||
|  |     .options        = coreaudio_options, | ||||||
|     .init           = coreaudio_audio_init, |     .init           = coreaudio_audio_init, | ||||||
|     .fini           = coreaudio_audio_fini, |     .fini           = coreaudio_audio_fini, | ||||||
|     .pcm_ops        = &coreaudio_pcm_ops, |     .pcm_ops        = &coreaudio_pcm_ops, | ||||||
|   | |||||||
| @@ -29,8 +29,6 @@ | |||||||
| #define BUFPTR LPDIRECTSOUNDCAPTUREBUFFER | #define BUFPTR LPDIRECTSOUNDCAPTUREBUFFER | ||||||
| #define FIELD dsound_capture_buffer | #define FIELD dsound_capture_buffer | ||||||
| #define FIELD2 dsound_capture | #define FIELD2 dsound_capture | ||||||
| #define HWVOICE HWVoiceIn |  | ||||||
| #define DSOUNDVOICE DSoundVoiceIn |  | ||||||
| #else | #else | ||||||
| #define NAME "playback buffer" | #define NAME "playback buffer" | ||||||
| #define NAME2 "DirectSound" | #define NAME2 "DirectSound" | ||||||
| @@ -39,8 +37,6 @@ | |||||||
| #define BUFPTR LPDIRECTSOUNDBUFFER | #define BUFPTR LPDIRECTSOUNDBUFFER | ||||||
| #define FIELD dsound_buffer | #define FIELD dsound_buffer | ||||||
| #define FIELD2 dsound | #define FIELD2 dsound | ||||||
| #define HWVOICE HWVoiceOut |  | ||||||
| #define DSOUNDVOICE DSoundVoiceOut |  | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| static int glue (dsound_unlock_, TYPE) ( | static int glue (dsound_unlock_, TYPE) ( | ||||||
| @@ -76,6 +72,8 @@ static int glue (dsound_lock_, TYPE) ( | |||||||
|     ) |     ) | ||||||
| { | { | ||||||
|     HRESULT hr; |     HRESULT hr; | ||||||
|  |     LPVOID p1 = NULL, p2 = NULL; | ||||||
|  |     DWORD blen1 = 0, blen2 = 0; | ||||||
|     DWORD flag; |     DWORD flag; | ||||||
|  |  | ||||||
| #ifdef DSBTYPE_IN | #ifdef DSBTYPE_IN | ||||||
| @@ -83,7 +81,7 @@ static int glue (dsound_lock_, TYPE) ( | |||||||
| #else | #else | ||||||
|     flag = entire ? DSBLOCK_ENTIREBUFFER : 0; |     flag = entire ? DSBLOCK_ENTIREBUFFER : 0; | ||||||
| #endif | #endif | ||||||
|     hr = glue(IFACE, _Lock)(buf, pos, len, p1p, blen1p, p2p, blen2p, flag); |     hr = glue(IFACE, _Lock)(buf, pos, len, &p1, &blen1, &p2, &blen2, flag); | ||||||
|  |  | ||||||
|     if (FAILED (hr)) { |     if (FAILED (hr)) { | ||||||
| #ifndef DSBTYPE_IN | #ifndef DSBTYPE_IN | ||||||
| @@ -98,34 +96,34 @@ static int glue (dsound_lock_, TYPE) ( | |||||||
|         goto fail; |         goto fail; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if ((p1p && *p1p && (*blen1p % info->bytes_per_frame)) || |     if ((p1 && (blen1 & info->align)) || (p2 && (blen2 & info->align))) { | ||||||
|         (p2p && *p2p && (*blen2p % info->bytes_per_frame))) { |         dolog ("DirectSound returned misaligned buffer %ld %ld\n", | ||||||
|         dolog("DirectSound returned misaligned buffer %ld %ld\n", |                blen1, blen2); | ||||||
|               *blen1p, *blen2p); |         glue (dsound_unlock_, TYPE) (buf, p1, p2, blen1, blen2); | ||||||
|         glue(dsound_unlock_, TYPE)(buf, *p1p, p2p ? *p2p : NULL, *blen1p, |  | ||||||
|                                    blen2p ? *blen2p : 0); |  | ||||||
|         goto fail; |         goto fail; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (p1p && !*p1p && *blen1p) { |     if (!p1 && blen1) { | ||||||
|         dolog("warning: !p1 && blen1=%ld\n", *blen1p); |         dolog ("warning: !p1 && blen1=%ld\n", blen1); | ||||||
|         *blen1p = 0; |         blen1 = 0; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (p2p && !*p2p && *blen2p) { |     if (!p2 && blen2) { | ||||||
|         dolog("warning: !p2 && blen2=%ld\n", *blen2p); |         dolog ("warning: !p2 && blen2=%ld\n", blen2); | ||||||
|         *blen2p = 0; |         blen2 = 0; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     *p1p = p1; | ||||||
|  |     *p2p = p2; | ||||||
|  |     *blen1p = blen1; | ||||||
|  |     *blen2p = blen2; | ||||||
|     return 0; |     return 0; | ||||||
|  |  | ||||||
|  fail: |  fail: | ||||||
|     *p1p = NULL - 1; |     *p1p = NULL - 1; | ||||||
|  |     *p2p = NULL - 1; | ||||||
|     *blen1p = -1; |     *blen1p = -1; | ||||||
|     if (p2p) { |     *blen2p = -1; | ||||||
|         *p2p = NULL - 1; |  | ||||||
|         *blen2p = -1; |  | ||||||
|     } |  | ||||||
|     return -1; |     return -1; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -169,18 +167,17 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as, | |||||||
|     dsound *s = drv_opaque; |     dsound *s = drv_opaque; | ||||||
|     WAVEFORMATEX wfx; |     WAVEFORMATEX wfx; | ||||||
|     struct audsettings obt_as; |     struct audsettings obt_as; | ||||||
|  |     DSoundConf *conf = &s->conf; | ||||||
| #ifdef DSBTYPE_IN | #ifdef DSBTYPE_IN | ||||||
|     const char *typ = "ADC"; |     const char *typ = "ADC"; | ||||||
|     DSoundVoiceIn *ds = (DSoundVoiceIn *) hw; |     DSoundVoiceIn *ds = (DSoundVoiceIn *) hw; | ||||||
|     DSCBUFFERDESC bd; |     DSCBUFFERDESC bd; | ||||||
|     DSCBCAPS bc; |     DSCBCAPS bc; | ||||||
|     AudiodevPerDirectionOptions *pdo = s->dev->u.dsound.in; |  | ||||||
| #else | #else | ||||||
|     const char *typ = "DAC"; |     const char *typ = "DAC"; | ||||||
|     DSoundVoiceOut *ds = (DSoundVoiceOut *) hw; |     DSoundVoiceOut *ds = (DSoundVoiceOut *) hw; | ||||||
|     DSBUFFERDESC bd; |     DSBUFFERDESC bd; | ||||||
|     DSBCAPS bc; |     DSBCAPS bc; | ||||||
|     AudiodevPerDirectionOptions *pdo = s->dev->u.dsound.out; |  | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|     if (!s->FIELD2) { |     if (!s->FIELD2) { | ||||||
| @@ -196,8 +193,8 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as, | |||||||
|     memset (&bd, 0, sizeof (bd)); |     memset (&bd, 0, sizeof (bd)); | ||||||
|     bd.dwSize = sizeof (bd); |     bd.dwSize = sizeof (bd); | ||||||
|     bd.lpwfxFormat = &wfx; |     bd.lpwfxFormat = &wfx; | ||||||
|     bd.dwBufferBytes = audio_buffer_bytes(pdo, as, 92880); |  | ||||||
| #ifdef DSBTYPE_IN | #ifdef DSBTYPE_IN | ||||||
|  |     bd.dwBufferBytes = conf->bufsize_in; | ||||||
|     hr = IDirectSoundCapture_CreateCaptureBuffer ( |     hr = IDirectSoundCapture_CreateCaptureBuffer ( | ||||||
|         s->dsound_capture, |         s->dsound_capture, | ||||||
|         &bd, |         &bd, | ||||||
| @@ -206,6 +203,7 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as, | |||||||
|         ); |         ); | ||||||
| #else | #else | ||||||
|     bd.dwFlags = DSBCAPS_STICKYFOCUS | DSBCAPS_GETCURRENTPOSITION2; |     bd.dwFlags = DSBCAPS_STICKYFOCUS | DSBCAPS_GETCURRENTPOSITION2; | ||||||
|  |     bd.dwBufferBytes = conf->bufsize_out; | ||||||
|     hr = IDirectSound_CreateSoundBuffer ( |     hr = IDirectSound_CreateSoundBuffer ( | ||||||
|         s->dsound, |         s->dsound, | ||||||
|         &bd, |         &bd, | ||||||
| @@ -244,22 +242,25 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as, | |||||||
|         goto fail0; |         goto fail0; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     ds->first_time = 1; | ||||||
|     obt_as.endianness = 0; |     obt_as.endianness = 0; | ||||||
|     audio_pcm_init_info (&hw->info, &obt_as); |     audio_pcm_init_info (&hw->info, &obt_as); | ||||||
|  |  | ||||||
|     if (bc.dwBufferBytes % hw->info.bytes_per_frame) { |     if (bc.dwBufferBytes & hw->info.align) { | ||||||
|         dolog ( |         dolog ( | ||||||
|             "GetCaps returned misaligned buffer size %ld, alignment %d\n", |             "GetCaps returned misaligned buffer size %ld, alignment %d\n", | ||||||
|             bc.dwBufferBytes, hw->info.bytes_per_frame |             bc.dwBufferBytes, hw->info.align + 1 | ||||||
|             ); |             ); | ||||||
|     } |     } | ||||||
|     hw->size_emul = bc.dwBufferBytes; |     hw->samples = bc.dwBufferBytes >> hw->info.shift; | ||||||
|     hw->samples = bc.dwBufferBytes / hw->info.bytes_per_frame; |  | ||||||
|     ds->s = s; |     ds->s = s; | ||||||
|  |  | ||||||
| #ifdef DEBUG_DSOUND | #ifdef DEBUG_DSOUND | ||||||
|     dolog ("caps %ld, desc %ld\n", |     dolog ("caps %ld, desc %ld\n", | ||||||
|            bc.dwBufferBytes, bd.dwBufferBytes); |            bc.dwBufferBytes, bd.dwBufferBytes); | ||||||
|  |  | ||||||
|  |     dolog ("bufsize %d, freq %d, chan %d, fmt %d\n", | ||||||
|  |            hw->bufsize, settings.freq, settings.nchannels, settings.fmt); | ||||||
| #endif | #endif | ||||||
|     return 0; |     return 0; | ||||||
|  |  | ||||||
| @@ -275,5 +276,3 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as, | |||||||
| #undef BUFPTR | #undef BUFPTR | ||||||
| #undef FIELD | #undef FIELD | ||||||
| #undef FIELD2 | #undef FIELD2 | ||||||
| #undef HWVOICE |  | ||||||
| #undef DSOUNDVOICE |  | ||||||
|   | |||||||
| @@ -27,12 +27,11 @@ | |||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #include "qemu/osdep.h" | #include "qemu/osdep.h" | ||||||
|  | #include "qemu-common.h" | ||||||
| #include "audio.h" | #include "audio.h" | ||||||
|  |  | ||||||
| #define AUDIO_CAP "dsound" | #define AUDIO_CAP "dsound" | ||||||
| #include "audio_int.h" | #include "audio_int.h" | ||||||
| #include "qemu/host-utils.h" |  | ||||||
| #include "qemu/module.h" |  | ||||||
|  |  | ||||||
| #include <windows.h> | #include <windows.h> | ||||||
| #include <mmsystem.h> | #include <mmsystem.h> | ||||||
| @@ -43,21 +42,35 @@ | |||||||
|  |  | ||||||
| /* #define DEBUG_DSOUND */ | /* #define DEBUG_DSOUND */ | ||||||
|  |  | ||||||
|  | typedef struct { | ||||||
|  |     int bufsize_in; | ||||||
|  |     int bufsize_out; | ||||||
|  |     int latency_millis; | ||||||
|  | } DSoundConf; | ||||||
|  |  | ||||||
| typedef struct { | typedef struct { | ||||||
|     LPDIRECTSOUND dsound; |     LPDIRECTSOUND dsound; | ||||||
|     LPDIRECTSOUNDCAPTURE dsound_capture; |     LPDIRECTSOUNDCAPTURE dsound_capture; | ||||||
|     struct audsettings settings; |     struct audsettings settings; | ||||||
|     Audiodev *dev; |     DSoundConf conf; | ||||||
| } dsound; | } dsound; | ||||||
|  |  | ||||||
| typedef struct { | typedef struct { | ||||||
|     HWVoiceOut hw; |     HWVoiceOut hw; | ||||||
|     LPDIRECTSOUNDBUFFER dsound_buffer; |     LPDIRECTSOUNDBUFFER dsound_buffer; | ||||||
|  |     DWORD old_pos; | ||||||
|  |     int first_time; | ||||||
|     dsound *s; |     dsound *s; | ||||||
|  | #ifdef DEBUG_DSOUND | ||||||
|  |     DWORD old_ppos; | ||||||
|  |     DWORD played; | ||||||
|  |     DWORD mixed; | ||||||
|  | #endif | ||||||
| } DSoundVoiceOut; | } DSoundVoiceOut; | ||||||
|  |  | ||||||
| typedef struct { | typedef struct { | ||||||
|     HWVoiceIn hw; |     HWVoiceIn hw; | ||||||
|  |     int first_time; | ||||||
|     LPDIRECTSOUNDCAPTUREBUFFER dsound_capture_buffer; |     LPDIRECTSOUNDCAPTUREBUFFER dsound_capture_buffer; | ||||||
|     dsound *s; |     dsound *s; | ||||||
| } DSoundVoiceIn; | } DSoundVoiceIn; | ||||||
| @@ -235,6 +248,11 @@ static void GCC_FMT_ATTR (3, 4) dsound_logerr2 ( | |||||||
|     dsound_log_hresult (hr); |     dsound_log_hresult (hr); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static DWORD millis_to_bytes (struct audio_pcm_info *info, DWORD millis) | ||||||
|  | { | ||||||
|  |     return (millis * info->bytes_per_second) / 1000; | ||||||
|  | } | ||||||
|  |  | ||||||
| #ifdef DEBUG_DSOUND | #ifdef DEBUG_DSOUND | ||||||
| static void print_wave_format (WAVEFORMATEX *wfx) | static void print_wave_format (WAVEFORMATEX *wfx) | ||||||
| { | { | ||||||
| @@ -299,6 +317,33 @@ static int dsound_get_status_in (LPDIRECTSOUNDCAPTUREBUFFER dscb, | |||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static void dsound_write_sample (HWVoiceOut *hw, uint8_t *dst, int dst_len) | ||||||
|  | { | ||||||
|  |     int src_len1 = dst_len; | ||||||
|  |     int src_len2 = 0; | ||||||
|  |     int pos = hw->rpos + dst_len; | ||||||
|  |     struct st_sample *src1 = hw->mix_buf + hw->rpos; | ||||||
|  |     struct st_sample *src2 = NULL; | ||||||
|  |  | ||||||
|  |     if (pos > hw->samples) { | ||||||
|  |         src_len1 = hw->samples - hw->rpos; | ||||||
|  |         src2 = hw->mix_buf; | ||||||
|  |         src_len2 = dst_len - src_len1; | ||||||
|  |         pos = src_len2; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (src_len1) { | ||||||
|  |         hw->clip (dst, src1, src_len1); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (src_len2) { | ||||||
|  |         dst = advance (dst, src_len1 << hw->info.shift); | ||||||
|  |         hw->clip (dst, src2, src_len2); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     hw->rpos = pos % hw->samples; | ||||||
|  | } | ||||||
|  |  | ||||||
| static void dsound_clear_sample (HWVoiceOut *hw, LPDIRECTSOUNDBUFFER dsb, | static void dsound_clear_sample (HWVoiceOut *hw, LPDIRECTSOUNDBUFFER dsb, | ||||||
|                                  dsound *s) |                                  dsound *s) | ||||||
| { | { | ||||||
| @@ -310,7 +355,7 @@ static void dsound_clear_sample (HWVoiceOut *hw, LPDIRECTSOUNDBUFFER dsb, | |||||||
|         dsb, |         dsb, | ||||||
|         &hw->info, |         &hw->info, | ||||||
|         0, |         0, | ||||||
|         hw->size_emul, |         hw->samples << hw->info.shift, | ||||||
|         &p1, &p2, |         &p1, &p2, | ||||||
|         &blen1, &blen2, |         &blen1, &blen2, | ||||||
|         1, |         1, | ||||||
| @@ -320,8 +365,8 @@ static void dsound_clear_sample (HWVoiceOut *hw, LPDIRECTSOUNDBUFFER dsb, | |||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     len1 = blen1 / hw->info.bytes_per_frame; |     len1 = blen1 >> hw->info.shift; | ||||||
|     len2 = blen2 / hw->info.bytes_per_frame; |     len2 = blen2 >> hw->info.shift; | ||||||
|  |  | ||||||
| #ifdef DEBUG_DSOUND | #ifdef DEBUG_DSOUND | ||||||
|     dolog ("clear %p,%ld,%ld %p,%ld,%ld\n", |     dolog ("clear %p,%ld,%ld %p,%ld,%ld\n", | ||||||
| @@ -361,7 +406,7 @@ static int dsound_open (dsound *s) | |||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void dsound_enable_out(HWVoiceOut *hw, bool enable) | static int dsound_ctl_out (HWVoiceOut *hw, int cmd, ...) | ||||||
| { | { | ||||||
|     HRESULT hr; |     HRESULT hr; | ||||||
|     DWORD status; |     DWORD status; | ||||||
| @@ -371,17 +416,18 @@ static void dsound_enable_out(HWVoiceOut *hw, bool enable) | |||||||
|  |  | ||||||
|     if (!dsb) { |     if (!dsb) { | ||||||
|         dolog ("Attempt to control voice without a buffer\n"); |         dolog ("Attempt to control voice without a buffer\n"); | ||||||
|         return; |         return 0; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (enable) { |     switch (cmd) { | ||||||
|  |     case VOICE_ENABLE: | ||||||
|         if (dsound_get_status_out (dsb, &status, s)) { |         if (dsound_get_status_out (dsb, &status, s)) { | ||||||
|             return; |             return -1; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (status & DSBSTATUS_PLAYING) { |         if (status & DSBSTATUS_PLAYING) { | ||||||
|             dolog ("warning: Voice is already playing\n"); |             dolog ("warning: Voice is already playing\n"); | ||||||
|             return; |             return 0; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         dsound_clear_sample (hw, dsb, s); |         dsound_clear_sample (hw, dsb, s); | ||||||
| @@ -389,74 +435,170 @@ static void dsound_enable_out(HWVoiceOut *hw, bool enable) | |||||||
|         hr = IDirectSoundBuffer_Play (dsb, 0, 0, DSBPLAY_LOOPING); |         hr = IDirectSoundBuffer_Play (dsb, 0, 0, DSBPLAY_LOOPING); | ||||||
|         if (FAILED (hr)) { |         if (FAILED (hr)) { | ||||||
|             dsound_logerr (hr, "Could not start playing buffer\n"); |             dsound_logerr (hr, "Could not start playing buffer\n"); | ||||||
|             return; |             return -1; | ||||||
|         } |         } | ||||||
|     } else { |         break; | ||||||
|  |  | ||||||
|  |     case VOICE_DISABLE: | ||||||
|         if (dsound_get_status_out (dsb, &status, s)) { |         if (dsound_get_status_out (dsb, &status, s)) { | ||||||
|             return; |             return -1; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (status & DSBSTATUS_PLAYING) { |         if (status & DSBSTATUS_PLAYING) { | ||||||
|             hr = IDirectSoundBuffer_Stop (dsb); |             hr = IDirectSoundBuffer_Stop (dsb); | ||||||
|             if (FAILED (hr)) { |             if (FAILED (hr)) { | ||||||
|                 dsound_logerr (hr, "Could not stop playing buffer\n"); |                 dsound_logerr (hr, "Could not stop playing buffer\n"); | ||||||
|                 return; |                 return -1; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         else { |         else { | ||||||
|             dolog ("warning: Voice is not playing\n"); |             dolog ("warning: Voice is not playing\n"); | ||||||
|         } |         } | ||||||
|  |         break; | ||||||
|     } |     } | ||||||
|  |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void *dsound_get_buffer_out(HWVoiceOut *hw, size_t *size) | static int dsound_write (SWVoiceOut *sw, void *buf, int len) | ||||||
|  | { | ||||||
|  |     return audio_pcm_sw_write (sw, buf, len); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int dsound_run_out (HWVoiceOut *hw, int live) | ||||||
| { | { | ||||||
|     DSoundVoiceOut *ds = (DSoundVoiceOut *) hw; |  | ||||||
|     LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer; |  | ||||||
|     HRESULT hr; |  | ||||||
|     DWORD ppos, act_size; |  | ||||||
|     size_t req_size; |  | ||||||
|     int err; |     int err; | ||||||
|     void *ret; |     HRESULT hr; | ||||||
|  |  | ||||||
|     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) |  | ||||||
| { |  | ||||||
|     DSoundVoiceOut *ds = (DSoundVoiceOut *) hw; |     DSoundVoiceOut *ds = (DSoundVoiceOut *) hw; | ||||||
|     LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer; |     LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer; | ||||||
|     int err = dsound_unlock_out(dsb, buf, NULL, len, 0); |     int len, hwshift; | ||||||
|  |     DWORD blen1, blen2; | ||||||
|  |     DWORD len1, len2; | ||||||
|  |     DWORD decr; | ||||||
|  |     DWORD wpos, ppos, old_pos; | ||||||
|  |     LPVOID p1, p2; | ||||||
|  |     int bufsize; | ||||||
|  |     dsound *s = ds->s; | ||||||
|  |     DSoundConf *conf = &s->conf; | ||||||
|  |  | ||||||
|     if (err) { |     if (!dsb) { | ||||||
|         dolog("Failed to unlock buffer!!\n"); |         dolog ("Attempt to run empty with playback buffer\n"); | ||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
|     hw->pos_emul = (hw->pos_emul + len) % hw->size_emul; |  | ||||||
|  |  | ||||||
|     return len; |     hwshift = hw->info.shift; | ||||||
|  |     bufsize = hw->samples << hwshift; | ||||||
|  |  | ||||||
|  |     hr = IDirectSoundBuffer_GetCurrentPosition ( | ||||||
|  |         dsb, | ||||||
|  |         &ppos, | ||||||
|  |         ds->first_time ? &wpos : NULL | ||||||
|  |         ); | ||||||
|  |     if (FAILED (hr)) { | ||||||
|  |         dsound_logerr (hr, "Could not get playback buffer position\n"); | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     len = live << hwshift; | ||||||
|  |  | ||||||
|  |     if (ds->first_time) { | ||||||
|  |         if (conf->latency_millis) { | ||||||
|  |             DWORD cur_blat; | ||||||
|  |  | ||||||
|  |             cur_blat = audio_ring_dist (wpos, ppos, bufsize); | ||||||
|  |             ds->first_time = 0; | ||||||
|  |             old_pos = wpos; | ||||||
|  |             old_pos += | ||||||
|  |                 millis_to_bytes (&hw->info, conf->latency_millis) - cur_blat; | ||||||
|  |             old_pos %= bufsize; | ||||||
|  |             old_pos &= ~hw->info.align; | ||||||
|  |         } | ||||||
|  |         else { | ||||||
|  |             old_pos = wpos; | ||||||
|  |         } | ||||||
|  | #ifdef DEBUG_DSOUND | ||||||
|  |         ds->played = 0; | ||||||
|  |         ds->mixed = 0; | ||||||
|  | #endif | ||||||
|  |     } | ||||||
|  |     else { | ||||||
|  |         if (ds->old_pos == ppos) { | ||||||
|  | #ifdef DEBUG_DSOUND | ||||||
|  |             dolog ("old_pos == ppos\n"); | ||||||
|  | #endif | ||||||
|  |             return 0; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  | #ifdef DEBUG_DSOUND | ||||||
|  |         ds->played += audio_ring_dist (ds->old_pos, ppos, hw->bufsize); | ||||||
|  | #endif | ||||||
|  |         old_pos = ds->old_pos; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if ((old_pos < ppos) && ((old_pos + len) > ppos)) { | ||||||
|  |         len = ppos - old_pos; | ||||||
|  |     } | ||||||
|  |     else { | ||||||
|  |         if ((old_pos > ppos) && ((old_pos + len) > (ppos + bufsize))) { | ||||||
|  |             len = bufsize - old_pos + ppos; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (audio_bug(__func__, len < 0 || len > bufsize)) { | ||||||
|  |         dolog ("len=%d bufsize=%d old_pos=%ld ppos=%ld\n", | ||||||
|  |                len, bufsize, old_pos, ppos); | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     len &= ~hw->info.align; | ||||||
|  |     if (!len) { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | #ifdef DEBUG_DSOUND | ||||||
|  |     ds->old_ppos = ppos; | ||||||
|  | #endif | ||||||
|  |     err = dsound_lock_out ( | ||||||
|  |         dsb, | ||||||
|  |         &hw->info, | ||||||
|  |         old_pos, | ||||||
|  |         len, | ||||||
|  |         &p1, &p2, | ||||||
|  |         &blen1, &blen2, | ||||||
|  |         0, | ||||||
|  |         s | ||||||
|  |         ); | ||||||
|  |     if (err) { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     len1 = blen1 >> hwshift; | ||||||
|  |     len2 = blen2 >> hwshift; | ||||||
|  |     decr = len1 + len2; | ||||||
|  |  | ||||||
|  |     if (p1 && len1) { | ||||||
|  |         dsound_write_sample (hw, p1, len1); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (p2 && len2) { | ||||||
|  |         dsound_write_sample (hw, p2, len2); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     dsound_unlock_out (dsb, p1, p2, blen1, blen2); | ||||||
|  |     ds->old_pos = (old_pos + (decr << hwshift)) % bufsize; | ||||||
|  |  | ||||||
|  | #ifdef DEBUG_DSOUND | ||||||
|  |     ds->mixed += decr << hwshift; | ||||||
|  |  | ||||||
|  |     dolog ("played %lu mixed %lu diff %ld sec %f\n", | ||||||
|  |            ds->played, | ||||||
|  |            ds->mixed, | ||||||
|  |            ds->mixed - ds->played, | ||||||
|  |            abs (ds->mixed - ds->played) / (double) hw->info.bytes_per_second); | ||||||
|  | #endif | ||||||
|  |     return decr; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void dsound_enable_in(HWVoiceIn *hw, bool enable) | static int dsound_ctl_in (HWVoiceIn *hw, int cmd, ...) | ||||||
| { | { | ||||||
|     HRESULT hr; |     HRESULT hr; | ||||||
|     DWORD status; |     DWORD status; | ||||||
| @@ -465,17 +607,18 @@ static void dsound_enable_in(HWVoiceIn *hw, bool enable) | |||||||
|  |  | ||||||
|     if (!dscb) { |     if (!dscb) { | ||||||
|         dolog ("Attempt to control capture voice without a buffer\n"); |         dolog ("Attempt to control capture voice without a buffer\n"); | ||||||
|         return; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (enable) { |     switch (cmd) { | ||||||
|  |     case VOICE_ENABLE: | ||||||
|         if (dsound_get_status_in (dscb, &status)) { |         if (dsound_get_status_in (dscb, &status)) { | ||||||
|             return; |             return -1; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (status & DSCBSTATUS_CAPTURING) { |         if (status & DSCBSTATUS_CAPTURING) { | ||||||
|             dolog ("warning: Voice is already capturing\n"); |             dolog ("warning: Voice is already capturing\n"); | ||||||
|             return; |             return 0; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         /* clear ?? */ |         /* clear ?? */ | ||||||
| @@ -483,71 +626,133 @@ static void dsound_enable_in(HWVoiceIn *hw, bool enable) | |||||||
|         hr = IDirectSoundCaptureBuffer_Start (dscb, DSCBSTART_LOOPING); |         hr = IDirectSoundCaptureBuffer_Start (dscb, DSCBSTART_LOOPING); | ||||||
|         if (FAILED (hr)) { |         if (FAILED (hr)) { | ||||||
|             dsound_logerr (hr, "Could not start capturing\n"); |             dsound_logerr (hr, "Could not start capturing\n"); | ||||||
|             return; |             return -1; | ||||||
|         } |         } | ||||||
|     } else { |         break; | ||||||
|  |  | ||||||
|  |     case VOICE_DISABLE: | ||||||
|         if (dsound_get_status_in (dscb, &status)) { |         if (dsound_get_status_in (dscb, &status)) { | ||||||
|             return; |             return -1; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (status & DSCBSTATUS_CAPTURING) { |         if (status & DSCBSTATUS_CAPTURING) { | ||||||
|             hr = IDirectSoundCaptureBuffer_Stop (dscb); |             hr = IDirectSoundCaptureBuffer_Stop (dscb); | ||||||
|             if (FAILED (hr)) { |             if (FAILED (hr)) { | ||||||
|                 dsound_logerr (hr, "Could not stop capturing\n"); |                 dsound_logerr (hr, "Could not stop capturing\n"); | ||||||
|                 return; |                 return -1; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         else { |         else { | ||||||
|             dolog ("warning: Voice is not capturing\n"); |             dolog ("warning: Voice is not capturing\n"); | ||||||
|         } |         } | ||||||
|  |         break; | ||||||
|     } |     } | ||||||
|  |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void *dsound_get_buffer_in(HWVoiceIn *hw, size_t *size) | static int dsound_read (SWVoiceIn *sw, void *buf, int len) | ||||||
|  | { | ||||||
|  |     return audio_pcm_sw_read (sw, buf, len); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int dsound_run_in (HWVoiceIn *hw) | ||||||
| { | { | ||||||
|     DSoundVoiceIn *ds = (DSoundVoiceIn *) hw; |  | ||||||
|     LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer; |  | ||||||
|     HRESULT hr; |  | ||||||
|     DWORD cpos, act_size; |  | ||||||
|     size_t req_size; |  | ||||||
|     int err; |     int err; | ||||||
|     void *ret; |     HRESULT hr; | ||||||
|  |  | ||||||
|     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) |  | ||||||
| { |  | ||||||
|     DSoundVoiceIn *ds = (DSoundVoiceIn *) hw; |     DSoundVoiceIn *ds = (DSoundVoiceIn *) hw; | ||||||
|     LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer; |     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) { |     if (!dscb) { | ||||||
|         dolog("Failed to unlock buffer!!\n"); |         dolog ("Attempt to run without capture buffer\n"); | ||||||
|         return; |         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) | static void dsound_audio_fini (void *opaque) | ||||||
| { | { | ||||||
|     HRESULT hr; |     HRESULT hr; | ||||||
| @@ -578,22 +783,13 @@ static void dsound_audio_fini (void *opaque) | |||||||
|     g_free(s); |     g_free(s); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void *dsound_audio_init(Audiodev *dev) | static void *dsound_audio_init (void) | ||||||
| { | { | ||||||
|     int err; |     int err; | ||||||
|     HRESULT hr; |     HRESULT hr; | ||||||
|     dsound *s = g_malloc0(sizeof(dsound)); |     dsound *s = g_malloc0(sizeof(dsound)); | ||||||
|     AudiodevDsoundOptions *dso; |  | ||||||
|  |  | ||||||
|     assert(dev->driver == AUDIODEV_DRIVER_DSOUND); |  | ||||||
|     s->dev = dev; |  | ||||||
|     dso = &dev->u.dsound; |  | ||||||
|  |  | ||||||
|     if (!dso->has_latency) { |  | ||||||
|         dso->has_latency = true; |  | ||||||
|         dso->latency = 10000; /* 10 ms */ |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|  |     s->conf = glob_conf; | ||||||
|     hr = CoInitialize (NULL); |     hr = CoInitialize (NULL); | ||||||
|     if (FAILED (hr)) { |     if (FAILED (hr)) { | ||||||
|         dsound_logerr (hr, "Could not initialize COM\n"); |         dsound_logerr (hr, "Could not initialize COM\n"); | ||||||
| @@ -658,25 +854,46 @@ static void *dsound_audio_init(Audiodev *dev) | |||||||
|     return s; |     return s; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static struct audio_option dsound_options[] = { | ||||||
|  |     { | ||||||
|  |         .name  = "LATENCY_MILLIS", | ||||||
|  |         .tag   = AUD_OPT_INT, | ||||||
|  |         .valp  = &glob_conf.latency_millis, | ||||||
|  |         .descr = "(undocumented)" | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |         .name  = "BUFSIZE_OUT", | ||||||
|  |         .tag   = AUD_OPT_INT, | ||||||
|  |         .valp  = &glob_conf.bufsize_out, | ||||||
|  |         .descr = "(undocumented)" | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |         .name  = "BUFSIZE_IN", | ||||||
|  |         .tag   = AUD_OPT_INT, | ||||||
|  |         .valp  = &glob_conf.bufsize_in, | ||||||
|  |         .descr = "(undocumented)" | ||||||
|  |     }, | ||||||
|  |     { /* End of list */ } | ||||||
|  | }; | ||||||
|  |  | ||||||
| static struct audio_pcm_ops dsound_pcm_ops = { | static struct audio_pcm_ops dsound_pcm_ops = { | ||||||
|     .init_out = dsound_init_out, |     .init_out = dsound_init_out, | ||||||
|     .fini_out = dsound_fini_out, |     .fini_out = dsound_fini_out, | ||||||
|     .write    = audio_generic_write, |     .run_out  = dsound_run_out, | ||||||
|     .get_buffer_out = dsound_get_buffer_out, |     .write    = dsound_write, | ||||||
|     .put_buffer_out = dsound_put_buffer_out, |     .ctl_out  = dsound_ctl_out, | ||||||
|     .enable_out = dsound_enable_out, |  | ||||||
|  |  | ||||||
|     .init_in  = dsound_init_in, |     .init_in  = dsound_init_in, | ||||||
|     .fini_in  = dsound_fini_in, |     .fini_in  = dsound_fini_in, | ||||||
|     .read     = audio_generic_read, |     .run_in   = dsound_run_in, | ||||||
|     .get_buffer_in = dsound_get_buffer_in, |     .read     = dsound_read, | ||||||
|     .put_buffer_in = dsound_put_buffer_in, |     .ctl_in   = dsound_ctl_in | ||||||
|     .enable_in = dsound_enable_in, |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| static struct audio_driver dsound_audio_driver = { | static struct audio_driver dsound_audio_driver = { | ||||||
|     .name           = "dsound", |     .name           = "dsound", | ||||||
|     .descr          = "DirectSound http://wikipedia.org/wiki/DirectSound", |     .descr          = "DirectSound http://wikipedia.org/wiki/DirectSound", | ||||||
|  |     .options        = dsound_options, | ||||||
|     .init           = dsound_audio_init, |     .init           = dsound_audio_init, | ||||||
|     .fini           = dsound_audio_fini, |     .fini           = dsound_audio_fini, | ||||||
|     .pcm_ops        = &dsound_pcm_ops, |     .pcm_ops        = &dsound_pcm_ops, | ||||||
|   | |||||||
| @@ -23,6 +23,7 @@ | |||||||
|  * THE SOFTWARE. |  * THE SOFTWARE. | ||||||
|  */ |  */ | ||||||
| #include "qemu/osdep.h" | #include "qemu/osdep.h" | ||||||
|  | #include "qemu-common.h" | ||||||
| #include "qemu/bswap.h" | #include "qemu/bswap.h" | ||||||
| #include "qemu/error-report.h" | #include "qemu/error-report.h" | ||||||
| #include "audio.h" | #include "audio.h" | ||||||
|   | |||||||
| @@ -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 mixeng_volume { int mute; int64_t r; int64_t l; }; | ||||||
| struct st_sample { int64_t l; int64_t r; }; | struct st_sample { int64_t l; int64_t r; }; | ||||||
| #endif | #endif | ||||||
| typedef struct st_sample st_sample; |  | ||||||
|  |  | ||||||
| typedef void (t_sample) (struct st_sample *dst, const void *src, int samples); | typedef void (t_sample) (struct st_sample *dst, const void *src, int samples); | ||||||
| typedef void (f_sample) (void *dst, const struct st_sample *src, int samples); | typedef void (f_sample) (void *dst, const struct st_sample *src, int samples); | ||||||
| @@ -42,10 +41,10 @@ extern t_sample *mixeng_conv[2][2][2][3]; | |||||||
| extern f_sample *mixeng_clip[2][2][2][3]; | extern f_sample *mixeng_clip[2][2][2][3]; | ||||||
|  |  | ||||||
| void *st_rate_start (int inrate, int outrate); | void *st_rate_start (int inrate, int outrate); | ||||||
| void st_rate_flow(void *opaque, st_sample *ibuf, st_sample *obuf, | void st_rate_flow (void *opaque, struct st_sample *ibuf, struct st_sample *obuf, | ||||||
|                   size_t *isamp, size_t *osamp); |                    int *isamp, int *osamp); | ||||||
| void st_rate_flow_mix(void *opaque, st_sample *ibuf, st_sample *obuf, | void st_rate_flow_mix (void *opaque, struct st_sample *ibuf, struct st_sample *obuf, | ||||||
|                       size_t *isamp, size_t *osamp); |                        int *isamp, int *osamp); | ||||||
| void st_rate_stop (void *opaque); | void st_rate_stop (void *opaque); | ||||||
| void mixeng_clear (struct st_sample *buf, int len); | void mixeng_clear (struct st_sample *buf, int len); | ||||||
| void mixeng_volume (struct st_sample *buf, int len, struct mixeng_volume *vol); | void mixeng_volume (struct st_sample *buf, int len, struct mixeng_volume *vol); | ||||||
|   | |||||||
| @@ -21,10 +21,9 @@ | |||||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||||
|  * THE SOFTWARE. |  * THE SOFTWARE. | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #include "qemu/osdep.h" | #include "qemu/osdep.h" | ||||||
|  | #include "qemu-common.h" | ||||||
| #include "qemu/host-utils.h" | #include "qemu/host-utils.h" | ||||||
| #include "qemu/module.h" |  | ||||||
| #include "audio.h" | #include "audio.h" | ||||||
| #include "qemu/timer.h" | #include "qemu/timer.h" | ||||||
|  |  | ||||||
| @@ -33,27 +32,43 @@ | |||||||
|  |  | ||||||
| typedef struct NoVoiceOut { | typedef struct NoVoiceOut { | ||||||
|     HWVoiceOut hw; |     HWVoiceOut hw; | ||||||
|     RateCtl rate; |     int64_t old_ticks; | ||||||
| } NoVoiceOut; | } NoVoiceOut; | ||||||
|  |  | ||||||
| typedef struct NoVoiceIn { | typedef struct NoVoiceIn { | ||||||
|     HWVoiceIn hw; |     HWVoiceIn hw; | ||||||
|     RateCtl rate; |     int64_t old_ticks; | ||||||
| } NoVoiceIn; | } NoVoiceIn; | ||||||
|  |  | ||||||
| static size_t no_write(HWVoiceOut *hw, void *buf, size_t len) | static int no_run_out (HWVoiceOut *hw, int live) | ||||||
| { | { | ||||||
|     NoVoiceOut *no = (NoVoiceOut *) hw; |     NoVoiceOut *no = (NoVoiceOut *) hw; | ||||||
|     return audio_rate_get_bytes(&hw->info, &no->rate, len); |     int decr, samples; | ||||||
|  |     int64_t now; | ||||||
|  |     int64_t ticks; | ||||||
|  |     int64_t bytes; | ||||||
|  |  | ||||||
|  |     now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); | ||||||
|  |     ticks = now - no->old_ticks; | ||||||
|  |     bytes = muldiv64(ticks, hw->info.bytes_per_second, NANOSECONDS_PER_SECOND); | ||||||
|  |     bytes = audio_MIN(bytes, INT_MAX); | ||||||
|  |     samples = bytes >> hw->info.shift; | ||||||
|  |  | ||||||
|  |     no->old_ticks = now; | ||||||
|  |     decr = audio_MIN (live, samples); | ||||||
|  |     hw->rpos = (hw->rpos + decr) % hw->samples; | ||||||
|  |     return decr; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int no_write (SWVoiceOut *sw, void *buf, int len) | ||||||
|  | { | ||||||
|  |     return audio_pcm_sw_write(sw, buf, len); | ||||||
| } | } | ||||||
|  |  | ||||||
| static int no_init_out(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque) | static int no_init_out(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque) | ||||||
| { | { | ||||||
|     NoVoiceOut *no = (NoVoiceOut *) hw; |  | ||||||
|  |  | ||||||
|     audio_pcm_init_info (&hw->info, as); |     audio_pcm_init_info (&hw->info, as); | ||||||
|     hw->samples = 1024; |     hw->samples = 1024; | ||||||
|     audio_rate_start(&no->rate); |  | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -62,22 +77,17 @@ static void no_fini_out (HWVoiceOut *hw) | |||||||
|     (void) hw; |     (void) hw; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void no_enable_out(HWVoiceOut *hw, bool enable) | static int no_ctl_out (HWVoiceOut *hw, int cmd, ...) | ||||||
| { | { | ||||||
|     NoVoiceOut *no = (NoVoiceOut *) hw; |     (void) hw; | ||||||
|  |     (void) cmd; | ||||||
|     if (enable) { |     return 0; | ||||||
|         audio_rate_start(&no->rate); |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static int no_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) | static int no_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) | ||||||
| { | { | ||||||
|     NoVoiceIn *no = (NoVoiceIn *) hw; |  | ||||||
|  |  | ||||||
|     audio_pcm_init_info (&hw->info, as); |     audio_pcm_init_info (&hw->info, as); | ||||||
|     hw->samples = 1024; |     hw->samples = 1024; | ||||||
|     audio_rate_start(&no->rate); |  | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -86,25 +96,47 @@ static void no_fini_in (HWVoiceIn *hw) | |||||||
|     (void) hw; |     (void) hw; | ||||||
| } | } | ||||||
|  |  | ||||||
| static size_t no_read(HWVoiceIn *hw, void *buf, size_t size) | static int no_run_in (HWVoiceIn *hw) | ||||||
| { | { | ||||||
|     NoVoiceIn *no = (NoVoiceIn *) hw; |     NoVoiceIn *no = (NoVoiceIn *) hw; | ||||||
|     int64_t bytes = audio_rate_get_bytes(&hw->info, &no->rate, size); |     int live = audio_pcm_hw_get_live_in (hw); | ||||||
|  |     int dead = hw->samples - live; | ||||||
|  |     int samples = 0; | ||||||
|  |  | ||||||
|     audio_pcm_info_clear_buf(&hw->info, buf, bytes / hw->info.bytes_per_frame); |     if (dead) { | ||||||
|     return bytes; |         int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); | ||||||
| } |         int64_t ticks = now - no->old_ticks; | ||||||
|  |         int64_t bytes = | ||||||
|  |             muldiv64(ticks, hw->info.bytes_per_second, NANOSECONDS_PER_SECOND); | ||||||
|  |  | ||||||
| static void no_enable_in(HWVoiceIn *hw, bool enable) |         no->old_ticks = now; | ||||||
| { |         bytes = audio_MIN (bytes, INT_MAX); | ||||||
|     NoVoiceIn *no = (NoVoiceIn *) hw; |         samples = bytes >> hw->info.shift; | ||||||
|  |         samples = audio_MIN (samples, dead); | ||||||
|     if (enable) { |  | ||||||
|         audio_rate_start(&no->rate); |  | ||||||
|     } |     } | ||||||
|  |     return samples; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void *no_audio_init(Audiodev *dev) | static int no_read (SWVoiceIn *sw, void *buf, int size) | ||||||
|  | { | ||||||
|  |     /* use custom code here instead of audio_pcm_sw_read() to avoid | ||||||
|  |      * useless resampling/mixing */ | ||||||
|  |     int samples = size >> sw->info.shift; | ||||||
|  |     int total = sw->hw->total_samples_captured - sw->total_hw_samples_acquired; | ||||||
|  |     int to_clear = audio_MIN (samples, total); | ||||||
|  |     sw->total_hw_samples_acquired += total; | ||||||
|  |     audio_pcm_info_clear_buf (&sw->info, buf, to_clear); | ||||||
|  |     return to_clear << sw->info.shift; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int no_ctl_in (HWVoiceIn *hw, int cmd, ...) | ||||||
|  | { | ||||||
|  |     (void) hw; | ||||||
|  |     (void) cmd; | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void *no_audio_init (void) | ||||||
| { | { | ||||||
|     return &no_audio_init; |     return &no_audio_init; | ||||||
| } | } | ||||||
| @@ -117,18 +149,21 @@ static void no_audio_fini (void *opaque) | |||||||
| static struct audio_pcm_ops no_pcm_ops = { | static struct audio_pcm_ops no_pcm_ops = { | ||||||
|     .init_out = no_init_out, |     .init_out = no_init_out, | ||||||
|     .fini_out = no_fini_out, |     .fini_out = no_fini_out, | ||||||
|  |     .run_out  = no_run_out, | ||||||
|     .write    = no_write, |     .write    = no_write, | ||||||
|     .enable_out = no_enable_out, |     .ctl_out  = no_ctl_out, | ||||||
|  |  | ||||||
|     .init_in  = no_init_in, |     .init_in  = no_init_in, | ||||||
|     .fini_in  = no_fini_in, |     .fini_in  = no_fini_in, | ||||||
|  |     .run_in   = no_run_in, | ||||||
|     .read     = no_read, |     .read     = no_read, | ||||||
|     .enable_in = no_enable_in |     .ctl_in   = no_ctl_in | ||||||
| }; | }; | ||||||
|  |  | ||||||
| static struct audio_driver no_audio_driver = { | static struct audio_driver no_audio_driver = { | ||||||
|     .name           = "none", |     .name           = "none", | ||||||
|     .descr          = "Timer based audio emulation", |     .descr          = "Timer based audio emulation", | ||||||
|  |     .options        = NULL, | ||||||
|     .init           = no_audio_init, |     .init           = no_audio_init, | ||||||
|     .fini           = no_audio_fini, |     .fini           = no_audio_fini, | ||||||
|     .pcm_ops        = &no_pcm_ops, |     .pcm_ops        = &no_pcm_ops, | ||||||
|   | |||||||
							
								
								
									
										597
									
								
								audio/ossaudio.c
									
									
									
									
									
								
							
							
						
						
									
										597
									
								
								audio/ossaudio.c
									
									
									
									
									
								
							| @@ -21,12 +21,11 @@ | |||||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||||
|  * THE SOFTWARE. |  * THE SOFTWARE. | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #include "qemu/osdep.h" | #include "qemu/osdep.h" | ||||||
| #include <sys/ioctl.h> | #include <sys/ioctl.h> | ||||||
| #include <sys/soundcard.h> | #include <sys/soundcard.h> | ||||||
|  | #include "qemu-common.h" | ||||||
| #include "qemu/main-loop.h" | #include "qemu/main-loop.h" | ||||||
| #include "qemu/module.h" |  | ||||||
| #include "qemu/host-utils.h" | #include "qemu/host-utils.h" | ||||||
| #include "audio.h" | #include "audio.h" | ||||||
| #include "trace.h" | #include "trace.h" | ||||||
| @@ -38,26 +37,40 @@ | |||||||
| #define USE_DSP_POLICY | #define USE_DSP_POLICY | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | typedef struct OSSConf { | ||||||
|  |     int try_mmap; | ||||||
|  |     int nfrags; | ||||||
|  |     int fragsize; | ||||||
|  |     const char *devpath_out; | ||||||
|  |     const char *devpath_in; | ||||||
|  |     int exclusive; | ||||||
|  |     int policy; | ||||||
|  | } OSSConf; | ||||||
|  |  | ||||||
| typedef struct OSSVoiceOut { | typedef struct OSSVoiceOut { | ||||||
|     HWVoiceOut hw; |     HWVoiceOut hw; | ||||||
|  |     void *pcm_buf; | ||||||
|     int fd; |     int fd; | ||||||
|  |     int wpos; | ||||||
|     int nfrags; |     int nfrags; | ||||||
|     int fragsize; |     int fragsize; | ||||||
|     int mmapped; |     int mmapped; | ||||||
|     Audiodev *dev; |     int pending; | ||||||
|  |     OSSConf *conf; | ||||||
| } OSSVoiceOut; | } OSSVoiceOut; | ||||||
|  |  | ||||||
| typedef struct OSSVoiceIn { | typedef struct OSSVoiceIn { | ||||||
|     HWVoiceIn hw; |     HWVoiceIn hw; | ||||||
|  |     void *pcm_buf; | ||||||
|     int fd; |     int fd; | ||||||
|     int nfrags; |     int nfrags; | ||||||
|     int fragsize; |     int fragsize; | ||||||
|     Audiodev *dev; |     OSSConf *conf; | ||||||
| } OSSVoiceIn; | } OSSVoiceIn; | ||||||
|  |  | ||||||
| struct oss_params { | struct oss_params { | ||||||
|     int freq; |     int freq; | ||||||
|     int fmt; |     audfmt_e fmt; | ||||||
|     int nchannels; |     int nchannels; | ||||||
|     int nfrags; |     int nfrags; | ||||||
|     int fragsize; |     int fragsize; | ||||||
| @@ -106,40 +119,45 @@ static void oss_anal_close (int *fdp) | |||||||
|  |  | ||||||
| static void oss_helper_poll_out (void *opaque) | static void oss_helper_poll_out (void *opaque) | ||||||
| { | { | ||||||
|     AudioState *s = opaque; |     (void) opaque; | ||||||
|     audio_run(s, "oss_poll_out"); |     audio_run ("oss_poll_out"); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void oss_helper_poll_in (void *opaque) | static void oss_helper_poll_in (void *opaque) | ||||||
| { | { | ||||||
|     AudioState *s = opaque; |     (void) opaque; | ||||||
|     audio_run(s, "oss_poll_in"); |     audio_run ("oss_poll_in"); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void oss_poll_out (HWVoiceOut *hw) | static void oss_poll_out (HWVoiceOut *hw) | ||||||
| { | { | ||||||
|     OSSVoiceOut *oss = (OSSVoiceOut *) hw; |     OSSVoiceOut *oss = (OSSVoiceOut *) hw; | ||||||
|  |  | ||||||
|     qemu_set_fd_handler(oss->fd, NULL, oss_helper_poll_out, hw->s); |     qemu_set_fd_handler (oss->fd, NULL, oss_helper_poll_out, NULL); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void oss_poll_in (HWVoiceIn *hw) | static void oss_poll_in (HWVoiceIn *hw) | ||||||
| { | { | ||||||
|     OSSVoiceIn *oss = (OSSVoiceIn *) hw; |     OSSVoiceIn *oss = (OSSVoiceIn *) hw; | ||||||
|  |  | ||||||
|     qemu_set_fd_handler(oss->fd, oss_helper_poll_in, NULL, hw->s); |     qemu_set_fd_handler (oss->fd, oss_helper_poll_in, NULL, NULL); | ||||||
| } | } | ||||||
|  |  | ||||||
| static int aud_to_ossfmt (AudioFormat fmt, int endianness) | static int oss_write (SWVoiceOut *sw, void *buf, int len) | ||||||
|  | { | ||||||
|  |     return audio_pcm_sw_write (sw, buf, len); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int aud_to_ossfmt (audfmt_e fmt, int endianness) | ||||||
| { | { | ||||||
|     switch (fmt) { |     switch (fmt) { | ||||||
|     case AUDIO_FORMAT_S8: |     case AUD_FMT_S8: | ||||||
|         return AFMT_S8; |         return AFMT_S8; | ||||||
|  |  | ||||||
|     case AUDIO_FORMAT_U8: |     case AUD_FMT_U8: | ||||||
|         return AFMT_U8; |         return AFMT_U8; | ||||||
|  |  | ||||||
|     case AUDIO_FORMAT_S16: |     case AUD_FMT_S16: | ||||||
|         if (endianness) { |         if (endianness) { | ||||||
|             return AFMT_S16_BE; |             return AFMT_S16_BE; | ||||||
|         } |         } | ||||||
| @@ -147,7 +165,7 @@ static int aud_to_ossfmt (AudioFormat fmt, int endianness) | |||||||
|             return AFMT_S16_LE; |             return AFMT_S16_LE; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|     case AUDIO_FORMAT_U16: |     case AUD_FMT_U16: | ||||||
|         if (endianness) { |         if (endianness) { | ||||||
|             return AFMT_U16_BE; |             return AFMT_U16_BE; | ||||||
|         } |         } | ||||||
| @@ -164,37 +182,37 @@ static int aud_to_ossfmt (AudioFormat fmt, int endianness) | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| static int oss_to_audfmt (int ossfmt, AudioFormat *fmt, int *endianness) | static int oss_to_audfmt (int ossfmt, audfmt_e *fmt, int *endianness) | ||||||
| { | { | ||||||
|     switch (ossfmt) { |     switch (ossfmt) { | ||||||
|     case AFMT_S8: |     case AFMT_S8: | ||||||
|         *endianness = 0; |         *endianness = 0; | ||||||
|         *fmt = AUDIO_FORMAT_S8; |         *fmt = AUD_FMT_S8; | ||||||
|         break; |         break; | ||||||
|  |  | ||||||
|     case AFMT_U8: |     case AFMT_U8: | ||||||
|         *endianness = 0; |         *endianness = 0; | ||||||
|         *fmt = AUDIO_FORMAT_U8; |         *fmt = AUD_FMT_U8; | ||||||
|         break; |         break; | ||||||
|  |  | ||||||
|     case AFMT_S16_LE: |     case AFMT_S16_LE: | ||||||
|         *endianness = 0; |         *endianness = 0; | ||||||
|         *fmt = AUDIO_FORMAT_S16; |         *fmt = AUD_FMT_S16; | ||||||
|         break; |         break; | ||||||
|  |  | ||||||
|     case AFMT_U16_LE: |     case AFMT_U16_LE: | ||||||
|         *endianness = 0; |         *endianness = 0; | ||||||
|         *fmt = AUDIO_FORMAT_U16; |         *fmt = AUD_FMT_U16; | ||||||
|         break; |         break; | ||||||
|  |  | ||||||
|     case AFMT_S16_BE: |     case AFMT_S16_BE: | ||||||
|         *endianness = 1; |         *endianness = 1; | ||||||
|         *fmt = AUDIO_FORMAT_S16; |         *fmt = AUD_FMT_S16; | ||||||
|         break; |         break; | ||||||
|  |  | ||||||
|     case AFMT_U16_BE: |     case AFMT_U16_BE: | ||||||
|         *endianness = 1; |         *endianness = 1; | ||||||
|         *fmt = AUDIO_FORMAT_U16; |         *fmt = AUD_FMT_U16; | ||||||
|         break; |         break; | ||||||
|  |  | ||||||
|     default: |     default: | ||||||
| @@ -244,25 +262,19 @@ static int oss_get_version (int fd, int *version, const char *typ) | |||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| static int oss_open(int in, struct oss_params *req, audsettings *as, | static int oss_open (int in, struct oss_params *req, | ||||||
|                     struct oss_params *obt, int *pfd, Audiodev *dev) |                      struct oss_params *obt, int *pfd, OSSConf* conf) | ||||||
| { | { | ||||||
|     AudiodevOssOptions *oopts = &dev->u.oss; |  | ||||||
|     AudiodevOssPerDirectionOptions *opdo = in ? oopts->in : oopts->out; |  | ||||||
|     int fd; |     int fd; | ||||||
|     int oflags = (oopts->has_exclusive && oopts->exclusive) ? O_EXCL : 0; |     int oflags = conf->exclusive ? O_EXCL : 0; | ||||||
|     audio_buf_info abinfo; |     audio_buf_info abinfo; | ||||||
|     int fmt, freq, nchannels; |     int fmt, freq, nchannels; | ||||||
|     int setfragment = 1; |     int setfragment = 1; | ||||||
|     const char *dspname = opdo->has_dev ? opdo->dev : "/dev/dsp"; |     const char *dspname = in ? conf->devpath_in : conf->devpath_out; | ||||||
|     const char *typ = in ? "ADC" : "DAC"; |     const char *typ = in ? "ADC" : "DAC"; | ||||||
| #ifdef USE_DSP_POLICY |  | ||||||
|     int policy = oopts->has_dsp_policy ? oopts->dsp_policy : 5; |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
|     /* Kludge needed to have working mmap on Linux */ |     /* Kludge needed to have working mmap on Linux */ | ||||||
|     oflags |= (oopts->has_try_mmap && oopts->try_mmap) ? |     oflags |= conf->try_mmap ? O_RDWR : (in ? O_RDONLY : O_WRONLY); | ||||||
|         O_RDWR : (in ? O_RDONLY : O_WRONLY); |  | ||||||
|  |  | ||||||
|     fd = open (dspname, oflags | O_NONBLOCK); |     fd = open (dspname, oflags | O_NONBLOCK); | ||||||
|     if (-1 == fd) { |     if (-1 == fd) { | ||||||
| @@ -273,9 +285,6 @@ static int oss_open(int in, struct oss_params *req, audsettings *as, | |||||||
|     freq = req->freq; |     freq = req->freq; | ||||||
|     nchannels = req->nchannels; |     nchannels = req->nchannels; | ||||||
|     fmt = req->fmt; |     fmt = req->fmt; | ||||||
|     req->nfrags = opdo->has_buffer_count ? opdo->buffer_count : 4; |  | ||||||
|     req->fragsize = audio_buffer_bytes( |  | ||||||
|         qapi_AudiodevOssPerDirectionOptions_base(opdo), as, 23220); |  | ||||||
|  |  | ||||||
|     if (ioctl (fd, SNDCTL_DSP_SAMPLESIZE, &fmt)) { |     if (ioctl (fd, SNDCTL_DSP_SAMPLESIZE, &fmt)) { | ||||||
|         oss_logerr2 (errno, typ, "Failed to set sample size %d\n", req->fmt); |         oss_logerr2 (errno, typ, "Failed to set sample size %d\n", req->fmt); | ||||||
| @@ -299,18 +308,18 @@ static int oss_open(int in, struct oss_params *req, audsettings *as, | |||||||
|     } |     } | ||||||
|  |  | ||||||
| #ifdef USE_DSP_POLICY | #ifdef USE_DSP_POLICY | ||||||
|     if (policy >= 0) { |     if (conf->policy >= 0) { | ||||||
|         int version; |         int version; | ||||||
|  |  | ||||||
|         if (!oss_get_version (fd, &version, typ)) { |         if (!oss_get_version (fd, &version, typ)) { | ||||||
|             trace_oss_version(version); |             trace_oss_version(version); | ||||||
|  |  | ||||||
|             if (version >= 0x040000) { |             if (version >= 0x040000) { | ||||||
|                 int policy2 = policy; |                 int policy = conf->policy; | ||||||
|                 if (ioctl(fd, SNDCTL_DSP_POLICY, &policy2)) { |                 if (ioctl (fd, SNDCTL_DSP_POLICY, &policy)) { | ||||||
|                     oss_logerr2 (errno, typ, |                     oss_logerr2 (errno, typ, | ||||||
|                                  "Failed to set timing policy to %d\n", |                                  "Failed to set timing policy to %d\n", | ||||||
|                                  policy); |                                  conf->policy); | ||||||
|                     goto err; |                     goto err; | ||||||
|                 } |                 } | ||||||
|                 setfragment = 0; |                 setfragment = 0; | ||||||
| @@ -367,87 +376,97 @@ static int oss_open(int in, struct oss_params *req, audsettings *as, | |||||||
|     return -1; |     return -1; | ||||||
| } | } | ||||||
|  |  | ||||||
| static size_t oss_get_available_bytes(OSSVoiceOut *oss) | static void oss_write_pending (OSSVoiceOut *oss) | ||||||
| { | { | ||||||
|     int err; |     HWVoiceOut *hw = &oss->hw; | ||||||
|     struct count_info cntinfo; |  | ||||||
|     assert(oss->mmapped); |  | ||||||
|  |  | ||||||
|     err = ioctl(oss->fd, SNDCTL_DSP_GETOPTR, &cntinfo); |  | ||||||
|     if (err < 0) { |  | ||||||
|         oss_logerr(errno, "SNDCTL_DSP_GETOPTR failed\n"); |  | ||||||
|         return 0; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return audio_ring_dist(cntinfo.ptr, oss->hw.pos_emul, oss->hw.size_emul); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void *oss_get_buffer_out(HWVoiceOut *hw, size_t *size) |  | ||||||
| { |  | ||||||
|     OSSVoiceOut *oss = (OSSVoiceOut *) hw; |  | ||||||
|     if (oss->mmapped) { |  | ||||||
|         *size = MIN(oss_get_available_bytes(oss), hw->size_emul - hw->pos_emul); |  | ||||||
|         return hw->buf_emul + hw->pos_emul; |  | ||||||
|     } else { |  | ||||||
|         return audio_generic_get_buffer_out(hw, size); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static size_t oss_put_buffer_out(HWVoiceOut *hw, void *buf, size_t size) |  | ||||||
| { |  | ||||||
|     OSSVoiceOut *oss = (OSSVoiceOut *) hw; |  | ||||||
|     if (oss->mmapped) { |  | ||||||
|         assert(buf == hw->buf_emul + hw->pos_emul && size < hw->size_emul); |  | ||||||
|  |  | ||||||
|         hw->pos_emul = (hw->pos_emul + size) % hw->size_emul; |  | ||||||
|         return size; |  | ||||||
|     } else { |  | ||||||
|         return audio_generic_put_buffer_out(hw, buf, size); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static size_t oss_write(HWVoiceOut *hw, void *buf, size_t len) |  | ||||||
| { |  | ||||||
|     OSSVoiceOut *oss = (OSSVoiceOut *) hw; |  | ||||||
|     size_t pos; |  | ||||||
|  |  | ||||||
|     if (oss->mmapped) { |     if (oss->mmapped) { | ||||||
|         size_t total_len; |         return; | ||||||
|         len = MIN(len, oss_get_available_bytes(oss)); |  | ||||||
|  |  | ||||||
|         total_len = len; |  | ||||||
|         while (len) { |  | ||||||
|             size_t to_copy = MIN(len, hw->size_emul - hw->pos_emul); |  | ||||||
|             memcpy(hw->buf_emul + hw->pos_emul, buf, to_copy); |  | ||||||
|  |  | ||||||
|             hw->pos_emul = (hw->pos_emul + to_copy) % hw->pos_emul; |  | ||||||
|             buf += to_copy; |  | ||||||
|             len -= to_copy; |  | ||||||
|         } |  | ||||||
|         return total_len; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     pos = 0; |     while (oss->pending) { | ||||||
|     while (len) { |         int samples_written; | ||||||
|         ssize_t bytes_written; |         ssize_t bytes_written; | ||||||
|         void *pcm = advance(buf, pos); |         int samples_till_end = hw->samples - oss->wpos; | ||||||
|  |         int samples_to_write = audio_MIN (oss->pending, samples_till_end); | ||||||
|  |         int bytes_to_write = samples_to_write << hw->info.shift; | ||||||
|  |         void *pcm = advance (oss->pcm_buf, oss->wpos << hw->info.shift); | ||||||
|  |  | ||||||
|         bytes_written = write(oss->fd, pcm, len); |         bytes_written = write (oss->fd, pcm, bytes_to_write); | ||||||
|         if (bytes_written < 0) { |         if (bytes_written < 0) { | ||||||
|             if (errno != EAGAIN) { |             if (errno != EAGAIN) { | ||||||
|                 oss_logerr(errno, "failed to write %zu bytes\n", |                 oss_logerr (errno, "failed to write %d bytes\n", | ||||||
|                            len); |                             bytes_to_write); | ||||||
|             } |             } | ||||||
|             return pos; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         pos += bytes_written; |  | ||||||
|         if (bytes_written < len) { |  | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|         len -= bytes_written; |  | ||||||
|  |         if (bytes_written & hw->info.align) { | ||||||
|  |             dolog ("misaligned write asked for %d, but got %zd\n", | ||||||
|  |                    bytes_to_write, bytes_written); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         samples_written = bytes_written >> hw->info.shift; | ||||||
|  |         oss->pending -= samples_written; | ||||||
|  |         oss->wpos = (oss->wpos + samples_written) % hw->samples; | ||||||
|  |         if (bytes_written - bytes_to_write) { | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|     return pos; | } | ||||||
|  |  | ||||||
|  | static int oss_run_out (HWVoiceOut *hw, int live) | ||||||
|  | { | ||||||
|  |     OSSVoiceOut *oss = (OSSVoiceOut *) hw; | ||||||
|  |     int err, decr; | ||||||
|  |     struct audio_buf_info abinfo; | ||||||
|  |     struct count_info cntinfo; | ||||||
|  |     int bufsize; | ||||||
|  |  | ||||||
|  |     bufsize = hw->samples << hw->info.shift; | ||||||
|  |  | ||||||
|  |     if (oss->mmapped) { | ||||||
|  |         int bytes, pos; | ||||||
|  |  | ||||||
|  |         err = ioctl (oss->fd, SNDCTL_DSP_GETOPTR, &cntinfo); | ||||||
|  |         if (err < 0) { | ||||||
|  |             oss_logerr (errno, "SNDCTL_DSP_GETOPTR failed\n"); | ||||||
|  |             return 0; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         pos = hw->rpos << hw->info.shift; | ||||||
|  |         bytes = audio_ring_dist (cntinfo.ptr, pos, bufsize); | ||||||
|  |         decr = audio_MIN (bytes >> hw->info.shift, live); | ||||||
|  |     } | ||||||
|  |     else { | ||||||
|  |         err = ioctl (oss->fd, SNDCTL_DSP_GETOSPACE, &abinfo); | ||||||
|  |         if (err < 0) { | ||||||
|  |             oss_logerr (errno, "SNDCTL_DSP_GETOPTR failed\n"); | ||||||
|  |             return 0; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (abinfo.bytes > bufsize) { | ||||||
|  |             trace_oss_invalid_available_size(abinfo.bytes, bufsize); | ||||||
|  |             abinfo.bytes = bufsize; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (abinfo.bytes < 0) { | ||||||
|  |             trace_oss_invalid_available_size(abinfo.bytes, bufsize); | ||||||
|  |             return 0; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         decr = audio_MIN (abinfo.bytes >> hw->info.shift, live); | ||||||
|  |         if (!decr) { | ||||||
|  |             return 0; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     decr = audio_pcm_hw_clip_out (hw, oss->pcm_buf, decr, oss->pending); | ||||||
|  |     oss->pending += decr; | ||||||
|  |     oss_write_pending (oss); | ||||||
|  |  | ||||||
|  |     return decr; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void oss_fini_out (HWVoiceOut *hw) | static void oss_fini_out (HWVoiceOut *hw) | ||||||
| @@ -458,13 +477,18 @@ static void oss_fini_out (HWVoiceOut *hw) | |||||||
|     ldebug ("oss_fini\n"); |     ldebug ("oss_fini\n"); | ||||||
|     oss_anal_close (&oss->fd); |     oss_anal_close (&oss->fd); | ||||||
|  |  | ||||||
|     if (oss->mmapped && hw->buf_emul) { |     if (oss->pcm_buf) { | ||||||
|         err = munmap(hw->buf_emul, hw->size_emul); |         if (oss->mmapped) { | ||||||
|         if (err) { |             err = munmap (oss->pcm_buf, hw->samples << hw->info.shift); | ||||||
|             oss_logerr(errno, "Failed to unmap buffer %p, size %zu\n", |             if (err) { | ||||||
|                        hw->buf_emul, hw->size_emul); |                 oss_logerr (errno, "Failed to unmap buffer %p, size %d\n", | ||||||
|  |                             oss->pcm_buf, hw->samples << hw->info.shift); | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|         hw->buf_emul = NULL; |         else { | ||||||
|  |             g_free (oss->pcm_buf); | ||||||
|  |         } | ||||||
|  |         oss->pcm_buf = NULL; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -476,18 +500,19 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as, | |||||||
|     int endianness; |     int endianness; | ||||||
|     int err; |     int err; | ||||||
|     int fd; |     int fd; | ||||||
|     AudioFormat effective_fmt; |     audfmt_e effective_fmt; | ||||||
|     struct audsettings obt_as; |     struct audsettings obt_as; | ||||||
|     Audiodev *dev = drv_opaque; |     OSSConf *conf = drv_opaque; | ||||||
|     AudiodevOssOptions *oopts = &dev->u.oss; |  | ||||||
|  |  | ||||||
|     oss->fd = -1; |     oss->fd = -1; | ||||||
|  |  | ||||||
|     req.fmt = aud_to_ossfmt (as->fmt, as->endianness); |     req.fmt = aud_to_ossfmt (as->fmt, as->endianness); | ||||||
|     req.freq = as->freq; |     req.freq = as->freq; | ||||||
|     req.nchannels = as->nchannels; |     req.nchannels = as->nchannels; | ||||||
|  |     req.fragsize = conf->fragsize; | ||||||
|  |     req.nfrags = conf->nfrags; | ||||||
|  |  | ||||||
|     if (oss_open(0, &req, as, &obt, &fd, dev)) { |     if (oss_open (0, &req, &obt, &fd, conf)) { | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -506,29 +531,28 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as, | |||||||
|     oss->nfrags = obt.nfrags; |     oss->nfrags = obt.nfrags; | ||||||
|     oss->fragsize = obt.fragsize; |     oss->fragsize = obt.fragsize; | ||||||
|  |  | ||||||
|     if (obt.nfrags * obt.fragsize % hw->info.bytes_per_frame) { |     if (obt.nfrags * obt.fragsize & hw->info.align) { | ||||||
|         dolog ("warning: Misaligned DAC buffer, size %d, alignment %d\n", |         dolog ("warning: Misaligned DAC buffer, size %d, alignment %d\n", | ||||||
|                obt.nfrags * obt.fragsize, hw->info.bytes_per_frame); |                obt.nfrags * obt.fragsize, hw->info.align + 1); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     hw->samples = (obt.nfrags * obt.fragsize) / hw->info.bytes_per_frame; |     hw->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift; | ||||||
|  |  | ||||||
|     oss->mmapped = 0; |     oss->mmapped = 0; | ||||||
|     if (oopts->has_try_mmap && oopts->try_mmap) { |     if (conf->try_mmap) { | ||||||
|         hw->size_emul = hw->samples * hw->info.bytes_per_frame; |         oss->pcm_buf = mmap ( | ||||||
|         hw->buf_emul = mmap( |  | ||||||
|             NULL, |             NULL, | ||||||
|             hw->size_emul, |             hw->samples << hw->info.shift, | ||||||
|             PROT_READ | PROT_WRITE, |             PROT_READ | PROT_WRITE, | ||||||
|             MAP_SHARED, |             MAP_SHARED, | ||||||
|             fd, |             fd, | ||||||
|             0 |             0 | ||||||
|             ); |             ); | ||||||
|         if (hw->buf_emul == MAP_FAILED) { |         if (oss->pcm_buf == MAP_FAILED) { | ||||||
|             oss_logerr(errno, "Failed to map %zu bytes of DAC\n", |             oss_logerr (errno, "Failed to map %d bytes of DAC\n", | ||||||
|                        hw->size_emul); |                         hw->samples << hw->info.shift); | ||||||
|             hw->buf_emul = NULL; |         } | ||||||
|         } else { |         else { | ||||||
|             int err; |             int err; | ||||||
|             int trig = 0; |             int trig = 0; | ||||||
|             if (ioctl (fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) { |             if (ioctl (fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) { | ||||||
| @@ -548,65 +572,92 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as, | |||||||
|             } |             } | ||||||
|  |  | ||||||
|             if (!oss->mmapped) { |             if (!oss->mmapped) { | ||||||
|                 err = munmap(hw->buf_emul, hw->size_emul); |                 err = munmap (oss->pcm_buf, hw->samples << hw->info.shift); | ||||||
|                 if (err) { |                 if (err) { | ||||||
|                     oss_logerr(errno, "Failed to unmap buffer %p size %zu\n", |                     oss_logerr (errno, "Failed to unmap buffer %p size %d\n", | ||||||
|                                hw->buf_emul, hw->size_emul); |                                 oss->pcm_buf, hw->samples << hw->info.shift); | ||||||
|                 } |                 } | ||||||
|                 hw->buf_emul = NULL; |  | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     if (!oss->mmapped) { | ||||||
|  |         oss->pcm_buf = audio_calloc(__func__, | ||||||
|  |                                     hw->samples, | ||||||
|  |                                     1 << hw->info.shift); | ||||||
|  |         if (!oss->pcm_buf) { | ||||||
|  |             dolog ( | ||||||
|  |                 "Could not allocate DAC buffer (%d samples, each %d bytes)\n", | ||||||
|  |                 hw->samples, | ||||||
|  |                 1 << hw->info.shift | ||||||
|  |                 ); | ||||||
|  |             oss_anal_close (&fd); | ||||||
|  |             return -1; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     oss->fd = fd; |     oss->fd = fd; | ||||||
|     oss->dev = dev; |     oss->conf = conf; | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void oss_enable_out(HWVoiceOut *hw, bool enable) | static int oss_ctl_out (HWVoiceOut *hw, int cmd, ...) | ||||||
| { | { | ||||||
|     int trig; |     int trig; | ||||||
|     OSSVoiceOut *oss = (OSSVoiceOut *) hw; |     OSSVoiceOut *oss = (OSSVoiceOut *) hw; | ||||||
|     AudiodevOssPerDirectionOptions *opdo = oss->dev->u.oss.out; |  | ||||||
|  |  | ||||||
|     if (enable) { |     switch (cmd) { | ||||||
|         bool poll_mode = opdo->try_poll; |     case VOICE_ENABLE: | ||||||
|  |         { | ||||||
|  |             va_list ap; | ||||||
|  |             int poll_mode; | ||||||
|  |  | ||||||
|         ldebug("enabling voice\n"); |             va_start (ap, cmd); | ||||||
|         if (poll_mode) { |             poll_mode = va_arg (ap, int); | ||||||
|             oss_poll_out(hw); |             va_end (ap); | ||||||
|             poll_mode = 0; |  | ||||||
|  |             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) { |     case VOICE_DISABLE: | ||||||
|             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 { |  | ||||||
|         if (hw->poll_mode) { |         if (hw->poll_mode) { | ||||||
|             qemu_set_fd_handler (oss->fd, NULL, NULL, NULL); |             qemu_set_fd_handler (oss->fd, NULL, NULL, NULL); | ||||||
|             hw->poll_mode = 0; |             hw->poll_mode = 0; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (!oss->mmapped) { |         if (!oss->mmapped) { | ||||||
|             return; |             return 0; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         ldebug ("disabling voice\n"); |         ldebug ("disabling voice\n"); | ||||||
|         trig = 0; |         trig = 0; | ||||||
|         if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) { |         if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) { | ||||||
|             oss_logerr (errno, "SNDCTL_DSP_SETTRIGGER 0 failed\n"); |             oss_logerr (errno, "SNDCTL_DSP_SETTRIGGER 0 failed\n"); | ||||||
|             return; |             return -1; | ||||||
|         } |         } | ||||||
|  |         break; | ||||||
|     } |     } | ||||||
|  |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) | static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) | ||||||
| @@ -616,16 +667,18 @@ static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) | |||||||
|     int endianness; |     int endianness; | ||||||
|     int err; |     int err; | ||||||
|     int fd; |     int fd; | ||||||
|     AudioFormat effective_fmt; |     audfmt_e effective_fmt; | ||||||
|     struct audsettings obt_as; |     struct audsettings obt_as; | ||||||
|     Audiodev *dev = drv_opaque; |     OSSConf *conf = drv_opaque; | ||||||
|  |  | ||||||
|     oss->fd = -1; |     oss->fd = -1; | ||||||
|  |  | ||||||
|     req.fmt = aud_to_ossfmt (as->fmt, as->endianness); |     req.fmt = aud_to_ossfmt (as->fmt, as->endianness); | ||||||
|     req.freq = as->freq; |     req.freq = as->freq; | ||||||
|     req.nchannels = as->nchannels; |     req.nchannels = as->nchannels; | ||||||
|     if (oss_open(1, &req, as, &obt, &fd, dev)) { |     req.fragsize = conf->fragsize; | ||||||
|  |     req.nfrags = conf->nfrags; | ||||||
|  |     if (oss_open (1, &req, &obt, &fd, conf)) { | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -644,15 +697,22 @@ static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) | |||||||
|     oss->nfrags = obt.nfrags; |     oss->nfrags = obt.nfrags; | ||||||
|     oss->fragsize = obt.fragsize; |     oss->fragsize = obt.fragsize; | ||||||
|  |  | ||||||
|     if (obt.nfrags * obt.fragsize % hw->info.bytes_per_frame) { |     if (obt.nfrags * obt.fragsize & hw->info.align) { | ||||||
|         dolog ("warning: Misaligned ADC buffer, size %d, alignment %d\n", |         dolog ("warning: Misaligned ADC buffer, size %d, alignment %d\n", | ||||||
|                obt.nfrags * obt.fragsize, hw->info.bytes_per_frame); |                obt.nfrags * obt.fragsize, hw->info.align + 1); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     hw->samples = (obt.nfrags * obt.fragsize) / hw->info.bytes_per_frame; |     hw->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift; | ||||||
|  |     oss->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift); | ||||||
|  |     if (!oss->pcm_buf) { | ||||||
|  |         dolog ("Could not allocate ADC buffer (%d samples, each %d bytes)\n", | ||||||
|  |                hw->samples, 1 << hw->info.shift); | ||||||
|  |         oss_anal_close (&fd); | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     oss->fd = fd; |     oss->fd = fd; | ||||||
|     oss->dev = dev; |     oss->conf = conf; | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -661,106 +721,211 @@ static void oss_fini_in (HWVoiceIn *hw) | |||||||
|     OSSVoiceIn *oss = (OSSVoiceIn *) hw; |     OSSVoiceIn *oss = (OSSVoiceIn *) hw; | ||||||
|  |  | ||||||
|     oss_anal_close (&oss->fd); |     oss_anal_close (&oss->fd); | ||||||
|  |  | ||||||
|  |     g_free(oss->pcm_buf); | ||||||
|  |     oss->pcm_buf = NULL; | ||||||
| } | } | ||||||
|  |  | ||||||
| static size_t oss_read(HWVoiceIn *hw, void *buf, size_t len) | static int oss_run_in (HWVoiceIn *hw) | ||||||
| { | { | ||||||
|     OSSVoiceIn *oss = (OSSVoiceIn *) hw; |     OSSVoiceIn *oss = (OSSVoiceIn *) hw; | ||||||
|     size_t pos = 0; |     int hwshift = hw->info.shift; | ||||||
|  |     int i; | ||||||
|  |     int live = audio_pcm_hw_get_live_in (hw); | ||||||
|  |     int dead = hw->samples - live; | ||||||
|  |     size_t read_samples = 0; | ||||||
|  |     struct { | ||||||
|  |         int add; | ||||||
|  |         int len; | ||||||
|  |     } bufs[2] = { | ||||||
|  |         { .add = hw->wpos, .len = 0 }, | ||||||
|  |         { .add = 0,        .len = 0 } | ||||||
|  |     }; | ||||||
|  |  | ||||||
|     while (len) { |     if (!dead) { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (hw->wpos + dead > hw->samples) { | ||||||
|  |         bufs[0].len = (hw->samples - hw->wpos) << hwshift; | ||||||
|  |         bufs[1].len = (dead - (hw->samples - hw->wpos)) << hwshift; | ||||||
|  |     } | ||||||
|  |     else { | ||||||
|  |         bufs[0].len = dead << hwshift; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     for (i = 0; i < 2; ++i) { | ||||||
|         ssize_t nread; |         ssize_t nread; | ||||||
|  |  | ||||||
|         void *dst = advance(buf, pos); |         if (bufs[i].len) { | ||||||
|         nread = read(oss->fd, dst, len); |             void *p = advance (oss->pcm_buf, bufs[i].add << hwshift); | ||||||
|  |             nread = read (oss->fd, p, bufs[i].len); | ||||||
|  |  | ||||||
|         if (nread == -1) { |             if (nread > 0) { | ||||||
|             switch (errno) { |                 if (nread & hw->info.align) { | ||||||
|             case EINTR: |                     dolog ("warning: Misaligned read %zd (requested %d), " | ||||||
|             case EAGAIN: |                            "alignment %d\n", nread, bufs[i].add << hwshift, | ||||||
|                 break; |                            hw->info.align + 1); | ||||||
|             default: |                 } | ||||||
|                 oss_logerr(errno, "Failed to read %zu bytes of audio (to %p)\n", |                 read_samples += nread >> hwshift; | ||||||
|                            len, dst); |                 hw->conv (hw->conv_buf + bufs[i].add, p, nread >> hwshift); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             if (bufs[i].len - nread) { | ||||||
|  |                 if (nread == -1) { | ||||||
|  |                     switch (errno) { | ||||||
|  |                     case EINTR: | ||||||
|  |                     case EAGAIN: | ||||||
|  |                         break; | ||||||
|  |                     default: | ||||||
|  |                         oss_logerr ( | ||||||
|  |                             errno, | ||||||
|  |                             "Failed to read %d bytes of audio (to %p)\n", | ||||||
|  |                             bufs[i].len, p | ||||||
|  |                             ); | ||||||
|  |                         break; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         pos += nread; |  | ||||||
|         len -= nread; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return pos; |     hw->wpos = (hw->wpos + read_samples) % hw->samples; | ||||||
|  |     return read_samples; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void oss_enable_in(HWVoiceIn *hw, bool enable) | static int oss_read (SWVoiceIn *sw, void *buf, int size) | ||||||
|  | { | ||||||
|  |     return audio_pcm_sw_read (sw, buf, size); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int oss_ctl_in (HWVoiceIn *hw, int cmd, ...) | ||||||
| { | { | ||||||
|     OSSVoiceIn *oss = (OSSVoiceIn *) hw; |     OSSVoiceIn *oss = (OSSVoiceIn *) hw; | ||||||
|     AudiodevOssPerDirectionOptions *opdo = oss->dev->u.oss.out; |  | ||||||
|  |  | ||||||
|     if (enable) { |     switch (cmd) { | ||||||
|         bool poll_mode = opdo->try_poll; |     case VOICE_ENABLE: | ||||||
|  |         { | ||||||
|  |             va_list ap; | ||||||
|  |             int poll_mode; | ||||||
|  |  | ||||||
|         if (poll_mode) { |             va_start (ap, cmd); | ||||||
|             oss_poll_in(hw); |             poll_mode = va_arg (ap, int); | ||||||
|             poll_mode = 0; |             va_end (ap); | ||||||
|  |  | ||||||
|  |             if (poll_mode) { | ||||||
|  |                 oss_poll_in (hw); | ||||||
|  |                 poll_mode = 0; | ||||||
|  |             } | ||||||
|  |             hw->poll_mode = poll_mode; | ||||||
|         } |         } | ||||||
|         hw->poll_mode = poll_mode; |         break; | ||||||
|     } else { |  | ||||||
|  |     case VOICE_DISABLE: | ||||||
|         if (hw->poll_mode) { |         if (hw->poll_mode) { | ||||||
|             hw->poll_mode = 0; |             hw->poll_mode = 0; | ||||||
|             qemu_set_fd_handler (oss->fd, NULL, NULL, NULL); |             qemu_set_fd_handler (oss->fd, NULL, NULL, NULL); | ||||||
|         } |         } | ||||||
|  |         break; | ||||||
|     } |     } | ||||||
|  |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void oss_init_per_direction(AudiodevOssPerDirectionOptions *opdo) | static OSSConf glob_conf = { | ||||||
|  |     .try_mmap = 0, | ||||||
|  |     .nfrags = 4, | ||||||
|  |     .fragsize = 4096, | ||||||
|  |     .devpath_out = "/dev/dsp", | ||||||
|  |     .devpath_in = "/dev/dsp", | ||||||
|  |     .exclusive = 0, | ||||||
|  |     .policy = 5 | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static void *oss_audio_init (void) | ||||||
| { | { | ||||||
|     if (!opdo->has_try_poll) { |     OSSConf *conf = g_malloc(sizeof(OSSConf)); | ||||||
|         opdo->try_poll = true; |     *conf = glob_conf; | ||||||
|         opdo->has_try_poll = true; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void *oss_audio_init(Audiodev *dev) |     if (access(conf->devpath_in, R_OK | W_OK) < 0 || | ||||||
| { |         access(conf->devpath_out, R_OK | W_OK) < 0) { | ||||||
|     AudiodevOssOptions *oopts; |         g_free(conf); | ||||||
|     assert(dev->driver == AUDIODEV_DRIVER_OSS); |  | ||||||
|  |  | ||||||
|     oopts = &dev->u.oss; |  | ||||||
|     oss_init_per_direction(oopts->in); |  | ||||||
|     oss_init_per_direction(oopts->out); |  | ||||||
|  |  | ||||||
|     if (access(oopts->in->has_dev ? oopts->in->dev : "/dev/dsp", |  | ||||||
|                R_OK | W_OK) < 0 || |  | ||||||
|         access(oopts->out->has_dev ? oopts->out->dev : "/dev/dsp", |  | ||||||
|                R_OK | W_OK) < 0) { |  | ||||||
|         return NULL; |         return NULL; | ||||||
|     } |     } | ||||||
|     return dev; |     return conf; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void oss_audio_fini (void *opaque) | static void oss_audio_fini (void *opaque) | ||||||
| { | { | ||||||
|  |     g_free(opaque); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static struct audio_option oss_options[] = { | ||||||
|  |     { | ||||||
|  |         .name  = "FRAGSIZE", | ||||||
|  |         .tag   = AUD_OPT_INT, | ||||||
|  |         .valp  = &glob_conf.fragsize, | ||||||
|  |         .descr = "Fragment size in bytes" | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |         .name  = "NFRAGS", | ||||||
|  |         .tag   = AUD_OPT_INT, | ||||||
|  |         .valp  = &glob_conf.nfrags, | ||||||
|  |         .descr = "Number of fragments" | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |         .name  = "MMAP", | ||||||
|  |         .tag   = AUD_OPT_BOOL, | ||||||
|  |         .valp  = &glob_conf.try_mmap, | ||||||
|  |         .descr = "Try using memory mapped access" | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |         .name  = "DAC_DEV", | ||||||
|  |         .tag   = AUD_OPT_STR, | ||||||
|  |         .valp  = &glob_conf.devpath_out, | ||||||
|  |         .descr = "Path to DAC device" | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |         .name  = "ADC_DEV", | ||||||
|  |         .tag   = AUD_OPT_STR, | ||||||
|  |         .valp  = &glob_conf.devpath_in, | ||||||
|  |         .descr = "Path to ADC device" | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |         .name  = "EXCLUSIVE", | ||||||
|  |         .tag   = AUD_OPT_BOOL, | ||||||
|  |         .valp  = &glob_conf.exclusive, | ||||||
|  |         .descr = "Open device in exclusive mode (vmix won't work)" | ||||||
|  |     }, | ||||||
|  | #ifdef USE_DSP_POLICY | ||||||
|  |     { | ||||||
|  |         .name  = "POLICY", | ||||||
|  |         .tag   = AUD_OPT_INT, | ||||||
|  |         .valp  = &glob_conf.policy, | ||||||
|  |         .descr = "Set the timing policy of the device, -1 to use fragment mode", | ||||||
|  |     }, | ||||||
|  | #endif | ||||||
|  |     { /* End of list */ } | ||||||
|  | }; | ||||||
|  |  | ||||||
| static struct audio_pcm_ops oss_pcm_ops = { | static struct audio_pcm_ops oss_pcm_ops = { | ||||||
|     .init_out = oss_init_out, |     .init_out = oss_init_out, | ||||||
|     .fini_out = oss_fini_out, |     .fini_out = oss_fini_out, | ||||||
|  |     .run_out  = oss_run_out, | ||||||
|     .write    = oss_write, |     .write    = oss_write, | ||||||
|     .get_buffer_out = oss_get_buffer_out, |     .ctl_out  = oss_ctl_out, | ||||||
|     .put_buffer_out = oss_put_buffer_out, |  | ||||||
|     .enable_out = oss_enable_out, |  | ||||||
|  |  | ||||||
|     .init_in  = oss_init_in, |     .init_in  = oss_init_in, | ||||||
|     .fini_in  = oss_fini_in, |     .fini_in  = oss_fini_in, | ||||||
|  |     .run_in   = oss_run_in, | ||||||
|     .read     = oss_read, |     .read     = oss_read, | ||||||
|     .enable_in = oss_enable_in |     .ctl_in   = oss_ctl_in | ||||||
| }; | }; | ||||||
|  |  | ||||||
| static struct audio_driver oss_audio_driver = { | static struct audio_driver oss_audio_driver = { | ||||||
|     .name           = "oss", |     .name           = "oss", | ||||||
|     .descr          = "OSS http://www.opensound.com", |     .descr          = "OSS http://www.opensound.com", | ||||||
|  |     .options        = oss_options, | ||||||
|     .init           = oss_audio_init, |     .init           = oss_audio_init, | ||||||
|     .fini           = oss_audio_fini, |     .fini           = oss_audio_fini, | ||||||
|     .pcm_ops        = &oss_pcm_ops, |     .pcm_ops        = &oss_pcm_ops, | ||||||
|   | |||||||
							
								
								
									
										1004
									
								
								audio/paaudio.c
									
									
									
									
									
								
							
							
						
						
									
										1004
									
								
								audio/paaudio.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -28,7 +28,7 @@ | |||||||
|  * Return number of samples processed. |  * Return number of samples processed. | ||||||
|  */ |  */ | ||||||
| void NAME (void *opaque, struct st_sample *ibuf, struct st_sample *obuf, | void NAME (void *opaque, struct st_sample *ibuf, struct st_sample *obuf, | ||||||
|            size_t *isamp, size_t *osamp) |            int *isamp, int *osamp) | ||||||
| { | { | ||||||
|     struct rate *rate = opaque; |     struct rate *rate = opaque; | ||||||
|     struct st_sample *istart, *iend; |     struct st_sample *istart, *iend; | ||||||
|   | |||||||
							
								
								
									
										303
									
								
								audio/sdlaudio.c
									
									
									
									
									
								
							
							
						
						
									
										303
									
								
								audio/sdlaudio.c
									
									
									
									
									
								
							| @@ -21,11 +21,10 @@ | |||||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||||
|  * THE SOFTWARE. |  * THE SOFTWARE. | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #include "qemu/osdep.h" | #include "qemu/osdep.h" | ||||||
| #include <SDL.h> | #include <SDL.h> | ||||||
| #include <SDL_thread.h> | #include <SDL_thread.h> | ||||||
| #include "qemu/module.h" | #include "qemu-common.h" | ||||||
| #include "audio.h" | #include "audio.h" | ||||||
|  |  | ||||||
| #ifndef _WIN32 | #ifndef _WIN32 | ||||||
| @@ -39,15 +38,31 @@ | |||||||
| #define AUDIO_CAP "sdl" | #define AUDIO_CAP "sdl" | ||||||
| #include "audio_int.h" | #include "audio_int.h" | ||||||
|  |  | ||||||
|  | #define USE_SEMAPHORE (SDL_MAJOR_VERSION < 2) | ||||||
|  |  | ||||||
| typedef struct SDLVoiceOut { | typedef struct SDLVoiceOut { | ||||||
|     HWVoiceOut hw; |     HWVoiceOut hw; | ||||||
|  |     int live; | ||||||
|  | #if USE_SEMAPHORE | ||||||
|  |     int rpos; | ||||||
|  | #endif | ||||||
|  |     int decr; | ||||||
| } SDLVoiceOut; | } SDLVoiceOut; | ||||||
|  |  | ||||||
|  | static struct { | ||||||
|  |     int nb_samples; | ||||||
|  | } conf = { | ||||||
|  |     .nb_samples = 1024 | ||||||
|  | }; | ||||||
|  |  | ||||||
| static struct SDLAudioState { | static struct SDLAudioState { | ||||||
|     int exit; |     int exit; | ||||||
|  | #if USE_SEMAPHORE | ||||||
|  |     SDL_mutex *mutex; | ||||||
|  |     SDL_sem *sem; | ||||||
|  | #endif | ||||||
|     int initialized; |     int initialized; | ||||||
|     bool driver_created; |     bool driver_created; | ||||||
|     Audiodev *dev; |  | ||||||
| } glob_sdl; | } glob_sdl; | ||||||
| typedef struct SDLAudioState SDLAudioState; | typedef struct SDLAudioState SDLAudioState; | ||||||
|  |  | ||||||
| @@ -62,19 +77,79 @@ static void GCC_FMT_ATTR (1, 2) sdl_logerr (const char *fmt, ...) | |||||||
|     AUD_log (AUDIO_CAP, "Reason: %s\n", SDL_GetError ()); |     AUD_log (AUDIO_CAP, "Reason: %s\n", SDL_GetError ()); | ||||||
| } | } | ||||||
|  |  | ||||||
| static int aud_to_sdlfmt (AudioFormat fmt) | static int sdl_lock (SDLAudioState *s, const char *forfn) | ||||||
|  | { | ||||||
|  | #if USE_SEMAPHORE | ||||||
|  |     if (SDL_LockMutex (s->mutex)) { | ||||||
|  |         sdl_logerr ("SDL_LockMutex for %s failed\n", forfn); | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  | #else | ||||||
|  |     SDL_LockAudio(); | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int sdl_unlock (SDLAudioState *s, const char *forfn) | ||||||
|  | { | ||||||
|  | #if USE_SEMAPHORE | ||||||
|  |     if (SDL_UnlockMutex (s->mutex)) { | ||||||
|  |         sdl_logerr ("SDL_UnlockMutex for %s failed\n", forfn); | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  | #else | ||||||
|  |     SDL_UnlockAudio(); | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int sdl_post (SDLAudioState *s, const char *forfn) | ||||||
|  | { | ||||||
|  | #if USE_SEMAPHORE | ||||||
|  |     if (SDL_SemPost (s->sem)) { | ||||||
|  |         sdl_logerr ("SDL_SemPost for %s failed\n", forfn); | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #if USE_SEMAPHORE | ||||||
|  | static int sdl_wait (SDLAudioState *s, const char *forfn) | ||||||
|  | { | ||||||
|  |     if (SDL_SemWait (s->sem)) { | ||||||
|  |         sdl_logerr ("SDL_SemWait for %s failed\n", forfn); | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | static int sdl_unlock_and_post (SDLAudioState *s, const char *forfn) | ||||||
|  | { | ||||||
|  |     if (sdl_unlock (s, forfn)) { | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return sdl_post (s, forfn); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int aud_to_sdlfmt (audfmt_e fmt) | ||||||
| { | { | ||||||
|     switch (fmt) { |     switch (fmt) { | ||||||
|     case AUDIO_FORMAT_S8: |     case AUD_FMT_S8: | ||||||
|         return AUDIO_S8; |         return AUDIO_S8; | ||||||
|  |  | ||||||
|     case AUDIO_FORMAT_U8: |     case AUD_FMT_U8: | ||||||
|         return AUDIO_U8; |         return AUDIO_U8; | ||||||
|  |  | ||||||
|     case AUDIO_FORMAT_S16: |     case AUD_FMT_S16: | ||||||
|         return AUDIO_S16LSB; |         return AUDIO_S16LSB; | ||||||
|  |  | ||||||
|     case AUDIO_FORMAT_U16: |     case AUD_FMT_U16: | ||||||
|         return AUDIO_U16LSB; |         return AUDIO_U16LSB; | ||||||
|  |  | ||||||
|     default: |     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) { |     switch (sdlfmt) { | ||||||
|     case AUDIO_S8: |     case AUDIO_S8: | ||||||
|         *endianness = 0; |         *endianness = 0; | ||||||
|         *fmt = AUDIO_FORMAT_S8; |         *fmt = AUD_FMT_S8; | ||||||
|         break; |         break; | ||||||
|  |  | ||||||
|     case AUDIO_U8: |     case AUDIO_U8: | ||||||
|         *endianness = 0; |         *endianness = 0; | ||||||
|         *fmt = AUDIO_FORMAT_U8; |         *fmt = AUD_FMT_U8; | ||||||
|         break; |         break; | ||||||
|  |  | ||||||
|     case AUDIO_S16LSB: |     case AUDIO_S16LSB: | ||||||
|         *endianness = 0; |         *endianness = 0; | ||||||
|         *fmt = AUDIO_FORMAT_S16; |         *fmt = AUD_FMT_S16; | ||||||
|         break; |         break; | ||||||
|  |  | ||||||
|     case AUDIO_U16LSB: |     case AUDIO_U16LSB: | ||||||
|         *endianness = 0; |         *endianness = 0; | ||||||
|         *fmt = AUDIO_FORMAT_U16; |         *fmt = AUD_FMT_U16; | ||||||
|         break; |         break; | ||||||
|  |  | ||||||
|     case AUDIO_S16MSB: |     case AUDIO_S16MSB: | ||||||
|         *endianness = 1; |         *endianness = 1; | ||||||
|         *fmt = AUDIO_FORMAT_S16; |         *fmt = AUD_FMT_S16; | ||||||
|         break; |         break; | ||||||
|  |  | ||||||
|     case AUDIO_U16MSB: |     case AUDIO_U16MSB: | ||||||
|         *endianness = 1; |         *endianness = 1; | ||||||
|         *fmt = AUDIO_FORMAT_U16; |         *fmt = AUD_FMT_U16; | ||||||
|         break; |         break; | ||||||
|  |  | ||||||
|     default: |     default: | ||||||
| @@ -168,9 +243,9 @@ static int sdl_open (SDL_AudioSpec *req, SDL_AudioSpec *obt) | |||||||
| static void sdl_close (SDLAudioState *s) | static void sdl_close (SDLAudioState *s) | ||||||
| { | { | ||||||
|     if (s->initialized) { |     if (s->initialized) { | ||||||
|         SDL_LockAudio(); |         sdl_lock (s, "sdl_close"); | ||||||
|         s->exit = 1; |         s->exit = 1; | ||||||
|         SDL_UnlockAudio(); |         sdl_unlock_and_post (s, "sdl_close"); | ||||||
|         SDL_PauseAudio (1); |         SDL_PauseAudio (1); | ||||||
|         SDL_CloseAudio (); |         SDL_CloseAudio (); | ||||||
|         s->initialized = 0; |         s->initialized = 0; | ||||||
| @@ -182,59 +257,119 @@ static void sdl_callback (void *opaque, Uint8 *buf, int len) | |||||||
|     SDLVoiceOut *sdl = opaque; |     SDLVoiceOut *sdl = opaque; | ||||||
|     SDLAudioState *s = &glob_sdl; |     SDLAudioState *s = &glob_sdl; | ||||||
|     HWVoiceOut *hw = &sdl->hw; |     HWVoiceOut *hw = &sdl->hw; | ||||||
|  |     int samples = len >> hw->info.shift; | ||||||
|  |  | ||||||
|     if (s->exit) { |     if (s->exit) { | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* dolog ("in callback samples=%zu live=%zu\n", samples, sdl->live); */ |     while (samples) { | ||||||
|  |         int to_mix, decr; | ||||||
|  |  | ||||||
|     while (hw->pending_emul && len) { |         /* dolog ("in callback samples=%d\n", samples); */ | ||||||
|         size_t write_len; | #if USE_SEMAPHORE | ||||||
|         ssize_t start = ((ssize_t) hw->pos_emul) - hw->pending_emul; |         sdl_wait (s, "sdl_callback"); | ||||||
|         if (start < 0) { |         if (s->exit) { | ||||||
|             start += hw->size_emul; |             return; | ||||||
|         } |         } | ||||||
|         assert(start >= 0 && start < hw->size_emul); |  | ||||||
|  |  | ||||||
|         write_len = MIN(MIN(hw->pending_emul, len), |         if (sdl_lock (s, "sdl_callback")) { | ||||||
|                         hw->size_emul - start); |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         memcpy(buf, hw->buf_emul + start, write_len); |         if (audio_bug(__func__, sdl->live < 0 || sdl->live > hw->samples)) { | ||||||
|         hw->pending_emul -= write_len; |             dolog ("sdl->live=%d hw->samples=%d\n", | ||||||
|         len -= write_len; |                    sdl->live, hw->samples); | ||||||
|         buf += write_len; |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (!sdl->live) { | ||||||
|  |             goto again; | ||||||
|  |         } | ||||||
|  | #else | ||||||
|  |         if (s->exit || !sdl->live) { | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |         /* dolog ("in callback live=%d\n", live); */ | ||||||
|  |         to_mix = audio_MIN (samples, sdl->live); | ||||||
|  |         decr = to_mix; | ||||||
|  |         while (to_mix) { | ||||||
|  |             int chunk = audio_MIN (to_mix, hw->samples - hw->rpos); | ||||||
|  |             struct st_sample *src = hw->mix_buf + hw->rpos; | ||||||
|  |  | ||||||
|  |             /* dolog ("in callback to_mix %d, chunk %d\n", to_mix, chunk); */ | ||||||
|  |             hw->clip (buf, src, chunk); | ||||||
|  | #if USE_SEMAPHORE | ||||||
|  |             sdl->rpos = (sdl->rpos + chunk) % hw->samples; | ||||||
|  | #else | ||||||
|  |             hw->rpos = (hw->rpos + chunk) % hw->samples; | ||||||
|  | #endif | ||||||
|  |             to_mix -= chunk; | ||||||
|  |             buf += chunk << hw->info.shift; | ||||||
|  |         } | ||||||
|  |         samples -= decr; | ||||||
|  |         sdl->live -= decr; | ||||||
|  |         sdl->decr += decr; | ||||||
|  |  | ||||||
|  | #if USE_SEMAPHORE | ||||||
|  |     again: | ||||||
|  |         if (sdl_unlock (s, "sdl_callback")) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | #endif | ||||||
|     } |     } | ||||||
|  |     /* dolog ("done len=%d\n", len); */ | ||||||
|  |  | ||||||
|     /* clear remaining buffer that we couldn't fill with data */ | #if (SDL_MAJOR_VERSION >= 2) | ||||||
|     if (len) { |     /* SDL2 does not clear the remaining buffer for us, so do it on our own */ | ||||||
|         memset(buf, 0, len); |     if (samples) { | ||||||
|  |         memset(buf, 0, samples << hw->info.shift); | ||||||
|     } |     } | ||||||
|  | #endif | ||||||
| } | } | ||||||
|  |  | ||||||
| #define SDL_WRAPPER_FUNC(name, ret_type, args_decl, args, fail, unlock) \ | static int sdl_write_out (SWVoiceOut *sw, void *buf, int len) | ||||||
|     static ret_type glue(sdl_, name)args_decl                           \ | { | ||||||
|     {                                                                   \ |     return audio_pcm_sw_write (sw, buf, len); | ||||||
|         ret_type ret;                                                   \ | } | ||||||
|                                                                         \ |  | ||||||
|         SDL_LockAudio();                                                \ | static int sdl_run_out (HWVoiceOut *hw, int live) | ||||||
|                                                                         \ | { | ||||||
|         ret = glue(audio_generic_, name)args;                           \ |     int decr; | ||||||
|                                                                         \ |     SDLVoiceOut *sdl = (SDLVoiceOut *) hw; | ||||||
|         SDL_UnlockAudio();                                              \ |     SDLAudioState *s = &glob_sdl; | ||||||
|         return ret;                                                     \ |  | ||||||
|  |     if (sdl_lock (s, "sdl_run_out")) { | ||||||
|  |         return 0; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| SDL_WRAPPER_FUNC(get_buffer_out, void *, (HWVoiceOut *hw, size_t *size), |     if (sdl->decr > live) { | ||||||
|                  (hw, size), *size = 0, sdl_unlock) |         ldebug ("sdl->decr %d live %d sdl->live %d\n", | ||||||
| SDL_WRAPPER_FUNC(put_buffer_out_nowrite, size_t, |                 sdl->decr, | ||||||
|                  (HWVoiceOut *hw, void *buf, size_t size), (hw, buf, size), |                 live, | ||||||
|                  /*nothing*/, sdl_unlock_and_post) |                 sdl->live); | ||||||
| SDL_WRAPPER_FUNC(write, size_t, |     } | ||||||
|                  (HWVoiceOut *hw, void *buf, size_t size), (hw, buf, size), |  | ||||||
|                  /*nothing*/, sdl_unlock_and_post) |  | ||||||
|  |  | ||||||
| #undef SDL_WRAPPER_FUNC |     decr = audio_MIN (sdl->decr, live); | ||||||
|  |     sdl->decr -= decr; | ||||||
|  |  | ||||||
|  | #if USE_SEMAPHORE | ||||||
|  |     sdl->live = live - decr; | ||||||
|  |     hw->rpos = sdl->rpos; | ||||||
|  | #else | ||||||
|  |     sdl->live = live; | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |     if (sdl->live > 0) { | ||||||
|  |         sdl_unlock_and_post (s, "sdl_run_out"); | ||||||
|  |     } | ||||||
|  |     else { | ||||||
|  |         sdl_unlock (s, "sdl_run_out"); | ||||||
|  |     } | ||||||
|  |     return decr; | ||||||
|  | } | ||||||
|  |  | ||||||
| static void sdl_fini_out (HWVoiceOut *hw) | static void sdl_fini_out (HWVoiceOut *hw) | ||||||
| { | { | ||||||
| @@ -251,13 +386,13 @@ static int sdl_init_out(HWVoiceOut *hw, struct audsettings *as, | |||||||
|     SDL_AudioSpec req, obt; |     SDL_AudioSpec req, obt; | ||||||
|     int endianness; |     int endianness; | ||||||
|     int err; |     int err; | ||||||
|     AudioFormat effective_fmt; |     audfmt_e effective_fmt; | ||||||
|     struct audsettings obt_as; |     struct audsettings obt_as; | ||||||
|  |  | ||||||
|     req.freq = as->freq; |     req.freq = as->freq; | ||||||
|     req.format = aud_to_sdlfmt (as->fmt); |     req.format = aud_to_sdlfmt (as->fmt); | ||||||
|     req.channels = as->nchannels; |     req.channels = as->nchannels; | ||||||
|     req.samples = audio_buffer_samples(s->dev->u.sdl.out, as, 11610); |     req.samples = conf.nb_samples; | ||||||
|     req.callback = sdl_callback; |     req.callback = sdl_callback; | ||||||
|     req.userdata = sdl; |     req.userdata = sdl; | ||||||
|  |  | ||||||
| @@ -285,12 +420,23 @@ static int sdl_init_out(HWVoiceOut *hw, struct audsettings *as, | |||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void sdl_enable_out(HWVoiceOut *hw, bool enable) | static int sdl_ctl_out (HWVoiceOut *hw, int cmd, ...) | ||||||
| { | { | ||||||
|     SDL_PauseAudio(!enable); |     (void) hw; | ||||||
|  |  | ||||||
|  |     switch (cmd) { | ||||||
|  |     case VOICE_ENABLE: | ||||||
|  |         SDL_PauseAudio (0); | ||||||
|  |         break; | ||||||
|  |  | ||||||
|  |     case VOICE_DISABLE: | ||||||
|  |         SDL_PauseAudio (1); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void *sdl_audio_init(Audiodev *dev) | static void *sdl_audio_init (void) | ||||||
| { | { | ||||||
|     SDLAudioState *s = &glob_sdl; |     SDLAudioState *s = &glob_sdl; | ||||||
|     if (s->driver_created) { |     if (s->driver_created) { | ||||||
| @@ -303,8 +449,24 @@ static void *sdl_audio_init(Audiodev *dev) | |||||||
|         return NULL; |         return NULL; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | #if USE_SEMAPHORE | ||||||
|  |     s->mutex = SDL_CreateMutex (); | ||||||
|  |     if (!s->mutex) { | ||||||
|  |         sdl_logerr ("Failed to create SDL mutex\n"); | ||||||
|  |         SDL_QuitSubSystem (SDL_INIT_AUDIO); | ||||||
|  |         return NULL; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     s->sem = SDL_CreateSemaphore (0); | ||||||
|  |     if (!s->sem) { | ||||||
|  |         sdl_logerr ("Failed to create SDL semaphore\n"); | ||||||
|  |         SDL_DestroyMutex (s->mutex); | ||||||
|  |         SDL_QuitSubSystem (SDL_INIT_AUDIO); | ||||||
|  |         return NULL; | ||||||
|  |     } | ||||||
|  | #endif | ||||||
|  |  | ||||||
|     s->driver_created = true; |     s->driver_created = true; | ||||||
|     s->dev = dev; |  | ||||||
|     return s; |     return s; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -312,23 +474,36 @@ static void sdl_audio_fini (void *opaque) | |||||||
| { | { | ||||||
|     SDLAudioState *s = opaque; |     SDLAudioState *s = opaque; | ||||||
|     sdl_close (s); |     sdl_close (s); | ||||||
|  | #if USE_SEMAPHORE | ||||||
|  |     SDL_DestroySemaphore (s->sem); | ||||||
|  |     SDL_DestroyMutex (s->mutex); | ||||||
|  | #endif | ||||||
|     SDL_QuitSubSystem (SDL_INIT_AUDIO); |     SDL_QuitSubSystem (SDL_INIT_AUDIO); | ||||||
|     s->driver_created = false; |     s->driver_created = false; | ||||||
|     s->dev = NULL; |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static struct audio_option sdl_options[] = { | ||||||
|  |     { | ||||||
|  |         .name  = "SAMPLES", | ||||||
|  |         .tag   = AUD_OPT_INT, | ||||||
|  |         .valp  = &conf.nb_samples, | ||||||
|  |         .descr = "Size of SDL buffer in samples" | ||||||
|  |     }, | ||||||
|  |     { /* End of list */ } | ||||||
|  | }; | ||||||
|  |  | ||||||
| static struct audio_pcm_ops sdl_pcm_ops = { | static struct audio_pcm_ops sdl_pcm_ops = { | ||||||
|     .init_out = sdl_init_out, |     .init_out = sdl_init_out, | ||||||
|     .fini_out = sdl_fini_out, |     .fini_out = sdl_fini_out, | ||||||
|     .write    = sdl_write, |     .run_out  = sdl_run_out, | ||||||
|     .get_buffer_out = sdl_get_buffer_out, |     .write    = sdl_write_out, | ||||||
|     .put_buffer_out = sdl_put_buffer_out_nowrite, |     .ctl_out  = sdl_ctl_out, | ||||||
|     .enable_out = sdl_enable_out, |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| static struct audio_driver sdl_audio_driver = { | static struct audio_driver sdl_audio_driver = { | ||||||
|     .name           = "sdl", |     .name           = "sdl", | ||||||
|     .descr          = "SDL http://www.libsdl.org", |     .descr          = "SDL http://www.libsdl.org", | ||||||
|  |     .options        = sdl_options, | ||||||
|     .init           = sdl_audio_init, |     .init           = sdl_audio_init, | ||||||
|     .fini           = sdl_audio_fini, |     .fini           = sdl_audio_fini, | ||||||
|     .pcm_ops        = &sdl_pcm_ops, |     .pcm_ops        = &sdl_pcm_ops, | ||||||
|   | |||||||
| @@ -18,8 +18,8 @@ | |||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #include "qemu/osdep.h" | #include "qemu/osdep.h" | ||||||
|  | #include "hw/hw.h" | ||||||
| #include "qemu/host-utils.h" | #include "qemu/host-utils.h" | ||||||
| #include "qemu/module.h" |  | ||||||
| #include "qemu/error-report.h" | #include "qemu/error-report.h" | ||||||
| #include "qemu/timer.h" | #include "qemu/timer.h" | ||||||
| #include "ui/qemu-spice.h" | #include "ui/qemu-spice.h" | ||||||
| @@ -40,21 +40,27 @@ | |||||||
| #define LINE_IN_SAMPLES (256 * 4) | #define LINE_IN_SAMPLES (256 * 4) | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | typedef struct SpiceRateCtl { | ||||||
|  |     int64_t               start_ticks; | ||||||
|  |     int64_t               bytes_sent; | ||||||
|  | } SpiceRateCtl; | ||||||
|  |  | ||||||
| typedef struct SpiceVoiceOut { | typedef struct SpiceVoiceOut { | ||||||
|     HWVoiceOut            hw; |     HWVoiceOut            hw; | ||||||
|     SpicePlaybackInstance sin; |     SpicePlaybackInstance sin; | ||||||
|     RateCtl               rate; |     SpiceRateCtl          rate; | ||||||
|     int                   active; |     int                   active; | ||||||
|     uint32_t              *frame; |     uint32_t              *frame; | ||||||
|     uint32_t              fpos; |     uint32_t              *fpos; | ||||||
|     uint32_t              fsize; |     uint32_t              fsize; | ||||||
| } SpiceVoiceOut; | } SpiceVoiceOut; | ||||||
|  |  | ||||||
| typedef struct SpiceVoiceIn { | typedef struct SpiceVoiceIn { | ||||||
|     HWVoiceIn             hw; |     HWVoiceIn             hw; | ||||||
|     SpiceRecordInstance   sin; |     SpiceRecordInstance   sin; | ||||||
|     RateCtl               rate; |     SpiceRateCtl          rate; | ||||||
|     int                   active; |     int                   active; | ||||||
|  |     uint32_t              samples[LINE_IN_SAMPLES]; | ||||||
| } SpiceVoiceIn; | } SpiceVoiceIn; | ||||||
|  |  | ||||||
| static const SpicePlaybackInterface playback_sif = { | static const SpicePlaybackInterface playback_sif = { | ||||||
| @@ -71,7 +77,7 @@ static const SpiceRecordInterface record_sif = { | |||||||
|     .base.minor_version = SPICE_INTERFACE_RECORD_MINOR, |     .base.minor_version = SPICE_INTERFACE_RECORD_MINOR, | ||||||
| }; | }; | ||||||
|  |  | ||||||
| static void *spice_audio_init(Audiodev *dev) | static void *spice_audio_init (void) | ||||||
| { | { | ||||||
|     if (!using_spice) { |     if (!using_spice) { | ||||||
|         return NULL; |         return NULL; | ||||||
| @@ -84,6 +90,32 @@ static void spice_audio_fini (void *opaque) | |||||||
|     /* nothing */ |     /* nothing */ | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static void rate_start (SpiceRateCtl *rate) | ||||||
|  | { | ||||||
|  |     memset (rate, 0, sizeof (*rate)); | ||||||
|  |     rate->start_ticks = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int rate_get_samples (struct audio_pcm_info *info, SpiceRateCtl *rate) | ||||||
|  | { | ||||||
|  |     int64_t now; | ||||||
|  |     int64_t ticks; | ||||||
|  |     int64_t bytes; | ||||||
|  |     int64_t samples; | ||||||
|  |  | ||||||
|  |     now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); | ||||||
|  |     ticks = now - rate->start_ticks; | ||||||
|  |     bytes = muldiv64(ticks, info->bytes_per_second, NANOSECONDS_PER_SECOND); | ||||||
|  |     samples = (bytes - rate->bytes_sent) >> info->shift; | ||||||
|  |     if (samples < 0 || samples > 65536) { | ||||||
|  |         error_report("Resetting rate control (%" PRId64 " samples)", samples); | ||||||
|  |         rate_start(rate); | ||||||
|  |         samples = 0; | ||||||
|  |     } | ||||||
|  |     rate->bytes_sent += samples << info->shift; | ||||||
|  |     return samples; | ||||||
|  | } | ||||||
|  |  | ||||||
| /* playback */ | /* playback */ | ||||||
|  |  | ||||||
| static int line_out_init(HWVoiceOut *hw, struct audsettings *as, | static int line_out_init(HWVoiceOut *hw, struct audsettings *as, | ||||||
| @@ -98,7 +130,7 @@ static int line_out_init(HWVoiceOut *hw, struct audsettings *as, | |||||||
|     settings.freq       = SPICE_INTERFACE_PLAYBACK_FREQ; |     settings.freq       = SPICE_INTERFACE_PLAYBACK_FREQ; | ||||||
| #endif | #endif | ||||||
|     settings.nchannels  = SPICE_INTERFACE_PLAYBACK_CHAN; |     settings.nchannels  = SPICE_INTERFACE_PLAYBACK_CHAN; | ||||||
|     settings.fmt        = AUDIO_FORMAT_S16; |     settings.fmt        = AUD_FMT_S16; | ||||||
|     settings.endianness = AUDIO_HOST_ENDIANNESS; |     settings.endianness = AUDIO_HOST_ENDIANNESS; | ||||||
|  |  | ||||||
|     audio_pcm_init_info (&hw->info, &settings); |     audio_pcm_init_info (&hw->info, &settings); | ||||||
| @@ -120,78 +152,98 @@ static void line_out_fini (HWVoiceOut *hw) | |||||||
|     spice_server_remove_interface (&out->sin.base); |     spice_server_remove_interface (&out->sin.base); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void *line_out_get_buffer(HWVoiceOut *hw, size_t *size) | static int line_out_run (HWVoiceOut *hw, int live) | ||||||
| { | { | ||||||
|     SpiceVoiceOut *out = container_of(hw, SpiceVoiceOut, hw); |     SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw); | ||||||
|  |     int rpos, decr; | ||||||
|  |     int samples; | ||||||
|  |  | ||||||
|     if (!out->frame) { |     if (!live) { | ||||||
|         spice_server_playback_get_buffer(&out->sin, &out->frame, &out->fsize); |         return 0; | ||||||
|         out->fpos = 0; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (out->frame) { |     decr = rate_get_samples (&hw->info, &out->rate); | ||||||
|         *size = audio_rate_get_bytes( |     decr = audio_MIN (live, decr); | ||||||
|             &hw->info, &out->rate, |  | ||||||
|             (out->fsize - out->fpos) * hw->info.bytes_per_frame); |     samples = decr; | ||||||
|     } else { |     rpos = hw->rpos; | ||||||
|         audio_rate_start(&out->rate); |     while (samples) { | ||||||
|  |         int left_till_end_samples = hw->samples - rpos; | ||||||
|  |         int len = audio_MIN (samples, left_till_end_samples); | ||||||
|  |  | ||||||
|  |         if (!out->frame) { | ||||||
|  |             spice_server_playback_get_buffer (&out->sin, &out->frame, &out->fsize); | ||||||
|  |             out->fpos = out->frame; | ||||||
|  |         } | ||||||
|  |         if (out->frame) { | ||||||
|  |             len = audio_MIN (len, out->fsize); | ||||||
|  |             hw->clip (out->fpos, hw->mix_buf + rpos, len); | ||||||
|  |             out->fsize -= len; | ||||||
|  |             out->fpos  += len; | ||||||
|  |             if (out->fsize == 0) { | ||||||
|  |                 spice_server_playback_put_samples (&out->sin, out->frame); | ||||||
|  |                 out->frame = out->fpos = NULL; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         rpos = (rpos + len) % hw->samples; | ||||||
|  |         samples -= len; | ||||||
|     } |     } | ||||||
|     return out->frame + out->fpos; |     hw->rpos = rpos; | ||||||
|  |     return decr; | ||||||
| } | } | ||||||
|  |  | ||||||
| static size_t line_out_put_buffer(HWVoiceOut *hw, void *buf, size_t size) | static int line_out_write (SWVoiceOut *sw, void *buf, int len) | ||||||
| { | { | ||||||
|     SpiceVoiceOut *out = container_of(hw, SpiceVoiceOut, hw); |     return audio_pcm_sw_write (sw, buf, len); | ||||||
|  |  | ||||||
|     assert(buf == out->frame + out->fpos && out->fpos <= out->fsize); |  | ||||||
|     out->fpos += size >> 2; |  | ||||||
|  |  | ||||||
|     if (out->fpos == out->fsize) { /* buffer full */ |  | ||||||
|         spice_server_playback_put_samples(&out->sin, out->frame); |  | ||||||
|         out->frame = NULL; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return size; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static void line_out_enable(HWVoiceOut *hw, bool enable) | static int line_out_ctl (HWVoiceOut *hw, int cmd, ...) | ||||||
| { | { | ||||||
|     SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw); |     SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw); | ||||||
|  |  | ||||||
|     if (enable) { |     switch (cmd) { | ||||||
|  |     case VOICE_ENABLE: | ||||||
|         if (out->active) { |         if (out->active) { | ||||||
|             return; |             break; | ||||||
|         } |         } | ||||||
|         out->active = 1; |         out->active = 1; | ||||||
|         audio_rate_start(&out->rate); |         rate_start (&out->rate); | ||||||
|         spice_server_playback_start (&out->sin); |         spice_server_playback_start (&out->sin); | ||||||
|     } else { |         break; | ||||||
|  |     case VOICE_DISABLE: | ||||||
|         if (!out->active) { |         if (!out->active) { | ||||||
|             return; |             break; | ||||||
|         } |         } | ||||||
|         out->active = 0; |         out->active = 0; | ||||||
|         if (out->frame) { |         if (out->frame) { | ||||||
|             memset(out->frame + out->fpos, 0, (out->fsize - out->fpos) << 2); |             memset (out->fpos, 0, out->fsize << 2); | ||||||
|             spice_server_playback_put_samples (&out->sin, out->frame); |             spice_server_playback_put_samples (&out->sin, out->frame); | ||||||
|             out->frame = NULL; |             out->frame = out->fpos = NULL; | ||||||
|         } |         } | ||||||
|         spice_server_playback_stop (&out->sin); |         spice_server_playback_stop (&out->sin); | ||||||
|     } |         break; | ||||||
| } |     case VOICE_VOLUME: | ||||||
|  |         { | ||||||
| #if ((SPICE_INTERFACE_PLAYBACK_MAJOR >= 1) && (SPICE_INTERFACE_PLAYBACK_MINOR >= 2)) | #if ((SPICE_INTERFACE_PLAYBACK_MAJOR >= 1) && (SPICE_INTERFACE_PLAYBACK_MINOR >= 2)) | ||||||
| static void line_out_volume(HWVoiceOut *hw, Volume *vol) |             SWVoiceOut *sw; | ||||||
| { |             va_list ap; | ||||||
|     SpiceVoiceOut *out = container_of(hw, SpiceVoiceOut, hw); |             uint16_t vol[2]; | ||||||
|     uint16_t svol[2]; |  | ||||||
|  |  | ||||||
|     assert(vol->channels == 2); |             va_start (ap, cmd); | ||||||
|     svol[0] = vol->vol[0] * 257; |             sw = va_arg (ap, SWVoiceOut *); | ||||||
|     svol[1] = vol->vol[1] * 257; |             va_end (ap); | ||||||
|     spice_server_playback_set_volume(&out->sin, 2, svol); |  | ||||||
|     spice_server_playback_set_mute(&out->sin, vol->mute); |             vol[0] = sw->vol.l / ((1ULL << 16) + 1); | ||||||
| } |             vol[1] = sw->vol.r / ((1ULL << 16) + 1); | ||||||
|  |             spice_server_playback_set_volume (&out->sin, 2, vol); | ||||||
|  |             spice_server_playback_set_mute (&out->sin, sw->vol.mute); | ||||||
| #endif | #endif | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
| /* record */ | /* record */ | ||||||
|  |  | ||||||
| @@ -206,7 +258,7 @@ static int line_in_init(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) | |||||||
|     settings.freq       = SPICE_INTERFACE_RECORD_FREQ; |     settings.freq       = SPICE_INTERFACE_RECORD_FREQ; | ||||||
| #endif | #endif | ||||||
|     settings.nchannels  = SPICE_INTERFACE_RECORD_CHAN; |     settings.nchannels  = SPICE_INTERFACE_RECORD_CHAN; | ||||||
|     settings.fmt        = AUDIO_FORMAT_S16; |     settings.fmt        = AUD_FMT_S16; | ||||||
|     settings.endianness = AUDIO_HOST_ENDIANNESS; |     settings.endianness = AUDIO_HOST_ENDIANNESS; | ||||||
|  |  | ||||||
|     audio_pcm_init_info (&hw->info, &settings); |     audio_pcm_init_info (&hw->info, &settings); | ||||||
| @@ -228,79 +280,121 @@ static void line_in_fini (HWVoiceIn *hw) | |||||||
|     spice_server_remove_interface (&in->sin.base); |     spice_server_remove_interface (&in->sin.base); | ||||||
| } | } | ||||||
|  |  | ||||||
| static size_t line_in_read(HWVoiceIn *hw, void *buf, size_t len) | static int line_in_run (HWVoiceIn *hw) | ||||||
| { | { | ||||||
|     SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw); |     SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw); | ||||||
|     uint64_t to_read = audio_rate_get_bytes(&hw->info, &in->rate, len) >> 2; |     int num_samples; | ||||||
|     size_t ready = spice_server_record_get_samples(&in->sin, buf, to_read); |     int ready; | ||||||
|  |     int len[2]; | ||||||
|  |     uint64_t delta_samp; | ||||||
|  |     const uint32_t *samples; | ||||||
|  |  | ||||||
|     /* XXX: do we need this? */ |     if (!(num_samples = hw->samples - audio_pcm_hw_get_live_in (hw))) { | ||||||
|     if (ready == 0) { |         return 0; | ||||||
|         memset(buf, 0, to_read << 2); |  | ||||||
|         ready = to_read; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return ready << 2; |     delta_samp = rate_get_samples (&hw->info, &in->rate); | ||||||
|  |     num_samples = audio_MIN (num_samples, delta_samp); | ||||||
|  |  | ||||||
|  |     ready = spice_server_record_get_samples (&in->sin, in->samples, num_samples); | ||||||
|  |     samples = in->samples; | ||||||
|  |     if (ready == 0) { | ||||||
|  |         static const uint32_t silence[LINE_IN_SAMPLES]; | ||||||
|  |         samples = silence; | ||||||
|  |         ready = LINE_IN_SAMPLES; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     num_samples = audio_MIN (ready, num_samples); | ||||||
|  |  | ||||||
|  |     if (hw->wpos + num_samples > hw->samples) { | ||||||
|  |         len[0] = hw->samples - hw->wpos; | ||||||
|  |         len[1] = num_samples - len[0]; | ||||||
|  |     } else { | ||||||
|  |         len[0] = num_samples; | ||||||
|  |         len[1] = 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     hw->conv (hw->conv_buf + hw->wpos, samples, len[0]); | ||||||
|  |  | ||||||
|  |     if (len[1]) { | ||||||
|  |         hw->conv (hw->conv_buf, samples + len[0], len[1]); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     hw->wpos = (hw->wpos + num_samples) % hw->samples; | ||||||
|  |  | ||||||
|  |     return num_samples; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void line_in_enable(HWVoiceIn *hw, bool enable) | static int line_in_read (SWVoiceIn *sw, void *buf, int size) | ||||||
|  | { | ||||||
|  |     return audio_pcm_sw_read (sw, buf, size); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int line_in_ctl (HWVoiceIn *hw, int cmd, ...) | ||||||
| { | { | ||||||
|     SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw); |     SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw); | ||||||
|  |  | ||||||
|     if (enable) { |     switch (cmd) { | ||||||
|  |     case VOICE_ENABLE: | ||||||
|         if (in->active) { |         if (in->active) { | ||||||
|             return; |             break; | ||||||
|         } |         } | ||||||
|         in->active = 1; |         in->active = 1; | ||||||
|         audio_rate_start(&in->rate); |         rate_start (&in->rate); | ||||||
|         spice_server_record_start (&in->sin); |         spice_server_record_start (&in->sin); | ||||||
|     } else { |         break; | ||||||
|  |     case VOICE_DISABLE: | ||||||
|         if (!in->active) { |         if (!in->active) { | ||||||
|             return; |             break; | ||||||
|         } |         } | ||||||
|         in->active = 0; |         in->active = 0; | ||||||
|         spice_server_record_stop (&in->sin); |         spice_server_record_stop (&in->sin); | ||||||
|     } |         break; | ||||||
| } |     case VOICE_VOLUME: | ||||||
|  |         { | ||||||
| #if ((SPICE_INTERFACE_RECORD_MAJOR >= 2) && (SPICE_INTERFACE_RECORD_MINOR >= 2)) | #if ((SPICE_INTERFACE_RECORD_MAJOR >= 2) && (SPICE_INTERFACE_RECORD_MINOR >= 2)) | ||||||
| static void line_in_volume(HWVoiceIn *hw, Volume *vol) |             SWVoiceIn *sw; | ||||||
| { |             va_list ap; | ||||||
|     SpiceVoiceIn *in = container_of(hw, SpiceVoiceIn, hw); |             uint16_t vol[2]; | ||||||
|     uint16_t svol[2]; |  | ||||||
|  |  | ||||||
|     assert(vol->channels == 2); |             va_start (ap, cmd); | ||||||
|     svol[0] = vol->vol[0] * 257; |             sw = va_arg (ap, SWVoiceIn *); | ||||||
|     svol[1] = vol->vol[1] * 257; |             va_end (ap); | ||||||
|     spice_server_record_set_volume(&in->sin, 2, svol); |  | ||||||
|     spice_server_record_set_mute(&in->sin, vol->mute); |             vol[0] = sw->vol.l / ((1ULL << 16) + 1); | ||||||
| } |             vol[1] = sw->vol.r / ((1ULL << 16) + 1); | ||||||
|  |             spice_server_record_set_volume (&in->sin, 2, vol); | ||||||
|  |             spice_server_record_set_mute (&in->sin, sw->vol.mute); | ||||||
| #endif | #endif | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static struct audio_option audio_options[] = { | ||||||
|  |     { /* end of list */ }, | ||||||
|  | }; | ||||||
|  |  | ||||||
| static struct audio_pcm_ops audio_callbacks = { | static struct audio_pcm_ops audio_callbacks = { | ||||||
|     .init_out = line_out_init, |     .init_out = line_out_init, | ||||||
|     .fini_out = line_out_fini, |     .fini_out = line_out_fini, | ||||||
|     .write    = audio_generic_write, |     .run_out  = line_out_run, | ||||||
|     .get_buffer_out = line_out_get_buffer, |     .write    = line_out_write, | ||||||
|     .put_buffer_out = line_out_put_buffer, |     .ctl_out  = line_out_ctl, | ||||||
|     .enable_out = line_out_enable, |  | ||||||
| #if (SPICE_INTERFACE_PLAYBACK_MAJOR >= 1) && \ |  | ||||||
|         (SPICE_INTERFACE_PLAYBACK_MINOR >= 2) |  | ||||||
|     .volume_out = line_out_volume, |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
|     .init_in  = line_in_init, |     .init_in  = line_in_init, | ||||||
|     .fini_in  = line_in_fini, |     .fini_in  = line_in_fini, | ||||||
|  |     .run_in   = line_in_run, | ||||||
|     .read     = line_in_read, |     .read     = line_in_read, | ||||||
|     .enable_in = line_in_enable, |     .ctl_in   = line_in_ctl, | ||||||
| #if ((SPICE_INTERFACE_RECORD_MAJOR >= 2) && (SPICE_INTERFACE_RECORD_MINOR >= 2)) |  | ||||||
|     .volume_in = line_in_volume, |  | ||||||
| #endif |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| static struct audio_driver spice_audio_driver = { | static struct audio_driver spice_audio_driver = { | ||||||
|     .name           = "spice", |     .name           = "spice", | ||||||
|     .descr          = "spice audio driver", |     .descr          = "spice audio driver", | ||||||
|  |     .options        = audio_options, | ||||||
|     .init           = spice_audio_init, |     .init           = spice_audio_init, | ||||||
|     .fini           = spice_audio_fini, |     .fini           = spice_audio_fini, | ||||||
|     .pcm_ops        = &audio_callbacks, |     .pcm_ops        = &audio_callbacks, | ||||||
| @@ -308,6 +402,9 @@ static struct audio_driver spice_audio_driver = { | |||||||
|     .max_voices_in  = 1, |     .max_voices_in  = 1, | ||||||
|     .voice_size_out = sizeof (SpiceVoiceOut), |     .voice_size_out = sizeof (SpiceVoiceOut), | ||||||
|     .voice_size_in  = sizeof (SpiceVoiceIn), |     .voice_size_in  = sizeof (SpiceVoiceIn), | ||||||
|  | #if ((SPICE_INTERFACE_PLAYBACK_MAJOR >= 1) && (SPICE_INTERFACE_PLAYBACK_MINOR >= 2)) | ||||||
|  |     .ctl_caps       = VOICE_VOLUME_CAP | ||||||
|  | #endif | ||||||
| }; | }; | ||||||
|  |  | ||||||
| void qemu_spice_audio_init (void) | void qemu_spice_audio_init (void) | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| # See docs/devel/tracing.txt for syntax documentation. | # See docs/devel/tracing.txt for syntax documentation. | ||||||
|  |  | ||||||
| # alsaaudio.c | # audio/alsaaudio.c | ||||||
| alsa_revents(int revents) "revents = %d" | alsa_revents(int revents) "revents = %d" | ||||||
| alsa_pollout(int i, int fd) "i = %d fd = %d" | alsa_pollout(int i, int fd) "i = %d fd = %d" | ||||||
| alsa_set_handler(int events, int index, int fd, int err) "events=0x%x index=%d fd=%d err=%d" | alsa_set_handler(int events, int index, int fd, int err) "events=0x%x index=%d fd=%d err=%d" | ||||||
| @@ -12,11 +12,6 @@ alsa_resume_out(void) "Resuming suspended output stream" | |||||||
| alsa_resume_in(void) "Resuming suspended input stream" | alsa_resume_in(void) "Resuming suspended input stream" | ||||||
| alsa_no_frames(int state) "No frames available and ALSA state is %d" | alsa_no_frames(int state) "No frames available and ALSA state is %d" | ||||||
|  |  | ||||||
| # ossaudio.c | # audio/ossaudio.c | ||||||
| oss_version(int version) "OSS version = 0x%x" | oss_version(int version) "OSS version = 0x%x" | ||||||
| oss_invalid_available_size(int size, int bufsize) "Invalid available size, size=%d bufsize=%d" | oss_invalid_available_size(int size, int bufsize) "Invalid available size, size=%d bufsize=%d" | ||||||
|  |  | ||||||
| # audio.c |  | ||||||
| audio_timer_start(int interval) "interval %d ms" |  | ||||||
| audio_timer_stop(void) "" |  | ||||||
| audio_timer_delayed(int interval) "interval %d ms" |  | ||||||
|   | |||||||
							
								
								
									
										159
									
								
								audio/wavaudio.c
									
									
									
									
									
								
							
							
						
						
									
										159
									
								
								audio/wavaudio.c
									
									
									
									
									
								
							| @@ -21,12 +21,9 @@ | |||||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||||
|  * THE SOFTWARE. |  * THE SOFTWARE. | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #include "qemu/osdep.h" | #include "qemu/osdep.h" | ||||||
| #include "qemu/host-utils.h" | #include "qemu/host-utils.h" | ||||||
| #include "qemu/module.h" |  | ||||||
| #include "qemu/timer.h" | #include "qemu/timer.h" | ||||||
| #include "qapi/opts-visitor.h" |  | ||||||
| #include "audio.h" | #include "audio.h" | ||||||
|  |  | ||||||
| #define AUDIO_CAP "wav" | #define AUDIO_CAP "wav" | ||||||
| @@ -35,23 +32,63 @@ | |||||||
| typedef struct WAVVoiceOut { | typedef struct WAVVoiceOut { | ||||||
|     HWVoiceOut hw; |     HWVoiceOut hw; | ||||||
|     FILE *f; |     FILE *f; | ||||||
|     RateCtl rate; |     int64_t old_ticks; | ||||||
|  |     void *pcm_buf; | ||||||
|     int total_samples; |     int total_samples; | ||||||
| } WAVVoiceOut; | } WAVVoiceOut; | ||||||
|  |  | ||||||
| static size_t wav_write_out(HWVoiceOut *hw, void *buf, size_t len) | typedef struct { | ||||||
|  |     struct audsettings settings; | ||||||
|  |     const char *wav_path; | ||||||
|  | } WAVConf; | ||||||
|  |  | ||||||
|  | static int wav_run_out (HWVoiceOut *hw, int live) | ||||||
| { | { | ||||||
|     WAVVoiceOut *wav = (WAVVoiceOut *) hw; |     WAVVoiceOut *wav = (WAVVoiceOut *) hw; | ||||||
|     int64_t bytes = audio_rate_get_bytes(&hw->info, &wav->rate, len); |     int rpos, decr, samples; | ||||||
|     assert(bytes % hw->info.bytes_per_frame == 0); |     uint8_t *dst; | ||||||
|  |     struct st_sample *src; | ||||||
|  |     int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); | ||||||
|  |     int64_t ticks = now - wav->old_ticks; | ||||||
|  |     int64_t bytes = | ||||||
|  |         muldiv64(ticks, hw->info.bytes_per_second, NANOSECONDS_PER_SECOND); | ||||||
|  |  | ||||||
|     if (bytes && fwrite(buf, bytes, 1, wav->f) != 1) { |     if (bytes > INT_MAX) { | ||||||
|         dolog("wav_write_out: fwrite of %" PRId64 " bytes failed\nReason: %s\n", |         samples = INT_MAX >> hw->info.shift; | ||||||
|               bytes, strerror(errno)); |     } | ||||||
|  |     else { | ||||||
|  |         samples = bytes >> hw->info.shift; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     wav->total_samples += bytes / hw->info.bytes_per_frame; |     wav->old_ticks = now; | ||||||
|     return bytes; |     decr = audio_MIN (live, samples); | ||||||
|  |     samples = decr; | ||||||
|  |     rpos = hw->rpos; | ||||||
|  |     while (samples) { | ||||||
|  |         int left_till_end_samples = hw->samples - rpos; | ||||||
|  |         int convert_samples = audio_MIN (samples, left_till_end_samples); | ||||||
|  |  | ||||||
|  |         src = hw->mix_buf + rpos; | ||||||
|  |         dst = advance (wav->pcm_buf, rpos << hw->info.shift); | ||||||
|  |  | ||||||
|  |         hw->clip (dst, src, convert_samples); | ||||||
|  |         if (fwrite (dst, convert_samples << hw->info.shift, 1, wav->f) != 1) { | ||||||
|  |             dolog ("wav_run_out: fwrite of %d bytes failed\nReaons: %s\n", | ||||||
|  |                    convert_samples << hw->info.shift, strerror (errno)); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         rpos = (rpos + convert_samples) % hw->samples; | ||||||
|  |         samples -= convert_samples; | ||||||
|  |         wav->total_samples += convert_samples; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     hw->rpos = rpos; | ||||||
|  |     return decr; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int wav_write_out (SWVoiceOut *sw, void *buf, int len) | ||||||
|  | { | ||||||
|  |     return audio_pcm_sw_write (sw, buf, len); | ||||||
| } | } | ||||||
|  |  | ||||||
| /* VICE code: Store number as little endian. */ | /* VICE code: Store number as little endian. */ | ||||||
| @@ -75,30 +112,25 @@ static int wav_init_out(HWVoiceOut *hw, struct audsettings *as, | |||||||
|         0x02, 0x00, 0x44, 0xac, 0x00, 0x00, 0x10, 0xb1, 0x02, 0x00, 0x04, |         0x02, 0x00, 0x44, 0xac, 0x00, 0x00, 0x10, 0xb1, 0x02, 0x00, 0x04, | ||||||
|         0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00 |         0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00 | ||||||
|     }; |     }; | ||||||
|     Audiodev *dev = drv_opaque; |     WAVConf *conf = drv_opaque; | ||||||
|     AudiodevWavOptions *wopts = &dev->u.wav; |     struct audsettings wav_as = conf->settings; | ||||||
|     struct audsettings wav_as = audiodev_to_audsettings(dev->u.wav.out); |  | ||||||
|     const char *wav_path = wopts->has_path ? wopts->path : "qemu.wav"; |  | ||||||
|  |  | ||||||
|     stereo = wav_as.nchannels == 2; |     stereo = wav_as.nchannels == 2; | ||||||
|     switch (wav_as.fmt) { |     switch (wav_as.fmt) { | ||||||
|     case AUDIO_FORMAT_S8: |     case AUD_FMT_S8: | ||||||
|     case AUDIO_FORMAT_U8: |     case AUD_FMT_U8: | ||||||
|         bits16 = 0; |         bits16 = 0; | ||||||
|         break; |         break; | ||||||
|  |  | ||||||
|     case AUDIO_FORMAT_S16: |     case AUD_FMT_S16: | ||||||
|     case AUDIO_FORMAT_U16: |     case AUD_FMT_U16: | ||||||
|         bits16 = 1; |         bits16 = 1; | ||||||
|         break; |         break; | ||||||
|  |  | ||||||
|     case AUDIO_FORMAT_S32: |     case AUD_FMT_S32: | ||||||
|     case AUDIO_FORMAT_U32: |     case AUD_FMT_U32: | ||||||
|         dolog ("WAVE files can not handle 32bit formats\n"); |         dolog ("WAVE files can not handle 32bit formats\n"); | ||||||
|         return -1; |         return -1; | ||||||
|  |  | ||||||
|     default: |  | ||||||
|         abort(); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     hdr[34] = bits16 ? 0x10 : 0x08; |     hdr[34] = bits16 ? 0x10 : 0x08; | ||||||
| @@ -107,15 +139,24 @@ static int wav_init_out(HWVoiceOut *hw, struct audsettings *as, | |||||||
|     audio_pcm_init_info (&hw->info, &wav_as); |     audio_pcm_init_info (&hw->info, &wav_as); | ||||||
|  |  | ||||||
|     hw->samples = 1024; |     hw->samples = 1024; | ||||||
|  |     wav->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift); | ||||||
|  |     if (!wav->pcm_buf) { | ||||||
|  |         dolog ("Could not allocate buffer (%d bytes)\n", | ||||||
|  |                hw->samples << hw->info.shift); | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     le_store (hdr + 22, hw->info.nchannels, 2); |     le_store (hdr + 22, hw->info.nchannels, 2); | ||||||
|     le_store (hdr + 24, hw->info.freq, 4); |     le_store (hdr + 24, hw->info.freq, 4); | ||||||
|     le_store (hdr + 28, hw->info.freq << (bits16 + stereo), 4); |     le_store (hdr + 28, hw->info.freq << (bits16 + stereo), 4); | ||||||
|     le_store (hdr + 32, 1 << (bits16 + stereo), 2); |     le_store (hdr + 32, 1 << (bits16 + stereo), 2); | ||||||
|  |  | ||||||
|     wav->f = fopen(wav_path, "wb"); |     wav->f = fopen (conf->wav_path, "wb"); | ||||||
|     if (!wav->f) { |     if (!wav->f) { | ||||||
|         dolog ("Failed to open wave file `%s'\nReason: %s\n", |         dolog ("Failed to open wave file `%s'\nReason: %s\n", | ||||||
|                wav_path, strerror(errno)); |                conf->wav_path, strerror (errno)); | ||||||
|  |         g_free (wav->pcm_buf); | ||||||
|  |         wav->pcm_buf = NULL; | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -124,8 +165,6 @@ static int wav_init_out(HWVoiceOut *hw, struct audsettings *as, | |||||||
|                strerror(errno)); |                strerror(errno)); | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     audio_rate_start(&wav->rate); |  | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -134,7 +173,7 @@ static void wav_fini_out (HWVoiceOut *hw) | |||||||
|     WAVVoiceOut *wav = (WAVVoiceOut *) hw; |     WAVVoiceOut *wav = (WAVVoiceOut *) hw; | ||||||
|     uint8_t rlen[4]; |     uint8_t rlen[4]; | ||||||
|     uint8_t dlen[4]; |     uint8_t dlen[4]; | ||||||
|     uint32_t datalen = wav->total_samples * hw->info.bytes_per_frame; |     uint32_t datalen = wav->total_samples << hw->info.shift; | ||||||
|     uint32_t rifflen = datalen + 36; |     uint32_t rifflen = datalen + 36; | ||||||
|  |  | ||||||
|     if (!wav->f) { |     if (!wav->f) { | ||||||
| @@ -171,38 +210,78 @@ static void wav_fini_out (HWVoiceOut *hw) | |||||||
|                wav->f, strerror (errno)); |                wav->f, strerror (errno)); | ||||||
|     } |     } | ||||||
|     wav->f = NULL; |     wav->f = NULL; | ||||||
|  |  | ||||||
|  |     g_free (wav->pcm_buf); | ||||||
|  |     wav->pcm_buf = NULL; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void wav_enable_out(HWVoiceOut *hw, bool enable) | static int wav_ctl_out (HWVoiceOut *hw, int cmd, ...) | ||||||
| { | { | ||||||
|     WAVVoiceOut *wav = (WAVVoiceOut *) hw; |     (void) hw; | ||||||
|  |     (void) cmd; | ||||||
|     if (enable) { |     return 0; | ||||||
|         audio_rate_start(&wav->rate); |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static void *wav_audio_init(Audiodev *dev) | static WAVConf glob_conf = { | ||||||
|  |     .settings.freq      = 44100, | ||||||
|  |     .settings.nchannels = 2, | ||||||
|  |     .settings.fmt       = AUD_FMT_S16, | ||||||
|  |     .wav_path           = "qemu.wav" | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static void *wav_audio_init (void) | ||||||
| { | { | ||||||
|     assert(dev->driver == AUDIODEV_DRIVER_WAV); |     WAVConf *conf = g_malloc(sizeof(WAVConf)); | ||||||
|     return dev; |     *conf = glob_conf; | ||||||
|  |     return conf; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void wav_audio_fini (void *opaque) | static void wav_audio_fini (void *opaque) | ||||||
| { | { | ||||||
|     ldebug ("wav_fini"); |     ldebug ("wav_fini"); | ||||||
|  |     g_free(opaque); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static struct audio_option wav_options[] = { | ||||||
|  |     { | ||||||
|  |         .name  = "FREQUENCY", | ||||||
|  |         .tag   = AUD_OPT_INT, | ||||||
|  |         .valp  = &glob_conf.settings.freq, | ||||||
|  |         .descr = "Frequency" | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |         .name  = "FORMAT", | ||||||
|  |         .tag   = AUD_OPT_FMT, | ||||||
|  |         .valp  = &glob_conf.settings.fmt, | ||||||
|  |         .descr = "Format" | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |         .name  = "DAC_FIXED_CHANNELS", | ||||||
|  |         .tag   = AUD_OPT_INT, | ||||||
|  |         .valp  = &glob_conf.settings.nchannels, | ||||||
|  |         .descr = "Number of channels (1 - mono, 2 - stereo)" | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |         .name  = "PATH", | ||||||
|  |         .tag   = AUD_OPT_STR, | ||||||
|  |         .valp  = &glob_conf.wav_path, | ||||||
|  |         .descr = "Path to wave file" | ||||||
|  |     }, | ||||||
|  |     { /* End of list */ } | ||||||
|  | }; | ||||||
|  |  | ||||||
| static struct audio_pcm_ops wav_pcm_ops = { | static struct audio_pcm_ops wav_pcm_ops = { | ||||||
|     .init_out = wav_init_out, |     .init_out = wav_init_out, | ||||||
|     .fini_out = wav_fini_out, |     .fini_out = wav_fini_out, | ||||||
|  |     .run_out  = wav_run_out, | ||||||
|     .write    = wav_write_out, |     .write    = wav_write_out, | ||||||
|     .enable_out = wav_enable_out, |     .ctl_out  = wav_ctl_out, | ||||||
| }; | }; | ||||||
|  |  | ||||||
| static struct audio_driver wav_audio_driver = { | static struct audio_driver wav_audio_driver = { | ||||||
|     .name           = "wav", |     .name           = "wav", | ||||||
|     .descr          = "WAV renderer http://wikipedia.org/wiki/WAV", |     .descr          = "WAV renderer http://wikipedia.org/wiki/WAV", | ||||||
|  |     .options        = wav_options, | ||||||
|     .init           = wav_audio_init, |     .init           = wav_audio_init, | ||||||
|     .fini           = wav_audio_fini, |     .fini           = wav_audio_fini, | ||||||
|     .pcm_ops        = &wav_pcm_ops, |     .pcm_ops        = &wav_pcm_ops, | ||||||
|   | |||||||
| @@ -1,4 +1,5 @@ | |||||||
| #include "qemu/osdep.h" | #include "qemu/osdep.h" | ||||||
|  | #include "hw/hw.h" | ||||||
| #include "monitor/monitor.h" | #include "monitor/monitor.h" | ||||||
| #include "qapi/error.h" | #include "qapi/error.h" | ||||||
| #include "qemu/error-report.h" | #include "qemu/error-report.h" | ||||||
| @@ -37,29 +38,30 @@ static void wav_destroy (void *opaque) | |||||||
|     uint8_t dlen[4]; |     uint8_t dlen[4]; | ||||||
|     uint32_t datalen = wav->bytes; |     uint32_t datalen = wav->bytes; | ||||||
|     uint32_t rifflen = datalen + 36; |     uint32_t rifflen = datalen + 36; | ||||||
|  |     Monitor *mon = cur_mon; | ||||||
|  |  | ||||||
|     if (wav->f) { |     if (wav->f) { | ||||||
|         le_store (rlen, rifflen, 4); |         le_store (rlen, rifflen, 4); | ||||||
|         le_store (dlen, datalen, 4); |         le_store (dlen, datalen, 4); | ||||||
|  |  | ||||||
|         if (fseek (wav->f, 4, SEEK_SET)) { |         if (fseek (wav->f, 4, SEEK_SET)) { | ||||||
|             error_report("wav_destroy: rlen fseek failed: %s", |             monitor_printf (mon, "wav_destroy: rlen fseek failed\nReason: %s\n", | ||||||
|                          strerror(errno)); |                             strerror (errno)); | ||||||
|             goto doclose; |             goto doclose; | ||||||
|         } |         } | ||||||
|         if (fwrite (rlen, 4, 1, wav->f) != 1) { |         if (fwrite (rlen, 4, 1, wav->f) != 1) { | ||||||
|             error_report("wav_destroy: rlen fwrite failed: %s", |             monitor_printf (mon, "wav_destroy: rlen fwrite failed\nReason %s\n", | ||||||
|                          strerror(errno)); |                             strerror (errno)); | ||||||
|             goto doclose; |             goto doclose; | ||||||
|         } |         } | ||||||
|         if (fseek (wav->f, 32, SEEK_CUR)) { |         if (fseek (wav->f, 32, SEEK_CUR)) { | ||||||
|             error_report("wav_destroy: dlen fseek failed: %s", |             monitor_printf (mon, "wav_destroy: dlen fseek failed\nReason %s\n", | ||||||
|                          strerror(errno)); |                             strerror (errno)); | ||||||
|             goto doclose; |             goto doclose; | ||||||
|         } |         } | ||||||
|         if (fwrite (dlen, 1, 4, wav->f) != 4) { |         if (fwrite (dlen, 1, 4, wav->f) != 4) { | ||||||
|             error_report("wav_destroy: dlen fwrite failed: %s", |             monitor_printf (mon, "wav_destroy: dlen fwrite failed\nReason %s\n", | ||||||
|                          strerror(errno)); |                             strerror (errno)); | ||||||
|             goto doclose; |             goto doclose; | ||||||
|         } |         } | ||||||
|     doclose: |     doclose: | ||||||
| @@ -76,7 +78,8 @@ static void wav_capture (void *opaque, void *buf, int size) | |||||||
|     WAVState *wav = opaque; |     WAVState *wav = opaque; | ||||||
|  |  | ||||||
|     if (fwrite (buf, size, 1, wav->f) != 1) { |     if (fwrite (buf, size, 1, wav->f) != 1) { | ||||||
|         error_report("wav_capture: fwrite error: %s", strerror(errno)); |         monitor_printf (cur_mon, "wav_capture: fwrite error\nReason: %s", | ||||||
|  |                         strerror (errno)); | ||||||
|     } |     } | ||||||
|     wav->bytes += size; |     wav->bytes += size; | ||||||
| } | } | ||||||
| @@ -104,9 +107,10 @@ static struct capture_ops wav_capture_ops = { | |||||||
|     .info = wav_capture_info |     .info = wav_capture_info | ||||||
| }; | }; | ||||||
|  |  | ||||||
| int wav_start_capture(AudioState *state, CaptureState *s, const char *path, | int wav_start_capture (CaptureState *s, const char *path, int freq, | ||||||
|                       int freq, int bits, int nchannels) |                        int bits, int nchannels) | ||||||
| { | { | ||||||
|  |     Monitor *mon = cur_mon; | ||||||
|     WAVState *wav; |     WAVState *wav; | ||||||
|     uint8_t hdr[] = { |     uint8_t hdr[] = { | ||||||
|         0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56, |         0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56, | ||||||
| @@ -120,13 +124,13 @@ int wav_start_capture(AudioState *state, CaptureState *s, const char *path, | |||||||
|     CaptureVoiceOut *cap; |     CaptureVoiceOut *cap; | ||||||
|  |  | ||||||
|     if (bits != 8 && bits != 16) { |     if (bits != 8 && bits != 16) { | ||||||
|         error_report("incorrect bit count %d, must be 8 or 16", bits); |         monitor_printf (mon, "incorrect bit count %d, must be 8 or 16\n", bits); | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (nchannels != 1 && nchannels != 2) { |     if (nchannels != 1 && nchannels != 2) { | ||||||
|         error_report("incorrect channel count %d, must be 1 or 2", |         monitor_printf (mon, "incorrect channel count %d, must be 1 or 2\n", | ||||||
|                      nchannels); |                         nchannels); | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -135,7 +139,7 @@ int wav_start_capture(AudioState *state, CaptureState *s, const char *path, | |||||||
|  |  | ||||||
|     as.freq = freq; |     as.freq = freq; | ||||||
|     as.nchannels = 1 << stereo; |     as.nchannels = 1 << stereo; | ||||||
|     as.fmt = bits16 ? AUDIO_FORMAT_S16 : AUDIO_FORMAT_U8; |     as.fmt = bits16 ? AUD_FMT_S16 : AUD_FMT_U8; | ||||||
|     as.endianness = 0; |     as.endianness = 0; | ||||||
|  |  | ||||||
|     ops.notify = wav_notify; |     ops.notify = wav_notify; | ||||||
| @@ -154,8 +158,8 @@ int wav_start_capture(AudioState *state, CaptureState *s, const char *path, | |||||||
|  |  | ||||||
|     wav->f = fopen (path, "wb"); |     wav->f = fopen (path, "wb"); | ||||||
|     if (!wav->f) { |     if (!wav->f) { | ||||||
|         error_report("Failed to open wave file `%s': %s", |         monitor_printf (mon, "Failed to open wave file `%s'\nReason: %s\n", | ||||||
|                      path, strerror(errno)); |                         path, strerror (errno)); | ||||||
|         g_free (wav); |         g_free (wav); | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
| @@ -166,13 +170,14 @@ int wav_start_capture(AudioState *state, CaptureState *s, const char *path, | |||||||
|     wav->freq = freq; |     wav->freq = freq; | ||||||
|  |  | ||||||
|     if (fwrite (hdr, sizeof (hdr), 1, wav->f) != 1) { |     if (fwrite (hdr, sizeof (hdr), 1, wav->f) != 1) { | ||||||
|         error_report("Failed to write header: %s", strerror(errno)); |         monitor_printf (mon, "Failed to write header\nReason: %s\n", | ||||||
|  |                         strerror (errno)); | ||||||
|         goto error_free; |         goto error_free; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     cap = AUD_add_capture(state, &as, &ops, wav); |     cap = AUD_add_capture (&as, &ops, wav); | ||||||
|     if (!cap) { |     if (!cap) { | ||||||
|         error_report("Failed to add audio capture"); |         monitor_printf (mon, "Failed to add audio capture\n"); | ||||||
|         goto error_free; |         goto error_free; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -184,7 +189,8 @@ int wav_start_capture(AudioState *state, CaptureState *s, const char *path, | |||||||
| error_free: | error_free: | ||||||
|     g_free (wav->path); |     g_free (wav->path); | ||||||
|     if (fclose (wav->f)) { |     if (fclose (wav->f)) { | ||||||
|         error_report("Failed to close wave file: %s", strerror(errno)); |         monitor_printf (mon, "Failed to close wave file\nReason: %s\n", | ||||||
|  |                         strerror (errno)); | ||||||
|     } |     } | ||||||
|     g_free (wav); |     g_free (wav); | ||||||
|     return -1; |     return -1; | ||||||
|   | |||||||
| @@ -1,7 +0,0 @@ | |||||||
| authz-obj-y += base.o |  | ||||||
| authz-obj-y += simple.o |  | ||||||
| authz-obj-y += list.o |  | ||||||
| authz-obj-y += listfile.o |  | ||||||
| authz-obj-$(CONFIG_AUTH_PAM) += pamacct.o |  | ||||||
|  |  | ||||||
| pamacct.o-libs = -lpam |  | ||||||
							
								
								
									
										83
									
								
								authz/base.c
									
									
									
									
									
								
							
							
						
						
									
										83
									
								
								authz/base.c
									
									
									
									
									
								
							| @@ -1,83 +0,0 @@ | |||||||
| /* |  | ||||||
|  * QEMU authorization framework base class |  | ||||||
|  * |  | ||||||
|  * Copyright (c) 2018 Red Hat, Inc. |  | ||||||
|  * |  | ||||||
|  * 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 of the License, or (at your option) any later version. |  | ||||||
|  * |  | ||||||
|  * This library is distributed in the hope that it will be useful, |  | ||||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of |  | ||||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU |  | ||||||
|  * Lesser General Public License for more details. |  | ||||||
|  * |  | ||||||
|  * You should have received a copy of the GNU Lesser General Public |  | ||||||
|  * License along with this library; if not, see <http://www.gnu.org/licenses/>. |  | ||||||
|  * |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #include "qemu/osdep.h" |  | ||||||
| #include "authz/base.h" |  | ||||||
| #include "qemu/module.h" |  | ||||||
| #include "trace.h" |  | ||||||
|  |  | ||||||
| bool qauthz_is_allowed(QAuthZ *authz, |  | ||||||
|                        const char *identity, |  | ||||||
|                        Error **errp) |  | ||||||
| { |  | ||||||
|     QAuthZClass *cls = QAUTHZ_GET_CLASS(authz); |  | ||||||
|     bool allowed; |  | ||||||
|  |  | ||||||
|     allowed = cls->is_allowed(authz, identity, errp); |  | ||||||
|     trace_qauthz_is_allowed(authz, identity, allowed); |  | ||||||
|  |  | ||||||
|     return allowed; |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| bool qauthz_is_allowed_by_id(const char *authzid, |  | ||||||
|                              const char *identity, |  | ||||||
|                              Error **errp) |  | ||||||
| { |  | ||||||
|     QAuthZ *authz; |  | ||||||
|     Object *obj; |  | ||||||
|     Object *container; |  | ||||||
|  |  | ||||||
|     container = object_get_objects_root(); |  | ||||||
|     obj = object_resolve_path_component(container, |  | ||||||
|                                         authzid); |  | ||||||
|     if (!obj) { |  | ||||||
|         error_setg(errp, "Cannot find QAuthZ object ID %s", |  | ||||||
|                    authzid); |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (!object_dynamic_cast(obj, TYPE_QAUTHZ)) { |  | ||||||
|         error_setg(errp, "Object '%s' is not a QAuthZ subclass", |  | ||||||
|                    authzid); |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     authz = QAUTHZ(obj); |  | ||||||
|  |  | ||||||
|     return qauthz_is_allowed(authz, identity, errp); |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| static const TypeInfo authz_info = { |  | ||||||
|     .parent = TYPE_OBJECT, |  | ||||||
|     .name = TYPE_QAUTHZ, |  | ||||||
|     .instance_size = sizeof(QAuthZ), |  | ||||||
|     .class_size = sizeof(QAuthZClass), |  | ||||||
|     .abstract = true, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static void qauthz_register_types(void) |  | ||||||
| { |  | ||||||
|     type_register_static(&authz_info); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type_init(qauthz_register_types) |  | ||||||
|  |  | ||||||
							
								
								
									
										272
									
								
								authz/list.c
									
									
									
									
									
								
							
							
						
						
									
										272
									
								
								authz/list.c
									
									
									
									
									
								
							| @@ -1,272 +0,0 @@ | |||||||
| /* |  | ||||||
|  * QEMU access control list authorization driver |  | ||||||
|  * |  | ||||||
|  * Copyright (c) 2018 Red Hat, Inc. |  | ||||||
|  * |  | ||||||
|  * 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 of the License, or (at your option) any later version. |  | ||||||
|  * |  | ||||||
|  * This library is distributed in the hope that it will be useful, |  | ||||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of |  | ||||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU |  | ||||||
|  * Lesser General Public License for more details. |  | ||||||
|  * |  | ||||||
|  * You should have received a copy of the GNU Lesser General Public |  | ||||||
|  * License along with this library; if not, see <http://www.gnu.org/licenses/>. |  | ||||||
|  * |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #include "qemu/osdep.h" |  | ||||||
| #include "authz/list.h" |  | ||||||
| #include "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, |  | ||||||
|                                    Error **errp) |  | ||||||
| { |  | ||||||
|     QAuthZList *lauthz = QAUTHZ_LIST(authz); |  | ||||||
|     QAuthZListRuleList *rules = lauthz->rules; |  | ||||||
|  |  | ||||||
|     while (rules) { |  | ||||||
|         QAuthZListRule *rule = rules->value; |  | ||||||
|         QAuthZListFormat format = rule->has_format ? rule->format : |  | ||||||
|             QAUTHZ_LIST_FORMAT_EXACT; |  | ||||||
|  |  | ||||||
|         trace_qauthz_list_check_rule(authz, rule->match, identity, |  | ||||||
|                                      format, rule->policy); |  | ||||||
|         switch (format) { |  | ||||||
|         case QAUTHZ_LIST_FORMAT_EXACT: |  | ||||||
|             if (g_str_equal(rule->match, identity)) { |  | ||||||
|                 return rule->policy == QAUTHZ_LIST_POLICY_ALLOW; |  | ||||||
|             } |  | ||||||
|             break; |  | ||||||
|         case QAUTHZ_LIST_FORMAT_GLOB: |  | ||||||
|             if (g_pattern_match_simple(rule->match, identity)) { |  | ||||||
|                 return rule->policy == QAUTHZ_LIST_POLICY_ALLOW; |  | ||||||
|             } |  | ||||||
|             break; |  | ||||||
|         default: |  | ||||||
|             g_warn_if_reached(); |  | ||||||
|             return false; |  | ||||||
|         } |  | ||||||
|         rules = rules->next; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     trace_qauthz_list_default_policy(authz, identity, lauthz->policy); |  | ||||||
|     return lauthz->policy == QAUTHZ_LIST_POLICY_ALLOW; |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| static void |  | ||||||
| qauthz_list_prop_set_policy(Object *obj, |  | ||||||
|                             int value, |  | ||||||
|                             Error **errp G_GNUC_UNUSED) |  | ||||||
| { |  | ||||||
|     QAuthZList *lauthz = QAUTHZ_LIST(obj); |  | ||||||
|  |  | ||||||
|     lauthz->policy = value; |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| static int |  | ||||||
| qauthz_list_prop_get_policy(Object *obj, |  | ||||||
|                             Error **errp G_GNUC_UNUSED) |  | ||||||
| { |  | ||||||
|     QAuthZList *lauthz = QAUTHZ_LIST(obj); |  | ||||||
|  |  | ||||||
|     return lauthz->policy; |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| static void |  | ||||||
| qauthz_list_prop_get_rules(Object *obj, Visitor *v, const char *name, |  | ||||||
|                            void *opaque, Error **errp) |  | ||||||
| { |  | ||||||
|     QAuthZList *lauthz = QAUTHZ_LIST(obj); |  | ||||||
|  |  | ||||||
|     visit_type_QAuthZListRuleList(v, name, &lauthz->rules, errp); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void |  | ||||||
| qauthz_list_prop_set_rules(Object *obj, Visitor *v, const char *name, |  | ||||||
|                            void *opaque, Error **errp) |  | ||||||
| { |  | ||||||
|     QAuthZList *lauthz = QAUTHZ_LIST(obj); |  | ||||||
|     QAuthZListRuleList *oldrules; |  | ||||||
|  |  | ||||||
|     oldrules = lauthz->rules; |  | ||||||
|     visit_type_QAuthZListRuleList(v, name, &lauthz->rules, errp); |  | ||||||
|  |  | ||||||
|     qapi_free_QAuthZListRuleList(oldrules); |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| static void |  | ||||||
| qauthz_list_finalize(Object *obj) |  | ||||||
| { |  | ||||||
|     QAuthZList *lauthz = QAUTHZ_LIST(obj); |  | ||||||
|  |  | ||||||
|     qapi_free_QAuthZListRuleList(lauthz->rules); |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| static void |  | ||||||
| qauthz_list_class_init(ObjectClass *oc, void *data) |  | ||||||
| { |  | ||||||
|     QAuthZClass *authz = QAUTHZ_CLASS(oc); |  | ||||||
|  |  | ||||||
|     object_class_property_add_enum(oc, "policy", |  | ||||||
|                                    "QAuthZListPolicy", |  | ||||||
|                                    &QAuthZListPolicy_lookup, |  | ||||||
|                                    qauthz_list_prop_get_policy, |  | ||||||
|                                    qauthz_list_prop_set_policy, |  | ||||||
|                                    NULL); |  | ||||||
|  |  | ||||||
|     object_class_property_add(oc, "rules", "QAuthZListRule", |  | ||||||
|                               qauthz_list_prop_get_rules, |  | ||||||
|                               qauthz_list_prop_set_rules, |  | ||||||
|                               NULL, NULL, NULL); |  | ||||||
|  |  | ||||||
|     authz->is_allowed = qauthz_list_is_allowed; |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| QAuthZList *qauthz_list_new(const char *id, |  | ||||||
|                             QAuthZListPolicy policy, |  | ||||||
|                             Error **errp) |  | ||||||
| { |  | ||||||
|     return QAUTHZ_LIST( |  | ||||||
|         object_new_with_props(TYPE_QAUTHZ_LIST, |  | ||||||
|                               object_get_objects_root(), |  | ||||||
|                               id, errp, |  | ||||||
|                               "policy", QAuthZListPolicy_str(policy), |  | ||||||
|                               NULL)); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| ssize_t qauthz_list_append_rule(QAuthZList *auth, |  | ||||||
|                                 const char *match, |  | ||||||
|                                 QAuthZListPolicy policy, |  | ||||||
|                                 QAuthZListFormat format, |  | ||||||
|                                 Error **errp) |  | ||||||
| { |  | ||||||
|     QAuthZListRule *rule; |  | ||||||
|     QAuthZListRuleList *rules, *tmp; |  | ||||||
|     size_t i = 0; |  | ||||||
|  |  | ||||||
|     rule = g_new0(QAuthZListRule, 1); |  | ||||||
|     rule->policy = policy; |  | ||||||
|     rule->match = g_strdup(match); |  | ||||||
|     rule->format = format; |  | ||||||
|     rule->has_format = true; |  | ||||||
|  |  | ||||||
|     tmp = g_new0(QAuthZListRuleList, 1); |  | ||||||
|     tmp->value = rule; |  | ||||||
|  |  | ||||||
|     rules = auth->rules; |  | ||||||
|     if (rules) { |  | ||||||
|         while (rules->next) { |  | ||||||
|             i++; |  | ||||||
|             rules = rules->next; |  | ||||||
|         } |  | ||||||
|         rules->next = tmp; |  | ||||||
|         return i + 1; |  | ||||||
|     } else { |  | ||||||
|         auth->rules = tmp; |  | ||||||
|         return 0; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| ssize_t qauthz_list_insert_rule(QAuthZList *auth, |  | ||||||
|                                 const char *match, |  | ||||||
|                                 QAuthZListPolicy policy, |  | ||||||
|                                 QAuthZListFormat format, |  | ||||||
|                                 size_t index, |  | ||||||
|                                 Error **errp) |  | ||||||
| { |  | ||||||
|     QAuthZListRule *rule; |  | ||||||
|     QAuthZListRuleList *rules, *tmp; |  | ||||||
|     size_t i = 0; |  | ||||||
|  |  | ||||||
|     rule = g_new0(QAuthZListRule, 1); |  | ||||||
|     rule->policy = policy; |  | ||||||
|     rule->match = g_strdup(match); |  | ||||||
|     rule->format = format; |  | ||||||
|     rule->has_format = true; |  | ||||||
|  |  | ||||||
|     tmp = g_new0(QAuthZListRuleList, 1); |  | ||||||
|     tmp->value = rule; |  | ||||||
|  |  | ||||||
|     rules = auth->rules; |  | ||||||
|     if (rules && index > 0) { |  | ||||||
|         while (rules->next && i < (index - 1)) { |  | ||||||
|             i++; |  | ||||||
|             rules = rules->next; |  | ||||||
|         } |  | ||||||
|         tmp->next = rules->next; |  | ||||||
|         rules->next = tmp; |  | ||||||
|         return i + 1; |  | ||||||
|     } else { |  | ||||||
|         tmp->next = auth->rules; |  | ||||||
|         auth->rules = tmp; |  | ||||||
|         return 0; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| ssize_t qauthz_list_delete_rule(QAuthZList *auth, const char *match) |  | ||||||
| { |  | ||||||
|     QAuthZListRule *rule; |  | ||||||
|     QAuthZListRuleList *rules, *prev; |  | ||||||
|     size_t i = 0; |  | ||||||
|  |  | ||||||
|     prev = NULL; |  | ||||||
|     rules = auth->rules; |  | ||||||
|     while (rules) { |  | ||||||
|         rule = rules->value; |  | ||||||
|         if (g_str_equal(rule->match, match)) { |  | ||||||
|             if (prev) { |  | ||||||
|                 prev->next = rules->next; |  | ||||||
|             } else { |  | ||||||
|                 auth->rules = rules->next; |  | ||||||
|             } |  | ||||||
|             rules->next = NULL; |  | ||||||
|             qapi_free_QAuthZListRuleList(rules); |  | ||||||
|             return i; |  | ||||||
|         } |  | ||||||
|         prev = rules; |  | ||||||
|         rules = rules->next; |  | ||||||
|         i++; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return -1; |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| static const TypeInfo qauthz_list_info = { |  | ||||||
|     .parent = TYPE_QAUTHZ, |  | ||||||
|     .name = TYPE_QAUTHZ_LIST, |  | ||||||
|     .instance_size = sizeof(QAuthZList), |  | ||||||
|     .instance_finalize = qauthz_list_finalize, |  | ||||||
|     .class_size = sizeof(QAuthZListClass), |  | ||||||
|     .class_init = qauthz_list_class_init, |  | ||||||
|     .interfaces = (InterfaceInfo[]) { |  | ||||||
|         { TYPE_USER_CREATABLE }, |  | ||||||
|         { } |  | ||||||
|     } |  | ||||||
| }; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| static void |  | ||||||
| qauthz_list_register_types(void) |  | ||||||
| { |  | ||||||
|     type_register_static(&qauthz_list_info); |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| type_init(qauthz_list_register_types); |  | ||||||
							
								
								
									
										284
									
								
								authz/listfile.c
									
									
									
									
									
								
							
							
						
						
									
										284
									
								
								authz/listfile.c
									
									
									
									
									
								
							| @@ -1,284 +0,0 @@ | |||||||
| /* |  | ||||||
|  * QEMU access control list file authorization driver |  | ||||||
|  * |  | ||||||
|  * Copyright (c) 2018 Red Hat, Inc. |  | ||||||
|  * |  | ||||||
|  * 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 of the License, or (at your option) any later version. |  | ||||||
|  * |  | ||||||
|  * This library is distributed in the hope that it will be useful, |  | ||||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of |  | ||||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU |  | ||||||
|  * Lesser General Public License for more details. |  | ||||||
|  * |  | ||||||
|  * You should have received a copy of the GNU Lesser General Public |  | ||||||
|  * License along with this library; if not, see <http://www.gnu.org/licenses/>. |  | ||||||
|  * |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #include "qemu/osdep.h" |  | ||||||
| #include "authz/listfile.h" |  | ||||||
| #include "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" |  | ||||||
| #include "qapi/qapi-visit-authz.h" |  | ||||||
| #include "qapi/qmp/qjson.h" |  | ||||||
| #include "qapi/qmp/qobject.h" |  | ||||||
| #include "qapi/qmp/qerror.h" |  | ||||||
| #include "qapi/qobject-input-visitor.h" |  | ||||||
|  |  | ||||||
|  |  | ||||||
| static bool |  | ||||||
| qauthz_list_file_is_allowed(QAuthZ *authz, |  | ||||||
|                             const char *identity, |  | ||||||
|                             Error **errp) |  | ||||||
| { |  | ||||||
|     QAuthZListFile *fauthz = QAUTHZ_LIST_FILE(authz); |  | ||||||
|     if (fauthz->list) { |  | ||||||
|         return qauthz_is_allowed(fauthz->list, identity, errp); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return false; |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| static QAuthZ * |  | ||||||
| qauthz_list_file_load(QAuthZListFile *fauthz, Error **errp) |  | ||||||
| { |  | ||||||
|     GError *err = NULL; |  | ||||||
|     gchar *content = NULL; |  | ||||||
|     gsize len; |  | ||||||
|     QObject *obj = NULL; |  | ||||||
|     QDict *pdict; |  | ||||||
|     Visitor *v = NULL; |  | ||||||
|     QAuthZ *ret = NULL; |  | ||||||
|  |  | ||||||
|     trace_qauthz_list_file_load(fauthz, fauthz->filename); |  | ||||||
|     if (!g_file_get_contents(fauthz->filename, &content, &len, &err)) { |  | ||||||
|         error_setg(errp, "Unable to read '%s': %s", |  | ||||||
|                    fauthz->filename, err->message); |  | ||||||
|         goto cleanup; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     obj = qobject_from_json(content, errp); |  | ||||||
|     if (!obj) { |  | ||||||
|         goto cleanup; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     pdict = qobject_to(QDict, obj); |  | ||||||
|     if (!pdict) { |  | ||||||
|         error_setg(errp, QERR_INVALID_PARAMETER_TYPE, "obj", "dict"); |  | ||||||
|         goto cleanup; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     v = qobject_input_visitor_new(obj); |  | ||||||
|  |  | ||||||
|     ret = (QAuthZ *)user_creatable_add_type(TYPE_QAUTHZ_LIST, |  | ||||||
|                                             NULL, pdict, v, errp); |  | ||||||
|  |  | ||||||
|  cleanup: |  | ||||||
|     visit_free(v); |  | ||||||
|     qobject_unref(obj); |  | ||||||
|     if (err) { |  | ||||||
|         g_error_free(err); |  | ||||||
|     } |  | ||||||
|     g_free(content); |  | ||||||
|     return ret; |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| static void |  | ||||||
| qauthz_list_file_event(int64_t wd G_GNUC_UNUSED, |  | ||||||
|                        QFileMonitorEvent ev G_GNUC_UNUSED, |  | ||||||
|                        const char *name G_GNUC_UNUSED, |  | ||||||
|                        void *opaque) |  | ||||||
| { |  | ||||||
|     QAuthZListFile *fauthz = opaque; |  | ||||||
|     Error *err = NULL; |  | ||||||
|  |  | ||||||
|     if (ev != QFILE_MONITOR_EVENT_MODIFIED && |  | ||||||
|         ev != QFILE_MONITOR_EVENT_CREATED) { |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     object_unref(OBJECT(fauthz->list)); |  | ||||||
|     fauthz->list = qauthz_list_file_load(fauthz, &err); |  | ||||||
|     trace_qauthz_list_file_refresh(fauthz, |  | ||||||
|                                    fauthz->filename, fauthz->list ? 1 : 0); |  | ||||||
|     if (!fauthz->list) { |  | ||||||
|         error_report_err(err); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void |  | ||||||
| qauthz_list_file_complete(UserCreatable *uc, Error **errp) |  | ||||||
| { |  | ||||||
|     QAuthZListFile *fauthz = QAUTHZ_LIST_FILE(uc); |  | ||||||
|     gchar *dir = NULL, *file = NULL; |  | ||||||
|  |  | ||||||
|     fauthz->list = qauthz_list_file_load(fauthz, errp); |  | ||||||
|  |  | ||||||
|     if (!fauthz->refresh) { |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     fauthz->file_monitor = qemu_file_monitor_new(errp); |  | ||||||
|     if (!fauthz->file_monitor) { |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     dir = g_path_get_dirname(fauthz->filename); |  | ||||||
|     if (g_str_equal(dir, ".")) { |  | ||||||
|         error_setg(errp, "Filename must be an absolute path"); |  | ||||||
|         goto cleanup; |  | ||||||
|     } |  | ||||||
|     file = g_path_get_basename(fauthz->filename); |  | ||||||
|     if (g_str_equal(file, ".")) { |  | ||||||
|         error_setg(errp, "Path has no trailing filename component"); |  | ||||||
|         goto cleanup; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     fauthz->file_watch = qemu_file_monitor_add_watch( |  | ||||||
|         fauthz->file_monitor, dir, file, |  | ||||||
|         qauthz_list_file_event, fauthz, errp); |  | ||||||
|     if (fauthz->file_watch < 0) { |  | ||||||
|         goto cleanup; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|  cleanup: |  | ||||||
|     g_free(file); |  | ||||||
|     g_free(dir); |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| static void |  | ||||||
| qauthz_list_file_prop_set_filename(Object *obj, |  | ||||||
|                                    const char *value, |  | ||||||
|                                    Error **errp G_GNUC_UNUSED) |  | ||||||
| { |  | ||||||
|     QAuthZListFile *fauthz = QAUTHZ_LIST_FILE(obj); |  | ||||||
|  |  | ||||||
|     g_free(fauthz->filename); |  | ||||||
|     fauthz->filename = g_strdup(value); |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| static char * |  | ||||||
| qauthz_list_file_prop_get_filename(Object *obj, |  | ||||||
|                                    Error **errp G_GNUC_UNUSED) |  | ||||||
| { |  | ||||||
|     QAuthZListFile *fauthz = QAUTHZ_LIST_FILE(obj); |  | ||||||
|  |  | ||||||
|     return g_strdup(fauthz->filename); |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| static void |  | ||||||
| qauthz_list_file_prop_set_refresh(Object *obj, |  | ||||||
|                                   bool value, |  | ||||||
|                                   Error **errp G_GNUC_UNUSED) |  | ||||||
| { |  | ||||||
|     QAuthZListFile *fauthz = QAUTHZ_LIST_FILE(obj); |  | ||||||
|  |  | ||||||
|     fauthz->refresh = value; |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| static bool |  | ||||||
| qauthz_list_file_prop_get_refresh(Object *obj, |  | ||||||
|                                   Error **errp G_GNUC_UNUSED) |  | ||||||
| { |  | ||||||
|     QAuthZListFile *fauthz = QAUTHZ_LIST_FILE(obj); |  | ||||||
|  |  | ||||||
|     return fauthz->refresh; |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| static void |  | ||||||
| qauthz_list_file_finalize(Object *obj) |  | ||||||
| { |  | ||||||
|     QAuthZListFile *fauthz = QAUTHZ_LIST_FILE(obj); |  | ||||||
|  |  | ||||||
|     object_unref(OBJECT(fauthz->list)); |  | ||||||
|     g_free(fauthz->filename); |  | ||||||
|     qemu_file_monitor_free(fauthz->file_monitor); |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| static void |  | ||||||
| qauthz_list_file_class_init(ObjectClass *oc, void *data) |  | ||||||
| { |  | ||||||
|     UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); |  | ||||||
|     QAuthZClass *authz = QAUTHZ_CLASS(oc); |  | ||||||
|  |  | ||||||
|     ucc->complete = qauthz_list_file_complete; |  | ||||||
|  |  | ||||||
|     object_class_property_add_str(oc, "filename", |  | ||||||
|                                   qauthz_list_file_prop_get_filename, |  | ||||||
|                                   qauthz_list_file_prop_set_filename, |  | ||||||
|                                   NULL); |  | ||||||
|     object_class_property_add_bool(oc, "refresh", |  | ||||||
|                                    qauthz_list_file_prop_get_refresh, |  | ||||||
|                                    qauthz_list_file_prop_set_refresh, |  | ||||||
|                                    NULL); |  | ||||||
|  |  | ||||||
|     authz->is_allowed = qauthz_list_file_is_allowed; |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| static void |  | ||||||
| qauthz_list_file_init(Object *obj) |  | ||||||
| { |  | ||||||
|     QAuthZListFile *authz = QAUTHZ_LIST_FILE(obj); |  | ||||||
|  |  | ||||||
|     authz->file_watch = -1; |  | ||||||
| #ifdef CONFIG_INOTIFY1 |  | ||||||
|     authz->refresh = TRUE; |  | ||||||
| #endif |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| QAuthZListFile *qauthz_list_file_new(const char *id, |  | ||||||
|                                      const char *filename, |  | ||||||
|                                      bool refresh, |  | ||||||
|                                      Error **errp) |  | ||||||
| { |  | ||||||
|     return QAUTHZ_LIST_FILE( |  | ||||||
|         object_new_with_props(TYPE_QAUTHZ_LIST_FILE, |  | ||||||
|                               object_get_objects_root(), |  | ||||||
|                               id, errp, |  | ||||||
|                               "filename", filename, |  | ||||||
|                               "refresh", refresh ? "yes" : "no", |  | ||||||
|                               NULL)); |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| static const TypeInfo qauthz_list_file_info = { |  | ||||||
|     .parent = TYPE_QAUTHZ, |  | ||||||
|     .name = TYPE_QAUTHZ_LIST_FILE, |  | ||||||
|     .instance_init = qauthz_list_file_init, |  | ||||||
|     .instance_size = sizeof(QAuthZListFile), |  | ||||||
|     .instance_finalize = qauthz_list_file_finalize, |  | ||||||
|     .class_size = sizeof(QAuthZListFileClass), |  | ||||||
|     .class_init = qauthz_list_file_class_init, |  | ||||||
|     .interfaces = (InterfaceInfo[]) { |  | ||||||
|         { TYPE_USER_CREATABLE }, |  | ||||||
|         { } |  | ||||||
|     } |  | ||||||
| }; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| static void |  | ||||||
| qauthz_list_file_register_types(void) |  | ||||||
| { |  | ||||||
|     type_register_static(&qauthz_list_file_info); |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| type_init(qauthz_list_file_register_types); |  | ||||||
							
								
								
									
										149
									
								
								authz/pamacct.c
									
									
									
									
									
								
							
							
						
						
									
										149
									
								
								authz/pamacct.c
									
									
									
									
									
								
							| @@ -1,149 +0,0 @@ | |||||||
| /* |  | ||||||
|  * QEMU PAM authorization driver |  | ||||||
|  * |  | ||||||
|  * Copyright (c) 2018 Red Hat, Inc. |  | ||||||
|  * |  | ||||||
|  * 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 of the License, or (at your option) any later version. |  | ||||||
|  * |  | ||||||
|  * This library is distributed in the hope that it will be useful, |  | ||||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of |  | ||||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU |  | ||||||
|  * Lesser General Public License for more details. |  | ||||||
|  * |  | ||||||
|  * You should have received a copy of the GNU Lesser General Public |  | ||||||
|  * License along with this library; if not, see <http://www.gnu.org/licenses/>. |  | ||||||
|  * |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #include "qemu/osdep.h" |  | ||||||
| #include "authz/pamacct.h" |  | ||||||
| #include "trace.h" |  | ||||||
| #include "qemu/module.h" |  | ||||||
| #include "qom/object_interfaces.h" |  | ||||||
|  |  | ||||||
| #include <security/pam_appl.h> |  | ||||||
|  |  | ||||||
|  |  | ||||||
| static bool qauthz_pam_is_allowed(QAuthZ *authz, |  | ||||||
|                                   const char *identity, |  | ||||||
|                                   Error **errp) |  | ||||||
| { |  | ||||||
|     QAuthZPAM *pauthz = QAUTHZ_PAM(authz); |  | ||||||
|     const struct pam_conv pam_conversation = { 0 }; |  | ||||||
|     pam_handle_t *pamh = NULL; |  | ||||||
|     int ret; |  | ||||||
|  |  | ||||||
|     trace_qauthz_pam_check(authz, identity, pauthz->service); |  | ||||||
|     ret = pam_start(pauthz->service, |  | ||||||
|                     identity, |  | ||||||
|                     &pam_conversation, |  | ||||||
|                     &pamh); |  | ||||||
|     if (ret != PAM_SUCCESS) { |  | ||||||
|         error_setg(errp, "Unable to start PAM transaction: %s", |  | ||||||
|                    pam_strerror(NULL, ret)); |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     ret = pam_acct_mgmt(pamh, PAM_SILENT); |  | ||||||
|     pam_end(pamh, ret); |  | ||||||
|     if (ret != PAM_SUCCESS) { |  | ||||||
|         error_setg(errp, "Unable to authorize user '%s': %s", |  | ||||||
|                    identity, pam_strerror(pamh, ret)); |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return true; |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| static void |  | ||||||
| qauthz_pam_prop_set_service(Object *obj, |  | ||||||
|                             const char *service, |  | ||||||
|                             Error **errp G_GNUC_UNUSED) |  | ||||||
| { |  | ||||||
|     QAuthZPAM *pauthz = QAUTHZ_PAM(obj); |  | ||||||
|  |  | ||||||
|     g_free(pauthz->service); |  | ||||||
|     pauthz->service = g_strdup(service); |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| static char * |  | ||||||
| qauthz_pam_prop_get_service(Object *obj, |  | ||||||
|                             Error **errp G_GNUC_UNUSED) |  | ||||||
| { |  | ||||||
|     QAuthZPAM *pauthz = QAUTHZ_PAM(obj); |  | ||||||
|  |  | ||||||
|     return g_strdup(pauthz->service); |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| static void |  | ||||||
| qauthz_pam_complete(UserCreatable *uc, Error **errp) |  | ||||||
| { |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| static void |  | ||||||
| qauthz_pam_finalize(Object *obj) |  | ||||||
| { |  | ||||||
|     QAuthZPAM *pauthz = QAUTHZ_PAM(obj); |  | ||||||
|  |  | ||||||
|     g_free(pauthz->service); |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| static void |  | ||||||
| qauthz_pam_class_init(ObjectClass *oc, void *data) |  | ||||||
| { |  | ||||||
|     UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); |  | ||||||
|     QAuthZClass *authz = QAUTHZ_CLASS(oc); |  | ||||||
|  |  | ||||||
|     ucc->complete = qauthz_pam_complete; |  | ||||||
|     authz->is_allowed = qauthz_pam_is_allowed; |  | ||||||
|  |  | ||||||
|     object_class_property_add_str(oc, "service", |  | ||||||
|                                   qauthz_pam_prop_get_service, |  | ||||||
|                                   qauthz_pam_prop_set_service, |  | ||||||
|                                   NULL); |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| QAuthZPAM *qauthz_pam_new(const char *id, |  | ||||||
|                           const char *service, |  | ||||||
|                           Error **errp) |  | ||||||
| { |  | ||||||
|     return QAUTHZ_PAM( |  | ||||||
|         object_new_with_props(TYPE_QAUTHZ_PAM, |  | ||||||
|                               object_get_objects_root(), |  | ||||||
|                               id, errp, |  | ||||||
|                               "service", service, |  | ||||||
|                               NULL)); |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| static const TypeInfo qauthz_pam_info = { |  | ||||||
|     .parent = TYPE_QAUTHZ, |  | ||||||
|     .name = TYPE_QAUTHZ_PAM, |  | ||||||
|     .instance_size = sizeof(QAuthZPAM), |  | ||||||
|     .instance_finalize = qauthz_pam_finalize, |  | ||||||
|     .class_size = sizeof(QAuthZPAMClass), |  | ||||||
|     .class_init = qauthz_pam_class_init, |  | ||||||
|     .interfaces = (InterfaceInfo[]) { |  | ||||||
|         { TYPE_USER_CREATABLE }, |  | ||||||
|         { } |  | ||||||
|     } |  | ||||||
| }; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| static void |  | ||||||
| qauthz_pam_register_types(void) |  | ||||||
| { |  | ||||||
|     type_register_static(&qauthz_pam_info); |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| type_init(qauthz_pam_register_types); |  | ||||||
							
								
								
									
										116
									
								
								authz/simple.c
									
									
									
									
									
								
							
							
						
						
									
										116
									
								
								authz/simple.c
									
									
									
									
									
								
							| @@ -1,116 +0,0 @@ | |||||||
| /* |  | ||||||
|  * QEMU simple authorization driver |  | ||||||
|  * |  | ||||||
|  * Copyright (c) 2018 Red Hat, Inc. |  | ||||||
|  * |  | ||||||
|  * 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 of the License, or (at your option) any later version. |  | ||||||
|  * |  | ||||||
|  * This library is distributed in the hope that it will be useful, |  | ||||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of |  | ||||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU |  | ||||||
|  * Lesser General Public License for more details. |  | ||||||
|  * |  | ||||||
|  * You should have received a copy of the GNU Lesser General Public |  | ||||||
|  * License along with this library; if not, see <http://www.gnu.org/licenses/>. |  | ||||||
|  * |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #include "qemu/osdep.h" |  | ||||||
| #include "authz/simple.h" |  | ||||||
| #include "trace.h" |  | ||||||
| #include "qemu/module.h" |  | ||||||
| #include "qom/object_interfaces.h" |  | ||||||
|  |  | ||||||
| static bool qauthz_simple_is_allowed(QAuthZ *authz, |  | ||||||
|                                      const char *identity, |  | ||||||
|                                      Error **errp) |  | ||||||
| { |  | ||||||
|     QAuthZSimple *sauthz = QAUTHZ_SIMPLE(authz); |  | ||||||
|  |  | ||||||
|     trace_qauthz_simple_is_allowed(authz, sauthz->identity, identity); |  | ||||||
|     return g_str_equal(identity, sauthz->identity); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void |  | ||||||
| qauthz_simple_prop_set_identity(Object *obj, |  | ||||||
|                                 const char *value, |  | ||||||
|                                 Error **errp G_GNUC_UNUSED) |  | ||||||
| { |  | ||||||
|     QAuthZSimple *sauthz = QAUTHZ_SIMPLE(obj); |  | ||||||
|  |  | ||||||
|     g_free(sauthz->identity); |  | ||||||
|     sauthz->identity = g_strdup(value); |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| static char * |  | ||||||
| qauthz_simple_prop_get_identity(Object *obj, |  | ||||||
|                                 Error **errp G_GNUC_UNUSED) |  | ||||||
| { |  | ||||||
|     QAuthZSimple *sauthz = QAUTHZ_SIMPLE(obj); |  | ||||||
|  |  | ||||||
|     return g_strdup(sauthz->identity); |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| static void |  | ||||||
| qauthz_simple_finalize(Object *obj) |  | ||||||
| { |  | ||||||
|     QAuthZSimple *sauthz = QAUTHZ_SIMPLE(obj); |  | ||||||
|  |  | ||||||
|     g_free(sauthz->identity); |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| static void |  | ||||||
| qauthz_simple_class_init(ObjectClass *oc, void *data) |  | ||||||
| { |  | ||||||
|     QAuthZClass *authz = QAUTHZ_CLASS(oc); |  | ||||||
|  |  | ||||||
|     authz->is_allowed = qauthz_simple_is_allowed; |  | ||||||
|  |  | ||||||
|     object_class_property_add_str(oc, "identity", |  | ||||||
|                                   qauthz_simple_prop_get_identity, |  | ||||||
|                                   qauthz_simple_prop_set_identity, |  | ||||||
|                                   NULL); |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| QAuthZSimple *qauthz_simple_new(const char *id, |  | ||||||
|                                 const char *identity, |  | ||||||
|                                 Error **errp) |  | ||||||
| { |  | ||||||
|     return QAUTHZ_SIMPLE( |  | ||||||
|         object_new_with_props(TYPE_QAUTHZ_SIMPLE, |  | ||||||
|                               object_get_objects_root(), |  | ||||||
|                               id, errp, |  | ||||||
|                               "identity", identity, |  | ||||||
|                               NULL)); |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| static const TypeInfo qauthz_simple_info = { |  | ||||||
|     .parent = TYPE_QAUTHZ, |  | ||||||
|     .name = TYPE_QAUTHZ_SIMPLE, |  | ||||||
|     .instance_size = sizeof(QAuthZSimple), |  | ||||||
|     .instance_finalize = qauthz_simple_finalize, |  | ||||||
|     .class_size = sizeof(QAuthZSimpleClass), |  | ||||||
|     .class_init = qauthz_simple_class_init, |  | ||||||
|     .interfaces = (InterfaceInfo[]) { |  | ||||||
|         { TYPE_USER_CREATABLE }, |  | ||||||
|         { } |  | ||||||
|     } |  | ||||||
| }; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| static void |  | ||||||
| qauthz_simple_register_types(void) |  | ||||||
| { |  | ||||||
|     type_register_static(&qauthz_simple_info); |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| type_init(qauthz_simple_register_types); |  | ||||||
| @@ -1,18 +0,0 @@ | |||||||
| # See docs/devel/tracing.txt for syntax documentation. |  | ||||||
|  |  | ||||||
| # base.c |  | ||||||
| qauthz_is_allowed(void *authz, const char *identity, bool allowed) "AuthZ %p check identity=%s allowed=%d" |  | ||||||
|  |  | ||||||
| # 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 |  | ||||||
| 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 |  | ||||||
| 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 |  | ||||||
| qauthz_pam_check(void *authz, const char *identity, const char *service) "AuthZ PAM %p identity=%s service=%s" |  | ||||||
| @@ -1,19 +1,18 @@ | |||||||
| common-obj-y += rng.o rng-egd.o rng-builtin.o | common-obj-y += rng.o rng-egd.o | ||||||
| common-obj-$(CONFIG_POSIX) += rng-random.o | common-obj-$(CONFIG_POSIX) += rng-random.o | ||||||
|  |  | ||||||
| common-obj-$(CONFIG_TPM) += tpm.o | common-obj-$(CONFIG_TPM) += tpm.o | ||||||
|  |  | ||||||
| common-obj-y += hostmem.o hostmem-ram.o | common-obj-y += hostmem.o hostmem-ram.o | ||||||
| common-obj-$(CONFIG_POSIX) += hostmem-file.o | common-obj-$(CONFIG_LINUX) += hostmem-file.o | ||||||
|  |  | ||||||
| common-obj-y += cryptodev.o | common-obj-y += cryptodev.o | ||||||
| common-obj-y += cryptodev-builtin.o | common-obj-y += cryptodev-builtin.o | ||||||
|  |  | ||||||
| ifeq ($(CONFIG_VIRTIO_CRYPTO),y) | ifeq ($(CONFIG_VIRTIO),y) | ||||||
| common-obj-y += cryptodev-vhost.o | common-obj-y += cryptodev-vhost.o | ||||||
| common-obj-$(CONFIG_VHOST_CRYPTO) += cryptodev-vhost-user.o | common-obj-$(call land,$(CONFIG_VHOST_USER),$(CONFIG_LINUX)) += \ | ||||||
|  |     cryptodev-vhost-user.o | ||||||
| endif | endif | ||||||
|  |  | ||||||
| common-obj-$(call land,$(CONFIG_VHOST_USER),$(CONFIG_VIRTIO)) += vhost-user.o |  | ||||||
|  |  | ||||||
| common-obj-$(CONFIG_LINUX) += hostmem-memfd.o | common-obj-$(CONFIG_LINUX) += hostmem-memfd.o | ||||||
|   | |||||||
| @@ -23,6 +23,7 @@ | |||||||
|  |  | ||||||
| #include "qemu/osdep.h" | #include "qemu/osdep.h" | ||||||
| #include "sysemu/cryptodev.h" | #include "sysemu/cryptodev.h" | ||||||
|  | #include "hw/boards.h" | ||||||
| #include "qapi/error.h" | #include "qapi/error.h" | ||||||
| #include "standard-headers/linux/virtio_crypto.h" | #include "standard-headers/linux/virtio_crypto.h" | ||||||
| #include "crypto/cipher.h" | #include "crypto/cipher.h" | ||||||
|   | |||||||
| @@ -22,10 +22,10 @@ | |||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #include "qemu/osdep.h" | #include "qemu/osdep.h" | ||||||
|  | #include "hw/boards.h" | ||||||
| #include "qapi/error.h" | #include "qapi/error.h" | ||||||
| #include "qapi/qmp/qerror.h" | #include "qapi/qmp/qerror.h" | ||||||
| #include "qemu/error-report.h" | #include "qemu/error-report.h" | ||||||
| #include "hw/virtio/vhost-user.h" |  | ||||||
| #include "standard-headers/linux/virtio_crypto.h" | #include "standard-headers/linux/virtio_crypto.h" | ||||||
| #include "sysemu/cryptodev-vhost.h" | #include "sysemu/cryptodev-vhost.h" | ||||||
| #include "chardev/char-fe.h" | #include "chardev/char-fe.h" | ||||||
| @@ -46,7 +46,6 @@ | |||||||
| typedef struct CryptoDevBackendVhostUser { | typedef struct CryptoDevBackendVhostUser { | ||||||
|     CryptoDevBackend parent_obj; |     CryptoDevBackend parent_obj; | ||||||
|  |  | ||||||
|     VhostUserState vhost_user; |  | ||||||
|     CharBackend chr; |     CharBackend chr; | ||||||
|     char *chr_name; |     char *chr_name; | ||||||
|     bool opened; |     bool opened; | ||||||
| @@ -103,7 +102,7 @@ cryptodev_vhost_user_start(int queues, | |||||||
|             continue; |             continue; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         options.opaque = &s->vhost_user; |         options.opaque = &s->chr; | ||||||
|         options.backend_type = VHOST_BACKEND_TYPE_USER; |         options.backend_type = VHOST_BACKEND_TYPE_USER; | ||||||
|         options.cc = b->conf.peers.ccs[i]; |         options.cc = b->conf.peers.ccs[i]; | ||||||
|         s->vhost_crypto[i] = cryptodev_vhost_init(&options); |         s->vhost_crypto[i] = cryptodev_vhost_init(&options); | ||||||
| @@ -156,6 +155,7 @@ static void cryptodev_vhost_user_event(void *opaque, int event) | |||||||
| { | { | ||||||
|     CryptoDevBackendVhostUser *s = opaque; |     CryptoDevBackendVhostUser *s = opaque; | ||||||
|     CryptoDevBackend *b = CRYPTODEV_BACKEND(s); |     CryptoDevBackend *b = CRYPTODEV_BACKEND(s); | ||||||
|  |     Error *err = NULL; | ||||||
|     int queues = b->conf.peers.queues; |     int queues = b->conf.peers.queues; | ||||||
|  |  | ||||||
|     assert(queues < MAX_CRYPTO_QUEUE_NUM); |     assert(queues < MAX_CRYPTO_QUEUE_NUM); | ||||||
| @@ -172,6 +172,10 @@ static void cryptodev_vhost_user_event(void *opaque, int event) | |||||||
|         cryptodev_vhost_user_stop(queues, s); |         cryptodev_vhost_user_stop(queues, s); | ||||||
|         break; |         break; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     if (err) { | ||||||
|  |         error_report_err(err); | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| static void cryptodev_vhost_user_init( | static void cryptodev_vhost_user_init( | ||||||
| @@ -211,10 +215,6 @@ static void cryptodev_vhost_user_init( | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (!vhost_user_init(&s->vhost_user, &s->chr, errp)) { |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     qemu_chr_fe_set_handlers(&s->chr, NULL, NULL, |     qemu_chr_fe_set_handlers(&s->chr, NULL, NULL, | ||||||
|                      cryptodev_vhost_user_event, NULL, s, NULL, true); |                      cryptodev_vhost_user_event, NULL, s, NULL, true); | ||||||
|  |  | ||||||
| @@ -299,8 +299,6 @@ static void cryptodev_vhost_user_cleanup( | |||||||
|             backend->conf.peers.ccs[i] = NULL; |             backend->conf.peers.ccs[i] = NULL; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     vhost_user_cleanup(&s->vhost_user); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static void cryptodev_vhost_user_set_chardev(Object *obj, | static void cryptodev_vhost_user_set_chardev(Object *obj, | ||||||
|   | |||||||
| @@ -23,6 +23,7 @@ | |||||||
|  |  | ||||||
| #include "qemu/osdep.h" | #include "qemu/osdep.h" | ||||||
| #include "sysemu/cryptodev.h" | #include "sysemu/cryptodev.h" | ||||||
|  | #include "hw/boards.h" | ||||||
| #include "qapi/error.h" | #include "qapi/error.h" | ||||||
| #include "qapi/visitor.h" | #include "qapi/visitor.h" | ||||||
| #include "qemu/config-file.h" | #include "qemu/config-file.h" | ||||||
|   | |||||||
| @@ -9,11 +9,9 @@ | |||||||
|  * This work is licensed under the terms of the GNU GPL, version 2 or later. |  * This work is licensed under the terms of the GNU GPL, version 2 or later. | ||||||
|  * See the COPYING file in the top-level directory. |  * See the COPYING file in the top-level directory. | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #include "qemu/osdep.h" | #include "qemu/osdep.h" | ||||||
| #include "qapi/error.h" | #include "qapi/error.h" | ||||||
| #include "qemu/error-report.h" | #include "qemu-common.h" | ||||||
| #include "qemu/module.h" |  | ||||||
| #include "sysemu/hostmem.h" | #include "sysemu/hostmem.h" | ||||||
| #include "sysemu/sysemu.h" | #include "sysemu/sysemu.h" | ||||||
| #include "qom/object_interfaces.h" | #include "qom/object_interfaces.h" | ||||||
| @@ -33,21 +31,15 @@ typedef struct HostMemoryBackendFile HostMemoryBackendFile; | |||||||
| struct HostMemoryBackendFile { | struct HostMemoryBackendFile { | ||||||
|     HostMemoryBackend parent_obj; |     HostMemoryBackend parent_obj; | ||||||
|  |  | ||||||
|  |     bool discard_data; | ||||||
|     char *mem_path; |     char *mem_path; | ||||||
|     uint64_t align; |     uint64_t align; | ||||||
|     bool discard_data; |  | ||||||
|     bool is_pmem; |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| static void | static void | ||||||
| file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) | file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) | ||||||
| { | { | ||||||
| #ifndef CONFIG_POSIX |  | ||||||
|     error_setg(errp, "backend '%s' not supported on this host", |  | ||||||
|                object_get_typename(OBJECT(backend))); |  | ||||||
| #else |  | ||||||
|     HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(backend); |     HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(backend); | ||||||
|     gchar *name; |  | ||||||
|  |  | ||||||
|     if (!backend->size) { |     if (!backend->size) { | ||||||
|         error_setg(errp, "can't create backend with size 0"); |         error_setg(errp, "can't create backend with size 0"); | ||||||
| @@ -57,16 +49,19 @@ file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) | |||||||
|         error_setg(errp, "mem-path property not set"); |         error_setg(errp, "mem-path property not set"); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  | #ifndef CONFIG_LINUX | ||||||
|     backend->force_prealloc = mem_prealloc; |     error_setg(errp, "-mem-path not supported on this host"); | ||||||
|     name = host_memory_backend_get_name(backend); | #else | ||||||
|     memory_region_init_ram_from_file(&backend->mr, OBJECT(backend), |     if (!host_memory_backend_mr_inited(backend)) { | ||||||
|                                      name, |         gchar *path; | ||||||
|                                      backend->size, fb->align, |         backend->force_prealloc = mem_prealloc; | ||||||
|                                      (backend->share ? RAM_SHARED : 0) | |         path = object_get_canonical_path(OBJECT(backend)); | ||||||
|                                      (fb->is_pmem ? RAM_PMEM : 0), |         memory_region_init_ram_from_file(&backend->mr, OBJECT(backend), | ||||||
|                                      fb->mem_path, errp); |                                  path, | ||||||
|     g_free(name); |                                  backend->size, fb->align, backend->share, | ||||||
|  |                                  fb->mem_path, errp); | ||||||
|  |         g_free(path); | ||||||
|  |     } | ||||||
| #endif | #endif | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -83,8 +78,7 @@ static void set_mem_path(Object *o, const char *str, Error **errp) | |||||||
|     HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o); |     HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o); | ||||||
|  |  | ||||||
|     if (host_memory_backend_mr_inited(backend)) { |     if (host_memory_backend_mr_inited(backend)) { | ||||||
|         error_setg(errp, "cannot change property 'mem-path' of %s", |         error_setg(errp, "cannot change property value"); | ||||||
|                    object_get_typename(o)); |  | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|     g_free(fb->mem_path); |     g_free(fb->mem_path); | ||||||
| @@ -122,8 +116,7 @@ static void file_memory_backend_set_align(Object *o, Visitor *v, | |||||||
|     uint64_t val; |     uint64_t val; | ||||||
|  |  | ||||||
|     if (host_memory_backend_mr_inited(backend)) { |     if (host_memory_backend_mr_inited(backend)) { | ||||||
|         error_setg(&local_err, "cannot change property '%s' of %s", |         error_setg(&local_err, "cannot change property value"); | ||||||
|                    name, object_get_typename(o)); |  | ||||||
|         goto out; |         goto out; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -137,39 +130,6 @@ static void file_memory_backend_set_align(Object *o, Visitor *v, | |||||||
|     error_propagate(errp, local_err); |     error_propagate(errp, local_err); | ||||||
| } | } | ||||||
|  |  | ||||||
| static bool file_memory_backend_get_pmem(Object *o, Error **errp) |  | ||||||
| { |  | ||||||
|     return MEMORY_BACKEND_FILE(o)->is_pmem; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void file_memory_backend_set_pmem(Object *o, bool value, Error **errp) |  | ||||||
| { |  | ||||||
|     HostMemoryBackend *backend = MEMORY_BACKEND(o); |  | ||||||
|     HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o); |  | ||||||
|  |  | ||||||
|     if (host_memory_backend_mr_inited(backend)) { |  | ||||||
|  |  | ||||||
|         error_setg(errp, "cannot change property 'pmem' of %s.", |  | ||||||
|                    object_get_typename(o)); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| #ifndef CONFIG_LIBPMEM |  | ||||||
|     if (value) { |  | ||||||
|         Error *local_err = NULL; |  | ||||||
|  |  | ||||||
|         error_setg(&local_err, |  | ||||||
|                    "Lack of libpmem support while setting the 'pmem=on'" |  | ||||||
|                    " of %s. We can't ensure data persistence.", |  | ||||||
|                    object_get_typename(o)); |  | ||||||
|         error_propagate(errp, local_err); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
|     fb->is_pmem = value; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void file_backend_unparent(Object *obj) | static void file_backend_unparent(Object *obj) | ||||||
| { | { | ||||||
|     HostMemoryBackend *backend = MEMORY_BACKEND(obj); |     HostMemoryBackend *backend = MEMORY_BACKEND(obj); | ||||||
| @@ -201,9 +161,6 @@ file_backend_class_init(ObjectClass *oc, void *data) | |||||||
|         file_memory_backend_get_align, |         file_memory_backend_get_align, | ||||||
|         file_memory_backend_set_align, |         file_memory_backend_set_align, | ||||||
|         NULL, NULL, &error_abort); |         NULL, NULL, &error_abort); | ||||||
|     object_class_property_add_bool(oc, "pmem", |  | ||||||
|         file_memory_backend_get_pmem, file_memory_backend_set_pmem, |  | ||||||
|         &error_abort); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static void file_backend_instance_finalize(Object *o) | static void file_backend_instance_finalize(Object *o) | ||||||
|   | |||||||
| @@ -9,13 +9,12 @@ | |||||||
|  * This work is licensed under the terms of the GNU GPL, version 2 or later. |  * This work is licensed under the terms of the GNU GPL, version 2 or later. | ||||||
|  * See the COPYING file in the top-level directory. |  * See the COPYING file in the top-level directory. | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #include "qemu/osdep.h" | #include "qemu/osdep.h" | ||||||
|  | #include "qemu-common.h" | ||||||
| #include "sysemu/hostmem.h" | #include "sysemu/hostmem.h" | ||||||
| #include "sysemu/sysemu.h" | #include "sysemu/sysemu.h" | ||||||
| #include "qom/object_interfaces.h" | #include "qom/object_interfaces.h" | ||||||
| #include "qemu/memfd.h" | #include "qemu/memfd.h" | ||||||
| #include "qemu/module.h" |  | ||||||
| #include "qapi/error.h" | #include "qapi/error.h" | ||||||
|  |  | ||||||
| #define TYPE_MEMORY_BACKEND_MEMFD "memory-backend-memfd" | #define TYPE_MEMORY_BACKEND_MEMFD "memory-backend-memfd" | ||||||
| @@ -45,6 +44,10 @@ memfd_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) | |||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     if (host_memory_backend_mr_inited(backend)) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     backend->force_prealloc = mem_prealloc; |     backend->force_prealloc = mem_prealloc; | ||||||
|     fd = qemu_memfd_create(TYPE_MEMORY_BACKEND_MEMFD, backend->size, |     fd = qemu_memfd_create(TYPE_MEMORY_BACKEND_MEMFD, backend->size, | ||||||
|                            m->hugetlb, m->hugetlbsize, m->seal ? |                            m->hugetlb, m->hugetlbsize, m->seal ? | ||||||
| @@ -54,10 +57,9 @@ memfd_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) | |||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     name = host_memory_backend_get_name(backend); |     name = object_get_canonical_path(OBJECT(backend)); | ||||||
|     memory_region_init_ram_from_fd(&backend->mr, OBJECT(backend), |     memory_region_init_ram_from_fd(&backend->mr, OBJECT(backend), | ||||||
|                                    name, backend->size, |                                    name, backend->size, true, fd, errp); | ||||||
|                                    backend->share, fd, errp); |  | ||||||
|     g_free(name); |     g_free(name); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -129,7 +131,6 @@ memfd_backend_instance_init(Object *obj) | |||||||
|  |  | ||||||
|     /* default to sealed file */ |     /* default to sealed file */ | ||||||
|     m->seal = true; |     m->seal = true; | ||||||
|     MEMORY_BACKEND(m)->share = true; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static void | static void | ||||||
| @@ -139,29 +140,18 @@ memfd_backend_class_init(ObjectClass *oc, void *data) | |||||||
|  |  | ||||||
|     bc->alloc = memfd_backend_memory_alloc; |     bc->alloc = memfd_backend_memory_alloc; | ||||||
|  |  | ||||||
|     if (qemu_memfd_check(MFD_HUGETLB)) { |     object_class_property_add_bool(oc, "hugetlb", | ||||||
|         object_class_property_add_bool(oc, "hugetlb", |                                    memfd_backend_get_hugetlb, | ||||||
|                                        memfd_backend_get_hugetlb, |                                    memfd_backend_set_hugetlb, | ||||||
|                                        memfd_backend_set_hugetlb, |                                    &error_abort); | ||||||
|                                        &error_abort); |     object_class_property_add(oc, "hugetlbsize", "int", | ||||||
|         object_class_property_set_description(oc, "hugetlb", |                               memfd_backend_get_hugetlbsize, | ||||||
|                                               "Use huge pages", |                               memfd_backend_set_hugetlbsize, | ||||||
|                                               &error_abort); |                               NULL, NULL, &error_abort); | ||||||
|         object_class_property_add(oc, "hugetlbsize", "int", |  | ||||||
|                                   memfd_backend_get_hugetlbsize, |  | ||||||
|                                   memfd_backend_set_hugetlbsize, |  | ||||||
|                                   NULL, NULL, &error_abort); |  | ||||||
|         object_class_property_set_description(oc, "hugetlbsize", |  | ||||||
|                                               "Huge pages size (ex: 2M, 1G)", |  | ||||||
|                                               &error_abort); |  | ||||||
|     } |  | ||||||
|     object_class_property_add_bool(oc, "seal", |     object_class_property_add_bool(oc, "seal", | ||||||
|                                    memfd_backend_get_seal, |                                    memfd_backend_get_seal, | ||||||
|                                    memfd_backend_set_seal, |                                    memfd_backend_set_seal, | ||||||
|                                    &error_abort); |                                    &error_abort); | ||||||
|     object_class_property_set_description(oc, "seal", |  | ||||||
|                                           "Seal growing & shrinking", |  | ||||||
|                                           &error_abort); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static const TypeInfo memfd_backend_info = { | static const TypeInfo memfd_backend_info = { | ||||||
| @@ -174,9 +164,7 @@ static const TypeInfo memfd_backend_info = { | |||||||
|  |  | ||||||
| static void register_types(void) | static void register_types(void) | ||||||
| { | { | ||||||
|     if (qemu_memfd_check(MFD_ALLOW_SEALING)) { |     type_register_static(&memfd_backend_info); | ||||||
|         type_register_static(&memfd_backend_info); |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| type_init(register_types); | type_init(register_types); | ||||||
|   | |||||||
| @@ -9,29 +9,28 @@ | |||||||
|  * This work is licensed under the terms of the GNU GPL, version 2 or later. |  * This work is licensed under the terms of the GNU GPL, version 2 or later. | ||||||
|  * See the COPYING file in the top-level directory. |  * See the COPYING file in the top-level directory. | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #include "qemu/osdep.h" | #include "qemu/osdep.h" | ||||||
| #include "sysemu/hostmem.h" | #include "sysemu/hostmem.h" | ||||||
| #include "qapi/error.h" | #include "qapi/error.h" | ||||||
| #include "qemu/module.h" |  | ||||||
| #include "qom/object_interfaces.h" | #include "qom/object_interfaces.h" | ||||||
|  |  | ||||||
| #define TYPE_MEMORY_BACKEND_RAM "memory-backend-ram" | #define TYPE_MEMORY_BACKEND_RAM "memory-backend-ram" | ||||||
|  |  | ||||||
|  |  | ||||||
| static void | static void | ||||||
| ram_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) | ram_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) | ||||||
| { | { | ||||||
|     char *name; |     char *path; | ||||||
|  |  | ||||||
|     if (!backend->size) { |     if (!backend->size) { | ||||||
|         error_setg(errp, "can't create backend with size 0"); |         error_setg(errp, "can't create backend with size 0"); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     name = host_memory_backend_get_name(backend); |     path = object_get_canonical_path_component(OBJECT(backend)); | ||||||
|     memory_region_init_ram_shared_nomigrate(&backend->mr, OBJECT(backend), name, |     memory_region_init_ram_shared_nomigrate(&backend->mr, OBJECT(backend), path, | ||||||
|                            backend->size, backend->share, errp); |                            backend->size, backend->share, errp); | ||||||
|     g_free(name); |     g_free(path); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void | static void | ||||||
|   | |||||||
| @@ -12,14 +12,12 @@ | |||||||
|  |  | ||||||
| #include "qemu/osdep.h" | #include "qemu/osdep.h" | ||||||
| #include "sysemu/hostmem.h" | #include "sysemu/hostmem.h" | ||||||
| #include "sysemu/sysemu.h" |  | ||||||
| #include "hw/boards.h" | #include "hw/boards.h" | ||||||
| #include "qapi/error.h" | #include "qapi/error.h" | ||||||
| #include "qapi/qapi-builtin-visit.h" | #include "qapi/qapi-builtin-visit.h" | ||||||
| #include "qapi/visitor.h" | #include "qapi/visitor.h" | ||||||
| #include "qemu/config-file.h" | #include "qemu/config-file.h" | ||||||
| #include "qom/object_interfaces.h" | #include "qom/object_interfaces.h" | ||||||
| #include "qemu/mmap-alloc.h" |  | ||||||
|  |  | ||||||
| #ifdef CONFIG_NUMA | #ifdef CONFIG_NUMA | ||||||
| #include <numaif.h> | #include <numaif.h> | ||||||
| @@ -29,16 +27,6 @@ QEMU_BUILD_BUG_ON(HOST_MEM_POLICY_BIND != MPOL_BIND); | |||||||
| QEMU_BUILD_BUG_ON(HOST_MEM_POLICY_INTERLEAVE != MPOL_INTERLEAVE); | QEMU_BUILD_BUG_ON(HOST_MEM_POLICY_INTERLEAVE != MPOL_INTERLEAVE); | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| char * |  | ||||||
| host_memory_backend_get_name(HostMemoryBackend *backend) |  | ||||||
| { |  | ||||||
|     if (!backend->use_canonical_path) { |  | ||||||
|         return object_get_canonical_path_component(OBJECT(backend)); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return object_get_canonical_path(OBJECT(backend)); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void | static void | ||||||
| host_memory_backend_get_size(Object *obj, Visitor *v, const char *name, | host_memory_backend_get_size(Object *obj, Visitor *v, const char *name, | ||||||
|                              void *opaque, Error **errp) |                              void *opaque, Error **errp) | ||||||
| @@ -58,8 +46,7 @@ host_memory_backend_set_size(Object *obj, Visitor *v, const char *name, | |||||||
|     uint64_t value; |     uint64_t value; | ||||||
|  |  | ||||||
|     if (host_memory_backend_mr_inited(backend)) { |     if (host_memory_backend_mr_inited(backend)) { | ||||||
|         error_setg(&local_err, "cannot change property %s of %s ", |         error_setg(&local_err, "cannot change property value"); | ||||||
|                    name, object_get_typename(obj)); |  | ||||||
|         goto out; |         goto out; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -68,9 +55,8 @@ host_memory_backend_set_size(Object *obj, Visitor *v, const char *name, | |||||||
|         goto out; |         goto out; | ||||||
|     } |     } | ||||||
|     if (!value) { |     if (!value) { | ||||||
|         error_setg(&local_err, |         error_setg(&local_err, "Property '%s.%s' doesn't take value '%" | ||||||
|                    "property '%s' of %s doesn't take value '%" PRIu64 "'", |                    PRIu64 "'", object_get_typename(obj), name, value); | ||||||
|                    name, object_get_typename(obj), value); |  | ||||||
|         goto out; |         goto out; | ||||||
|     } |     } | ||||||
|     backend->size = value; |     backend->size = value; | ||||||
| @@ -89,7 +75,7 @@ host_memory_backend_get_host_nodes(Object *obj, Visitor *v, const char *name, | |||||||
|  |  | ||||||
|     value = find_first_bit(backend->host_nodes, MAX_NODES); |     value = find_first_bit(backend->host_nodes, MAX_NODES); | ||||||
|     if (value == MAX_NODES) { |     if (value == MAX_NODES) { | ||||||
|         goto ret; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     *node = g_malloc0(sizeof(**node)); |     *node = g_malloc0(sizeof(**node)); | ||||||
| @@ -107,7 +93,6 @@ host_memory_backend_get_host_nodes(Object *obj, Visitor *v, const char *name, | |||||||
|         node = &(*node)->next; |         node = &(*node)->next; | ||||||
|     } while (true); |     } while (true); | ||||||
|  |  | ||||||
| ret: |  | ||||||
|     visit_type_uint16List(v, name, &host_nodes, errp); |     visit_type_uint16List(v, name, &host_nodes, errp); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -117,23 +102,14 @@ host_memory_backend_set_host_nodes(Object *obj, Visitor *v, const char *name, | |||||||
| { | { | ||||||
| #ifdef CONFIG_NUMA | #ifdef CONFIG_NUMA | ||||||
|     HostMemoryBackend *backend = MEMORY_BACKEND(obj); |     HostMemoryBackend *backend = MEMORY_BACKEND(obj); | ||||||
|     uint16List *l, *host_nodes = NULL; |     uint16List *l = NULL; | ||||||
|  |  | ||||||
|     visit_type_uint16List(v, name, &host_nodes, errp); |     visit_type_uint16List(v, name, &l, errp); | ||||||
|  |  | ||||||
|     for (l = host_nodes; l; l = l->next) { |     while (l) { | ||||||
|         if (l->value >= MAX_NODES) { |  | ||||||
|             error_setg(errp, "Invalid host-nodes value: %d", l->value); |  | ||||||
|             goto out; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     for (l = host_nodes; l; l = l->next) { |  | ||||||
|         bitmap_set(backend->host_nodes, l->value, 1); |         bitmap_set(backend->host_nodes, l->value, 1); | ||||||
|  |         l = l->next; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| out: |  | ||||||
|     qapi_free_uint16List(host_nodes); |  | ||||||
| #else | #else | ||||||
|     error_setg(errp, "NUMA node binding are not supported by this QEMU"); |     error_setg(errp, "NUMA node binding are not supported by this QEMU"); | ||||||
| #endif | #endif | ||||||
| @@ -223,7 +199,6 @@ static void host_memory_backend_set_prealloc(Object *obj, bool value, | |||||||
| { | { | ||||||
|     Error *local_err = NULL; |     Error *local_err = NULL; | ||||||
|     HostMemoryBackend *backend = MEMORY_BACKEND(obj); |     HostMemoryBackend *backend = MEMORY_BACKEND(obj); | ||||||
|     MachineState *ms = MACHINE(qdev_get_machine()); |  | ||||||
|  |  | ||||||
|     if (backend->force_prealloc) { |     if (backend->force_prealloc) { | ||||||
|         if (value) { |         if (value) { | ||||||
| @@ -243,7 +218,7 @@ static void host_memory_backend_set_prealloc(Object *obj, bool value, | |||||||
|         void *ptr = memory_region_get_ram_ptr(&backend->mr); |         void *ptr = memory_region_get_ram_ptr(&backend->mr); | ||||||
|         uint64_t sz = memory_region_size(&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) { |         if (local_err) { | ||||||
|             error_propagate(errp, local_err); |             error_propagate(errp, local_err); | ||||||
|             return; |             return; | ||||||
| @@ -262,11 +237,6 @@ static void host_memory_backend_init(Object *obj) | |||||||
|     backend->prealloc = mem_prealloc; |     backend->prealloc = mem_prealloc; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void host_memory_backend_post_init(Object *obj) |  | ||||||
| { |  | ||||||
|     object_apply_compat_props(obj); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| bool host_memory_backend_mr_inited(HostMemoryBackend *backend) | bool host_memory_backend_mr_inited(HostMemoryBackend *backend) | ||||||
| { | { | ||||||
|     /* |     /* | ||||||
| @@ -276,7 +246,8 @@ bool host_memory_backend_mr_inited(HostMemoryBackend *backend) | |||||||
|     return memory_region_size(&backend->mr) != 0; |     return memory_region_size(&backend->mr) != 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| MemoryRegion *host_memory_backend_get_memory(HostMemoryBackend *backend) | MemoryRegion * | ||||||
|  | host_memory_backend_get_memory(HostMemoryBackend *backend, Error **errp) | ||||||
| { | { | ||||||
|     return host_memory_backend_mr_inited(backend) ? &backend->mr : NULL; |     return host_memory_backend_mr_inited(backend) ? &backend->mr : NULL; | ||||||
| } | } | ||||||
| @@ -291,29 +262,11 @@ bool host_memory_backend_is_mapped(HostMemoryBackend *backend) | |||||||
|     return backend->is_mapped; |     return backend->is_mapped; | ||||||
| } | } | ||||||
|  |  | ||||||
| #ifdef __linux__ |  | ||||||
| size_t host_memory_backend_pagesize(HostMemoryBackend *memdev) |  | ||||||
| { |  | ||||||
|     Object *obj = OBJECT(memdev); |  | ||||||
|     char *path = object_property_get_str(obj, "mem-path", NULL); |  | ||||||
|     size_t pagesize = qemu_mempath_getpagesize(path); |  | ||||||
|  |  | ||||||
|     g_free(path); |  | ||||||
|     return pagesize; |  | ||||||
| } |  | ||||||
| #else |  | ||||||
| size_t host_memory_backend_pagesize(HostMemoryBackend *memdev) |  | ||||||
| { |  | ||||||
|     return qemu_real_host_page_size; |  | ||||||
| } |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| static void | static void | ||||||
| host_memory_backend_memory_complete(UserCreatable *uc, Error **errp) | host_memory_backend_memory_complete(UserCreatable *uc, Error **errp) | ||||||
| { | { | ||||||
|     HostMemoryBackend *backend = MEMORY_BACKEND(uc); |     HostMemoryBackend *backend = MEMORY_BACKEND(uc); | ||||||
|     HostMemoryBackendClass *bc = MEMORY_BACKEND_GET_CLASS(uc); |     HostMemoryBackendClass *bc = MEMORY_BACKEND_GET_CLASS(uc); | ||||||
|     MachineState *ms = MACHINE(qdev_get_machine()); |  | ||||||
|     Error *local_err = NULL; |     Error *local_err = NULL; | ||||||
|     void *ptr; |     void *ptr; | ||||||
|     uint64_t sz; |     uint64_t sz; | ||||||
| @@ -378,7 +331,7 @@ host_memory_backend_memory_complete(UserCreatable *uc, Error **errp) | |||||||
|          */ |          */ | ||||||
|         if (backend->prealloc) { |         if (backend->prealloc) { | ||||||
|             os_mem_prealloc(memory_region_get_fd(&backend->mr), ptr, sz, |             os_mem_prealloc(memory_region_get_fd(&backend->mr), ptr, sz, | ||||||
|                             ms->smp.cpus, &local_err); |                             smp_cpus, &local_err); | ||||||
|             if (local_err) { |             if (local_err) { | ||||||
|                 goto out; |                 goto out; | ||||||
|             } |             } | ||||||
| @@ -398,6 +351,24 @@ host_memory_backend_can_be_deleted(UserCreatable *uc) | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static char *get_id(Object *o, Error **errp) | ||||||
|  | { | ||||||
|  |     HostMemoryBackend *backend = MEMORY_BACKEND(o); | ||||||
|  |  | ||||||
|  |     return g_strdup(backend->id); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void set_id(Object *o, const char *str, Error **errp) | ||||||
|  | { | ||||||
|  |     HostMemoryBackend *backend = MEMORY_BACKEND(o); | ||||||
|  |  | ||||||
|  |     if (backend->id) { | ||||||
|  |         error_setg(errp, "cannot change property value"); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |     backend->id = g_strdup(str); | ||||||
|  | } | ||||||
|  |  | ||||||
| static bool host_memory_backend_get_share(Object *o, Error **errp) | static bool host_memory_backend_get_share(Object *o, Error **errp) | ||||||
| { | { | ||||||
|     HostMemoryBackend *backend = MEMORY_BACKEND(o); |     HostMemoryBackend *backend = MEMORY_BACKEND(o); | ||||||
| @@ -416,23 +387,6 @@ static void host_memory_backend_set_share(Object *o, bool value, Error **errp) | |||||||
|     backend->share = value; |     backend->share = value; | ||||||
| } | } | ||||||
|  |  | ||||||
| static bool |  | ||||||
| host_memory_backend_get_use_canonical_path(Object *obj, Error **errp) |  | ||||||
| { |  | ||||||
|     HostMemoryBackend *backend = MEMORY_BACKEND(obj); |  | ||||||
|  |  | ||||||
|     return backend->use_canonical_path; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void |  | ||||||
| host_memory_backend_set_use_canonical_path(Object *obj, bool value, |  | ||||||
|                                            Error **errp) |  | ||||||
| { |  | ||||||
|     HostMemoryBackend *backend = MEMORY_BACKEND(obj); |  | ||||||
|  |  | ||||||
|     backend->use_canonical_path = value; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void | static void | ||||||
| host_memory_backend_class_init(ObjectClass *oc, void *data) | host_memory_backend_class_init(ObjectClass *oc, void *data) | ||||||
| { | { | ||||||
| @@ -444,44 +398,34 @@ host_memory_backend_class_init(ObjectClass *oc, void *data) | |||||||
|     object_class_property_add_bool(oc, "merge", |     object_class_property_add_bool(oc, "merge", | ||||||
|         host_memory_backend_get_merge, |         host_memory_backend_get_merge, | ||||||
|         host_memory_backend_set_merge, &error_abort); |         host_memory_backend_set_merge, &error_abort); | ||||||
|     object_class_property_set_description(oc, "merge", |  | ||||||
|         "Mark memory as mergeable", &error_abort); |  | ||||||
|     object_class_property_add_bool(oc, "dump", |     object_class_property_add_bool(oc, "dump", | ||||||
|         host_memory_backend_get_dump, |         host_memory_backend_get_dump, | ||||||
|         host_memory_backend_set_dump, &error_abort); |         host_memory_backend_set_dump, &error_abort); | ||||||
|     object_class_property_set_description(oc, "dump", |  | ||||||
|         "Set to 'off' to exclude from core dump", &error_abort); |  | ||||||
|     object_class_property_add_bool(oc, "prealloc", |     object_class_property_add_bool(oc, "prealloc", | ||||||
|         host_memory_backend_get_prealloc, |         host_memory_backend_get_prealloc, | ||||||
|         host_memory_backend_set_prealloc, &error_abort); |         host_memory_backend_set_prealloc, &error_abort); | ||||||
|     object_class_property_set_description(oc, "prealloc", |  | ||||||
|         "Preallocate memory", &error_abort); |  | ||||||
|     object_class_property_add(oc, "size", "int", |     object_class_property_add(oc, "size", "int", | ||||||
|         host_memory_backend_get_size, |         host_memory_backend_get_size, | ||||||
|         host_memory_backend_set_size, |         host_memory_backend_set_size, | ||||||
|         NULL, NULL, &error_abort); |         NULL, NULL, &error_abort); | ||||||
|     object_class_property_set_description(oc, "size", |  | ||||||
|         "Size of the memory region (ex: 500M)", &error_abort); |  | ||||||
|     object_class_property_add(oc, "host-nodes", "int", |     object_class_property_add(oc, "host-nodes", "int", | ||||||
|         host_memory_backend_get_host_nodes, |         host_memory_backend_get_host_nodes, | ||||||
|         host_memory_backend_set_host_nodes, |         host_memory_backend_set_host_nodes, | ||||||
|         NULL, NULL, &error_abort); |         NULL, NULL, &error_abort); | ||||||
|     object_class_property_set_description(oc, "host-nodes", |  | ||||||
|         "Binds memory to the list of NUMA host nodes", &error_abort); |  | ||||||
|     object_class_property_add_enum(oc, "policy", "HostMemPolicy", |     object_class_property_add_enum(oc, "policy", "HostMemPolicy", | ||||||
|         &HostMemPolicy_lookup, |         &HostMemPolicy_lookup, | ||||||
|         host_memory_backend_get_policy, |         host_memory_backend_get_policy, | ||||||
|         host_memory_backend_set_policy, &error_abort); |         host_memory_backend_set_policy, &error_abort); | ||||||
|     object_class_property_set_description(oc, "policy", |     object_class_property_add_str(oc, "id", get_id, set_id, &error_abort); | ||||||
|         "Set the NUMA policy", &error_abort); |  | ||||||
|     object_class_property_add_bool(oc, "share", |     object_class_property_add_bool(oc, "share", | ||||||
|         host_memory_backend_get_share, host_memory_backend_set_share, |         host_memory_backend_get_share, host_memory_backend_set_share, | ||||||
|         &error_abort); |         &error_abort); | ||||||
|     object_class_property_set_description(oc, "share", | } | ||||||
|         "Mark the memory as private to QEMU or shared", &error_abort); |  | ||||||
|     object_class_property_add_bool(oc, "x-use-canonical-path-for-ramblock-id", | static void host_memory_backend_finalize(Object *o) | ||||||
|         host_memory_backend_get_use_canonical_path, | { | ||||||
|         host_memory_backend_set_use_canonical_path, &error_abort); |     HostMemoryBackend *backend = MEMORY_BACKEND(o); | ||||||
|  |     g_free(backend->id); | ||||||
| } | } | ||||||
|  |  | ||||||
| static const TypeInfo host_memory_backend_info = { | static const TypeInfo host_memory_backend_info = { | ||||||
| @@ -492,7 +436,7 @@ static const TypeInfo host_memory_backend_info = { | |||||||
|     .class_init = host_memory_backend_class_init, |     .class_init = host_memory_backend_class_init, | ||||||
|     .instance_size = sizeof(HostMemoryBackend), |     .instance_size = sizeof(HostMemoryBackend), | ||||||
|     .instance_init = host_memory_backend_init, |     .instance_init = host_memory_backend_init, | ||||||
|     .instance_post_init = host_memory_backend_post_init, |     .instance_finalize = host_memory_backend_finalize, | ||||||
|     .interfaces = (InterfaceInfo[]) { |     .interfaces = (InterfaceInfo[]) { | ||||||
|         { TYPE_USER_CREATABLE }, |         { TYPE_USER_CREATABLE }, | ||||||
|         { } |         { } | ||||||
|   | |||||||
| @@ -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); |  | ||||||
| @@ -15,7 +15,6 @@ | |||||||
| #include "chardev/char-fe.h" | #include "chardev/char-fe.h" | ||||||
| #include "qapi/error.h" | #include "qapi/error.h" | ||||||
| #include "qapi/qmp/qerror.h" | #include "qapi/qmp/qerror.h" | ||||||
| #include "qemu/module.h" |  | ||||||
|  |  | ||||||
| #define TYPE_RNG_EGD "rng-egd" | #define TYPE_RNG_EGD "rng-egd" | ||||||
| #define RNG_EGD(obj) OBJECT_CHECK(RngEgd, (obj), TYPE_RNG_EGD) | #define RNG_EGD(obj) OBJECT_CHECK(RngEgd, (obj), TYPE_RNG_EGD) | ||||||
|   | |||||||
| @@ -16,7 +16,6 @@ | |||||||
| #include "qapi/error.h" | #include "qapi/error.h" | ||||||
| #include "qapi/qmp/qerror.h" | #include "qapi/qmp/qerror.h" | ||||||
| #include "qemu/main-loop.h" | #include "qemu/main-loop.h" | ||||||
| #include "qemu/module.h" |  | ||||||
|  |  | ||||||
| struct RngRandom | struct RngRandom | ||||||
| { | { | ||||||
| @@ -113,7 +112,7 @@ static void rng_random_init(Object *obj) | |||||||
|                             rng_random_set_filename, |                             rng_random_set_filename, | ||||||
|                             NULL); |                             NULL); | ||||||
|  |  | ||||||
|     s->filename = g_strdup("/dev/urandom"); |     s->filename = g_strdup("/dev/random"); | ||||||
|     s->fd = -1; |     s->fd = -1; | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -14,7 +14,6 @@ | |||||||
| #include "sysemu/rng.h" | #include "sysemu/rng.h" | ||||||
| #include "qapi/error.h" | #include "qapi/error.h" | ||||||
| #include "qapi/qmp/qerror.h" | #include "qapi/qmp/qerror.h" | ||||||
| #include "qemu/module.h" |  | ||||||
| #include "qom/object_interfaces.h" | #include "qom/object_interfaces.h" | ||||||
|  |  | ||||||
| void rng_backend_request_entropy(RngBackend *s, size_t size, | void rng_backend_request_entropy(RngBackend *s, size_t size, | ||||||
|   | |||||||
| @@ -18,7 +18,6 @@ | |||||||
| #include "sysemu/tpm.h" | #include "sysemu/tpm.h" | ||||||
| #include "qemu/thread.h" | #include "qemu/thread.h" | ||||||
| #include "qemu/main-loop.h" | #include "qemu/main-loop.h" | ||||||
| #include "qemu/module.h" |  | ||||||
| #include "block/thread-pool.h" | #include "block/thread-pool.h" | ||||||
| #include "qemu/error-report.h" | #include "qemu/error-report.h" | ||||||
|  |  | ||||||
|   | |||||||
| @@ -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); |  | ||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user