Compare commits
	
		
			1 Commits
		
	
	
		
			rm-protoco
			...
			release_0_
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 20a407eba1 | 
							
								
								
									
										27
									
								
								.cvsignore
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								.cvsignore
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | |||||||
|  | config-host.* | ||||||
|  | dyngen | ||||||
|  | i386 | ||||||
|  | *-softmmu | ||||||
|  | *-darwin-user | ||||||
|  | *-linux-user | ||||||
|  | qemu-doc.html | ||||||
|  | qemu-tech.html | ||||||
|  | qemu-doc.info | ||||||
|  | qemu-tech.info | ||||||
|  | qemu.1 | ||||||
|  | qemu.pod | ||||||
|  | qemu-img.1 | ||||||
|  | qemu-img.pod | ||||||
|  | qemu-img | ||||||
|  | .gdbinit | ||||||
|  | *.aux | ||||||
|  | *.cp | ||||||
|  | *.dvi | ||||||
|  | *.fn | ||||||
|  | *.ky | ||||||
|  | *.log | ||||||
|  | *.pg | ||||||
|  | *.toc | ||||||
|  | *.tp | ||||||
|  | *.vr | ||||||
|  | *.d | ||||||
| @@ -1,2 +0,0 @@ | |||||||
| ((c-mode . ((c-file-style . "stroustrup") |  | ||||||
| 	    (indent-tabs-mode . nil)))) |  | ||||||
| @@ -1,15 +0,0 @@ | |||||||
| # http://editorconfig.org |  | ||||||
| root = true |  | ||||||
|  |  | ||||||
| [*] |  | ||||||
| end_of_line = lf |  | ||||||
| insert_final_newline = true |  | ||||||
| charset = utf-8 |  | ||||||
|  |  | ||||||
| [Makefile*] |  | ||||||
| indent_style = tab |  | ||||||
| indent_size = 8 |  | ||||||
|  |  | ||||||
| [*.{c,h}] |  | ||||||
| indent_style = space |  | ||||||
| indent_size = 4 |  | ||||||
							
								
								
									
										7
									
								
								.exrc
									
									
									
									
									
								
							
							
						
						
									
										7
									
								
								.exrc
									
									
									
									
									
								
							| @@ -1,7 +0,0 @@ | |||||||
| "VIM settings to match QEMU coding style.  They are activated by adding the |  | ||||||
| "following settings (without the " symbol) as last two lines in $HOME/.vimrc: |  | ||||||
| "set secure |  | ||||||
| "set exrc |  | ||||||
| set expandtab |  | ||||||
| set shiftwidth=4 |  | ||||||
| set smarttab |  | ||||||
							
								
								
									
										8
									
								
								.gdbinit
									
									
									
									
									
								
							
							
						
						
									
										8
									
								
								.gdbinit
									
									
									
									
									
								
							| @@ -1,8 +0,0 @@ | |||||||
| # GDB may have ./.gdbinit loading disabled by default.  In that case you can |  | ||||||
| # follow the instructions it prints.  They boil down to adding the following to |  | ||||||
| # your home directory's ~/.gdbinit file: |  | ||||||
| # |  | ||||||
| #   add-auto-load-safe-path /path/to/qemu/.gdbinit |  | ||||||
|  |  | ||||||
| # Load QEMU-specific sub-commands and settings |  | ||||||
| source scripts/qemu-gdb.py |  | ||||||
							
								
								
									
										208
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										208
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1,208 +0,0 @@ | |||||||
| /config-devices.* |  | ||||||
| /config-all-devices.* |  | ||||||
| /config-all-disas.* |  | ||||||
| /config-host.* |  | ||||||
| /config-target.* |  | ||||||
| /config.status |  | ||||||
| /config-temp |  | ||||||
| /trace-events-all |  | ||||||
| /trace/generated-events.h |  | ||||||
| /trace/generated-events.c |  | ||||||
| /trace/generated-helpers-wrappers.h |  | ||||||
| /trace/generated-helpers.h |  | ||||||
| /trace/generated-helpers.c |  | ||||||
| /trace/generated-tcg-tracers.h |  | ||||||
| /ui/shader/texture-blit-frag.h |  | ||||||
| /ui/shader/texture-blit-vert.h |  | ||||||
| /ui/shader/texture-blit-flip-vert.h |  | ||||||
| /ui/input-keymap-*.c |  | ||||||
| *-timestamp |  | ||||||
| /*-softmmu |  | ||||||
| /*-darwin-user |  | ||||||
| /*-linux-user |  | ||||||
| /*-bsd-user |  | ||||||
| /ivshmem-client |  | ||||||
| /ivshmem-server |  | ||||||
| /libdis* |  | ||||||
| /libuser |  | ||||||
| /linux-headers/asm |  | ||||||
| /qga/qapi-generated |  | ||||||
| /qapi-gen-timestamp |  | ||||||
| /qapi/qapi-builtin-types.[ch] |  | ||||||
| /qapi/qapi-builtin-visit.[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-events-block-core.[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-introspect.[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-visit-block-core.[ch] |  | ||||||
| /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-doc.texi |  | ||||||
| /qemu-doc.html |  | ||||||
| /qemu-doc.info |  | ||||||
| /qemu-doc.txt |  | ||||||
| /qemu-img |  | ||||||
| /qemu-nbd |  | ||||||
| /qemu-options.def |  | ||||||
| /qemu-options.texi |  | ||||||
| /qemu-img-cmds.texi |  | ||||||
| /qemu-img-cmds.h |  | ||||||
| /qemu-io |  | ||||||
| /qemu-ga |  | ||||||
| /qemu-bridge-helper |  | ||||||
| /qemu-keymap |  | ||||||
| /qemu-monitor.texi |  | ||||||
| /qemu-monitor-info.texi |  | ||||||
| /qemu-version.h |  | ||||||
| /qemu-version.h.tmp |  | ||||||
| /module_block.h |  | ||||||
| /scsi/qemu-pr-helper |  | ||||||
| /vhost-user-scsi |  | ||||||
| /vhost-user-blk |  | ||||||
| /fsdev/virtfs-proxy-helper |  | ||||||
| *.tmp |  | ||||||
| *.[1-9] |  | ||||||
| *.a |  | ||||||
| *.aux |  | ||||||
| *.cp |  | ||||||
| *.exe |  | ||||||
| *.msi |  | ||||||
| *.dll |  | ||||||
| *.so |  | ||||||
| *.mo |  | ||||||
| *.fn |  | ||||||
| *.ky |  | ||||||
| *.log |  | ||||||
| *.pdf |  | ||||||
| *.pod |  | ||||||
| *.cps |  | ||||||
| *.fns |  | ||||||
| *.kys |  | ||||||
| *.pg |  | ||||||
| *.pyc |  | ||||||
| *.toc |  | ||||||
| *.tp |  | ||||||
| *.vr |  | ||||||
| *.d |  | ||||||
| !/scripts/qemu-guest-agent/fsfreeze-hook.d |  | ||||||
| *.o |  | ||||||
| .sdk |  | ||||||
| *.gcda |  | ||||||
| *.gcno |  | ||||||
| /pc-bios/bios-pq/status |  | ||||||
| /pc-bios/vgabios-pq/status |  | ||||||
| /pc-bios/optionrom/linuxboot.asm |  | ||||||
| /pc-bios/optionrom/linuxboot.bin |  | ||||||
| /pc-bios/optionrom/linuxboot.raw |  | ||||||
| /pc-bios/optionrom/linuxboot.img |  | ||||||
| /pc-bios/optionrom/linuxboot_dma.asm |  | ||||||
| /pc-bios/optionrom/linuxboot_dma.bin |  | ||||||
| /pc-bios/optionrom/linuxboot_dma.raw |  | ||||||
| /pc-bios/optionrom/linuxboot_dma.img |  | ||||||
| /pc-bios/optionrom/multiboot.asm |  | ||||||
| /pc-bios/optionrom/multiboot.bin |  | ||||||
| /pc-bios/optionrom/multiboot.raw |  | ||||||
| /pc-bios/optionrom/multiboot.img |  | ||||||
| /pc-bios/optionrom/kvmvapic.asm |  | ||||||
| /pc-bios/optionrom/kvmvapic.bin |  | ||||||
| /pc-bios/optionrom/kvmvapic.raw |  | ||||||
| /pc-bios/optionrom/kvmvapic.img |  | ||||||
| /pc-bios/s390-ccw/s390-ccw.elf |  | ||||||
| /pc-bios/s390-ccw/s390-ccw.img |  | ||||||
| /docs/interop/qemu-ga-qapi.texi |  | ||||||
| /docs/interop/qemu-ga-ref.html |  | ||||||
| /docs/interop/qemu-ga-ref.info* |  | ||||||
| /docs/interop/qemu-ga-ref.txt |  | ||||||
| /docs/interop/qemu-qmp-qapi.texi |  | ||||||
| /docs/interop/qemu-qmp-ref.html |  | ||||||
| /docs/interop/qemu-qmp-ref.info* |  | ||||||
| /docs/interop/qemu-qmp-ref.txt |  | ||||||
| /docs/version.texi |  | ||||||
| *.tps |  | ||||||
| .stgit-* |  | ||||||
| .git-submodule-status |  | ||||||
| cscope.* |  | ||||||
| tags |  | ||||||
| TAGS |  | ||||||
| docker-src.* |  | ||||||
| *~ |  | ||||||
| *.ast_raw |  | ||||||
| *.depend_raw |  | ||||||
| trace.h |  | ||||||
| trace.c |  | ||||||
| trace-ust.h |  | ||||||
| trace-ust.h |  | ||||||
| trace-dtrace.h |  | ||||||
| trace-dtrace.dtrace |  | ||||||
| trace-root.h |  | ||||||
| trace-root.c |  | ||||||
| trace-ust-root.h |  | ||||||
| trace-ust-root.h |  | ||||||
| trace-ust-all.h |  | ||||||
| trace-ust-all.c |  | ||||||
| trace-dtrace-root.h |  | ||||||
| trace-dtrace-root.dtrace |  | ||||||
| trace-ust-all.h |  | ||||||
| trace-ust-all.c |  | ||||||
							
								
								
									
										48
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										48
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							| @@ -1,48 +0,0 @@ | |||||||
| [submodule "roms/vgabios"] |  | ||||||
| 	path = roms/vgabios |  | ||||||
| 	url = git://git.qemu-project.org/vgabios.git/ |  | ||||||
| [submodule "roms/seabios"] |  | ||||||
| 	path = roms/seabios |  | ||||||
| 	url = git://git.qemu-project.org/seabios.git/ |  | ||||||
| [submodule "roms/SLOF"] |  | ||||||
| 	path = roms/SLOF |  | ||||||
| 	url = git://git.qemu-project.org/SLOF.git |  | ||||||
| [submodule "roms/ipxe"] |  | ||||||
| 	path = roms/ipxe |  | ||||||
| 	url = git://git.qemu-project.org/ipxe.git |  | ||||||
| [submodule "roms/openbios"] |  | ||||||
| 	path = roms/openbios |  | ||||||
| 	url = git://git.qemu-project.org/openbios.git |  | ||||||
| [submodule "roms/openhackware"] |  | ||||||
| 	path = roms/openhackware |  | ||||||
| 	url = git://git.qemu-project.org/openhackware.git |  | ||||||
| [submodule "roms/qemu-palcode"] |  | ||||||
| 	path = roms/qemu-palcode |  | ||||||
| 	url = git://github.com/rth7680/qemu-palcode.git |  | ||||||
| [submodule "roms/sgabios"] |  | ||||||
| 	path = roms/sgabios |  | ||||||
| 	url = git://git.qemu-project.org/sgabios.git |  | ||||||
| [submodule "dtc"] |  | ||||||
| 	path = dtc |  | ||||||
| 	url = git://git.qemu-project.org/dtc.git |  | ||||||
| [submodule "roms/u-boot"] |  | ||||||
| 	path = roms/u-boot |  | ||||||
| 	url = git://git.qemu-project.org/u-boot.git |  | ||||||
| [submodule "roms/skiboot"] |  | ||||||
| 	path = roms/skiboot |  | ||||||
| 	url = git://git.qemu.org/skiboot.git |  | ||||||
| [submodule "roms/QemuMacDrivers"] |  | ||||||
| 	path = roms/QemuMacDrivers |  | ||||||
| 	url = git://git.qemu.org/QemuMacDrivers.git |  | ||||||
| [submodule "ui/keycodemapdb"] |  | ||||||
| 	path = ui/keycodemapdb |  | ||||||
| 	url = git://git.qemu.org/keycodemapdb.git |  | ||||||
| [submodule "capstone"] |  | ||||||
| 	path = capstone |  | ||||||
| 	url = git://git.qemu.org/capstone.git |  | ||||||
| [submodule "roms/seabios-hppa"] |  | ||||||
| 	path = roms/seabios-hppa |  | ||||||
| 	url = git://github.com/hdeller/seabios-hppa.git |  | ||||||
| [submodule "roms/u-boot-sam460ex"] |  | ||||||
| 	path = roms/u-boot-sam460ex |  | ||||||
| 	url = git://github.com/zbalaton/u-boot-sam460ex |  | ||||||
							
								
								
									
										51
									
								
								.gitpublish
									
									
									
									
									
								
							
							
						
						
									
										51
									
								
								.gitpublish
									
									
									
									
									
								
							| @@ -1,51 +0,0 @@ | |||||||
| # |  | ||||||
| # Common git-publish profiles that can be used to send patches to QEMU upstream. |  | ||||||
| # |  | ||||||
| # See https://github.com/stefanha/git-publish for more information |  | ||||||
| # |  | ||||||
| [gitpublishprofile "default"] |  | ||||||
| base = master |  | ||||||
| to = qemu-devel@nongnu.org |  | ||||||
| cccmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback 2>/dev/null |  | ||||||
|  |  | ||||||
| [gitpublishprofile "rfc"] |  | ||||||
| base = master |  | ||||||
| prefix = RFC PATCH |  | ||||||
| to = qemu-devel@nongnu.org |  | ||||||
| cccmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback 2>/dev/null |  | ||||||
|  |  | ||||||
| [gitpublishprofile "stable"] |  | ||||||
| base = master |  | ||||||
| to = qemu-devel@nongnu.org |  | ||||||
| cc = qemu-stable@nongnu.org |  | ||||||
| cccmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback 2>/dev/null |  | ||||||
|  |  | ||||||
| [gitpublishprofile "trivial"] |  | ||||||
| base = master |  | ||||||
| to = qemu-devel@nongnu.org |  | ||||||
| cc = qemu-trivial@nongnu.org |  | ||||||
| cccmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback 2>/dev/null |  | ||||||
|  |  | ||||||
| [gitpublishprofile "block"] |  | ||||||
| base = master |  | ||||||
| to = qemu-devel@nongnu.org |  | ||||||
| cc = qemu-block@nongnu.org |  | ||||||
| cccmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback 2>/dev/null |  | ||||||
|  |  | ||||||
| [gitpublishprofile "arm"] |  | ||||||
| base = master |  | ||||||
| to = qemu-devel@nongnu.org |  | ||||||
| cc = qemu-arm@nongnu.org |  | ||||||
| cccmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback 2>/dev/null |  | ||||||
|  |  | ||||||
| [gitpublishprofile "s390"] |  | ||||||
| base = master |  | ||||||
| to = qemu-devel@nongnu.org |  | ||||||
| cc = qemu-s390@nongnu.org |  | ||||||
| cccmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback 2>/dev/null |  | ||||||
|  |  | ||||||
| [gitpublishprofile "ppc"] |  | ||||||
| base = master |  | ||||||
| to = qemu-devel@nongnu.org |  | ||||||
| cc = qemu-ppc@nongnu.org |  | ||||||
| cccmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback 2>/dev/null |  | ||||||
							
								
								
									
										24
									
								
								.mailmap
									
									
									
									
									
								
							
							
						
						
									
										24
									
								
								.mailmap
									
									
									
									
									
								
							| @@ -1,24 +0,0 @@ | |||||||
| # 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. |  | ||||||
| # |  | ||||||
| Andrzej Zaborowski <balrogg@gmail.com> balrog <balrog@c046a42c-6fe2-441c-8c8c-71466251a162> |  | ||||||
| Anthony Liguori <anthony@codemonkey.ws> aliguori <aliguori@c046a42c-6fe2-441c-8c8c-71466251a162> |  | ||||||
| Anthony Liguori <anthony@codemonkey.ws> Anthony Liguori <aliguori@us.ibm.com> |  | ||||||
| Aurelien Jarno <aurelien@aurel32.net> aurel32 <aurel32@c046a42c-6fe2-441c-8c8c-71466251a162> |  | ||||||
| Blue Swirl <blauwirbel@gmail.com> blueswir1 <blueswir1@c046a42c-6fe2-441c-8c8c-71466251a162> |  | ||||||
| Edgar E. Iglesias <edgar.iglesias@gmail.com> edgar_igl <edgar_igl@c046a42c-6fe2-441c-8c8c-71466251a162> |  | ||||||
| Fabrice Bellard <fabrice@bellard.org> bellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162> |  | ||||||
| James Hogan <jhogan@kernel.org> <james.hogan@imgtec.com> |  | ||||||
| Jocelyn Mayer <l_indien@magic.fr> j_mayer <j_mayer@c046a42c-6fe2-441c-8c8c-71466251a162> |  | ||||||
| Paul Brook <paul@codesourcery.com> pbrook <pbrook@c046a42c-6fe2-441c-8c8c-71466251a162> |  | ||||||
| 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> |  | ||||||
| malc <av1474@comtv.ru> malc <malc@c046a42c-6fe2-441c-8c8c-71466251a162> |  | ||||||
| # There is also a: |  | ||||||
| #    (no author) <(no author)@c046a42c-6fe2-441c-8c8c-71466251a162> |  | ||||||
| # for the cvs2svn initialization commit e63c3dc74bf. |  | ||||||
| # |  | ||||||
| # Also list preferred name forms where people have changed their |  | ||||||
| # git author config |  | ||||||
| Daniel P. Berrangé <berrange@redhat.com> |  | ||||||
| @@ -1,47 +0,0 @@ | |||||||
| language: c |  | ||||||
| git: |  | ||||||
|    submodules: false |  | ||||||
| env: |  | ||||||
|   global: |  | ||||||
|     - LC_ALL=C |  | ||||||
|   matrix: |  | ||||||
|     - IMAGE=debian-amd64 |  | ||||||
|       TARGET_LIST=x86_64-softmmu,x86_64-linux-user |  | ||||||
|     - IMAGE=debian-win32-cross |  | ||||||
|       TARGET_LIST=arm-softmmu,i386-softmmu,lm32-softmmu |  | ||||||
|     - IMAGE=debian-win64-cross |  | ||||||
|       TARGET_LIST=aarch64-softmmu,sparc64-softmmu,x86_64-softmmu |  | ||||||
|     - IMAGE=debian-armel-cross |  | ||||||
|       TARGET_LIST=arm-softmmu,arm-linux-user,armeb-linux-user |  | ||||||
|     - IMAGE=debian-armhf-cross |  | ||||||
|       TARGET_LIST=arm-softmmu,arm-linux-user,armeb-linux-user |  | ||||||
|     - IMAGE=debian-arm64-cross |  | ||||||
|       TARGET_LIST=aarch64-softmmu,aarch64-linux-user |  | ||||||
|     - IMAGE=debian-s390x-cross |  | ||||||
|       TARGET_LIST=s390x-softmmu,s390x-linux-user |  | ||||||
|     - IMAGE=debian-mips-cross |  | ||||||
|       TARGET_LIST=mips-softmmu,mipsel-linux-user |  | ||||||
|     - IMAGE=debian-mips64el-cross |  | ||||||
|       TARGET_LIST=mips64el-softmmu,mips64el-linux-user |  | ||||||
|     - IMAGE=debian-ppc64el-cross |  | ||||||
|       TARGET_LIST=ppc64-softmmu,ppc64-linux-user,ppc64abi32-linux-user |  | ||||||
| build: |  | ||||||
|   pre_ci: |  | ||||||
|     - make docker-image-${IMAGE} V=1 |  | ||||||
|   pre_ci_boot: |  | ||||||
|     image_name: qemu |  | ||||||
|     image_tag: ${IMAGE} |  | ||||||
|     pull: false |  | ||||||
|     options: "-e HOME=/root" |  | ||||||
|   ci: |  | ||||||
|     - 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} |  | ||||||
|     - make -j$(($(getconf _NPROCESSORS_ONLN) + 1)) |  | ||||||
							
								
								
									
										203
									
								
								.travis.yml
									
									
									
									
									
								
							
							
						
						
									
										203
									
								
								.travis.yml
									
									
									
									
									
								
							| @@ -1,203 +0,0 @@ | |||||||
| sudo: false |  | ||||||
| language: c |  | ||||||
| python: |  | ||||||
|   - "2.6" |  | ||||||
| compiler: |  | ||||||
|   - gcc |  | ||||||
| cache: ccache |  | ||||||
| addons: |  | ||||||
|   apt: |  | ||||||
|     packages: |  | ||||||
|       # Build dependencies |  | ||||||
|       - libaio-dev |  | ||||||
|       - libattr1-dev |  | ||||||
|       - libbrlapi-dev |  | ||||||
|       - libcap-ng-dev |  | ||||||
|       - libgcc-4.8-dev |  | ||||||
|       - libgnutls-dev |  | ||||||
|       - libgtk-3-dev |  | ||||||
|       - libiscsi-dev |  | ||||||
|       - liblttng-ust-dev |  | ||||||
|       - libncurses5-dev |  | ||||||
|       - libnfs-dev |  | ||||||
|       - libnss3-dev |  | ||||||
|       - libpixman-1-dev |  | ||||||
|       - libpng12-dev |  | ||||||
|       - librados-dev |  | ||||||
|       - libsdl1.2-dev |  | ||||||
|       - libseccomp-dev |  | ||||||
|       - libspice-protocol-dev |  | ||||||
|       - libspice-server-dev |  | ||||||
|       - libssh2-1-dev |  | ||||||
|       - liburcu-dev |  | ||||||
|       - libusb-1.0-0-dev |  | ||||||
|       - libvte-2.90-dev |  | ||||||
|       - sparse |  | ||||||
|       - uuid-dev |  | ||||||
|  |  | ||||||
| # The channel name "irc.oftc.net#qemu" is encrypted against qemu/qemu |  | ||||||
| # to prevent IRC notifications from forks. This was created using: |  | ||||||
| # $ travis encrypt -r "qemu/qemu" "irc.oftc.net#qemu" |  | ||||||
| notifications: |  | ||||||
|   irc: |  | ||||||
|     channels: |  | ||||||
|       - secure: "F7GDRgjuOo5IUyRLqSkmDL7kvdU4UcH3Lm/W2db2JnDHTGCqgEdaYEYKciyCLZ57vOTsTsOgesN8iUT7hNHBd1KWKjZe9KDTZWppWRYVwAwQMzVeSOsbbU4tRoJ6Pp+3qhH1Z0eGYR9ZgKYAoTumDFgSAYRp4IscKS8jkoedOqM=" |  | ||||||
|     on_success: change |  | ||||||
|     on_failure: always |  | ||||||
| env: |  | ||||||
|   global: |  | ||||||
|     - TEST_CMD="make check" |  | ||||||
|     - MAKEFLAGS="-j3" |  | ||||||
|   matrix: |  | ||||||
|     - CONFIG="" |  | ||||||
|     - CONFIG="--enable-debug --enable-debug-tcg --enable-trace-backends=log" |  | ||||||
|     - CONFIG="--disable-linux-aio --disable-cap-ng --disable-attr --disable-brlapi --disable-uuid --disable-libusb" |  | ||||||
|     - CONFIG="--enable-modules --disable-linux-user" |  | ||||||
|     - CONFIG="--with-coroutine=ucontext --disable-linux-user" |  | ||||||
|     - CONFIG="--with-coroutine=sigaltstack --disable-linux-user" |  | ||||||
| git: |  | ||||||
|   # we want to do this ourselves |  | ||||||
|   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: |  | ||||||
|   - ./configure ${CONFIG} |  | ||||||
| script: |  | ||||||
|   - make ${MAKEFLAGS} && ${TEST_CMD} |  | ||||||
| matrix: |  | ||||||
|   include: |  | ||||||
|     # Test with CLang for compile portability |  | ||||||
|     - env: CONFIG="" |  | ||||||
|       compiler: clang |  | ||||||
|     # gprof/gcov are GCC features |  | ||||||
|     - env: CONFIG="--enable-gprof --enable-gcov --disable-pie" |  | ||||||
|       compiler: gcc |  | ||||||
|     # We manually include builds which we disable "make check" for |  | ||||||
|     - env: CONFIG="--enable-debug --enable-tcg-interpreter" |  | ||||||
|            TEST_CMD="" |  | ||||||
|       compiler: gcc |  | ||||||
|     - env: CONFIG="--enable-trace-backends=simple" |  | ||||||
|            TEST_CMD="" |  | ||||||
|       compiler: gcc |  | ||||||
|     - env: CONFIG="--enable-trace-backends=ftrace" |  | ||||||
|            TEST_CMD="" |  | ||||||
|       compiler: gcc |  | ||||||
|     - env: CONFIG="--enable-trace-backends=ust" |  | ||||||
|            TEST_CMD="" |  | ||||||
|       compiler: gcc |  | ||||||
|     - env: CONFIG="--disable-tcg" |  | ||||||
|            TEST_CMD="" |  | ||||||
|       compiler: gcc |  | ||||||
|     - env: CONFIG="" |  | ||||||
|       os: osx |  | ||||||
|       compiler: clang |  | ||||||
|     # Plain Trusty System Build |  | ||||||
|     - env: CONFIG="--disable-linux-user" |  | ||||||
|       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 |  | ||||||
|     # Plain Trusty Linux User Build |  | ||||||
|     - env: CONFIG="--disable-system" |  | ||||||
|       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: |  | ||||||
|         - "3.0" |  | ||||||
|       env: |  | ||||||
|         - COMPILER_NAME=clang CXX=clang++-3.9 CC=clang-3.9 |  | ||||||
|         - CONFIG="--disable-linux-user --cc=clang-3.9 --cxx=clang++-3.9 --python=/usr/bin/python3" |  | ||||||
|       before_install: |  | ||||||
|         - wget -nv -O - http://llvm.org/apt/llvm-snapshot.gpg.key | sudo apt-key add - |  | ||||||
|         - 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: |  | ||||||
|         - "3.6" |  | ||||||
|       env: |  | ||||||
|         - COMPILER_NAME=clang CXX=clang++-3.9 CC=clang-3.9 |  | ||||||
|         - CONFIG="--disable-system --cc=clang-3.9 --cxx=clang++-3.9 --python=/usr/bin/python3" |  | ||||||
|       before_install: |  | ||||||
|         - wget -nv -O - http://llvm.org/apt/llvm-snapshot.gpg.key | sudo apt-key add - |  | ||||||
|         - 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 |  | ||||||
|     # Using newer GCC with sanitizers |  | ||||||
|     - addons: |  | ||||||
|         apt: |  | ||||||
|           sources: |  | ||||||
|             # PPAs for newer toolchains |  | ||||||
|             - ubuntu-toolchain-r-test |  | ||||||
|           packages: |  | ||||||
|             # Extra toolchains |  | ||||||
|             - gcc-5 |  | ||||||
|             - g++-5 |  | ||||||
|             # Build dependencies |  | ||||||
|             - libaio-dev |  | ||||||
|             - libattr1-dev |  | ||||||
|             - libbrlapi-dev |  | ||||||
|             - libcap-ng-dev |  | ||||||
|             - libgnutls-dev |  | ||||||
|             - libgtk-3-dev |  | ||||||
|             - libiscsi-dev |  | ||||||
|             - liblttng-ust-dev |  | ||||||
|             - libnfs-dev |  | ||||||
|             - libncurses5-dev |  | ||||||
|             - libnss3-dev |  | ||||||
|             - libpixman-1-dev |  | ||||||
|             - libpng12-dev |  | ||||||
|             - librados-dev |  | ||||||
|             - libsdl1.2-dev |  | ||||||
|             - libseccomp-dev |  | ||||||
|             - libspice-protocol-dev |  | ||||||
|             - libspice-server-dev |  | ||||||
|             - libssh2-1-dev |  | ||||||
|             - liburcu-dev |  | ||||||
|             - libusb-1.0-0-dev |  | ||||||
|             - libvte-2.90-dev |  | ||||||
|             - sparse |  | ||||||
|             - uuid-dev |  | ||||||
|       language: generic |  | ||||||
|       compiler: none |  | ||||||
|       env: |  | ||||||
|         - COMPILER_NAME=gcc CXX=g++-5 CC=gcc-5 |  | ||||||
|         - CONFIG="--cc=gcc-5 --cxx=g++-5 --disable-pie --disable-linux-user" |  | ||||||
|         - TEST_CMD="" |  | ||||||
|       before_script: |  | ||||||
|         - ./configure ${CONFIG} --extra-cflags="-g3 -O0 -fsanitize=thread -fuse-ld=gold" || cat config.log |  | ||||||
							
								
								
									
										160
									
								
								CODING_STYLE
									
									
									
									
									
								
							
							
						
						
									
										160
									
								
								CODING_STYLE
									
									
									
									
									
								
							| @@ -1,160 +0,0 @@ | |||||||
| 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 |  | ||||||
							
								
								
									
										18
									
								
								COPYING
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								COPYING
									
									
									
									
									
								
							| @@ -1,8 +1,8 @@ | |||||||
| 		    GNU GENERAL PUBLIC LICENSE | 		    GNU GENERAL PUBLIC LICENSE | ||||||
| 		       Version 2, June 1991 | 		       Version 2, June 1991 | ||||||
|  |  | ||||||
|  Copyright (C) 1989, 1991 Free Software Foundation, Inc., |  Copyright (C) 1989, 1991 Free Software Foundation, Inc. | ||||||
|  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |                           675 Mass Ave, Cambridge, MA 02139, 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. | ||||||
|  |  | ||||||
| @@ -15,7 +15,7 @@ software--to make sure the software is free for all its users.  This | |||||||
| General Public License applies to most of the Free Software | General Public License applies to most of the Free Software | ||||||
| Foundation's software and to any other program whose authors commit to | Foundation's software and to any other program whose authors commit to | ||||||
| using it.  (Some other Free Software Foundation software is covered by | using it.  (Some other Free Software Foundation software is covered by | ||||||
| the GNU Lesser General Public License instead.)  You can apply it to | the GNU Library General Public License instead.)  You can apply it to | ||||||
| your programs, too. | your programs, too. | ||||||
|  |  | ||||||
|   When we speak of free software, we are referring to freedom, not |   When we speak of free software, we are referring to freedom, not | ||||||
| @@ -291,7 +291,7 @@ convey the exclusion of warranty; and each file should have at least | |||||||
| the "copyright" line and a pointer to where the full notice is found. | the "copyright" line and a pointer to where the full notice is found. | ||||||
|  |  | ||||||
|     <one line to give the program's name and a brief idea of what it does.> |     <one line to give the program's name and a brief idea of what it does.> | ||||||
|     Copyright (C) <year>  <name of author> |     Copyright (C) 19yy  <name of author> | ||||||
|  |  | ||||||
|     This program is free software; you can redistribute it and/or modify |     This program is free software; you can redistribute it and/or modify | ||||||
|     it under the terms of the GNU General Public License as published by |     it under the terms of the GNU General Public License as published by | ||||||
| @@ -303,16 +303,16 @@ the "copyright" line and a pointer to where the full notice is found. | |||||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the |     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|     GNU General Public License for more details. |     GNU General Public License for more details. | ||||||
|  |  | ||||||
|     You should have received a copy of the GNU General Public License along |     You should have received a copy of the GNU General Public License | ||||||
|     with this program; if not, write to the Free Software Foundation, Inc., |     along with this program; if not, write to the Free Software | ||||||
|     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, 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. | ||||||
|  |  | ||||||
| If the program is interactive, make it output a short notice like this | If the program is interactive, make it output a short notice like this | ||||||
| when it starts in an interactive mode: | when it starts in an interactive mode: | ||||||
|  |  | ||||||
|     Gnomovision version 69, Copyright (C) year name of author |     Gnomovision version 69, Copyright (C) 19yy name of author | ||||||
|     Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. |     Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. | ||||||
|     This is free software, and you are welcome to redistribute it |     This is free software, and you are welcome to redistribute it | ||||||
|     under certain conditions; type `show c' for details. |     under certain conditions; type `show c' for details. | ||||||
| @@ -335,5 +335,5 @@ necessary.  Here is a sample; alter the names: | |||||||
| This General Public License does not permit incorporating your program into | This General Public License does not permit incorporating your program into | ||||||
| proprietary programs.  If your program is a subroutine library, you may | proprietary programs.  If your program is a subroutine library, you may | ||||||
| consider it more useful to permit linking proprietary applications with the | consider it more useful to permit linking proprietary applications with the | ||||||
| library.  If this is what you want to do, use the GNU Lesser General | library.  If this is what you want to do, use the GNU Library General | ||||||
| Public License instead of this License. | Public License instead of this License. | ||||||
|   | |||||||
| @@ -2,7 +2,7 @@ | |||||||
| 		       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 St, Fifth Floor, Boston, MA  02110-1301  USA |      59 Temple Place, Suite 330, Boston, MA  02111-1307  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. | ||||||
|  |  | ||||||
| @@ -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 St, Fifth Floor, Boston, MA  02110-1301  USA |     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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. | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										270
									
								
								COPYING.PYTHON
									
									
									
									
									
								
							
							
						
						
									
										270
									
								
								COPYING.PYTHON
									
									
									
									
									
								
							| @@ -1,270 +0,0 @@ | |||||||
| 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. |  | ||||||
							
								
								
									
										147
									
								
								Changelog
									
									
									
									
									
								
							
							
						
						
									
										147
									
								
								Changelog
									
									
									
									
									
								
							| @@ -1,144 +1,3 @@ | |||||||
| This file documents changes for QEMU releases 0.12 and earlier. |  | ||||||
| For changelog information for later releases, see |  | ||||||
| https://wiki.qemu.org/ChangeLog or look at the git history for |  | ||||||
| more detailed information. |  | ||||||
|  |  | ||||||
|  |  | ||||||
| version 0.12.0: |  | ||||||
|  |  | ||||||
|   - Update to SeaBIOS 0.5.0 |  | ||||||
|   - e1000: fix device link status in Linux (Anthony Liguori) |  | ||||||
|   - monitor: fix QMP for balloon command (Luiz Capitulino) |  | ||||||
|   - QMP: Return an empty dict by default (Luiz Capitulino) |  | ||||||
|   - QMP: Only handle converted commands (Luiz Capitulino) |  | ||||||
|   - pci: support PCI based option rom loading (Gerd Hoffman/Anthony Liguori) |  | ||||||
|   - Fix backcompat for hotplug of SCSI controllers (Daniel P. Berrange) |  | ||||||
|   - fdc: fix migration from 0.11 (Juan Quintela) |  | ||||||
|   - vmware-vga: fix segv on cursor resize. (Dave Airlie) |  | ||||||
|   - vmware-vga: various fixes (Dave Airlie/Anthony Liguori) |  | ||||||
|   - qdev: improve property error reporting. (Gerd Hoffmann) |  | ||||||
|   - fix vga names in default_list (Gerd Hoffmann) |  | ||||||
|   - usb-host: check mon before using it. (Gerd Hoffmann) |  | ||||||
|   - usb-net: use qdev for -usbdevice (Gerd Hoffmann) |  | ||||||
|   - monitor: Catch printing to non-existent monitor (Luiz Capitulino) |  | ||||||
|   - Avoid permanently disabled QEMU monitor when UNIX migration fails (Daniel P. Berrange) |  | ||||||
|   - Fix loading of ELF multiboot kernels (Kevin Wolf) |  | ||||||
|   - qemu-io: Fix memory leak (Kevin Wolf) |  | ||||||
|   - Fix thinko in linuxboot.S (Paolo Bonzini) |  | ||||||
|   - target-i386: Fix evaluation of DR7 register (Jan Kiszka) |  | ||||||
|   - vnc: hextile: do not generate ForegroundSpecified and SubrectsColoured tiles (Anthony Liguori) |  | ||||||
|   - S390: Bail out without KVM (Alexander Graf) |  | ||||||
|   - S390: Don't tell guest we're updating config space (Alexander Graf) |  | ||||||
|   - target-s390: Fail on unknown instructions (Alexander Graf) |  | ||||||
|   - osdep: Fix runtime failure on older Linux kernels (Andre Przywara) |  | ||||||
|   - Fix a make -j race (Juergen Lock) |  | ||||||
|   - target-alpha: Fix generic ctz64. (Richard Henderson) |  | ||||||
|   - s390: Fix buggy assignment (Stefan Weil) |  | ||||||
|   - target-mips: fix user-mode emulation startup (Nathan Froyd) |  | ||||||
|   - target-i386: Update CPUID feature set for TCG (Andre Przywara) |  | ||||||
|   - s390: fix build on 32 bit host (Michael S. Tsirkin) |  | ||||||
| 	 |  | ||||||
| version 0.12.0-rc2: |  | ||||||
|  |  | ||||||
|   - v2: properly save kvm system time msr registers (Glauber Costa) |  | ||||||
|   - convert more monitor commands to qmp (Luiz Capitulino) |  | ||||||
|   - vnc: fix capslock tracking logic. (Gerd Hoffmann) |  | ||||||
|   - QemuOpts: allow larger option values. (Gerd Hoffmann) |  | ||||||
|   - scsi: fix drive hotplug. (Gerd Hoffmann) |  | ||||||
|   - pci: don't hw_error() when no slot is available. (Gerd Hoffmann) |  | ||||||
|   - pci: don't abort() when trying to hotplug with acpi off. (Gerd Hoffmann) |  | ||||||
|   - allow default devices to be implemented in config file (Gerd Hoffman) |  | ||||||
|   - vc: colorize chardev title line with blue background. (Gerd Hoffmann) |  | ||||||
|   - chardev: make chardevs specified in config file work. (Gerd Hoffmann) |  | ||||||
|   - qdev: also match bus name for global properties (Gerd Hoffmann) |  | ||||||
|   - qdev: add command line option to set global defaults for properties. (Gerd Hoffmann) |  | ||||||
|   - kvm: x86: Save/restore exception_index (Jan Kiszka) |  | ||||||
|   - qdev: Replace device names containing whitespace (Markus Armbruster) |  | ||||||
|   - fix rtc-td-hack on host without high-res timers (Gleb Natapov) |  | ||||||
|   - virtio: verify features on load (Michael S. Tsirkin) |  | ||||||
|   - vmware_vga: add rom file so that it boots. (Dave Airlie) |  | ||||||
|   - Do not abort on qemu_malloc(0) in production builds (Anthony Liguori) |  | ||||||
|   - Fix ARM userspace strex implementation. (Paul Brook) |  | ||||||
|   - qemu: delete rule target on error (Michael S. Tsirkin) |  | ||||||
|   - QMP: add human-readable description to error response (Markus Armbruster) |  | ||||||
|   - convert more monitor commands to QError (Markus Armbruster) |  | ||||||
|   - monitor: Fix double-prompt after "change vnc passwd BLA" (Markus Armbruster) |  | ||||||
|   - monitor: do_cont(): Don't ask for passwords (Luiz Capitulino) |  | ||||||
|   - monitor: Introduce 'block_passwd' command (Luiz Capitulino) |  | ||||||
|   - pci: interrupt disable bit support (Michael S. Tsirkin) |  | ||||||
|   - pci: interrupt status bit implementation (Michael S. Tsirkin) |  | ||||||
|   - pci: prepare irq code for interrupt state (Michael S. Tsirkin) |  | ||||||
|   - msix: function mask support (Michael S. Tsirkin) |  | ||||||
|   - msix: macro rename for function mask support (Michael S. Tsirkin) |  | ||||||
|   - cpuid: Fix multicore setup on Intel (Andre Przywara) |  | ||||||
|   - kvm: x86: Fix initial kvm_has_msr_star (Jan Kiszka) |  | ||||||
|   - Update OpenBIOS images to r640 (Aurelien Jarno)	 |  | ||||||
|  |  | ||||||
| version 0.10.2: |  | ||||||
|  |  | ||||||
|   - fix savevm/loadvm (Anthony Liguori) |  | ||||||
|   - live migration: fix dirty tracking windows (Glauber Costa) |  | ||||||
|   - live migration: improve error propagation (Glauber Costa) |  | ||||||
|   - qcow2: fix image creation for > ~2TB images (Chris Wright) |  | ||||||
|   - hotplug: fix error handling for if= parameter (Eduardo Habkost) |  | ||||||
|   - qcow2: fix data corruption (Nolan Leake) |  | ||||||
|   - virtio: fix guest oops with 2.6.25 kernels (Rusty Russell) |  | ||||||
|   - SH4: add support for -kernel (Takashi Yoshii, Aurelien Jarno) |  | ||||||
|   - hotplug: fix closing of char devices (Jan Kiszka) |  | ||||||
|   - hotplug: remove incorrect check for device name (Eduardo Habkost) |  | ||||||
|   - enable -k on win32 (Herve Poussineau) |  | ||||||
|   - configure: use LANG=C for grep (Andreas Faerber) |  | ||||||
|   - fix VGA regression (malc) |  | ||||||
| 	 |  | ||||||
| version 0.10.1: |  | ||||||
|  |  | ||||||
|   - virtio-net: check right return size on sg list (Alex Williamson) |  | ||||||
|   - Make qemu_announce_self handle holes (live migration after hotplug) |  | ||||||
|     (Marcelo Tosatti) |  | ||||||
|   - Revert r6804-r6808 (qcow2 allocation info).  This series of changes added |  | ||||||
|     a high cost to startup for large qcow2 images (Anthony Liguori) |  | ||||||
|   - qemu-img: fix help message (Aurelien Jarno) |  | ||||||
|   - Fix build for non-default installs of SDL (Anthony Liguori) |  | ||||||
|   - Fix race condition in env->interrupt_request.  When using TCG and a dynticks |  | ||||||
|     host timer, this condition could cause TCG to get stuck in an infinite |  | ||||||
|     loop (Aurelien Jarno) |  | ||||||
|   - Fix reading encrypted hard disk passwords during early startup (Jan Kiszka) |  | ||||||
|   - Fix encrypted disk reporting in 'info block' (Jan Kiszka) |  | ||||||
|   - Fix console size with tiny displays (MusicPal) (Jan Kiszka) |  | ||||||
|   - Improve error handling in bdrv_open2 (Jan Kiszka) |  | ||||||
|   - Avoid leaking data in mux'ed character devices (Jan Kiszka) |  | ||||||
|   - Fix initial character device reset (no banner in monitor) (Jan Kiszka) |  | ||||||
|   - Fix cpuid KVM crash on i386 host (Lubomir Rintel) |  | ||||||
|   - Fix SLES10sp2 installation by adding ISTAT1 register to LSI SCSI emulation |  | ||||||
|     (Ryan Harper) |  | ||||||
|  |  | ||||||
| version 0.10.0: |  | ||||||
|  |  | ||||||
|   - TCG support (No longer requires GCC 3.x) |  | ||||||
|   - Kernel Virtual Machine acceleration support |  | ||||||
|   - BSD userspace emulation |  | ||||||
|   - Bluetooth emulation and host passthrough support |  | ||||||
|   - GDB XML register description support |  | ||||||
|   - Intel e1000 emulation |  | ||||||
|   - HPET emulation |  | ||||||
|   - VirtIO paravirtual device support |  | ||||||
|   - Marvell 88w8618 / MusicPal emulation |  | ||||||
|   - Nokia N-series tablet emulation / OMAP2 processor emulation |  | ||||||
|   - PCI hotplug support |  | ||||||
|   - Live migration and new save/restore formats |  | ||||||
|   - Curses display support |  | ||||||
|   - qemu-nbd utility to mount supported block formats |  | ||||||
|   - Altivec support in PPC emulation and new firmware (OpenBIOS) |  | ||||||
|   - Multiple VNC clients are now supported |  | ||||||
|   - TLS encryption is now supported in VNC |  | ||||||
|   - MIPS Magnum R4000 machine (Hervé Poussineau) |  | ||||||
|   - Braille support (Samuel Thibault) |  | ||||||
|   - Freecom MusicPal system emulation (Jan Kiszka) |  | ||||||
|   - OMAP242x and Nokia N800, N810 machines (Andrzej Zaborowski) |  | ||||||
|   - EsounD audio driver (Frederick Reeve) |  | ||||||
|   - Gravis Ultrasound GF1 sound card (Tibor "TS" Schütz) |  | ||||||
|   - Many, many, bug fixes and new features |  | ||||||
|  |  | ||||||
| version 0.9.1: | version 0.9.1: | ||||||
|  |  | ||||||
|   - TFTP booting from host directory (Anthony Liguori, Erwan Velu) |   - TFTP booting from host directory (Anthony Liguori, Erwan Velu) | ||||||
| @@ -386,7 +245,7 @@ version 0.5.3: | |||||||
|   - support of CD-ROM change |   - support of CD-ROM change | ||||||
|   - multiple network interface support |   - multiple network interface support | ||||||
|   - initial x86-64 host support (Gwenole Beauchesne) |   - initial x86-64 host support (Gwenole Beauchesne) | ||||||
|   - lret to outer privilege fix (OS/2 install fix) |   - lret to outer priviledge fix (OS/2 install fix) | ||||||
|   - task switch fixes (SkyOS boot) |   - task switch fixes (SkyOS boot) | ||||||
|   - VM save/restore commands |   - VM save/restore commands | ||||||
|   - new timer API |   - new timer API | ||||||
| @@ -447,7 +306,7 @@ version 0.5.0: | |||||||
|   - multi-target build |   - multi-target build | ||||||
|   - fixed: no error code in hardware interrupts |   - fixed: no error code in hardware interrupts | ||||||
|   - fixed: pop ss, mov ss, x and sti disable hardware irqs for the next insn |   - fixed: pop ss, mov ss, x and sti disable hardware irqs for the next insn | ||||||
|   - correct single stepping through string operations |   - correct single stepping thru string operations | ||||||
|   - preliminary SPARC target support (Thomas M. Ogrisegg) |   - preliminary SPARC target support (Thomas M. Ogrisegg) | ||||||
|   - tun-fd option (Rusty Russell) |   - tun-fd option (Rusty Russell) | ||||||
|   - automatic IDE geometry detection |   - automatic IDE geometry detection | ||||||
| @@ -531,7 +390,7 @@ version 0.1.5: | |||||||
|  |  | ||||||
|  - ppc64 support + personality() patch (Rusty Russell) |  - ppc64 support + personality() patch (Rusty Russell) | ||||||
|  - first Alpha CPU patches (Falk Hueffner) |  - first Alpha CPU patches (Falk Hueffner) | ||||||
|  - removed bfd.h dependency |  - removed bfd.h dependancy | ||||||
|  - fixed shrd, shld, idivl and divl on PowerPC. |  - fixed shrd, shld, idivl and divl on PowerPC. | ||||||
|  - fixed buggy glibc PowerPC rint() function (test-i386 passes now on PowerPC). |  - fixed buggy glibc PowerPC rint() function (test-i386 passes now on PowerPC). | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										236
									
								
								HACKING
									
									
									
									
									
								
							
							
						
						
									
										236
									
								
								HACKING
									
									
									
									
									
								
							| @@ -1,236 +0,0 @@ | |||||||
| 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(). |  | ||||||
							
								
								
									
										22
									
								
								LICENSE
									
									
									
									
									
								
							
							
						
						
									
										22
									
								
								LICENSE
									
									
									
									
									
								
							| @@ -1,21 +1,15 @@ | |||||||
| The following points clarify the QEMU license: | The following points clarify the QEMU license: | ||||||
|  |  | ||||||
| 1) QEMU as a whole is released under the GNU General Public License, | 1) QEMU as a whole is released under the GNU General Public License | ||||||
| version 2. |  | ||||||
|  |  | ||||||
| 2) Parts of QEMU have specific licenses which are compatible with the | 2) Parts of QEMU have specific licenses which are compatible with the | ||||||
| GNU General Public License, version 2. Hence each source file contains | GNU General Public License. Hence each source file contains its own | ||||||
| its own licensing information.  Source files with no licensing information | 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 | In particular, the QEMU virtual CPU core library (libqemu.a) is | ||||||
| License (and no later version) are only accepted for the following files | released under the GNU Lesser General Public License. Many hardware | ||||||
| or directories: bsd-user/, linux-user/, hw/vfio/, hw/xen/xen_pt*. | device emulation sources are released under the BSD license. | ||||||
|  |  | ||||||
| 3) The Tiny Code Generator (TCG) is released under the BSD license | 3) QEMU is a trademark of Fabrice Bellard. | ||||||
|    (see license headers in files). |  | ||||||
|  |  | ||||||
| 4) QEMU is a trademark of Fabrice Bellard. | Fabrice Bellard. | ||||||
|  |  | ||||||
| Fabrice Bellard and the QEMU team |  | ||||||
							
								
								
									
										2099
									
								
								MAINTAINERS
									
									
									
									
									
								
							
							
						
						
									
										2099
									
								
								MAINTAINERS
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										261
									
								
								Makefile.objs
									
									
									
									
									
								
							
							
						
						
									
										261
									
								
								Makefile.objs
									
									
									
									
									
								
							| @@ -1,261 +0,0 @@ | |||||||
| ####################################################################### |  | ||||||
| # Common libraries for tools and emulators |  | ||||||
| stub-obj-y = stubs/ crypto/ |  | ||||||
| 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/ |  | ||||||
|  |  | ||||||
| ####################################################################### |  | ||||||
| # block-obj-y is code used by both qemu system emulation and qemu-img |  | ||||||
|  |  | ||||||
| block-obj-y += nbd/ |  | ||||||
| block-obj-y += block.o blockjob.o |  | ||||||
| block-obj-y += block/ scsi/ |  | ||||||
| block-obj-y += qemu-io-cmds.o |  | ||||||
| block-obj-$(CONFIG_REPLICATION) += replication.o |  | ||||||
|  |  | ||||||
| block-obj-m = block/ |  | ||||||
|  |  | ||||||
| ####################################################################### |  | ||||||
| # crypto-obj-y is code used by both qemu system emulation and qemu-img |  | ||||||
|  |  | ||||||
| crypto-obj-y = crypto/ |  | ||||||
| crypto-aes-obj-y = crypto/ |  | ||||||
|  |  | ||||||
| ####################################################################### |  | ||||||
| # qom-obj-y is code used by both qemu system emulation and qemu-img |  | ||||||
|  |  | ||||||
| qom-obj-y = qom/ |  | ||||||
|  |  | ||||||
| ####################################################################### |  | ||||||
| # io-obj-y is code used by both qemu system emulation and qemu-img |  | ||||||
|  |  | ||||||
| io-obj-y = io/ |  | ||||||
|  |  | ||||||
| ###################################################################### |  | ||||||
| # Target independent part of system emulation. The long term path is to |  | ||||||
| # suppress *all* target specific code in case of system emulation, i.e. a |  | ||||||
| # single QEMU executable should support all CPUs and machines. |  | ||||||
|  |  | ||||||
| ifeq ($(CONFIG_SOFTMMU),y) |  | ||||||
| common-obj-y = blockdev.o blockdev-nbd.o block/ |  | ||||||
| common-obj-y += bootdevice.o iothread.o |  | ||||||
| common-obj-y += net/ |  | ||||||
| common-obj-y += qdev-monitor.o device-hotplug.o |  | ||||||
| common-obj-$(CONFIG_WIN32) += os-win32.o |  | ||||||
| common-obj-$(CONFIG_POSIX) += os-posix.o |  | ||||||
|  |  | ||||||
| common-obj-$(CONFIG_LINUX) += fsdev/ |  | ||||||
|  |  | ||||||
| common-obj-y += migration/ |  | ||||||
|  |  | ||||||
| common-obj-y += audio/ |  | ||||||
| common-obj-m += audio/ |  | ||||||
| common-obj-y += hw/ |  | ||||||
|  |  | ||||||
| common-obj-y += replay/ |  | ||||||
|  |  | ||||||
| common-obj-y += ui/ |  | ||||||
| common-obj-m += ui/ |  | ||||||
| common-obj-y += bt-host.o bt-vhci.o |  | ||||||
| bt-host.o-cflags := $(BLUEZ_CFLAGS) |  | ||||||
|  |  | ||||||
| common-obj-y += dma-helpers.o |  | ||||||
| common-obj-y += vl.o |  | ||||||
| vl.o-cflags := $(GPROF_CFLAGS) $(SDL_CFLAGS) |  | ||||||
| common-obj-$(CONFIG_TPM) += tpm.o |  | ||||||
|  |  | ||||||
| common-obj-$(CONFIG_SLIRP) += slirp/ |  | ||||||
|  |  | ||||||
| common-obj-y += backends/ |  | ||||||
| common-obj-y += chardev/ |  | ||||||
|  |  | ||||||
| common-obj-$(CONFIG_SECCOMP) += qemu-seccomp.o |  | ||||||
| qemu-seccomp.o-cflags := $(SECCOMP_CFLAGS) |  | ||||||
| qemu-seccomp.o-libs := $(SECCOMP_LIBS) |  | ||||||
|  |  | ||||||
| common-obj-$(CONFIG_FDT) += device_tree.o |  | ||||||
|  |  | ||||||
| ###################################################################### |  | ||||||
| # 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 |  | ||||||
|  |  | ||||||
| ####################################################################### |  | ||||||
| # Target-independent parts used in system and user emulation |  | ||||||
| common-obj-y += cpus-common.o |  | ||||||
| common-obj-y += hw/ |  | ||||||
| common-obj-y += qom/ |  | ||||||
| common-obj-y += disas/ |  | ||||||
|  |  | ||||||
| ###################################################################### |  | ||||||
| # Resource file for Windows executables |  | ||||||
| version-obj-$(CONFIG_WIN32) += $(BUILD_DIR)/version.o |  | ||||||
|  |  | ||||||
| ###################################################################### |  | ||||||
| # tracing |  | ||||||
| util-obj-y +=  trace/ |  | ||||||
| target-obj-y += trace/ |  | ||||||
|  |  | ||||||
| ###################################################################### |  | ||||||
| # guest agent |  | ||||||
|  |  | ||||||
| # FIXME: a few definitions from qapi/qapi-types.o and |  | ||||||
| # qapi/qapi-visit.o are needed by libqemuutil.a.  These should be |  | ||||||
| # extracted into a QAPI schema module, or perhaps a separate schema. |  | ||||||
| qga-obj-y = qga/ |  | ||||||
| qga-vss-dll-obj-y = qga/ |  | ||||||
|  |  | ||||||
| ###################################################################### |  | ||||||
| # contrib |  | ||||||
| ivshmem-client-obj-$(CONFIG_IVSHMEM) = contrib/ivshmem-client/ |  | ||||||
| ivshmem-server-obj-$(CONFIG_IVSHMEM) = contrib/ivshmem-server/ |  | ||||||
| libvhost-user-obj-y = contrib/libvhost-user/ |  | ||||||
| vhost-user-scsi.o-cflags := $(LIBISCSI_CFLAGS) |  | ||||||
| vhost-user-scsi.o-libs := $(LIBISCSI_LIBS) |  | ||||||
| vhost-user-scsi-obj-y = contrib/vhost-user-scsi/ |  | ||||||
| vhost-user-blk-obj-y = contrib/vhost-user-blk/ |  | ||||||
|  |  | ||||||
| ###################################################################### |  | ||||||
| trace-events-subdirs = |  | ||||||
| trace-events-subdirs += util |  | ||||||
| trace-events-subdirs += crypto |  | ||||||
| trace-events-subdirs += io |  | ||||||
| trace-events-subdirs += migration |  | ||||||
| trace-events-subdirs += block |  | ||||||
| trace-events-subdirs += chardev |  | ||||||
| trace-events-subdirs += hw/block |  | ||||||
| trace-events-subdirs += hw/block/dataplane |  | ||||||
| trace-events-subdirs += hw/char |  | ||||||
| trace-events-subdirs += hw/intc |  | ||||||
| trace-events-subdirs += hw/net |  | ||||||
| trace-events-subdirs += hw/rdma |  | ||||||
| trace-events-subdirs += hw/rdma/vmw |  | ||||||
| trace-events-subdirs += hw/virtio |  | ||||||
| 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/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/sparc64 |  | ||||||
| trace-events-subdirs += hw/sd |  | ||||||
| trace-events-subdirs += hw/isa |  | ||||||
| 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/acpi |  | ||||||
| trace-events-subdirs += hw/arm |  | ||||||
| trace-events-subdirs += hw/alpha |  | ||||||
| trace-events-subdirs += hw/hppa |  | ||||||
| trace-events-subdirs += hw/xen |  | ||||||
| trace-events-subdirs += hw/ide |  | ||||||
| trace-events-subdirs += hw/tpm |  | ||||||
| trace-events-subdirs += ui |  | ||||||
| trace-events-subdirs += audio |  | ||||||
| trace-events-subdirs += net |  | ||||||
| trace-events-subdirs += target/arm |  | ||||||
| trace-events-subdirs += target/i386 |  | ||||||
| trace-events-subdirs += target/mips |  | ||||||
| trace-events-subdirs += target/sparc |  | ||||||
| trace-events-subdirs += target/s390x |  | ||||||
| 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-obj-y = trace-root.o |  | ||||||
| trace-obj-y += $(trace-events-subdirs:%=%/trace.o) |  | ||||||
| trace-obj-$(CONFIG_TRACE_UST) += trace-ust-all.o |  | ||||||
| trace-obj-$(CONFIG_TRACE_DTRACE) += trace-dtrace-root.o |  | ||||||
| trace-obj-$(CONFIG_TRACE_DTRACE) += $(trace-events-subdirs:%=%/trace-dtrace.o) |  | ||||||
							
								
								
									
										796
									
								
								Makefile.target
									
									
									
									
									
								
							
							
						
						
									
										796
									
								
								Makefile.target
									
									
									
									
									
								
							| @@ -1,227 +1,649 @@ | |||||||
| # -*- Mode: makefile -*- | include config.mak | ||||||
|  |  | ||||||
| BUILD_DIR?=$(CURDIR)/.. | TARGET_BASE_ARCH:=$(TARGET_ARCH) | ||||||
|  | ifeq ($(TARGET_ARCH), x86_64) | ||||||
| include ../config-host.mak | TARGET_BASE_ARCH:=i386 | ||||||
| include config-target.mak |  | ||||||
| include config-devices.mak |  | ||||||
| include $(SRC_PATH)/rules.mak |  | ||||||
|  |  | ||||||
| $(call set-vpath, $(SRC_PATH):$(BUILD_DIR)) |  | ||||||
| ifdef CONFIG_LINUX |  | ||||||
| QEMU_CFLAGS += -I../linux-headers |  | ||||||
| endif | endif | ||||||
| QEMU_CFLAGS += -I.. -I$(SRC_PATH)/target/$(TARGET_BASE_ARCH) -DNEED_CPU_H | ifeq ($(TARGET_ARCH), mipsn32) | ||||||
|  | TARGET_BASE_ARCH:=mips | ||||||
| QEMU_CFLAGS+=-I$(SRC_PATH)/include | endif | ||||||
|  | ifeq ($(TARGET_ARCH), mips64) | ||||||
| ifdef CONFIG_USER_ONLY | TARGET_BASE_ARCH:=mips | ||||||
|  | endif | ||||||
|  | ifeq ($(TARGET_ARCH), ppc64) | ||||||
|  | TARGET_BASE_ARCH:=ppc | ||||||
|  | endif | ||||||
|  | ifeq ($(TARGET_ARCH), ppc64h) | ||||||
|  | TARGET_BASE_ARCH:=ppc | ||||||
|  | endif | ||||||
|  | ifeq ($(TARGET_ARCH), ppcemb) | ||||||
|  | TARGET_BASE_ARCH:=ppc | ||||||
|  | endif | ||||||
|  | ifeq ($(TARGET_ARCH), sparc64) | ||||||
|  | TARGET_BASE_ARCH:=sparc | ||||||
|  | endif | ||||||
|  | TARGET_PATH=$(SRC_PATH)/target-$(TARGET_BASE_ARCH) | ||||||
|  | VPATH=$(SRC_PATH):$(TARGET_PATH):$(SRC_PATH)/hw | ||||||
|  | CPPFLAGS=-I. -I.. -I$(TARGET_PATH) -I$(SRC_PATH) -MMD -MP -DNEED_CPU_H | ||||||
|  | ifdef CONFIG_DARWIN_USER | ||||||
|  | VPATH+=:$(SRC_PATH)/darwin-user | ||||||
|  | CPPFLAGS+=-I$(SRC_PATH)/darwin-user -I$(SRC_PATH)/darwin-user/$(TARGET_ARCH) | ||||||
|  | endif | ||||||
|  | ifdef CONFIG_LINUX_USER | ||||||
|  | VPATH+=:$(SRC_PATH)/linux-user | ||||||
|  | ifndef TARGET_ABI_DIR | ||||||
|  |   TARGET_ABI_DIR=$(TARGET_ARCH) | ||||||
|  | endif | ||||||
|  | CPPFLAGS+=-I$(SRC_PATH)/linux-user -I$(SRC_PATH)/linux-user/$(TARGET_ABI_DIR) | ||||||
|  | endif | ||||||
|  | BASE_CFLAGS= | ||||||
|  | BASE_LDFLAGS= | ||||||
|  | #CFLAGS+=-Werror | ||||||
|  | LIBS= | ||||||
|  | HELPER_CFLAGS=$(CFLAGS) | ||||||
|  | DYNGEN=../dyngen$(EXESUF) | ||||||
| # user emulator name | # user emulator name | ||||||
| QEMU_PROG=qemu-$(TARGET_NAME) | ifndef TARGET_ARCH2 | ||||||
| QEMU_PROG_BUILD = $(QEMU_PROG) | TARGET_ARCH2=$(TARGET_ARCH) | ||||||
| else | endif | ||||||
|  | ifeq ($(TARGET_ARCH),arm) | ||||||
|  |   ifeq ($(TARGET_WORDS_BIGENDIAN),yes) | ||||||
|  |     TARGET_ARCH2=armeb | ||||||
|  |   endif | ||||||
|  | endif | ||||||
|  | ifeq ($(TARGET_ARCH),sh4) | ||||||
|  |   ifeq ($(TARGET_WORDS_BIGENDIAN),yes) | ||||||
|  |     TARGET_ARCH2=sh4eb | ||||||
|  |   endif | ||||||
|  | endif | ||||||
|  | ifeq ($(TARGET_ARCH),mips) | ||||||
|  |   ifneq ($(TARGET_WORDS_BIGENDIAN),yes) | ||||||
|  |     TARGET_ARCH2=mipsel | ||||||
|  |   endif | ||||||
|  | endif | ||||||
|  | ifeq ($(TARGET_ARCH),mipsn32) | ||||||
|  |   ifneq ($(TARGET_WORDS_BIGENDIAN),yes) | ||||||
|  |     TARGET_ARCH2=mipsn32el | ||||||
|  |   endif | ||||||
|  | endif | ||||||
|  | ifeq ($(TARGET_ARCH),mips64) | ||||||
|  |   ifneq ($(TARGET_WORDS_BIGENDIAN),yes) | ||||||
|  |     TARGET_ARCH2=mips64el | ||||||
|  |   endif | ||||||
|  | endif | ||||||
|  | QEMU_USER=qemu-$(TARGET_ARCH2) | ||||||
| # system emulator name | # system emulator name | ||||||
| QEMU_PROG=qemu-system-$(TARGET_NAME)$(EXESUF) | ifdef CONFIG_SOFTMMU | ||||||
| ifneq (,$(findstring -mwindows,$(SDL_LIBS))) | ifeq ($(TARGET_ARCH), i386) | ||||||
| # Terminate program name with a 'w' because the linker builds a windows executable. | QEMU_SYSTEM=qemu$(EXESUF) | ||||||
| QEMU_PROGW=qemu-system-$(TARGET_NAME)w$(EXESUF) |  | ||||||
| $(QEMU_PROG): $(QEMU_PROGW) |  | ||||||
| 	$(call quiet-command,$(OBJCOPY) --subsystem console $(QEMU_PROGW) $(QEMU_PROG),"GEN","$(TARGET_DIR)$(QEMU_PROG)") |  | ||||||
| QEMU_PROG_BUILD = $(QEMU_PROGW) |  | ||||||
| else | else | ||||||
| QEMU_PROG_BUILD = $(QEMU_PROG) | QEMU_SYSTEM=qemu-system-$(TARGET_ARCH2)$(EXESUF) | ||||||
| endif | endif | ||||||
|  | else | ||||||
|  | QEMU_SYSTEM=qemu-fast | ||||||
| endif | endif | ||||||
|  |  | ||||||
| PROGS=$(QEMU_PROG) $(QEMU_PROGW) |  | ||||||
| STPFILES= |  | ||||||
|  |  | ||||||
| config-target.h: config-target.h-timestamp |  | ||||||
| config-target.h-timestamp: config-target.mak |  | ||||||
|  |  | ||||||
| ifdef CONFIG_TRACE_SYSTEMTAP |  | ||||||
| stap: $(QEMU_PROG).stp-installed $(QEMU_PROG).stp $(QEMU_PROG)-simpletrace.stp |  | ||||||
|  |  | ||||||
| ifdef CONFIG_USER_ONLY | ifdef CONFIG_USER_ONLY | ||||||
| TARGET_TYPE=user | PROGS=$(QEMU_USER) | ||||||
| else | else | ||||||
| TARGET_TYPE=system | PROGS+=$(QEMU_SYSTEM) | ||||||
|  | ifndef CONFIG_SOFTMMU | ||||||
|  | CONFIG_STATIC=y | ||||||
|  | endif | ||||||
|  | endif # !CONFIG_USER_ONLY | ||||||
|  |  | ||||||
|  | ifdef CONFIG_STATIC | ||||||
|  | BASE_LDFLAGS+=-static | ||||||
| endif | endif | ||||||
|  |  | ||||||
| tracetool-y = $(SRC_PATH)/scripts/tracetool.py | # We require -O2 to avoid the stack setup prologue in EXIT_TB | ||||||
| tracetool-y += $(shell find $(SRC_PATH)/scripts/tracetool -name "*.py") | OP_CFLAGS := -Wall -O2 -g -fno-strict-aliasing | ||||||
|  |  | ||||||
| $(QEMU_PROG).stp-installed: $(BUILD_DIR)/trace-events-all $(tracetool-y) | # cc-option | ||||||
| 	$(call quiet-command,$(TRACETOOL) \ | # Usage: OP_CFLAGS+=$(call cc-option, -falign-functions=0, -malign-functions=0) | ||||||
| 		--group=all \ |  | ||||||
| 		--format=stap \ |  | ||||||
| 		--backends=$(TRACE_BACKENDS) \ |  | ||||||
| 		--binary=$(bindir)/$(QEMU_PROG) \ |  | ||||||
| 		--target-name=$(TARGET_NAME) \ |  | ||||||
| 		--target-type=$(TARGET_TYPE) \ |  | ||||||
| 		$< > $@,"GEN","$(TARGET_DIR)$(QEMU_PROG).stp-installed") |  | ||||||
|  |  | ||||||
| $(QEMU_PROG).stp: $(BUILD_DIR)/trace-events-all $(tracetool-y) | cc-option = $(shell if $(CC) $(OP_CFLAGS) $(1) -S -o /dev/null -xc /dev/null \ | ||||||
| 	$(call quiet-command,$(TRACETOOL) \ |               > /dev/null 2>&1; then echo "$(1)"; else echo "$(2)"; fi ;) | ||||||
| 		--group=all \ |  | ||||||
| 		--format=stap \ |  | ||||||
| 		--backends=$(TRACE_BACKENDS) \ |  | ||||||
| 		--binary=$(realpath .)/$(QEMU_PROG) \ |  | ||||||
| 		--target-name=$(TARGET_NAME) \ |  | ||||||
| 		--target-type=$(TARGET_TYPE) \ |  | ||||||
| 		$< > $@,"GEN","$(TARGET_DIR)$(QEMU_PROG).stp") |  | ||||||
|  |  | ||||||
| $(QEMU_PROG)-simpletrace.stp: $(BUILD_DIR)/trace-events-all $(tracetool-y) | OP_CFLAGS+=$(call cc-option, -fno-reorder-blocks, "") | ||||||
| 	$(call quiet-command,$(TRACETOOL) \ | OP_CFLAGS+=$(call cc-option, -fno-gcse, "") | ||||||
| 		--group=all \ | OP_CFLAGS+=$(call cc-option, -fno-tree-ch, "") | ||||||
| 		--format=simpletrace-stap \ | OP_CFLAGS+=$(call cc-option, -fno-optimize-sibling-calls, "") | ||||||
| 		--backends=$(TRACE_BACKENDS) \ | OP_CFLAGS+=$(call cc-option, -fno-crossjumping, "") | ||||||
| 		--probe-prefix=qemu.$(TARGET_TYPE).$(TARGET_NAME) \ | OP_CFLAGS+=$(call cc-option, -fno-align-labels, "") | ||||||
| 		$< > $@,"GEN","$(TARGET_DIR)$(QEMU_PROG)-simpletrace.stp") | OP_CFLAGS+=$(call cc-option, -fno-align-jumps, "") | ||||||
|  | OP_CFLAGS+=$(call cc-option, -fno-align-functions, $(call cc-option, -malign-functions=0, "")) | ||||||
|  | OP_CFLAGS+=$(call cc-option, -fno-section-anchors, "") | ||||||
|  |  | ||||||
| else | ifeq ($(ARCH),i386) | ||||||
| stap: | HELPER_CFLAGS+=-fomit-frame-pointer | ||||||
|  | OP_CFLAGS+=-mpreferred-stack-boundary=2 -fomit-frame-pointer | ||||||
|  | ifdef TARGET_GPROF | ||||||
|  | USE_I386_LD=y | ||||||
|  | endif | ||||||
|  | ifdef CONFIG_STATIC | ||||||
|  | USE_I386_LD=y | ||||||
|  | endif | ||||||
|  | ifdef USE_I386_LD | ||||||
|  | BASE_LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld | ||||||
|  | else | ||||||
|  | ifdef CONFIG_LINUX_USER | ||||||
|  | # WARNING: this LDFLAGS is _very_ tricky : qemu is an ELF shared object | ||||||
|  | # that the kernel ELF loader considers as an executable. I think this | ||||||
|  | # is the simplest way to make it self virtualizable! | ||||||
|  | BASE_LDFLAGS+=-Wl,-shared | ||||||
|  | endif | ||||||
|  | endif | ||||||
| endif | endif | ||||||
| .PHONY: stap |  | ||||||
|  |  | ||||||
| all: $(PROGS) stap | ifeq ($(ARCH),x86_64) | ||||||
|  |   ifneq ($(CONFIG_SOLARIS),yes) | ||||||
|  |     BASE_LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld | ||||||
|  |   endif | ||||||
|  | endif | ||||||
|  |  | ||||||
| # Dummy command so that make thinks it has done something | ifeq ($(ARCH),ppc) | ||||||
| 	@true | CPPFLAGS+= -D__powerpc__ | ||||||
|  | BASE_LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld | ||||||
|  | endif | ||||||
|  |  | ||||||
|  | ifeq ($(ARCH),s390) | ||||||
|  | BASE_LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld | ||||||
|  | endif | ||||||
|  |  | ||||||
|  | ifeq ($(ARCH),sparc) | ||||||
|  |   BASE_CFLAGS+=-ffixed-g2 -ffixed-g3 | ||||||
|  |   OP_CFLAGS+=-fno-delayed-branch -ffixed-i0 | ||||||
|  |   ifeq ($(CONFIG_SOLARIS),yes) | ||||||
|  |     OP_CFLAGS+=-fno-omit-frame-pointer | ||||||
|  |   else | ||||||
|  |     BASE_CFLAGS+=-ffixed-g1 -ffixed-g6 | ||||||
|  |     HELPER_CFLAGS=$(CFLAGS) -ffixed-i0 | ||||||
|  |     # -static is used to avoid g1/g3 usage by the dynamic linker | ||||||
|  |     BASE_LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld -static | ||||||
|  |   endif | ||||||
|  | endif | ||||||
|  |  | ||||||
|  | ifeq ($(ARCH),sparc64) | ||||||
|  |   BASE_CFLAGS+=-ffixed-g1 -ffixed-g4 -ffixed-g5 -ffixed-g7 | ||||||
|  |   OP_CFLAGS+=-mcpu=ultrasparc -m64 -fno-delayed-branch -ffixed-i0 | ||||||
|  |   ifneq ($(CONFIG_SOLARIS),yes) | ||||||
|  |     BASE_LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld | ||||||
|  |     OP_CFLAGS+=-ffixed-g1 -ffixed-g4 -ffixed-g5 -ffixed-g7 | ||||||
|  |   endif | ||||||
|  | endif | ||||||
|  |  | ||||||
|  | ifeq ($(ARCH),alpha) | ||||||
|  | # -msmall-data is not used for OP_CFLAGS because we want two-instruction | ||||||
|  | # relocations for the constant constructions | ||||||
|  | # Ensure there's only a single GP | ||||||
|  | BASE_CFLAGS+=-msmall-data | ||||||
|  | BASE_LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld | ||||||
|  | endif | ||||||
|  |  | ||||||
|  | ifeq ($(ARCH),ia64) | ||||||
|  | BASE_CFLAGS+=-mno-sdata | ||||||
|  | OP_CFLAGS+=-mno-sdata | ||||||
|  | BASE_LDFLAGS+=-Wl,-G0 -Wl,-T,$(SRC_PATH)/$(ARCH).ld | ||||||
|  | endif | ||||||
|  |  | ||||||
|  | ifeq ($(ARCH),arm) | ||||||
|  | OP_CFLAGS+=-mno-sched-prolog -fno-omit-frame-pointer | ||||||
|  | BASE_LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld | ||||||
|  | endif | ||||||
|  |  | ||||||
|  | ifeq ($(ARCH),m68k) | ||||||
|  | OP_CFLAGS+=-fomit-frame-pointer | ||||||
|  | BASE_LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld | ||||||
|  | endif | ||||||
|  |  | ||||||
|  | ifeq ($(ARCH),mips) | ||||||
|  | OP_CFLAGS+=-mabi=32 -G0 -fno-PIC -mno-abicalls -fomit-frame-pointer -fno-delayed-branch -Wa,-O0 | ||||||
|  | ifeq ($(WORDS_BIGENDIAN),yes) | ||||||
|  | BASE_LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld | ||||||
|  | else | ||||||
|  | BASE_LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH)el.ld | ||||||
|  | endif | ||||||
|  | endif | ||||||
|  |  | ||||||
|  | ifeq ($(ARCH),mips64) | ||||||
|  | OP_CFLAGS+=-mabi=n32 -G0 -fno-PIC -mno-abicalls -fomit-frame-pointer -fno-delayed-branch -Wa,-O0 | ||||||
|  | ifeq ($(WORDS_BIGENDIAN),yes) | ||||||
|  | BASE_LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld | ||||||
|  | else | ||||||
|  | BASE_LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH)el.ld | ||||||
|  | endif | ||||||
|  | endif | ||||||
|  |  | ||||||
|  | ifeq ($(CONFIG_DARWIN),yes) | ||||||
|  | LIBS+=-lmx | ||||||
|  | endif | ||||||
|  |  | ||||||
|  | ifdef CONFIG_DARWIN_USER | ||||||
|  | # Leave some space for the regular program loading zone | ||||||
|  | BASE_LDFLAGS+=-Wl,-segaddr,__STD_PROG_ZONE,0x1000 -image_base 0x0e000000 | ||||||
|  | endif | ||||||
|  |  | ||||||
|  | BASE_CFLAGS+=$(OS_CFLAGS) $(ARCH_CFLAGS) | ||||||
|  | BASE_LDFLAGS+=$(OS_LDFLAGS) $(ARCH_LDFLAGS) | ||||||
|  | OP_CFLAGS+=$(OS_CFLAGS) $(ARCH_CFLAGS) | ||||||
|  | OP_LDFLAGS+=$(OS_LDFLAGS) $(ARCH_LDFLAGS) | ||||||
|  |  | ||||||
| ######################################################### | ######################################################### | ||||||
| # cpu emulator library |  | ||||||
| obj-y += exec.o |  | ||||||
| obj-y += accel/ |  | ||||||
| obj-$(CONFIG_TCG) += tcg/tcg.o tcg/tcg-op.o tcg/tcg-op-vec.o tcg/tcg-op-gvec.o |  | ||||||
| obj-$(CONFIG_TCG) += tcg/tcg-common.o tcg/optimize.o |  | ||||||
| obj-$(CONFIG_TCG_INTERPRETER) += tcg/tci.o |  | ||||||
| obj-$(CONFIG_TCG_INTERPRETER) += disas/tci.o |  | ||||||
| obj-y += fpu/softfloat.o |  | ||||||
| obj-y += target/$(TARGET_BASE_ARCH)/ |  | ||||||
| obj-y += disas.o |  | ||||||
| obj-$(call notempty,$(TARGET_XML_FILES)) += gdbstub-xml.o |  | ||||||
|  |  | ||||||
| ######################################################### | CPPFLAGS+=-D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE | ||||||
| # Linux user emulator target | LIBS+=-lm | ||||||
|  | ifndef CONFIG_USER_ONLY | ||||||
|  | LIBS+=-lz | ||||||
|  | endif | ||||||
|  | ifdef CONFIG_WIN32 | ||||||
|  | LIBS+=-lwinmm -lws2_32 -liphlpapi | ||||||
|  | endif | ||||||
|  | ifdef CONFIG_SOLARIS | ||||||
|  | LIBS+=-lsocket -lnsl -lresolv | ||||||
|  | ifdef NEEDS_LIBSUNMATH | ||||||
|  | LIBS+=-lsunmath | ||||||
|  | LDFLAGS+=-L/opt/SUNWspro/prod/lib -R/opt/SUNWspro/prod/lib | ||||||
|  | OP_CFLAGS+=-I/opt/SUNWspro/prod/include/cc | ||||||
|  | BASE_CFLAGS+=-I/opt/SUNWspro/prod/include/cc | ||||||
|  | endif | ||||||
|  | endif | ||||||
|  |  | ||||||
|  | # profiling code | ||||||
|  | ifdef TARGET_GPROF | ||||||
|  | BASE_LDFLAGS+=-p | ||||||
|  | main.o: BASE_CFLAGS+=-p | ||||||
|  | endif | ||||||
|  |  | ||||||
| ifdef CONFIG_LINUX_USER | ifdef CONFIG_LINUX_USER | ||||||
|  | OBJS= main.o syscall.o strace.o mmap.o signal.o path.o osdep.o thunk.o \ | ||||||
|  |       elfload.o linuxload.o uaccess.o | ||||||
|  | LIBS+= $(AIOLIBS) | ||||||
|  | ifdef TARGET_HAS_BFLT | ||||||
|  | OBJS+= flatload.o | ||||||
|  | endif | ||||||
|  | ifdef TARGET_HAS_ELFLOAD32 | ||||||
|  | OBJS+= elfload32.o | ||||||
|  | elfload32.o: elfload.c | ||||||
|  | endif | ||||||
|  |  | ||||||
| QEMU_CFLAGS+=-I$(SRC_PATH)/linux-user/$(TARGET_ABI_DIR) \ | ifeq ($(TARGET_ARCH), i386) | ||||||
|              -I$(SRC_PATH)/linux-user/host/$(ARCH) \ | OBJS+= vm86.o | ||||||
|              -I$(SRC_PATH)/linux-user | endif | ||||||
|  | ifeq ($(TARGET_ARCH), arm) | ||||||
| obj-y += linux-user/ | OBJS+=nwfpe/fpa11.o nwfpe/fpa11_cpdo.o \ | ||||||
| obj-y += gdbstub.o thunk.o | nwfpe/fpa11_cpdt.o nwfpe/fpa11_cprt.o nwfpe/fpopcode.o nwfpe/single_cpdo.o \ | ||||||
|  |  nwfpe/double_cpdo.o nwfpe/extended_cpdo.o arm-semi.o | ||||||
|  | endif | ||||||
|  | ifeq ($(TARGET_ARCH), m68k) | ||||||
|  | OBJS+= m68k-sim.o m68k-semi.o | ||||||
|  | endif | ||||||
| endif #CONFIG_LINUX_USER | endif #CONFIG_LINUX_USER | ||||||
|  |  | ||||||
| ######################################################### | ifdef CONFIG_DARWIN_USER | ||||||
| # BSD user emulator target | OBJS= main.o commpage.o machload.o mmap.o osdep.o signal.o syscall.o thunk.o | ||||||
|  | endif | ||||||
|  |  | ||||||
| ifdef CONFIG_BSD_USER | SRCS:= $(OBJS:.o=.c) | ||||||
|  | OBJS+= libqemu.a | ||||||
|  |  | ||||||
| QEMU_CFLAGS+=-I$(SRC_PATH)/bsd-user -I$(SRC_PATH)/bsd-user/$(TARGET_ABI_DIR) \ | # cpu emulator library | ||||||
| 			 -I$(SRC_PATH)/bsd-user/$(HOST_VARIANT_DIR) | LIBOBJS=exec.o kqemu.o translate-op.o translate-all.o cpu-exec.o\ | ||||||
|  |         translate.o op.o host-utils.o | ||||||
| obj-y += bsd-user/ | ifdef CONFIG_SOFTFLOAT | ||||||
| obj-y += gdbstub.o | LIBOBJS+=fpu/softfloat.o | ||||||
|  |  | ||||||
| endif #CONFIG_BSD_USER |  | ||||||
|  |  | ||||||
| ######################################################### |  | ||||||
| # System emulator target |  | ||||||
| ifdef CONFIG_SOFTMMU |  | ||||||
| obj-y += arch_init.o cpus.o monitor.o gdbstub.o balloon.o ioport.o numa.o |  | ||||||
| obj-y += qtest.o |  | ||||||
| obj-y += hw/ |  | ||||||
| obj-y += memory.o |  | ||||||
| obj-y += memory_mapping.o |  | ||||||
| obj-y += dump.o |  | ||||||
| obj-y += migration/ram.o |  | ||||||
| LIBS := $(libs_softmmu) $(LIBS) |  | ||||||
|  |  | ||||||
| # Hardware support |  | ||||||
| ifeq ($(TARGET_NAME), sparc64) |  | ||||||
| obj-y += hw/sparc64/ |  | ||||||
| else | else | ||||||
| obj-y += hw/$(TARGET_BASE_ARCH)/ | LIBOBJS+=fpu/softfloat-native.o | ||||||
|  | endif | ||||||
|  | CPPFLAGS+=-I$(SRC_PATH)/fpu | ||||||
|  |  | ||||||
|  | ifeq ($(TARGET_ARCH), i386) | ||||||
|  | LIBOBJS+=helper.o helper2.o | ||||||
| endif | endif | ||||||
|  |  | ||||||
| GENERATED_FILES += hmp-commands.h hmp-commands-info.h | ifeq ($(TARGET_ARCH), x86_64) | ||||||
|  | LIBOBJS+=helper.o helper2.o | ||||||
| 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) |  | ||||||
| all-obj-y := $(obj-y) |  | ||||||
|  |  | ||||||
| target-obj-y := |  | ||||||
| block-obj-y := |  | ||||||
| common-obj-y := |  | ||||||
| chardev-obj-y := |  | ||||||
| include $(SRC_PATH)/Makefile.objs |  | ||||||
| dummy := $(call unnest-vars,,target-obj-y) |  | ||||||
| target-obj-y-save := $(target-obj-y) |  | ||||||
| dummy := $(call unnest-vars,.., \ |  | ||||||
|                block-obj-y \ |  | ||||||
|                block-obj-m \ |  | ||||||
|                chardev-obj-y \ |  | ||||||
|                crypto-obj-y \ |  | ||||||
|                crypto-aes-obj-y \ |  | ||||||
|                qom-obj-y \ |  | ||||||
|                io-obj-y \ |  | ||||||
|                common-obj-y \ |  | ||||||
|                common-obj-m) |  | ||||||
| target-obj-y := $(target-obj-y-save) |  | ||||||
| all-obj-y += $(common-obj-y) |  | ||||||
| all-obj-y += $(target-obj-y) |  | ||||||
| all-obj-y += $(qom-obj-y) |  | ||||||
| all-obj-$(CONFIG_SOFTMMU) += $(block-obj-y) $(chardev-obj-y) |  | ||||||
| all-obj-$(CONFIG_USER_ONLY) += $(crypto-aes-obj-y) |  | ||||||
| all-obj-$(CONFIG_SOFTMMU) += $(crypto-obj-y) |  | ||||||
| all-obj-$(CONFIG_SOFTMMU) += $(io-obj-y) |  | ||||||
|  |  | ||||||
| $(QEMU_PROG_BUILD): config-devices.mak |  | ||||||
|  |  | ||||||
| COMMON_LDADDS = ../libqemuutil.a |  | ||||||
|  |  | ||||||
| # build either PROG or PROGW |  | ||||||
| $(QEMU_PROG_BUILD): $(all-obj-y) $(COMMON_LDADDS) |  | ||||||
| 	$(call LINK, $(filter-out %.mak, $^)) |  | ||||||
| ifdef CONFIG_DARWIN |  | ||||||
| 	$(call quiet-command,Rez -append $(SRC_PATH)/pc-bios/qemu.rsrc -o $@,"REZ","$(TARGET_DIR)$@") |  | ||||||
| 	$(call quiet-command,SetFile -a C $@,"SETFILE","$(TARGET_DIR)$@") |  | ||||||
| endif | endif | ||||||
|  |  | ||||||
| gdbstub-xml.c: $(TARGET_XML_FILES) $(SRC_PATH)/scripts/feature_to_c.sh | ifeq ($(TARGET_BASE_ARCH), ppc) | ||||||
| 	$(call quiet-command,rm -f $@ && $(SHELL) $(SRC_PATH)/scripts/feature_to_c.sh $@ $(TARGET_XML_FILES),"GEN","$(TARGET_DIR)$@") | LIBOBJS+= op_helper.o helper.o | ||||||
|  |  | ||||||
| hmp-commands.h: $(SRC_PATH)/hmp-commands.hx $(SRC_PATH)/scripts/hxtool |  | ||||||
| 	$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@,"GEN","$(TARGET_DIR)$@") |  | ||||||
|  |  | ||||||
| hmp-commands-info.h: $(SRC_PATH)/hmp-commands-info.hx $(SRC_PATH)/scripts/hxtool |  | ||||||
| 	$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@,"GEN","$(TARGET_DIR)$@") |  | ||||||
|  |  | ||||||
| clean: clean-target |  | ||||||
| 	rm -f *.a *~ $(PROGS) |  | ||||||
| 	rm -f $(shell find . -name '*.[od]') |  | ||||||
| 	rm -f hmp-commands.h gdbstub-xml.c |  | ||||||
| ifdef CONFIG_TRACE_SYSTEMTAP |  | ||||||
| 	rm -f *.stp |  | ||||||
| endif | endif | ||||||
|  |  | ||||||
|  | ifeq ($(TARGET_BASE_ARCH), mips) | ||||||
|  | LIBOBJS+= op_helper.o helper.o | ||||||
|  | endif | ||||||
|  |  | ||||||
|  | ifeq ($(TARGET_BASE_ARCH), sparc) | ||||||
|  | LIBOBJS+= op_helper.o helper.o | ||||||
|  | endif | ||||||
|  |  | ||||||
|  | ifeq ($(TARGET_BASE_ARCH), arm) | ||||||
|  | LIBOBJS+= op_helper.o helper.o | ||||||
|  | endif | ||||||
|  |  | ||||||
|  | ifeq ($(TARGET_BASE_ARCH), sh4) | ||||||
|  | LIBOBJS+= op_helper.o helper.o | ||||||
|  | endif | ||||||
|  |  | ||||||
|  | ifeq ($(TARGET_BASE_ARCH), m68k) | ||||||
|  | LIBOBJS+= op_helper.o helper.o | ||||||
|  | endif | ||||||
|  |  | ||||||
|  | ifeq ($(TARGET_BASE_ARCH), alpha) | ||||||
|  | LIBOBJS+= op_helper.o helper.o alpha_palcode.o | ||||||
|  | endif | ||||||
|  |  | ||||||
|  | ifeq ($(TARGET_BASE_ARCH), cris) | ||||||
|  | LIBOBJS+= op_helper.o helper.o | ||||||
|  | LIBOBJS+= cris-dis.o | ||||||
|  |  | ||||||
|  | ifndef CONFIG_USER_ONLY | ||||||
|  | LIBOBJS+= mmu.o | ||||||
|  | endif | ||||||
|  | endif | ||||||
|  |  | ||||||
|  | # NOTE: the disassembler code is only needed for debugging | ||||||
|  | LIBOBJS+=disas.o | ||||||
|  | ifeq ($(findstring i386, $(TARGET_ARCH) $(ARCH)),i386) | ||||||
|  | USE_I386_DIS=y | ||||||
|  | endif | ||||||
|  | ifeq ($(findstring x86_64, $(TARGET_ARCH) $(ARCH)),x86_64) | ||||||
|  | USE_I386_DIS=y | ||||||
|  | endif | ||||||
|  | ifdef USE_I386_DIS | ||||||
|  | LIBOBJS+=i386-dis.o | ||||||
|  | endif | ||||||
|  | ifeq ($(findstring alpha, $(TARGET_ARCH) $(ARCH)),alpha) | ||||||
|  | LIBOBJS+=alpha-dis.o | ||||||
|  | endif | ||||||
|  | ifeq ($(findstring ppc, $(TARGET_BASE_ARCH) $(ARCH)),ppc) | ||||||
|  | LIBOBJS+=ppc-dis.o | ||||||
|  | endif | ||||||
|  | ifeq ($(findstring mips, $(TARGET_BASE_ARCH) $(ARCH)),mips) | ||||||
|  | LIBOBJS+=mips-dis.o | ||||||
|  | endif | ||||||
|  | ifeq ($(findstring sparc, $(TARGET_BASE_ARCH) $(ARCH)),sparc) | ||||||
|  | LIBOBJS+=sparc-dis.o | ||||||
|  | endif | ||||||
|  | ifeq ($(findstring arm, $(TARGET_ARCH) $(ARCH)),arm) | ||||||
|  | LIBOBJS+=arm-dis.o | ||||||
|  | endif | ||||||
|  | ifeq ($(findstring m68k, $(TARGET_ARCH) $(ARCH)),m68k) | ||||||
|  | LIBOBJS+=m68k-dis.o | ||||||
|  | endif | ||||||
|  | ifeq ($(findstring sh4, $(TARGET_ARCH) $(ARCH)),sh4) | ||||||
|  | LIBOBJS+=sh4-dis.o | ||||||
|  | endif | ||||||
|  | ifeq ($(findstring s390, $(TARGET_ARCH) $(ARCH)),s390) | ||||||
|  | LIBOBJS+=s390-dis.o | ||||||
|  | endif | ||||||
|  |  | ||||||
|  | ifdef CONFIG_GDBSTUB | ||||||
|  | OBJS+=gdbstub.o | ||||||
|  | endif | ||||||
|  |  | ||||||
|  | all: $(PROGS) | ||||||
|  |  | ||||||
|  | $(QEMU_USER): $(OBJS) | ||||||
|  | 	$(CC) $(LDFLAGS) $(BASE_LDFLAGS) -o $@ $^  $(LIBS) | ||||||
|  | ifeq ($(ARCH),alpha) | ||||||
|  | # Mark as 32 bit binary, i. e. it will be mapped into the low 31 bit of | ||||||
|  | # the address space (31 bit so sign extending doesn't matter) | ||||||
|  | 	echo -ne '\001\000\000\000' | dd of=qemu bs=1 seek=48 count=4 conv=notrunc | ||||||
|  | endif | ||||||
|  |  | ||||||
|  | # must use static linking to avoid leaving stuff in virtual address space | ||||||
|  | VL_OBJS=vl.o osdep.o monitor.o pci.o loader.o isa_mmio.o | ||||||
|  | # XXX: suppress QEMU_TOOL tests | ||||||
|  | ifdef CONFIG_WIN32 | ||||||
|  | VL_OBJS+=block-raw-win32.o | ||||||
|  | else | ||||||
|  | VL_OBJS+=block-raw-posix.o | ||||||
|  | endif | ||||||
|  |  | ||||||
|  | ifdef CONFIG_ALSA | ||||||
|  | LIBS += -lasound | ||||||
|  | endif | ||||||
|  | ifdef CONFIG_DSOUND | ||||||
|  | LIBS += -lole32 -ldxguid | ||||||
|  | endif | ||||||
|  | ifdef CONFIG_FMOD | ||||||
|  | LIBS += $(CONFIG_FMOD_LIB) | ||||||
|  | endif | ||||||
|  |  | ||||||
|  | SOUND_HW = sb16.o es1370.o | ||||||
|  | ifdef CONFIG_ADLIB | ||||||
|  | SOUND_HW += fmopl.o adlib.o | ||||||
|  | endif | ||||||
|  |  | ||||||
|  | ifdef CONFIG_VNC_TLS | ||||||
|  | CPPFLAGS += $(CONFIG_VNC_TLS_CFLAGS) | ||||||
|  | LIBS += $(CONFIG_VNC_TLS_LIBS) | ||||||
|  | endif | ||||||
|  |  | ||||||
|  | # SCSI layer | ||||||
|  | VL_OBJS+= lsi53c895a.o | ||||||
|  |  | ||||||
|  | # USB layer | ||||||
|  | VL_OBJS+= usb-ohci.o | ||||||
|  |  | ||||||
|  | # EEPROM emulation | ||||||
|  | VL_OBJS += eeprom93xx.o | ||||||
|  |  | ||||||
|  | # PCI network cards | ||||||
|  | VL_OBJS += eepro100.o | ||||||
|  | VL_OBJS += ne2000.o | ||||||
|  | VL_OBJS += pcnet.o | ||||||
|  | VL_OBJS += rtl8139.o | ||||||
|  |  | ||||||
|  | ifeq ($(TARGET_BASE_ARCH), i386) | ||||||
|  | # Hardware support | ||||||
|  | VL_OBJS+= ide.o pckbd.o ps2.o vga.o $(SOUND_HW) dma.o | ||||||
|  | VL_OBJS+= fdc.o mc146818rtc.o serial.o i8259.o i8254.o pcspk.o pc.o | ||||||
|  | VL_OBJS+= cirrus_vga.o apic.o parallel.o acpi.o piix_pci.o | ||||||
|  | VL_OBJS+= usb-uhci.o vmmouse.o vmport.o vmware_vga.o | ||||||
|  | CPPFLAGS += -DHAS_AUDIO -DHAS_AUDIO_CHOICE | ||||||
|  | endif | ||||||
|  | ifeq ($(TARGET_BASE_ARCH), ppc) | ||||||
|  | CPPFLAGS += -DHAS_AUDIO -DHAS_AUDIO_CHOICE | ||||||
|  | # shared objects | ||||||
|  | VL_OBJS+= ppc.o ide.o vga.o $(SOUND_HW) dma.o openpic.o | ||||||
|  | # PREP target | ||||||
|  | VL_OBJS+= pckbd.o ps2.o serial.o i8259.o i8254.o fdc.o m48t59.o mc146818rtc.o | ||||||
|  | VL_OBJS+= prep_pci.o ppc_prep.o | ||||||
|  | # Mac shared devices | ||||||
|  | VL_OBJS+= macio.o cuda.o adb.o mac_nvram.o mac_dbdma.o | ||||||
|  | # OldWorld PowerMac | ||||||
|  | VL_OBJS+= heathrow_pic.o grackle_pci.o ppc_oldworld.o | ||||||
|  | # NewWorld PowerMac | ||||||
|  | VL_OBJS+= unin_pci.o ppc_chrp.o | ||||||
|  | # PowerPC 4xx boards | ||||||
|  | VL_OBJS+= pflash_cfi02.o ppc4xx_devs.o ppc405_uc.o ppc405_boards.o | ||||||
|  | endif | ||||||
|  | ifeq ($(TARGET_BASE_ARCH), mips) | ||||||
|  | VL_OBJS+= mips_r4k.o mips_malta.o mips_pica61.o mips_mipssim.o | ||||||
|  | VL_OBJS+= mips_timer.o mips_int.o dma.o vga.o serial.o i8254.o i8259.o | ||||||
|  | VL_OBJS+= jazz_led.o | ||||||
|  | VL_OBJS+= ide.o gt64xxx.o pckbd.o ps2.o fdc.o mc146818rtc.o usb-uhci.o acpi.o ds1225y.o | ||||||
|  | VL_OBJS+= piix_pci.o parallel.o cirrus_vga.o $(SOUND_HW) | ||||||
|  | VL_OBJS+= mipsnet.o | ||||||
|  | VL_OBJS+= pflash_cfi01.o | ||||||
|  | CPPFLAGS += -DHAS_AUDIO | ||||||
|  | endif | ||||||
|  | ifeq ($(TARGET_BASE_ARCH), cris) | ||||||
|  | VL_OBJS+= etraxfs.o | ||||||
|  | VL_OBJS+= ptimer.o | ||||||
|  | VL_OBJS+= etraxfs_timer.o | ||||||
|  | VL_OBJS+= etraxfs_ser.o | ||||||
|  | endif | ||||||
|  | ifeq ($(TARGET_BASE_ARCH), sparc) | ||||||
|  | ifeq ($(TARGET_ARCH), sparc64) | ||||||
|  | VL_OBJS+= sun4u.o ide.o pckbd.o ps2.o vga.o apb_pci.o | ||||||
|  | VL_OBJS+= fdc.o mc146818rtc.o serial.o m48t59.o | ||||||
|  | VL_OBJS+= cirrus_vga.o parallel.o ptimer.o | ||||||
|  | else | ||||||
|  | VL_OBJS+= sun4m.o tcx.o pcnet.o iommu.o m48t59.o slavio_intctl.o | ||||||
|  | VL_OBJS+= slavio_timer.o slavio_serial.o slavio_misc.o fdc.o esp.o sparc32_dma.o | ||||||
|  | VL_OBJS+= cs4231.o ptimer.o eccmemctl.o sbi.o sun4c_intctl.o | ||||||
|  | endif | ||||||
|  | endif | ||||||
|  | ifeq ($(TARGET_BASE_ARCH), arm) | ||||||
|  | VL_OBJS+= integratorcp.o versatilepb.o ps2.o smc91c111.o arm_pic.o arm_timer.o | ||||||
|  | VL_OBJS+= arm_boot.o pl011.o pl031.o pl050.o pl080.o pl110.o pl181.o pl190.o | ||||||
|  | VL_OBJS+= versatile_pci.o ptimer.o | ||||||
|  | VL_OBJS+= realview_gic.o realview.o arm_sysctl.o mpcore.o | ||||||
|  | VL_OBJS+= armv7m.o armv7m_nvic.o stellaris.o pl022.o stellaris_enet.o | ||||||
|  | VL_OBJS+= pl061.o | ||||||
|  | VL_OBJS+= arm-semi.o | ||||||
|  | VL_OBJS+= pxa2xx.o pxa2xx_pic.o pxa2xx_gpio.o pxa2xx_timer.o pxa2xx_dma.o | ||||||
|  | VL_OBJS+= pxa2xx_lcd.o pxa2xx_mmci.o pxa2xx_pcmcia.o pxa2xx_keypad.o | ||||||
|  | VL_OBJS+= pflash_cfi01.o gumstix.o | ||||||
|  | VL_OBJS+= spitz.o ide.o serial.o nand.o ecc.o | ||||||
|  | VL_OBJS+= omap.o omap_lcdc.o omap1_clk.o omap_mmc.o omap_i2c.o | ||||||
|  | VL_OBJS+= palm.o tsc210x.o | ||||||
|  | VL_OBJS+= mst_fpga.o mainstone.o | ||||||
|  | CPPFLAGS += -DHAS_AUDIO | ||||||
|  | endif | ||||||
|  | ifeq ($(TARGET_BASE_ARCH), sh4) | ||||||
|  | VL_OBJS+= shix.o r2d.o sh7750.o sh7750_regnames.o tc58128.o | ||||||
|  | VL_OBJS+= sh_timer.o ptimer.o sh_serial.o sh_intc.o | ||||||
|  | endif | ||||||
|  | ifeq ($(TARGET_BASE_ARCH), m68k) | ||||||
|  | VL_OBJS+= an5206.o mcf5206.o ptimer.o mcf_uart.o mcf_intc.o mcf5208.o mcf_fec.o | ||||||
|  | VL_OBJS+= m68k-semi.o dummy_m68k.o | ||||||
|  | endif | ||||||
|  | ifdef CONFIG_GDBSTUB | ||||||
|  | VL_OBJS+=gdbstub.o | ||||||
|  | endif | ||||||
|  | ifdef CONFIG_COCOA | ||||||
|  | COCOA_LIBS=-F/System/Library/Frameworks -framework Cocoa -framework IOKit | ||||||
|  | ifdef CONFIG_COREAUDIO | ||||||
|  | COCOA_LIBS+=-framework CoreAudio | ||||||
|  | endif | ||||||
|  | endif | ||||||
|  | ifdef CONFIG_SLIRP | ||||||
|  | CPPFLAGS+=-I$(SRC_PATH)/slirp | ||||||
|  | endif | ||||||
|  |  | ||||||
|  | VL_LDFLAGS=$(VL_OS_LDFLAGS) | ||||||
|  | VL_LIBS=$(AIOLIBS) | ||||||
|  | # specific flags are needed for non soft mmu emulator | ||||||
|  | ifdef CONFIG_STATIC | ||||||
|  | VL_LDFLAGS+=-static | ||||||
|  | endif | ||||||
|  | ifndef CONFIG_SOFTMMU | ||||||
|  | VL_LDFLAGS+=-Wl,-T,$(SRC_PATH)/i386-vl.ld | ||||||
|  | endif | ||||||
|  | ifndef CONFIG_DARWIN | ||||||
|  | ifndef CONFIG_WIN32 | ||||||
|  | ifndef CONFIG_SOLARIS | ||||||
|  | VL_LIBS+=-lutil | ||||||
|  | endif | ||||||
|  | endif | ||||||
|  | endif | ||||||
|  | ifdef TARGET_GPROF | ||||||
|  | vl.o: BASE_CFLAGS+=-p | ||||||
|  | VL_LDFLAGS+=-p | ||||||
|  | endif | ||||||
|  |  | ||||||
|  | ifeq ($(ARCH),ia64) | ||||||
|  | VL_LDFLAGS+=-Wl,-G0 -Wl,-T,$(SRC_PATH)/ia64.ld | ||||||
|  | endif | ||||||
|  |  | ||||||
|  | ifeq ($(ARCH),sparc64) | ||||||
|  |   VL_LDFLAGS+=-m64 | ||||||
|  |   ifneq ($(CONFIG_SOLARIS),yes) | ||||||
|  |     VL_LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld | ||||||
|  |   endif | ||||||
|  | endif | ||||||
|  |  | ||||||
|  | ifeq ($(ARCH),x86_64) | ||||||
|  |   VL_LDFLAGS+=-m64 | ||||||
|  |   ifneq ($(CONFIG_SOLARIS),yes) | ||||||
|  |     VL_LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld | ||||||
|  |   endif | ||||||
|  | endif | ||||||
|  |  | ||||||
|  | ifdef CONFIG_WIN32 | ||||||
|  | SDL_LIBS := $(filter-out -mwindows, $(SDL_LIBS)) -mconsole | ||||||
|  | endif | ||||||
|  |  | ||||||
|  | $(QEMU_SYSTEM): $(VL_OBJS) ../libqemu_common.a libqemu.a | ||||||
|  | 	$(CC) $(VL_LDFLAGS) $(LDFLAGS) -o $@ $^ $(LIBS) $(SDL_LIBS) $(COCOA_LIBS) $(VL_LIBS) | ||||||
|  |  | ||||||
|  | depend: $(SRCS) | ||||||
|  | 	$(CC) -MM $(CFLAGS) $(CPPFLAGS) $(BASE_CFLAGS) $^ 1>.depend | ||||||
|  |  | ||||||
|  | vldepend: $(VL_OBJS:.o=.c) | ||||||
|  | 	$(CC) -MM $(CFLAGS) $(CPPFLAGS) $(BASE_CFLAGS) $^ 1>.depend | ||||||
|  |  | ||||||
|  | # libqemu | ||||||
|  |  | ||||||
|  | libqemu.a: $(LIBOBJS) | ||||||
|  | 	rm -f $@ | ||||||
|  | 	$(AR) rcs $@ $(LIBOBJS) | ||||||
|  |  | ||||||
|  | translate.o: translate.c gen-op.h opc.h cpu.h | ||||||
|  |  | ||||||
|  | translate-all.o: translate-all.c opc.h cpu.h | ||||||
|  |  | ||||||
|  | translate-op.o: translate-all.c op.h opc.h cpu.h | ||||||
|  |  | ||||||
|  | op.h: op.o $(DYNGEN) | ||||||
|  | 	$(DYNGEN) -o $@ $< | ||||||
|  |  | ||||||
|  | opc.h: op.o $(DYNGEN) | ||||||
|  | 	$(DYNGEN) -c -o $@ $< | ||||||
|  |  | ||||||
|  | gen-op.h: op.o $(DYNGEN) | ||||||
|  | 	$(DYNGEN) -g -o $@ $< | ||||||
|  |  | ||||||
|  | op.o: op.c | ||||||
|  | 	$(CC) $(OP_CFLAGS) $(CPPFLAGS) -c -o $@ $< | ||||||
|  |  | ||||||
|  | # HELPER_CFLAGS is used for all the code compiled with static register | ||||||
|  | # variables | ||||||
|  | ifeq ($(TARGET_BASE_ARCH), i386) | ||||||
|  | # XXX: rename helper.c to op_helper.c | ||||||
|  | helper.o: helper.c | ||||||
|  | 	$(CC) $(HELPER_CFLAGS) $(CPPFLAGS) $(BASE_CFLAGS) -c -o $@ $< | ||||||
|  | else | ||||||
|  | op_helper.o: op_helper.c | ||||||
|  | 	$(CC) $(HELPER_CFLAGS) $(CPPFLAGS) $(BASE_CFLAGS) -c -o $@ $< | ||||||
|  | endif | ||||||
|  |  | ||||||
|  | cpu-exec.o: cpu-exec.c | ||||||
|  | 	$(CC) $(HELPER_CFLAGS) $(CPPFLAGS) $(BASE_CFLAGS) -c -o $@ $< | ||||||
|  |  | ||||||
|  | # Note: this is a workaround. The real fix is to avoid compiling | ||||||
|  | # cpu_signal_handler() in cpu-exec.c. | ||||||
|  | signal.o: signal.c | ||||||
|  | 	$(CC) $(HELPER_CFLAGS) $(CPPFLAGS) $(BASE_CFLAGS) -c -o $@ $< | ||||||
|  |  | ||||||
|  | %.o: %.c | ||||||
|  | 	$(CC) $(CFLAGS) $(CPPFLAGS) $(BASE_CFLAGS) -c -o $@ $< | ||||||
|  |  | ||||||
|  | %.o: %.S | ||||||
|  | 	$(CC) $(CPPFLAGS) -c -o $@ $< | ||||||
|  |  | ||||||
|  | clean: | ||||||
|  | 	rm -f *.o *.a *~ $(PROGS) gen-op.h opc.h op.h nwfpe/*.o fpu/*.o | ||||||
|  | 	rm -f *.d */*.d | ||||||
|  |  | ||||||
| install: all | install: all | ||||||
| ifneq ($(PROGS),) | ifneq ($(PROGS),) | ||||||
| 	$(call install-prog,$(PROGS),$(DESTDIR)$(bindir)) | 	$(INSTALL) -m 755 -s $(PROGS) "$(DESTDIR)$(bindir)" | ||||||
| endif |  | ||||||
| ifdef CONFIG_TRACE_SYSTEMTAP |  | ||||||
| 	$(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)-simpletrace.stp "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset/$(QEMU_PROG)-simpletrace.stp" |  | ||||||
| endif | endif | ||||||
|  |  | ||||||
| GENERATED_FILES += config-target.h | ifneq ($(wildcard .depend),) | ||||||
| Makefile: $(GENERATED_FILES) | include .depend | ||||||
|  | endif | ||||||
|  |  | ||||||
|  | ifeq (1, 0) | ||||||
|  | audio.o sdlaudio.o dsoundaudio.o ossaudio.o wavaudio.o noaudio.o \ | ||||||
|  | fmodaudio.o alsaaudio.o mixeng.o sb16.o es1370.o gus.o adlib.o: \ | ||||||
|  | CFLAGS := $(CFLAGS) -Wall -Werror -W -Wsign-compare | ||||||
|  | endif | ||||||
|  |  | ||||||
|  | # Include automatically generated dependency files | ||||||
|  | -include $(wildcard *.d */*.d) | ||||||
|   | |||||||
							
								
								
									
										140
									
								
								README
									
									
									
									
									
								
							
							
						
						
									
										140
									
								
								README
									
									
									
									
									
								
							| @@ -1,139 +1,3 @@ | |||||||
|          QEMU README | Read the documentation in qemu-doc.html. | ||||||
|          =========== |  | ||||||
|  |  | ||||||
| QEMU is a generic and open source machine & userspace emulator and | Fabrice Bellard. | ||||||
| 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 |  | ||||||
							
								
								
									
										55
									
								
								TODO
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								TODO
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,55 @@ | |||||||
|  | short term: | ||||||
|  | ---------- | ||||||
|  | - cycle counter for all archs | ||||||
|  | - cpu_interrupt() win32/SMP fix | ||||||
|  | - support variable tsc freq | ||||||
|  | - USB host async | ||||||
|  | - IDE async | ||||||
|  | - debug option in 'configure' script + disable -fomit-frame-pointer | ||||||
|  | - Precise VGA timings for old games/demos (malc patch) | ||||||
|  | - merge PIC spurious interrupt patch | ||||||
|  | - warning for OS/2: must not use 128 MB memory (merge bochs cmos patch ?) | ||||||
|  | - config file (at least for windows/Mac OS X) | ||||||
|  | - update doc: PCI infos. | ||||||
|  | - basic VGA optimizations | ||||||
|  | - better code fetch (different exception handling + CS.limit support) | ||||||
|  | - do not resize vga if invalid size. | ||||||
|  | - avoid looping if only exceptions | ||||||
|  | - TLB code protection support for PPC | ||||||
|  | - see openMosix Doc | ||||||
|  | - disable SMC handling for ARM/SPARC/PPC (not finished) | ||||||
|  | - see undefined flags for BTx insn | ||||||
|  | - user/kernel PUSHL/POPL in helper.c | ||||||
|  | - keyboard output buffer filling timing emulation | ||||||
|  | - return UD exception if LOCK prefix incorrectly used | ||||||
|  | - test ldt limit < 7 ? | ||||||
|  | - tests for each target CPU | ||||||
|  | - fix CCOP optimisation | ||||||
|  | - fix all remaining thread lock issues (must put TBs in a specific invalid | ||||||
|  |   state, find a solution for tb_flush()). | ||||||
|  |  | ||||||
|  | ppc specific: | ||||||
|  | ------------ | ||||||
|  | - TLB invalidate not needed if msr_pr changes | ||||||
|  | - enable shift optimizations ? | ||||||
|  |  | ||||||
|  | linux-user specific: | ||||||
|  | ------------------- | ||||||
|  | - add IPC syscalls | ||||||
|  | - handle rare page fault cases (in particular if page fault in helpers or | ||||||
|  |   in syscall emulation code). | ||||||
|  | - more syscalls (in particular all 64 bit ones, IPCs, fix 64 bit | ||||||
|  |   issues, fix 16 bit uid issues) | ||||||
|  | - use page_unprotect_range in every suitable syscall to handle all | ||||||
|  |   cases of self modifying code. | ||||||
|  | - fix thread stack freeing (use kernel 2.5.x CLONE_CHILD_CLEARTID) | ||||||
|  | - use kernel traps for unaligned accesses on ARM ? | ||||||
|  |  | ||||||
|  |  | ||||||
|  | lower priority: | ||||||
|  | -------------- | ||||||
|  | - int15 ah=86: use better timing | ||||||
|  | - suppress shift_mem ops | ||||||
|  | - fix some 16 bit sp push/pop overflow (pusha/popa, lcall lret) | ||||||
|  | - optimize FPU operations (evaluate x87 stack pointer statically) | ||||||
|  | - use -msoft-float on ARM | ||||||
							
								
								
									
										431
									
								
								a.out.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										431
									
								
								a.out.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,431 @@ | |||||||
|  | /* a.out.h | ||||||
|  |  | ||||||
|  |    Copyright 1997, 1998, 1999, 2001 Red Hat, Inc. | ||||||
|  |  | ||||||
|  | This file is part of Cygwin. | ||||||
|  |  | ||||||
|  | This software is a copyrighted work licensed under the terms of the | ||||||
|  | Cygwin license.  Please consult the file "CYGWIN_LICENSE" for | ||||||
|  | details. */ | ||||||
|  |  | ||||||
|  | #ifndef _A_OUT_H_ | ||||||
|  | #define _A_OUT_H_ | ||||||
|  |  | ||||||
|  | #ifdef __cplusplus | ||||||
|  | extern "C" { | ||||||
|  | #endif | ||||||
|  | #define COFF_IMAGE_WITH_PE | ||||||
|  | #define COFF_LONG_SECTION_NAMES | ||||||
|  |  | ||||||
|  | /*** coff information for Intel 386/486.  */ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /********************** FILE HEADER **********************/ | ||||||
|  |  | ||||||
|  | struct external_filehdr { | ||||||
|  |   short f_magic;	/* magic number			*/ | ||||||
|  |   short f_nscns;	/* number of sections		*/ | ||||||
|  |   host_ulong f_timdat;	/* time & date stamp		*/ | ||||||
|  |   host_ulong f_symptr;	/* file pointer to symtab	*/ | ||||||
|  |   host_ulong f_nsyms;	/* number of symtab entries	*/ | ||||||
|  |   short f_opthdr;	/* sizeof(optional hdr)		*/ | ||||||
|  |   short f_flags;	/* flags			*/ | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /* Bits for f_flags: | ||||||
|  |  *	F_RELFLG	relocation info stripped from file | ||||||
|  |  *	F_EXEC		file is executable (no unresolved external references) | ||||||
|  |  *	F_LNNO		line numbers stripped from file | ||||||
|  |  *	F_LSYMS		local symbols stripped from file | ||||||
|  |  *	F_AR32WR	file has byte ordering of an AR32WR machine (e.g. vax) | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #define F_RELFLG	(0x0001) | ||||||
|  | #define F_EXEC		(0x0002) | ||||||
|  | #define F_LNNO		(0x0004) | ||||||
|  | #define F_LSYMS		(0x0008) | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #define	I386MAGIC	0x14c | ||||||
|  | #define I386PTXMAGIC	0x154 | ||||||
|  | #define I386AIXMAGIC	0x175 | ||||||
|  |  | ||||||
|  | /* This is Lynx's all-platform magic number for executables. */ | ||||||
|  |  | ||||||
|  | #define LYNXCOFFMAGIC	0415 | ||||||
|  |  | ||||||
|  | #define I386BADMAG(x) (((x).f_magic != I386MAGIC) \ | ||||||
|  | 		       && (x).f_magic != I386AIXMAGIC \ | ||||||
|  | 		       && (x).f_magic != I386PTXMAGIC \ | ||||||
|  | 		       && (x).f_magic != LYNXCOFFMAGIC) | ||||||
|  |  | ||||||
|  | #define	FILHDR	struct external_filehdr | ||||||
|  | #define	FILHSZ	20 | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /********************** AOUT "OPTIONAL HEADER"= | ||||||
|  |  **********************/ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | typedef struct | ||||||
|  | { | ||||||
|  |   unsigned short magic;		/* type of file				*/ | ||||||
|  |   unsigned short vstamp;	/* version stamp			*/ | ||||||
|  |   host_ulong	tsize;		/* text size in bytes, padded to FW bdry*/ | ||||||
|  |   host_ulong	dsize;		/* initialized data "  "		*/ | ||||||
|  |   host_ulong	bsize;		/* uninitialized data "   "		*/ | ||||||
|  |   host_ulong	entry;		/* entry pt.				*/ | ||||||
|  |   host_ulong text_start;	/* base of text used for this file */ | ||||||
|  |   host_ulong data_start;	/* base of data used for this file= | ||||||
|  |  */ | ||||||
|  | } | ||||||
|  | AOUTHDR; | ||||||
|  |  | ||||||
|  | #define AOUTSZ 28 | ||||||
|  | #define AOUTHDRSZ 28 | ||||||
|  |  | ||||||
|  | #define OMAGIC          0404    /* object files, eg as output */ | ||||||
|  | #define ZMAGIC          0413    /* demand load format, eg normal ld output */ | ||||||
|  | #define STMAGIC		0401	/* target shlib */ | ||||||
|  | #define SHMAGIC		0443	/* host   shlib */ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* define some NT default values */ | ||||||
|  | /*  #define NT_IMAGE_BASE        0x400000 moved to internal.h */ | ||||||
|  | #define NT_SECTION_ALIGNMENT 0x1000 | ||||||
|  | #define NT_FILE_ALIGNMENT    0x200 | ||||||
|  | #define NT_DEF_RESERVE       0x100000 | ||||||
|  | #define NT_DEF_COMMIT        0x1000 | ||||||
|  |  | ||||||
|  | /********************** SECTION HEADER **********************/ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | struct external_scnhdr { | ||||||
|  |   char		s_name[8];	/* section name			*/ | ||||||
|  |   host_ulong	s_paddr;	/* physical address, offset | ||||||
|  | 				   of last addr in scn */ | ||||||
|  |   host_ulong	s_vaddr;	/* virtual address		*/ | ||||||
|  |   host_ulong	s_size;		/* section size			*/ | ||||||
|  |   host_ulong	s_scnptr;	/* file ptr to raw data for section */ | ||||||
|  |   host_ulong	s_relptr;	/* file ptr to relocation	*/ | ||||||
|  |   host_ulong	s_lnnoptr;	/* file ptr to line numbers	*/ | ||||||
|  |   unsigned short s_nreloc;	/* number of relocation entries	*/ | ||||||
|  |   unsigned short s_nlnno;	/* number of line number entries*/ | ||||||
|  |   host_ulong	s_flags;	/* flags			*/ | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | #define	SCNHDR	struct external_scnhdr | ||||||
|  | #define	SCNHSZ	40 | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * names of "special" sections | ||||||
|  |  */ | ||||||
|  | #define _TEXT	".text" | ||||||
|  | #define _DATA	".data" | ||||||
|  | #define _BSS	".bss" | ||||||
|  | #define _COMMENT ".comment" | ||||||
|  | #define _LIB ".lib" | ||||||
|  |  | ||||||
|  | /********************** LINE NUMBERS **********************/ | ||||||
|  |  | ||||||
|  | /* 1 line number entry for every "breakpointable" source line in a section. | ||||||
|  |  * Line numbers are grouped on a per function basis; first entry in a function | ||||||
|  |  * grouping will have l_lnno = 0 and in place of physical address will be the | ||||||
|  |  * symbol table index of the function name. | ||||||
|  |  */ | ||||||
|  | struct external_lineno { | ||||||
|  |   union { | ||||||
|  |     host_ulong l_symndx; /* function name symbol index, iff l_lnno 0 */ | ||||||
|  |     host_ulong l_paddr;	/* (physical) address of line number	*/ | ||||||
|  |   } l_addr; | ||||||
|  |   unsigned short l_lnno;	/* line number		*/ | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | #define	LINENO	struct external_lineno | ||||||
|  | #define	LINESZ	6 | ||||||
|  |  | ||||||
|  | /********************** SYMBOLS **********************/ | ||||||
|  |  | ||||||
|  | #define E_SYMNMLEN	8	/* # characters in a symbol name	*/ | ||||||
|  | #define E_FILNMLEN	14	/* # characters in a file name		*/ | ||||||
|  | #define E_DIMNUM	4	/* # array dimensions in auxiliary entry */ | ||||||
|  |  | ||||||
|  | struct __attribute__((packed)) external_syment | ||||||
|  | { | ||||||
|  |   union { | ||||||
|  |     char e_name[E_SYMNMLEN]; | ||||||
|  |     struct { | ||||||
|  |       host_ulong e_zeroes; | ||||||
|  |       host_ulong e_offset; | ||||||
|  |     } e; | ||||||
|  |   } e; | ||||||
|  |   host_ulong e_value; | ||||||
|  |   unsigned short e_scnum; | ||||||
|  |   unsigned short e_type; | ||||||
|  |   char e_sclass[1]; | ||||||
|  |   char e_numaux[1]; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | #define N_BTMASK	(0xf) | ||||||
|  | #define N_TMASK		(0x30) | ||||||
|  | #define N_BTSHFT	(4) | ||||||
|  | #define N_TSHIFT	(2) | ||||||
|  |  | ||||||
|  | union external_auxent { | ||||||
|  |   struct { | ||||||
|  |     host_ulong x_tagndx;	/* str, un, or enum tag indx */ | ||||||
|  |     union { | ||||||
|  |       struct { | ||||||
|  | 	unsigned short  x_lnno; /* declaration line number */ | ||||||
|  | 	unsigned short  x_size; /* str/union/array size */ | ||||||
|  |       } x_lnsz; | ||||||
|  |       host_ulong x_fsize;	/* size of function */ | ||||||
|  |     } x_misc; | ||||||
|  |     union { | ||||||
|  |       struct {			/* if ISFCN, tag, or .bb */ | ||||||
|  | 	host_ulong x_lnnoptr;/* ptr to fcn line # */ | ||||||
|  | 	host_ulong x_endndx;	/* entry ndx past block end */ | ||||||
|  |       } x_fcn; | ||||||
|  |       struct {			/* if ISARY, up to 4 dimen. */ | ||||||
|  | 	char x_dimen[E_DIMNUM][2]; | ||||||
|  |       } x_ary; | ||||||
|  |     } x_fcnary; | ||||||
|  |     unsigned short x_tvndx;	/* tv index */ | ||||||
|  |   } x_sym; | ||||||
|  |  | ||||||
|  |   union { | ||||||
|  |     char x_fname[E_FILNMLEN]; | ||||||
|  |     struct { | ||||||
|  |       host_ulong x_zeroes; | ||||||
|  |       host_ulong x_offset; | ||||||
|  |     } x_n; | ||||||
|  |   } x_file; | ||||||
|  |  | ||||||
|  |   struct { | ||||||
|  |     host_ulong x_scnlen;	/* section length */ | ||||||
|  |     unsigned short x_nreloc;	/* # relocation entries */ | ||||||
|  |     unsigned short x_nlinno;	/* # line numbers */ | ||||||
|  |     host_ulong x_checksum;	/* section COMDAT checksum */ | ||||||
|  |     unsigned short x_associated;/* COMDAT associated section index */ | ||||||
|  |     char x_comdat[1];		/* COMDAT selection number */ | ||||||
|  |   } x_scn; | ||||||
|  |  | ||||||
|  |   struct { | ||||||
|  |     host_ulong x_tvfill;	/* tv fill value */ | ||||||
|  |     unsigned short x_tvlen;	/* length of .tv */ | ||||||
|  |     char x_tvran[2][2];		/* tv range */ | ||||||
|  |   } x_tv;	/* info about .tv section (in auxent of symbol .tv)) */ | ||||||
|  |  | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | #define	SYMENT	struct external_syment | ||||||
|  | #define	SYMESZ	18 | ||||||
|  | #define	AUXENT	union external_auxent | ||||||
|  | #define	AUXESZ	18 | ||||||
|  |  | ||||||
|  | #define _ETEXT	"etext" | ||||||
|  |  | ||||||
|  | /********************** RELOCATION DIRECTIVES **********************/ | ||||||
|  |  | ||||||
|  | struct external_reloc { | ||||||
|  |   char r_vaddr[4]; | ||||||
|  |   char r_symndx[4]; | ||||||
|  |   char r_type[2]; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | #define RELOC struct external_reloc | ||||||
|  | #define RELSZ 10 | ||||||
|  |  | ||||||
|  | /* end of coff/i386.h */ | ||||||
|  |  | ||||||
|  | /* PE COFF header information */ | ||||||
|  |  | ||||||
|  | #ifndef _PE_H | ||||||
|  | #define _PE_H | ||||||
|  |  | ||||||
|  | /* NT specific file attributes */ | ||||||
|  | #define IMAGE_FILE_RELOCS_STRIPPED           0x0001 | ||||||
|  | #define IMAGE_FILE_EXECUTABLE_IMAGE          0x0002 | ||||||
|  | #define IMAGE_FILE_LINE_NUMS_STRIPPED        0x0004 | ||||||
|  | #define IMAGE_FILE_LOCAL_SYMS_STRIPPED       0x0008 | ||||||
|  | #define IMAGE_FILE_BYTES_REVERSED_LO         0x0080 | ||||||
|  | #define IMAGE_FILE_32BIT_MACHINE             0x0100 | ||||||
|  | #define IMAGE_FILE_DEBUG_STRIPPED            0x0200 | ||||||
|  | #define IMAGE_FILE_SYSTEM                    0x1000 | ||||||
|  | #define IMAGE_FILE_DLL                       0x2000 | ||||||
|  | #define IMAGE_FILE_BYTES_REVERSED_HI         0x8000 | ||||||
|  |  | ||||||
|  | /* additional flags to be set for section headers to allow the NT loader to | ||||||
|  |    read and write to the section data (to replace the addresses of data in | ||||||
|  |    dlls for one thing); also to execute the section in .text's case= | ||||||
|  |  */ | ||||||
|  | #define IMAGE_SCN_MEM_DISCARDABLE 0x02000000 | ||||||
|  | #define IMAGE_SCN_MEM_EXECUTE     0x20000000 | ||||||
|  | #define IMAGE_SCN_MEM_READ        0x40000000 | ||||||
|  | #define IMAGE_SCN_MEM_WRITE       0x80000000 | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Section characteristics added for ppc-nt | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #define IMAGE_SCN_TYPE_NO_PAD                0x00000008  /* Reserved.  */ | ||||||
|  |  | ||||||
|  | #define IMAGE_SCN_CNT_CODE                   0x00000020  /* Section contains code. */ | ||||||
|  | #define IMAGE_SCN_CNT_INITIALIZED_DATA       0x00000040  /* Section contains initialized data. */ | ||||||
|  | #define IMAGE_SCN_CNT_UNINITIALIZED_DATA     0x00000080  /* Section contains uninitialized data. */ | ||||||
|  |  | ||||||
|  | #define IMAGE_SCN_LNK_OTHER                  0x00000100  /* Reserved.  */ | ||||||
|  | #define IMAGE_SCN_LNK_INFO                   0x00000200  /* Section contains comments or some other type of information. */ | ||||||
|  | #define IMAGE_SCN_LNK_REMOVE                 0x00000800  /* Section contents will not become part of image. */ | ||||||
|  | #define IMAGE_SCN_LNK_COMDAT                 0x00001000  /* Section contents comdat. */ | ||||||
|  |  | ||||||
|  | #define IMAGE_SCN_MEM_FARDATA                0x00008000 | ||||||
|  |  | ||||||
|  | #define IMAGE_SCN_MEM_PURGEABLE              0x00020000 | ||||||
|  | #define IMAGE_SCN_MEM_16BIT                  0x00020000 | ||||||
|  | #define IMAGE_SCN_MEM_LOCKED                 0x00040000 | ||||||
|  | #define IMAGE_SCN_MEM_PRELOAD                0x00080000 | ||||||
|  |  | ||||||
|  | #define IMAGE_SCN_ALIGN_1BYTES               0x00100000 | ||||||
|  | #define IMAGE_SCN_ALIGN_2BYTES               0x00200000 | ||||||
|  | #define IMAGE_SCN_ALIGN_4BYTES               0x00300000 | ||||||
|  | #define IMAGE_SCN_ALIGN_8BYTES               0x00400000 | ||||||
|  | #define IMAGE_SCN_ALIGN_16BYTES              0x00500000  /* Default alignment if no others are specified. */ | ||||||
|  | #define IMAGE_SCN_ALIGN_32BYTES              0x00600000 | ||||||
|  | #define IMAGE_SCN_ALIGN_64BYTES              0x00700000 | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #define IMAGE_SCN_LNK_NRELOC_OVFL            0x01000000  /* Section contains extended relocations. */ | ||||||
|  | #define IMAGE_SCN_MEM_NOT_CACHED             0x04000000  /* Section is not cachable.               */ | ||||||
|  | #define IMAGE_SCN_MEM_NOT_PAGED              0x08000000  /* Section is not pageable.               */ | ||||||
|  | #define IMAGE_SCN_MEM_SHARED                 0x10000000  /* Section is shareable.                  */ | ||||||
|  |  | ||||||
|  | /* COMDAT selection codes.  */ | ||||||
|  |  | ||||||
|  | #define IMAGE_COMDAT_SELECT_NODUPLICATES     (1) /* Warn if duplicates.  */ | ||||||
|  | #define IMAGE_COMDAT_SELECT_ANY		     (2) /* No warning.  */ | ||||||
|  | #define IMAGE_COMDAT_SELECT_SAME_SIZE	     (3) /* Warn if different size.  */ | ||||||
|  | #define IMAGE_COMDAT_SELECT_EXACT_MATCH	     (4) /* Warn if different.  */ | ||||||
|  | #define IMAGE_COMDAT_SELECT_ASSOCIATIVE	     (5) /* Base on other section.  */ | ||||||
|  |  | ||||||
|  | /* Magic values that are true for all dos/nt implementations */ | ||||||
|  | #define DOSMAGIC       0x5a4d | ||||||
|  | #define NT_SIGNATURE   0x00004550 | ||||||
|  |  | ||||||
|  | /* NT allows long filenames, we want to accommodate this.  This may break | ||||||
|  |      some of the bfd functions */ | ||||||
|  | #undef  FILNMLEN | ||||||
|  | #define FILNMLEN	18	/* # characters in a file name		*/ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #ifdef COFF_IMAGE_WITH_PE | ||||||
|  | /* The filehdr is only weired in images */ | ||||||
|  |  | ||||||
|  | #undef FILHDR | ||||||
|  | struct external_PE_filehdr | ||||||
|  | { | ||||||
|  |   /* DOS header fields */ | ||||||
|  |   unsigned short e_magic;	/* Magic number, 0x5a4d */ | ||||||
|  |   unsigned short e_cblp;	/* Bytes on last page of file, 0x90 */ | ||||||
|  |   unsigned short e_cp;		/* Pages in file, 0x3 */ | ||||||
|  |   unsigned short e_crlc;	/* Relocations, 0x0 */ | ||||||
|  |   unsigned short e_cparhdr;	/* Size of header in paragraphs, 0x4 */ | ||||||
|  |   unsigned short e_minalloc;	/* Minimum extra paragraphs needed, 0x0 */ | ||||||
|  |   unsigned short e_maxalloc;	/* Maximum extra paragraphs needed, 0xFFFF */ | ||||||
|  |   unsigned short e_ss;		/* Initial (relative) SS value, 0x0 */ | ||||||
|  |   unsigned short e_sp;		/* Initial SP value, 0xb8 */ | ||||||
|  |   unsigned short e_csum;	/* Checksum, 0x0 */ | ||||||
|  |   unsigned short e_ip;		/* Initial IP value, 0x0 */ | ||||||
|  |   unsigned short e_cs;		/* Initial (relative) CS value, 0x0 */ | ||||||
|  |   unsigned short e_lfarlc;	/* File address of relocation table, 0x40 */ | ||||||
|  |   unsigned short e_ovno;	/* Overlay number, 0x0 */ | ||||||
|  |   char e_res[4][2];		/* Reserved words, all 0x0 */ | ||||||
|  |   unsigned short e_oemid;	/* OEM identifier (for e_oeminfo), 0x0 */ | ||||||
|  |   unsigned short e_oeminfo;	/* OEM information; e_oemid specific, 0x0 */ | ||||||
|  |   char e_res2[10][2];		/* Reserved words, all 0x0 */ | ||||||
|  |   host_ulong e_lfanew;	/* File address of new exe header, 0x80 */ | ||||||
|  |   char dos_message[16][4];	/* other stuff, always follow DOS header */ | ||||||
|  |   unsigned int nt_signature;	/* required NT signature, 0x4550 */ | ||||||
|  |  | ||||||
|  |   /* From standard header */ | ||||||
|  |  | ||||||
|  |   unsigned short f_magic;	/* magic number			*/ | ||||||
|  |   unsigned short f_nscns;	/* number of sections		*/ | ||||||
|  |   host_ulong f_timdat;	/* time & date stamp		*/ | ||||||
|  |   host_ulong f_symptr;	/* file pointer to symtab	*/ | ||||||
|  |   host_ulong f_nsyms;	/* number of symtab entries	*/ | ||||||
|  |   unsigned short f_opthdr;	/* sizeof(optional hdr)		*/ | ||||||
|  |   unsigned short f_flags;	/* flags			*/ | ||||||
|  | }; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #define FILHDR struct external_PE_filehdr | ||||||
|  | #undef FILHSZ | ||||||
|  | #define FILHSZ 152 | ||||||
|  |  | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | typedef struct | ||||||
|  | { | ||||||
|  |   unsigned short magic;		/* type of file				*/ | ||||||
|  |   unsigned short vstamp;	/* version stamp			*/ | ||||||
|  |   host_ulong	tsize;		/* text size in bytes, padded to FW bdry*/ | ||||||
|  |   host_ulong	dsize;		/* initialized data "  "		*/ | ||||||
|  |   host_ulong	bsize;		/* uninitialized data "   "		*/ | ||||||
|  |   host_ulong	entry;		/* entry pt.				*/ | ||||||
|  |   host_ulong text_start;	/* base of text used for this file */ | ||||||
|  |   host_ulong data_start;	/* base of all data used for this file */ | ||||||
|  |  | ||||||
|  |   /* NT extra fields; see internal.h for descriptions */ | ||||||
|  |   host_ulong  ImageBase; | ||||||
|  |   host_ulong  SectionAlignment; | ||||||
|  |   host_ulong  FileAlignment; | ||||||
|  |   unsigned short  MajorOperatingSystemVersion; | ||||||
|  |   unsigned short  MinorOperatingSystemVersion; | ||||||
|  |   unsigned short  MajorImageVersion; | ||||||
|  |   unsigned short  MinorImageVersion; | ||||||
|  |   unsigned short  MajorSubsystemVersion; | ||||||
|  |   unsigned short  MinorSubsystemVersion; | ||||||
|  |   char  Reserved1[4]; | ||||||
|  |   host_ulong  SizeOfImage; | ||||||
|  |   host_ulong  SizeOfHeaders; | ||||||
|  |   host_ulong  CheckSum; | ||||||
|  |   unsigned short Subsystem; | ||||||
|  |   unsigned short DllCharacteristics; | ||||||
|  |   host_ulong  SizeOfStackReserve; | ||||||
|  |   host_ulong  SizeOfStackCommit; | ||||||
|  |   host_ulong  SizeOfHeapReserve; | ||||||
|  |   host_ulong  SizeOfHeapCommit; | ||||||
|  |   host_ulong  LoaderFlags; | ||||||
|  |   host_ulong  NumberOfRvaAndSizes; | ||||||
|  |   /* IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; */ | ||||||
|  |   char  DataDirectory[16][2][4]; /* 16 entries, 2 elements/entry, 4 chars */ | ||||||
|  |  | ||||||
|  | } PEAOUTHDR; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #undef AOUTSZ | ||||||
|  | #define AOUTSZ (AOUTHDRSZ + 196) | ||||||
|  |  | ||||||
|  | #undef  E_FILNMLEN | ||||||
|  | #define E_FILNMLEN	18	/* # characters in a file name		*/ | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | /* end of coff/pe.h */ | ||||||
|  |  | ||||||
|  | #define DT_NON		(0)	/* no derived type */ | ||||||
|  | #define DT_PTR		(1)	/* pointer */ | ||||||
|  | #define DT_FCN		(2)	/* function */ | ||||||
|  | #define DT_ARY		(3)	/* array */ | ||||||
|  |  | ||||||
|  | #define ISPTR(x)	(((x) & N_TMASK) == (DT_PTR << N_BTSHFT)) | ||||||
|  | #define ISFCN(x)	(((x) & N_TMASK) == (DT_FCN << N_BTSHFT)) | ||||||
|  | #define ISARY(x)	(((x) & N_TMASK) == (DT_ARY << N_BTSHFT)) | ||||||
|  |  | ||||||
|  | #ifdef __cplusplus | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #endif /* _A_OUT_H_ */ | ||||||
|  |  | ||||||
| @@ -1,4 +0,0 @@ | |||||||
| obj-$(CONFIG_SOFTMMU) += accel.o |  | ||||||
| obj-y += kvm/ |  | ||||||
| obj-$(CONFIG_TCG) += tcg/ |  | ||||||
| obj-y += stubs/ |  | ||||||
							
								
								
									
										134
									
								
								accel/accel.c
									
									
									
									
									
								
							
							
						
						
									
										134
									
								
								accel/accel.c
									
									
									
									
									
								
							| @@ -1,134 +0,0 @@ | |||||||
| /* |  | ||||||
|  * QEMU System Emulator, accelerator interfaces |  | ||||||
|  * |  | ||||||
|  * Copyright (c) 2003-2008 Fabrice Bellard |  | ||||||
|  * Copyright (c) 2014 Red Hat Inc. |  | ||||||
|  * |  | ||||||
|  * 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 "sysemu/accel.h" |  | ||||||
| #include "hw/boards.h" |  | ||||||
| #include "sysemu/arch_init.h" |  | ||||||
| #include "sysemu/sysemu.h" |  | ||||||
| #include "sysemu/kvm.h" |  | ||||||
| #include "sysemu/qtest.h" |  | ||||||
| #include "hw/xen/xen.h" |  | ||||||
| #include "qom/object.h" |  | ||||||
| #include "qemu/error-report.h" |  | ||||||
| #include "qemu/option.h" |  | ||||||
|  |  | ||||||
| static const TypeInfo accel_type = { |  | ||||||
|     .name = TYPE_ACCEL, |  | ||||||
|     .parent = TYPE_OBJECT, |  | ||||||
|     .class_size = sizeof(AccelClass), |  | ||||||
|     .instance_size = sizeof(AccelState), |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| /* Lookup AccelClass from opt_name. Returns NULL if not found */ |  | ||||||
| static AccelClass *accel_find(const char *opt_name) |  | ||||||
| { |  | ||||||
|     char *class_name = g_strdup_printf(ACCEL_CLASS_NAME("%s"), opt_name); |  | ||||||
|     AccelClass *ac = ACCEL_CLASS(object_class_by_name(class_name)); |  | ||||||
|     g_free(class_name); |  | ||||||
|     return ac; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int accel_init_machine(AccelClass *acc, MachineState *ms) |  | ||||||
| { |  | ||||||
|     ObjectClass *oc = OBJECT_CLASS(acc); |  | ||||||
|     const char *cname = object_class_get_name(oc); |  | ||||||
|     AccelState *accel = ACCEL(object_new(cname)); |  | ||||||
|     int ret; |  | ||||||
|     ms->accelerator = accel; |  | ||||||
|     *(acc->allowed) = true; |  | ||||||
|     ret = acc->init_machine(ms); |  | ||||||
|     if (ret < 0) { |  | ||||||
|         ms->accelerator = NULL; |  | ||||||
|         *(acc->allowed) = false; |  | ||||||
|         object_unref(OBJECT(accel)); |  | ||||||
|     } |  | ||||||
|     return ret; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void configure_accelerator(MachineState *ms) |  | ||||||
| { |  | ||||||
|     const char *accel, *p; |  | ||||||
|     char buf[10]; |  | ||||||
|     int ret; |  | ||||||
|     bool accel_initialised = false; |  | ||||||
|     bool init_failed = false; |  | ||||||
|     AccelClass *acc = NULL; |  | ||||||
|  |  | ||||||
|     accel = qemu_opt_get(qemu_get_machine_opts(), "accel"); |  | ||||||
|     if (accel == NULL) { |  | ||||||
|         /* Use the default "accelerator", tcg */ |  | ||||||
|         accel = "tcg"; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     p = accel; |  | ||||||
|     while (!accel_initialised && *p != '\0') { |  | ||||||
|         if (*p == ':') { |  | ||||||
|             p++; |  | ||||||
|         } |  | ||||||
|         p = get_opt_name(buf, sizeof(buf), p, ':'); |  | ||||||
|         acc = accel_find(buf); |  | ||||||
|         if (!acc) { |  | ||||||
|             continue; |  | ||||||
|         } |  | ||||||
|         if (acc->available && !acc->available()) { |  | ||||||
|             printf("%s not supported for this target\n", |  | ||||||
|                    acc->name); |  | ||||||
|             continue; |  | ||||||
|         } |  | ||||||
|         ret = accel_init_machine(acc, ms); |  | ||||||
|         if (ret < 0) { |  | ||||||
|             init_failed = true; |  | ||||||
|             error_report("failed to initialize %s: %s", |  | ||||||
|                          acc->name, strerror(-ret)); |  | ||||||
|         } else { |  | ||||||
|             accel_initialised = true; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (!accel_initialised) { |  | ||||||
|         if (!init_failed) { |  | ||||||
|             error_report("-machine accel=%s: No accelerator found", accel); |  | ||||||
|         } |  | ||||||
|         exit(1); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (init_failed) { |  | ||||||
|         error_report("Back to %s accelerator", acc->name); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void accel_register_compat_props(AccelState *accel) |  | ||||||
| { |  | ||||||
|     AccelClass *class = ACCEL_GET_CLASS(accel); |  | ||||||
|     register_compat_props_array(class->global_props); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void register_accel_types(void) |  | ||||||
| { |  | ||||||
|     type_register_static(&accel_type); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type_init(register_accel_types); |  | ||||||
| @@ -1 +0,0 @@ | |||||||
| obj-$(CONFIG_KVM) += kvm-all.o |  | ||||||
							
								
								
									
										2522
									
								
								accel/kvm/kvm-all.c
									
									
									
									
									
								
							
							
						
						
									
										2522
									
								
								accel/kvm/kvm-all.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,16 +0,0 @@ | |||||||
| # Trace events for debugging and performance instrumentation |  | ||||||
|  |  | ||||||
| # kvm-all.c |  | ||||||
| kvm_ioctl(int type, void *arg) "type 0x%x, arg %p" |  | ||||||
| kvm_vm_ioctl(int type, void *arg) "type 0x%x, arg %p" |  | ||||||
| kvm_vcpu_ioctl(int cpu_index, int type, void *arg) "cpu_index %d, type 0x%x, arg %p" |  | ||||||
| kvm_run_exit(int cpu_index, uint32_t reason) "cpu_index %d, reason %d" |  | ||||||
| kvm_device_ioctl(int fd, int type, void *arg) "dev fd %d, type 0x%x, arg %p" |  | ||||||
| kvm_failed_reg_get(uint64_t id, const char *msg) "Warning: Unable to retrieve ONEREG %" PRIu64 " from KVM: %s" |  | ||||||
| kvm_failed_reg_set(uint64_t id, const char *msg) "Warning: Unable to set ONEREG %" PRIu64 " to KVM: %s" |  | ||||||
| kvm_irqchip_commit_routes(void) "" |  | ||||||
| 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_release_virq(int virq) "virq %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" |  | ||||||
|  |  | ||||||
| @@ -1,5 +0,0 @@ | |||||||
| obj-$(call lnot,$(CONFIG_HAX))  += hax-stub.o |  | ||||||
| obj-$(call lnot,$(CONFIG_HVF))  += hvf-stub.o |  | ||||||
| obj-$(call lnot,$(CONFIG_WHPX)) += whpx-stub.o |  | ||||||
| obj-$(call lnot,$(CONFIG_KVM))  += kvm-stub.o |  | ||||||
| obj-$(call lnot,$(CONFIG_TCG))  += tcg-stub.o |  | ||||||
| @@ -1,34 +0,0 @@ | |||||||
| /* |  | ||||||
|  * QEMU HAXM support |  | ||||||
|  * |  | ||||||
|  * Copyright (c) 2015, Intel Corporation |  | ||||||
|  * |  | ||||||
|  * Copyright 2016 Google, Inc. |  | ||||||
|  * |  | ||||||
|  * This software is licensed under the terms of the GNU General Public |  | ||||||
|  * License version 2, as published by the Free Software Foundation, and |  | ||||||
|  * may be copied, distributed, and modified under those terms. |  | ||||||
|  * |  | ||||||
|  * See the COPYING file in the top-level directory. |  | ||||||
|  * |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #include "qemu/osdep.h" |  | ||||||
| #include "qemu-common.h" |  | ||||||
| #include "cpu.h" |  | ||||||
| #include "sysemu/hax.h" |  | ||||||
|  |  | ||||||
| int hax_sync_vcpus(void) |  | ||||||
| { |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int hax_init_vcpu(CPUState *cpu) |  | ||||||
| { |  | ||||||
|     return -ENOSYS; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int hax_smp_cpu_exec(CPUState *cpu) |  | ||||||
| { |  | ||||||
|     return -ENOSYS; |  | ||||||
| } |  | ||||||
| @@ -1,31 +0,0 @@ | |||||||
| /* |  | ||||||
|  * QEMU HVF support |  | ||||||
|  * |  | ||||||
|  * Copyright 2017 Red Hat, Inc. |  | ||||||
|  * |  | ||||||
|  * This software is licensed under the terms of the GNU General Public |  | ||||||
|  * License version 2 or later, as published by the Free Software Foundation, |  | ||||||
|  * and may be copied, distributed, and modified under those terms. |  | ||||||
|  * |  | ||||||
|  * See the COPYING file in the top-level directory. |  | ||||||
|  * |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #include "qemu/osdep.h" |  | ||||||
| #include "qemu-common.h" |  | ||||||
| #include "cpu.h" |  | ||||||
| #include "sysemu/hvf.h" |  | ||||||
|  |  | ||||||
| int hvf_init_vcpu(CPUState *cpu) |  | ||||||
| { |  | ||||||
|     return -ENOSYS; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int hvf_vcpu_exec(CPUState *cpu) |  | ||||||
| { |  | ||||||
|     return -ENOSYS; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void hvf_vcpu_destroy(CPUState *cpu) |  | ||||||
| { |  | ||||||
| } |  | ||||||
| @@ -1,163 +0,0 @@ | |||||||
| /* |  | ||||||
|  * QEMU KVM stub |  | ||||||
|  * |  | ||||||
|  * Copyright Red Hat, Inc. 2010 |  | ||||||
|  * |  | ||||||
|  * Author: Paolo Bonzini     <pbonzini@redhat.com> |  | ||||||
|  * |  | ||||||
|  * This work is licensed under the terms of the GNU GPL, version 2 or later. |  | ||||||
|  * See the COPYING file in the top-level directory. |  | ||||||
|  * |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #include "qemu/osdep.h" |  | ||||||
| #include "qemu-common.h" |  | ||||||
| #include "cpu.h" |  | ||||||
| #include "sysemu/kvm.h" |  | ||||||
|  |  | ||||||
| #ifndef CONFIG_USER_ONLY |  | ||||||
| #include "hw/pci/msi.h" |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| KVMState *kvm_state; |  | ||||||
| bool kvm_kernel_irqchip; |  | ||||||
| bool kvm_async_interrupts_allowed; |  | ||||||
| bool kvm_eventfds_allowed; |  | ||||||
| bool kvm_irqfds_allowed; |  | ||||||
| bool kvm_resamplefds_allowed; |  | ||||||
| bool kvm_msi_via_irqfd_allowed; |  | ||||||
| bool kvm_gsi_routing_allowed; |  | ||||||
| bool kvm_gsi_direct_mapping; |  | ||||||
| bool kvm_allowed; |  | ||||||
| bool kvm_readonly_mem_allowed; |  | ||||||
| bool kvm_ioeventfd_any_length_allowed; |  | ||||||
| bool kvm_msi_use_devid; |  | ||||||
|  |  | ||||||
| int kvm_destroy_vcpu(CPUState *cpu) |  | ||||||
| { |  | ||||||
|     return -ENOSYS; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int kvm_init_vcpu(CPUState *cpu) |  | ||||||
| { |  | ||||||
|     return -ENOSYS; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void kvm_flush_coalesced_mmio_buffer(void) |  | ||||||
| { |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void kvm_cpu_synchronize_state(CPUState *cpu) |  | ||||||
| { |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void kvm_cpu_synchronize_post_reset(CPUState *cpu) |  | ||||||
| { |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void kvm_cpu_synchronize_post_init(CPUState *cpu) |  | ||||||
| { |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int kvm_cpu_exec(CPUState *cpu) |  | ||||||
| { |  | ||||||
|     abort(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| bool kvm_has_sync_mmu(void) |  | ||||||
| { |  | ||||||
|     return false; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int kvm_has_many_ioeventfds(void) |  | ||||||
| { |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int kvm_update_guest_debug(CPUState *cpu, unsigned long reinject_trap) |  | ||||||
| { |  | ||||||
|     return -ENOSYS; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int kvm_insert_breakpoint(CPUState *cpu, target_ulong addr, |  | ||||||
|                           target_ulong len, int type) |  | ||||||
| { |  | ||||||
|     return -EINVAL; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int kvm_remove_breakpoint(CPUState *cpu, target_ulong addr, |  | ||||||
|                           target_ulong len, int type) |  | ||||||
| { |  | ||||||
|     return -EINVAL; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void kvm_remove_all_breakpoints(CPUState *cpu) |  | ||||||
| { |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int kvm_on_sigbus_vcpu(CPUState *cpu, int code, void *addr) |  | ||||||
| { |  | ||||||
|     return 1; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int kvm_on_sigbus(int code, void *addr) |  | ||||||
| { |  | ||||||
|     return 1; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #ifndef CONFIG_USER_ONLY |  | ||||||
| int kvm_irqchip_add_msi_route(KVMState *s, int vector, PCIDevice *dev) |  | ||||||
| { |  | ||||||
|     return -ENOSYS; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void kvm_init_irq_routing(KVMState *s) |  | ||||||
| { |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void kvm_irqchip_release_virq(KVMState *s, int virq) |  | ||||||
| { |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int kvm_irqchip_update_msi_route(KVMState *s, int virq, MSIMessage msg, |  | ||||||
|                                  PCIDevice *dev) |  | ||||||
| { |  | ||||||
|     return -ENOSYS; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void kvm_irqchip_commit_routes(KVMState *s) |  | ||||||
| { |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int kvm_irqchip_add_adapter_route(KVMState *s, AdapterInfo *adapter) |  | ||||||
| { |  | ||||||
|     return -ENOSYS; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int kvm_irqchip_add_irqfd_notifier_gsi(KVMState *s, EventNotifier *n, |  | ||||||
|                                        EventNotifier *rn, int virq) |  | ||||||
| { |  | ||||||
|     return -ENOSYS; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int kvm_irqchip_remove_irqfd_notifier_gsi(KVMState *s, EventNotifier *n, |  | ||||||
|                                           int virq) |  | ||||||
| { |  | ||||||
|     return -ENOSYS; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| bool kvm_has_free_slot(MachineState *ms) |  | ||||||
| { |  | ||||||
|     return false; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void kvm_init_cpu_signals(CPUState *cpu) |  | ||||||
| { |  | ||||||
|     abort(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| bool kvm_arm_supports_user_irq(void) |  | ||||||
| { |  | ||||||
|     return false; |  | ||||||
| } |  | ||||||
| #endif |  | ||||||
| @@ -1,30 +0,0 @@ | |||||||
| /* |  | ||||||
|  * QEMU TCG accelerator stub |  | ||||||
|  * |  | ||||||
|  * Copyright Red Hat, Inc. 2013 |  | ||||||
|  * |  | ||||||
|  * Author: Paolo Bonzini     <pbonzini@redhat.com> |  | ||||||
|  * |  | ||||||
|  * This work is licensed under the terms of the GNU GPL, version 2 or later. |  | ||||||
|  * See the COPYING file in the top-level directory. |  | ||||||
|  * |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #include "qemu/osdep.h" |  | ||||||
| #include "qemu-common.h" |  | ||||||
| #include "cpu.h" |  | ||||||
| #include "tcg/tcg.h" |  | ||||||
| #include "exec/cpu-common.h" |  | ||||||
| #include "exec/exec-all.h" |  | ||||||
|  |  | ||||||
| void tb_flush(CPUState *cpu) |  | ||||||
| { |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void tb_unlock(void) |  | ||||||
| { |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void tlb_set_dirty(CPUState *cpu, target_ulong vaddr) |  | ||||||
| { |  | ||||||
| } |  | ||||||
| @@ -1,48 +0,0 @@ | |||||||
| /* |  | ||||||
|  * QEMU Windows Hypervisor Platform accelerator (WHPX) stub |  | ||||||
|  * |  | ||||||
|  * Copyright Microsoft Corp. 2017 |  | ||||||
|  * |  | ||||||
|  * 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 "cpu.h" |  | ||||||
| #include "sysemu/whpx.h" |  | ||||||
|  |  | ||||||
| int whpx_init_vcpu(CPUState *cpu) |  | ||||||
| { |  | ||||||
|     return -1; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int whpx_vcpu_exec(CPUState *cpu) |  | ||||||
| { |  | ||||||
|     return -1; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void whpx_destroy_vcpu(CPUState *cpu) |  | ||||||
| { |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void whpx_vcpu_kick(CPUState *cpu) |  | ||||||
| { |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void whpx_cpu_synchronize_state(CPUState *cpu) |  | ||||||
| { |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void whpx_cpu_synchronize_post_reset(CPUState *cpu) |  | ||||||
| { |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void whpx_cpu_synchronize_post_init(CPUState *cpu) |  | ||||||
| { |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void whpx_cpu_synchronize_pre_loadvm(CPUState *cpu) |  | ||||||
| { |  | ||||||
| } |  | ||||||
| @@ -1,8 +0,0 @@ | |||||||
| obj-$(CONFIG_SOFTMMU) += tcg-all.o |  | ||||||
| obj-$(CONFIG_SOFTMMU) += cputlb.o |  | ||||||
| obj-y += tcg-runtime.o tcg-runtime-gvec.o |  | ||||||
| obj-y += cpu-exec.o cpu-exec-common.o translate-all.o |  | ||||||
| obj-y += translator.o |  | ||||||
|  |  | ||||||
| obj-$(CONFIG_USER_ONLY) += user-exec.o |  | ||||||
| obj-$(call lnot,$(CONFIG_SOFTMMU)) += user-exec-stub.o |  | ||||||
| @@ -1,245 +0,0 @@ | |||||||
| /* |  | ||||||
|  * Atomic helper templates |  | ||||||
|  * Included from tcg-runtime.c and cputlb.c. |  | ||||||
|  * |  | ||||||
|  * Copyright (c) 2016 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/>. |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #if DATA_SIZE == 16 |  | ||||||
| # define SUFFIX     o |  | ||||||
| # define DATA_TYPE  Int128 |  | ||||||
| # define BSWAP      bswap128 |  | ||||||
| #elif DATA_SIZE == 8 |  | ||||||
| # define SUFFIX     q |  | ||||||
| # define DATA_TYPE  uint64_t |  | ||||||
| # define BSWAP      bswap64 |  | ||||||
| #elif DATA_SIZE == 4 |  | ||||||
| # define SUFFIX     l |  | ||||||
| # define DATA_TYPE  uint32_t |  | ||||||
| # define BSWAP      bswap32 |  | ||||||
| #elif DATA_SIZE == 2 |  | ||||||
| # define SUFFIX     w |  | ||||||
| # define DATA_TYPE  uint16_t |  | ||||||
| # define BSWAP      bswap16 |  | ||||||
| #elif DATA_SIZE == 1 |  | ||||||
| # define SUFFIX     b |  | ||||||
| # define DATA_TYPE  uint8_t |  | ||||||
| # define BSWAP |  | ||||||
| #else |  | ||||||
| # error unsupported data size |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| #if DATA_SIZE >= 4 |  | ||||||
| # define ABI_TYPE  DATA_TYPE |  | ||||||
| #else |  | ||||||
| # define ABI_TYPE  uint32_t |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| /* Define host-endian atomic operations.  Note that END is used within |  | ||||||
|    the ATOMIC_NAME macro, and redefined below.  */ |  | ||||||
| #if DATA_SIZE == 1 |  | ||||||
| # define END |  | ||||||
| #elif defined(HOST_WORDS_BIGENDIAN) |  | ||||||
| # define END  _be |  | ||||||
| #else |  | ||||||
| # define END  _le |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr, |  | ||||||
|                               ABI_TYPE cmpv, ABI_TYPE newv EXTRA_ARGS) |  | ||||||
| { |  | ||||||
|     ATOMIC_MMU_DECLS; |  | ||||||
|     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; |  | ||||||
|     DATA_TYPE ret = atomic_cmpxchg__nocheck(haddr, cmpv, newv); |  | ||||||
|     ATOMIC_MMU_CLEANUP; |  | ||||||
|     return ret; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #if DATA_SIZE >= 16 |  | ||||||
| ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS) |  | ||||||
| { |  | ||||||
|     ATOMIC_MMU_DECLS; |  | ||||||
|     DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP; |  | ||||||
|     __atomic_load(haddr, &val, __ATOMIC_RELAXED); |  | ||||||
|     ATOMIC_MMU_CLEANUP; |  | ||||||
|     return val; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr, |  | ||||||
|                      ABI_TYPE val EXTRA_ARGS) |  | ||||||
| { |  | ||||||
|     ATOMIC_MMU_DECLS; |  | ||||||
|     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; |  | ||||||
|     __atomic_store(haddr, &val, __ATOMIC_RELAXED); |  | ||||||
|     ATOMIC_MMU_CLEANUP; |  | ||||||
| } |  | ||||||
| #else |  | ||||||
| ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr, |  | ||||||
|                            ABI_TYPE val EXTRA_ARGS) |  | ||||||
| { |  | ||||||
|     ATOMIC_MMU_DECLS; |  | ||||||
|     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; |  | ||||||
|     DATA_TYPE ret = atomic_xchg__nocheck(haddr, val); |  | ||||||
|     ATOMIC_MMU_CLEANUP; |  | ||||||
|     return ret; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #define GEN_ATOMIC_HELPER(X)                                        \ |  | ||||||
| ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr,       \ |  | ||||||
|                  ABI_TYPE val EXTRA_ARGS)                           \ |  | ||||||
| {                                                                   \ |  | ||||||
|     ATOMIC_MMU_DECLS;                                               \ |  | ||||||
|     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;                           \ |  | ||||||
|     DATA_TYPE ret = atomic_##X(haddr, val);                         \ |  | ||||||
|     ATOMIC_MMU_CLEANUP;                                             \ |  | ||||||
|     return ret;                                                     \ |  | ||||||
| } |  | ||||||
|  |  | ||||||
| GEN_ATOMIC_HELPER(fetch_add) |  | ||||||
| GEN_ATOMIC_HELPER(fetch_and) |  | ||||||
| GEN_ATOMIC_HELPER(fetch_or) |  | ||||||
| GEN_ATOMIC_HELPER(fetch_xor) |  | ||||||
| GEN_ATOMIC_HELPER(add_fetch) |  | ||||||
| GEN_ATOMIC_HELPER(and_fetch) |  | ||||||
| GEN_ATOMIC_HELPER(or_fetch) |  | ||||||
| GEN_ATOMIC_HELPER(xor_fetch) |  | ||||||
|  |  | ||||||
| #undef GEN_ATOMIC_HELPER |  | ||||||
| #endif /* DATA SIZE >= 16 */ |  | ||||||
|  |  | ||||||
| #undef END |  | ||||||
|  |  | ||||||
| #if DATA_SIZE > 1 |  | ||||||
|  |  | ||||||
| /* Define reverse-host-endian atomic operations.  Note that END is used |  | ||||||
|    within the ATOMIC_NAME macro.  */ |  | ||||||
| #ifdef HOST_WORDS_BIGENDIAN |  | ||||||
| # define END  _le |  | ||||||
| #else |  | ||||||
| # define END  _be |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr, |  | ||||||
|                               ABI_TYPE cmpv, ABI_TYPE newv EXTRA_ARGS) |  | ||||||
| { |  | ||||||
|     ATOMIC_MMU_DECLS; |  | ||||||
|     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; |  | ||||||
|     DATA_TYPE ret = atomic_cmpxchg__nocheck(haddr, BSWAP(cmpv), BSWAP(newv)); |  | ||||||
|     ATOMIC_MMU_CLEANUP; |  | ||||||
|     return BSWAP(ret); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #if DATA_SIZE >= 16 |  | ||||||
| ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS) |  | ||||||
| { |  | ||||||
|     ATOMIC_MMU_DECLS; |  | ||||||
|     DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP; |  | ||||||
|     __atomic_load(haddr, &val, __ATOMIC_RELAXED); |  | ||||||
|     ATOMIC_MMU_CLEANUP; |  | ||||||
|     return BSWAP(val); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr, |  | ||||||
|                      ABI_TYPE val EXTRA_ARGS) |  | ||||||
| { |  | ||||||
|     ATOMIC_MMU_DECLS; |  | ||||||
|     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; |  | ||||||
|     val = BSWAP(val); |  | ||||||
|     __atomic_store(haddr, &val, __ATOMIC_RELAXED); |  | ||||||
|     ATOMIC_MMU_CLEANUP; |  | ||||||
| } |  | ||||||
| #else |  | ||||||
| ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr, |  | ||||||
|                            ABI_TYPE val EXTRA_ARGS) |  | ||||||
| { |  | ||||||
|     ATOMIC_MMU_DECLS; |  | ||||||
|     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; |  | ||||||
|     ABI_TYPE ret = atomic_xchg__nocheck(haddr, BSWAP(val)); |  | ||||||
|     ATOMIC_MMU_CLEANUP; |  | ||||||
|     return BSWAP(ret); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #define GEN_ATOMIC_HELPER(X)                                        \ |  | ||||||
| ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr,       \ |  | ||||||
|                  ABI_TYPE val EXTRA_ARGS)                           \ |  | ||||||
| {                                                                   \ |  | ||||||
|     ATOMIC_MMU_DECLS;                                               \ |  | ||||||
|     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;                           \ |  | ||||||
|     DATA_TYPE ret = atomic_##X(haddr, BSWAP(val));                  \ |  | ||||||
|     ATOMIC_MMU_CLEANUP;                                             \ |  | ||||||
|     return BSWAP(ret);                                              \ |  | ||||||
| } |  | ||||||
|  |  | ||||||
| GEN_ATOMIC_HELPER(fetch_and) |  | ||||||
| GEN_ATOMIC_HELPER(fetch_or) |  | ||||||
| GEN_ATOMIC_HELPER(fetch_xor) |  | ||||||
| GEN_ATOMIC_HELPER(and_fetch) |  | ||||||
| GEN_ATOMIC_HELPER(or_fetch) |  | ||||||
| GEN_ATOMIC_HELPER(xor_fetch) |  | ||||||
|  |  | ||||||
| #undef GEN_ATOMIC_HELPER |  | ||||||
|  |  | ||||||
| /* Note that for addition, we need to use a separate cmpxchg loop instead |  | ||||||
|    of bswaps for the reverse-host-endian helpers.  */ |  | ||||||
| ABI_TYPE ATOMIC_NAME(fetch_add)(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); |  | ||||||
|         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 */ |  | ||||||
|  |  | ||||||
| #undef END |  | ||||||
| #endif /* DATA_SIZE > 1 */ |  | ||||||
|  |  | ||||||
| #undef BSWAP |  | ||||||
| #undef ABI_TYPE |  | ||||||
| #undef DATA_TYPE |  | ||||||
| #undef SUFFIX |  | ||||||
| #undef DATA_SIZE |  | ||||||
| @@ -1,83 +0,0 @@ | |||||||
| /* |  | ||||||
|  *  emulator main execution loop |  | ||||||
|  * |  | ||||||
|  *  Copyright (c) 2003-2005 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/>. |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #include "qemu/osdep.h" |  | ||||||
| #include "cpu.h" |  | ||||||
| #include "sysemu/cpus.h" |  | ||||||
| #include "exec/exec-all.h" |  | ||||||
|  |  | ||||||
| bool tcg_allowed; |  | ||||||
|  |  | ||||||
| /* exit the current TB, but without causing any exception to be raised */ |  | ||||||
| void cpu_loop_exit_noexc(CPUState *cpu) |  | ||||||
| { |  | ||||||
|     /* XXX: restore cpu registers saved in host registers */ |  | ||||||
|  |  | ||||||
|     cpu->exception_index = -1; |  | ||||||
|     siglongjmp(cpu->jmp_env, 1); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #if defined(CONFIG_SOFTMMU) |  | ||||||
| void cpu_reloading_memory_map(void) |  | ||||||
| { |  | ||||||
|     if (qemu_in_vcpu_thread() && current_cpu->running) { |  | ||||||
|         /* The guest can in theory prolong the RCU critical section as long |  | ||||||
|          * as it feels like. The major problem with this is that because it |  | ||||||
|          * can do multiple reconfigurations of the memory map within the |  | ||||||
|          * critical section, we could potentially accumulate an unbounded |  | ||||||
|          * collection of memory data structures awaiting reclamation. |  | ||||||
|          * |  | ||||||
|          * Because the only thing we're currently protecting with RCU is the |  | ||||||
|          * memory data structures, it's sufficient to break the critical section |  | ||||||
|          * in this callback, which we know will get called every time the |  | ||||||
|          * memory map is rearranged. |  | ||||||
|          * |  | ||||||
|          * (If we add anything else in the system that uses RCU to protect |  | ||||||
|          * its data structures, we will need to implement some other mechanism |  | ||||||
|          * to force TCG CPUs to exit the critical section, at which point this |  | ||||||
|          * part of this callback might become unnecessary.) |  | ||||||
|          * |  | ||||||
|          * This pair matches cpu_exec's rcu_read_lock()/rcu_read_unlock(), which |  | ||||||
|          * only protects cpu->as->dispatch. Since we know our caller is about |  | ||||||
|          * to reload it, it's safe to split the critical section. |  | ||||||
|          */ |  | ||||||
|         rcu_read_unlock(); |  | ||||||
|         rcu_read_lock(); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| void cpu_loop_exit(CPUState *cpu) |  | ||||||
| { |  | ||||||
|     siglongjmp(cpu->jmp_env, 1); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void cpu_loop_exit_restore(CPUState *cpu, uintptr_t pc) |  | ||||||
| { |  | ||||||
|     if (pc) { |  | ||||||
|         cpu_restore_state(cpu, pc); |  | ||||||
|     } |  | ||||||
|     siglongjmp(cpu->jmp_env, 1); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void cpu_loop_exit_atomic(CPUState *cpu, uintptr_t pc) |  | ||||||
| { |  | ||||||
|     cpu->exception_index = EXCP_ATOMIC; |  | ||||||
|     cpu_loop_exit_restore(cpu, pc); |  | ||||||
| } |  | ||||||
| @@ -1,743 +0,0 @@ | |||||||
| /* |  | ||||||
|  *  emulator main execution loop |  | ||||||
|  * |  | ||||||
|  *  Copyright (c) 2003-2005 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/>. |  | ||||||
|  */ |  | ||||||
| #include "qemu/osdep.h" |  | ||||||
| #include "cpu.h" |  | ||||||
| #include "trace.h" |  | ||||||
| #include "disas/disas.h" |  | ||||||
| #include "exec/exec-all.h" |  | ||||||
| #include "tcg.h" |  | ||||||
| #include "qemu/atomic.h" |  | ||||||
| #include "sysemu/qtest.h" |  | ||||||
| #include "qemu/timer.h" |  | ||||||
| #include "exec/address-spaces.h" |  | ||||||
| #include "qemu/rcu.h" |  | ||||||
| #include "exec/tb-hash.h" |  | ||||||
| #include "exec/tb-lookup.h" |  | ||||||
| #include "exec/log.h" |  | ||||||
| #include "qemu/main-loop.h" |  | ||||||
| #if defined(TARGET_I386) && !defined(CONFIG_USER_ONLY) |  | ||||||
| #include "hw/i386/apic.h" |  | ||||||
| #endif |  | ||||||
| #include "sysemu/cpus.h" |  | ||||||
| #include "sysemu/replay.h" |  | ||||||
|  |  | ||||||
| /* -icount align implementation. */ |  | ||||||
|  |  | ||||||
| typedef struct SyncClocks { |  | ||||||
|     int64_t diff_clk; |  | ||||||
|     int64_t last_cpu_icount; |  | ||||||
|     int64_t realtime_clock; |  | ||||||
| } SyncClocks; |  | ||||||
|  |  | ||||||
| #if !defined(CONFIG_USER_ONLY) |  | ||||||
| /* Allow the guest to have a max 3ms advance. |  | ||||||
|  * The difference between the 2 clocks could therefore |  | ||||||
|  * oscillate around 0. |  | ||||||
|  */ |  | ||||||
| #define VM_CLOCK_ADVANCE 3000000 |  | ||||||
| #define THRESHOLD_REDUCE 1.5 |  | ||||||
| #define MAX_DELAY_PRINT_RATE 2000000000LL |  | ||||||
| #define MAX_NB_PRINTS 100 |  | ||||||
|  |  | ||||||
| static void align_clocks(SyncClocks *sc, const CPUState *cpu) |  | ||||||
| { |  | ||||||
|     int64_t cpu_icount; |  | ||||||
|  |  | ||||||
|     if (!icount_align_option) { |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     cpu_icount = cpu->icount_extra + cpu->icount_decr.u16.low; |  | ||||||
|     sc->diff_clk += cpu_icount_to_ns(sc->last_cpu_icount - cpu_icount); |  | ||||||
|     sc->last_cpu_icount = cpu_icount; |  | ||||||
|  |  | ||||||
|     if (sc->diff_clk > VM_CLOCK_ADVANCE) { |  | ||||||
| #ifndef _WIN32 |  | ||||||
|         struct timespec sleep_delay, rem_delay; |  | ||||||
|         sleep_delay.tv_sec = sc->diff_clk / 1000000000LL; |  | ||||||
|         sleep_delay.tv_nsec = sc->diff_clk % 1000000000LL; |  | ||||||
|         if (nanosleep(&sleep_delay, &rem_delay) < 0) { |  | ||||||
|             sc->diff_clk = rem_delay.tv_sec * 1000000000LL + rem_delay.tv_nsec; |  | ||||||
|         } else { |  | ||||||
|             sc->diff_clk = 0; |  | ||||||
|         } |  | ||||||
| #else |  | ||||||
|         Sleep(sc->diff_clk / SCALE_MS); |  | ||||||
|         sc->diff_clk = 0; |  | ||||||
| #endif |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void print_delay(const SyncClocks *sc) |  | ||||||
| { |  | ||||||
|     static float threshold_delay; |  | ||||||
|     static int64_t last_realtime_clock; |  | ||||||
|     static int nb_prints; |  | ||||||
|  |  | ||||||
|     if (icount_align_option && |  | ||||||
|         sc->realtime_clock - last_realtime_clock >= MAX_DELAY_PRINT_RATE && |  | ||||||
|         nb_prints < MAX_NB_PRINTS) { |  | ||||||
|         if ((-sc->diff_clk / (float)1000000000LL > threshold_delay) || |  | ||||||
|             (-sc->diff_clk / (float)1000000000LL < |  | ||||||
|              (threshold_delay - THRESHOLD_REDUCE))) { |  | ||||||
|             threshold_delay = (-sc->diff_clk / 1000000000LL) + 1; |  | ||||||
|             printf("Warning: The guest is now late by %.1f to %.1f seconds\n", |  | ||||||
|                    threshold_delay - 1, |  | ||||||
|                    threshold_delay); |  | ||||||
|             nb_prints++; |  | ||||||
|             last_realtime_clock = sc->realtime_clock; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void init_delay_params(SyncClocks *sc, |  | ||||||
|                               const CPUState *cpu) |  | ||||||
| { |  | ||||||
|     if (!icount_align_option) { |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|     sc->realtime_clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL_RT); |  | ||||||
|     sc->diff_clk = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - sc->realtime_clock; |  | ||||||
|     sc->last_cpu_icount = cpu->icount_extra + cpu->icount_decr.u16.low; |  | ||||||
|     if (sc->diff_clk < max_delay) { |  | ||||||
|         max_delay = sc->diff_clk; |  | ||||||
|     } |  | ||||||
|     if (sc->diff_clk > max_advance) { |  | ||||||
|         max_advance = sc->diff_clk; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /* Print every 2s max if the guest is late. We limit the number |  | ||||||
|        of printed messages to NB_PRINT_MAX(currently 100) */ |  | ||||||
|     print_delay(sc); |  | ||||||
| } |  | ||||||
| #else |  | ||||||
| static void align_clocks(SyncClocks *sc, const CPUState *cpu) |  | ||||||
| { |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void init_delay_params(SyncClocks *sc, const CPUState *cpu) |  | ||||||
| { |  | ||||||
| } |  | ||||||
| #endif /* CONFIG USER ONLY */ |  | ||||||
|  |  | ||||||
| /* Execute a TB, and fix up the CPU state afterwards if necessary */ |  | ||||||
| static inline tcg_target_ulong cpu_tb_exec(CPUState *cpu, TranslationBlock *itb) |  | ||||||
| { |  | ||||||
|     CPUArchState *env = cpu->env_ptr; |  | ||||||
|     uintptr_t ret; |  | ||||||
|     TranslationBlock *last_tb; |  | ||||||
|     int tb_exit; |  | ||||||
|     uint8_t *tb_ptr = itb->tc.ptr; |  | ||||||
|  |  | ||||||
|     qemu_log_mask_and_addr(CPU_LOG_EXEC, itb->pc, |  | ||||||
|                            "Trace %d: %p [" |  | ||||||
|                            TARGET_FMT_lx "/" TARGET_FMT_lx "/%#x] %s\n", |  | ||||||
|                            cpu->cpu_index, itb->tc.ptr, |  | ||||||
|                            itb->cs_base, itb->pc, itb->flags, |  | ||||||
|                            lookup_symbol(itb->pc)); |  | ||||||
|  |  | ||||||
| #if defined(DEBUG_DISAS) |  | ||||||
|     if (qemu_loglevel_mask(CPU_LOG_TB_CPU) |  | ||||||
|         && qemu_log_in_addr_range(itb->pc)) { |  | ||||||
|         qemu_log_lock(); |  | ||||||
| #if defined(TARGET_I386) |  | ||||||
|         log_cpu_state(cpu, CPU_DUMP_CCOP); |  | ||||||
| #else |  | ||||||
|         log_cpu_state(cpu, 0); |  | ||||||
| #endif |  | ||||||
|         qemu_log_unlock(); |  | ||||||
|     } |  | ||||||
| #endif /* DEBUG_DISAS */ |  | ||||||
|  |  | ||||||
|     cpu->can_do_io = !use_icount; |  | ||||||
|     ret = tcg_qemu_tb_exec(env, tb_ptr); |  | ||||||
|     cpu->can_do_io = 1; |  | ||||||
|     last_tb = (TranslationBlock *)(ret & ~TB_EXIT_MASK); |  | ||||||
|     tb_exit = ret & TB_EXIT_MASK; |  | ||||||
|     trace_exec_tb_exit(last_tb, tb_exit); |  | ||||||
|  |  | ||||||
|     if (tb_exit > TB_EXIT_IDX1) { |  | ||||||
|         /* We didn't start executing this TB (eg because the instruction |  | ||||||
|          * counter hit zero); we must restore the guest PC to the address |  | ||||||
|          * of the start of the TB. |  | ||||||
|          */ |  | ||||||
|         CPUClass *cc = CPU_GET_CLASS(cpu); |  | ||||||
|         qemu_log_mask_and_addr(CPU_LOG_EXEC, last_tb->pc, |  | ||||||
|                                "Stopped execution of TB chain before %p [" |  | ||||||
|                                TARGET_FMT_lx "] %s\n", |  | ||||||
|                                last_tb->tc.ptr, last_tb->pc, |  | ||||||
|                                lookup_symbol(last_tb->pc)); |  | ||||||
|         if (cc->synchronize_from_tb) { |  | ||||||
|             cc->synchronize_from_tb(cpu, last_tb); |  | ||||||
|         } else { |  | ||||||
|             assert(cc->set_pc); |  | ||||||
|             cc->set_pc(cpu, last_tb->pc); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     return ret; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #ifndef CONFIG_USER_ONLY |  | ||||||
| /* Execute the code without caching the generated code. An interpreter |  | ||||||
|    could be used if available. */ |  | ||||||
| static void cpu_exec_nocache(CPUState *cpu, int max_cycles, |  | ||||||
|                              TranslationBlock *orig_tb, bool ignore_icount) |  | ||||||
| { |  | ||||||
|     TranslationBlock *tb; |  | ||||||
|     uint32_t cflags = curr_cflags() | CF_NOCACHE; |  | ||||||
|  |  | ||||||
|     if (ignore_icount) { |  | ||||||
|         cflags &= ~CF_USE_ICOUNT; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /* Should never happen. |  | ||||||
|        We only end up here when an existing TB is too long.  */ |  | ||||||
|     cflags |= MIN(max_cycles, CF_COUNT_MASK); |  | ||||||
|  |  | ||||||
|     tb_lock(); |  | ||||||
|     tb = tb_gen_code(cpu, orig_tb->pc, orig_tb->cs_base, |  | ||||||
|                      orig_tb->flags, cflags); |  | ||||||
|     tb->orig_tb = orig_tb; |  | ||||||
|     tb_unlock(); |  | ||||||
|  |  | ||||||
|     /* execute the generated code */ |  | ||||||
|     trace_exec_tb_nocache(tb, tb->pc); |  | ||||||
|     cpu_tb_exec(cpu, tb); |  | ||||||
|  |  | ||||||
|     tb_lock(); |  | ||||||
|     tb_phys_invalidate(tb, -1); |  | ||||||
|     tb_remove(tb); |  | ||||||
|     tb_unlock(); |  | ||||||
| } |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| void cpu_exec_step_atomic(CPUState *cpu) |  | ||||||
| { |  | ||||||
|     CPUClass *cc = CPU_GET_CLASS(cpu); |  | ||||||
|     TranslationBlock *tb; |  | ||||||
|     target_ulong cs_base, pc; |  | ||||||
|     uint32_t flags; |  | ||||||
|     uint32_t cflags = 1; |  | ||||||
|     uint32_t cf_mask = cflags & CF_HASH_MASK; |  | ||||||
|     /* volatile because we modify it between setjmp and longjmp */ |  | ||||||
|     volatile bool in_exclusive_region = false; |  | ||||||
|  |  | ||||||
|     if (sigsetjmp(cpu->jmp_env, 0) == 0) { |  | ||||||
|         tb = tb_lookup__cpu_state(cpu, &pc, &cs_base, &flags, cf_mask); |  | ||||||
|         if (tb == NULL) { |  | ||||||
|             mmap_lock(); |  | ||||||
|             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(); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         start_exclusive(); |  | ||||||
|  |  | ||||||
|         /* Since we got here, we know that parallel_cpus must be true.  */ |  | ||||||
|         parallel_cpus = false; |  | ||||||
|         in_exclusive_region = true; |  | ||||||
|         cc->cpu_exec_enter(cpu); |  | ||||||
|         /* execute the generated code */ |  | ||||||
|         trace_exec_tb(tb, pc); |  | ||||||
|         cpu_tb_exec(cpu, tb); |  | ||||||
|         cc->cpu_exec_exit(cpu); |  | ||||||
|     } 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 |  | ||||||
|          * memory. |  | ||||||
|          */ |  | ||||||
| #ifndef CONFIG_SOFTMMU |  | ||||||
|         tcg_debug_assert(!have_mmap_lock()); |  | ||||||
| #endif |  | ||||||
|         tb_lock_reset(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (in_exclusive_region) { |  | ||||||
|         /* We might longjump out of either the codegen or the |  | ||||||
|          * execution, so must make sure we only end the exclusive |  | ||||||
|          * region if we started it. |  | ||||||
|          */ |  | ||||||
|         parallel_cpus = true; |  | ||||||
|         end_exclusive(); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| struct tb_desc { |  | ||||||
|     target_ulong pc; |  | ||||||
|     target_ulong cs_base; |  | ||||||
|     CPUArchState *env; |  | ||||||
|     tb_page_addr_t phys_page1; |  | ||||||
|     uint32_t flags; |  | ||||||
|     uint32_t cf_mask; |  | ||||||
|     uint32_t trace_vcpu_dstate; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static bool tb_cmp(const void *p, const void *d) |  | ||||||
| { |  | ||||||
|     const TranslationBlock *tb = p; |  | ||||||
|     const struct tb_desc *desc = d; |  | ||||||
|  |  | ||||||
|     if (tb->pc == desc->pc && |  | ||||||
|         tb->page_addr[0] == desc->phys_page1 && |  | ||||||
|         tb->cs_base == desc->cs_base && |  | ||||||
|         tb->flags == desc->flags && |  | ||||||
|         tb->trace_vcpu_dstate == desc->trace_vcpu_dstate && |  | ||||||
|         (tb_cflags(tb) & (CF_HASH_MASK | CF_INVALID)) == desc->cf_mask) { |  | ||||||
|         /* check next page if needed */ |  | ||||||
|         if (tb->page_addr[1] == -1) { |  | ||||||
|             return true; |  | ||||||
|         } else { |  | ||||||
|             tb_page_addr_t phys_page2; |  | ||||||
|             target_ulong virt_page2; |  | ||||||
|  |  | ||||||
|             virt_page2 = (desc->pc & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE; |  | ||||||
|             phys_page2 = get_page_addr_code(desc->env, virt_page2); |  | ||||||
|             if (tb->page_addr[1] == phys_page2) { |  | ||||||
|                 return true; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     return false; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| TranslationBlock *tb_htable_lookup(CPUState *cpu, target_ulong pc, |  | ||||||
|                                    target_ulong cs_base, uint32_t flags, |  | ||||||
|                                    uint32_t cf_mask) |  | ||||||
| { |  | ||||||
|     tb_page_addr_t phys_pc; |  | ||||||
|     struct tb_desc desc; |  | ||||||
|     uint32_t h; |  | ||||||
|  |  | ||||||
|     desc.env = (CPUArchState *)cpu->env_ptr; |  | ||||||
|     desc.cs_base = cs_base; |  | ||||||
|     desc.flags = flags; |  | ||||||
|     desc.cf_mask = cf_mask; |  | ||||||
|     desc.trace_vcpu_dstate = *cpu->trace_dstate; |  | ||||||
|     desc.pc = pc; |  | ||||||
|     phys_pc = get_page_addr_code(desc.env, pc); |  | ||||||
|     desc.phys_page1 = phys_pc & TARGET_PAGE_MASK; |  | ||||||
|     h = tb_hash_func(phys_pc, pc, flags, cf_mask, *cpu->trace_dstate); |  | ||||||
|     return qht_lookup(&tb_ctx.htable, tb_cmp, &desc, h); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void tb_set_jmp_target(TranslationBlock *tb, int n, uintptr_t addr) |  | ||||||
| { |  | ||||||
|     if (TCG_TARGET_HAS_direct_jump) { |  | ||||||
|         uintptr_t offset = tb->jmp_target_arg[n]; |  | ||||||
|         uintptr_t tc_ptr = (uintptr_t)tb->tc.ptr; |  | ||||||
|         tb_target_set_jmp_target(tc_ptr, tc_ptr + offset, addr); |  | ||||||
|     } else { |  | ||||||
|         tb->jmp_target_arg[n] = addr; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* Called with tb_lock held.  */ |  | ||||||
| static inline void tb_add_jump(TranslationBlock *tb, int n, |  | ||||||
|                                TranslationBlock *tb_next) |  | ||||||
| { |  | ||||||
|     assert(n < ARRAY_SIZE(tb->jmp_list_next)); |  | ||||||
|     if (tb->jmp_list_next[n]) { |  | ||||||
|         /* Another thread has already done this while we were |  | ||||||
|          * outside of the lock; nothing to do in this case */ |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|     qemu_log_mask_and_addr(CPU_LOG_EXEC, tb->pc, |  | ||||||
|                            "Linking TBs %p [" TARGET_FMT_lx |  | ||||||
|                            "] index %d -> %p [" TARGET_FMT_lx "]\n", |  | ||||||
|                            tb->tc.ptr, tb->pc, n, |  | ||||||
|                            tb_next->tc.ptr, tb_next->pc); |  | ||||||
|  |  | ||||||
|     /* patch the native jump address */ |  | ||||||
|     tb_set_jmp_target(tb, n, (uintptr_t)tb_next->tc.ptr); |  | ||||||
|  |  | ||||||
|     /* 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, |  | ||||||
|                                         TranslationBlock *last_tb, |  | ||||||
|                                         int tb_exit, uint32_t cf_mask) |  | ||||||
| { |  | ||||||
|     TranslationBlock *tb; |  | ||||||
|     target_ulong cs_base, pc; |  | ||||||
|     uint32_t flags; |  | ||||||
|     bool acquired_tb_lock = false; |  | ||||||
|  |  | ||||||
|     tb = tb_lookup__cpu_state(cpu, &pc, &cs_base, &flags, cf_mask); |  | ||||||
|     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(); |  | ||||||
|         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(); |  | ||||||
|         /* 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); |  | ||||||
|     } |  | ||||||
| #ifndef CONFIG_USER_ONLY |  | ||||||
|     /* We don't take care of direct jumps when address mapping changes in |  | ||||||
|      * system emulation. So it's not safe to make a direct jump to a TB |  | ||||||
|      * spanning two pages because the mapping for the second page can change. |  | ||||||
|      */ |  | ||||||
|     if (tb->page_addr[1] != -1) { |  | ||||||
|         last_tb = NULL; |  | ||||||
|     } |  | ||||||
| #endif |  | ||||||
|     /* See if we can patch the calling TB. */ |  | ||||||
|     if (last_tb && !qemu_loglevel_mask(CPU_LOG_TB_NOCHAIN)) { |  | ||||||
|         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; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static inline bool cpu_handle_halt(CPUState *cpu) |  | ||||||
| { |  | ||||||
|     if (cpu->halted) { |  | ||||||
| #if defined(TARGET_I386) && !defined(CONFIG_USER_ONLY) |  | ||||||
|         if ((cpu->interrupt_request & CPU_INTERRUPT_POLL) |  | ||||||
|             && replay_interrupt()) { |  | ||||||
|             X86CPU *x86_cpu = X86_CPU(cpu); |  | ||||||
|             qemu_mutex_lock_iothread(); |  | ||||||
|             apic_poll_irq(x86_cpu->apic_state); |  | ||||||
|             cpu_reset_interrupt(cpu, CPU_INTERRUPT_POLL); |  | ||||||
|             qemu_mutex_unlock_iothread(); |  | ||||||
|         } |  | ||||||
| #endif |  | ||||||
|         if (!cpu_has_work(cpu)) { |  | ||||||
|             return true; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         cpu->halted = 0; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return false; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static inline void cpu_handle_debug_exception(CPUState *cpu) |  | ||||||
| { |  | ||||||
|     CPUClass *cc = CPU_GET_CLASS(cpu); |  | ||||||
|     CPUWatchpoint *wp; |  | ||||||
|  |  | ||||||
|     if (!cpu->watchpoint_hit) { |  | ||||||
|         QTAILQ_FOREACH(wp, &cpu->watchpoints, entry) { |  | ||||||
|             wp->flags &= ~BP_WATCHPOINT_HIT; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     cc->debug_excp_handler(cpu); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static inline bool cpu_handle_exception(CPUState *cpu, int *ret) |  | ||||||
| { |  | ||||||
|     if (cpu->exception_index < 0) { |  | ||||||
| #ifndef CONFIG_USER_ONLY |  | ||||||
|         if (replay_has_exception() |  | ||||||
|                && cpu->icount_decr.u16.low + cpu->icount_extra == 0) { |  | ||||||
|             /* try to cause an exception pending in the log */ |  | ||||||
|             cpu_exec_nocache(cpu, 1, tb_find(cpu, NULL, 0, curr_cflags()), true); |  | ||||||
|         } |  | ||||||
| #endif |  | ||||||
|         if (cpu->exception_index < 0) { |  | ||||||
|             return false; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (cpu->exception_index >= EXCP_INTERRUPT) { |  | ||||||
|         /* exit request from the cpu execution loop */ |  | ||||||
|         *ret = cpu->exception_index; |  | ||||||
|         if (*ret == EXCP_DEBUG) { |  | ||||||
|             cpu_handle_debug_exception(cpu); |  | ||||||
|         } |  | ||||||
|         cpu->exception_index = -1; |  | ||||||
|         return true; |  | ||||||
|     } else { |  | ||||||
| #if defined(CONFIG_USER_ONLY) |  | ||||||
|         /* if user mode only, we simulate a fake exception |  | ||||||
|            which will be handled outside the cpu execution |  | ||||||
|            loop */ |  | ||||||
| #if defined(TARGET_I386) |  | ||||||
|         CPUClass *cc = CPU_GET_CLASS(cpu); |  | ||||||
|         cc->do_interrupt(cpu); |  | ||||||
| #endif |  | ||||||
|         *ret = cpu->exception_index; |  | ||||||
|         cpu->exception_index = -1; |  | ||||||
|         return true; |  | ||||||
| #else |  | ||||||
|         if (replay_exception()) { |  | ||||||
|             CPUClass *cc = CPU_GET_CLASS(cpu); |  | ||||||
|             qemu_mutex_lock_iothread(); |  | ||||||
|             cc->do_interrupt(cpu); |  | ||||||
|             qemu_mutex_unlock_iothread(); |  | ||||||
|             cpu->exception_index = -1; |  | ||||||
|         } else if (!replay_has_interrupt()) { |  | ||||||
|             /* give a chance to iothread in replay mode */ |  | ||||||
|             *ret = EXCP_INTERRUPT; |  | ||||||
|             return true; |  | ||||||
|         } |  | ||||||
| #endif |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return false; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static inline bool cpu_handle_interrupt(CPUState *cpu, |  | ||||||
|                                         TranslationBlock **last_tb) |  | ||||||
| { |  | ||||||
|     CPUClass *cc = CPU_GET_CLASS(cpu); |  | ||||||
|  |  | ||||||
|     /* Clear the interrupt flag now since we're processing |  | ||||||
|      * cpu->interrupt_request and cpu->exit_request. |  | ||||||
|      * Ensure zeroing happens before reading cpu->exit_request or |  | ||||||
|      * cpu->interrupt_request (see also smp_wmb in cpu_exit()) |  | ||||||
|      */ |  | ||||||
|     atomic_mb_set(&cpu->icount_decr.u16.high, 0); |  | ||||||
|  |  | ||||||
|     if (unlikely(atomic_read(&cpu->interrupt_request))) { |  | ||||||
|         int interrupt_request; |  | ||||||
|         qemu_mutex_lock_iothread(); |  | ||||||
|         interrupt_request = cpu->interrupt_request; |  | ||||||
|         if (unlikely(cpu->singlestep_enabled & SSTEP_NOIRQ)) { |  | ||||||
|             /* Mask out external interrupts for this step. */ |  | ||||||
|             interrupt_request &= ~CPU_INTERRUPT_SSTEP_MASK; |  | ||||||
|         } |  | ||||||
|         if (interrupt_request & CPU_INTERRUPT_DEBUG) { |  | ||||||
|             cpu->interrupt_request &= ~CPU_INTERRUPT_DEBUG; |  | ||||||
|             cpu->exception_index = EXCP_DEBUG; |  | ||||||
|             qemu_mutex_unlock_iothread(); |  | ||||||
|             return true; |  | ||||||
|         } |  | ||||||
|         if (replay_mode == REPLAY_MODE_PLAY && !replay_has_interrupt()) { |  | ||||||
|             /* Do nothing */ |  | ||||||
|         } else if (interrupt_request & CPU_INTERRUPT_HALT) { |  | ||||||
|             replay_interrupt(); |  | ||||||
|             cpu->interrupt_request &= ~CPU_INTERRUPT_HALT; |  | ||||||
|             cpu->halted = 1; |  | ||||||
|             cpu->exception_index = EXCP_HLT; |  | ||||||
|             qemu_mutex_unlock_iothread(); |  | ||||||
|             return true; |  | ||||||
|         } |  | ||||||
| #if defined(TARGET_I386) |  | ||||||
|         else if (interrupt_request & CPU_INTERRUPT_INIT) { |  | ||||||
|             X86CPU *x86_cpu = X86_CPU(cpu); |  | ||||||
|             CPUArchState *env = &x86_cpu->env; |  | ||||||
|             replay_interrupt(); |  | ||||||
|             cpu_svm_check_intercept_param(env, SVM_EXIT_INIT, 0, 0); |  | ||||||
|             do_cpu_init(x86_cpu); |  | ||||||
|             cpu->exception_index = EXCP_HALTED; |  | ||||||
|             qemu_mutex_unlock_iothread(); |  | ||||||
|             return true; |  | ||||||
|         } |  | ||||||
| #else |  | ||||||
|         else if (interrupt_request & CPU_INTERRUPT_RESET) { |  | ||||||
|             replay_interrupt(); |  | ||||||
|             cpu_reset(cpu); |  | ||||||
|             qemu_mutex_unlock_iothread(); |  | ||||||
|             return true; |  | ||||||
|         } |  | ||||||
| #endif |  | ||||||
|         /* The target hook has 3 exit conditions: |  | ||||||
|            False when the interrupt isn't processed, |  | ||||||
|            True when it is, and we should restart on a new TB, |  | ||||||
|            and via longjmp via cpu_loop_exit.  */ |  | ||||||
|         else { |  | ||||||
|             if (cc->cpu_exec_interrupt(cpu, interrupt_request)) { |  | ||||||
|                 replay_interrupt(); |  | ||||||
|                 *last_tb = NULL; |  | ||||||
|             } |  | ||||||
|             /* The target hook may have updated the 'cpu->interrupt_request'; |  | ||||||
|              * reload the 'interrupt_request' value */ |  | ||||||
|             interrupt_request = cpu->interrupt_request; |  | ||||||
|         } |  | ||||||
|         if (interrupt_request & CPU_INTERRUPT_EXITTB) { |  | ||||||
|             cpu->interrupt_request &= ~CPU_INTERRUPT_EXITTB; |  | ||||||
|             /* ensure that no TB jump will be modified as |  | ||||||
|                the program flow was changed */ |  | ||||||
|             *last_tb = NULL; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         /* If we exit via cpu_loop_exit/longjmp it is reset in cpu_exec */ |  | ||||||
|         qemu_mutex_unlock_iothread(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /* Finally, check if we need to exit to the main loop.  */ |  | ||||||
|     if (unlikely(atomic_read(&cpu->exit_request) |  | ||||||
|         || (use_icount && cpu->icount_decr.u16.low + cpu->icount_extra == 0))) { |  | ||||||
|         atomic_set(&cpu->exit_request, 0); |  | ||||||
|         cpu->exception_index = EXCP_INTERRUPT; |  | ||||||
|         return true; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return false; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static inline void cpu_loop_exec_tb(CPUState *cpu, TranslationBlock *tb, |  | ||||||
|                                     TranslationBlock **last_tb, int *tb_exit) |  | ||||||
| { |  | ||||||
|     uintptr_t ret; |  | ||||||
|     int32_t insns_left; |  | ||||||
|  |  | ||||||
|     trace_exec_tb(tb, tb->pc); |  | ||||||
|     ret = cpu_tb_exec(cpu, tb); |  | ||||||
|     tb = (TranslationBlock *)(ret & ~TB_EXIT_MASK); |  | ||||||
|     *tb_exit = ret & TB_EXIT_MASK; |  | ||||||
|     if (*tb_exit != TB_EXIT_REQUESTED) { |  | ||||||
|         *last_tb = tb; |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     *last_tb = NULL; |  | ||||||
|     insns_left = atomic_read(&cpu->icount_decr.u32); |  | ||||||
|     if (insns_left < 0) { |  | ||||||
|         /* Something asked us to stop executing chained TBs; just |  | ||||||
|          * continue round the main loop. Whatever requested the exit |  | ||||||
|          * will also have set something else (eg exit_request or |  | ||||||
|          * interrupt_request) which will be handled by |  | ||||||
|          * cpu_handle_interrupt.  cpu_handle_interrupt will also |  | ||||||
|          * clear cpu->icount_decr.u16.high. |  | ||||||
|          */ |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /* Instruction counter expired.  */ |  | ||||||
|     assert(use_icount); |  | ||||||
| #ifndef CONFIG_USER_ONLY |  | ||||||
|     /* Ensure global icount has gone forward */ |  | ||||||
|     cpu_update_icount(cpu); |  | ||||||
|     /* Refill decrementer and continue execution.  */ |  | ||||||
|     insns_left = MIN(0xffff, cpu->icount_budget); |  | ||||||
|     cpu->icount_decr.u16.low = insns_left; |  | ||||||
|     cpu->icount_extra = cpu->icount_budget - insns_left; |  | ||||||
|     if (!cpu->icount_extra) { |  | ||||||
|         /* Execute any remaining instructions, then let the main loop |  | ||||||
|          * handle the next event. |  | ||||||
|          */ |  | ||||||
|         if (insns_left > 0) { |  | ||||||
|             cpu_exec_nocache(cpu, insns_left, tb, false); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| #endif |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* main execution loop */ |  | ||||||
|  |  | ||||||
| int cpu_exec(CPUState *cpu) |  | ||||||
| { |  | ||||||
|     CPUClass *cc = CPU_GET_CLASS(cpu); |  | ||||||
|     int ret; |  | ||||||
|     SyncClocks sc = { 0 }; |  | ||||||
|  |  | ||||||
|     /* replay_interrupt may need current_cpu */ |  | ||||||
|     current_cpu = cpu; |  | ||||||
|  |  | ||||||
|     if (cpu_handle_halt(cpu)) { |  | ||||||
|         return EXCP_HALTED; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     rcu_read_lock(); |  | ||||||
|  |  | ||||||
|     cc->cpu_exec_enter(cpu); |  | ||||||
|  |  | ||||||
|     /* Calculate difference between guest clock and host clock. |  | ||||||
|      * This delay includes the delay of the last cycle, so |  | ||||||
|      * what we have to do is sleep until it is 0. As for the |  | ||||||
|      * advance/delay we gain here, we try to fix it next time. |  | ||||||
|      */ |  | ||||||
|     init_delay_params(&sc, cpu); |  | ||||||
|  |  | ||||||
|     /* prepare setjmp context for exception handling */ |  | ||||||
|     if (sigsetjmp(cpu->jmp_env, 0) != 0) { |  | ||||||
| #if defined(__clang__) || !QEMU_GNUC_PREREQ(4, 6) |  | ||||||
|         /* Some compilers wrongly smash all local variables after |  | ||||||
|          * siglongjmp. There were bug reports for gcc 4.5.0 and clang. |  | ||||||
|          * Reload essential local variables here for those compilers. |  | ||||||
|          * Newer versions of gcc would complain about this code (-Wclobbered). */ |  | ||||||
|         cpu = current_cpu; |  | ||||||
|         cc = CPU_GET_CLASS(cpu); |  | ||||||
| #else /* buggy compiler */ |  | ||||||
|         /* Assert that the compiler does not smash local variables. */ |  | ||||||
|         g_assert(cpu == current_cpu); |  | ||||||
|         g_assert(cc == CPU_GET_CLASS(cpu)); |  | ||||||
| #endif /* buggy compiler */ |  | ||||||
|         cpu->can_do_io = 1; |  | ||||||
|         tb_lock_reset(); |  | ||||||
|         if (qemu_mutex_iothread_locked()) { |  | ||||||
|             qemu_mutex_unlock_iothread(); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /* if an exception is pending, we execute it here */ |  | ||||||
|     while (!cpu_handle_exception(cpu, &ret)) { |  | ||||||
|         TranslationBlock *last_tb = NULL; |  | ||||||
|         int tb_exit = 0; |  | ||||||
|  |  | ||||||
|         while (!cpu_handle_interrupt(cpu, &last_tb)) { |  | ||||||
|             uint32_t cflags = cpu->cflags_next_tb; |  | ||||||
|             TranslationBlock *tb; |  | ||||||
|  |  | ||||||
|             /* When requested, use an exact setting for cflags for the next |  | ||||||
|                execution.  This is used for icount, precise smc, and stop- |  | ||||||
|                after-access watchpoints.  Since this request should never |  | ||||||
|                have CF_INVALID set, -1 is a convenient invalid value that |  | ||||||
|                does not require tcg headers for cpu_common_reset.  */ |  | ||||||
|             if (cflags == -1) { |  | ||||||
|                 cflags = curr_cflags(); |  | ||||||
|             } else { |  | ||||||
|                 cpu->cflags_next_tb = -1; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             tb = tb_find(cpu, last_tb, tb_exit, cflags); |  | ||||||
|             cpu_loop_exec_tb(cpu, tb, &last_tb, &tb_exit); |  | ||||||
|             /* Try to align the host and virtual clocks |  | ||||||
|                if the guest is in advance */ |  | ||||||
|             align_clocks(&sc, cpu); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     cc->cpu_exec_exit(cpu); |  | ||||||
|     rcu_read_unlock(); |  | ||||||
|  |  | ||||||
|     return ret; |  | ||||||
| } |  | ||||||
							
								
								
									
										1121
									
								
								accel/tcg/cputlb.c
									
									
									
									
									
								
							
							
						
						
									
										1121
									
								
								accel/tcg/cputlb.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,435 +0,0 @@ | |||||||
| /* |  | ||||||
|  *  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 |  | ||||||
| @@ -1,92 +0,0 @@ | |||||||
| /* |  | ||||||
|  * QEMU System Emulator, accelerator interfaces |  | ||||||
|  * |  | ||||||
|  * Copyright (c) 2003-2008 Fabrice Bellard |  | ||||||
|  * Copyright (c) 2014 Red Hat Inc. |  | ||||||
|  * |  | ||||||
|  * 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 "sysemu/accel.h" |  | ||||||
| #include "sysemu/sysemu.h" |  | ||||||
| #include "qom/object.h" |  | ||||||
| #include "qemu-common.h" |  | ||||||
| #include "qom/cpu.h" |  | ||||||
| #include "sysemu/cpus.h" |  | ||||||
| #include "qemu/main-loop.h" |  | ||||||
|  |  | ||||||
| unsigned long tcg_tb_size; |  | ||||||
|  |  | ||||||
| #ifndef CONFIG_USER_ONLY |  | ||||||
| /* mask must never be zero, except for A20 change call */ |  | ||||||
| static void tcg_handle_interrupt(CPUState *cpu, int mask) |  | ||||||
| { |  | ||||||
|     int old_mask; |  | ||||||
|     g_assert(qemu_mutex_iothread_locked()); |  | ||||||
|  |  | ||||||
|     old_mask = cpu->interrupt_request; |  | ||||||
|     cpu->interrupt_request |= mask; |  | ||||||
|  |  | ||||||
|     /* |  | ||||||
|      * If called from iothread context, wake the target cpu in |  | ||||||
|      * case its halted. |  | ||||||
|      */ |  | ||||||
|     if (!qemu_cpu_is_self(cpu)) { |  | ||||||
|         qemu_cpu_kick(cpu); |  | ||||||
|     } else { |  | ||||||
|         cpu->icount_decr.u16.high = -1; |  | ||||||
|         if (use_icount && |  | ||||||
|             !cpu->can_do_io |  | ||||||
|             && (mask & ~old_mask) != 0) { |  | ||||||
|             cpu_abort(cpu, "Raised interrupt while not in I/O function"); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| static int tcg_init(MachineState *ms) |  | ||||||
| { |  | ||||||
|     tcg_exec_init(tcg_tb_size * 1024 * 1024); |  | ||||||
|     cpu_interrupt_handler = tcg_handle_interrupt; |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void tcg_accel_class_init(ObjectClass *oc, void *data) |  | ||||||
| { |  | ||||||
|     AccelClass *ac = ACCEL_CLASS(oc); |  | ||||||
|     ac->name = "tcg"; |  | ||||||
|     ac->init_machine = tcg_init; |  | ||||||
|     ac->allowed = &tcg_allowed; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #define TYPE_TCG_ACCEL ACCEL_CLASS_NAME("tcg") |  | ||||||
|  |  | ||||||
| static const TypeInfo tcg_accel_type = { |  | ||||||
|     .name = TYPE_TCG_ACCEL, |  | ||||||
|     .parent = TYPE_ACCEL, |  | ||||||
|     .class_init = tcg_accel_class_init, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static void register_accel_types(void) |  | ||||||
| { |  | ||||||
|     type_register_static(&tcg_accel_type); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type_init(register_accel_types); |  | ||||||
| @@ -1,997 +0,0 @@ | |||||||
| /* |  | ||||||
|  * Generic vectorized operation runtime |  | ||||||
|  * |  | ||||||
|  * Copyright (c) 2018 Linaro |  | ||||||
|  * |  | ||||||
|  * 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 "qemu/host-utils.h" |  | ||||||
| #include "cpu.h" |  | ||||||
| #include "exec/helper-proto.h" |  | ||||||
| #include "tcg-gvec-desc.h" |  | ||||||
|  |  | ||||||
|  |  | ||||||
| /* Virtually all hosts support 16-byte vectors.  Those that don't can emulate |  | ||||||
|  * them via GCC's generic vector extension.  This turns out to be simpler and |  | ||||||
|  * more reliable than getting the compiler to autovectorize. |  | ||||||
|  * |  | ||||||
|  * In tcg-op-gvec.c, we asserted that both the size and alignment of the data |  | ||||||
|  * are multiples of 16. |  | ||||||
|  * |  | ||||||
|  * When the compiler does not support all of the operations we require, the |  | ||||||
|  * loops are written so that we can always fall back on the base types. |  | ||||||
|  */ |  | ||||||
| #ifdef CONFIG_VECTOR16 |  | ||||||
| typedef uint8_t vec8 __attribute__((vector_size(16))); |  | ||||||
| typedef uint16_t vec16 __attribute__((vector_size(16))); |  | ||||||
| typedef uint32_t vec32 __attribute__((vector_size(16))); |  | ||||||
| typedef uint64_t vec64 __attribute__((vector_size(16))); |  | ||||||
|  |  | ||||||
| typedef int8_t svec8 __attribute__((vector_size(16))); |  | ||||||
| typedef int16_t svec16 __attribute__((vector_size(16))); |  | ||||||
| typedef int32_t svec32 __attribute__((vector_size(16))); |  | ||||||
| typedef int64_t svec64 __attribute__((vector_size(16))); |  | ||||||
|  |  | ||||||
| #define DUP16(X)  { X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X } |  | ||||||
| #define DUP8(X)   { X, X, X, X, X, X, X, X } |  | ||||||
| #define DUP4(X)   { X, X, X, X } |  | ||||||
| #define DUP2(X)   { X, X } |  | ||||||
| #else |  | ||||||
| typedef uint8_t vec8; |  | ||||||
| typedef uint16_t vec16; |  | ||||||
| typedef uint32_t vec32; |  | ||||||
| typedef uint64_t vec64; |  | ||||||
|  |  | ||||||
| typedef int8_t svec8; |  | ||||||
| typedef int16_t svec16; |  | ||||||
| typedef int32_t svec32; |  | ||||||
| typedef int64_t svec64; |  | ||||||
|  |  | ||||||
| #define DUP16(X)  X |  | ||||||
| #define DUP8(X)   X |  | ||||||
| #define DUP4(X)   X |  | ||||||
| #define DUP2(X)   X |  | ||||||
| #endif /* CONFIG_VECTOR16 */ |  | ||||||
|  |  | ||||||
| static inline void clear_high(void *d, intptr_t oprsz, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t maxsz = simd_maxsz(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     if (unlikely(maxsz > oprsz)) { |  | ||||||
|         for (i = oprsz; i < maxsz; i += sizeof(uint64_t)) { |  | ||||||
|             *(uint64_t *)(d + i) = 0; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_add8)(void *d, void *a, void *b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(vec8)) { |  | ||||||
|         *(vec8 *)(d + i) = *(vec8 *)(a + i) + *(vec8 *)(b + i); |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_add16)(void *d, void *a, void *b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(vec16)) { |  | ||||||
|         *(vec16 *)(d + i) = *(vec16 *)(a + i) + *(vec16 *)(b + i); |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_add32)(void *d, void *a, void *b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(vec32)) { |  | ||||||
|         *(vec32 *)(d + i) = *(vec32 *)(a + i) + *(vec32 *)(b + i); |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_add64)(void *d, void *a, void *b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(vec64)) { |  | ||||||
|         *(vec64 *)(d + i) = *(vec64 *)(a + i) + *(vec64 *)(b + i); |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_adds8)(void *d, void *a, uint64_t b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     vec8 vecb = (vec8)DUP16(b); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(vec8)) { |  | ||||||
|         *(vec8 *)(d + i) = *(vec8 *)(a + i) + vecb; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_adds16)(void *d, void *a, uint64_t b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     vec16 vecb = (vec16)DUP8(b); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(vec16)) { |  | ||||||
|         *(vec16 *)(d + i) = *(vec16 *)(a + i) + vecb; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_adds32)(void *d, void *a, uint64_t b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     vec32 vecb = (vec32)DUP4(b); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(vec32)) { |  | ||||||
|         *(vec32 *)(d + i) = *(vec32 *)(a + i) + vecb; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_adds64)(void *d, void *a, uint64_t b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     vec64 vecb = (vec64)DUP2(b); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(vec64)) { |  | ||||||
|         *(vec64 *)(d + i) = *(vec64 *)(a + i) + vecb; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_sub8)(void *d, void *a, void *b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(vec8)) { |  | ||||||
|         *(vec8 *)(d + i) = *(vec8 *)(a + i) - *(vec8 *)(b + i); |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_sub16)(void *d, void *a, void *b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(vec16)) { |  | ||||||
|         *(vec16 *)(d + i) = *(vec16 *)(a + i) - *(vec16 *)(b + i); |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_sub32)(void *d, void *a, void *b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(vec32)) { |  | ||||||
|         *(vec32 *)(d + i) = *(vec32 *)(a + i) - *(vec32 *)(b + i); |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_sub64)(void *d, void *a, void *b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(vec64)) { |  | ||||||
|         *(vec64 *)(d + i) = *(vec64 *)(a + i) - *(vec64 *)(b + i); |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_subs8)(void *d, void *a, uint64_t b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     vec8 vecb = (vec8)DUP16(b); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(vec8)) { |  | ||||||
|         *(vec8 *)(d + i) = *(vec8 *)(a + i) - vecb; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_subs16)(void *d, void *a, uint64_t b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     vec16 vecb = (vec16)DUP8(b); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(vec16)) { |  | ||||||
|         *(vec16 *)(d + i) = *(vec16 *)(a + i) - vecb; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_subs32)(void *d, void *a, uint64_t b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     vec32 vecb = (vec32)DUP4(b); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(vec32)) { |  | ||||||
|         *(vec32 *)(d + i) = *(vec32 *)(a + i) - vecb; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_subs64)(void *d, void *a, uint64_t b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     vec64 vecb = (vec64)DUP2(b); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(vec64)) { |  | ||||||
|         *(vec64 *)(d + i) = *(vec64 *)(a + i) - vecb; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_mul8)(void *d, void *a, void *b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(vec8)) { |  | ||||||
|         *(vec8 *)(d + i) = *(vec8 *)(a + i) * *(vec8 *)(b + i); |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_mul16)(void *d, void *a, void *b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(vec16)) { |  | ||||||
|         *(vec16 *)(d + i) = *(vec16 *)(a + i) * *(vec16 *)(b + i); |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_mul32)(void *d, void *a, void *b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(vec32)) { |  | ||||||
|         *(vec32 *)(d + i) = *(vec32 *)(a + i) * *(vec32 *)(b + i); |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_mul64)(void *d, void *a, void *b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(vec64)) { |  | ||||||
|         *(vec64 *)(d + i) = *(vec64 *)(a + i) * *(vec64 *)(b + i); |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_muls8)(void *d, void *a, uint64_t b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     vec8 vecb = (vec8)DUP16(b); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(vec8)) { |  | ||||||
|         *(vec8 *)(d + i) = *(vec8 *)(a + i) * vecb; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_muls16)(void *d, void *a, uint64_t b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     vec16 vecb = (vec16)DUP8(b); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(vec16)) { |  | ||||||
|         *(vec16 *)(d + i) = *(vec16 *)(a + i) * vecb; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_muls32)(void *d, void *a, uint64_t b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     vec32 vecb = (vec32)DUP4(b); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(vec32)) { |  | ||||||
|         *(vec32 *)(d + i) = *(vec32 *)(a + i) * vecb; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_muls64)(void *d, void *a, uint64_t b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     vec64 vecb = (vec64)DUP2(b); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(vec64)) { |  | ||||||
|         *(vec64 *)(d + i) = *(vec64 *)(a + i) * vecb; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_neg8)(void *d, void *a, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(vec8)) { |  | ||||||
|         *(vec8 *)(d + i) = -*(vec8 *)(a + i); |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_neg16)(void *d, void *a, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(vec16)) { |  | ||||||
|         *(vec16 *)(d + i) = -*(vec16 *)(a + i); |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_neg32)(void *d, void *a, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(vec32)) { |  | ||||||
|         *(vec32 *)(d + i) = -*(vec32 *)(a + i); |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_neg64)(void *d, void *a, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(vec64)) { |  | ||||||
|         *(vec64 *)(d + i) = -*(vec64 *)(a + i); |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_mov)(void *d, void *a, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|  |  | ||||||
|     memcpy(d, a, oprsz); |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_dup64)(void *d, uint32_t desc, uint64_t c) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     if (c == 0) { |  | ||||||
|         oprsz = 0; |  | ||||||
|     } else { |  | ||||||
|         for (i = 0; i < oprsz; i += sizeof(uint64_t)) { |  | ||||||
|             *(uint64_t *)(d + i) = c; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_dup32)(void *d, uint32_t desc, uint32_t c) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     if (c == 0) { |  | ||||||
|         oprsz = 0; |  | ||||||
|     } else { |  | ||||||
|         for (i = 0; i < oprsz; i += sizeof(uint32_t)) { |  | ||||||
|             *(uint32_t *)(d + i) = c; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_dup16)(void *d, uint32_t desc, uint32_t c) |  | ||||||
| { |  | ||||||
|     HELPER(gvec_dup32)(d, desc, 0x00010001 * (c & 0xffff)); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_dup8)(void *d, uint32_t desc, uint32_t c) |  | ||||||
| { |  | ||||||
|     HELPER(gvec_dup32)(d, desc, 0x01010101 * (c & 0xff)); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_not)(void *d, void *a, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(vec64)) { |  | ||||||
|         *(vec64 *)(d + i) = ~*(vec64 *)(a + i); |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_and)(void *d, void *a, void *b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(vec64)) { |  | ||||||
|         *(vec64 *)(d + i) = *(vec64 *)(a + i) & *(vec64 *)(b + i); |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_or)(void *d, void *a, void *b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(vec64)) { |  | ||||||
|         *(vec64 *)(d + i) = *(vec64 *)(a + i) | *(vec64 *)(b + i); |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_xor)(void *d, void *a, void *b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(vec64)) { |  | ||||||
|         *(vec64 *)(d + i) = *(vec64 *)(a + i) ^ *(vec64 *)(b + i); |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_andc)(void *d, void *a, void *b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(vec64)) { |  | ||||||
|         *(vec64 *)(d + i) = *(vec64 *)(a + i) &~ *(vec64 *)(b + i); |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_orc)(void *d, void *a, void *b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(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) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     vec64 vecb = (vec64)DUP2(b); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(vec64)) { |  | ||||||
|         *(vec64 *)(d + i) = *(vec64 *)(a + i) & vecb; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_xors)(void *d, void *a, uint64_t b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     vec64 vecb = (vec64)DUP2(b); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(vec64)) { |  | ||||||
|         *(vec64 *)(d + i) = *(vec64 *)(a + i) ^ vecb; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_ors)(void *d, void *a, uint64_t b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     vec64 vecb = (vec64)DUP2(b); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(vec64)) { |  | ||||||
|         *(vec64 *)(d + i) = *(vec64 *)(a + i) | vecb; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_shl8i)(void *d, void *a, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     int shift = simd_data(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(vec8)) { |  | ||||||
|         *(vec8 *)(d + i) = *(vec8 *)(a + i) << shift; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_shl16i)(void *d, void *a, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     int shift = simd_data(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(vec16)) { |  | ||||||
|         *(vec16 *)(d + i) = *(vec16 *)(a + i) << shift; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_shl32i)(void *d, void *a, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     int shift = simd_data(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(vec32)) { |  | ||||||
|         *(vec32 *)(d + i) = *(vec32 *)(a + i) << shift; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_shl64i)(void *d, void *a, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     int shift = simd_data(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(vec64)) { |  | ||||||
|         *(vec64 *)(d + i) = *(vec64 *)(a + i) << shift; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_shr8i)(void *d, void *a, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     int shift = simd_data(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(vec8)) { |  | ||||||
|         *(vec8 *)(d + i) = *(vec8 *)(a + i) >> shift; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_shr16i)(void *d, void *a, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     int shift = simd_data(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(vec16)) { |  | ||||||
|         *(vec16 *)(d + i) = *(vec16 *)(a + i) >> shift; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_shr32i)(void *d, void *a, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     int shift = simd_data(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(vec32)) { |  | ||||||
|         *(vec32 *)(d + i) = *(vec32 *)(a + i) >> shift; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_shr64i)(void *d, void *a, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     int shift = simd_data(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(vec64)) { |  | ||||||
|         *(vec64 *)(d + i) = *(vec64 *)(a + i) >> shift; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_sar8i)(void *d, void *a, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     int shift = simd_data(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(vec8)) { |  | ||||||
|         *(svec8 *)(d + i) = *(svec8 *)(a + i) >> shift; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_sar16i)(void *d, void *a, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     int shift = simd_data(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(vec16)) { |  | ||||||
|         *(svec16 *)(d + i) = *(svec16 *)(a + i) >> shift; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_sar32i)(void *d, void *a, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     int shift = simd_data(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(vec32)) { |  | ||||||
|         *(svec32 *)(d + i) = *(svec32 *)(a + i) >> shift; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_sar64i)(void *d, void *a, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     int shift = simd_data(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(vec64)) { |  | ||||||
|         *(svec64 *)(d + i) = *(svec64 *)(a + i) >> shift; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* If vectors are enabled, the compiler fills in -1 for true. |  | ||||||
|    Otherwise, we must take care of this by hand.  */ |  | ||||||
| #ifdef CONFIG_VECTOR16 |  | ||||||
| # define DO_CMP0(X)  X |  | ||||||
| #else |  | ||||||
| # define DO_CMP0(X)  -(X) |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| #define DO_CMP1(NAME, TYPE, OP)                                            \ |  | ||||||
| void HELPER(NAME)(void *d, void *a, void *b, uint32_t desc)                \ |  | ||||||
| {                                                                          \ |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc);                                     \ |  | ||||||
|     intptr_t i;                                                            \ |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(vec64)) {                           \ |  | ||||||
|         *(TYPE *)(d + i) = DO_CMP0(*(TYPE *)(a + i) OP *(TYPE *)(b + i));  \ |  | ||||||
|     }                                                                      \ |  | ||||||
|     clear_high(d, oprsz, desc);                                            \ |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #define DO_CMP2(SZ) \ |  | ||||||
|     DO_CMP1(gvec_eq##SZ, vec##SZ, ==)    \ |  | ||||||
|     DO_CMP1(gvec_ne##SZ, vec##SZ, !=)    \ |  | ||||||
|     DO_CMP1(gvec_lt##SZ, svec##SZ, <)    \ |  | ||||||
|     DO_CMP1(gvec_le##SZ, svec##SZ, <=)   \ |  | ||||||
|     DO_CMP1(gvec_ltu##SZ, vec##SZ, <)    \ |  | ||||||
|     DO_CMP1(gvec_leu##SZ, vec##SZ, <=) |  | ||||||
|  |  | ||||||
| DO_CMP2(8) |  | ||||||
| DO_CMP2(16) |  | ||||||
| DO_CMP2(32) |  | ||||||
| DO_CMP2(64) |  | ||||||
|  |  | ||||||
| #undef DO_CMP0 |  | ||||||
| #undef DO_CMP1 |  | ||||||
| #undef DO_CMP2 |  | ||||||
|  |  | ||||||
| void HELPER(gvec_ssadd8)(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)) { |  | ||||||
|         int r = *(int8_t *)(a + i) + *(int8_t *)(b + i); |  | ||||||
|         if (r > INT8_MAX) { |  | ||||||
|             r = INT8_MAX; |  | ||||||
|         } else if (r < INT8_MIN) { |  | ||||||
|             r = INT8_MIN; |  | ||||||
|         } |  | ||||||
|         *(int8_t *)(d + i) = r; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_ssadd16)(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)) { |  | ||||||
|         int r = *(int16_t *)(a + i) + *(int16_t *)(b + i); |  | ||||||
|         if (r > INT16_MAX) { |  | ||||||
|             r = INT16_MAX; |  | ||||||
|         } else if (r < INT16_MIN) { |  | ||||||
|             r = INT16_MIN; |  | ||||||
|         } |  | ||||||
|         *(int16_t *)(d + i) = r; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_ssadd32)(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 ai = *(int32_t *)(a + i); |  | ||||||
|         int32_t bi = *(int32_t *)(b + i); |  | ||||||
|         int32_t di = ai + bi; |  | ||||||
|         if (((di ^ ai) &~ (ai ^ bi)) < 0) { |  | ||||||
|             /* Signed overflow.  */ |  | ||||||
|             di = (di < 0 ? INT32_MAX : INT32_MIN); |  | ||||||
|         } |  | ||||||
|         *(int32_t *)(d + i) = di; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_ssadd64)(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 ai = *(int64_t *)(a + i); |  | ||||||
|         int64_t bi = *(int64_t *)(b + i); |  | ||||||
|         int64_t di = ai + bi; |  | ||||||
|         if (((di ^ ai) &~ (ai ^ bi)) < 0) { |  | ||||||
|             /* Signed overflow.  */ |  | ||||||
|             di = (di < 0 ? INT64_MAX : INT64_MIN); |  | ||||||
|         } |  | ||||||
|         *(int64_t *)(d + i) = di; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_sssub8)(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)) { |  | ||||||
|         int r = *(int8_t *)(a + i) - *(int8_t *)(b + i); |  | ||||||
|         if (r > INT8_MAX) { |  | ||||||
|             r = INT8_MAX; |  | ||||||
|         } else if (r < INT8_MIN) { |  | ||||||
|             r = INT8_MIN; |  | ||||||
|         } |  | ||||||
|         *(uint8_t *)(d + i) = r; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_sssub16)(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)) { |  | ||||||
|         int r = *(int16_t *)(a + i) - *(int16_t *)(b + i); |  | ||||||
|         if (r > INT16_MAX) { |  | ||||||
|             r = INT16_MAX; |  | ||||||
|         } else if (r < INT16_MIN) { |  | ||||||
|             r = INT16_MIN; |  | ||||||
|         } |  | ||||||
|         *(int16_t *)(d + i) = r; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_sssub32)(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 ai = *(int32_t *)(a + i); |  | ||||||
|         int32_t bi = *(int32_t *)(b + i); |  | ||||||
|         int32_t di = ai - bi; |  | ||||||
|         if (((di ^ ai) & (ai ^ bi)) < 0) { |  | ||||||
|             /* Signed overflow.  */ |  | ||||||
|             di = (di < 0 ? INT32_MAX : INT32_MIN); |  | ||||||
|         } |  | ||||||
|         *(int32_t *)(d + i) = di; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_sssub64)(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 ai = *(int64_t *)(a + i); |  | ||||||
|         int64_t bi = *(int64_t *)(b + i); |  | ||||||
|         int64_t di = ai - bi; |  | ||||||
|         if (((di ^ ai) & (ai ^ bi)) < 0) { |  | ||||||
|             /* Signed overflow.  */ |  | ||||||
|             di = (di < 0 ? INT64_MAX : INT64_MIN); |  | ||||||
|         } |  | ||||||
|         *(int64_t *)(d + i) = di; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_usadd8)(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)) { |  | ||||||
|         unsigned r = *(uint8_t *)(a + i) + *(uint8_t *)(b + i); |  | ||||||
|         if (r > UINT8_MAX) { |  | ||||||
|             r = UINT8_MAX; |  | ||||||
|         } |  | ||||||
|         *(uint8_t *)(d + i) = r; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_usadd16)(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)) { |  | ||||||
|         unsigned r = *(uint16_t *)(a + i) + *(uint16_t *)(b + i); |  | ||||||
|         if (r > UINT16_MAX) { |  | ||||||
|             r = UINT16_MAX; |  | ||||||
|         } |  | ||||||
|         *(uint16_t *)(d + i) = r; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_usadd32)(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 ai = *(uint32_t *)(a + i); |  | ||||||
|         uint32_t bi = *(uint32_t *)(b + i); |  | ||||||
|         uint32_t di = ai + bi; |  | ||||||
|         if (di < ai) { |  | ||||||
|             di = UINT32_MAX; |  | ||||||
|         } |  | ||||||
|         *(uint32_t *)(d + i) = di; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_usadd64)(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 ai = *(uint64_t *)(a + i); |  | ||||||
|         uint64_t bi = *(uint64_t *)(b + i); |  | ||||||
|         uint64_t di = ai + bi; |  | ||||||
|         if (di < ai) { |  | ||||||
|             di = UINT64_MAX; |  | ||||||
|         } |  | ||||||
|         *(uint64_t *)(d + i) = di; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_ussub8)(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)) { |  | ||||||
|         int r = *(uint8_t *)(a + i) - *(uint8_t *)(b + i); |  | ||||||
|         if (r < 0) { |  | ||||||
|             r = 0; |  | ||||||
|         } |  | ||||||
|         *(uint8_t *)(d + i) = r; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_ussub16)(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)) { |  | ||||||
|         int r = *(uint16_t *)(a + i) - *(uint16_t *)(b + i); |  | ||||||
|         if (r < 0) { |  | ||||||
|             r = 0; |  | ||||||
|         } |  | ||||||
|         *(uint16_t *)(d + i) = r; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_ussub32)(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 ai = *(uint32_t *)(a + i); |  | ||||||
|         uint32_t bi = *(uint32_t *)(b + i); |  | ||||||
|         uint32_t di = ai - bi; |  | ||||||
|         if (ai < bi) { |  | ||||||
|             di = 0; |  | ||||||
|         } |  | ||||||
|         *(uint32_t *)(d + i) = di; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_ussub64)(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 ai = *(uint64_t *)(a + i); |  | ||||||
|         uint64_t bi = *(uint64_t *)(b + i); |  | ||||||
|         uint64_t di = ai - bi; |  | ||||||
|         if (ai < bi) { |  | ||||||
|             di = 0; |  | ||||||
|         } |  | ||||||
|         *(uint64_t *)(d + i) = di; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
| @@ -1,169 +0,0 @@ | |||||||
| /* |  | ||||||
|  * Tiny Code Generator for QEMU |  | ||||||
|  * |  | ||||||
|  * Copyright (c) 2008 Fabrice Bellard |  | ||||||
|  * |  | ||||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy |  | ||||||
|  * of this software and associated documentation files (the "Software"), to deal |  | ||||||
|  * in the Software without restriction, including without limitation the rights |  | ||||||
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |  | ||||||
|  * copies of the Software, and to permit persons to whom the Software is |  | ||||||
|  * furnished to do so, subject to the following conditions: |  | ||||||
|  * |  | ||||||
|  * The above copyright notice and this permission notice shall be included in |  | ||||||
|  * all copies or substantial portions of the Software. |  | ||||||
|  * |  | ||||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |  | ||||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |  | ||||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |  | ||||||
|  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |  | ||||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |  | ||||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |  | ||||||
|  * THE SOFTWARE. |  | ||||||
|  */ |  | ||||||
| #include "qemu/osdep.h" |  | ||||||
| #include "qemu/host-utils.h" |  | ||||||
| #include "cpu.h" |  | ||||||
| #include "exec/helper-proto.h" |  | ||||||
| #include "exec/cpu_ldst.h" |  | ||||||
| #include "exec/exec-all.h" |  | ||||||
| #include "exec/tb-lookup.h" |  | ||||||
| #include "disas/disas.h" |  | ||||||
| #include "exec/log.h" |  | ||||||
|  |  | ||||||
| /* 32-bit helpers */ |  | ||||||
|  |  | ||||||
| int32_t HELPER(div_i32)(int32_t arg1, int32_t arg2) |  | ||||||
| { |  | ||||||
|     return arg1 / arg2; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int32_t HELPER(rem_i32)(int32_t arg1, int32_t arg2) |  | ||||||
| { |  | ||||||
|     return arg1 % arg2; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| uint32_t HELPER(divu_i32)(uint32_t arg1, uint32_t arg2) |  | ||||||
| { |  | ||||||
|     return arg1 / arg2; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| uint32_t HELPER(remu_i32)(uint32_t arg1, uint32_t arg2) |  | ||||||
| { |  | ||||||
|     return arg1 % arg2; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* 64-bit helpers */ |  | ||||||
|  |  | ||||||
| uint64_t HELPER(shl_i64)(uint64_t arg1, uint64_t arg2) |  | ||||||
| { |  | ||||||
|     return arg1 << arg2; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| uint64_t HELPER(shr_i64)(uint64_t arg1, uint64_t arg2) |  | ||||||
| { |  | ||||||
|     return arg1 >> arg2; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int64_t HELPER(sar_i64)(int64_t arg1, int64_t arg2) |  | ||||||
| { |  | ||||||
|     return arg1 >> arg2; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int64_t HELPER(div_i64)(int64_t arg1, int64_t arg2) |  | ||||||
| { |  | ||||||
|     return arg1 / arg2; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int64_t HELPER(rem_i64)(int64_t arg1, int64_t arg2) |  | ||||||
| { |  | ||||||
|     return arg1 % arg2; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| uint64_t HELPER(divu_i64)(uint64_t arg1, uint64_t arg2) |  | ||||||
| { |  | ||||||
|     return arg1 / arg2; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| uint64_t HELPER(remu_i64)(uint64_t arg1, uint64_t arg2) |  | ||||||
| { |  | ||||||
|     return arg1 % arg2; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| uint64_t HELPER(muluh_i64)(uint64_t arg1, uint64_t arg2) |  | ||||||
| { |  | ||||||
|     uint64_t l, h; |  | ||||||
|     mulu64(&l, &h, arg1, arg2); |  | ||||||
|     return h; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int64_t HELPER(mulsh_i64)(int64_t arg1, int64_t arg2) |  | ||||||
| { |  | ||||||
|     uint64_t l, h; |  | ||||||
|     muls64(&l, &h, arg1, arg2); |  | ||||||
|     return h; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| uint32_t HELPER(clz_i32)(uint32_t arg, uint32_t zero_val) |  | ||||||
| { |  | ||||||
|     return arg ? clz32(arg) : zero_val; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| uint32_t HELPER(ctz_i32)(uint32_t arg, uint32_t zero_val) |  | ||||||
| { |  | ||||||
|     return arg ? ctz32(arg) : zero_val; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| uint64_t HELPER(clz_i64)(uint64_t arg, uint64_t zero_val) |  | ||||||
| { |  | ||||||
|     return arg ? clz64(arg) : zero_val; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| uint64_t HELPER(ctz_i64)(uint64_t arg, uint64_t zero_val) |  | ||||||
| { |  | ||||||
|     return arg ? ctz64(arg) : zero_val; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| uint32_t HELPER(clrsb_i32)(uint32_t arg) |  | ||||||
| { |  | ||||||
|     return clrsb32(arg); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| uint64_t HELPER(clrsb_i64)(uint64_t arg) |  | ||||||
| { |  | ||||||
|     return clrsb64(arg); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| uint32_t HELPER(ctpop_i32)(uint32_t arg) |  | ||||||
| { |  | ||||||
|     return ctpop32(arg); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| uint64_t HELPER(ctpop_i64)(uint64_t arg) |  | ||||||
| { |  | ||||||
|     return ctpop64(arg); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void *HELPER(lookup_tb_ptr)(CPUArchState *env) |  | ||||||
| { |  | ||||||
|     CPUState *cpu = ENV_GET_CPU(env); |  | ||||||
|     TranslationBlock *tb; |  | ||||||
|     target_ulong cs_base, pc; |  | ||||||
|     uint32_t flags; |  | ||||||
|  |  | ||||||
|     tb = tb_lookup__cpu_state(cpu, &pc, &cs_base, &flags, curr_cflags()); |  | ||||||
|     if (tb == NULL) { |  | ||||||
|         return tcg_ctx->code_gen_epilogue; |  | ||||||
|     } |  | ||||||
|     qemu_log_mask_and_addr(CPU_LOG_EXEC, pc, |  | ||||||
|                            "Chain %d: %p [" |  | ||||||
|                            TARGET_FMT_lx "/" TARGET_FMT_lx "/%#x] %s\n", |  | ||||||
|                            cpu->cpu_index, tb->tc.ptr, cs_base, pc, flags, |  | ||||||
|                            lookup_symbol(pc)); |  | ||||||
|     return tb->tc.ptr; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(exit_atomic)(CPUArchState *env) |  | ||||||
| { |  | ||||||
|     cpu_loop_exit_atomic(ENV_GET_CPU(env), GETPC()); |  | ||||||
| } |  | ||||||
| @@ -1,254 +0,0 @@ | |||||||
| DEF_HELPER_FLAGS_2(div_i32, TCG_CALL_NO_RWG_SE, s32, s32, s32) |  | ||||||
| DEF_HELPER_FLAGS_2(rem_i32, TCG_CALL_NO_RWG_SE, s32, s32, s32) |  | ||||||
| DEF_HELPER_FLAGS_2(divu_i32, TCG_CALL_NO_RWG_SE, i32, i32, i32) |  | ||||||
| DEF_HELPER_FLAGS_2(remu_i32, TCG_CALL_NO_RWG_SE, i32, i32, i32) |  | ||||||
|  |  | ||||||
| DEF_HELPER_FLAGS_2(div_i64, TCG_CALL_NO_RWG_SE, s64, s64, s64) |  | ||||||
| DEF_HELPER_FLAGS_2(rem_i64, TCG_CALL_NO_RWG_SE, s64, s64, s64) |  | ||||||
| DEF_HELPER_FLAGS_2(divu_i64, TCG_CALL_NO_RWG_SE, i64, i64, i64) |  | ||||||
| DEF_HELPER_FLAGS_2(remu_i64, TCG_CALL_NO_RWG_SE, i64, i64, i64) |  | ||||||
|  |  | ||||||
| DEF_HELPER_FLAGS_2(shl_i64, TCG_CALL_NO_RWG_SE, i64, i64, i64) |  | ||||||
| DEF_HELPER_FLAGS_2(shr_i64, TCG_CALL_NO_RWG_SE, i64, i64, i64) |  | ||||||
| DEF_HELPER_FLAGS_2(sar_i64, TCG_CALL_NO_RWG_SE, s64, s64, s64) |  | ||||||
|  |  | ||||||
| DEF_HELPER_FLAGS_2(mulsh_i64, TCG_CALL_NO_RWG_SE, s64, s64, s64) |  | ||||||
| DEF_HELPER_FLAGS_2(muluh_i64, TCG_CALL_NO_RWG_SE, i64, i64, i64) |  | ||||||
|  |  | ||||||
| DEF_HELPER_FLAGS_2(clz_i32, TCG_CALL_NO_RWG_SE, i32, i32, i32) |  | ||||||
| DEF_HELPER_FLAGS_2(ctz_i32, TCG_CALL_NO_RWG_SE, i32, i32, i32) |  | ||||||
| DEF_HELPER_FLAGS_2(clz_i64, TCG_CALL_NO_RWG_SE, i64, i64, i64) |  | ||||||
| DEF_HELPER_FLAGS_2(ctz_i64, TCG_CALL_NO_RWG_SE, i64, i64, i64) |  | ||||||
| DEF_HELPER_FLAGS_1(clrsb_i32, TCG_CALL_NO_RWG_SE, i32, i32) |  | ||||||
| DEF_HELPER_FLAGS_1(clrsb_i64, TCG_CALL_NO_RWG_SE, i64, i64) |  | ||||||
| DEF_HELPER_FLAGS_1(ctpop_i32, TCG_CALL_NO_RWG_SE, i32, i32) |  | ||||||
| DEF_HELPER_FLAGS_1(ctpop_i64, TCG_CALL_NO_RWG_SE, i64, i64) |  | ||||||
|  |  | ||||||
| DEF_HELPER_FLAGS_1(lookup_tb_ptr, TCG_CALL_NO_WG_SE, ptr, env) |  | ||||||
|  |  | ||||||
| DEF_HELPER_FLAGS_1(exit_atomic, TCG_CALL_NO_WG, noreturn, env) |  | ||||||
|  |  | ||||||
| #ifdef CONFIG_SOFTMMU |  | ||||||
|  |  | ||||||
| DEF_HELPER_FLAGS_5(atomic_cmpxchgb, TCG_CALL_NO_WG, |  | ||||||
|                    i32, env, tl, i32, i32, i32) |  | ||||||
| DEF_HELPER_FLAGS_5(atomic_cmpxchgw_be, TCG_CALL_NO_WG, |  | ||||||
|                    i32, env, tl, i32, i32, i32) |  | ||||||
| DEF_HELPER_FLAGS_5(atomic_cmpxchgw_le, TCG_CALL_NO_WG, |  | ||||||
|                    i32, env, tl, i32, i32, i32) |  | ||||||
| DEF_HELPER_FLAGS_5(atomic_cmpxchgl_be, TCG_CALL_NO_WG, |  | ||||||
|                    i32, env, tl, i32, i32, i32) |  | ||||||
| DEF_HELPER_FLAGS_5(atomic_cmpxchgl_le, TCG_CALL_NO_WG, |  | ||||||
|                    i32, env, tl, i32, i32, i32) |  | ||||||
| #ifdef CONFIG_ATOMIC64 |  | ||||||
| DEF_HELPER_FLAGS_5(atomic_cmpxchgq_be, TCG_CALL_NO_WG, |  | ||||||
|                    i64, env, tl, i64, i64, i32) |  | ||||||
| DEF_HELPER_FLAGS_5(atomic_cmpxchgq_le, TCG_CALL_NO_WG, |  | ||||||
|                    i64, env, tl, i64, i64, i32) |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| #ifdef CONFIG_ATOMIC64 |  | ||||||
| #define GEN_ATOMIC_HELPERS(NAME)                                  \ |  | ||||||
|     DEF_HELPER_FLAGS_4(glue(glue(atomic_, NAME), b),              \ |  | ||||||
|                        TCG_CALL_NO_WG, i32, env, tl, i32, i32)    \ |  | ||||||
|     DEF_HELPER_FLAGS_4(glue(glue(atomic_, NAME), w_le),           \ |  | ||||||
|                        TCG_CALL_NO_WG, i32, env, tl, i32, i32)    \ |  | ||||||
|     DEF_HELPER_FLAGS_4(glue(glue(atomic_, NAME), w_be),           \ |  | ||||||
|                        TCG_CALL_NO_WG, i32, env, tl, i32, i32)    \ |  | ||||||
|     DEF_HELPER_FLAGS_4(glue(glue(atomic_, NAME), l_le),           \ |  | ||||||
|                        TCG_CALL_NO_WG, i32, env, tl, i32, i32)    \ |  | ||||||
|     DEF_HELPER_FLAGS_4(glue(glue(atomic_, NAME), l_be),           \ |  | ||||||
|                        TCG_CALL_NO_WG, i32, env, tl, i32, i32)    \ |  | ||||||
|     DEF_HELPER_FLAGS_4(glue(glue(atomic_, NAME), q_le),           \ |  | ||||||
|                        TCG_CALL_NO_WG, i64, env, tl, i64, i32)    \ |  | ||||||
|     DEF_HELPER_FLAGS_4(glue(glue(atomic_, NAME), q_be),           \ |  | ||||||
|                        TCG_CALL_NO_WG, i64, env, tl, i64, i32) |  | ||||||
| #else |  | ||||||
| #define GEN_ATOMIC_HELPERS(NAME)                                  \ |  | ||||||
|     DEF_HELPER_FLAGS_4(glue(glue(atomic_, NAME), b),              \ |  | ||||||
|                        TCG_CALL_NO_WG, i32, env, tl, i32, i32)    \ |  | ||||||
|     DEF_HELPER_FLAGS_4(glue(glue(atomic_, NAME), w_le),           \ |  | ||||||
|                        TCG_CALL_NO_WG, i32, env, tl, i32, i32)    \ |  | ||||||
|     DEF_HELPER_FLAGS_4(glue(glue(atomic_, NAME), w_be),           \ |  | ||||||
|                        TCG_CALL_NO_WG, i32, env, tl, i32, i32)    \ |  | ||||||
|     DEF_HELPER_FLAGS_4(glue(glue(atomic_, NAME), l_le),           \ |  | ||||||
|                        TCG_CALL_NO_WG, i32, env, tl, i32, i32)    \ |  | ||||||
|     DEF_HELPER_FLAGS_4(glue(glue(atomic_, NAME), l_be),           \ |  | ||||||
|                        TCG_CALL_NO_WG, i32, env, tl, i32, i32) |  | ||||||
| #endif /* CONFIG_ATOMIC64 */ |  | ||||||
|  |  | ||||||
| #else |  | ||||||
|  |  | ||||||
| DEF_HELPER_FLAGS_4(atomic_cmpxchgb, TCG_CALL_NO_WG, i32, env, tl, i32, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(atomic_cmpxchgw_be, TCG_CALL_NO_WG, i32, env, tl, i32, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(atomic_cmpxchgw_le, TCG_CALL_NO_WG, i32, env, tl, i32, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(atomic_cmpxchgl_be, TCG_CALL_NO_WG, i32, env, tl, i32, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(atomic_cmpxchgl_le, TCG_CALL_NO_WG, i32, env, tl, i32, i32) |  | ||||||
| #ifdef CONFIG_ATOMIC64 |  | ||||||
| DEF_HELPER_FLAGS_4(atomic_cmpxchgq_be, TCG_CALL_NO_WG, i64, env, tl, i64, i64) |  | ||||||
| DEF_HELPER_FLAGS_4(atomic_cmpxchgq_le, TCG_CALL_NO_WG, i64, env, tl, i64, i64) |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| #ifdef CONFIG_ATOMIC64 |  | ||||||
| #define GEN_ATOMIC_HELPERS(NAME)                             \ |  | ||||||
|     DEF_HELPER_FLAGS_3(glue(glue(atomic_, NAME), b),         \ |  | ||||||
|                        TCG_CALL_NO_WG, i32, env, tl, i32)    \ |  | ||||||
|     DEF_HELPER_FLAGS_3(glue(glue(atomic_, NAME), w_le),      \ |  | ||||||
|                        TCG_CALL_NO_WG, i32, env, tl, i32)    \ |  | ||||||
|     DEF_HELPER_FLAGS_3(glue(glue(atomic_, NAME), w_be),      \ |  | ||||||
|                        TCG_CALL_NO_WG, i32, env, tl, i32)    \ |  | ||||||
|     DEF_HELPER_FLAGS_3(glue(glue(atomic_, NAME), l_le),      \ |  | ||||||
|                        TCG_CALL_NO_WG, i32, env, tl, i32)    \ |  | ||||||
|     DEF_HELPER_FLAGS_3(glue(glue(atomic_, NAME), l_be),      \ |  | ||||||
|                        TCG_CALL_NO_WG, i32, env, tl, i32)    \ |  | ||||||
|     DEF_HELPER_FLAGS_3(glue(glue(atomic_, NAME), q_le),      \ |  | ||||||
|                        TCG_CALL_NO_WG, i64, env, tl, i64)    \ |  | ||||||
|     DEF_HELPER_FLAGS_3(glue(glue(atomic_, NAME), q_be),      \ |  | ||||||
|                        TCG_CALL_NO_WG, i64, env, tl, i64) |  | ||||||
| #else |  | ||||||
| #define GEN_ATOMIC_HELPERS(NAME)                             \ |  | ||||||
|     DEF_HELPER_FLAGS_3(glue(glue(atomic_, NAME), b),         \ |  | ||||||
|                        TCG_CALL_NO_WG, i32, env, tl, i32)    \ |  | ||||||
|     DEF_HELPER_FLAGS_3(glue(glue(atomic_, NAME), w_le),      \ |  | ||||||
|                        TCG_CALL_NO_WG, i32, env, tl, i32)    \ |  | ||||||
|     DEF_HELPER_FLAGS_3(glue(glue(atomic_, NAME), w_be),      \ |  | ||||||
|                        TCG_CALL_NO_WG, i32, env, tl, i32)    \ |  | ||||||
|     DEF_HELPER_FLAGS_3(glue(glue(atomic_, NAME), l_le),      \ |  | ||||||
|                        TCG_CALL_NO_WG, i32, env, tl, i32)    \ |  | ||||||
|     DEF_HELPER_FLAGS_3(glue(glue(atomic_, NAME), l_be),      \ |  | ||||||
|                        TCG_CALL_NO_WG, i32, env, tl, i32) |  | ||||||
| #endif /* CONFIG_ATOMIC64 */ |  | ||||||
|  |  | ||||||
| #endif /* CONFIG_SOFTMMU */ |  | ||||||
|  |  | ||||||
| GEN_ATOMIC_HELPERS(fetch_add) |  | ||||||
| GEN_ATOMIC_HELPERS(fetch_and) |  | ||||||
| GEN_ATOMIC_HELPERS(fetch_or) |  | ||||||
| GEN_ATOMIC_HELPERS(fetch_xor) |  | ||||||
|  |  | ||||||
| GEN_ATOMIC_HELPERS(add_fetch) |  | ||||||
| GEN_ATOMIC_HELPERS(and_fetch) |  | ||||||
| GEN_ATOMIC_HELPERS(or_fetch) |  | ||||||
| GEN_ATOMIC_HELPERS(xor_fetch) |  | ||||||
|  |  | ||||||
| GEN_ATOMIC_HELPERS(xchg) |  | ||||||
|  |  | ||||||
| #undef GEN_ATOMIC_HELPERS |  | ||||||
|  |  | ||||||
| DEF_HELPER_FLAGS_3(gvec_mov, TCG_CALL_NO_RWG, void, ptr, ptr, i32) |  | ||||||
|  |  | ||||||
| DEF_HELPER_FLAGS_3(gvec_dup8, TCG_CALL_NO_RWG, void, ptr, i32, i32) |  | ||||||
| DEF_HELPER_FLAGS_3(gvec_dup16, TCG_CALL_NO_RWG, void, ptr, i32, i32) |  | ||||||
| DEF_HELPER_FLAGS_3(gvec_dup32, TCG_CALL_NO_RWG, void, ptr, i32, i32) |  | ||||||
| DEF_HELPER_FLAGS_3(gvec_dup64, TCG_CALL_NO_RWG, void, ptr, i32, i64) |  | ||||||
|  |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_add8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_add16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_add32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_add64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
|  |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_adds8, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_adds16, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_adds32, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_adds64, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) |  | ||||||
|  |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_sub8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_sub16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_sub32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_sub64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
|  |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_subs8, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_subs16, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_subs32, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_subs64, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) |  | ||||||
|  |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_mul8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_mul16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_mul32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_mul64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
|  |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_muls8, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_muls16, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_muls32, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_muls64, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) |  | ||||||
|  |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_ssadd8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_ssadd16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_ssadd32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_ssadd64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
|  |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_sssub8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_sssub16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_sssub32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_sssub64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
|  |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_usadd8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_usadd16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_usadd32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_usadd64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
|  |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_ussub8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| 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_ussub64, 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_neg16, TCG_CALL_NO_RWG, void, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_3(gvec_neg32, TCG_CALL_NO_RWG, void, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_3(gvec_neg64, TCG_CALL_NO_RWG, void, ptr, ptr, i32) |  | ||||||
|  |  | ||||||
| DEF_HELPER_FLAGS_3(gvec_not, TCG_CALL_NO_RWG, void, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_and, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_or, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| 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_orc, 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_xors, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_ors, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) |  | ||||||
|  |  | ||||||
| DEF_HELPER_FLAGS_3(gvec_shl8i, TCG_CALL_NO_RWG, void, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_3(gvec_shl16i, TCG_CALL_NO_RWG, void, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_3(gvec_shl32i, TCG_CALL_NO_RWG, void, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_3(gvec_shl64i, TCG_CALL_NO_RWG, void, ptr, ptr, i32) |  | ||||||
|  |  | ||||||
| DEF_HELPER_FLAGS_3(gvec_shr8i, TCG_CALL_NO_RWG, void, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_3(gvec_shr16i, TCG_CALL_NO_RWG, void, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_3(gvec_shr32i, TCG_CALL_NO_RWG, void, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_3(gvec_shr64i, TCG_CALL_NO_RWG, void, ptr, ptr, i32) |  | ||||||
|  |  | ||||||
| DEF_HELPER_FLAGS_3(gvec_sar8i, TCG_CALL_NO_RWG, void, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_3(gvec_sar16i, TCG_CALL_NO_RWG, void, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_3(gvec_sar32i, TCG_CALL_NO_RWG, void, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_3(gvec_sar64i, TCG_CALL_NO_RWG, void, ptr, ptr, i32) |  | ||||||
|  |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_eq8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_eq16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_eq32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_eq64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
|  |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_ne8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_ne16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_ne32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_ne64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
|  |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_lt8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_lt16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_lt32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_lt64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
|  |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_le8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_le16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_le32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_le64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
|  |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_ltu8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_ltu16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_ltu32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_ltu64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
|  |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_leu8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_leu16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_leu32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_leu64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| @@ -1,10 +0,0 @@ | |||||||
| # Trace events for debugging and performance instrumentation |  | ||||||
|  |  | ||||||
| # TCG related tracing (mostly disabled by default) |  | ||||||
| # cpu-exec.c |  | ||||||
| disable exec_tb(void *tb, uintptr_t pc) "tb:%p pc=0x%"PRIxPTR |  | ||||||
| disable exec_tb_nocache(void *tb, uintptr_t pc) "tb:%p pc=0x%"PRIxPTR |  | ||||||
| disable exec_tb_exit(void *last_tb, unsigned int flags) "tb:%p flags=0x%x" |  | ||||||
|  |  | ||||||
| # translate-all.c |  | ||||||
| translate_block(void *tb, uintptr_t pc, uint8_t *tb_code) "tb:%p, pc:0x%"PRIxPTR", tb_code:%p" |  | ||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,36 +0,0 @@ | |||||||
| /* |  | ||||||
|  *  Translated block handling |  | ||||||
|  * |  | ||||||
|  *  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/>. |  | ||||||
|  */ |  | ||||||
| #ifndef TRANSLATE_ALL_H |  | ||||||
| #define TRANSLATE_ALL_H |  | ||||||
|  |  | ||||||
| #include "exec/exec-all.h" |  | ||||||
|  |  | ||||||
|  |  | ||||||
| /* translate-all.c */ |  | ||||||
| void tb_invalidate_phys_page_fast(tb_page_addr_t start, int len); |  | ||||||
| void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end, |  | ||||||
|                                    int is_cpu_write_access); |  | ||||||
| void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t end); |  | ||||||
| void tb_check_watchpoint(CPUState *cpu); |  | ||||||
|  |  | ||||||
| #ifdef CONFIG_USER_ONLY |  | ||||||
| int page_unprotect(target_ulong address, uintptr_t pc); |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| #endif /* TRANSLATE_ALL_H */ |  | ||||||
| @@ -1,138 +0,0 @@ | |||||||
| /* |  | ||||||
|  * Generic intermediate code generation. |  | ||||||
|  * |  | ||||||
|  * Copyright (C) 2016-2017 Lluís Vilanova <vilanova@ac.upc.edu> |  | ||||||
|  * |  | ||||||
|  * 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 "qemu/error-report.h" |  | ||||||
| #include "cpu.h" |  | ||||||
| #include "tcg/tcg.h" |  | ||||||
| #include "tcg/tcg-op.h" |  | ||||||
| #include "exec/exec-all.h" |  | ||||||
| #include "exec/gen-icount.h" |  | ||||||
| #include "exec/log.h" |  | ||||||
| #include "exec/translator.h" |  | ||||||
|  |  | ||||||
| /* Pairs with tcg_clear_temp_count. |  | ||||||
|    To be called by #TranslatorOps.{translate_insn,tb_stop} if |  | ||||||
|    (1) the target is sufficiently clean to support reporting, |  | ||||||
|    (2) as and when all temporaries are known to be consumed. |  | ||||||
|    For most targets, (2) is at the end of translate_insn.  */ |  | ||||||
| void translator_loop_temp_check(DisasContextBase *db) |  | ||||||
| { |  | ||||||
|     if (tcg_check_temp_count()) { |  | ||||||
|         qemu_log("warning: TCG temporary leaks before " |  | ||||||
|                  TARGET_FMT_lx "\n", db->pc_next); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void translator_loop(const TranslatorOps *ops, DisasContextBase *db, |  | ||||||
|                      CPUState *cpu, TranslationBlock *tb) |  | ||||||
| { |  | ||||||
|     int max_insns; |  | ||||||
|  |  | ||||||
|     /* Initialize DisasContext */ |  | ||||||
|     db->tb = tb; |  | ||||||
|     db->pc_first = tb->pc; |  | ||||||
|     db->pc_next = db->pc_first; |  | ||||||
|     db->is_jmp = DISAS_NEXT; |  | ||||||
|     db->num_insns = 0; |  | ||||||
|     db->singlestep_enabled = cpu->singlestep_enabled; |  | ||||||
|  |  | ||||||
|     /* 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 */ |  | ||||||
|  |  | ||||||
|     /* Reset the temp count so that we can identify leaks */ |  | ||||||
|     tcg_clear_temp_count(); |  | ||||||
|  |  | ||||||
|     /* Start translating.  */ |  | ||||||
|     gen_tb_start(db->tb); |  | ||||||
|     ops->tb_start(db, cpu); |  | ||||||
|     tcg_debug_assert(db->is_jmp == DISAS_NEXT);  /* no early exit */ |  | ||||||
|  |  | ||||||
|     while (true) { |  | ||||||
|         db->num_insns++; |  | ||||||
|         ops->insn_start(db, cpu); |  | ||||||
|         tcg_debug_assert(db->is_jmp == DISAS_NEXT);  /* no early exit */ |  | ||||||
|  |  | ||||||
|         /* Pass breakpoint hits to target for further processing */ |  | ||||||
|         if (unlikely(!QTAILQ_EMPTY(&cpu->breakpoints))) { |  | ||||||
|             CPUBreakpoint *bp; |  | ||||||
|             QTAILQ_FOREACH(bp, &cpu->breakpoints, entry) { |  | ||||||
|                 if (bp->pc == db->pc_next) { |  | ||||||
|                     if (ops->breakpoint_check(db, cpu, bp)) { |  | ||||||
|                         break; |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|             /* The breakpoint_check hook may use DISAS_TOO_MANY to indicate |  | ||||||
|                that only one more instruction is to be executed.  Otherwise |  | ||||||
|                it should use DISAS_NORETURN when generating an exception, |  | ||||||
|                but may use a DISAS_TARGET_* value for Something Else.  */ |  | ||||||
|             if (db->is_jmp > DISAS_TOO_MANY) { |  | ||||||
|                 break; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         /* Disassemble one instruction.  The translate_insn hook should |  | ||||||
|            update db->pc_next and db->is_jmp to indicate what should be |  | ||||||
|            done next -- either exiting this loop or locate the start of |  | ||||||
|            the next instruction.  */ |  | ||||||
|         if (db->num_insns == max_insns && (tb_cflags(db->tb) & CF_LAST_IO)) { |  | ||||||
|             /* Accept I/O on the last instruction.  */ |  | ||||||
|             gen_io_start(); |  | ||||||
|             ops->translate_insn(db, cpu); |  | ||||||
|             gen_io_end(); |  | ||||||
|         } else { |  | ||||||
|             ops->translate_insn(db, cpu); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         /* Stop translation if translate_insn so indicated.  */ |  | ||||||
|         if (db->is_jmp != DISAS_NEXT) { |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         /* Stop translation if the output buffer is full, |  | ||||||
|            or we have executed all of the allowed instructions.  */ |  | ||||||
|         if (tcg_op_buf_full() || db->num_insns >= max_insns) { |  | ||||||
|             db->is_jmp = DISAS_TOO_MANY; |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /* Emit code to exit the TB, as indicated by db->is_jmp.  */ |  | ||||||
|     ops->tb_stop(db, cpu); |  | ||||||
|     gen_tb_end(db->tb, db->num_insns); |  | ||||||
|  |  | ||||||
|     /* The disas_log hook may use these values rather than recompute.  */ |  | ||||||
|     db->tb->size = db->pc_next - db->pc_first; |  | ||||||
|     db->tb->icount = db->num_insns; |  | ||||||
|  |  | ||||||
| #ifdef DEBUG_DISAS |  | ||||||
|     if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM) |  | ||||||
|         && qemu_log_in_addr_range(db->pc_first)) { |  | ||||||
|         qemu_log_lock(); |  | ||||||
|         qemu_log("----------------\n"); |  | ||||||
|         ops->disas_log(db, cpu); |  | ||||||
|         qemu_log("\n"); |  | ||||||
|         qemu_log_unlock(); |  | ||||||
|     } |  | ||||||
| #endif |  | ||||||
| } |  | ||||||
| @@ -1,34 +0,0 @@ | |||||||
| #include "qemu/osdep.h" |  | ||||||
| #include "qemu-common.h" |  | ||||||
| #include "qom/cpu.h" |  | ||||||
| #include "sysemu/replay.h" |  | ||||||
|  |  | ||||||
| void cpu_resume(CPUState *cpu) |  | ||||||
| { |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void qemu_init_vcpu(CPUState *cpu) |  | ||||||
| { |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* User mode emulation does not support record/replay yet.  */ |  | ||||||
|  |  | ||||||
| bool replay_exception(void) |  | ||||||
| { |  | ||||||
|     return true; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| bool replay_has_exception(void) |  | ||||||
| { |  | ||||||
|     return false; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| bool replay_interrupt(void) |  | ||||||
| { |  | ||||||
|     return true; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| bool replay_has_interrupt(void) |  | ||||||
| { |  | ||||||
|     return false; |  | ||||||
| } |  | ||||||
| @@ -1,631 +0,0 @@ | |||||||
| /* |  | ||||||
|  *  User emulator execution |  | ||||||
|  * |  | ||||||
|  *  Copyright (c) 2003-2005 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/>. |  | ||||||
|  */ |  | ||||||
| #include "qemu/osdep.h" |  | ||||||
| #include "cpu.h" |  | ||||||
| #include "disas/disas.h" |  | ||||||
| #include "exec/exec-all.h" |  | ||||||
| #include "tcg.h" |  | ||||||
| #include "qemu/bitops.h" |  | ||||||
| #include "exec/cpu_ldst.h" |  | ||||||
| #include "translate-all.h" |  | ||||||
| #include "exec/helper-proto.h" |  | ||||||
|  |  | ||||||
| #undef EAX |  | ||||||
| #undef ECX |  | ||||||
| #undef EDX |  | ||||||
| #undef EBX |  | ||||||
| #undef ESP |  | ||||||
| #undef EBP |  | ||||||
| #undef ESI |  | ||||||
| #undef EDI |  | ||||||
| #undef EIP |  | ||||||
| #ifdef __linux__ |  | ||||||
| #include <sys/ucontext.h> |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| __thread uintptr_t helper_retaddr; |  | ||||||
|  |  | ||||||
| //#define DEBUG_SIGNAL |  | ||||||
|  |  | ||||||
| /* exit the current TB from a signal handler. The host registers are |  | ||||||
|    restored in a state compatible with the CPU emulator |  | ||||||
|  */ |  | ||||||
| static void cpu_exit_tb_from_sighandler(CPUState *cpu, sigset_t *old_set) |  | ||||||
| { |  | ||||||
|     /* XXX: use siglongjmp ? */ |  | ||||||
|     sigprocmask(SIG_SETMASK, old_set, NULL); |  | ||||||
|     cpu_loop_exit_noexc(cpu); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* 'pc' is the host PC at which the exception was raised. 'address' is |  | ||||||
|    the effective address of the memory exception. 'is_write' is 1 if a |  | ||||||
|    write caused the exception and otherwise 0'. 'old_set' is the |  | ||||||
|    signal set which should be restored */ |  | ||||||
| static inline int handle_cpu_signal(uintptr_t pc, siginfo_t *info, |  | ||||||
|                                     int is_write, sigset_t *old_set) |  | ||||||
| { |  | ||||||
|     CPUState *cpu = current_cpu; |  | ||||||
|     CPUClass *cc; |  | ||||||
|     int ret; |  | ||||||
|     unsigned long address = (unsigned long)info->si_addr; |  | ||||||
|  |  | ||||||
|     /* We must handle PC addresses from two different sources: |  | ||||||
|      * a call return address and a signal frame address. |  | ||||||
|      * |  | ||||||
|      * Within cpu_restore_state_from_tb we assume the former and adjust |  | ||||||
|      * the address by -GETPC_ADJ so that the address is within the call |  | ||||||
|      * insn so that addr does not accidentally match the beginning of the |  | ||||||
|      * next guest insn. |  | ||||||
|      * |  | ||||||
|      * However, when the PC comes from the signal frame, it points to |  | ||||||
|      * the actual faulting host insn and not a call insn.  Subtracting |  | ||||||
|      * GETPC_ADJ in that case may accidentally match the previous guest insn. |  | ||||||
|      * |  | ||||||
|      * So for the later case, adjust forward to compensate for what |  | ||||||
|      * will be done later by cpu_restore_state_from_tb. |  | ||||||
|      */ |  | ||||||
|     if (helper_retaddr) { |  | ||||||
|         pc = helper_retaddr; |  | ||||||
|     } else { |  | ||||||
|         pc += GETPC_ADJ; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /* For synchronous signals we expect to be coming from the vCPU |  | ||||||
|      * thread (so current_cpu should be valid) and either from running |  | ||||||
|      * code or during translation which can fault as we cross pages. |  | ||||||
|      * |  | ||||||
|      * If neither is true then something has gone wrong and we should |  | ||||||
|      * abort rather than try and restart the vCPU execution. |  | ||||||
|      */ |  | ||||||
|     if (!cpu || !cpu->running) { |  | ||||||
|         printf("qemu:%s received signal outside vCPU context @ pc=0x%" |  | ||||||
|                PRIxPTR "\n",  __func__, pc); |  | ||||||
|         abort(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| #if defined(DEBUG_SIGNAL) |  | ||||||
|     printf("qemu: SIGSEGV pc=0x%08lx address=%08lx w=%d oldset=0x%08lx\n", |  | ||||||
|            pc, address, is_write, *(unsigned long *)old_set); |  | ||||||
| #endif |  | ||||||
|     /* XXX: locking issue */ |  | ||||||
|     /* Note that it is important that we don't call page_unprotect() unless |  | ||||||
|      * this is really a "write to nonwriteable page" fault, because |  | ||||||
|      * page_unprotect() assumes that if it is called for an access to |  | ||||||
|      * a page that's writeable this means we had two threads racing and |  | ||||||
|      * another thread got there first and already made the page writeable; |  | ||||||
|      * so we will retry the access. If we were to call page_unprotect() |  | ||||||
|      * for some other kind of fault that should really be passed to the |  | ||||||
|      * guest, we'd end up in an infinite loop of retrying the faulting |  | ||||||
|      * access. |  | ||||||
|      */ |  | ||||||
|     if (is_write && info->si_signo == SIGSEGV && info->si_code == SEGV_ACCERR && |  | ||||||
|         h2g_valid(address)) { |  | ||||||
|         switch (page_unprotect(h2g(address), pc)) { |  | ||||||
|         case 0: |  | ||||||
|             /* Fault not caused by a page marked unwritable to protect |  | ||||||
|              * cached translations, must be the guest binary's problem. |  | ||||||
|              */ |  | ||||||
|             break; |  | ||||||
|         case 1: |  | ||||||
|             /* Fault caused by protection of cached translation; TBs |  | ||||||
|              * invalidated, so resume execution.  Retain helper_retaddr |  | ||||||
|              * for a possible second fault. |  | ||||||
|              */ |  | ||||||
|             return 1; |  | ||||||
|         case 2: |  | ||||||
|             /* Fault caused by protection of cached translation, and the |  | ||||||
|              * currently executing TB was modified and must be exited |  | ||||||
|              * immediately.  Clear helper_retaddr for next execution. |  | ||||||
|              */ |  | ||||||
|             helper_retaddr = 0; |  | ||||||
|             cpu_exit_tb_from_sighandler(cpu, old_set); |  | ||||||
|             /* NORETURN */ |  | ||||||
|  |  | ||||||
|         default: |  | ||||||
|             g_assert_not_reached(); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /* Convert forcefully to guest address space, invalid addresses |  | ||||||
|        are still valid segv ones */ |  | ||||||
|     address = h2g_nocheck(address); |  | ||||||
|  |  | ||||||
|     cc = CPU_GET_CLASS(cpu); |  | ||||||
|     /* see if it is an MMU fault */ |  | ||||||
|     g_assert(cc->handle_mmu_fault); |  | ||||||
|     ret = cc->handle_mmu_fault(cpu, address, 0, is_write, MMU_USER_IDX); |  | ||||||
|  |  | ||||||
|     if (ret == 0) { |  | ||||||
|         /* The MMU fault was handled without causing real CPU fault. |  | ||||||
|          *  Retain helper_retaddr for a possible second fault. |  | ||||||
|          */ |  | ||||||
|         return 1; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /* All other paths lead to cpu_exit; clear helper_retaddr |  | ||||||
|      * for next execution. |  | ||||||
|      */ |  | ||||||
|     helper_retaddr = 0; |  | ||||||
|  |  | ||||||
|     if (ret < 0) { |  | ||||||
|         return 0; /* not an MMU fault */ |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /* 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(__NetBSD__) |  | ||||||
| #include <ucontext.h> |  | ||||||
|  |  | ||||||
| #define EIP_sig(context)     ((context)->uc_mcontext.__gregs[_REG_EIP]) |  | ||||||
| #define TRAP_sig(context)    ((context)->uc_mcontext.__gregs[_REG_TRAPNO]) |  | ||||||
| #define ERROR_sig(context)   ((context)->uc_mcontext.__gregs[_REG_ERR]) |  | ||||||
| #define MASK_sig(context)    ((context)->uc_sigmask) |  | ||||||
| #elif defined(__FreeBSD__) || defined(__DragonFly__) |  | ||||||
| #include <ucontext.h> |  | ||||||
|  |  | ||||||
| #define EIP_sig(context)  (*((unsigned long *)&(context)->uc_mcontext.mc_eip)) |  | ||||||
| #define TRAP_sig(context)    ((context)->uc_mcontext.mc_trapno) |  | ||||||
| #define ERROR_sig(context)   ((context)->uc_mcontext.mc_err) |  | ||||||
| #define MASK_sig(context)    ((context)->uc_sigmask) |  | ||||||
| #elif defined(__OpenBSD__) |  | ||||||
| #define EIP_sig(context)     ((context)->sc_eip) |  | ||||||
| #define TRAP_sig(context)    ((context)->sc_trapno) |  | ||||||
| #define ERROR_sig(context)   ((context)->sc_err) |  | ||||||
| #define MASK_sig(context)    ((context)->sc_mask) |  | ||||||
| #else |  | ||||||
| #define EIP_sig(context)     ((context)->uc_mcontext.gregs[REG_EIP]) |  | ||||||
| #define TRAP_sig(context)    ((context)->uc_mcontext.gregs[REG_TRAPNO]) |  | ||||||
| #define ERROR_sig(context)   ((context)->uc_mcontext.gregs[REG_ERR]) |  | ||||||
| #define MASK_sig(context)    ((context)->uc_sigmask) |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| int cpu_signal_handler(int host_signum, void *pinfo, |  | ||||||
|                        void *puc) |  | ||||||
| { |  | ||||||
|     siginfo_t *info = pinfo; |  | ||||||
| #if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__) |  | ||||||
|     ucontext_t *uc = puc; |  | ||||||
| #elif defined(__OpenBSD__) |  | ||||||
|     struct sigcontext *uc = puc; |  | ||||||
| #else |  | ||||||
|     ucontext_t *uc = puc; |  | ||||||
| #endif |  | ||||||
|     unsigned long pc; |  | ||||||
|     int trapno; |  | ||||||
|  |  | ||||||
| #ifndef REG_EIP |  | ||||||
| /* for glibc 2.1 */ |  | ||||||
| #define REG_EIP    EIP |  | ||||||
| #define REG_ERR    ERR |  | ||||||
| #define REG_TRAPNO TRAPNO |  | ||||||
| #endif |  | ||||||
|     pc = EIP_sig(uc); |  | ||||||
|     trapno = TRAP_sig(uc); |  | ||||||
|     return handle_cpu_signal(pc, info, |  | ||||||
|                              trapno == 0xe ? (ERROR_sig(uc) >> 1) & 1 : 0, |  | ||||||
|                              &MASK_sig(uc)); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #elif defined(__x86_64__) |  | ||||||
|  |  | ||||||
| #ifdef __NetBSD__ |  | ||||||
| #define PC_sig(context)       _UC_MACHINE_PC(context) |  | ||||||
| #define TRAP_sig(context)     ((context)->uc_mcontext.__gregs[_REG_TRAPNO]) |  | ||||||
| #define ERROR_sig(context)    ((context)->uc_mcontext.__gregs[_REG_ERR]) |  | ||||||
| #define MASK_sig(context)     ((context)->uc_sigmask) |  | ||||||
| #elif defined(__OpenBSD__) |  | ||||||
| #define PC_sig(context)       ((context)->sc_rip) |  | ||||||
| #define TRAP_sig(context)     ((context)->sc_trapno) |  | ||||||
| #define ERROR_sig(context)    ((context)->sc_err) |  | ||||||
| #define MASK_sig(context)     ((context)->sc_mask) |  | ||||||
| #elif defined(__FreeBSD__) || defined(__DragonFly__) |  | ||||||
| #include <ucontext.h> |  | ||||||
|  |  | ||||||
| #define PC_sig(context)  (*((unsigned long *)&(context)->uc_mcontext.mc_rip)) |  | ||||||
| #define TRAP_sig(context)     ((context)->uc_mcontext.mc_trapno) |  | ||||||
| #define ERROR_sig(context)    ((context)->uc_mcontext.mc_err) |  | ||||||
| #define MASK_sig(context)     ((context)->uc_sigmask) |  | ||||||
| #else |  | ||||||
| #define PC_sig(context)       ((context)->uc_mcontext.gregs[REG_RIP]) |  | ||||||
| #define TRAP_sig(context)     ((context)->uc_mcontext.gregs[REG_TRAPNO]) |  | ||||||
| #define ERROR_sig(context)    ((context)->uc_mcontext.gregs[REG_ERR]) |  | ||||||
| #define MASK_sig(context)     ((context)->uc_sigmask) |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| int cpu_signal_handler(int host_signum, void *pinfo, |  | ||||||
|                        void *puc) |  | ||||||
| { |  | ||||||
|     siginfo_t *info = pinfo; |  | ||||||
|     unsigned long pc; |  | ||||||
| #if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__) |  | ||||||
|     ucontext_t *uc = puc; |  | ||||||
| #elif defined(__OpenBSD__) |  | ||||||
|     struct sigcontext *uc = puc; |  | ||||||
| #else |  | ||||||
|     ucontext_t *uc = puc; |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
|     pc = PC_sig(uc); |  | ||||||
|     return handle_cpu_signal(pc, info, |  | ||||||
|                              TRAP_sig(uc) == 0xe ? (ERROR_sig(uc) >> 1) & 1 : 0, |  | ||||||
|                              &MASK_sig(uc)); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #elif defined(_ARCH_PPC) |  | ||||||
|  |  | ||||||
| /*********************************************************************** |  | ||||||
|  * signal context platform-specific definitions |  | ||||||
|  * From Wine |  | ||||||
|  */ |  | ||||||
| #ifdef linux |  | ||||||
| /* All Registers access - only for local access */ |  | ||||||
| #define REG_sig(reg_name, context)              \ |  | ||||||
|     ((context)->uc_mcontext.regs->reg_name) |  | ||||||
| /* Gpr Registers access  */ |  | ||||||
| #define GPR_sig(reg_num, context)              REG_sig(gpr[reg_num], context) |  | ||||||
| /* Program counter */ |  | ||||||
| #define IAR_sig(context)                       REG_sig(nip, context) |  | ||||||
| /* Machine State Register (Supervisor) */ |  | ||||||
| #define MSR_sig(context)                       REG_sig(msr, context) |  | ||||||
| /* Count register */ |  | ||||||
| #define CTR_sig(context)                       REG_sig(ctr, context) |  | ||||||
| /* User's integer exception register */ |  | ||||||
| #define XER_sig(context)                       REG_sig(xer, context) |  | ||||||
| /* Link register */ |  | ||||||
| #define LR_sig(context)                        REG_sig(link, context) |  | ||||||
| /* Condition register */ |  | ||||||
| #define CR_sig(context)                        REG_sig(ccr, context) |  | ||||||
|  |  | ||||||
| /* Float Registers access  */ |  | ||||||
| #define FLOAT_sig(reg_num, context)                                     \ |  | ||||||
|     (((double *)((char *)((context)->uc_mcontext.regs + 48 * 4)))[reg_num]) |  | ||||||
| #define FPSCR_sig(context) \ |  | ||||||
|     (*(int *)((char *)((context)->uc_mcontext.regs + (48 + 32 * 2) * 4))) |  | ||||||
| /* Exception Registers access */ |  | ||||||
| #define DAR_sig(context)                       REG_sig(dar, context) |  | ||||||
| #define DSISR_sig(context)                     REG_sig(dsisr, context) |  | ||||||
| #define TRAP_sig(context)                      REG_sig(trap, context) |  | ||||||
| #endif /* linux */ |  | ||||||
|  |  | ||||||
| #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) |  | ||||||
| #include <ucontext.h> |  | ||||||
| #define IAR_sig(context)               ((context)->uc_mcontext.mc_srr0) |  | ||||||
| #define MSR_sig(context)               ((context)->uc_mcontext.mc_srr1) |  | ||||||
| #define CTR_sig(context)               ((context)->uc_mcontext.mc_ctr) |  | ||||||
| #define XER_sig(context)               ((context)->uc_mcontext.mc_xer) |  | ||||||
| #define LR_sig(context)                ((context)->uc_mcontext.mc_lr) |  | ||||||
| #define CR_sig(context)                ((context)->uc_mcontext.mc_cr) |  | ||||||
| /* Exception Registers access */ |  | ||||||
| #define DAR_sig(context)               ((context)->uc_mcontext.mc_dar) |  | ||||||
| #define DSISR_sig(context)             ((context)->uc_mcontext.mc_dsisr) |  | ||||||
| #define TRAP_sig(context)              ((context)->uc_mcontext.mc_exc) |  | ||||||
| #endif /* __FreeBSD__|| __FreeBSD_kernel__ */ |  | ||||||
|  |  | ||||||
| int cpu_signal_handler(int host_signum, void *pinfo, |  | ||||||
|                        void *puc) |  | ||||||
| { |  | ||||||
|     siginfo_t *info = pinfo; |  | ||||||
| #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) |  | ||||||
|     ucontext_t *uc = puc; |  | ||||||
| #else |  | ||||||
|     ucontext_t *uc = puc; |  | ||||||
| #endif |  | ||||||
|     unsigned long pc; |  | ||||||
|     int is_write; |  | ||||||
|  |  | ||||||
|     pc = IAR_sig(uc); |  | ||||||
|     is_write = 0; |  | ||||||
| #if 0 |  | ||||||
|     /* ppc 4xx case */ |  | ||||||
|     if (DSISR_sig(uc) & 0x00800000) { |  | ||||||
|         is_write = 1; |  | ||||||
|     } |  | ||||||
| #else |  | ||||||
|     if (TRAP_sig(uc) != 0x400 && (DSISR_sig(uc) & 0x02000000)) { |  | ||||||
|         is_write = 1; |  | ||||||
|     } |  | ||||||
| #endif |  | ||||||
|     return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #elif defined(__alpha__) |  | ||||||
|  |  | ||||||
| int cpu_signal_handler(int host_signum, void *pinfo, |  | ||||||
|                            void *puc) |  | ||||||
| { |  | ||||||
|     siginfo_t *info = pinfo; |  | ||||||
|     ucontext_t *uc = puc; |  | ||||||
|     uint32_t *pc = uc->uc_mcontext.sc_pc; |  | ||||||
|     uint32_t insn = *pc; |  | ||||||
|     int is_write = 0; |  | ||||||
|  |  | ||||||
|     /* XXX: need kernel patch to get write flag faster */ |  | ||||||
|     switch (insn >> 26) { |  | ||||||
|     case 0x0d: /* stw */ |  | ||||||
|     case 0x0e: /* stb */ |  | ||||||
|     case 0x0f: /* stq_u */ |  | ||||||
|     case 0x24: /* stf */ |  | ||||||
|     case 0x25: /* stg */ |  | ||||||
|     case 0x26: /* sts */ |  | ||||||
|     case 0x27: /* stt */ |  | ||||||
|     case 0x2c: /* stl */ |  | ||||||
|     case 0x2d: /* stq */ |  | ||||||
|     case 0x2e: /* stl_c */ |  | ||||||
|     case 0x2f: /* stq_c */ |  | ||||||
|         is_write = 1; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask); |  | ||||||
| } |  | ||||||
| #elif defined(__sparc__) |  | ||||||
|  |  | ||||||
| int cpu_signal_handler(int host_signum, void *pinfo, |  | ||||||
|                        void *puc) |  | ||||||
| { |  | ||||||
|     siginfo_t *info = pinfo; |  | ||||||
|     int is_write; |  | ||||||
|     uint32_t insn; |  | ||||||
| #if !defined(__arch64__) || defined(CONFIG_SOLARIS) |  | ||||||
|     uint32_t *regs = (uint32_t *)(info + 1); |  | ||||||
|     void *sigmask = (regs + 20); |  | ||||||
|     /* XXX: is there a standard glibc define ? */ |  | ||||||
|     unsigned long pc = regs[1]; |  | ||||||
| #else |  | ||||||
| #ifdef __linux__ |  | ||||||
|     struct sigcontext *sc = puc; |  | ||||||
|     unsigned long pc = sc->sigc_regs.tpc; |  | ||||||
|     void *sigmask = (void *)sc->sigc_mask; |  | ||||||
| #elif defined(__OpenBSD__) |  | ||||||
|     struct sigcontext *uc = puc; |  | ||||||
|     unsigned long pc = uc->sc_pc; |  | ||||||
|     void *sigmask = (void *)(long)uc->sc_mask; |  | ||||||
| #elif defined(__NetBSD__) |  | ||||||
|     ucontext_t *uc = puc; |  | ||||||
|     unsigned long pc = _UC_MACHINE_PC(uc); |  | ||||||
|     void *sigmask = (void *)&uc->uc_sigmask; |  | ||||||
| #endif |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
|     /* XXX: need kernel patch to get write flag faster */ |  | ||||||
|     is_write = 0; |  | ||||||
|     insn = *(uint32_t *)pc; |  | ||||||
|     if ((insn >> 30) == 3) { |  | ||||||
|         switch ((insn >> 19) & 0x3f) { |  | ||||||
|         case 0x05: /* stb */ |  | ||||||
|         case 0x15: /* stba */ |  | ||||||
|         case 0x06: /* sth */ |  | ||||||
|         case 0x16: /* stha */ |  | ||||||
|         case 0x04: /* st */ |  | ||||||
|         case 0x14: /* sta */ |  | ||||||
|         case 0x07: /* std */ |  | ||||||
|         case 0x17: /* stda */ |  | ||||||
|         case 0x0e: /* stx */ |  | ||||||
|         case 0x1e: /* stxa */ |  | ||||||
|         case 0x24: /* stf */ |  | ||||||
|         case 0x34: /* stfa */ |  | ||||||
|         case 0x27: /* stdf */ |  | ||||||
|         case 0x37: /* stdfa */ |  | ||||||
|         case 0x26: /* stqf */ |  | ||||||
|         case 0x36: /* stqfa */ |  | ||||||
|         case 0x25: /* stfsr */ |  | ||||||
|         case 0x3c: /* casa */ |  | ||||||
|         case 0x3e: /* casxa */ |  | ||||||
|             is_write = 1; |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     return handle_cpu_signal(pc, info, is_write, sigmask); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #elif defined(__arm__) |  | ||||||
|  |  | ||||||
| #if defined(__NetBSD__) |  | ||||||
| #include <ucontext.h> |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| int cpu_signal_handler(int host_signum, void *pinfo, |  | ||||||
|                        void *puc) |  | ||||||
| { |  | ||||||
|     siginfo_t *info = pinfo; |  | ||||||
| #if defined(__NetBSD__) |  | ||||||
|     ucontext_t *uc = puc; |  | ||||||
| #else |  | ||||||
|     ucontext_t *uc = puc; |  | ||||||
| #endif |  | ||||||
|     unsigned long pc; |  | ||||||
|     int is_write; |  | ||||||
|  |  | ||||||
| #if defined(__NetBSD__) |  | ||||||
|     pc = uc->uc_mcontext.__gregs[_REG_R15]; |  | ||||||
| #elif defined(__GLIBC__) && (__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ <= 3)) |  | ||||||
|     pc = uc->uc_mcontext.gregs[R15]; |  | ||||||
| #else |  | ||||||
|     pc = uc->uc_mcontext.arm_pc; |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
|     /* error_code is the FSR value, in which bit 11 is WnR (assuming a v6 or |  | ||||||
|      * later processor; on v5 we will always report this as a read). |  | ||||||
|      */ |  | ||||||
|     is_write = extract32(uc->uc_mcontext.error_code, 11, 1); |  | ||||||
|     return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #elif defined(__aarch64__) |  | ||||||
|  |  | ||||||
| int cpu_signal_handler(int host_signum, void *pinfo, void *puc) |  | ||||||
| { |  | ||||||
|     siginfo_t *info = pinfo; |  | ||||||
|     ucontext_t *uc = puc; |  | ||||||
|     uintptr_t pc = uc->uc_mcontext.pc; |  | ||||||
|     uint32_t insn = *(uint32_t *)pc; |  | ||||||
|     bool is_write; |  | ||||||
|  |  | ||||||
|     /* XXX: need kernel patch to get write flag faster.  */ |  | ||||||
|     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 */ |  | ||||||
|                 /* 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 */ |  | ||||||
|  |  | ||||||
|     return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #elif defined(__s390__) |  | ||||||
|  |  | ||||||
| int cpu_signal_handler(int host_signum, void *pinfo, |  | ||||||
|                        void *puc) |  | ||||||
| { |  | ||||||
|     siginfo_t *info = pinfo; |  | ||||||
|     ucontext_t *uc = puc; |  | ||||||
|     unsigned long pc; |  | ||||||
|     uint16_t *pinsn; |  | ||||||
|     int is_write = 0; |  | ||||||
|  |  | ||||||
|     pc = uc->uc_mcontext.psw.addr; |  | ||||||
|  |  | ||||||
|     /* ??? On linux, the non-rt signal handler has 4 (!) arguments instead |  | ||||||
|        of the normal 2 arguments.  The 3rd argument contains the "int_code" |  | ||||||
|        from the hardware which does in fact contain the is_write value. |  | ||||||
|        The rt signal handler, as far as I can tell, does not give this value |  | ||||||
|        at all.  Not that we could get to it from here even if it were.  */ |  | ||||||
|     /* ??? This is not even close to complete, since it ignores all |  | ||||||
|        of the read-modify-write instructions.  */ |  | ||||||
|     pinsn = (uint16_t *)pc; |  | ||||||
|     switch (pinsn[0] >> 8) { |  | ||||||
|     case 0x50: /* ST */ |  | ||||||
|     case 0x42: /* STC */ |  | ||||||
|     case 0x40: /* STH */ |  | ||||||
|         is_write = 1; |  | ||||||
|         break; |  | ||||||
|     case 0xc4: /* RIL format insns */ |  | ||||||
|         switch (pinsn[0] & 0xf) { |  | ||||||
|         case 0xf: /* STRL */ |  | ||||||
|         case 0xb: /* STGRL */ |  | ||||||
|         case 0x7: /* STHRL */ |  | ||||||
|             is_write = 1; |  | ||||||
|         } |  | ||||||
|         break; |  | ||||||
|     case 0xe3: /* RXY format insns */ |  | ||||||
|         switch (pinsn[2] & 0xff) { |  | ||||||
|         case 0x50: /* STY */ |  | ||||||
|         case 0x24: /* STG */ |  | ||||||
|         case 0x72: /* STCY */ |  | ||||||
|         case 0x70: /* STHY */ |  | ||||||
|         case 0x8e: /* STPQ */ |  | ||||||
|         case 0x3f: /* STRVH */ |  | ||||||
|         case 0x3e: /* STRV */ |  | ||||||
|         case 0x2f: /* STRVG */ |  | ||||||
|             is_write = 1; |  | ||||||
|         } |  | ||||||
|         break; |  | ||||||
|     } |  | ||||||
|     return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #elif defined(__mips__) |  | ||||||
|  |  | ||||||
| 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.pc; |  | ||||||
|     int is_write; |  | ||||||
|  |  | ||||||
|     /* XXX: compute is_write */ |  | ||||||
|     is_write = 0; |  | ||||||
|     return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #else |  | ||||||
|  |  | ||||||
| #error host CPU specific signal handler needed |  | ||||||
|  |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| /* The softmmu versions of these helpers are in cputlb.c.  */ |  | ||||||
|  |  | ||||||
| /* Do not allow unaligned operations to proceed.  Return the host address.  */ |  | ||||||
| static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr, |  | ||||||
|                                int size, uintptr_t retaddr) |  | ||||||
| { |  | ||||||
|     /* Enforce qemu required alignment.  */ |  | ||||||
|     if (unlikely(addr & (size - 1))) { |  | ||||||
|         cpu_loop_exit_atomic(ENV_GET_CPU(env), retaddr); |  | ||||||
|     } |  | ||||||
|     helper_retaddr = retaddr; |  | ||||||
|     return g2h(addr); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* Macro to call the above, with local variables from the use context.  */ |  | ||||||
| #define ATOMIC_MMU_DECLS do {} while (0) |  | ||||||
| #define ATOMIC_MMU_LOOKUP  atomic_mmu_lookup(env, addr, DATA_SIZE, GETPC()) |  | ||||||
| #define ATOMIC_MMU_CLEANUP do { helper_retaddr = 0; } while (0) |  | ||||||
|  |  | ||||||
| #define ATOMIC_NAME(X)   HELPER(glue(glue(atomic_ ## X, SUFFIX), END)) |  | ||||||
| #define EXTRA_ARGS |  | ||||||
|  |  | ||||||
| #define DATA_SIZE 1 |  | ||||||
| #include "atomic_template.h" |  | ||||||
|  |  | ||||||
| #define DATA_SIZE 2 |  | ||||||
| #include "atomic_template.h" |  | ||||||
|  |  | ||||||
| #define DATA_SIZE 4 |  | ||||||
| #include "atomic_template.h" |  | ||||||
|  |  | ||||||
| #ifdef CONFIG_ATOMIC64 |  | ||||||
| #define DATA_SIZE 8 |  | ||||||
| #include "atomic_template.h" |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| /* The following is only callable from other helpers, and matches up |  | ||||||
|    with the softmmu version.  */ |  | ||||||
|  |  | ||||||
| #ifdef CONFIG_ATOMIC128 |  | ||||||
|  |  | ||||||
| #undef EXTRA_ARGS |  | ||||||
| #undef ATOMIC_NAME |  | ||||||
| #undef ATOMIC_MMU_LOOKUP |  | ||||||
|  |  | ||||||
| #define EXTRA_ARGS     , TCGMemOpIdx oi, uintptr_t retaddr |  | ||||||
| #define ATOMIC_NAME(X) \ |  | ||||||
|     HELPER(glue(glue(glue(atomic_ ## X, SUFFIX), END), _mmu)) |  | ||||||
| #define ATOMIC_MMU_LOOKUP  atomic_mmu_lookup(env, addr, DATA_SIZE, retaddr) |  | ||||||
|  |  | ||||||
| #define DATA_SIZE 16 |  | ||||||
| #include "atomic_template.h" |  | ||||||
| #endif /* CONFIG_ATOMIC128 */ |  | ||||||
							
								
								
									
										26
									
								
								aes.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								aes.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | |||||||
|  | #ifndef QEMU_AES_H | ||||||
|  | #define QEMU_AES_H | ||||||
|  |  | ||||||
|  | #define AES_MAXNR 14 | ||||||
|  | #define AES_BLOCK_SIZE 16 | ||||||
|  |  | ||||||
|  | struct aes_key_st { | ||||||
|  |     uint32_t rd_key[4 *(AES_MAXNR + 1)]; | ||||||
|  |     int rounds; | ||||||
|  | }; | ||||||
|  | typedef struct aes_key_st AES_KEY; | ||||||
|  |  | ||||||
|  | int AES_set_encrypt_key(const unsigned char *userKey, const int bits, | ||||||
|  | 	AES_KEY *key); | ||||||
|  | int AES_set_decrypt_key(const unsigned char *userKey, const int bits, | ||||||
|  | 	AES_KEY *key); | ||||||
|  |  | ||||||
|  | void AES_encrypt(const unsigned char *in, unsigned char *out, | ||||||
|  | 	const AES_KEY *key); | ||||||
|  | void AES_decrypt(const unsigned char *in, unsigned char *out, | ||||||
|  | 	const AES_KEY *key); | ||||||
|  | void AES_cbc_encrypt(const unsigned char *in, unsigned char *out, | ||||||
|  | 		     const unsigned long length, const AES_KEY *key, | ||||||
|  | 		     unsigned char *ivec, const int enc); | ||||||
|  |  | ||||||
|  | #endif | ||||||
							
								
								
									
										1960
									
								
								alpha-dis.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1960
									
								
								alpha-dis.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										128
									
								
								alpha.ld
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										128
									
								
								alpha.ld
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,128 @@ | |||||||
|  | OUTPUT_FORMAT("elf64-alpha", "elf64-alpha", | ||||||
|  | 	      "elf64-alpha") | ||||||
|  | OUTPUT_ARCH(alpha) | ||||||
|  | ENTRY(__start) | ||||||
|  | SEARCH_DIR(/lib); SEARCH_DIR(/usr/lib); SEARCH_DIR(/usr/local/lib); SEARCH_DIR(/usr/alpha-unknown-linux-gnu/lib); | ||||||
|  | SECTIONS | ||||||
|  | { | ||||||
|  |   /* Read-only sections, merged into text segment: */ | ||||||
|  |   . = 0x60000000 + SIZEOF_HEADERS; | ||||||
|  |   .interp     : { *(.interp) 	} | ||||||
|  |   .hash          : { *(.hash)		} | ||||||
|  |   .dynsym        : { *(.dynsym)		} | ||||||
|  |   .dynstr        : { *(.dynstr)		} | ||||||
|  |   .gnu.version   : { *(.gnu.version)	} | ||||||
|  |   .gnu.version_d   : { *(.gnu.version_d)	} | ||||||
|  |   .gnu.version_r   : { *(.gnu.version_r)	} | ||||||
|  |   .rel.text      : | ||||||
|  |     { *(.rel.text) *(.rel.gnu.linkonce.t*) } | ||||||
|  |   .rela.text     : | ||||||
|  |     { *(.rela.text) *(.rela.gnu.linkonce.t*) } | ||||||
|  |   .rel.data      : | ||||||
|  |     { *(.rel.data) *(.rel.gnu.linkonce.d*) } | ||||||
|  |   .rela.data     : | ||||||
|  |     { *(.rela.data) *(.rela.gnu.linkonce.d*) } | ||||||
|  |   .rel.rodata    : | ||||||
|  |     { *(.rel.rodata) *(.rel.gnu.linkonce.r*) } | ||||||
|  |   .rela.rodata   : | ||||||
|  |     { *(.rela.rodata) *(.rela.gnu.linkonce.r*) } | ||||||
|  |   .rel.got       : { *(.rel.got)		} | ||||||
|  |   .rela.got      : { *(.rela.got)		} | ||||||
|  |   .rel.ctors     : { *(.rel.ctors)	} | ||||||
|  |   .rela.ctors    : { *(.rela.ctors)	} | ||||||
|  |   .rel.dtors     : { *(.rel.dtors)	} | ||||||
|  |   .rela.dtors    : { *(.rela.dtors)	} | ||||||
|  |   .rel.init      : { *(.rel.init)	} | ||||||
|  |   .rela.init     : { *(.rela.init)	} | ||||||
|  |   .rel.fini      : { *(.rel.fini)	} | ||||||
|  |   .rela.fini     : { *(.rela.fini)	} | ||||||
|  |   .rel.bss       : { *(.rel.bss)		} | ||||||
|  |   .rela.bss      : { *(.rela.bss)		} | ||||||
|  |   .rel.plt       : { *(.rel.plt)		} | ||||||
|  |   .rela.plt      : { *(.rela.plt)		} | ||||||
|  |   .init          : { *(.init)	} =0x47ff041f | ||||||
|  |   .text      : | ||||||
|  |   { | ||||||
|  |     *(.text) | ||||||
|  |     /* .gnu.warning sections are handled specially by elf32.em.  */ | ||||||
|  |     *(.gnu.warning) | ||||||
|  |     *(.gnu.linkonce.t*) | ||||||
|  |   } =0x47ff041f | ||||||
|  |   _etext = .; | ||||||
|  |   PROVIDE (etext = .); | ||||||
|  |   .fini      : { *(.fini)    } =0x47ff041f | ||||||
|  |   .rodata    : { *(.rodata) *(.gnu.linkonce.r*) } | ||||||
|  |   .rodata1   : { *(.rodata1) } | ||||||
|  |   .reginfo : { *(.reginfo) } | ||||||
|  |   /* Adjust the address for the data segment.  We want to adjust up to | ||||||
|  |      the same address within the page on the next page up.  */ | ||||||
|  |   . = ALIGN(0x100000) + (. & (0x100000 - 1)); | ||||||
|  |   .data    : | ||||||
|  |   { | ||||||
|  |     *(.data) | ||||||
|  |     *(.gnu.linkonce.d*) | ||||||
|  |     CONSTRUCTORS | ||||||
|  |   } | ||||||
|  |   .data1   : { *(.data1) } | ||||||
|  |   .ctors         : | ||||||
|  |   { | ||||||
|  |     *(.ctors) | ||||||
|  |   } | ||||||
|  |   .dtors         : | ||||||
|  |   { | ||||||
|  |     *(.dtors) | ||||||
|  |   } | ||||||
|  |   .plt      : { *(.plt)	} | ||||||
|  |   .got           : { *(.got.plt) *(.got) } | ||||||
|  |   .dynamic       : { *(.dynamic) } | ||||||
|  |   /* We want the small data sections together, so single-instruction offsets | ||||||
|  |      can access them all, and initialized data all before uninitialized, so | ||||||
|  |      we can shorten the on-disk segment size.  */ | ||||||
|  |   .sdata     : { *(.sdata) } | ||||||
|  |   _edata  =  .; | ||||||
|  |   PROVIDE (edata = .); | ||||||
|  |   __bss_start = .; | ||||||
|  |   .sbss      : { *(.sbss) *(.scommon) } | ||||||
|  |   .bss       : | ||||||
|  |   { | ||||||
|  |    *(.dynbss) | ||||||
|  |    *(.bss) | ||||||
|  |    *(COMMON) | ||||||
|  |   } | ||||||
|  |   _end = . ; | ||||||
|  |   PROVIDE (end = .); | ||||||
|  |   /* Stabs debugging sections.  */ | ||||||
|  |   .stab 0 : { *(.stab) } | ||||||
|  |   .stabstr 0 : { *(.stabstr) } | ||||||
|  |   .stab.excl 0 : { *(.stab.excl) } | ||||||
|  |   .stab.exclstr 0 : { *(.stab.exclstr) } | ||||||
|  |   .stab.index 0 : { *(.stab.index) } | ||||||
|  |   .stab.indexstr 0 : { *(.stab.indexstr) } | ||||||
|  |   .comment 0 : { *(.comment) } | ||||||
|  |   /* DWARF debug sections. | ||||||
|  |      Symbols in the DWARF debugging sections are relative to the beginning | ||||||
|  |      of the section so we begin them at 0.  */ | ||||||
|  |   /* DWARF 1 */ | ||||||
|  |   .debug          0 : { *(.debug) } | ||||||
|  |   .line           0 : { *(.line) } | ||||||
|  |   /* GNU DWARF 1 extensions */ | ||||||
|  |   .debug_srcinfo  0 : { *(.debug_srcinfo) } | ||||||
|  |   .debug_sfnames  0 : { *(.debug_sfnames) } | ||||||
|  |   /* DWARF 1.1 and DWARF 2 */ | ||||||
|  |   .debug_aranges  0 : { *(.debug_aranges) } | ||||||
|  |   .debug_pubnames 0 : { *(.debug_pubnames) } | ||||||
|  |   /* DWARF 2 */ | ||||||
|  |   .debug_info     0 : { *(.debug_info) } | ||||||
|  |   .debug_abbrev   0 : { *(.debug_abbrev) } | ||||||
|  |   .debug_line     0 : { *(.debug_line) } | ||||||
|  |   .debug_frame    0 : { *(.debug_frame) } | ||||||
|  |   .debug_str      0 : { *(.debug_str) } | ||||||
|  |   .debug_loc      0 : { *(.debug_loc) } | ||||||
|  |   .debug_macinfo  0 : { *(.debug_macinfo) } | ||||||
|  |   /* SGI/MIPS DWARF 2 extensions */ | ||||||
|  |   .debug_weaknames 0 : { *(.debug_weaknames) } | ||||||
|  |   .debug_funcnames 0 : { *(.debug_funcnames) } | ||||||
|  |   .debug_typenames 0 : { *(.debug_typenames) } | ||||||
|  |   .debug_varnames  0 : { *(.debug_varnames) } | ||||||
|  |   /* These must appear regardless of  .  */ | ||||||
|  | } | ||||||
							
								
								
									
										118
									
								
								arch_init.c
									
									
									
									
									
								
							
							
						
						
									
										118
									
								
								arch_init.c
									
									
									
									
									
								
							| @@ -1,118 +0,0 @@ | |||||||
| /* |  | ||||||
|  * QEMU System Emulator |  | ||||||
|  * |  | ||||||
|  * Copyright (c) 2003-2008 Fabrice Bellard |  | ||||||
|  * |  | ||||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy |  | ||||||
|  * of this software and associated documentation files (the "Software"), to deal |  | ||||||
|  * in the Software without restriction, including without limitation the rights |  | ||||||
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |  | ||||||
|  * copies of the Software, and to permit persons to whom the Software is |  | ||||||
|  * furnished to do so, subject to the following conditions: |  | ||||||
|  * |  | ||||||
|  * The above copyright notice and this permission notice shall be included in |  | ||||||
|  * all copies or substantial portions of the Software. |  | ||||||
|  * |  | ||||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |  | ||||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |  | ||||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |  | ||||||
|  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |  | ||||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |  | ||||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |  | ||||||
|  * THE SOFTWARE. |  | ||||||
|  */ |  | ||||||
| #include "qemu/osdep.h" |  | ||||||
| #include "qemu-common.h" |  | ||||||
| #include "cpu.h" |  | ||||||
| #include "sysemu/sysemu.h" |  | ||||||
| #include "sysemu/arch_init.h" |  | ||||||
| #include "hw/pci/pci.h" |  | ||||||
| #include "hw/audio/soundhw.h" |  | ||||||
| #include "qapi/qapi-commands-misc.h" |  | ||||||
| #include "qemu/config-file.h" |  | ||||||
| #include "qemu/error-report.h" |  | ||||||
| #include "hw/acpi/acpi.h" |  | ||||||
| #include "qemu/help_option.h" |  | ||||||
|  |  | ||||||
| #ifdef TARGET_SPARC |  | ||||||
| int graphic_width = 1024; |  | ||||||
| int graphic_height = 768; |  | ||||||
| int graphic_depth = 8; |  | ||||||
| #else |  | ||||||
| int graphic_width = 800; |  | ||||||
| int graphic_height = 600; |  | ||||||
| int graphic_depth = 32; |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
|  |  | ||||||
| #if defined(TARGET_ALPHA) |  | ||||||
| #define QEMU_ARCH QEMU_ARCH_ALPHA |  | ||||||
| #elif defined(TARGET_ARM) |  | ||||||
| #define QEMU_ARCH QEMU_ARCH_ARM |  | ||||||
| #elif defined(TARGET_CRIS) |  | ||||||
| #define QEMU_ARCH QEMU_ARCH_CRIS |  | ||||||
| #elif defined(TARGET_I386) |  | ||||||
| #define QEMU_ARCH QEMU_ARCH_I386 |  | ||||||
| #elif defined(TARGET_HPPA) |  | ||||||
| #define QEMU_ARCH QEMU_ARCH_HPPA |  | ||||||
| #elif defined(TARGET_M68K) |  | ||||||
| #define QEMU_ARCH QEMU_ARCH_M68K |  | ||||||
| #elif defined(TARGET_LM32) |  | ||||||
| #define QEMU_ARCH QEMU_ARCH_LM32 |  | ||||||
| #elif defined(TARGET_MICROBLAZE) |  | ||||||
| #define QEMU_ARCH QEMU_ARCH_MICROBLAZE |  | ||||||
| #elif defined(TARGET_MIPS) |  | ||||||
| #define QEMU_ARCH QEMU_ARCH_MIPS |  | ||||||
| #elif defined(TARGET_MOXIE) |  | ||||||
| #define QEMU_ARCH QEMU_ARCH_MOXIE |  | ||||||
| #elif defined(TARGET_NIOS2) |  | ||||||
| #define QEMU_ARCH QEMU_ARCH_NIOS2 |  | ||||||
| #elif defined(TARGET_OPENRISC) |  | ||||||
| #define QEMU_ARCH QEMU_ARCH_OPENRISC |  | ||||||
| #elif defined(TARGET_PPC) |  | ||||||
| #define QEMU_ARCH QEMU_ARCH_PPC |  | ||||||
| #elif defined(TARGET_RISCV) |  | ||||||
| #define QEMU_ARCH QEMU_ARCH_RISCV |  | ||||||
| #elif defined(TARGET_S390X) |  | ||||||
| #define QEMU_ARCH QEMU_ARCH_S390X |  | ||||||
| #elif defined(TARGET_SH4) |  | ||||||
| #define QEMU_ARCH QEMU_ARCH_SH4 |  | ||||||
| #elif defined(TARGET_SPARC) |  | ||||||
| #define QEMU_ARCH QEMU_ARCH_SPARC |  | ||||||
| #elif defined(TARGET_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 |  | ||||||
|  |  | ||||||
| const uint32_t arch_type = QEMU_ARCH; |  | ||||||
|  |  | ||||||
| int kvm_available(void) |  | ||||||
| { |  | ||||||
| #ifdef CONFIG_KVM |  | ||||||
|     return 1; |  | ||||||
| #else |  | ||||||
|     return 0; |  | ||||||
| #endif |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int xen_available(void) |  | ||||||
| { |  | ||||||
| #ifdef CONFIG_XEN |  | ||||||
|     return 1; |  | ||||||
| #else |  | ||||||
|     return 0; |  | ||||||
| #endif |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| TargetInfo *qmp_query_target(Error **errp) |  | ||||||
| { |  | ||||||
|     TargetInfo *info = g_malloc0(sizeof(*info)); |  | ||||||
|  |  | ||||||
|     info->arch = g_strdup(TARGET_NAME); |  | ||||||
|  |  | ||||||
|     return info; |  | ||||||
| } |  | ||||||
							
								
								
									
										468
									
								
								arm-semi.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										468
									
								
								arm-semi.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,468 @@ | |||||||
|  | /* | ||||||
|  |  *  Arm "Angel" semihosting syscalls | ||||||
|  |  * | ||||||
|  |  *  Copyright (c) 2005, 2007 CodeSourcery. | ||||||
|  |  *  Written by Paul Brook. | ||||||
|  |  * | ||||||
|  |  *  This program is free software; you can redistribute it and/or modify | ||||||
|  |  *  it under the terms of the GNU General Public License as published by | ||||||
|  |  *  the Free Software Foundation; either version 2 of the License, or | ||||||
|  |  *  (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  *  This program is distributed in the hope that it will be useful, | ||||||
|  |  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  *  GNU General Public License for more details. | ||||||
|  |  * | ||||||
|  |  *  You should have received a copy of the GNU General Public License | ||||||
|  |  *  along with this program; if not, write to the Free Software | ||||||
|  |  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #include <sys/types.h> | ||||||
|  | #include <sys/stat.h> | ||||||
|  | #include <fcntl.h> | ||||||
|  | #include <unistd.h> | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <stdio.h> | ||||||
|  | #include <time.h> | ||||||
|  |  | ||||||
|  | #include "cpu.h" | ||||||
|  | #ifdef CONFIG_USER_ONLY | ||||||
|  | #include "qemu.h" | ||||||
|  |  | ||||||
|  | #define ARM_ANGEL_HEAP_SIZE (128 * 1024 * 1024) | ||||||
|  | #else | ||||||
|  | #include "qemu-common.h" | ||||||
|  | #include "sysemu.h" | ||||||
|  | #include "gdbstub.h" | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #define SYS_OPEN        0x01 | ||||||
|  | #define SYS_CLOSE       0x02 | ||||||
|  | #define SYS_WRITEC      0x03 | ||||||
|  | #define SYS_WRITE0      0x04 | ||||||
|  | #define SYS_WRITE       0x05 | ||||||
|  | #define SYS_READ        0x06 | ||||||
|  | #define SYS_READC       0x07 | ||||||
|  | #define SYS_ISTTY       0x09 | ||||||
|  | #define SYS_SEEK        0x0a | ||||||
|  | #define SYS_FLEN        0x0c | ||||||
|  | #define SYS_TMPNAM      0x0d | ||||||
|  | #define SYS_REMOVE      0x0e | ||||||
|  | #define SYS_RENAME      0x0f | ||||||
|  | #define SYS_CLOCK       0x10 | ||||||
|  | #define SYS_TIME        0x11 | ||||||
|  | #define SYS_SYSTEM      0x12 | ||||||
|  | #define SYS_ERRNO       0x13 | ||||||
|  | #define SYS_GET_CMDLINE 0x15 | ||||||
|  | #define SYS_HEAPINFO    0x16 | ||||||
|  | #define SYS_EXIT        0x18 | ||||||
|  |  | ||||||
|  | #ifndef O_BINARY | ||||||
|  | #define O_BINARY 0 | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #define GDB_O_RDONLY  0x000 | ||||||
|  | #define GDB_O_WRONLY  0x001 | ||||||
|  | #define GDB_O_RDWR    0x002 | ||||||
|  | #define GDB_O_APPEND  0x008 | ||||||
|  | #define GDB_O_CREAT   0x200 | ||||||
|  | #define GDB_O_TRUNC   0x400 | ||||||
|  | #define GDB_O_BINARY  0 | ||||||
|  |  | ||||||
|  | static int gdb_open_modeflags[12] = { | ||||||
|  |     GDB_O_RDONLY, | ||||||
|  |     GDB_O_RDONLY | GDB_O_BINARY, | ||||||
|  |     GDB_O_RDWR, | ||||||
|  |     GDB_O_RDWR | GDB_O_BINARY, | ||||||
|  |     GDB_O_WRONLY | GDB_O_CREAT | GDB_O_TRUNC, | ||||||
|  |     GDB_O_WRONLY | GDB_O_CREAT | GDB_O_TRUNC | GDB_O_BINARY, | ||||||
|  |     GDB_O_RDWR | GDB_O_CREAT | GDB_O_TRUNC, | ||||||
|  |     GDB_O_RDWR | GDB_O_CREAT | GDB_O_TRUNC | GDB_O_BINARY, | ||||||
|  |     GDB_O_WRONLY | GDB_O_CREAT | GDB_O_APPEND, | ||||||
|  |     GDB_O_WRONLY | GDB_O_CREAT | GDB_O_APPEND | GDB_O_BINARY, | ||||||
|  |     GDB_O_RDWR | GDB_O_CREAT | GDB_O_APPEND, | ||||||
|  |     GDB_O_RDWR | GDB_O_CREAT | GDB_O_APPEND | GDB_O_BINARY | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static int open_modeflags[12] = { | ||||||
|  |     O_RDONLY, | ||||||
|  |     O_RDONLY | O_BINARY, | ||||||
|  |     O_RDWR, | ||||||
|  |     O_RDWR | O_BINARY, | ||||||
|  |     O_WRONLY | O_CREAT | O_TRUNC, | ||||||
|  |     O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, | ||||||
|  |     O_RDWR | O_CREAT | O_TRUNC, | ||||||
|  |     O_RDWR | O_CREAT | O_TRUNC | O_BINARY, | ||||||
|  |     O_WRONLY | O_CREAT | O_APPEND, | ||||||
|  |     O_WRONLY | O_CREAT | O_APPEND | O_BINARY, | ||||||
|  |     O_RDWR | O_CREAT | O_APPEND, | ||||||
|  |     O_RDWR | O_CREAT | O_APPEND | O_BINARY | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | #ifdef CONFIG_USER_ONLY | ||||||
|  | static inline uint32_t set_swi_errno(TaskState *ts, uint32_t code) | ||||||
|  | { | ||||||
|  |     if (code == (uint32_t)-1) | ||||||
|  |         ts->swi_errno = errno; | ||||||
|  |     return code; | ||||||
|  | } | ||||||
|  | #else | ||||||
|  | static inline uint32_t set_swi_errno(CPUState *env, uint32_t code) | ||||||
|  | { | ||||||
|  |     return code; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #include "softmmu-semi.h" | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | static target_ulong arm_semi_syscall_len; | ||||||
|  |  | ||||||
|  | #if !defined(CONFIG_USER_ONLY) | ||||||
|  | static target_ulong syscall_err; | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | static void arm_semi_cb(CPUState *env, target_ulong ret, target_ulong err) | ||||||
|  | { | ||||||
|  | #ifdef CONFIG_USER_ONLY | ||||||
|  |     TaskState *ts = env->opaque; | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |     if (ret == (target_ulong)-1) { | ||||||
|  | #ifdef CONFIG_USER_ONLY | ||||||
|  |         ts->swi_errno = err; | ||||||
|  | #else | ||||||
|  | 	syscall_err = err; | ||||||
|  | #endif | ||||||
|  |         env->regs[0] = ret; | ||||||
|  |     } else { | ||||||
|  |         /* Fixup syscalls that use nonstardard return conventions.  */ | ||||||
|  |         switch (env->regs[0]) { | ||||||
|  |         case SYS_WRITE: | ||||||
|  |         case SYS_READ: | ||||||
|  |             env->regs[0] = arm_semi_syscall_len - ret; | ||||||
|  |             break; | ||||||
|  |         case SYS_SEEK: | ||||||
|  |             env->regs[0] = 0; | ||||||
|  |             break; | ||||||
|  |         default: | ||||||
|  |             env->regs[0] = ret; | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void arm_semi_flen_cb(CPUState *env, target_ulong ret, target_ulong err) | ||||||
|  | { | ||||||
|  |     /* The size is always stored in big-endian order, extract | ||||||
|  |        the value. We assume the size always fit in 32 bits.  */ | ||||||
|  |     uint32_t size; | ||||||
|  |     cpu_memory_rw_debug(env, env->regs[13]-64+32, (uint8_t *)&size, 4, 0); | ||||||
|  |     env->regs[0] = be32_to_cpu(size); | ||||||
|  | #ifdef CONFIG_USER_ONLY | ||||||
|  |     ((TaskState *)env->opaque)->swi_errno = err; | ||||||
|  | #else | ||||||
|  |     syscall_err = err; | ||||||
|  | #endif | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #define ARG(n)					\ | ||||||
|  | ({						\ | ||||||
|  |     target_ulong __arg;				\ | ||||||
|  |     /* FIXME - handle get_user() failure */	\ | ||||||
|  |     get_user_ual(__arg, args + (n) * 4);	\ | ||||||
|  |     __arg;					\ | ||||||
|  | }) | ||||||
|  | #define SET_ARG(n, val) put_user_ual(val, args + (n) * 4) | ||||||
|  | uint32_t do_arm_semihosting(CPUState *env) | ||||||
|  | { | ||||||
|  |     target_ulong args; | ||||||
|  |     char * s; | ||||||
|  |     int nr; | ||||||
|  |     uint32_t ret; | ||||||
|  |     uint32_t len; | ||||||
|  | #ifdef CONFIG_USER_ONLY | ||||||
|  |     TaskState *ts = env->opaque; | ||||||
|  | #else | ||||||
|  |     CPUState *ts = env; | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |     nr = env->regs[0]; | ||||||
|  |     args = env->regs[1]; | ||||||
|  |     switch (nr) { | ||||||
|  |     case SYS_OPEN: | ||||||
|  |         if (!(s = lock_user_string(ARG(0)))) | ||||||
|  |             /* FIXME - should this error code be -TARGET_EFAULT ? */ | ||||||
|  |             return (uint32_t)-1; | ||||||
|  |         if (ARG(1) >= 12) | ||||||
|  |             return (uint32_t)-1; | ||||||
|  |         if (strcmp(s, ":tt") == 0) { | ||||||
|  |             if (ARG(1) < 4) | ||||||
|  |                 return STDIN_FILENO; | ||||||
|  |             else | ||||||
|  |                 return STDOUT_FILENO; | ||||||
|  |         } | ||||||
|  |         if (use_gdb_syscalls()) { | ||||||
|  |             gdb_do_syscall(arm_semi_cb, "open,%s,%x,1a4", ARG(0), | ||||||
|  | 			   (int)ARG(2)+1, gdb_open_modeflags[ARG(1)]); | ||||||
|  |             return env->regs[0]; | ||||||
|  |         } else { | ||||||
|  |             ret = set_swi_errno(ts, open(s, open_modeflags[ARG(1)], 0644)); | ||||||
|  |         } | ||||||
|  |         unlock_user(s, ARG(0), 0); | ||||||
|  |         return ret; | ||||||
|  |     case SYS_CLOSE: | ||||||
|  |         if (use_gdb_syscalls()) { | ||||||
|  |             gdb_do_syscall(arm_semi_cb, "close,%x", ARG(0)); | ||||||
|  |             return env->regs[0]; | ||||||
|  |         } else { | ||||||
|  |             return set_swi_errno(ts, close(ARG(0))); | ||||||
|  |         } | ||||||
|  |     case SYS_WRITEC: | ||||||
|  |         { | ||||||
|  |           char c; | ||||||
|  |  | ||||||
|  |           if (get_user_u8(c, args)) | ||||||
|  |               /* FIXME - should this error code be -TARGET_EFAULT ? */ | ||||||
|  |               return (uint32_t)-1; | ||||||
|  |           /* Write to debug console.  stderr is near enough.  */ | ||||||
|  |           if (use_gdb_syscalls()) { | ||||||
|  |                 gdb_do_syscall(arm_semi_cb, "write,2,%x,1", args); | ||||||
|  |                 return env->regs[0]; | ||||||
|  |           } else { | ||||||
|  |                 return write(STDERR_FILENO, &c, 1); | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |     case SYS_WRITE0: | ||||||
|  |         if (!(s = lock_user_string(args))) | ||||||
|  |             /* FIXME - should this error code be -TARGET_EFAULT ? */ | ||||||
|  |             return (uint32_t)-1; | ||||||
|  |         len = strlen(s); | ||||||
|  |         if (use_gdb_syscalls()) { | ||||||
|  |             gdb_do_syscall(arm_semi_cb, "write,2,%x,%x\n", args, len); | ||||||
|  |             ret = env->regs[0]; | ||||||
|  |         } else { | ||||||
|  |             ret = write(STDERR_FILENO, s, len); | ||||||
|  |         } | ||||||
|  |         unlock_user(s, args, 0); | ||||||
|  |         return ret; | ||||||
|  |     case SYS_WRITE: | ||||||
|  |         len = ARG(2); | ||||||
|  |         if (use_gdb_syscalls()) { | ||||||
|  |             arm_semi_syscall_len = len; | ||||||
|  |             gdb_do_syscall(arm_semi_cb, "write,%x,%x,%x", ARG(0), ARG(1), len); | ||||||
|  |             return env->regs[0]; | ||||||
|  |         } else { | ||||||
|  |             if (!(s = lock_user(VERIFY_READ, ARG(1), len, 1))) | ||||||
|  |                 /* FIXME - should this error code be -TARGET_EFAULT ? */ | ||||||
|  |                 return (uint32_t)-1; | ||||||
|  |             ret = set_swi_errno(ts, write(ARG(0), s, len)); | ||||||
|  |             unlock_user(s, ARG(1), 0); | ||||||
|  |             if (ret == (uint32_t)-1) | ||||||
|  |                 return -1; | ||||||
|  |             return len - ret; | ||||||
|  |         } | ||||||
|  |     case SYS_READ: | ||||||
|  |         len = ARG(2); | ||||||
|  |         if (use_gdb_syscalls()) { | ||||||
|  |             arm_semi_syscall_len = len; | ||||||
|  |             gdb_do_syscall(arm_semi_cb, "read,%x,%x,%x", ARG(0), ARG(1), len); | ||||||
|  |             return env->regs[0]; | ||||||
|  |         } else { | ||||||
|  |             if (!(s = lock_user(VERIFY_WRITE, ARG(1), len, 0))) | ||||||
|  |                 /* FIXME - should this error code be -TARGET_EFAULT ? */ | ||||||
|  |                 return (uint32_t)-1; | ||||||
|  |             do | ||||||
|  |               ret = set_swi_errno(ts, read(ARG(0), s, len)); | ||||||
|  |             while (ret == -1 && errno == EINTR); | ||||||
|  |             unlock_user(s, ARG(1), len); | ||||||
|  |             if (ret == (uint32_t)-1) | ||||||
|  |                 return -1; | ||||||
|  |             return len - ret; | ||||||
|  |         } | ||||||
|  |     case SYS_READC: | ||||||
|  |        /* XXX: Read from debug cosole. Not implemented.  */ | ||||||
|  |         return 0; | ||||||
|  |     case SYS_ISTTY: | ||||||
|  |         if (use_gdb_syscalls()) { | ||||||
|  |             gdb_do_syscall(arm_semi_cb, "isatty,%x", ARG(0)); | ||||||
|  |             return env->regs[0]; | ||||||
|  |         } else { | ||||||
|  |             return isatty(ARG(0)); | ||||||
|  |         } | ||||||
|  |     case SYS_SEEK: | ||||||
|  |         if (use_gdb_syscalls()) { | ||||||
|  |             gdb_do_syscall(arm_semi_cb, "lseek,%x,%x,0", ARG(0), ARG(1)); | ||||||
|  |             return env->regs[0]; | ||||||
|  |         } else { | ||||||
|  |             ret = set_swi_errno(ts, lseek(ARG(0), ARG(1), SEEK_SET)); | ||||||
|  |             if (ret == (uint32_t)-1) | ||||||
|  |               return -1; | ||||||
|  |             return 0; | ||||||
|  |         } | ||||||
|  |     case SYS_FLEN: | ||||||
|  |         if (use_gdb_syscalls()) { | ||||||
|  |             gdb_do_syscall(arm_semi_flen_cb, "fstat,%x,%x", | ||||||
|  | 			   ARG(0), env->regs[13]-64); | ||||||
|  |             return env->regs[0]; | ||||||
|  |         } else { | ||||||
|  |             struct stat buf; | ||||||
|  |             ret = set_swi_errno(ts, fstat(ARG(0), &buf)); | ||||||
|  |             if (ret == (uint32_t)-1) | ||||||
|  |                 return -1; | ||||||
|  |             return buf.st_size; | ||||||
|  |         } | ||||||
|  |     case SYS_TMPNAM: | ||||||
|  |         /* XXX: Not implemented.  */ | ||||||
|  |         return -1; | ||||||
|  |     case SYS_REMOVE: | ||||||
|  |         if (use_gdb_syscalls()) { | ||||||
|  |             gdb_do_syscall(arm_semi_cb, "unlink,%s", ARG(0), (int)ARG(1)+1); | ||||||
|  |             ret = env->regs[0]; | ||||||
|  |         } else { | ||||||
|  |             if (!(s = lock_user_string(ARG(0)))) | ||||||
|  |                 /* FIXME - should this error code be -TARGET_EFAULT ? */ | ||||||
|  |                 return (uint32_t)-1; | ||||||
|  |             ret =  set_swi_errno(ts, remove(s)); | ||||||
|  |             unlock_user(s, ARG(0), 0); | ||||||
|  |         } | ||||||
|  |         return ret; | ||||||
|  |     case SYS_RENAME: | ||||||
|  |         if (use_gdb_syscalls()) { | ||||||
|  |             gdb_do_syscall(arm_semi_cb, "rename,%s,%s", | ||||||
|  |                            ARG(0), (int)ARG(1)+1, ARG(2), (int)ARG(3)+1); | ||||||
|  |             return env->regs[0]; | ||||||
|  |         } else { | ||||||
|  |             char *s2; | ||||||
|  |             s = lock_user_string(ARG(0)); | ||||||
|  |             s2 = lock_user_string(ARG(2)); | ||||||
|  |             if (!s || !s2) | ||||||
|  |                 /* FIXME - should this error code be -TARGET_EFAULT ? */ | ||||||
|  |                 ret = (uint32_t)-1; | ||||||
|  |             else | ||||||
|  |                 ret = set_swi_errno(ts, rename(s, s2)); | ||||||
|  |             if (s2) | ||||||
|  |                 unlock_user(s2, ARG(2), 0); | ||||||
|  |             if (s) | ||||||
|  |                 unlock_user(s, ARG(0), 0); | ||||||
|  |             return ret; | ||||||
|  |         } | ||||||
|  |     case SYS_CLOCK: | ||||||
|  |         return clock() / (CLOCKS_PER_SEC / 100); | ||||||
|  |     case SYS_TIME: | ||||||
|  |         return set_swi_errno(ts, time(NULL)); | ||||||
|  |     case SYS_SYSTEM: | ||||||
|  |         if (use_gdb_syscalls()) { | ||||||
|  |             gdb_do_syscall(arm_semi_cb, "system,%s", ARG(0), (int)ARG(1)+1); | ||||||
|  |             return env->regs[0]; | ||||||
|  |         } else { | ||||||
|  |             if (!(s = lock_user_string(ARG(0)))) | ||||||
|  |                 /* FIXME - should this error code be -TARGET_EFAULT ? */ | ||||||
|  |                 return (uint32_t)-1; | ||||||
|  |             ret = set_swi_errno(ts, system(s)); | ||||||
|  |             unlock_user(s, ARG(0), 0); | ||||||
|  |         } | ||||||
|  |     case SYS_ERRNO: | ||||||
|  | #ifdef CONFIG_USER_ONLY | ||||||
|  |         return ts->swi_errno; | ||||||
|  | #else | ||||||
|  |         return syscall_err; | ||||||
|  | #endif | ||||||
|  |     case SYS_GET_CMDLINE: | ||||||
|  | #ifdef CONFIG_USER_ONLY | ||||||
|  |         /* Build a commandline from the original argv.  */ | ||||||
|  |         { | ||||||
|  |             char **arg = ts->info->host_argv; | ||||||
|  |             int len = ARG(1); | ||||||
|  |             /* lock the buffer on the ARM side */ | ||||||
|  |             char *cmdline_buffer = (char*)lock_user(VERIFY_WRITE, ARG(0), len, 0); | ||||||
|  |  | ||||||
|  |             if (!cmdline_buffer) | ||||||
|  |                 /* FIXME - should this error code be -TARGET_EFAULT ? */ | ||||||
|  |                 return (uint32_t)-1; | ||||||
|  |  | ||||||
|  |             s = cmdline_buffer; | ||||||
|  |             while (*arg && len > 2) { | ||||||
|  |                 int n = strlen(*arg); | ||||||
|  |  | ||||||
|  |                 if (s != cmdline_buffer) { | ||||||
|  |                     *(s++) = ' '; | ||||||
|  |                     len--; | ||||||
|  |                 } | ||||||
|  |                 if (n >= len) | ||||||
|  |                     n = len - 1; | ||||||
|  |                 memcpy(s, *arg, n); | ||||||
|  |                 s += n; | ||||||
|  |                 len -= n; | ||||||
|  |                 arg++; | ||||||
|  |             } | ||||||
|  |             /* Null terminate the string.  */ | ||||||
|  |             *s = 0; | ||||||
|  |             len = s - cmdline_buffer; | ||||||
|  |  | ||||||
|  |             /* Unlock the buffer on the ARM side.  */ | ||||||
|  |             unlock_user(cmdline_buffer, ARG(0), len); | ||||||
|  |  | ||||||
|  |             /* Adjust the commandline length argument.  */ | ||||||
|  |             SET_ARG(1, len); | ||||||
|  |  | ||||||
|  |             /* Return success if commandline fit into buffer.  */ | ||||||
|  |             return *arg ? -1 : 0; | ||||||
|  |         } | ||||||
|  | #else | ||||||
|  |       return -1; | ||||||
|  | #endif | ||||||
|  |     case SYS_HEAPINFO: | ||||||
|  |         { | ||||||
|  |             uint32_t *ptr; | ||||||
|  |             uint32_t limit; | ||||||
|  |  | ||||||
|  | #ifdef CONFIG_USER_ONLY | ||||||
|  |             /* Some C libraries assume the heap immediately follows .bss, so | ||||||
|  |                allocate it using sbrk.  */ | ||||||
|  |             if (!ts->heap_limit) { | ||||||
|  |                 long ret; | ||||||
|  |  | ||||||
|  |                 ts->heap_base = do_brk(0); | ||||||
|  |                 limit = ts->heap_base + ARM_ANGEL_HEAP_SIZE; | ||||||
|  |                 /* Try a big heap, and reduce the size if that fails.  */ | ||||||
|  |                 for (;;) { | ||||||
|  |                     ret = do_brk(limit); | ||||||
|  |                     if (ret != -1) | ||||||
|  |                         break; | ||||||
|  |                     limit = (ts->heap_base >> 1) + (limit >> 1); | ||||||
|  |                 } | ||||||
|  |                 ts->heap_limit = limit; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             if (!(ptr = lock_user(VERIFY_WRITE, ARG(0), 16, 0))) | ||||||
|  |                 /* FIXME - should this error code be -TARGET_EFAULT ? */ | ||||||
|  |                 return (uint32_t)-1; | ||||||
|  |             ptr[0] = tswap32(ts->heap_base); | ||||||
|  |             ptr[1] = tswap32(ts->heap_limit); | ||||||
|  |             ptr[2] = tswap32(ts->stack_base); | ||||||
|  |             ptr[3] = tswap32(0); /* Stack limit.  */ | ||||||
|  |             unlock_user(ptr, ARG(0), 16); | ||||||
|  | #else | ||||||
|  |             limit = ram_size; | ||||||
|  |             if (!(ptr = lock_user(VERIFY_WRITE, ARG(0), 16, 0))) | ||||||
|  |                 /* FIXME - should this error code be -TARGET_EFAULT ? */ | ||||||
|  |                 return (uint32_t)-1; | ||||||
|  |             /* TODO: Make this use the limit of the loaded application.  */ | ||||||
|  |             ptr[0] = tswap32(limit / 2); | ||||||
|  |             ptr[1] = tswap32(limit); | ||||||
|  |             ptr[2] = tswap32(limit); /* Stack base */ | ||||||
|  |             ptr[3] = tswap32(0); /* Stack limit.  */ | ||||||
|  |             unlock_user(ptr, ARG(0), 16); | ||||||
|  | #endif | ||||||
|  |             return 0; | ||||||
|  |         } | ||||||
|  |     case SYS_EXIT: | ||||||
|  |         exit(0); | ||||||
|  |     default: | ||||||
|  |         fprintf(stderr, "qemu: Unsupported SemiHosting SWI 0x%02x\n", nr); | ||||||
|  |         cpu_dump_state(env, stderr, fprintf, 0); | ||||||
|  |         abort(); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										153
									
								
								arm.ld
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										153
									
								
								arm.ld
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,153 @@ | |||||||
|  | OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", | ||||||
|  | 	      "elf32-littlearm") | ||||||
|  | OUTPUT_ARCH(arm) | ||||||
|  | ENTRY(_start) | ||||||
|  | SEARCH_DIR(/lib); SEARCH_DIR(/usr/lib); SEARCH_DIR(/usr/local/lib); SEARCH_DIR(/usr/alpha-unknown-linux-gnu/lib); | ||||||
|  | SECTIONS | ||||||
|  | { | ||||||
|  |   /* Read-only sections, merged into text segment: */ | ||||||
|  |   . = 0x60000000 + SIZEOF_HEADERS; | ||||||
|  |   .interp     : { *(.interp) 	} | ||||||
|  |   .hash          : { *(.hash)		} | ||||||
|  |   .dynsym        : { *(.dynsym)		} | ||||||
|  |   .dynstr        : { *(.dynstr)		} | ||||||
|  |   .gnu.version   : { *(.gnu.version)	} | ||||||
|  |   .gnu.version_d   : { *(.gnu.version_d)	} | ||||||
|  |   .gnu.version_r   : { *(.gnu.version_r)	} | ||||||
|  |   .rel.text      : | ||||||
|  |     { *(.rel.text) *(.rel.gnu.linkonce.t*) } | ||||||
|  |   .rela.text     : | ||||||
|  |     { *(.rela.text) *(.rela.gnu.linkonce.t*) } | ||||||
|  |   .rel.data      : | ||||||
|  |     { *(.rel.data) *(.rel.gnu.linkonce.d*) } | ||||||
|  |   .rela.data     : | ||||||
|  |     { *(.rela.data) *(.rela.gnu.linkonce.d*) } | ||||||
|  |   .rel.rodata    : | ||||||
|  |     { *(.rel.rodata) *(.rel.gnu.linkonce.r*) } | ||||||
|  |   .rela.rodata   : | ||||||
|  |     { *(.rela.rodata) *(.rela.gnu.linkonce.r*) } | ||||||
|  |   .rel.got       : { *(.rel.got)		} | ||||||
|  |   .rela.got      : { *(.rela.got)		} | ||||||
|  |   .rel.ctors     : { *(.rel.ctors)	} | ||||||
|  |   .rela.ctors    : { *(.rela.ctors)	} | ||||||
|  |   .rel.dtors     : { *(.rel.dtors)	} | ||||||
|  |   .rela.dtors    : { *(.rela.dtors)	} | ||||||
|  |   .rel.init      : { *(.rel.init)	} | ||||||
|  |   .rela.init     : { *(.rela.init)	} | ||||||
|  |   .rel.fini      : { *(.rel.fini)	} | ||||||
|  |   .rela.fini     : { *(.rela.fini)	} | ||||||
|  |   .rel.bss       : { *(.rel.bss)		} | ||||||
|  |   .rela.bss      : { *(.rela.bss)		} | ||||||
|  |   .rel.plt       : { *(.rel.plt)		} | ||||||
|  |   .rela.plt      : { *(.rela.plt)		} | ||||||
|  |   .init          : { *(.init)	} =0x47ff041f | ||||||
|  |   .text      : | ||||||
|  |   { | ||||||
|  |     *(.text) | ||||||
|  |     /* .gnu.warning sections are handled specially by elf32.em.  */ | ||||||
|  |     *(.gnu.warning) | ||||||
|  |     *(.gnu.linkonce.t*) | ||||||
|  |   } =0x47ff041f | ||||||
|  |   _etext = .; | ||||||
|  |   PROVIDE (etext = .); | ||||||
|  |   .fini      : { *(.fini)    } =0x47ff041f | ||||||
|  |   .rodata    : { *(.rodata) *(.gnu.linkonce.r*) } | ||||||
|  |   .rodata1   : { *(.rodata1) } | ||||||
|  |   .ARM.extab   : { *(.ARM.extab* .gnu.linkonce.armextab.*) } | ||||||
|  |    __exidx_start = .; | ||||||
|  |   .ARM.exidx   : { *(.ARM.exidx* .gnu.linkonce.armexidx.*) } | ||||||
|  |    __exidx_end = .; | ||||||
|  |   .reginfo : { *(.reginfo) } | ||||||
|  |   /* Adjust the address for the data segment.  We want to adjust up to | ||||||
|  |      the same address within the page on the next page up.  */ | ||||||
|  |   . = ALIGN(0x100000) + (. & (0x100000 - 1)); | ||||||
|  |   .data    : | ||||||
|  |   { | ||||||
|  |     *(.data) | ||||||
|  |     *(.gnu.linkonce.d*) | ||||||
|  |     CONSTRUCTORS | ||||||
|  |   } | ||||||
|  |   .tbss           : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) } | ||||||
|  |   .data1   : { *(.data1) } | ||||||
|  |   .preinit_array     : | ||||||
|  |   { | ||||||
|  |     PROVIDE_HIDDEN (__preinit_array_start = .); | ||||||
|  |     KEEP (*(.preinit_array)) | ||||||
|  |     PROVIDE_HIDDEN (__preinit_array_end = .); | ||||||
|  |   } | ||||||
|  |   .init_array     : | ||||||
|  |   { | ||||||
|  |      PROVIDE_HIDDEN (__init_array_start = .); | ||||||
|  |      KEEP (*(SORT(.init_array.*))) | ||||||
|  |      KEEP (*(.init_array)) | ||||||
|  |      PROVIDE_HIDDEN (__init_array_end = .); | ||||||
|  |   } | ||||||
|  |   .fini_array     : | ||||||
|  |   { | ||||||
|  |     PROVIDE_HIDDEN (__fini_array_start = .); | ||||||
|  |     KEEP (*(.fini_array)) | ||||||
|  |     KEEP (*(SORT(.fini_array.*))) | ||||||
|  |     PROVIDE_HIDDEN (__fini_array_end = .); | ||||||
|  |   } | ||||||
|  |   .ctors         : | ||||||
|  |   { | ||||||
|  |     *(.ctors) | ||||||
|  |   } | ||||||
|  |   .dtors         : | ||||||
|  |   { | ||||||
|  |     *(.dtors) | ||||||
|  |   } | ||||||
|  |   .plt      : { *(.plt)	} | ||||||
|  |   .got           : { *(.got.plt) *(.got) } | ||||||
|  |   .dynamic       : { *(.dynamic) } | ||||||
|  |   /* We want the small data sections together, so single-instruction offsets | ||||||
|  |      can access them all, and initialized data all before uninitialized, so | ||||||
|  |      we can shorten the on-disk segment size.  */ | ||||||
|  |   .sdata     : { *(.sdata) } | ||||||
|  |   _edata  =  .; | ||||||
|  |   PROVIDE (edata = .); | ||||||
|  |   __bss_start = .; | ||||||
|  |   .sbss      : { *(.sbss) *(.scommon) } | ||||||
|  |   .bss       : | ||||||
|  |   { | ||||||
|  |    *(.dynbss) | ||||||
|  |    *(.bss) | ||||||
|  |    *(COMMON) | ||||||
|  |   } | ||||||
|  |   _end = . ; | ||||||
|  |   PROVIDE (end = .); | ||||||
|  |   /* Stabs debugging sections.  */ | ||||||
|  |   .stab 0 : { *(.stab) } | ||||||
|  |   .stabstr 0 : { *(.stabstr) } | ||||||
|  |   .stab.excl 0 : { *(.stab.excl) } | ||||||
|  |   .stab.exclstr 0 : { *(.stab.exclstr) } | ||||||
|  |   .stab.index 0 : { *(.stab.index) } | ||||||
|  |   .stab.indexstr 0 : { *(.stab.indexstr) } | ||||||
|  |   .comment 0 : { *(.comment) } | ||||||
|  |   /* DWARF debug sections. | ||||||
|  |      Symbols in the DWARF debugging sections are relative to the beginning | ||||||
|  |      of the section so we begin them at 0.  */ | ||||||
|  |   /* DWARF 1 */ | ||||||
|  |   .debug          0 : { *(.debug) } | ||||||
|  |   .line           0 : { *(.line) } | ||||||
|  |   /* GNU DWARF 1 extensions */ | ||||||
|  |   .debug_srcinfo  0 : { *(.debug_srcinfo) } | ||||||
|  |   .debug_sfnames  0 : { *(.debug_sfnames) } | ||||||
|  |   /* DWARF 1.1 and DWARF 2 */ | ||||||
|  |   .debug_aranges  0 : { *(.debug_aranges) } | ||||||
|  |   .debug_pubnames 0 : { *(.debug_pubnames) } | ||||||
|  |   /* DWARF 2 */ | ||||||
|  |   .debug_info     0 : { *(.debug_info) } | ||||||
|  |   .debug_abbrev   0 : { *(.debug_abbrev) } | ||||||
|  |   .debug_line     0 : { *(.debug_line) } | ||||||
|  |   .debug_frame    0 : { *(.debug_frame) } | ||||||
|  |   .debug_str      0 : { *(.debug_str) } | ||||||
|  |   .debug_loc      0 : { *(.debug_loc) } | ||||||
|  |   .debug_macinfo  0 : { *(.debug_macinfo) } | ||||||
|  |   /* SGI/MIPS DWARF 2 extensions */ | ||||||
|  |   .debug_weaknames 0 : { *(.debug_weaknames) } | ||||||
|  |   .debug_funcnames 0 : { *(.debug_funcnames) } | ||||||
|  |   .debug_typenames 0 : { *(.debug_typenames) } | ||||||
|  |   .debug_varnames  0 : { *(.debug_varnames) } | ||||||
|  |   /* These must appear regardless of  .  */ | ||||||
|  | } | ||||||
							
								
								
									
										1
									
								
								audio/.cvsignore
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								audio/.cvsignore
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | |||||||
|  | *.d | ||||||
| @@ -1,31 +0,0 @@ | |||||||
| common-obj-y = audio.o noaudio.o wavaudio.o mixeng.o |  | ||||||
| common-obj-$(CONFIG_SPICE) += spiceaudio.o |  | ||||||
| common-obj-$(CONFIG_AUDIO_COREAUDIO) += coreaudio.o |  | ||||||
| common-obj-$(CONFIG_AUDIO_DSOUND) += dsoundaudio.o |  | ||||||
| common-obj-$(CONFIG_AUDIO_PT_INT) += audio_pt_int.o |  | ||||||
| common-obj-$(CONFIG_AUDIO_WIN_INT) += audio_win_int.o |  | ||||||
| common-obj-y += wavcapture.o |  | ||||||
|  |  | ||||||
| coreaudio.o-libs := $(COREAUDIO_LIBS) |  | ||||||
| dsoundaudio.o-libs := $(DSOUND_LIBS) |  | ||||||
|  |  | ||||||
| # alsa module |  | ||||||
| common-obj-$(CONFIG_AUDIO_ALSA) += alsa.mo |  | ||||||
| alsa.mo-objs = alsaaudio.o |  | ||||||
| alsa.mo-libs := $(ALSA_LIBS) |  | ||||||
|  |  | ||||||
| # oss module |  | ||||||
| common-obj-$(CONFIG_AUDIO_OSS) += oss.mo |  | ||||||
| oss.mo-objs = ossaudio.o |  | ||||||
| oss.mo-libs := $(OSS_LIBS) |  | ||||||
|  |  | ||||||
| # pulseaudio module |  | ||||||
| common-obj-$(CONFIG_AUDIO_PA) += pa.mo |  | ||||||
| pa.mo-objs = paaudio.o |  | ||||||
| pa.mo-libs := $(PULSE_LIBS) |  | ||||||
|  |  | ||||||
| # sdl module |  | ||||||
| common-obj-$(CONFIG_AUDIO_SDL) += sdl.mo |  | ||||||
| sdl.mo-objs = sdlaudio.o |  | ||||||
| sdl.mo-cflags := $(SDL_CFLAGS) |  | ||||||
| sdl.mo-libs := $(SDL_LIBS) |  | ||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										847
									
								
								audio/audio.c
									
									
									
									
									
								
							
							
						
						
									
										847
									
								
								audio/audio.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -21,13 +21,13 @@ | |||||||
|  * 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. | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #ifndef QEMU_AUDIO_H | #ifndef QEMU_AUDIO_H | ||||||
| #define QEMU_AUDIO_H | #define QEMU_AUDIO_H | ||||||
|  |  | ||||||
| #include "qemu/queue.h" | #include "config-host.h" | ||||||
|  | #include "sys-queue.h" | ||||||
|  |  | ||||||
| typedef void (*audio_callback_fn) (void *opaque, int avail); | typedef void (*audio_callback_fn_t) (void *opaque, int avail); | ||||||
|  |  | ||||||
| typedef enum { | typedef enum { | ||||||
|     AUD_FMT_U8, |     AUD_FMT_U8, | ||||||
| @@ -38,18 +38,18 @@ typedef enum { | |||||||
|     AUD_FMT_S32 |     AUD_FMT_S32 | ||||||
| } audfmt_e; | } audfmt_e; | ||||||
|  |  | ||||||
| #ifdef HOST_WORDS_BIGENDIAN | #ifdef 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 | ||||||
|  |  | ||||||
| struct audsettings { | typedef struct { | ||||||
|     int freq; |     int freq; | ||||||
|     int nchannels; |     int nchannels; | ||||||
|     audfmt_e fmt; |     audfmt_e fmt; | ||||||
|     int endianness; |     int endianness; | ||||||
| }; | } audsettings_t; | ||||||
|  |  | ||||||
| typedef enum { | typedef enum { | ||||||
|     AUD_CNOTIFY_ENABLE, |     AUD_CNOTIFY_ENABLE, | ||||||
| @@ -70,7 +70,7 @@ struct capture_ops { | |||||||
| typedef struct CaptureState { | typedef struct CaptureState { | ||||||
|     void *opaque; |     void *opaque; | ||||||
|     struct capture_ops ops; |     struct capture_ops ops; | ||||||
|     QLIST_ENTRY (CaptureState) entries; |     LIST_ENTRY (CaptureState) entries; | ||||||
| } CaptureState; | } CaptureState; | ||||||
|  |  | ||||||
| typedef struct SWVoiceOut SWVoiceOut; | typedef struct SWVoiceOut SWVoiceOut; | ||||||
| @@ -78,22 +78,29 @@ typedef struct CaptureVoiceOut CaptureVoiceOut; | |||||||
| typedef struct SWVoiceIn SWVoiceIn; | typedef struct SWVoiceIn SWVoiceIn; | ||||||
|  |  | ||||||
| typedef struct QEMUSoundCard { | typedef struct QEMUSoundCard { | ||||||
|  |     AudioState *audio; | ||||||
|     char *name; |     char *name; | ||||||
|     QLIST_ENTRY (QEMUSoundCard) entries; |     LIST_ENTRY (QEMUSoundCard) entries; | ||||||
| } QEMUSoundCard; | } QEMUSoundCard; | ||||||
|  |  | ||||||
| typedef struct QEMUAudioTimeStamp { | typedef struct QEMUAudioTimeStamp { | ||||||
|     uint64_t old_ts; |     uint64_t old_ts; | ||||||
| } QEMUAudioTimeStamp; | } 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); | ||||||
| void AUD_log (const char *cap, const char *fmt, ...) GCC_FMT_ATTR(2, 3); | void AUD_log (const char *cap, const char *fmt, ...) | ||||||
|  | #ifdef __GNUC__ | ||||||
|  |     __attribute__ ((__format__ (__printf__, 2, 3))) | ||||||
|  | #endif | ||||||
|  |     ; | ||||||
|  |  | ||||||
|  | AudioState *AUD_init (void); | ||||||
| void AUD_help (void); | void AUD_help (void); | ||||||
| void AUD_register_card (const char *name, QEMUSoundCard *card); | void AUD_register_card (AudioState *s, 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 ( | ||||||
|     struct audsettings *as, |     AudioState *s, | ||||||
|  |     audsettings_t *as, | ||||||
|     struct audio_capture_ops *ops, |     struct audio_capture_ops *ops, | ||||||
|     void *opaque |     void *opaque | ||||||
|     ); |     ); | ||||||
| @@ -104,8 +111,8 @@ SWVoiceOut *AUD_open_out ( | |||||||
|     SWVoiceOut *sw, |     SWVoiceOut *sw, | ||||||
|     const char *name, |     const char *name, | ||||||
|     void *callback_opaque, |     void *callback_opaque, | ||||||
|     audio_callback_fn callback_fn, |     audio_callback_fn_t callback_fn, | ||||||
|     struct audsettings *settings |     audsettings_t *settings | ||||||
|     ); |     ); | ||||||
|  |  | ||||||
| void AUD_close_out (QEMUSoundCard *card, SWVoiceOut *sw); | void AUD_close_out (QEMUSoundCard *card, SWVoiceOut *sw); | ||||||
| @@ -117,16 +124,13 @@ int  AUD_is_active_out (SWVoiceOut *sw); | |||||||
| void     AUD_init_time_stamp_out (SWVoiceOut *sw, QEMUAudioTimeStamp *ts); | void     AUD_init_time_stamp_out (SWVoiceOut *sw, QEMUAudioTimeStamp *ts); | ||||||
| uint64_t AUD_get_elapsed_usec_out (SWVoiceOut *sw, QEMUAudioTimeStamp *ts); | uint64_t AUD_get_elapsed_usec_out (SWVoiceOut *sw, QEMUAudioTimeStamp *ts); | ||||||
|  |  | ||||||
| void AUD_set_volume_out (SWVoiceOut *sw, int mute, uint8_t lvol, uint8_t rvol); |  | ||||||
| void AUD_set_volume_in (SWVoiceIn *sw, int mute, uint8_t lvol, uint8_t rvol); |  | ||||||
|  |  | ||||||
| SWVoiceIn *AUD_open_in ( | SWVoiceIn *AUD_open_in ( | ||||||
|     QEMUSoundCard *card, |     QEMUSoundCard *card, | ||||||
|     SWVoiceIn *sw, |     SWVoiceIn *sw, | ||||||
|     const char *name, |     const char *name, | ||||||
|     void *callback_opaque, |     void *callback_opaque, | ||||||
|     audio_callback_fn callback_fn, |     audio_callback_fn_t callback_fn, | ||||||
|     struct audsettings *settings |     audsettings_t *settings | ||||||
|     ); |     ); | ||||||
|  |  | ||||||
| void AUD_close_in (QEMUSoundCard *card, SWVoiceIn *sw); | void AUD_close_in (QEMUSoundCard *card, SWVoiceIn *sw); | ||||||
| @@ -143,6 +147,9 @@ static inline void *advance (void *p, int incr) | |||||||
|     return (d + incr); |     return (d + incr); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | uint32_t popcount (uint32_t u); | ||||||
|  | uint32_t lsbindex (uint32_t u); | ||||||
|  |  | ||||||
| #ifdef __GNUC__ | #ifdef __GNUC__ | ||||||
| #define audio_MIN(a, b) ( __extension__ ({      \ | #define audio_MIN(a, b) ( __extension__ ({      \ | ||||||
|     __typeof (a) ta = a;                        \ |     __typeof (a) ta = a;                        \ | ||||||
| @@ -160,15 +167,4 @@ static inline void *advance (void *p, int incr) | |||||||
| #define audio_MAX(a, b) ((a)<(b)?(b):(a)) | #define audio_MAX(a, b) ((a)<(b)?(b):(a)) | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| int wav_start_capture (CaptureState *s, const char *path, int freq, | #endif  /* audio.h */ | ||||||
|                        int bits, int nchannels); |  | ||||||
|  |  | ||||||
| bool audio_is_cleaning_up(void); |  | ||||||
| void audio_cleanup(void); |  | ||||||
|  |  | ||||||
| void audio_sample_to_uint64(void *samples, int pos, |  | ||||||
|                             uint64_t *left, uint64_t *right); |  | ||||||
| void audio_sample_from_uint64(void *samples, int pos, |  | ||||||
|                             uint64_t left, uint64_t right); |  | ||||||
|  |  | ||||||
| #endif /* QEMU_AUDIO_H */ |  | ||||||
|   | |||||||
| @@ -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. | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #ifndef QEMU_AUDIO_INT_H | #ifndef QEMU_AUDIO_INT_H | ||||||
| #define QEMU_AUDIO_INT_H | #define QEMU_AUDIO_INT_H | ||||||
|  |  | ||||||
| #ifdef CONFIG_AUDIO_COREAUDIO | #ifdef CONFIG_COREAUDIO | ||||||
| #define FLOAT_MIXENG | #define FLOAT_MIXENG | ||||||
| /* #define RECIPROCAL */ | /* #define RECIPROCAL */ | ||||||
| #endif | #endif | ||||||
| @@ -51,7 +50,7 @@ struct audio_option { | |||||||
|  |  | ||||||
| struct audio_callback { | struct audio_callback { | ||||||
|     void *opaque; |     void *opaque; | ||||||
|     audio_callback_fn fn; |     audio_callback_fn_t fn; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| struct audio_pcm_info { | struct audio_pcm_info { | ||||||
| @@ -69,7 +68,6 @@ typedef struct SWVoiceCap SWVoiceCap; | |||||||
|  |  | ||||||
| typedef struct HWVoiceOut { | typedef struct HWVoiceOut { | ||||||
|     int enabled; |     int enabled; | ||||||
|     int poll_mode; |  | ||||||
|     int pending_disable; |     int pending_disable; | ||||||
|     struct audio_pcm_info info; |     struct audio_pcm_info info; | ||||||
|  |  | ||||||
| @@ -78,19 +76,17 @@ typedef struct HWVoiceOut { | |||||||
|     int rpos; |     int rpos; | ||||||
|     uint64_t ts_helper; |     uint64_t ts_helper; | ||||||
|  |  | ||||||
|     struct st_sample *mix_buf; |     st_sample_t *mix_buf; | ||||||
|  |  | ||||||
|     int samples; |     int samples; | ||||||
|     QLIST_HEAD (sw_out_listhead, SWVoiceOut) sw_head; |     LIST_HEAD (sw_out_listhead, SWVoiceOut) sw_head; | ||||||
|     QLIST_HEAD (sw_cap_listhead, SWVoiceCap) cap_head; |     LIST_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; |     LIST_ENTRY (HWVoiceOut) entries; | ||||||
| } HWVoiceOut; | } HWVoiceOut; | ||||||
|  |  | ||||||
| typedef struct HWVoiceIn { | typedef struct HWVoiceIn { | ||||||
|     int enabled; |     int enabled; | ||||||
|     int poll_mode; |  | ||||||
|     struct audio_pcm_info info; |     struct audio_pcm_info info; | ||||||
|  |  | ||||||
|     t_sample *conv; |     t_sample *conv; | ||||||
| @@ -99,49 +95,45 @@ typedef struct HWVoiceIn { | |||||||
|     int total_samples_captured; |     int total_samples_captured; | ||||||
|     uint64_t ts_helper; |     uint64_t ts_helper; | ||||||
|  |  | ||||||
|     struct st_sample *conv_buf; |     st_sample_t *conv_buf; | ||||||
|  |  | ||||||
|     int samples; |     int samples; | ||||||
|     QLIST_HEAD (sw_in_listhead, SWVoiceIn) sw_head; |     LIST_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; |     LIST_ENTRY (HWVoiceIn) entries; | ||||||
| } HWVoiceIn; | } HWVoiceIn; | ||||||
|  |  | ||||||
| struct SWVoiceOut { | struct SWVoiceOut { | ||||||
|     QEMUSoundCard *card; |  | ||||||
|     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; |     st_sample_t *buf; | ||||||
|     void *rate; |     void *rate; | ||||||
|     int total_hw_samples_mixed; |     int total_hw_samples_mixed; | ||||||
|     int active; |     int active; | ||||||
|     int empty; |     int empty; | ||||||
|     HWVoiceOut *hw; |     HWVoiceOut *hw; | ||||||
|     char *name; |     char *name; | ||||||
|     struct mixeng_volume vol; |     volume_t vol; | ||||||
|     struct audio_callback callback; |     struct audio_callback callback; | ||||||
|     QLIST_ENTRY (SWVoiceOut) entries; |     LIST_ENTRY (SWVoiceOut) entries; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| struct SWVoiceIn { | struct SWVoiceIn { | ||||||
|     QEMUSoundCard *card; |  | ||||||
|     int active; |     int active; | ||||||
|     struct audio_pcm_info info; |     struct audio_pcm_info info; | ||||||
|     int64_t ratio; |     int64_t ratio; | ||||||
|     void *rate; |     void *rate; | ||||||
|     int total_hw_samples_acquired; |     int total_hw_samples_acquired; | ||||||
|     struct st_sample *buf; |     st_sample_t *buf; | ||||||
|     f_sample *clip; |     f_sample *clip; | ||||||
|     HWVoiceIn *hw; |     HWVoiceIn *hw; | ||||||
|     char *name; |     char *name; | ||||||
|     struct mixeng_volume vol; |     volume_t vol; | ||||||
|     struct audio_callback callback; |     struct audio_callback callback; | ||||||
|     QLIST_ENTRY (SWVoiceIn) entries; |     LIST_ENTRY (SWVoiceIn) entries; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| typedef struct audio_driver audio_driver; |  | ||||||
| struct audio_driver { | struct audio_driver { | ||||||
|     const char *name; |     const char *name; | ||||||
|     const char *descr; |     const char *descr; | ||||||
| @@ -154,18 +146,16 @@ 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; |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| struct audio_pcm_ops { | struct audio_pcm_ops { | ||||||
|     int  (*init_out)(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque); |     int  (*init_out)(HWVoiceOut *hw, audsettings_t *as); | ||||||
|     void (*fini_out)(HWVoiceOut *hw); |     void (*fini_out)(HWVoiceOut *hw); | ||||||
|     int  (*run_out) (HWVoiceOut *hw, int live); |     int  (*run_out) (HWVoiceOut *hw); | ||||||
|     int  (*write)   (SWVoiceOut *sw, void *buf, int size); |     int  (*write)   (SWVoiceOut *sw, void *buf, int size); | ||||||
|     int  (*ctl_out) (HWVoiceOut *hw, int cmd, ...); |     int  (*ctl_out) (HWVoiceOut *hw, int cmd, ...); | ||||||
|  |  | ||||||
|     int  (*init_in) (HWVoiceIn *hw, struct audsettings *as, void *drv_opaque); |     int  (*init_in) (HWVoiceIn *hw, audsettings_t *as); | ||||||
|     void (*fini_in) (HWVoiceIn *hw); |     void (*fini_in) (HWVoiceIn *hw); | ||||||
|     int  (*run_in)  (HWVoiceIn *hw); |     int  (*run_in)  (HWVoiceIn *hw); | ||||||
|     int  (*read)    (SWVoiceIn *sw, void *buf, int size); |     int  (*read)    (SWVoiceIn *sw, void *buf, int size); | ||||||
| @@ -175,20 +165,20 @@ struct audio_pcm_ops { | |||||||
| struct capture_callback { | struct capture_callback { | ||||||
|     struct audio_capture_ops ops; |     struct audio_capture_ops ops; | ||||||
|     void *opaque; |     void *opaque; | ||||||
|     QLIST_ENTRY (capture_callback) entries; |     LIST_ENTRY (capture_callback) entries; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| struct CaptureVoiceOut { | struct CaptureVoiceOut { | ||||||
|     HWVoiceOut hw; |     HWVoiceOut hw; | ||||||
|     void *buf; |     void *buf; | ||||||
|     QLIST_HEAD (cb_listhead, capture_callback) cb_head; |     LIST_HEAD (cb_listhead, capture_callback) cb_head; | ||||||
|     QLIST_ENTRY (CaptureVoiceOut) entries; |     LIST_ENTRY (CaptureVoiceOut) entries; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| struct SWVoiceCap { | struct SWVoiceCap { | ||||||
|     SWVoiceOut sw; |     SWVoiceOut sw; | ||||||
|     CaptureVoiceOut *cap; |     CaptureVoiceOut *cap; | ||||||
|     QLIST_ENTRY (SWVoiceCap) entries; |     LIST_ENTRY (SWVoiceCap) entries; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| struct AudioState { | struct AudioState { | ||||||
| @@ -196,56 +186,95 @@ struct AudioState { | |||||||
|     void *drv_opaque; |     void *drv_opaque; | ||||||
|  |  | ||||||
|     QEMUTimer *ts; |     QEMUTimer *ts; | ||||||
|     QLIST_HEAD (card_listhead, QEMUSoundCard) card_head; |     LIST_HEAD (card_listhead, QEMUSoundCard) card_head; | ||||||
|     QLIST_HEAD (hw_in_listhead, HWVoiceIn) hw_head_in; |     LIST_HEAD (hw_in_listhead, HWVoiceIn) hw_head_in; | ||||||
|     QLIST_HEAD (hw_out_listhead, HWVoiceOut) hw_head_out; |     LIST_HEAD (hw_out_listhead, HWVoiceOut) hw_head_out; | ||||||
|     QLIST_HEAD (cap_listhead, CaptureVoiceOut) cap_head; |     LIST_HEAD (cap_listhead, CaptureVoiceOut) cap_head; | ||||||
|     int nb_hw_voices_out; |     int nb_hw_voices_out; | ||||||
|     int nb_hw_voices_in; |     int nb_hw_voices_in; | ||||||
|     int vm_running; |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| extern const struct mixeng_volume nominal_volume; | extern struct audio_driver no_audio_driver; | ||||||
|  | extern struct audio_driver oss_audio_driver; | ||||||
|  | extern struct audio_driver sdl_audio_driver; | ||||||
|  | extern struct audio_driver wav_audio_driver; | ||||||
|  | extern struct audio_driver fmod_audio_driver; | ||||||
|  | extern struct audio_driver alsa_audio_driver; | ||||||
|  | extern struct audio_driver coreaudio_audio_driver; | ||||||
|  | extern struct audio_driver dsound_audio_driver; | ||||||
|  | extern volume_t nominal_volume; | ||||||
|  |  | ||||||
| void audio_driver_register(audio_driver *drv); | void audio_pcm_init_info (struct audio_pcm_info *info, audsettings_t *as); | ||||||
| audio_driver *audio_driver_lookup(const char *name); |  | ||||||
|  |  | ||||||
| void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as); |  | ||||||
| void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len); | 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_sw_write (SWVoiceOut *sw, void *buf, int len); | ||||||
| int  audio_pcm_hw_get_live_in (HWVoiceIn *hw); | int  audio_pcm_hw_get_live_in (HWVoiceIn *hw); | ||||||
|  |  | ||||||
| int  audio_pcm_sw_read (SWVoiceIn *sw, void *buf, int len); | int  audio_pcm_sw_read (SWVoiceIn *sw, void *buf, int len); | ||||||
|  | int  audio_pcm_hw_get_live_out (HWVoiceOut *hw); | ||||||
| int audio_pcm_hw_clip_out (HWVoiceOut *hw, void *pcm_buf, | int  audio_pcm_hw_get_live_out2 (HWVoiceOut *hw, int *nb_live); | ||||||
|                            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 (const char *msg); |  | ||||||
|  |  | ||||||
| #define VOICE_ENABLE 1 | #define VOICE_ENABLE 1 | ||||||
| #define VOICE_DISABLE 2 | #define VOICE_DISABLE 2 | ||||||
| #define VOICE_VOLUME 3 |  | ||||||
|  |  | ||||||
| #define VOICE_VOLUME_CAP (1 << VOICE_VOLUME) |  | ||||||
|  |  | ||||||
| static inline int audio_ring_dist (int dst, int src, int 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); | ||||||
| } | } | ||||||
|  |  | ||||||
| #define dolog(fmt, ...) AUD_log(AUDIO_CAP, fmt, ## __VA_ARGS__) | #if defined __GNUC__ | ||||||
|  | #define GCC_ATTR __attribute__ ((__unused__, __format__ (__printf__, 1, 2))) | ||||||
|  | #define INIT_FIELD(f) . f | ||||||
|  | #define GCC_FMT_ATTR(n, m) __attribute__ ((__format__ (__printf__, n, m))) | ||||||
|  | #else | ||||||
|  | #define GCC_ATTR /**/ | ||||||
|  | #define INIT_FIELD(f) /**/ | ||||||
|  | #define GCC_FMT_ATTR(n, m) | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | static void GCC_ATTR dolog (const char *fmt, ...) | ||||||
|  | { | ||||||
|  |     va_list ap; | ||||||
|  |  | ||||||
|  |     va_start (ap, fmt); | ||||||
|  |     AUD_vlog (AUDIO_CAP, fmt, ap); | ||||||
|  |     va_end (ap); | ||||||
|  | } | ||||||
|  |  | ||||||
| #ifdef DEBUG | #ifdef DEBUG | ||||||
| #define ldebug(fmt, ...) AUD_log(AUDIO_CAP, fmt, ## __VA_ARGS__) | static void GCC_ATTR ldebug (const char *fmt, ...) | ||||||
|  | { | ||||||
|  |     va_list ap; | ||||||
|  |  | ||||||
|  |     va_start (ap, fmt); | ||||||
|  |     AUD_vlog (AUDIO_CAP, fmt, ap); | ||||||
|  |     va_end (ap); | ||||||
|  | } | ||||||
| #else | #else | ||||||
| #define ldebug(fmt, ...) (void)0 | #if defined NDEBUG && defined __GNUC__ | ||||||
|  | #define ldebug(...) | ||||||
|  | #elif defined NDEBUG && defined _MSC_VER | ||||||
|  | #define ldebug __noop | ||||||
|  | #else | ||||||
|  | static void GCC_ATTR ldebug (const char *fmt, ...) | ||||||
|  | { | ||||||
|  |     (void) fmt; | ||||||
|  | } | ||||||
| #endif | #endif | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #undef GCC_ATTR | ||||||
|  |  | ||||||
| #define AUDIO_STRINGIFY_(n) #n | #define AUDIO_STRINGIFY_(n) #n | ||||||
| #define AUDIO_STRINGIFY(n) AUDIO_STRINGIFY_(n) | #define AUDIO_STRINGIFY(n) AUDIO_STRINGIFY_(n) | ||||||
|  |  | ||||||
| #endif /* QEMU_AUDIO_INT_H */ | #if defined _MSC_VER || defined __GNUC__ | ||||||
|  | #define AUDIO_FUNC __FUNCTION__ | ||||||
|  | #else | ||||||
|  | #define AUDIO_FUNC __FILE__ ":" AUDIO_STRINGIFY (__LINE__) | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #endif /* audio_int.h */ | ||||||
|   | |||||||
| @@ -1,174 +0,0 @@ | |||||||
| #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; |  | ||||||
| } |  | ||||||
| @@ -1,22 +0,0 @@ | |||||||
| #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,11 @@ | |||||||
| #define HWBUF hw->conv_buf | #define HWBUF hw->conv_buf | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| static void glue (audio_init_nb_voices_, TYPE) (struct audio_driver *drv) | static void glue (audio_init_nb_voices_, TYPE) ( | ||||||
|  |     AudioState *s, | ||||||
|  |     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); | ||||||
|  |  | ||||||
| @@ -57,13 +59,13 @@ static void glue (audio_init_nb_voices_, TYPE) (struct audio_driver *drv) | |||||||
|         glue (s->nb_hw_voices_, TYPE) = max_voices; |         glue (s->nb_hw_voices_, TYPE) = max_voices; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (audio_bug(__func__, !voice_size && max_voices)) { |     if (audio_bug (AUDIO_FUNC, !voice_size && max_voices)) { | ||||||
|         dolog ("drv=`%s' voice_size=0 max_voices=%d\n", |         dolog ("drv=`%s' voice_size=0 max_voices=%d\n", | ||||||
|                drv->name, max_voices); |                drv->name, max_voices); | ||||||
|         glue (s->nb_hw_voices_, TYPE) = 0; |         glue (s->nb_hw_voices_, TYPE) = 0; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (audio_bug(__func__, voice_size && !max_voices)) { |     if (audio_bug (AUDIO_FUNC, voice_size && !max_voices)) { | ||||||
|         dolog ("drv=`%s' voice_size=%d max_voices=0\n", |         dolog ("drv=`%s' voice_size=%d max_voices=0\n", | ||||||
|                drv->name, voice_size); |                drv->name, voice_size); | ||||||
|     } |     } | ||||||
| @@ -71,13 +73,16 @@ static void glue (audio_init_nb_voices_, TYPE) (struct audio_driver *drv) | |||||||
|  |  | ||||||
| static void glue (audio_pcm_hw_free_resources_, TYPE) (HW *hw) | static void glue (audio_pcm_hw_free_resources_, TYPE) (HW *hw) | ||||||
| { | { | ||||||
|     g_free (HWBUF); |     if (HWBUF) { | ||||||
|  |         qemu_free (HWBUF); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     HWBUF = NULL; |     HWBUF = NULL; | ||||||
| } | } | ||||||
|  |  | ||||||
| static int glue (audio_pcm_hw_alloc_resources_, TYPE) (HW *hw) | static int glue (audio_pcm_hw_alloc_resources_, TYPE) (HW *hw) | ||||||
| { | { | ||||||
|     HWBUF = audio_calloc(__func__, hw->samples, sizeof(struct st_sample)); |     HWBUF = audio_calloc (AUDIO_FUNC, hw->samples, sizeof (st_sample_t)); | ||||||
|     if (!HWBUF) { |     if (!HWBUF) { | ||||||
|         dolog ("Could not allocate " NAME " buffer (%d samples)\n", |         dolog ("Could not allocate " NAME " buffer (%d samples)\n", | ||||||
|                hw->samples); |                hw->samples); | ||||||
| @@ -89,7 +94,9 @@ static int glue (audio_pcm_hw_alloc_resources_, TYPE) (HW *hw) | |||||||
|  |  | ||||||
| static void glue (audio_pcm_sw_free_resources_, TYPE) (SW *sw) | static void glue (audio_pcm_sw_free_resources_, TYPE) (SW *sw) | ||||||
| { | { | ||||||
|     g_free (sw->buf); |     if (sw->buf) { | ||||||
|  |         qemu_free (sw->buf); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     if (sw->rate) { |     if (sw->rate) { | ||||||
|         st_rate_stop (sw->rate); |         st_rate_stop (sw->rate); | ||||||
| @@ -103,9 +110,13 @@ static int glue (audio_pcm_sw_alloc_resources_, TYPE) (SW *sw) | |||||||
| { | { | ||||||
|     int samples; |     int samples; | ||||||
|  |  | ||||||
|  | #ifdef DAC | ||||||
|  |     samples = sw->hw->samples; | ||||||
|  | #else | ||||||
|     samples = ((int64_t) sw->hw->samples << 32) / sw->ratio; |     samples = ((int64_t) sw->hw->samples << 32) / sw->ratio; | ||||||
|  | #endif | ||||||
|  |  | ||||||
|     sw->buf = audio_calloc(__func__, samples, sizeof(struct st_sample)); |     sw->buf = audio_calloc (AUDIO_FUNC, samples, sizeof (st_sample_t)); | ||||||
|     if (!sw->buf) { |     if (!sw->buf) { | ||||||
|         dolog ("Could not allocate buffer for `%s' (%d samples)\n", |         dolog ("Could not allocate buffer for `%s' (%d samples)\n", | ||||||
|                SW_NAME (sw), samples); |                SW_NAME (sw), samples); | ||||||
| @@ -118,7 +129,7 @@ static int glue (audio_pcm_sw_alloc_resources_, TYPE) (SW *sw) | |||||||
|     sw->rate = st_rate_start (sw->hw->info.freq, sw->info.freq); |     sw->rate = st_rate_start (sw->hw->info.freq, sw->info.freq); | ||||||
| #endif | #endif | ||||||
|     if (!sw->rate) { |     if (!sw->rate) { | ||||||
|         g_free (sw->buf); |         qemu_free (sw->buf); | ||||||
|         sw->buf = NULL; |         sw->buf = NULL; | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
| @@ -129,7 +140,7 @@ static int glue (audio_pcm_sw_init_, TYPE) ( | |||||||
|     SW *sw, |     SW *sw, | ||||||
|     HW *hw, |     HW *hw, | ||||||
|     const char *name, |     const char *name, | ||||||
|     struct audsettings *as |     audsettings_t *as | ||||||
|     ) |     ) | ||||||
| { | { | ||||||
|     int err; |     int err; | ||||||
| @@ -155,10 +166,10 @@ static int glue (audio_pcm_sw_init_, TYPE) ( | |||||||
|         [sw->info.swap_endianness] |         [sw->info.swap_endianness] | ||||||
|         [audio_bits_to_index (sw->info.bits)]; |         [audio_bits_to_index (sw->info.bits)]; | ||||||
|  |  | ||||||
|     sw->name = g_strdup (name); |     sw->name = qemu_strdup (name); | ||||||
|     err = glue (audio_pcm_sw_alloc_resources_, TYPE) (sw); |     err = glue (audio_pcm_sw_alloc_resources_, TYPE) (sw); | ||||||
|     if (err) { |     if (err) { | ||||||
|         g_free (sw->name); |         qemu_free (sw->name); | ||||||
|         sw->name = NULL; |         sw->name = NULL; | ||||||
|     } |     } | ||||||
|     return err; |     return err; | ||||||
| @@ -167,47 +178,47 @@ static int glue (audio_pcm_sw_init_, TYPE) ( | |||||||
| static void glue (audio_pcm_sw_fini_, TYPE) (SW *sw) | static void glue (audio_pcm_sw_fini_, TYPE) (SW *sw) | ||||||
| { | { | ||||||
|     glue (audio_pcm_sw_free_resources_, TYPE) (sw); |     glue (audio_pcm_sw_free_resources_, TYPE) (sw); | ||||||
|     g_free (sw->name); |     if (sw->name) { | ||||||
|  |         qemu_free (sw->name); | ||||||
|         sw->name = NULL; |         sw->name = NULL; | ||||||
|     } |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| static void glue (audio_pcm_hw_add_sw_, TYPE) (HW *hw, SW *sw) | static void glue (audio_pcm_hw_add_sw_, TYPE) (HW *hw, SW *sw) | ||||||
| { | { | ||||||
|     QLIST_INSERT_HEAD (&hw->sw_head, sw, entries); |     LIST_INSERT_HEAD (&hw->sw_head, sw, entries); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void glue (audio_pcm_hw_del_sw_, TYPE) (SW *sw) | static void glue (audio_pcm_hw_del_sw_, TYPE) (SW *sw) | ||||||
| { | { | ||||||
|     QLIST_REMOVE (sw, entries); |     LIST_REMOVE (sw, entries); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void glue (audio_pcm_hw_gc_, TYPE) (HW **hwp) | static void glue (audio_pcm_hw_gc_, TYPE) (AudioState *s, HW **hwp) | ||||||
| { | { | ||||||
|     AudioState *s = &glob_audio_state; |  | ||||||
|     HW *hw = *hwp; |     HW *hw = *hwp; | ||||||
|  |  | ||||||
|     if (!hw->sw_head.lh_first) { |     if (!hw->sw_head.lh_first) { | ||||||
| #ifdef DAC | #ifdef DAC | ||||||
|         audio_detach_capture (hw); |         audio_detach_capture (hw); | ||||||
| #endif | #endif | ||||||
|         QLIST_REMOVE (hw, entries); |         LIST_REMOVE (hw, entries); | ||||||
|         glue (hw->pcm_ops->fini_, TYPE) (hw); |  | ||||||
|         glue (s->nb_hw_voices_, TYPE) += 1; |         glue (s->nb_hw_voices_, TYPE) += 1; | ||||||
|         glue (audio_pcm_hw_free_resources_ ,TYPE) (hw); |         glue (audio_pcm_hw_free_resources_ ,TYPE) (hw); | ||||||
|         g_free (hw); |         glue (hw->pcm_ops->fini_, TYPE) (hw); | ||||||
|  |         qemu_free (hw); | ||||||
|         *hwp = NULL; |         *hwp = NULL; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| static HW *glue (audio_pcm_hw_find_any_, TYPE) (HW *hw) | static HW *glue (audio_pcm_hw_find_any_, TYPE) (AudioState *s, HW *hw) | ||||||
| { | { | ||||||
|     AudioState *s = &glob_audio_state; |     return hw ? hw->entries.le_next : s->glue (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) (HW *hw) | static HW *glue (audio_pcm_hw_find_any_enabled_, TYPE) (AudioState *s, HW *hw) | ||||||
| { | { | ||||||
|     while ((hw = glue (audio_pcm_hw_find_any_, TYPE) (hw))) { |     while ((hw = glue (audio_pcm_hw_find_any_, TYPE) (s, hw))) { | ||||||
|         if (hw->enabled) { |         if (hw->enabled) { | ||||||
|             return hw; |             return hw; | ||||||
|         } |         } | ||||||
| @@ -216,11 +227,12 @@ static HW *glue (audio_pcm_hw_find_any_enabled_, TYPE) (HW *hw) | |||||||
| } | } | ||||||
|  |  | ||||||
| static HW *glue (audio_pcm_hw_find_specific_, TYPE) ( | static HW *glue (audio_pcm_hw_find_specific_, TYPE) ( | ||||||
|  |     AudioState *s, | ||||||
|     HW *hw, |     HW *hw, | ||||||
|     struct audsettings *as |     audsettings_t *as | ||||||
|     ) |     ) | ||||||
| { | { | ||||||
|     while ((hw = glue (audio_pcm_hw_find_any_, TYPE) (hw))) { |     while ((hw = glue (audio_pcm_hw_find_any_, TYPE) (s, hw))) { | ||||||
|         if (audio_pcm_info_eq (&hw->info, as)) { |         if (audio_pcm_info_eq (&hw->info, as)) { | ||||||
|             return hw; |             return hw; | ||||||
|         } |         } | ||||||
| @@ -228,27 +240,26 @@ static HW *glue (audio_pcm_hw_find_specific_, TYPE) ( | |||||||
|     return NULL; |     return NULL; | ||||||
| } | } | ||||||
|  |  | ||||||
| static HW *glue (audio_pcm_hw_add_new_, TYPE) (struct audsettings *as) | static HW *glue (audio_pcm_hw_add_new_, TYPE) (AudioState *s, audsettings_t *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)) { | ||||||
|         return NULL; |         return NULL; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (audio_bug(__func__, !drv)) { |     if (audio_bug (AUDIO_FUNC, !drv)) { | ||||||
|         dolog ("No host audio driver\n"); |         dolog ("No host audio driver\n"); | ||||||
|         return NULL; |         return NULL; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (audio_bug(__func__, !drv->pcm_ops)) { |     if (audio_bug (AUDIO_FUNC, !drv->pcm_ops)) { | ||||||
|         dolog ("Host audio driver without pcm_ops\n"); |         dolog ("Host audio driver without pcm_ops\n"); | ||||||
|         return NULL; |         return NULL; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     hw = audio_calloc(__func__, 1, glue(drv->voice_size_, TYPE)); |     hw = audio_calloc (AUDIO_FUNC, 1, glue (drv->voice_size_, TYPE)); | ||||||
|     if (!hw) { |     if (!hw) { | ||||||
|         dolog ("Can not allocate voice `%s' size %d\n", |         dolog ("Can not allocate voice `%s' size %d\n", | ||||||
|                drv->name, glue (drv->voice_size_, TYPE)); |                drv->name, glue (drv->voice_size_, TYPE)); | ||||||
| @@ -256,17 +267,15 @@ static HW *glue (audio_pcm_hw_add_new_, TYPE) (struct audsettings *as) | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     hw->pcm_ops = drv->pcm_ops; |     hw->pcm_ops = drv->pcm_ops; | ||||||
|     hw->ctl_caps = drv->ctl_caps; |     LIST_INIT (&hw->sw_head); | ||||||
|  |  | ||||||
|     QLIST_INIT (&hw->sw_head); |  | ||||||
| #ifdef DAC | #ifdef DAC | ||||||
|     QLIST_INIT (&hw->cap_head); |     LIST_INIT (&hw->cap_head); | ||||||
| #endif | #endif | ||||||
|     if (glue (hw->pcm_ops->init_, TYPE) (hw, as, s->drv_opaque)) { |     if (glue (hw->pcm_ops->init_, TYPE) (hw, as)) { | ||||||
|         goto err0; |         goto err0; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (audio_bug(__func__, hw->samples <= 0)) { |     if (audio_bug (AUDIO_FUNC, hw->samples <= 0)) { | ||||||
|         dolog ("hw->samples=%d\n", hw->samples); |         dolog ("hw->samples=%d\n", hw->samples); | ||||||
|         goto err1; |         goto err1; | ||||||
|     } |     } | ||||||
| @@ -285,52 +294,53 @@ static HW *glue (audio_pcm_hw_add_new_, TYPE) (struct audsettings *as) | |||||||
|         goto err1; |         goto err1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     QLIST_INSERT_HEAD (&s->glue (hw_head_, TYPE), hw, entries); |     LIST_INSERT_HEAD (&s->glue (hw_head_, TYPE), hw, entries); | ||||||
|     glue (s->nb_hw_voices_, TYPE) -= 1; |     glue (s->nb_hw_voices_, TYPE) -= 1; | ||||||
| #ifdef DAC | #ifdef DAC | ||||||
|     audio_attach_capture (hw); |     audio_attach_capture (s, hw); | ||||||
| #endif | #endif | ||||||
|     return hw; |     return hw; | ||||||
|  |  | ||||||
|  err1: |  err1: | ||||||
|     glue (hw->pcm_ops->fini_, TYPE) (hw); |     glue (hw->pcm_ops->fini_, TYPE) (hw); | ||||||
|  err0: |  err0: | ||||||
|     g_free (hw); |     qemu_free (hw); | ||||||
|     return NULL; |     return NULL; | ||||||
| } | } | ||||||
|  |  | ||||||
| static HW *glue (audio_pcm_hw_add_, TYPE) (struct audsettings *as) | static HW *glue (audio_pcm_hw_add_, TYPE) (AudioState *s, audsettings_t *as) | ||||||
| { | { | ||||||
|     HW *hw; |     HW *hw; | ||||||
|  |  | ||||||
|     if (glue (conf.fixed_, TYPE).enabled && glue (conf.fixed_, TYPE).greedy) { |     if (glue (conf.fixed_, TYPE).enabled && glue (conf.fixed_, TYPE).greedy) { | ||||||
|         hw = glue (audio_pcm_hw_add_new_, TYPE) (as); |         hw = glue (audio_pcm_hw_add_new_, TYPE) (s, as); | ||||||
|         if (hw) { |         if (hw) { | ||||||
|             return hw; |             return hw; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     hw = glue (audio_pcm_hw_find_specific_, TYPE) (NULL, as); |     hw = glue (audio_pcm_hw_find_specific_, TYPE) (s, NULL, as); | ||||||
|     if (hw) { |     if (hw) { | ||||||
|         return hw; |         return hw; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     hw = glue (audio_pcm_hw_add_new_, TYPE) (as); |     hw = glue (audio_pcm_hw_add_new_, TYPE) (s, as); | ||||||
|     if (hw) { |     if (hw) { | ||||||
|         return hw; |         return hw; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return glue (audio_pcm_hw_find_any_, TYPE) (NULL); |     return glue (audio_pcm_hw_find_any_, TYPE) (s, 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 |     audsettings_t *as | ||||||
|     ) |     ) | ||||||
| { | { | ||||||
|     SW *sw; |     SW *sw; | ||||||
|     HW *hw; |     HW *hw; | ||||||
|     struct audsettings hw_as; |     audsettings_t hw_as; | ||||||
|  |  | ||||||
|     if (glue (conf.fixed_, TYPE).enabled) { |     if (glue (conf.fixed_, TYPE).enabled) { | ||||||
|         hw_as = glue (conf.fixed_, TYPE).settings; |         hw_as = glue (conf.fixed_, TYPE).settings; | ||||||
| @@ -339,14 +349,14 @@ static SW *glue (audio_pcm_create_voice_pair_, TYPE) ( | |||||||
|         hw_as = *as; |         hw_as = *as; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     sw = audio_calloc(__func__, 1, sizeof(*sw)); |     sw = audio_calloc (AUDIO_FUNC, 1, sizeof (*sw)); | ||||||
|     if (!sw) { |     if (!sw) { | ||||||
|         dolog ("Could not allocate soft voice `%s' (%zu bytes)\n", |         dolog ("Could not allocate soft voice `%s' (%zu bytes)\n", | ||||||
|                sw_name ? sw_name : "unknown", sizeof (*sw)); |                sw_name ? sw_name : "unknown", sizeof (*sw)); | ||||||
|         goto err1; |         goto err1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     hw = glue (audio_pcm_hw_add_, TYPE) (&hw_as); |     hw = glue (audio_pcm_hw_add_, TYPE) (s, &hw_as); | ||||||
|     if (!hw) { |     if (!hw) { | ||||||
|         goto err2; |         goto err2; | ||||||
|     } |     } | ||||||
| @@ -361,30 +371,31 @@ static SW *glue (audio_pcm_create_voice_pair_, TYPE) ( | |||||||
|  |  | ||||||
| err3: | err3: | ||||||
|     glue (audio_pcm_hw_del_sw_, TYPE) (sw); |     glue (audio_pcm_hw_del_sw_, TYPE) (sw); | ||||||
|     glue (audio_pcm_hw_gc_, TYPE) (&hw); |     glue (audio_pcm_hw_gc_, TYPE) (s, &hw); | ||||||
| err2: | err2: | ||||||
|     g_free (sw); |     qemu_free (sw); | ||||||
| err1: | err1: | ||||||
|     return NULL; |     return NULL; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void glue (audio_close_, TYPE) (SW *sw) | static void glue (audio_close_, TYPE) (AudioState *s, SW *sw) | ||||||
| { | { | ||||||
|     glue (audio_pcm_sw_fini_, TYPE) (sw); |     glue (audio_pcm_sw_fini_, TYPE) (sw); | ||||||
|     glue (audio_pcm_hw_del_sw_, TYPE) (sw); |     glue (audio_pcm_hw_del_sw_, TYPE) (sw); | ||||||
|     glue (audio_pcm_hw_gc_, TYPE) (&sw->hw); |     glue (audio_pcm_hw_gc_, TYPE) (s, &sw->hw); | ||||||
|     g_free (sw); |     qemu_free (sw); | ||||||
| } | } | ||||||
|  |  | ||||||
| void glue (AUD_close_, TYPE) (QEMUSoundCard *card, SW *sw) | void glue (AUD_close_, TYPE) (QEMUSoundCard *card, SW *sw) | ||||||
| { | { | ||||||
|     if (sw) { |     if (sw) { | ||||||
|         if (audio_bug(__func__, !card)) { |         if (audio_bug (AUDIO_FUNC, !card || !card->audio)) { | ||||||
|             dolog ("card=%p\n", card); |             dolog ("card=%p card->audio=%p\n", | ||||||
|  |                    card, card ? card->audio : NULL); | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         glue (audio_close_, TYPE) (sw); |         glue (audio_close_, TYPE) (card->audio, sw); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -393,27 +404,34 @@ SW *glue (AUD_open_, TYPE) ( | |||||||
|     SW *sw, |     SW *sw, | ||||||
|     const char *name, |     const char *name, | ||||||
|     void *callback_opaque , |     void *callback_opaque , | ||||||
|     audio_callback_fn callback_fn, |     audio_callback_fn_t callback_fn, | ||||||
|     struct audsettings *as |     audsettings_t *as | ||||||
|     ) |     ) | ||||||
| { | { | ||||||
|     AudioState *s = &glob_audio_state; |     AudioState *s; | ||||||
|  | #ifdef DAC | ||||||
|     if (audio_bug(__func__, !card || !name || !callback_fn || !as)) { |     int live = 0; | ||||||
|         dolog ("card=%p name=%p callback_fn=%p as=%p\n", |     SW *old_sw = NULL; | ||||||
|                card, name, callback_fn, as); | #endif | ||||||
|         goto fail; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     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); | ||||||
|  |  | ||||||
|     if (audio_bug(__func__, audio_validate_settings(as))) { |     if (audio_bug (AUDIO_FUNC, | ||||||
|  |                    !card || !card->audio || !name || !callback_fn || !as)) { | ||||||
|  |         dolog ("card=%p card->audio=%p name=%p callback_fn=%p as=%p\n", | ||||||
|  |                card, card ? card->audio : NULL, name, callback_fn, as); | ||||||
|  |         goto fail; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     s = card->audio; | ||||||
|  |  | ||||||
|  |     if (audio_bug (AUDIO_FUNC, audio_validate_settings (as))) { | ||||||
|         audio_print_settings (as); |         audio_print_settings (as); | ||||||
|         goto fail; |         goto fail; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (audio_bug(__func__, !s->drv)) { |     if (audio_bug (AUDIO_FUNC, !s->drv)) { | ||||||
|         dolog ("Can not open `%s' (no host audio driver)\n", name); |         dolog ("Can not open `%s' (no host audio driver)\n", name); | ||||||
|         goto fail; |         goto fail; | ||||||
|     } |     } | ||||||
| @@ -422,6 +440,29 @@ SW *glue (AUD_open_, TYPE) ( | |||||||
|         return sw; |         return sw; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | #ifdef DAC | ||||||
|  |     if (conf.plive && sw && (!sw->active && !sw->empty)) { | ||||||
|  |         live = sw->total_hw_samples_mixed; | ||||||
|  |  | ||||||
|  | #ifdef DEBUG_PLIVE | ||||||
|  |         dolog ("Replacing voice %s with %d live samples\n", SW_NAME (sw), live); | ||||||
|  |         dolog ("Old %s freq %d, bits %d, channels %d\n", | ||||||
|  |                SW_NAME (sw), sw->info.freq, sw->info.bits, sw->info.nchannels); | ||||||
|  |         dolog ("New %s freq %d, bits %d, channels %d\n", | ||||||
|  |                name, | ||||||
|  |                freq, | ||||||
|  |                (fmt == AUD_FMT_S16 || fmt == AUD_FMT_U16) ? 16 : 8, | ||||||
|  |                nchannels); | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |         if (live) { | ||||||
|  |             old_sw = sw; | ||||||
|  |             old_sw->callback.fn = NULL; | ||||||
|  |             sw = NULL; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | #endif | ||||||
|  |  | ||||||
|     if (!glue (conf.fixed_, TYPE).enabled && sw) { |     if (!glue (conf.fixed_, TYPE).enabled && sw) { | ||||||
|         glue (AUD_close_, TYPE) (card, sw); |         glue (AUD_close_, TYPE) (card, sw); | ||||||
|         sw = NULL; |         sw = NULL; | ||||||
| @@ -442,23 +483,38 @@ SW *glue (AUD_open_, TYPE) ( | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     else { |     else { | ||||||
|         sw = glue (audio_pcm_create_voice_pair_, TYPE) (name, as); |         sw = glue (audio_pcm_create_voice_pair_, TYPE) (s, 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; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     sw->card = card; |     if (sw) { | ||||||
|         sw->vol = nominal_volume; |         sw->vol = nominal_volume; | ||||||
|         sw->callback.fn = callback_fn; |         sw->callback.fn = callback_fn; | ||||||
|         sw->callback.opaque = callback_opaque; |         sw->callback.opaque = callback_opaque; | ||||||
|  |  | ||||||
|  | #ifdef DAC | ||||||
|  |         if (live) { | ||||||
|  |             int mixed = | ||||||
|  |                 (live << old_sw->info.shift) | ||||||
|  |                 * old_sw->info.bytes_per_second | ||||||
|  |                 / sw->info.bytes_per_second; | ||||||
|  |  | ||||||
|  | #ifdef DEBUG_PLIVE | ||||||
|  |             dolog ("Silence will be mixed %d\n", mixed); | ||||||
|  | #endif | ||||||
|  |             sw->total_hw_samples_mixed += mixed; | ||||||
|  |         } | ||||||
|  | #endif | ||||||
|  |  | ||||||
| #ifdef DEBUG_AUDIO | #ifdef DEBUG_AUDIO | ||||||
|         dolog ("%s\n", name); |         dolog ("%s\n", name); | ||||||
|         audio_pcm_print_info ("hw", &sw->hw->info); |         audio_pcm_print_info ("hw", &sw->hw->info); | ||||||
|         audio_pcm_print_info ("sw", &sw->info); |         audio_pcm_print_info ("sw", &sw->info); | ||||||
| #endif | #endif | ||||||
|  |     } | ||||||
|  |  | ||||||
|     return sw; |     return sw; | ||||||
|  |  | ||||||
| @@ -491,7 +547,7 @@ uint64_t glue (AUD_get_elapsed_usec_, TYPE) (SW *sw, QEMUAudioTimeStamp *ts) | |||||||
|  |  | ||||||
|     cur_ts = sw->hw->ts_helper; |     cur_ts = sw->hw->ts_helper; | ||||||
|     old_ts = ts->old_ts; |     old_ts = ts->old_ts; | ||||||
|     /* dolog ("cur %" PRId64 " old %" PRId64 "\n", cur_ts, old_ts); */ |     /* dolog ("cur %lld old %lld\n", cur_ts, old_ts); */ | ||||||
|  |  | ||||||
|     if (cur_ts >= old_ts) { |     if (cur_ts >= old_ts) { | ||||||
|         delta = cur_ts - old_ts; |         delta = cur_ts - old_ts; | ||||||
| @@ -504,7 +560,7 @@ uint64_t glue (AUD_get_elapsed_usec_, TYPE) (SW *sw, QEMUAudioTimeStamp *ts) | |||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return muldiv64 (delta, sw->hw->info.freq, 1000000); |     return (delta * sw->hw->info.freq) / 1000000; | ||||||
| } | } | ||||||
|  |  | ||||||
| #undef TYPE | #undef TYPE | ||||||
|   | |||||||
| @@ -1,108 +0,0 @@ | |||||||
| /* public domain */ |  | ||||||
|  |  | ||||||
| #include "qemu/osdep.h" |  | ||||||
| #include "qemu-common.h" |  | ||||||
|  |  | ||||||
| #define AUDIO_CAP "win-int" |  | ||||||
| #include <windows.h> |  | ||||||
| #include <mmsystem.h> |  | ||||||
|  |  | ||||||
| #include "audio.h" |  | ||||||
| #include "audio_int.h" |  | ||||||
| #include "audio_win_int.h" |  | ||||||
|  |  | ||||||
| int waveformat_from_audio_settings (WAVEFORMATEX *wfx, |  | ||||||
|                                     struct audsettings *as) |  | ||||||
| { |  | ||||||
|     memset (wfx, 0, sizeof (*wfx)); |  | ||||||
|  |  | ||||||
|     wfx->wFormatTag = WAVE_FORMAT_PCM; |  | ||||||
|     wfx->nChannels = as->nchannels; |  | ||||||
|     wfx->nSamplesPerSec = as->freq; |  | ||||||
|     wfx->nAvgBytesPerSec = as->freq << (as->nchannels == 2); |  | ||||||
|     wfx->nBlockAlign = 1 << (as->nchannels == 2); |  | ||||||
|     wfx->cbSize = 0; |  | ||||||
|  |  | ||||||
|     switch (as->fmt) { |  | ||||||
|     case AUD_FMT_S8: |  | ||||||
|     case AUD_FMT_U8: |  | ||||||
|         wfx->wBitsPerSample = 8; |  | ||||||
|         break; |  | ||||||
|  |  | ||||||
|     case AUD_FMT_S16: |  | ||||||
|     case AUD_FMT_U16: |  | ||||||
|         wfx->wBitsPerSample = 16; |  | ||||||
|         wfx->nAvgBytesPerSec <<= 1; |  | ||||||
|         wfx->nBlockAlign <<= 1; |  | ||||||
|         break; |  | ||||||
|  |  | ||||||
|     case AUD_FMT_S32: |  | ||||||
|     case AUD_FMT_U32: |  | ||||||
|         wfx->wBitsPerSample = 32; |  | ||||||
|         wfx->nAvgBytesPerSec <<= 2; |  | ||||||
|         wfx->nBlockAlign <<= 2; |  | ||||||
|         break; |  | ||||||
|  |  | ||||||
|     default: |  | ||||||
|         dolog ("Internal logic error: Bad audio format %d\n", as->freq); |  | ||||||
|         return -1; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int waveformat_to_audio_settings (WAVEFORMATEX *wfx, |  | ||||||
|                                   struct audsettings *as) |  | ||||||
| { |  | ||||||
|     if (wfx->wFormatTag != WAVE_FORMAT_PCM) { |  | ||||||
|         dolog ("Invalid wave format, tag is not PCM, but %d\n", |  | ||||||
|                wfx->wFormatTag); |  | ||||||
|         return -1; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (!wfx->nSamplesPerSec) { |  | ||||||
|         dolog ("Invalid wave format, frequency is zero\n"); |  | ||||||
|         return -1; |  | ||||||
|     } |  | ||||||
|     as->freq = wfx->nSamplesPerSec; |  | ||||||
|  |  | ||||||
|     switch (wfx->nChannels) { |  | ||||||
|     case 1: |  | ||||||
|         as->nchannels = 1; |  | ||||||
|         break; |  | ||||||
|  |  | ||||||
|     case 2: |  | ||||||
|         as->nchannels = 2; |  | ||||||
|         break; |  | ||||||
|  |  | ||||||
|     default: |  | ||||||
|         dolog ( |  | ||||||
|             "Invalid wave format, number of channels is not 1 or 2, but %d\n", |  | ||||||
|             wfx->nChannels |  | ||||||
|             ); |  | ||||||
|         return -1; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     switch (wfx->wBitsPerSample) { |  | ||||||
|     case 8: |  | ||||||
|         as->fmt = AUD_FMT_U8; |  | ||||||
|         break; |  | ||||||
|  |  | ||||||
|     case 16: |  | ||||||
|         as->fmt = AUD_FMT_S16; |  | ||||||
|         break; |  | ||||||
|  |  | ||||||
|     case 32: |  | ||||||
|         as->fmt = AUD_FMT_S32; |  | ||||||
|         break; |  | ||||||
|  |  | ||||||
|     default: |  | ||||||
|         dolog ("Invalid wave format, bits per sample is not " |  | ||||||
|                "8, 16 or 32, but %d\n", |  | ||||||
|                wfx->wBitsPerSample); |  | ||||||
|         return -1; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| @@ -1,10 +0,0 @@ | |||||||
| #ifndef AUDIO_WIN_INT_H |  | ||||||
| #define AUDIO_WIN_INT_H |  | ||||||
|  |  | ||||||
| int waveformat_from_audio_settings (WAVEFORMATEX *wfx, |  | ||||||
|                                     struct audsettings *as); |  | ||||||
|  |  | ||||||
| int waveformat_to_audio_settings (WAVEFORMATEX *wfx, |  | ||||||
|                                   struct audsettings *as); |  | ||||||
|  |  | ||||||
| #endif /* AUDIO_WIN_INT_H */ |  | ||||||
| @@ -22,8 +22,8 @@ | |||||||
|  * THE SOFTWARE. |  * THE SOFTWARE. | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #include "qemu/osdep.h" |  | ||||||
| #include <CoreAudio/CoreAudio.h> | #include <CoreAudio/CoreAudio.h> | ||||||
|  | #include <string.h>             /* strerror */ | ||||||
| #include <pthread.h>            /* pthread_X */ | #include <pthread.h>            /* pthread_X */ | ||||||
|  |  | ||||||
| #include "qemu-common.h" | #include "qemu-common.h" | ||||||
| @@ -32,251 +32,31 @@ | |||||||
| #define AUDIO_CAP "coreaudio" | #define AUDIO_CAP "coreaudio" | ||||||
| #include "audio_int.h" | #include "audio_int.h" | ||||||
|  |  | ||||||
| #ifndef MAC_OS_X_VERSION_10_6 | struct { | ||||||
| #define MAC_OS_X_VERSION_10_6 1060 |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| typedef struct { |  | ||||||
|     int buffer_frames; |     int buffer_frames; | ||||||
|     int nbuffers; |     int nbuffers; | ||||||
| } CoreaudioConf; |     int isAtexit; | ||||||
|  | } conf = { | ||||||
|  |     .buffer_frames = 512, | ||||||
|  |     .nbuffers = 4, | ||||||
|  |     .isAtexit = 0 | ||||||
|  | }; | ||||||
|  |  | ||||||
| typedef struct coreaudioVoiceOut { | typedef struct coreaudioVoiceOut { | ||||||
|     HWVoiceOut hw; |     HWVoiceOut hw; | ||||||
|     pthread_mutex_t mutex; |     pthread_mutex_t mutex; | ||||||
|  |     int isAtexit; | ||||||
|     AudioDeviceID outputDeviceID; |     AudioDeviceID outputDeviceID; | ||||||
|     UInt32 audioDevicePropertyBufferFrameSize; |     UInt32 audioDevicePropertyBufferFrameSize; | ||||||
|     AudioStreamBasicDescription outputStreamBasicDescription; |     AudioStreamBasicDescription outputStreamBasicDescription; | ||||||
|     AudioDeviceIOProcID ioprocid; |  | ||||||
|     int live; |     int live; | ||||||
|     int decr; |     int decr; | ||||||
|     int rpos; |     int rpos; | ||||||
| } coreaudioVoiceOut; | } coreaudioVoiceOut; | ||||||
|  |  | ||||||
| #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 |  | ||||||
| /* The APIs used here only become available from 10.6 */ |  | ||||||
|  |  | ||||||
| static OSStatus coreaudio_get_voice(AudioDeviceID *id) |  | ||||||
| { |  | ||||||
|     UInt32 size = sizeof(*id); |  | ||||||
|     AudioObjectPropertyAddress addr = { |  | ||||||
|         kAudioHardwarePropertyDefaultOutputDevice, |  | ||||||
|         kAudioObjectPropertyScopeGlobal, |  | ||||||
|         kAudioObjectPropertyElementMaster |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     return AudioObjectGetPropertyData(kAudioObjectSystemObject, |  | ||||||
|                                       &addr, |  | ||||||
|                                       0, |  | ||||||
|                                       NULL, |  | ||||||
|                                       &size, |  | ||||||
|                                       id); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static OSStatus coreaudio_get_framesizerange(AudioDeviceID id, |  | ||||||
|                                              AudioValueRange *framerange) |  | ||||||
| { |  | ||||||
|     UInt32 size = sizeof(*framerange); |  | ||||||
|     AudioObjectPropertyAddress addr = { |  | ||||||
|         kAudioDevicePropertyBufferFrameSizeRange, |  | ||||||
|         kAudioDevicePropertyScopeOutput, |  | ||||||
|         kAudioObjectPropertyElementMaster |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     return AudioObjectGetPropertyData(id, |  | ||||||
|                                       &addr, |  | ||||||
|                                       0, |  | ||||||
|                                       NULL, |  | ||||||
|                                       &size, |  | ||||||
|                                       framerange); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static OSStatus coreaudio_get_framesize(AudioDeviceID id, UInt32 *framesize) |  | ||||||
| { |  | ||||||
|     UInt32 size = sizeof(*framesize); |  | ||||||
|     AudioObjectPropertyAddress addr = { |  | ||||||
|         kAudioDevicePropertyBufferFrameSize, |  | ||||||
|         kAudioDevicePropertyScopeOutput, |  | ||||||
|         kAudioObjectPropertyElementMaster |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     return AudioObjectGetPropertyData(id, |  | ||||||
|                                       &addr, |  | ||||||
|                                       0, |  | ||||||
|                                       NULL, |  | ||||||
|                                       &size, |  | ||||||
|                                       framesize); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static OSStatus coreaudio_set_framesize(AudioDeviceID id, UInt32 *framesize) |  | ||||||
| { |  | ||||||
|     UInt32 size = sizeof(*framesize); |  | ||||||
|     AudioObjectPropertyAddress addr = { |  | ||||||
|         kAudioDevicePropertyBufferFrameSize, |  | ||||||
|         kAudioDevicePropertyScopeOutput, |  | ||||||
|         kAudioObjectPropertyElementMaster |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     return AudioObjectSetPropertyData(id, |  | ||||||
|                                       &addr, |  | ||||||
|                                       0, |  | ||||||
|                                       NULL, |  | ||||||
|                                       size, |  | ||||||
|                                       framesize); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static OSStatus coreaudio_get_streamformat(AudioDeviceID id, |  | ||||||
|                                            AudioStreamBasicDescription *d) |  | ||||||
| { |  | ||||||
|     UInt32 size = sizeof(*d); |  | ||||||
|     AudioObjectPropertyAddress addr = { |  | ||||||
|         kAudioDevicePropertyStreamFormat, |  | ||||||
|         kAudioDevicePropertyScopeOutput, |  | ||||||
|         kAudioObjectPropertyElementMaster |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     return AudioObjectGetPropertyData(id, |  | ||||||
|                                       &addr, |  | ||||||
|                                       0, |  | ||||||
|                                       NULL, |  | ||||||
|                                       &size, |  | ||||||
|                                       d); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static OSStatus coreaudio_set_streamformat(AudioDeviceID id, |  | ||||||
|                                            AudioStreamBasicDescription *d) |  | ||||||
| { |  | ||||||
|     UInt32 size = sizeof(*d); |  | ||||||
|     AudioObjectPropertyAddress addr = { |  | ||||||
|         kAudioDevicePropertyStreamFormat, |  | ||||||
|         kAudioDevicePropertyScopeOutput, |  | ||||||
|         kAudioObjectPropertyElementMaster |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     return AudioObjectSetPropertyData(id, |  | ||||||
|                                       &addr, |  | ||||||
|                                       0, |  | ||||||
|                                       NULL, |  | ||||||
|                                       size, |  | ||||||
|                                       d); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static OSStatus coreaudio_get_isrunning(AudioDeviceID id, UInt32 *result) |  | ||||||
| { |  | ||||||
|     UInt32 size = sizeof(*result); |  | ||||||
|     AudioObjectPropertyAddress addr = { |  | ||||||
|         kAudioDevicePropertyDeviceIsRunning, |  | ||||||
|         kAudioDevicePropertyScopeOutput, |  | ||||||
|         kAudioObjectPropertyElementMaster |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     return AudioObjectGetPropertyData(id, |  | ||||||
|                                       &addr, |  | ||||||
|                                       0, |  | ||||||
|                                       NULL, |  | ||||||
|                                       &size, |  | ||||||
|                                       result); |  | ||||||
| } |  | ||||||
| #else |  | ||||||
| /* Legacy versions of functions using deprecated APIs */ |  | ||||||
|  |  | ||||||
| static OSStatus coreaudio_get_voice(AudioDeviceID *id) |  | ||||||
| { |  | ||||||
|     UInt32 size = sizeof(*id); |  | ||||||
|  |  | ||||||
|     return AudioHardwareGetProperty( |  | ||||||
|         kAudioHardwarePropertyDefaultOutputDevice, |  | ||||||
|         &size, |  | ||||||
|         id); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static OSStatus coreaudio_get_framesizerange(AudioDeviceID id, |  | ||||||
|                                              AudioValueRange *framerange) |  | ||||||
| { |  | ||||||
|     UInt32 size = sizeof(*framerange); |  | ||||||
|  |  | ||||||
|     return AudioDeviceGetProperty( |  | ||||||
|         id, |  | ||||||
|         0, |  | ||||||
|         0, |  | ||||||
|         kAudioDevicePropertyBufferFrameSizeRange, |  | ||||||
|         &size, |  | ||||||
|         framerange); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static OSStatus coreaudio_get_framesize(AudioDeviceID id, UInt32 *framesize) |  | ||||||
| { |  | ||||||
|     UInt32 size = sizeof(*framesize); |  | ||||||
|  |  | ||||||
|     return AudioDeviceGetProperty( |  | ||||||
|         id, |  | ||||||
|         0, |  | ||||||
|         false, |  | ||||||
|         kAudioDevicePropertyBufferFrameSize, |  | ||||||
|         &size, |  | ||||||
|         framesize); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static OSStatus coreaudio_set_framesize(AudioDeviceID id, UInt32 *framesize) |  | ||||||
| { |  | ||||||
|     UInt32 size = sizeof(*framesize); |  | ||||||
|  |  | ||||||
|     return AudioDeviceSetProperty( |  | ||||||
|         id, |  | ||||||
|         NULL, |  | ||||||
|         0, |  | ||||||
|         false, |  | ||||||
|         kAudioDevicePropertyBufferFrameSize, |  | ||||||
|         size, |  | ||||||
|         framesize); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static OSStatus coreaudio_get_streamformat(AudioDeviceID id, |  | ||||||
|                                            AudioStreamBasicDescription *d) |  | ||||||
| { |  | ||||||
|     UInt32 size = sizeof(*d); |  | ||||||
|  |  | ||||||
|     return AudioDeviceGetProperty( |  | ||||||
|         id, |  | ||||||
|         0, |  | ||||||
|         false, |  | ||||||
|         kAudioDevicePropertyStreamFormat, |  | ||||||
|         &size, |  | ||||||
|         d); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static OSStatus coreaudio_set_streamformat(AudioDeviceID id, |  | ||||||
|                                            AudioStreamBasicDescription *d) |  | ||||||
| { |  | ||||||
|     UInt32 size = sizeof(*d); |  | ||||||
|  |  | ||||||
|     return AudioDeviceSetProperty( |  | ||||||
|         id, |  | ||||||
|         0, |  | ||||||
|         0, |  | ||||||
|         0, |  | ||||||
|         kAudioDevicePropertyStreamFormat, |  | ||||||
|         size, |  | ||||||
|         d); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static OSStatus coreaudio_get_isrunning(AudioDeviceID id, UInt32 *result) |  | ||||||
| { |  | ||||||
|     UInt32 size = sizeof(*result); |  | ||||||
|  |  | ||||||
|     return AudioDeviceGetProperty( |  | ||||||
|         id, |  | ||||||
|         0, |  | ||||||
|         0, |  | ||||||
|         kAudioDevicePropertyDeviceIsRunning, |  | ||||||
|         &size, |  | ||||||
|         result); |  | ||||||
| } |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| static void coreaudio_logstatus (OSStatus status) | static void coreaudio_logstatus (OSStatus status) | ||||||
| { | { | ||||||
|     const char *str = "BUG"; |     char *str = "BUG"; | ||||||
|  |  | ||||||
|     switch(status) { |     switch(status) { | ||||||
|     case kAudioHardwareNoError: |     case kAudioHardwareNoError: | ||||||
| @@ -324,7 +104,7 @@ static void coreaudio_logstatus (OSStatus status) | |||||||
|         break; |         break; | ||||||
|  |  | ||||||
|     default: |     default: | ||||||
|         AUD_log (AUDIO_CAP, "Reason: status code %" PRId32 "\n", (int32_t)status); |         AUD_log (AUDIO_CAP, "Reason: status code %ld\n", status); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -368,7 +148,10 @@ static inline UInt32 isPlaying (AudioDeviceID outputDeviceID) | |||||||
| { | { | ||||||
|     OSStatus status; |     OSStatus status; | ||||||
|     UInt32 result = 0; |     UInt32 result = 0; | ||||||
|     status = coreaudio_get_isrunning(outputDeviceID, &result); |     UInt32 propertySize = sizeof(outputDeviceID); | ||||||
|  |     status = AudioDeviceGetProperty( | ||||||
|  |         outputDeviceID, 0, 0, | ||||||
|  |         kAudioDevicePropertyDeviceIsRunning, &propertySize, &result); | ||||||
|     if (status != kAudioHardwareNoError) { |     if (status != kAudioHardwareNoError) { | ||||||
|         coreaudio_logerr(status, |         coreaudio_logerr(status, | ||||||
|                          "Could not determine whether Device is playing\n"); |                          "Could not determine whether Device is playing\n"); | ||||||
| @@ -376,6 +159,11 @@ static inline UInt32 isPlaying (AudioDeviceID outputDeviceID) | |||||||
|     return result; |     return result; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static void coreaudio_atexit (void) | ||||||
|  | { | ||||||
|  |     conf.isAtexit = 1; | ||||||
|  | } | ||||||
|  |  | ||||||
| static int coreaudio_lock (coreaudioVoiceOut *core, const char *fn_name) | static int coreaudio_lock (coreaudioVoiceOut *core, const char *fn_name) | ||||||
| { | { | ||||||
|     int err; |     int err; | ||||||
| @@ -402,15 +190,17 @@ static int coreaudio_unlock (coreaudioVoiceOut *core, const char *fn_name) | |||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| static int coreaudio_run_out (HWVoiceOut *hw, int live) | static int coreaudio_run_out (HWVoiceOut *hw) | ||||||
| { | { | ||||||
|     int decr; |     int live, decr; | ||||||
|     coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; |     coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; | ||||||
|  |  | ||||||
|     if (coreaudio_lock (core, "coreaudio_run_out")) { |     if (coreaudio_lock (core, "coreaudio_run_out")) { | ||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     live = audio_pcm_hw_get_live_out (hw); | ||||||
|  |  | ||||||
|     if (core->decr > live) { |     if (core->decr > live) { | ||||||
|         ldebug ("core->decr %d live %d core->live %d\n", |         ldebug ("core->decr %d live %d core->live %d\n", | ||||||
|                 core->decr, |                 core->decr, | ||||||
| @@ -443,7 +233,7 @@ static OSStatus audioDeviceIOProc( | |||||||
|     HWVoiceOut *hw = hwptr; |     HWVoiceOut *hw = hwptr; | ||||||
|     coreaudioVoiceOut *core = (coreaudioVoiceOut *) hwptr; |     coreaudioVoiceOut *core = (coreaudioVoiceOut *) hwptr; | ||||||
|     int rpos, live; |     int rpos, live; | ||||||
|     struct st_sample *src; |     st_sample_t *src; | ||||||
| #ifndef FLOAT_MIXENG | #ifndef FLOAT_MIXENG | ||||||
| #ifdef RECIPROCAL | #ifdef RECIPROCAL | ||||||
|     const float scale = 1.f / UINT_MAX; |     const float scale = 1.f / UINT_MAX; | ||||||
| @@ -499,15 +289,14 @@ static int coreaudio_write (SWVoiceOut *sw, void *buf, int len) | |||||||
|     return audio_pcm_sw_write (sw, buf, len); |     return audio_pcm_sw_write (sw, buf, len); | ||||||
| } | } | ||||||
|  |  | ||||||
| static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as, | static int coreaudio_init_out (HWVoiceOut *hw, audsettings_t *as) | ||||||
|                               void *drv_opaque) |  | ||||||
| { | { | ||||||
|     OSStatus status; |     OSStatus status; | ||||||
|     coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; |     coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; | ||||||
|  |     UInt32 propertySize; | ||||||
|     int err; |     int err; | ||||||
|     const char *typ = "playback"; |     const char *typ = "playback"; | ||||||
|     AudioValueRange frameRange; |     AudioValueRange frameRange; | ||||||
|     CoreaudioConf *conf = drv_opaque; |  | ||||||
|  |  | ||||||
|     /* create mutex */ |     /* create mutex */ | ||||||
|     err = pthread_mutex_init(&core->mutex, NULL); |     err = pthread_mutex_init(&core->mutex, NULL); | ||||||
| @@ -518,7 +307,12 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as, | |||||||
|  |  | ||||||
|     audio_pcm_init_info (&hw->info, as); |     audio_pcm_init_info (&hw->info, as); | ||||||
|  |  | ||||||
|     status = coreaudio_get_voice(&core->outputDeviceID); |     /* open default output device */ | ||||||
|  |     propertySize = sizeof(core->outputDeviceID); | ||||||
|  |     status = AudioHardwareGetProperty( | ||||||
|  |         kAudioHardwarePropertyDefaultOutputDevice, | ||||||
|  |         &propertySize, | ||||||
|  |         &core->outputDeviceID); | ||||||
|     if (status != kAudioHardwareNoError) { |     if (status != kAudioHardwareNoError) { | ||||||
|         coreaudio_logerr2 (status, typ, |         coreaudio_logerr2 (status, typ, | ||||||
|                            "Could not get default output Device\n"); |                            "Could not get default output Device\n"); | ||||||
| @@ -530,7 +324,13 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as, | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* get minimum and maximum buffer frame sizes */ |     /* get minimum and maximum buffer frame sizes */ | ||||||
|     status = coreaudio_get_framesizerange(core->outputDeviceID, |     propertySize = sizeof(frameRange); | ||||||
|  |     status = AudioDeviceGetProperty( | ||||||
|  |         core->outputDeviceID, | ||||||
|  |         0, | ||||||
|  |         0, | ||||||
|  |         kAudioDevicePropertyBufferFrameSizeRange, | ||||||
|  |         &propertySize, | ||||||
|         &frameRange); |         &frameRange); | ||||||
|     if (status != kAudioHardwareNoError) { |     if (status != kAudioHardwareNoError) { | ||||||
|         coreaudio_logerr2 (status, typ, |         coreaudio_logerr2 (status, typ, | ||||||
| @@ -538,40 +338,59 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as, | |||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (frameRange.mMinimum > conf->buffer_frames) { |     if (frameRange.mMinimum > conf.buffer_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 < conf->buffer_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 = conf->buffer_frames; |         core->audioDevicePropertyBufferFrameSize = conf.buffer_frames; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* set Buffer Frame Size */ |     /* set Buffer Frame Size */ | ||||||
|     status = coreaudio_set_framesize(core->outputDeviceID, |     propertySize = sizeof(core->audioDevicePropertyBufferFrameSize); | ||||||
|  |     status = AudioDeviceSetProperty( | ||||||
|  |         core->outputDeviceID, | ||||||
|  |         NULL, | ||||||
|  |         0, | ||||||
|  |         false, | ||||||
|  |         kAudioDevicePropertyBufferFrameSize, | ||||||
|  |         propertySize, | ||||||
|         &core->audioDevicePropertyBufferFrameSize); |         &core->audioDevicePropertyBufferFrameSize); | ||||||
|     if (status != kAudioHardwareNoError) { |     if (status != kAudioHardwareNoError) { | ||||||
|         coreaudio_logerr2 (status, typ, |         coreaudio_logerr2 (status, typ, | ||||||
|                            "Could not set device buffer frame size %" PRIu32 "\n", |                            "Could not set device buffer frame size %ld\n", | ||||||
|                            (uint32_t)core->audioDevicePropertyBufferFrameSize); |                            core->audioDevicePropertyBufferFrameSize); | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* get Buffer Frame Size */ |     /* get Buffer Frame Size */ | ||||||
|     status = coreaudio_get_framesize(core->outputDeviceID, |     propertySize = sizeof(core->audioDevicePropertyBufferFrameSize); | ||||||
|  |     status = AudioDeviceGetProperty( | ||||||
|  |         core->outputDeviceID, | ||||||
|  |         0, | ||||||
|  |         false, | ||||||
|  |         kAudioDevicePropertyBufferFrameSize, | ||||||
|  |         &propertySize, | ||||||
|         &core->audioDevicePropertyBufferFrameSize); |         &core->audioDevicePropertyBufferFrameSize); | ||||||
|     if (status != kAudioHardwareNoError) { |     if (status != kAudioHardwareNoError) { | ||||||
|         coreaudio_logerr2 (status, typ, |         coreaudio_logerr2 (status, typ, | ||||||
|                            "Could not get device buffer frame size\n"); |                            "Could not get device buffer frame size\n"); | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|     hw->samples = conf->nbuffers * core->audioDevicePropertyBufferFrameSize; |     hw->samples = conf.nbuffers * core->audioDevicePropertyBufferFrameSize; | ||||||
|  |  | ||||||
|     /* get StreamFormat */ |     /* get StreamFormat */ | ||||||
|     status = coreaudio_get_streamformat(core->outputDeviceID, |     propertySize = sizeof(core->outputStreamBasicDescription); | ||||||
|  |     status = AudioDeviceGetProperty( | ||||||
|  |         core->outputDeviceID, | ||||||
|  |         0, | ||||||
|  |         false, | ||||||
|  |         kAudioDevicePropertyStreamFormat, | ||||||
|  |         &propertySize, | ||||||
|         &core->outputStreamBasicDescription); |         &core->outputStreamBasicDescription); | ||||||
|     if (status != kAudioHardwareNoError) { |     if (status != kAudioHardwareNoError) { | ||||||
|         coreaudio_logerr2 (status, typ, |         coreaudio_logerr2 (status, typ, | ||||||
| @@ -582,7 +401,14 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as, | |||||||
|  |  | ||||||
|     /* set Samplerate */ |     /* set Samplerate */ | ||||||
|     core->outputStreamBasicDescription.mSampleRate = (Float64) as->freq; |     core->outputStreamBasicDescription.mSampleRate = (Float64) as->freq; | ||||||
|     status = coreaudio_set_streamformat(core->outputDeviceID, |     propertySize = sizeof(core->outputStreamBasicDescription); | ||||||
|  |     status = AudioDeviceSetProperty( | ||||||
|  |         core->outputDeviceID, | ||||||
|  |         0, | ||||||
|  |         0, | ||||||
|  |         0, | ||||||
|  |         kAudioDevicePropertyStreamFormat, | ||||||
|  |         propertySize, | ||||||
|         &core->outputStreamBasicDescription); |         &core->outputStreamBasicDescription); | ||||||
|     if (status != kAudioHardwareNoError) { |     if (status != kAudioHardwareNoError) { | ||||||
|         coreaudio_logerr2 (status, typ, "Could not set samplerate %d\n", |         coreaudio_logerr2 (status, typ, "Could not set samplerate %d\n", | ||||||
| @@ -592,12 +418,8 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as, | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* set Callback */ |     /* set Callback */ | ||||||
|     core->ioprocid = NULL; |     status = AudioDeviceAddIOProc(core->outputDeviceID, audioDeviceIOProc, hw); | ||||||
|     status = AudioDeviceCreateIOProcID(core->outputDeviceID, |     if (status != kAudioHardwareNoError) { | ||||||
|                                        audioDeviceIOProc, |  | ||||||
|                                        hw, |  | ||||||
|                                        &core->ioprocid); |  | ||||||
|     if (status != kAudioHardwareNoError || core->ioprocid == NULL) { |  | ||||||
|         coreaudio_logerr2 (status, typ, "Could not set IOProc\n"); |         coreaudio_logerr2 (status, typ, "Could not set IOProc\n"); | ||||||
|         core->outputDeviceID = kAudioDeviceUnknown; |         core->outputDeviceID = kAudioDeviceUnknown; | ||||||
|         return -1; |         return -1; | ||||||
| @@ -605,10 +427,10 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as, | |||||||
|  |  | ||||||
|     /* start Playback */ |     /* start Playback */ | ||||||
|     if (!isPlaying(core->outputDeviceID)) { |     if (!isPlaying(core->outputDeviceID)) { | ||||||
|         status = AudioDeviceStart(core->outputDeviceID, core->ioprocid); |         status = AudioDeviceStart(core->outputDeviceID, audioDeviceIOProc); | ||||||
|         if (status != kAudioHardwareNoError) { |         if (status != kAudioHardwareNoError) { | ||||||
|             coreaudio_logerr2 (status, typ, "Could not start playback\n"); |             coreaudio_logerr2 (status, typ, "Could not start playback\n"); | ||||||
|             AudioDeviceDestroyIOProcID(core->outputDeviceID, core->ioprocid); |             AudioDeviceRemoveIOProc(core->outputDeviceID, audioDeviceIOProc); | ||||||
|             core->outputDeviceID = kAudioDeviceUnknown; |             core->outputDeviceID = kAudioDeviceUnknown; | ||||||
|             return -1; |             return -1; | ||||||
|         } |         } | ||||||
| @@ -623,18 +445,18 @@ static void coreaudio_fini_out (HWVoiceOut *hw) | |||||||
|     int err; |     int err; | ||||||
|     coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; |     coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; | ||||||
|  |  | ||||||
|     if (!audio_is_cleaning_up()) { |     if (!conf.isAtexit) { | ||||||
|         /* stop playback */ |         /* stop playback */ | ||||||
|         if (isPlaying(core->outputDeviceID)) { |         if (isPlaying(core->outputDeviceID)) { | ||||||
|             status = AudioDeviceStop(core->outputDeviceID, core->ioprocid); |             status = AudioDeviceStop(core->outputDeviceID, audioDeviceIOProc); | ||||||
|             if (status != kAudioHardwareNoError) { |             if (status != kAudioHardwareNoError) { | ||||||
|                 coreaudio_logerr (status, "Could not stop playback\n"); |                 coreaudio_logerr (status, "Could not stop playback\n"); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         /* remove callback */ |         /* remove callback */ | ||||||
|         status = AudioDeviceDestroyIOProcID(core->outputDeviceID, |         status = AudioDeviceRemoveIOProc(core->outputDeviceID, | ||||||
|                                             core->ioprocid); |                                          audioDeviceIOProc); | ||||||
|         if (status != kAudioHardwareNoError) { |         if (status != kAudioHardwareNoError) { | ||||||
|             coreaudio_logerr (status, "Could not remove IOProc\n"); |             coreaudio_logerr (status, "Could not remove IOProc\n"); | ||||||
|         } |         } | ||||||
| @@ -657,7 +479,7 @@ static int coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...) | |||||||
|     case VOICE_ENABLE: |     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, audioDeviceIOProc); | ||||||
|             if (status != kAudioHardwareNoError) { |             if (status != kAudioHardwareNoError) { | ||||||
|                 coreaudio_logerr (status, "Could not resume playback\n"); |                 coreaudio_logerr (status, "Could not resume playback\n"); | ||||||
|             } |             } | ||||||
| @@ -666,10 +488,9 @@ static int coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...) | |||||||
|  |  | ||||||
|     case VOICE_DISABLE: |     case VOICE_DISABLE: | ||||||
|         /* stop playback */ |         /* stop playback */ | ||||||
|         if (!audio_is_cleaning_up()) { |         if (!conf.isAtexit) { | ||||||
|             if (isPlaying(core->outputDeviceID)) { |             if (isPlaying(core->outputDeviceID)) { | ||||||
|                 status = AudioDeviceStop(core->outputDeviceID, |                 status = AudioDeviceStop(core->outputDeviceID, audioDeviceIOProc); | ||||||
|                                          core->ioprocid); |  | ||||||
|                 if (status != kAudioHardwareNoError) { |                 if (status != kAudioHardwareNoError) { | ||||||
|                     coreaudio_logerr (status, "Could not pause playback\n"); |                     coreaudio_logerr (status, "Could not pause playback\n"); | ||||||
|                 } |                 } | ||||||
| @@ -680,64 +501,50 @@ static int coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...) | |||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| static CoreaudioConf glob_conf = { |  | ||||||
|     .buffer_frames = 512, |  | ||||||
|     .nbuffers = 4, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static void *coreaudio_audio_init (void) | static void *coreaudio_audio_init (void) | ||||||
| { | { | ||||||
|     CoreaudioConf *conf = g_malloc(sizeof(CoreaudioConf)); |     atexit(coreaudio_atexit); | ||||||
|     *conf = glob_conf; |     return &coreaudio_audio_init; | ||||||
|  |  | ||||||
|     return conf; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static void coreaudio_audio_fini (void *opaque) | static void coreaudio_audio_fini (void *opaque) | ||||||
| { | { | ||||||
|     g_free(opaque); |     (void) opaque; | ||||||
| } | } | ||||||
|  |  | ||||||
| static struct audio_option coreaudio_options[] = { | static struct audio_option coreaudio_options[] = { | ||||||
|     { |     {"BUFFER_SIZE", AUD_OPT_INT, &conf.buffer_frames, | ||||||
|         .name  = "BUFFER_SIZE", |      "Size of the buffer in frames", NULL, 0}, | ||||||
|         .tag   = AUD_OPT_INT, |     {"BUFFER_COUNT", AUD_OPT_INT, &conf.nbuffers, | ||||||
|         .valp  = &glob_conf.buffer_frames, |      "Number of buffers", NULL, 0}, | ||||||
|         .descr = "Size of the buffer in frames" |     {NULL, 0, NULL, NULL, NULL, 0} | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         .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, |     coreaudio_init_out, | ||||||
|     .fini_out = coreaudio_fini_out, |     coreaudio_fini_out, | ||||||
|     .run_out  = coreaudio_run_out, |     coreaudio_run_out, | ||||||
|     .write    = coreaudio_write, |     coreaudio_write, | ||||||
|     .ctl_out  = coreaudio_ctl_out |     coreaudio_ctl_out, | ||||||
|  |  | ||||||
|  |     NULL, | ||||||
|  |     NULL, | ||||||
|  |     NULL, | ||||||
|  |     NULL, | ||||||
|  |     NULL | ||||||
| }; | }; | ||||||
|  |  | ||||||
| static struct audio_driver coreaudio_audio_driver = { | struct audio_driver coreaudio_audio_driver = { | ||||||
|     .name           = "coreaudio", |     INIT_FIELD (name           = ) "coreaudio", | ||||||
|     .descr          = "CoreAudio http://developer.apple.com/audio/coreaudio.html", |     INIT_FIELD (descr          = ) | ||||||
|     .options        = coreaudio_options, |     "CoreAudio http://developer.apple.com/audio/coreaudio.html", | ||||||
|     .init           = coreaudio_audio_init, |     INIT_FIELD (options        = ) coreaudio_options, | ||||||
|     .fini           = coreaudio_audio_fini, |     INIT_FIELD (init           = ) coreaudio_audio_init, | ||||||
|     .pcm_ops        = &coreaudio_pcm_ops, |     INIT_FIELD (fini           = ) coreaudio_audio_fini, | ||||||
|     .can_be_default = 1, |     INIT_FIELD (pcm_ops        = ) &coreaudio_pcm_ops, | ||||||
|     .max_voices_out = 1, |     INIT_FIELD (can_be_default = ) 1, | ||||||
|     .max_voices_in  = 0, |     INIT_FIELD (max_voices_out = ) 1, | ||||||
|     .voice_size_out = sizeof (coreaudioVoiceOut), |     INIT_FIELD (max_voices_in  = ) 0, | ||||||
|     .voice_size_in  = 0 |     INIT_FIELD (voice_size_out = ) sizeof (coreaudioVoiceOut), | ||||||
|  |     INIT_FIELD (voice_size_in  = ) 0 | ||||||
| }; | }; | ||||||
|  |  | ||||||
| static void register_audio_coreaudio(void) |  | ||||||
| { |  | ||||||
|     audio_driver_register(&coreaudio_audio_driver); |  | ||||||
| } |  | ||||||
| type_init(register_audio_coreaudio); |  | ||||||
|   | |||||||
| @@ -23,20 +23,16 @@ | |||||||
|  */ |  */ | ||||||
| #ifdef DSBTYPE_IN | #ifdef DSBTYPE_IN | ||||||
| #define NAME "capture buffer" | #define NAME "capture buffer" | ||||||
| #define NAME2 "DirectSoundCapture" |  | ||||||
| #define TYPE in | #define TYPE in | ||||||
| #define IFACE IDirectSoundCaptureBuffer | #define IFACE IDirectSoundCaptureBuffer | ||||||
| #define BUFPTR LPDIRECTSOUNDCAPTUREBUFFER | #define BUFPTR LPDIRECTSOUNDCAPTUREBUFFER | ||||||
| #define FIELD dsound_capture_buffer | #define FIELD dsound_capture_buffer | ||||||
| #define FIELD2 dsound_capture |  | ||||||
| #else | #else | ||||||
| #define NAME "playback buffer" | #define NAME "playback buffer" | ||||||
| #define NAME2 "DirectSound" |  | ||||||
| #define TYPE out | #define TYPE out | ||||||
| #define IFACE IDirectSoundBuffer | #define IFACE IDirectSoundBuffer | ||||||
| #define BUFPTR LPDIRECTSOUNDBUFFER | #define BUFPTR LPDIRECTSOUNDBUFFER | ||||||
| #define FIELD dsound_buffer | #define FIELD dsound_buffer | ||||||
| #define FIELD2 dsound |  | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| static int glue (dsound_unlock_, TYPE) ( | static int glue (dsound_unlock_, TYPE) ( | ||||||
| @@ -67,11 +63,11 @@ static int glue (dsound_lock_, TYPE) ( | |||||||
|     LPVOID *p2p, |     LPVOID *p2p, | ||||||
|     DWORD *blen1p, |     DWORD *blen1p, | ||||||
|     DWORD *blen2p, |     DWORD *blen2p, | ||||||
|     int entire, |     int entire | ||||||
|     dsound *s |  | ||||||
|     ) |     ) | ||||||
| { | { | ||||||
|     HRESULT hr; |     HRESULT hr; | ||||||
|  |     int i; | ||||||
|     LPVOID p1 = NULL, p2 = NULL; |     LPVOID p1 = NULL, p2 = NULL; | ||||||
|     DWORD blen1 = 0, blen2 = 0; |     DWORD blen1 = 0, blen2 = 0; | ||||||
|     DWORD flag; |     DWORD flag; | ||||||
| @@ -81,21 +77,40 @@ 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, &p1, &blen1, &p2, &blen2, flag); |     for (i = 0; i < conf.lock_retries; ++i) { | ||||||
|  |         hr = glue (IFACE, _Lock) ( | ||||||
|  |             buf, | ||||||
|  |             pos, | ||||||
|  |             len, | ||||||
|  |             &p1, | ||||||
|  |             &blen1, | ||||||
|  |             &p2, | ||||||
|  |             &blen2, | ||||||
|  |             flag | ||||||
|  |             ); | ||||||
|  |  | ||||||
|         if (FAILED (hr)) { |         if (FAILED (hr)) { | ||||||
| #ifndef DSBTYPE_IN | #ifndef DSBTYPE_IN | ||||||
|             if (hr == DSERR_BUFFERLOST) { |             if (hr == DSERR_BUFFERLOST) { | ||||||
|             if (glue (dsound_restore_, TYPE) (buf, s)) { |                 if (glue (dsound_restore_, TYPE) (buf)) { | ||||||
|                     dsound_logerr (hr, "Could not lock " NAME "\n"); |                     dsound_logerr (hr, "Could not lock " NAME "\n"); | ||||||
|             } |  | ||||||
|                     goto fail; |                     goto fail; | ||||||
|                 } |                 } | ||||||
|  |                 continue; | ||||||
|  |             } | ||||||
| #endif | #endif | ||||||
|             dsound_logerr (hr, "Could not lock " NAME "\n"); |             dsound_logerr (hr, "Could not lock " NAME "\n"); | ||||||
|             goto fail; |             goto fail; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (i == conf.lock_retries) { | ||||||
|  |         dolog ("%d attempts to lock " NAME " failed\n", i); | ||||||
|  |         goto fail; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     if ((p1 && (blen1 & info->align)) || (p2 && (blen2 & info->align))) { |     if ((p1 && (blen1 & info->align)) || (p2 && (blen2 & info->align))) { | ||||||
|         dolog ("DirectSound returned misaligned buffer %ld %ld\n", |         dolog ("DirectSound returned misaligned buffer %ld %ld\n", | ||||||
|                blen1, blen2); |                blen1, blen2); | ||||||
| @@ -155,19 +170,16 @@ static void dsound_fini_out (HWVoiceOut *hw) | |||||||
| } | } | ||||||
|  |  | ||||||
| #ifdef DSBTYPE_IN | #ifdef DSBTYPE_IN | ||||||
| static int dsound_init_in(HWVoiceIn *hw, struct audsettings *as, | static int dsound_init_in (HWVoiceIn *hw, audsettings_t *as) | ||||||
|                           void *drv_opaque) |  | ||||||
| #else | #else | ||||||
| static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as, | static int dsound_init_out (HWVoiceOut *hw, audsettings_t *as) | ||||||
|                            void *drv_opaque) |  | ||||||
| #endif | #endif | ||||||
| { | { | ||||||
|     int err; |     int err; | ||||||
|     HRESULT hr; |     HRESULT hr; | ||||||
|     dsound *s = drv_opaque; |     dsound *s = &glob_dsound; | ||||||
|     WAVEFORMATEX wfx; |     WAVEFORMATEX wfx; | ||||||
|     struct audsettings obt_as; |     audsettings_t 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; | ||||||
| @@ -180,11 +192,6 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as, | |||||||
|     DSBCAPS bc; |     DSBCAPS bc; | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|     if (!s->FIELD2) { |  | ||||||
|         dolog ("Attempt to initialize voice without " NAME2 " object\n"); |  | ||||||
|         return -1; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     err = waveformat_from_audio_settings (&wfx, as); |     err = waveformat_from_audio_settings (&wfx, as); | ||||||
|     if (err) { |     if (err) { | ||||||
|         return -1; |         return -1; | ||||||
| @@ -194,7 +201,7 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as, | |||||||
|     bd.dwSize = sizeof (bd); |     bd.dwSize = sizeof (bd); | ||||||
|     bd.lpwfxFormat = &wfx; |     bd.lpwfxFormat = &wfx; | ||||||
| #ifdef DSBTYPE_IN | #ifdef DSBTYPE_IN | ||||||
|     bd.dwBufferBytes = conf->bufsize_in; |     bd.dwBufferBytes = conf.bufsize_in; | ||||||
|     hr = IDirectSoundCapture_CreateCaptureBuffer ( |     hr = IDirectSoundCapture_CreateCaptureBuffer ( | ||||||
|         s->dsound_capture, |         s->dsound_capture, | ||||||
|         &bd, |         &bd, | ||||||
| @@ -203,7 +210,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; |     bd.dwBufferBytes = conf.bufsize_out; | ||||||
|     hr = IDirectSound_CreateSoundBuffer ( |     hr = IDirectSound_CreateSoundBuffer ( | ||||||
|         s->dsound, |         s->dsound, | ||||||
|         &bd, |         &bd, | ||||||
| @@ -253,7 +260,6 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as, | |||||||
|             ); |             ); | ||||||
|     } |     } | ||||||
|     hw->samples = bc.dwBufferBytes >> hw->info.shift; |     hw->samples = bc.dwBufferBytes >> hw->info.shift; | ||||||
|     ds->s = s; |  | ||||||
|  |  | ||||||
| #ifdef DEBUG_DSOUND | #ifdef DEBUG_DSOUND | ||||||
|     dolog ("caps %ld, desc %ld\n", |     dolog ("caps %ld, desc %ld\n", | ||||||
| @@ -270,9 +276,7 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as, | |||||||
| } | } | ||||||
|  |  | ||||||
| #undef NAME | #undef NAME | ||||||
| #undef NAME2 |  | ||||||
| #undef TYPE | #undef TYPE | ||||||
| #undef IFACE | #undef IFACE | ||||||
| #undef BUFPTR | #undef BUFPTR | ||||||
| #undef FIELD | #undef FIELD | ||||||
| #undef FIELD2 |  | ||||||
|   | |||||||
| @@ -26,41 +26,58 @@ | |||||||
|  * SEAL 1.07 by Carlos 'pel' Hasan was used as documentation |  * SEAL 1.07 by Carlos 'pel' Hasan was used as documentation | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #include "qemu/osdep.h" |  | ||||||
| #include "qemu-common.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" | ||||||
|  |  | ||||||
|  | #define WIN32_LEAN_AND_MEAN | ||||||
| #include <windows.h> | #include <windows.h> | ||||||
| #include <mmsystem.h> | #include <mmsystem.h> | ||||||
| #include <objbase.h> | #include <objbase.h> | ||||||
| #include <dsound.h> | #include <dsound.h> | ||||||
|  |  | ||||||
| #include "audio_win_int.h" |  | ||||||
|  |  | ||||||
| /* #define DEBUG_DSOUND */ | /* #define DEBUG_DSOUND */ | ||||||
|  |  | ||||||
| typedef struct { | static struct { | ||||||
|  |     int lock_retries; | ||||||
|  |     int restore_retries; | ||||||
|  |     int getstatus_retries; | ||||||
|  |     int set_primary; | ||||||
|     int bufsize_in; |     int bufsize_in; | ||||||
|     int bufsize_out; |     int bufsize_out; | ||||||
|  |     audsettings_t settings; | ||||||
|     int latency_millis; |     int latency_millis; | ||||||
| } DSoundConf; | } conf = { | ||||||
|  |     1, | ||||||
|  |     1, | ||||||
|  |     1, | ||||||
|  |     0, | ||||||
|  |     16384, | ||||||
|  |     16384, | ||||||
|  |     { | ||||||
|  |         44100, | ||||||
|  |         2, | ||||||
|  |         AUD_FMT_S16 | ||||||
|  |     }, | ||||||
|  |     10 | ||||||
|  | }; | ||||||
|  |  | ||||||
| typedef struct { | typedef struct { | ||||||
|     LPDIRECTSOUND dsound; |     LPDIRECTSOUND dsound; | ||||||
|     LPDIRECTSOUNDCAPTURE dsound_capture; |     LPDIRECTSOUNDCAPTURE dsound_capture; | ||||||
|     struct audsettings settings; |     LPDIRECTSOUNDBUFFER dsound_primary_buffer; | ||||||
|     DSoundConf conf; |     audsettings_t settings; | ||||||
| } dsound; | } dsound; | ||||||
|  |  | ||||||
|  | static dsound glob_dsound; | ||||||
|  |  | ||||||
| typedef struct { | typedef struct { | ||||||
|     HWVoiceOut hw; |     HWVoiceOut hw; | ||||||
|     LPDIRECTSOUNDBUFFER dsound_buffer; |     LPDIRECTSOUNDBUFFER dsound_buffer; | ||||||
|     DWORD old_pos; |     DWORD old_pos; | ||||||
|     int first_time; |     int first_time; | ||||||
|     dsound *s; |  | ||||||
| #ifdef DEBUG_DSOUND | #ifdef DEBUG_DSOUND | ||||||
|     DWORD old_ppos; |     DWORD old_ppos; | ||||||
|     DWORD played; |     DWORD played; | ||||||
| @@ -72,7 +89,6 @@ typedef struct { | |||||||
|     HWVoiceIn hw; |     HWVoiceIn hw; | ||||||
|     int first_time; |     int first_time; | ||||||
|     LPDIRECTSOUNDCAPTUREBUFFER dsound_capture_buffer; |     LPDIRECTSOUNDCAPTUREBUFFER dsound_capture_buffer; | ||||||
|     dsound *s; |  | ||||||
| } DSoundVoiceIn; | } DSoundVoiceIn; | ||||||
|  |  | ||||||
| static void dsound_log_hresult (HRESULT hr) | static void dsound_log_hresult (HRESULT hr) | ||||||
| @@ -266,16 +282,117 @@ static void print_wave_format (WAVEFORMATEX *wfx) | |||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| static int dsound_restore_out (LPDIRECTSOUNDBUFFER dsb, dsound *s) | static int dsound_restore_out (LPDIRECTSOUNDBUFFER dsb) | ||||||
| { | { | ||||||
|     HRESULT hr; |     HRESULT hr; | ||||||
|  |     int i; | ||||||
|  |  | ||||||
|  |     for (i = 0; i < conf.restore_retries; ++i) { | ||||||
|         hr = IDirectSoundBuffer_Restore (dsb); |         hr = IDirectSoundBuffer_Restore (dsb); | ||||||
|  |  | ||||||
|     if (hr != DS_OK) { |         switch (hr) { | ||||||
|  |         case DS_OK: | ||||||
|  |             return 0; | ||||||
|  |  | ||||||
|  |         case DSERR_BUFFERLOST: | ||||||
|  |             continue; | ||||||
|  |  | ||||||
|  |         default: | ||||||
|             dsound_logerr (hr, "Could not restore playback buffer\n"); |             dsound_logerr (hr, "Could not restore playback buffer\n"); | ||||||
|             return -1; |             return -1; | ||||||
|         } |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     dolog ("%d attempts to restore playback buffer failed\n", i); | ||||||
|  |     return -1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int waveformat_from_audio_settings (WAVEFORMATEX *wfx, audsettings_t *as) | ||||||
|  | { | ||||||
|  |     memset (wfx, 0, sizeof (*wfx)); | ||||||
|  |  | ||||||
|  |     wfx->wFormatTag = WAVE_FORMAT_PCM; | ||||||
|  |     wfx->nChannels = as->nchannels; | ||||||
|  |     wfx->nSamplesPerSec = as->freq; | ||||||
|  |     wfx->nAvgBytesPerSec = as->freq << (as->nchannels == 2); | ||||||
|  |     wfx->nBlockAlign = 1 << (as->nchannels == 2); | ||||||
|  |     wfx->cbSize = 0; | ||||||
|  |  | ||||||
|  |     switch (as->fmt) { | ||||||
|  |     case AUD_FMT_S8: | ||||||
|  |         wfx->wBitsPerSample = 8; | ||||||
|  |         break; | ||||||
|  |  | ||||||
|  |     case AUD_FMT_U8: | ||||||
|  |         wfx->wBitsPerSample = 8; | ||||||
|  |         break; | ||||||
|  |  | ||||||
|  |     case AUD_FMT_S16: | ||||||
|  |         wfx->wBitsPerSample = 16; | ||||||
|  |         wfx->nAvgBytesPerSec <<= 1; | ||||||
|  |         wfx->nBlockAlign <<= 1; | ||||||
|  |         break; | ||||||
|  |  | ||||||
|  |     case AUD_FMT_U16: | ||||||
|  |         wfx->wBitsPerSample = 16; | ||||||
|  |         wfx->nAvgBytesPerSec <<= 1; | ||||||
|  |         wfx->nBlockAlign <<= 1; | ||||||
|  |         break; | ||||||
|  |  | ||||||
|  |     default: | ||||||
|  |         dolog ("Internal logic error: Bad audio format %d\n", as->freq); | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int waveformat_to_audio_settings (WAVEFORMATEX *wfx, audsettings_t *as) | ||||||
|  | { | ||||||
|  |     if (wfx->wFormatTag != WAVE_FORMAT_PCM) { | ||||||
|  |         dolog ("Invalid wave format, tag is not PCM, but %d\n", | ||||||
|  |                wfx->wFormatTag); | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (!wfx->nSamplesPerSec) { | ||||||
|  |         dolog ("Invalid wave format, frequency is zero\n"); | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |     as->freq = wfx->nSamplesPerSec; | ||||||
|  |  | ||||||
|  |     switch (wfx->nChannels) { | ||||||
|  |     case 1: | ||||||
|  |         as->nchannels = 1; | ||||||
|  |         break; | ||||||
|  |  | ||||||
|  |     case 2: | ||||||
|  |         as->nchannels = 2; | ||||||
|  |         break; | ||||||
|  |  | ||||||
|  |     default: | ||||||
|  |         dolog ( | ||||||
|  |             "Invalid wave format, number of channels is not 1 or 2, but %d\n", | ||||||
|  |             wfx->nChannels | ||||||
|  |             ); | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     switch (wfx->wBitsPerSample) { | ||||||
|  |     case 8: | ||||||
|  |         as->fmt = AUD_FMT_U8; | ||||||
|  |         break; | ||||||
|  |  | ||||||
|  |     case 16: | ||||||
|  |         as->fmt = AUD_FMT_S16; | ||||||
|  |         break; | ||||||
|  |  | ||||||
|  |     default: | ||||||
|  |         dolog ("Invalid wave format, bits per sample is not 8 or 16, but %d\n", | ||||||
|  |                wfx->wBitsPerSample); | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -284,11 +401,12 @@ static int dsound_restore_out (LPDIRECTSOUNDBUFFER dsb, dsound *s) | |||||||
| #include "dsound_template.h" | #include "dsound_template.h" | ||||||
| #undef DSBTYPE_IN | #undef DSBTYPE_IN | ||||||
|  |  | ||||||
| static int dsound_get_status_out (LPDIRECTSOUNDBUFFER dsb, DWORD *statusp, | static int dsound_get_status_out (LPDIRECTSOUNDBUFFER dsb, DWORD *statusp) | ||||||
|                                   dsound *s) |  | ||||||
| { | { | ||||||
|     HRESULT hr; |     HRESULT hr; | ||||||
|  |     int i; | ||||||
|  |  | ||||||
|  |     for (i = 0; i < conf.getstatus_retries; ++i) { | ||||||
|         hr = IDirectSoundBuffer_GetStatus (dsb, statusp); |         hr = IDirectSoundBuffer_GetStatus (dsb, statusp); | ||||||
|         if (FAILED (hr)) { |         if (FAILED (hr)) { | ||||||
|             dsound_logerr (hr, "Could not get playback buffer status\n"); |             dsound_logerr (hr, "Could not get playback buffer status\n"); | ||||||
| @@ -296,9 +414,13 @@ static int dsound_get_status_out (LPDIRECTSOUNDBUFFER dsb, DWORD *statusp, | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (*statusp & DSERR_BUFFERLOST) { |         if (*statusp & DSERR_BUFFERLOST) { | ||||||
|         dsound_restore_out(dsb, s); |             if (dsound_restore_out (dsb)) { | ||||||
|                 return -1; |                 return -1; | ||||||
|             } |             } | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
| @@ -322,8 +444,8 @@ static void dsound_write_sample (HWVoiceOut *hw, uint8_t *dst, int dst_len) | |||||||
|     int src_len1 = dst_len; |     int src_len1 = dst_len; | ||||||
|     int src_len2 = 0; |     int src_len2 = 0; | ||||||
|     int pos = hw->rpos + dst_len; |     int pos = hw->rpos + dst_len; | ||||||
|     struct st_sample *src1 = hw->mix_buf + hw->rpos; |     st_sample_t *src1 = hw->mix_buf + hw->rpos; | ||||||
|     struct st_sample *src2 = NULL; |     st_sample_t *src2 = NULL; | ||||||
|  |  | ||||||
|     if (pos > hw->samples) { |     if (pos > hw->samples) { | ||||||
|         src_len1 = hw->samples - hw->rpos; |         src_len1 = hw->samples - hw->rpos; | ||||||
| @@ -344,8 +466,7 @@ static void dsound_write_sample (HWVoiceOut *hw, uint8_t *dst, int dst_len) | |||||||
|     hw->rpos = pos % hw->samples; |     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) |  | ||||||
| { | { | ||||||
|     int err; |     int err; | ||||||
|     LPVOID p1, p2; |     LPVOID p1, p2; | ||||||
| @@ -358,8 +479,7 @@ static void dsound_clear_sample (HWVoiceOut *hw, LPDIRECTSOUNDBUFFER dsb, | |||||||
|         hw->samples << hw->info.shift, |         hw->samples << hw->info.shift, | ||||||
|         &p1, &p2, |         &p1, &p2, | ||||||
|         &blen1, &blen2, |         &blen1, &blen2, | ||||||
|         1, |         1 | ||||||
|         s |  | ||||||
|         ); |         ); | ||||||
|     if (err) { |     if (err) { | ||||||
|         return; |         return; | ||||||
| @@ -385,9 +505,25 @@ static void dsound_clear_sample (HWVoiceOut *hw, LPDIRECTSOUNDBUFFER dsb, | |||||||
|     dsound_unlock_out (dsb, p1, p2, blen1, blen2); |     dsound_unlock_out (dsb, p1, p2, blen1, blen2); | ||||||
| } | } | ||||||
|  |  | ||||||
| static int dsound_open (dsound *s) | static void dsound_close (dsound *s) | ||||||
| { | { | ||||||
|     HRESULT hr; |     HRESULT hr; | ||||||
|  |  | ||||||
|  |     if (s->dsound_primary_buffer) { | ||||||
|  |         hr = IDirectSoundBuffer_Release (s->dsound_primary_buffer); | ||||||
|  |         if (FAILED (hr)) { | ||||||
|  |             dsound_logerr (hr, "Could not release primary buffer\n"); | ||||||
|  |         } | ||||||
|  |         s->dsound_primary_buffer = NULL; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int dsound_open (dsound *s) | ||||||
|  | { | ||||||
|  |     int err; | ||||||
|  |     HRESULT hr; | ||||||
|  |     WAVEFORMATEX wfx; | ||||||
|  |     DSBUFFERDESC dsbd; | ||||||
|     HWND hwnd; |     HWND hwnd; | ||||||
|  |  | ||||||
|     hwnd = GetForegroundWindow (); |     hwnd = GetForegroundWindow (); | ||||||
| @@ -403,16 +539,71 @@ static int dsound_open (dsound *s) | |||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     if (!conf.set_primary) { | ||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     err = waveformat_from_audio_settings (&wfx, &conf.settings); | ||||||
|  |     if (err) { | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     memset (&dsbd, 0, sizeof (dsbd)); | ||||||
|  |     dsbd.dwSize = sizeof (dsbd); | ||||||
|  |     dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER; | ||||||
|  |     dsbd.dwBufferBytes = 0; | ||||||
|  |     dsbd.lpwfxFormat = NULL; | ||||||
|  |  | ||||||
|  |     hr = IDirectSound_CreateSoundBuffer ( | ||||||
|  |         s->dsound, | ||||||
|  |         &dsbd, | ||||||
|  |         &s->dsound_primary_buffer, | ||||||
|  |         NULL | ||||||
|  |         ); | ||||||
|  |     if (FAILED (hr)) { | ||||||
|  |         dsound_logerr (hr, "Could not create primary playback buffer\n"); | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     hr = IDirectSoundBuffer_SetFormat (s->dsound_primary_buffer, &wfx); | ||||||
|  |     if (FAILED (hr)) { | ||||||
|  |         dsound_logerr (hr, "Could not set primary playback buffer format\n"); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     hr = IDirectSoundBuffer_GetFormat ( | ||||||
|  |         s->dsound_primary_buffer, | ||||||
|  |         &wfx, | ||||||
|  |         sizeof (wfx), | ||||||
|  |         NULL | ||||||
|  |         ); | ||||||
|  |     if (FAILED (hr)) { | ||||||
|  |         dsound_logerr (hr, "Could not get primary playback buffer format\n"); | ||||||
|  |         goto fail0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | #ifdef DEBUG_DSOUND | ||||||
|  |     dolog ("Primary\n"); | ||||||
|  |     print_wave_format (&wfx); | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |     err = waveformat_to_audio_settings (&wfx, &s->settings); | ||||||
|  |     if (err) { | ||||||
|  |         goto fail0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return 0; | ||||||
|  |  | ||||||
|  |  fail0: | ||||||
|  |     dsound_close (s); | ||||||
|  |     return -1; | ||||||
|  | } | ||||||
|  |  | ||||||
| static int dsound_ctl_out (HWVoiceOut *hw, int cmd, ...) | static int dsound_ctl_out (HWVoiceOut *hw, int cmd, ...) | ||||||
| { | { | ||||||
|     HRESULT hr; |     HRESULT hr; | ||||||
|     DWORD status; |     DWORD status; | ||||||
|     DSoundVoiceOut *ds = (DSoundVoiceOut *) hw; |     DSoundVoiceOut *ds = (DSoundVoiceOut *) hw; | ||||||
|     LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer; |     LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer; | ||||||
|     dsound *s = ds->s; |  | ||||||
|  |  | ||||||
|     if (!dsb) { |     if (!dsb) { | ||||||
|         dolog ("Attempt to control voice without a buffer\n"); |         dolog ("Attempt to control voice without a buffer\n"); | ||||||
| @@ -421,7 +612,7 @@ static int dsound_ctl_out (HWVoiceOut *hw, int cmd, ...) | |||||||
|  |  | ||||||
|     switch (cmd) { |     switch (cmd) { | ||||||
|     case VOICE_ENABLE: |     case VOICE_ENABLE: | ||||||
|         if (dsound_get_status_out (dsb, &status, s)) { |         if (dsound_get_status_out (dsb, &status)) { | ||||||
|             return -1; |             return -1; | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -430,7 +621,7 @@ static int dsound_ctl_out (HWVoiceOut *hw, int cmd, ...) | |||||||
|             return 0; |             return 0; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         dsound_clear_sample (hw, dsb, s); |         dsound_clear_sample (hw, dsb); | ||||||
|  |  | ||||||
|         hr = IDirectSoundBuffer_Play (dsb, 0, 0, DSBPLAY_LOOPING); |         hr = IDirectSoundBuffer_Play (dsb, 0, 0, DSBPLAY_LOOPING); | ||||||
|         if (FAILED (hr)) { |         if (FAILED (hr)) { | ||||||
| @@ -440,7 +631,7 @@ static int dsound_ctl_out (HWVoiceOut *hw, int cmd, ...) | |||||||
|         break; |         break; | ||||||
|  |  | ||||||
|     case VOICE_DISABLE: |     case VOICE_DISABLE: | ||||||
|         if (dsound_get_status_out (dsb, &status, s)) { |         if (dsound_get_status_out (dsb, &status)) { | ||||||
|             return -1; |             return -1; | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -464,21 +655,19 @@ static int dsound_write (SWVoiceOut *sw, void *buf, int len) | |||||||
|     return audio_pcm_sw_write (sw, buf, len); |     return audio_pcm_sw_write (sw, buf, len); | ||||||
| } | } | ||||||
|  |  | ||||||
| static int dsound_run_out (HWVoiceOut *hw, int live) | static int dsound_run_out (HWVoiceOut *hw) | ||||||
| { | { | ||||||
|     int err; |     int err; | ||||||
|     HRESULT hr; |     HRESULT hr; | ||||||
|     DSoundVoiceOut *ds = (DSoundVoiceOut *) hw; |     DSoundVoiceOut *ds = (DSoundVoiceOut *) hw; | ||||||
|     LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer; |     LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer; | ||||||
|     int len, hwshift; |     int live, len, hwshift; | ||||||
|     DWORD blen1, blen2; |     DWORD blen1, blen2; | ||||||
|     DWORD len1, len2; |     DWORD len1, len2; | ||||||
|     DWORD decr; |     DWORD decr; | ||||||
|     DWORD wpos, ppos, old_pos; |     DWORD wpos, ppos, old_pos; | ||||||
|     LPVOID p1, p2; |     LPVOID p1, p2; | ||||||
|     int bufsize; |     int bufsize; | ||||||
|     dsound *s = ds->s; |  | ||||||
|     DSoundConf *conf = &s->conf; |  | ||||||
|  |  | ||||||
|     if (!dsb) { |     if (!dsb) { | ||||||
|         dolog ("Attempt to run empty with playback buffer\n"); |         dolog ("Attempt to run empty with playback buffer\n"); | ||||||
| @@ -488,6 +677,8 @@ static int dsound_run_out (HWVoiceOut *hw, int live) | |||||||
|     hwshift = hw->info.shift; |     hwshift = hw->info.shift; | ||||||
|     bufsize = hw->samples << hwshift; |     bufsize = hw->samples << hwshift; | ||||||
|  |  | ||||||
|  |     live = audio_pcm_hw_get_live_out (hw); | ||||||
|  |  | ||||||
|     hr = IDirectSoundBuffer_GetCurrentPosition ( |     hr = IDirectSoundBuffer_GetCurrentPosition ( | ||||||
|         dsb, |         dsb, | ||||||
|         &ppos, |         &ppos, | ||||||
| @@ -501,14 +692,14 @@ static int dsound_run_out (HWVoiceOut *hw, int live) | |||||||
|     len = live << hwshift; |     len = live << hwshift; | ||||||
|  |  | ||||||
|     if (ds->first_time) { |     if (ds->first_time) { | ||||||
|         if (conf->latency_millis) { |         if (conf.latency_millis) { | ||||||
|             DWORD cur_blat; |             DWORD cur_blat; | ||||||
|  |  | ||||||
|             cur_blat = audio_ring_dist (wpos, ppos, bufsize); |             cur_blat = audio_ring_dist (wpos, ppos, bufsize); | ||||||
|             ds->first_time = 0; |             ds->first_time = 0; | ||||||
|             old_pos = wpos; |             old_pos = wpos; | ||||||
|             old_pos += |             old_pos += | ||||||
|                 millis_to_bytes (&hw->info, conf->latency_millis) - cur_blat; |                 millis_to_bytes (&hw->info, conf.latency_millis) - cur_blat; | ||||||
|             old_pos %= bufsize; |             old_pos %= bufsize; | ||||||
|             old_pos &= ~hw->info.align; |             old_pos &= ~hw->info.align; | ||||||
|         } |         } | ||||||
| @@ -543,7 +734,7 @@ static int dsound_run_out (HWVoiceOut *hw, int live) | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (audio_bug(__func__, len < 0 || len > bufsize)) { |     if (audio_bug (AUDIO_FUNC, len < 0 || len > bufsize)) { | ||||||
|         dolog ("len=%d bufsize=%d old_pos=%ld ppos=%ld\n", |         dolog ("len=%d bufsize=%d old_pos=%ld ppos=%ld\n", | ||||||
|                len, bufsize, old_pos, ppos); |                len, bufsize, old_pos, ppos); | ||||||
|         return 0; |         return 0; | ||||||
| @@ -564,8 +755,7 @@ static int dsound_run_out (HWVoiceOut *hw, int live) | |||||||
|         len, |         len, | ||||||
|         &p1, &p2, |         &p1, &p2, | ||||||
|         &blen1, &blen2, |         &blen1, &blen2, | ||||||
|         0, |         0 | ||||||
|         s |  | ||||||
|         ); |         ); | ||||||
|     if (err) { |     if (err) { | ||||||
|         return 0; |         return 0; | ||||||
| @@ -668,7 +858,6 @@ static int dsound_run_in (HWVoiceIn *hw) | |||||||
|     DWORD cpos, rpos; |     DWORD cpos, rpos; | ||||||
|     LPVOID p1, p2; |     LPVOID p1, p2; | ||||||
|     int hwshift; |     int hwshift; | ||||||
|     dsound *s = ds->s; |  | ||||||
|  |  | ||||||
|     if (!dscb) { |     if (!dscb) { | ||||||
|         dolog ("Attempt to run without capture buffer\n"); |         dolog ("Attempt to run without capture buffer\n"); | ||||||
| @@ -723,8 +912,7 @@ static int dsound_run_in (HWVoiceIn *hw) | |||||||
|         &p2, |         &p2, | ||||||
|         &blen1, |         &blen1, | ||||||
|         &blen2, |         &blen2, | ||||||
|         0, |         0 | ||||||
|         s |  | ||||||
|         ); |         ); | ||||||
|     if (err) { |     if (err) { | ||||||
|         return 0; |         return 0; | ||||||
| @@ -735,11 +923,11 @@ static int dsound_run_in (HWVoiceIn *hw) | |||||||
|     decr = len1 + len2; |     decr = len1 + len2; | ||||||
|  |  | ||||||
|     if (p1 && len1) { |     if (p1 && len1) { | ||||||
|         hw->conv (hw->conv_buf + hw->wpos, p1, len1); |         hw->conv (hw->conv_buf + hw->wpos, p1, len1, &nominal_volume); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (p2 && len2) { |     if (p2 && len2) { | ||||||
|         hw->conv (hw->conv_buf, p2, len2); |         hw->conv (hw->conv_buf, p2, len2, &nominal_volume); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     dsound_unlock_in (dscb, p1, p2, blen1, blen2); |     dsound_unlock_in (dscb, p1, p2, blen1, blen2); | ||||||
| @@ -747,19 +935,12 @@ static int dsound_run_in (HWVoiceIn *hw) | |||||||
|     return decr; |     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; | ||||||
|     dsound *s = opaque; |     dsound *s = opaque; | ||||||
|  |  | ||||||
|     if (!s->dsound) { |     if (!s->dsound) { | ||||||
|         g_free(s); |  | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -770,7 +951,6 @@ static void dsound_audio_fini (void *opaque) | |||||||
|     s->dsound = NULL; |     s->dsound = NULL; | ||||||
|  |  | ||||||
|     if (!s->dsound_capture) { |     if (!s->dsound_capture) { | ||||||
|         g_free(s); |  | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -779,21 +959,17 @@ static void dsound_audio_fini (void *opaque) | |||||||
|         dsound_logerr (hr, "Could not release DirectSoundCapture\n"); |         dsound_logerr (hr, "Could not release DirectSoundCapture\n"); | ||||||
|     } |     } | ||||||
|     s->dsound_capture = NULL; |     s->dsound_capture = NULL; | ||||||
|  |  | ||||||
|     g_free(s); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static void *dsound_audio_init (void) | static void *dsound_audio_init (void) | ||||||
| { | { | ||||||
|     int err; |     int err; | ||||||
|     HRESULT hr; |     HRESULT hr; | ||||||
|     dsound *s = g_malloc0(sizeof(dsound)); |     dsound *s = &glob_dsound; | ||||||
|  |  | ||||||
|     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"); | ||||||
|         g_free(s); |  | ||||||
|         return NULL; |         return NULL; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -806,7 +982,6 @@ static void *dsound_audio_init (void) | |||||||
|         ); |         ); | ||||||
|     if (FAILED (hr)) { |     if (FAILED (hr)) { | ||||||
|         dsound_logerr (hr, "Could not create DirectSound instance\n"); |         dsound_logerr (hr, "Could not create DirectSound instance\n"); | ||||||
|         g_free(s); |  | ||||||
|         return NULL; |         return NULL; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -818,7 +993,7 @@ static void *dsound_audio_init (void) | |||||||
|         if (FAILED (hr)) { |         if (FAILED (hr)) { | ||||||
|             dsound_logerr (hr, "Could not release DirectSound\n"); |             dsound_logerr (hr, "Could not release DirectSound\n"); | ||||||
|         } |         } | ||||||
|         g_free(s); |         s->dsound = NULL; | ||||||
|         return NULL; |         return NULL; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -855,57 +1030,54 @@ static void *dsound_audio_init (void) | |||||||
| } | } | ||||||
|  |  | ||||||
| static struct audio_option dsound_options[] = { | static struct audio_option dsound_options[] = { | ||||||
|     { |     {"LOCK_RETRIES", AUD_OPT_INT, &conf.lock_retries, | ||||||
|         .name  = "LATENCY_MILLIS", |      "Number of times to attempt locking the buffer", NULL, 0}, | ||||||
|         .tag   = AUD_OPT_INT, |     {"RESTOURE_RETRIES", AUD_OPT_INT, &conf.restore_retries, | ||||||
|         .valp  = &glob_conf.latency_millis, |      "Number of times to attempt restoring the buffer", NULL, 0}, | ||||||
|         .descr = "(undocumented)" |     {"GETSTATUS_RETRIES", AUD_OPT_INT, &conf.getstatus_retries, | ||||||
|     }, |      "Number of times to attempt getting status of the buffer", NULL, 0}, | ||||||
|     { |     {"SET_PRIMARY", AUD_OPT_BOOL, &conf.set_primary, | ||||||
|         .name  = "BUFSIZE_OUT", |      "Set the parameters of primary buffer", NULL, 0}, | ||||||
|         .tag   = AUD_OPT_INT, |     {"LATENCY_MILLIS", AUD_OPT_INT, &conf.latency_millis, | ||||||
|         .valp  = &glob_conf.bufsize_out, |      "(undocumented)", NULL, 0}, | ||||||
|         .descr = "(undocumented)" |     {"PRIMARY_FREQ", AUD_OPT_INT, &conf.settings.freq, | ||||||
|     }, |      "Primary buffer frequency", NULL, 0}, | ||||||
|     { |     {"PRIMARY_CHANNELS", AUD_OPT_INT, &conf.settings.nchannels, | ||||||
|         .name  = "BUFSIZE_IN", |      "Primary buffer number of channels (1 - mono, 2 - stereo)", NULL, 0}, | ||||||
|         .tag   = AUD_OPT_INT, |     {"PRIMARY_FMT", AUD_OPT_FMT, &conf.settings.fmt, | ||||||
|         .valp  = &glob_conf.bufsize_in, |      "Primary buffer format", NULL, 0}, | ||||||
|         .descr = "(undocumented)" |     {"BUFSIZE_OUT", AUD_OPT_INT, &conf.bufsize_out, | ||||||
|     }, |      "(undocumented)", NULL, 0}, | ||||||
|     { /* End of list */ } |     {"BUFSIZE_IN", AUD_OPT_INT, &conf.bufsize_in, | ||||||
|  |      "(undocumented)", NULL, 0}, | ||||||
|  |     {NULL, 0, NULL, NULL, NULL, 0} | ||||||
| }; | }; | ||||||
|  |  | ||||||
| static struct audio_pcm_ops dsound_pcm_ops = { | static struct audio_pcm_ops dsound_pcm_ops = { | ||||||
|     .init_out = dsound_init_out, |     dsound_init_out, | ||||||
|     .fini_out = dsound_fini_out, |     dsound_fini_out, | ||||||
|     .run_out  = dsound_run_out, |     dsound_run_out, | ||||||
|     .write    = dsound_write, |     dsound_write, | ||||||
|     .ctl_out  = dsound_ctl_out, |     dsound_ctl_out, | ||||||
|  |  | ||||||
|     .init_in  = dsound_init_in, |     dsound_init_in, | ||||||
|     .fini_in  = dsound_fini_in, |     dsound_fini_in, | ||||||
|     .run_in   = dsound_run_in, |     dsound_run_in, | ||||||
|     .read     = dsound_read, |     dsound_read, | ||||||
|     .ctl_in   = dsound_ctl_in |     dsound_ctl_in | ||||||
| }; | }; | ||||||
|  |  | ||||||
| static struct audio_driver dsound_audio_driver = { | struct audio_driver dsound_audio_driver = { | ||||||
|     .name           = "dsound", |     INIT_FIELD (name           = ) "dsound", | ||||||
|     .descr          = "DirectSound http://wikipedia.org/wiki/DirectSound", |     INIT_FIELD (descr          = ) | ||||||
|     .options        = dsound_options, |     "DirectSound http://wikipedia.org/wiki/DirectSound", | ||||||
|     .init           = dsound_audio_init, |     INIT_FIELD (options        = ) dsound_options, | ||||||
|     .fini           = dsound_audio_fini, |     INIT_FIELD (init           = ) dsound_audio_init, | ||||||
|     .pcm_ops        = &dsound_pcm_ops, |     INIT_FIELD (fini           = ) dsound_audio_fini, | ||||||
|     .can_be_default = 1, |     INIT_FIELD (pcm_ops        = ) &dsound_pcm_ops, | ||||||
|     .max_voices_out = INT_MAX, |     INIT_FIELD (can_be_default = ) 1, | ||||||
|     .max_voices_in  = 1, |     INIT_FIELD (max_voices_out = ) INT_MAX, | ||||||
|     .voice_size_out = sizeof (DSoundVoiceOut), |     INIT_FIELD (max_voices_in  = ) 1, | ||||||
|     .voice_size_in  = sizeof (DSoundVoiceIn) |     INIT_FIELD (voice_size_out = ) sizeof (DSoundVoiceOut), | ||||||
|  |     INIT_FIELD (voice_size_in  = ) sizeof (DSoundVoiceIn) | ||||||
| }; | }; | ||||||
|  |  | ||||||
| static void register_audio_dsound(void) |  | ||||||
| { |  | ||||||
|     audio_driver_register(&dsound_audio_driver); |  | ||||||
| } |  | ||||||
| type_init(register_audio_dsound); |  | ||||||
|   | |||||||
							
								
								
									
										686
									
								
								audio/fmodaudio.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										686
									
								
								audio/fmodaudio.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,686 @@ | |||||||
|  | /* | ||||||
|  |  * QEMU FMOD audio driver | ||||||
|  |  * | ||||||
|  |  * Copyright (c) 2004-2005 Vassili Karpov (malc) | ||||||
|  |  * | ||||||
|  |  * 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 <fmod.h> | ||||||
|  | #include <fmod_errors.h> | ||||||
|  | #include "qemu-common.h" | ||||||
|  | #include "audio.h" | ||||||
|  |  | ||||||
|  | #define AUDIO_CAP "fmod" | ||||||
|  | #include "audio_int.h" | ||||||
|  |  | ||||||
|  | typedef struct FMODVoiceOut { | ||||||
|  |     HWVoiceOut hw; | ||||||
|  |     unsigned int old_pos; | ||||||
|  |     FSOUND_SAMPLE *fmod_sample; | ||||||
|  |     int channel; | ||||||
|  | } FMODVoiceOut; | ||||||
|  |  | ||||||
|  | typedef struct FMODVoiceIn { | ||||||
|  |     HWVoiceIn hw; | ||||||
|  |     FSOUND_SAMPLE *fmod_sample; | ||||||
|  | } FMODVoiceIn; | ||||||
|  |  | ||||||
|  | static struct { | ||||||
|  |     const char *drvname; | ||||||
|  |     int nb_samples; | ||||||
|  |     int freq; | ||||||
|  |     int nb_channels; | ||||||
|  |     int bufsize; | ||||||
|  |     int threshold; | ||||||
|  |     int broken_adc; | ||||||
|  | } conf = { | ||||||
|  |     NULL, | ||||||
|  |     2048 * 2, | ||||||
|  |     44100, | ||||||
|  |     2, | ||||||
|  |     0, | ||||||
|  |     0, | ||||||
|  |     0 | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static void GCC_FMT_ATTR (1, 2) fmod_logerr (const char *fmt, ...) | ||||||
|  | { | ||||||
|  |     va_list ap; | ||||||
|  |  | ||||||
|  |     va_start (ap, fmt); | ||||||
|  |     AUD_vlog (AUDIO_CAP, fmt, ap); | ||||||
|  |     va_end (ap); | ||||||
|  |  | ||||||
|  |     AUD_log (AUDIO_CAP, "Reason: %s\n", | ||||||
|  |              FMOD_ErrorString (FSOUND_GetError ())); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void GCC_FMT_ATTR (2, 3) fmod_logerr2 ( | ||||||
|  |     const char *typ, | ||||||
|  |     const char *fmt, | ||||||
|  |     ... | ||||||
|  |     ) | ||||||
|  | { | ||||||
|  |     va_list ap; | ||||||
|  |  | ||||||
|  |     AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ); | ||||||
|  |  | ||||||
|  |     va_start (ap, fmt); | ||||||
|  |     AUD_vlog (AUDIO_CAP, fmt, ap); | ||||||
|  |     va_end (ap); | ||||||
|  |  | ||||||
|  |     AUD_log (AUDIO_CAP, "Reason: %s\n", | ||||||
|  |              FMOD_ErrorString (FSOUND_GetError ())); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int fmod_write (SWVoiceOut *sw, void *buf, int len) | ||||||
|  | { | ||||||
|  |     return audio_pcm_sw_write (sw, buf, len); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void fmod_clear_sample (FMODVoiceOut *fmd) | ||||||
|  | { | ||||||
|  |     HWVoiceOut *hw = &fmd->hw; | ||||||
|  |     int status; | ||||||
|  |     void *p1 = 0, *p2 = 0; | ||||||
|  |     unsigned int len1 = 0, len2 = 0; | ||||||
|  |  | ||||||
|  |     status = FSOUND_Sample_Lock ( | ||||||
|  |         fmd->fmod_sample, | ||||||
|  |         0, | ||||||
|  |         hw->samples << hw->info.shift, | ||||||
|  |         &p1, | ||||||
|  |         &p2, | ||||||
|  |         &len1, | ||||||
|  |         &len2 | ||||||
|  |         ); | ||||||
|  |  | ||||||
|  |     if (!status) { | ||||||
|  |         fmod_logerr ("Failed to lock sample\n"); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if ((len1 & hw->info.align) || (len2 & hw->info.align)) { | ||||||
|  |         dolog ("Lock returned misaligned length %d, %d, alignment %d\n", | ||||||
|  |                len1, len2, hw->info.align + 1); | ||||||
|  |         goto fail; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if ((len1 + len2) - (hw->samples << hw->info.shift)) { | ||||||
|  |         dolog ("Lock returned incomplete length %d, %d\n", | ||||||
|  |                len1 + len2, hw->samples << hw->info.shift); | ||||||
|  |         goto fail; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     audio_pcm_info_clear_buf (&hw->info, p1, hw->samples); | ||||||
|  |  | ||||||
|  |  fail: | ||||||
|  |     status = FSOUND_Sample_Unlock (fmd->fmod_sample, p1, p2, len1, len2); | ||||||
|  |     if (!status) { | ||||||
|  |         fmod_logerr ("Failed to unlock sample\n"); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void fmod_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; | ||||||
|  |     st_sample_t *src1 = hw->mix_buf + hw->rpos; | ||||||
|  |     st_sample_t *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 int fmod_unlock_sample (FSOUND_SAMPLE *sample, void *p1, void *p2, | ||||||
|  |                                unsigned int blen1, unsigned int blen2) | ||||||
|  | { | ||||||
|  |     int status = FSOUND_Sample_Unlock (sample, p1, p2, blen1, blen2); | ||||||
|  |     if (!status) { | ||||||
|  |         fmod_logerr ("Failed to unlock sample\n"); | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int fmod_lock_sample ( | ||||||
|  |     FSOUND_SAMPLE *sample, | ||||||
|  |     struct audio_pcm_info *info, | ||||||
|  |     int pos, | ||||||
|  |     int len, | ||||||
|  |     void **p1, | ||||||
|  |     void **p2, | ||||||
|  |     unsigned int *blen1, | ||||||
|  |     unsigned int *blen2 | ||||||
|  |     ) | ||||||
|  | { | ||||||
|  |     int status; | ||||||
|  |  | ||||||
|  |     status = FSOUND_Sample_Lock ( | ||||||
|  |         sample, | ||||||
|  |         pos << info->shift, | ||||||
|  |         len << info->shift, | ||||||
|  |         p1, | ||||||
|  |         p2, | ||||||
|  |         blen1, | ||||||
|  |         blen2 | ||||||
|  |         ); | ||||||
|  |  | ||||||
|  |     if (!status) { | ||||||
|  |         fmod_logerr ("Failed to lock sample\n"); | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if ((*blen1 & info->align) || (*blen2 & info->align)) { | ||||||
|  |         dolog ("Lock returned misaligned length %d, %d, alignment %d\n", | ||||||
|  |                *blen1, *blen2, info->align + 1); | ||||||
|  |  | ||||||
|  |         fmod_unlock_sample (sample, *p1, *p2, *blen1, *blen2); | ||||||
|  |  | ||||||
|  |         *p1 = NULL - 1; | ||||||
|  |         *p2 = NULL - 1; | ||||||
|  |         *blen1 = ~0U; | ||||||
|  |         *blen2 = ~0U; | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (!*p1 && *blen1) { | ||||||
|  |         dolog ("warning: !p1 && blen1=%d\n", *blen1); | ||||||
|  |         *blen1 = 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (!p2 && *blen2) { | ||||||
|  |         dolog ("warning: !p2 && blen2=%d\n", *blen2); | ||||||
|  |         *blen2 = 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int fmod_run_out (HWVoiceOut *hw) | ||||||
|  | { | ||||||
|  |     FMODVoiceOut *fmd = (FMODVoiceOut *) hw; | ||||||
|  |     int live, decr; | ||||||
|  |     void *p1 = 0, *p2 = 0; | ||||||
|  |     unsigned int blen1 = 0, blen2 = 0; | ||||||
|  |     unsigned int len1 = 0, len2 = 0; | ||||||
|  |     int nb_live; | ||||||
|  |  | ||||||
|  |     live = audio_pcm_hw_get_live_out2 (hw, &nb_live); | ||||||
|  |     if (!live) { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (!hw->pending_disable | ||||||
|  |         && nb_live | ||||||
|  |         && (conf.threshold && live <= conf.threshold)) { | ||||||
|  |         ldebug ("live=%d nb_live=%d\n", live, nb_live); | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     decr = live; | ||||||
|  |  | ||||||
|  |     if (fmd->channel >= 0) { | ||||||
|  |         int len = decr; | ||||||
|  |         int old_pos = fmd->old_pos; | ||||||
|  |         int ppos = FSOUND_GetCurrentPosition (fmd->channel); | ||||||
|  |  | ||||||
|  |         if (ppos == old_pos || !ppos) { | ||||||
|  |             return 0; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if ((old_pos < ppos) && ((old_pos + len) > ppos)) { | ||||||
|  |             len = ppos - old_pos; | ||||||
|  |         } | ||||||
|  |         else { | ||||||
|  |             if ((old_pos > ppos) && ((old_pos + len) > (ppos + hw->samples))) { | ||||||
|  |                 len = hw->samples - old_pos + ppos; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         decr = len; | ||||||
|  |  | ||||||
|  |         if (audio_bug (AUDIO_FUNC, decr < 0)) { | ||||||
|  |             dolog ("decr=%d live=%d ppos=%d old_pos=%d len=%d\n", | ||||||
|  |                    decr, live, ppos, old_pos, len); | ||||||
|  |             return 0; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     if (!decr) { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (fmod_lock_sample (fmd->fmod_sample, &fmd->hw.info, | ||||||
|  |                           fmd->old_pos, decr, | ||||||
|  |                           &p1, &p2, | ||||||
|  |                           &blen1, &blen2)) { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     len1 = blen1 >> hw->info.shift; | ||||||
|  |     len2 = blen2 >> hw->info.shift; | ||||||
|  |     ldebug ("%p %p %d %d %d %d\n", p1, p2, len1, len2, blen1, blen2); | ||||||
|  |     decr = len1 + len2; | ||||||
|  |  | ||||||
|  |     if (p1 && len1) { | ||||||
|  |         fmod_write_sample (hw, p1, len1); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (p2 && len2) { | ||||||
|  |         fmod_write_sample (hw, p2, len2); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     fmod_unlock_sample (fmd->fmod_sample, p1, p2, blen1, blen2); | ||||||
|  |  | ||||||
|  |     fmd->old_pos = (fmd->old_pos + decr) % hw->samples; | ||||||
|  |     return decr; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int aud_to_fmodfmt (audfmt_e fmt, int stereo) | ||||||
|  | { | ||||||
|  |     int mode = FSOUND_LOOP_NORMAL; | ||||||
|  |  | ||||||
|  |     switch (fmt) { | ||||||
|  |     case AUD_FMT_S8: | ||||||
|  |         mode |= FSOUND_SIGNED | FSOUND_8BITS; | ||||||
|  |         break; | ||||||
|  |  | ||||||
|  |     case AUD_FMT_U8: | ||||||
|  |         mode |= FSOUND_UNSIGNED | FSOUND_8BITS; | ||||||
|  |         break; | ||||||
|  |  | ||||||
|  |     case AUD_FMT_S16: | ||||||
|  |         mode |= FSOUND_SIGNED | FSOUND_16BITS; | ||||||
|  |         break; | ||||||
|  |  | ||||||
|  |     case AUD_FMT_U16: | ||||||
|  |         mode |= FSOUND_UNSIGNED | FSOUND_16BITS; | ||||||
|  |         break; | ||||||
|  |  | ||||||
|  |     default: | ||||||
|  |         dolog ("Internal logic error: Bad audio format %d\n", fmt); | ||||||
|  | #ifdef DEBUG_FMOD | ||||||
|  |         abort (); | ||||||
|  | #endif | ||||||
|  |         mode |= FSOUND_8BITS; | ||||||
|  |     } | ||||||
|  |     mode |= stereo ? FSOUND_STEREO : FSOUND_MONO; | ||||||
|  |     return mode; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void fmod_fini_out (HWVoiceOut *hw) | ||||||
|  | { | ||||||
|  |     FMODVoiceOut *fmd = (FMODVoiceOut *) hw; | ||||||
|  |  | ||||||
|  |     if (fmd->fmod_sample) { | ||||||
|  |         FSOUND_Sample_Free (fmd->fmod_sample); | ||||||
|  |         fmd->fmod_sample = 0; | ||||||
|  |  | ||||||
|  |         if (fmd->channel >= 0) { | ||||||
|  |             FSOUND_StopSound (fmd->channel); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int fmod_init_out (HWVoiceOut *hw, audsettings_t *as) | ||||||
|  | { | ||||||
|  |     int bits16, mode, channel; | ||||||
|  |     FMODVoiceOut *fmd = (FMODVoiceOut *) hw; | ||||||
|  |     audsettings_t obt_as = *as; | ||||||
|  |  | ||||||
|  |     mode = aud_to_fmodfmt (as->fmt, as->nchannels == 2 ? 1 : 0); | ||||||
|  |     fmd->fmod_sample = FSOUND_Sample_Alloc ( | ||||||
|  |         FSOUND_FREE,            /* index */ | ||||||
|  |         conf.nb_samples,        /* length */ | ||||||
|  |         mode,                   /* mode */ | ||||||
|  |         as->freq,               /* freq */ | ||||||
|  |         255,                    /* volume */ | ||||||
|  |         128,                    /* pan */ | ||||||
|  |         255                     /* priority */ | ||||||
|  |         ); | ||||||
|  |  | ||||||
|  |     if (!fmd->fmod_sample) { | ||||||
|  |         fmod_logerr2 ("DAC", "Failed to allocate FMOD sample\n"); | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     channel = FSOUND_PlaySoundEx (FSOUND_FREE, fmd->fmod_sample, 0, 1); | ||||||
|  |     if (channel < 0) { | ||||||
|  |         fmod_logerr2 ("DAC", "Failed to start playing sound\n"); | ||||||
|  |         FSOUND_Sample_Free (fmd->fmod_sample); | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |     fmd->channel = channel; | ||||||
|  |  | ||||||
|  |     /* FMOD always operates on little endian frames? */ | ||||||
|  |     obt_as.endianness = 0; | ||||||
|  |     audio_pcm_init_info (&hw->info, &obt_as); | ||||||
|  |     bits16 = (mode & FSOUND_16BITS) != 0; | ||||||
|  |     hw->samples = conf.nb_samples; | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int fmod_ctl_out (HWVoiceOut *hw, int cmd, ...) | ||||||
|  | { | ||||||
|  |     int status; | ||||||
|  |     FMODVoiceOut *fmd = (FMODVoiceOut *) hw; | ||||||
|  |  | ||||||
|  |     switch (cmd) { | ||||||
|  |     case VOICE_ENABLE: | ||||||
|  |         fmod_clear_sample (fmd); | ||||||
|  |         status = FSOUND_SetPaused (fmd->channel, 0); | ||||||
|  |         if (!status) { | ||||||
|  |             fmod_logerr ("Failed to resume channel %d\n", fmd->channel); | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |  | ||||||
|  |     case VOICE_DISABLE: | ||||||
|  |         status = FSOUND_SetPaused (fmd->channel, 1); | ||||||
|  |         if (!status) { | ||||||
|  |             fmod_logerr ("Failed to pause channel %d\n", fmd->channel); | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int fmod_init_in (HWVoiceIn *hw, audsettings_t *as) | ||||||
|  | { | ||||||
|  |     int bits16, mode; | ||||||
|  |     FMODVoiceIn *fmd = (FMODVoiceIn *) hw; | ||||||
|  |     audsettings_t obt_as = *as; | ||||||
|  |  | ||||||
|  |     if (conf.broken_adc) { | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     mode = aud_to_fmodfmt (as->fmt, as->nchannels == 2 ? 1 : 0); | ||||||
|  |     fmd->fmod_sample = FSOUND_Sample_Alloc ( | ||||||
|  |         FSOUND_FREE,            /* index */ | ||||||
|  |         conf.nb_samples,        /* length */ | ||||||
|  |         mode,                   /* mode */ | ||||||
|  |         as->freq,               /* freq */ | ||||||
|  |         255,                    /* volume */ | ||||||
|  |         128,                    /* pan */ | ||||||
|  |         255                     /* priority */ | ||||||
|  |         ); | ||||||
|  |  | ||||||
|  |     if (!fmd->fmod_sample) { | ||||||
|  |         fmod_logerr2 ("ADC", "Failed to allocate FMOD sample\n"); | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /* FMOD always operates on little endian frames? */ | ||||||
|  |     obt_as.endianness = 0; | ||||||
|  |     audio_pcm_init_info (&hw->info, &obt_as); | ||||||
|  |     bits16 = (mode & FSOUND_16BITS) != 0; | ||||||
|  |     hw->samples = conf.nb_samples; | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void fmod_fini_in (HWVoiceIn *hw) | ||||||
|  | { | ||||||
|  |     FMODVoiceIn *fmd = (FMODVoiceIn *) hw; | ||||||
|  |  | ||||||
|  |     if (fmd->fmod_sample) { | ||||||
|  |         FSOUND_Record_Stop (); | ||||||
|  |         FSOUND_Sample_Free (fmd->fmod_sample); | ||||||
|  |         fmd->fmod_sample = 0; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int fmod_run_in (HWVoiceIn *hw) | ||||||
|  | { | ||||||
|  |     FMODVoiceIn *fmd = (FMODVoiceIn *) hw; | ||||||
|  |     int hwshift = hw->info.shift; | ||||||
|  |     int live, dead, new_pos, len; | ||||||
|  |     unsigned int blen1 = 0, blen2 = 0; | ||||||
|  |     unsigned int len1, len2; | ||||||
|  |     unsigned int decr; | ||||||
|  |     void *p1, *p2; | ||||||
|  |  | ||||||
|  |     live = audio_pcm_hw_get_live_in (hw); | ||||||
|  |     dead = hw->samples - live; | ||||||
|  |     if (!dead) { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     new_pos = FSOUND_Record_GetPosition (); | ||||||
|  |     if (new_pos < 0) { | ||||||
|  |         fmod_logerr ("Could not get recording position\n"); | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     len = audio_ring_dist (new_pos,  hw->wpos, hw->samples); | ||||||
|  |     if (!len) { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |     len = audio_MIN (len, dead); | ||||||
|  |  | ||||||
|  |     if (fmod_lock_sample (fmd->fmod_sample, &fmd->hw.info, | ||||||
|  |                           hw->wpos, len, | ||||||
|  |                           &p1, &p2, | ||||||
|  |                           &blen1, &blen2)) { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     len1 = blen1 >> hwshift; | ||||||
|  |     len2 = blen2 >> hwshift; | ||||||
|  |     decr = len1 + len2; | ||||||
|  |  | ||||||
|  |     if (p1 && blen1) { | ||||||
|  |         hw->conv (hw->conv_buf + hw->wpos, p1, len1, &nominal_volume); | ||||||
|  |     } | ||||||
|  |     if (p2 && len2) { | ||||||
|  |         hw->conv (hw->conv_buf, p2, len2, &nominal_volume); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     fmod_unlock_sample (fmd->fmod_sample, p1, p2, blen1, blen2); | ||||||
|  |     hw->wpos = (hw->wpos + decr) % hw->samples; | ||||||
|  |     return decr; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static struct { | ||||||
|  |     const char *name; | ||||||
|  |     int type; | ||||||
|  | } drvtab[] = { | ||||||
|  |     {"none", FSOUND_OUTPUT_NOSOUND}, | ||||||
|  | #ifdef _WIN32 | ||||||
|  |     {"winmm", FSOUND_OUTPUT_WINMM}, | ||||||
|  |     {"dsound", FSOUND_OUTPUT_DSOUND}, | ||||||
|  |     {"a3d", FSOUND_OUTPUT_A3D}, | ||||||
|  |     {"asio", FSOUND_OUTPUT_ASIO}, | ||||||
|  | #endif | ||||||
|  | #ifdef __linux__ | ||||||
|  |     {"oss", FSOUND_OUTPUT_OSS}, | ||||||
|  |     {"alsa", FSOUND_OUTPUT_ALSA}, | ||||||
|  |     {"esd", FSOUND_OUTPUT_ESD}, | ||||||
|  | #endif | ||||||
|  | #ifdef __APPLE__ | ||||||
|  |     {"mac", FSOUND_OUTPUT_MAC}, | ||||||
|  | #endif | ||||||
|  | #if 0 | ||||||
|  |     {"xbox", FSOUND_OUTPUT_XBOX}, | ||||||
|  |     {"ps2", FSOUND_OUTPUT_PS2}, | ||||||
|  |     {"gcube", FSOUND_OUTPUT_GC}, | ||||||
|  | #endif | ||||||
|  |     {"none-realtime", FSOUND_OUTPUT_NOSOUND_NONREALTIME} | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static void *fmod_audio_init (void) | ||||||
|  | { | ||||||
|  |     size_t i; | ||||||
|  |     double ver; | ||||||
|  |     int status; | ||||||
|  |     int output_type = -1; | ||||||
|  |     const char *drv = conf.drvname; | ||||||
|  |  | ||||||
|  |     ver = FSOUND_GetVersion (); | ||||||
|  |     if (ver < FMOD_VERSION) { | ||||||
|  |         dolog ("Wrong FMOD version %f, need at least %f\n", ver, FMOD_VERSION); | ||||||
|  |         return NULL; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | #ifdef __linux__ | ||||||
|  |     if (ver < 3.75) { | ||||||
|  |         dolog ("FMOD before 3.75 has bug preventing ADC from working\n" | ||||||
|  |                "ADC will be disabled.\n"); | ||||||
|  |         conf.broken_adc = 1; | ||||||
|  |     } | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |     if (drv) { | ||||||
|  |         int found = 0; | ||||||
|  |         for (i = 0; i < sizeof (drvtab) / sizeof (drvtab[0]); i++) { | ||||||
|  |             if (!strcmp (drv, drvtab[i].name)) { | ||||||
|  |                 output_type = drvtab[i].type; | ||||||
|  |                 found = 1; | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         if (!found) { | ||||||
|  |             dolog ("Unknown FMOD driver `%s'\n", drv); | ||||||
|  |             dolog ("Valid drivers:\n"); | ||||||
|  |             for (i = 0; i < sizeof (drvtab) / sizeof (drvtab[0]); i++) { | ||||||
|  |                 dolog ("  %s\n", drvtab[i].name); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (output_type != -1) { | ||||||
|  |         status = FSOUND_SetOutput (output_type); | ||||||
|  |         if (!status) { | ||||||
|  |             fmod_logerr ("FSOUND_SetOutput(%d) failed\n", output_type); | ||||||
|  |             return NULL; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (conf.bufsize) { | ||||||
|  |         status = FSOUND_SetBufferSize (conf.bufsize); | ||||||
|  |         if (!status) { | ||||||
|  |             fmod_logerr ("FSOUND_SetBufferSize (%d) failed\n", conf.bufsize); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     status = FSOUND_Init (conf.freq, conf.nb_channels, 0); | ||||||
|  |     if (!status) { | ||||||
|  |         fmod_logerr ("FSOUND_Init failed\n"); | ||||||
|  |         return NULL; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return &conf; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int fmod_read (SWVoiceIn *sw, void *buf, int size) | ||||||
|  | { | ||||||
|  |     return audio_pcm_sw_read (sw, buf, size); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int fmod_ctl_in (HWVoiceIn *hw, int cmd, ...) | ||||||
|  | { | ||||||
|  |     int status; | ||||||
|  |     FMODVoiceIn *fmd = (FMODVoiceIn *) hw; | ||||||
|  |  | ||||||
|  |     switch (cmd) { | ||||||
|  |     case VOICE_ENABLE: | ||||||
|  |         status = FSOUND_Record_StartSample (fmd->fmod_sample, 1); | ||||||
|  |         if (!status) { | ||||||
|  |             fmod_logerr ("Failed to start recording\n"); | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |  | ||||||
|  |     case VOICE_DISABLE: | ||||||
|  |         status = FSOUND_Record_Stop (); | ||||||
|  |         if (!status) { | ||||||
|  |             fmod_logerr ("Failed to stop recording\n"); | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void fmod_audio_fini (void *opaque) | ||||||
|  | { | ||||||
|  |     (void) opaque; | ||||||
|  |     FSOUND_Close (); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static struct audio_option fmod_options[] = { | ||||||
|  |     {"DRV", AUD_OPT_STR, &conf.drvname, | ||||||
|  |      "FMOD driver", NULL, 0}, | ||||||
|  |     {"FREQ", AUD_OPT_INT, &conf.freq, | ||||||
|  |      "Default frequency", NULL, 0}, | ||||||
|  |     {"SAMPLES", AUD_OPT_INT, &conf.nb_samples, | ||||||
|  |      "Buffer size in samples", NULL, 0}, | ||||||
|  |     {"CHANNELS", AUD_OPT_INT, &conf.nb_channels, | ||||||
|  |      "Number of default channels (1 - mono, 2 - stereo)", NULL, 0}, | ||||||
|  |     {"BUFSIZE", AUD_OPT_INT, &conf.bufsize, | ||||||
|  |      "(undocumented)", NULL, 0}, | ||||||
|  | #if 0 | ||||||
|  |     {"THRESHOLD", AUD_OPT_INT, &conf.threshold, | ||||||
|  |      "(undocumented)"}, | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |     {NULL, 0, NULL, NULL, NULL, 0} | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static struct audio_pcm_ops fmod_pcm_ops = { | ||||||
|  |     fmod_init_out, | ||||||
|  |     fmod_fini_out, | ||||||
|  |     fmod_run_out, | ||||||
|  |     fmod_write, | ||||||
|  |     fmod_ctl_out, | ||||||
|  |  | ||||||
|  |     fmod_init_in, | ||||||
|  |     fmod_fini_in, | ||||||
|  |     fmod_run_in, | ||||||
|  |     fmod_read, | ||||||
|  |     fmod_ctl_in | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | struct audio_driver fmod_audio_driver = { | ||||||
|  |     INIT_FIELD (name           = ) "fmod", | ||||||
|  |     INIT_FIELD (descr          = ) "FMOD 3.xx http://www.fmod.org", | ||||||
|  |     INIT_FIELD (options        = ) fmod_options, | ||||||
|  |     INIT_FIELD (init           = ) fmod_audio_init, | ||||||
|  |     INIT_FIELD (fini           = ) fmod_audio_fini, | ||||||
|  |     INIT_FIELD (pcm_ops        = ) &fmod_pcm_ops, | ||||||
|  |     INIT_FIELD (can_be_default = ) 1, | ||||||
|  |     INIT_FIELD (max_voices_out = ) INT_MAX, | ||||||
|  |     INIT_FIELD (max_voices_in  = ) INT_MAX, | ||||||
|  |     INIT_FIELD (voice_size_out = ) sizeof (FMODVoiceOut), | ||||||
|  |     INIT_FIELD (voice_size_in  = ) sizeof (FMODVoiceIn) | ||||||
|  | }; | ||||||
							
								
								
									
										105
									
								
								audio/mixeng.c
									
									
									
									
									
								
							
							
						
						
									
										105
									
								
								audio/mixeng.c
									
									
									
									
									
								
							| @@ -22,22 +22,20 @@ | |||||||
|  * 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-common.h" | #include "qemu-common.h" | ||||||
| #include "qemu/bswap.h" |  | ||||||
| #include "qemu/error-report.h" |  | ||||||
| #include "audio.h" | #include "audio.h" | ||||||
|  |  | ||||||
| #define AUDIO_CAP "mixeng" | #define AUDIO_CAP "mixeng" | ||||||
| #include "audio_int.h" | #include "audio_int.h" | ||||||
|  |  | ||||||
|  | #define NOVOL | ||||||
|  |  | ||||||
| /* 8 bit */ | /* 8 bit */ | ||||||
| #define ENDIAN_CONVERSION natural | #define ENDIAN_CONVERSION natural | ||||||
| #define ENDIAN_CONVERT(v) (v) | #define ENDIAN_CONVERT(v) (v) | ||||||
|  |  | ||||||
| /* Signed 8 bit */ | /* Signed 8 bit */ | ||||||
| #define BSIZE 8 | #define IN_T int8_t | ||||||
| #define ITYPE int |  | ||||||
| #define IN_MIN SCHAR_MIN | #define IN_MIN SCHAR_MIN | ||||||
| #define IN_MAX SCHAR_MAX | #define IN_MAX SCHAR_MAX | ||||||
| #define SIGNED | #define SIGNED | ||||||
| @@ -46,29 +44,25 @@ | |||||||
| #undef SIGNED | #undef SIGNED | ||||||
| #undef IN_MAX | #undef IN_MAX | ||||||
| #undef IN_MIN | #undef IN_MIN | ||||||
| #undef BSIZE | #undef IN_T | ||||||
| #undef ITYPE |  | ||||||
| #undef SHIFT | #undef SHIFT | ||||||
|  |  | ||||||
| /* Unsigned 8 bit */ | /* Unsigned 8 bit */ | ||||||
| #define BSIZE 8 | #define IN_T uint8_t | ||||||
| #define ITYPE uint |  | ||||||
| #define IN_MIN 0 | #define IN_MIN 0 | ||||||
| #define IN_MAX UCHAR_MAX | #define IN_MAX UCHAR_MAX | ||||||
| #define SHIFT 8 | #define SHIFT 8 | ||||||
| #include "mixeng_template.h" | #include "mixeng_template.h" | ||||||
| #undef IN_MAX | #undef IN_MAX | ||||||
| #undef IN_MIN | #undef IN_MIN | ||||||
| #undef BSIZE | #undef IN_T | ||||||
| #undef ITYPE |  | ||||||
| #undef SHIFT | #undef SHIFT | ||||||
|  |  | ||||||
| #undef ENDIAN_CONVERT | #undef ENDIAN_CONVERT | ||||||
| #undef ENDIAN_CONVERSION | #undef ENDIAN_CONVERSION | ||||||
|  |  | ||||||
| /* Signed 16 bit */ | /* Signed 16 bit */ | ||||||
| #define BSIZE 16 | #define IN_T int16_t | ||||||
| #define ITYPE int |  | ||||||
| #define IN_MIN SHRT_MIN | #define IN_MIN SHRT_MIN | ||||||
| #define IN_MAX SHRT_MAX | #define IN_MAX SHRT_MAX | ||||||
| #define SIGNED | #define SIGNED | ||||||
| @@ -86,13 +80,11 @@ | |||||||
| #undef SIGNED | #undef SIGNED | ||||||
| #undef IN_MAX | #undef IN_MAX | ||||||
| #undef IN_MIN | #undef IN_MIN | ||||||
| #undef BSIZE | #undef IN_T | ||||||
| #undef ITYPE |  | ||||||
| #undef SHIFT | #undef SHIFT | ||||||
|  |  | ||||||
| /* Unsigned 16 bit */ | /* Unsigned 16 bit */ | ||||||
| #define BSIZE 16 | #define IN_T uint16_t | ||||||
| #define ITYPE uint |  | ||||||
| #define IN_MIN 0 | #define IN_MIN 0 | ||||||
| #define IN_MAX USHRT_MAX | #define IN_MAX USHRT_MAX | ||||||
| #define SHIFT 16 | #define SHIFT 16 | ||||||
| @@ -108,13 +100,11 @@ | |||||||
| #undef ENDIAN_CONVERSION | #undef ENDIAN_CONVERSION | ||||||
| #undef IN_MAX | #undef IN_MAX | ||||||
| #undef IN_MIN | #undef IN_MIN | ||||||
| #undef BSIZE | #undef IN_T | ||||||
| #undef ITYPE |  | ||||||
| #undef SHIFT | #undef SHIFT | ||||||
|  |  | ||||||
| /* Signed 32 bit */ | /* Signed 32 bit */ | ||||||
| #define BSIZE 32 | #define IN_T int32_t | ||||||
| #define ITYPE int |  | ||||||
| #define IN_MIN INT32_MIN | #define IN_MIN INT32_MIN | ||||||
| #define IN_MAX INT32_MAX | #define IN_MAX INT32_MAX | ||||||
| #define SIGNED | #define SIGNED | ||||||
| @@ -132,13 +122,11 @@ | |||||||
| #undef SIGNED | #undef SIGNED | ||||||
| #undef IN_MAX | #undef IN_MAX | ||||||
| #undef IN_MIN | #undef IN_MIN | ||||||
| #undef BSIZE | #undef IN_T | ||||||
| #undef ITYPE |  | ||||||
| #undef SHIFT | #undef SHIFT | ||||||
|  |  | ||||||
| /* Unsigned 32 bit */ | /* Unsigned 16 bit */ | ||||||
| #define BSIZE 32 | #define IN_T uint32_t | ||||||
| #define ITYPE uint |  | ||||||
| #define IN_MIN 0 | #define IN_MIN 0 | ||||||
| #define IN_MAX UINT32_MAX | #define IN_MAX UINT32_MAX | ||||||
| #define SHIFT 32 | #define SHIFT 32 | ||||||
| @@ -154,8 +142,7 @@ | |||||||
| #undef ENDIAN_CONVERSION | #undef ENDIAN_CONVERSION | ||||||
| #undef IN_MAX | #undef IN_MAX | ||||||
| #undef IN_MIN | #undef IN_MIN | ||||||
| #undef BSIZE | #undef IN_T | ||||||
| #undef ITYPE |  | ||||||
| #undef SHIFT | #undef SHIFT | ||||||
|  |  | ||||||
| t_sample *mixeng_conv[2][2][2][3] = { | t_sample *mixeng_conv[2][2][2][3] = { | ||||||
| @@ -268,42 +255,11 @@ f_sample *mixeng_clip[2][2][2][3] = { | |||||||
|     } |     } | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  |  | ||||||
| void audio_sample_to_uint64(void *samples, int pos, |  | ||||||
|                             uint64_t *left, uint64_t *right) |  | ||||||
| { |  | ||||||
|     struct st_sample *sample = samples; |  | ||||||
|     sample += pos; |  | ||||||
| #ifdef FLOAT_MIXENG |  | ||||||
|     error_report( |  | ||||||
|         "Coreaudio and floating point samples are not supported by replay yet"); |  | ||||||
|     abort(); |  | ||||||
| #else |  | ||||||
|     *left = sample->l; |  | ||||||
|     *right = sample->r; |  | ||||||
| #endif |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void audio_sample_from_uint64(void *samples, int pos, |  | ||||||
|                             uint64_t left, uint64_t right) |  | ||||||
| { |  | ||||||
|     struct st_sample *sample = samples; |  | ||||||
|     sample += pos; |  | ||||||
| #ifdef FLOAT_MIXENG |  | ||||||
|     error_report( |  | ||||||
|         "Coreaudio and floating point samples are not supported by replay yet"); |  | ||||||
|     abort(); |  | ||||||
| #else |  | ||||||
|     sample->l = left; |  | ||||||
|     sample->r = right; |  | ||||||
| #endif |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* | /* | ||||||
|  * August 21, 1998 |  * August 21, 1998 | ||||||
|  * Copyright 1998 Fabrice Bellard. |  * Copyright 1998 Fabrice Bellard. | ||||||
|  * |  * | ||||||
|  * [Rewrote completely the code of Lance Norskog And Sundry |  * [Rewrote completly the code of Lance Norskog And Sundry | ||||||
|  * Contributors with a more efficient algorithm.] |  * Contributors with a more efficient algorithm.] | ||||||
|  * |  * | ||||||
|  * This source code is freely redistributable and may be used for |  * This source code is freely redistributable and may be used for | ||||||
| @@ -336,7 +292,7 @@ struct rate { | |||||||
|     uint64_t opos; |     uint64_t opos; | ||||||
|     uint64_t opos_inc; |     uint64_t opos_inc; | ||||||
|     uint32_t ipos;              /* position in the input stream (integer) */ |     uint32_t ipos;              /* position in the input stream (integer) */ | ||||||
|     struct st_sample ilast;          /* last sample in the input stream */ |     st_sample_t ilast;          /* last sample in the input stream */ | ||||||
| }; | }; | ||||||
|  |  | ||||||
| /* | /* | ||||||
| @@ -344,7 +300,7 @@ struct rate { | |||||||
|  */ |  */ | ||||||
| void *st_rate_start (int inrate, int outrate) | void *st_rate_start (int inrate, int outrate) | ||||||
| { | { | ||||||
|     struct rate *rate = audio_calloc(__func__, 1, sizeof(*rate)); |     struct rate *rate = audio_calloc (AUDIO_FUNC, 1, sizeof (*rate)); | ||||||
|  |  | ||||||
|     if (!rate) { |     if (!rate) { | ||||||
|         dolog ("Could not allocate resampler (%zu bytes)\n", sizeof (*rate)); |         dolog ("Could not allocate resampler (%zu bytes)\n", sizeof (*rate)); | ||||||
| @@ -372,29 +328,10 @@ void *st_rate_start (int inrate, int outrate) | |||||||
|  |  | ||||||
| void st_rate_stop (void *opaque) | void st_rate_stop (void *opaque) | ||||||
| { | { | ||||||
|     g_free (opaque); |     qemu_free (opaque); | ||||||
| } | } | ||||||
|  |  | ||||||
| void mixeng_clear (struct st_sample *buf, int len) | void mixeng_clear (st_sample_t *buf, int len) | ||||||
| { | { | ||||||
|     memset (buf, 0, len * sizeof (struct st_sample)); |     memset (buf, 0, len * sizeof (st_sample_t)); | ||||||
| } |  | ||||||
|  |  | ||||||
| void mixeng_volume (struct st_sample *buf, int len, struct mixeng_volume *vol) |  | ||||||
| { |  | ||||||
|     if (vol->mute) { |  | ||||||
|         mixeng_clear (buf, len); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     while (len--) { |  | ||||||
| #ifdef FLOAT_MIXENG |  | ||||||
|         buf->l = buf->l * vol->l; |  | ||||||
|         buf->r = buf->r * vol->r; |  | ||||||
| #else |  | ||||||
|         buf->l = (buf->l * vol->l) >> 32; |  | ||||||
|         buf->r = (buf->r * vol->r) >> 32; |  | ||||||
| #endif |  | ||||||
|         buf += 1; |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -21,32 +21,31 @@ | |||||||
|  * 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. | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #ifndef QEMU_MIXENG_H | #ifndef QEMU_MIXENG_H | ||||||
| #define QEMU_MIXENG_H | #define QEMU_MIXENG_H | ||||||
|  |  | ||||||
| #ifdef FLOAT_MIXENG | #ifdef FLOAT_MIXENG | ||||||
| typedef float mixeng_real; | typedef float real_t; | ||||||
| struct mixeng_volume { int mute; mixeng_real r; mixeng_real l; }; | typedef struct { int mute; real_t r; real_t l; } volume_t; | ||||||
| struct st_sample { mixeng_real l; mixeng_real r; }; | typedef struct { real_t l; real_t r; } st_sample_t; | ||||||
| #else | #else | ||||||
| struct mixeng_volume { int mute; int64_t r; int64_t l; }; | typedef struct { int mute; int64_t r; int64_t l; } volume_t; | ||||||
| struct st_sample { int64_t l; int64_t r; }; | typedef struct { int64_t l; int64_t r; } st_sample_t; | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| typedef void (t_sample) (struct st_sample *dst, const void *src, int samples); | typedef void (t_sample) (st_sample_t *dst, const void *src, | ||||||
| typedef void (f_sample) (void *dst, const struct st_sample *src, int samples); |                          int samples, volume_t *vol); | ||||||
|  | typedef void (f_sample) (void *dst, const st_sample_t *src, int samples); | ||||||
|  |  | ||||||
| extern t_sample *mixeng_conv[2][2][2][3]; | extern t_sample *mixeng_conv[2][2][2][3]; | ||||||
| extern f_sample *mixeng_clip[2][2][2][3]; | extern f_sample *mixeng_clip[2][2][2][3]; | ||||||
|  |  | ||||||
| void *st_rate_start (int inrate, int outrate); | void *st_rate_start (int inrate, int outrate); | ||||||
| void st_rate_flow (void *opaque, struct st_sample *ibuf, struct st_sample *obuf, | void st_rate_flow (void *opaque, st_sample_t *ibuf, st_sample_t *obuf, | ||||||
|                    int *isamp, int *osamp); |                    int *isamp, int *osamp); | ||||||
| void st_rate_flow_mix (void *opaque, struct st_sample *ibuf, struct st_sample *obuf, | void st_rate_flow_mix (void *opaque, st_sample_t *ibuf, st_sample_t *obuf, | ||||||
|                        int *isamp, int *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 (st_sample_t *buf, int len); | ||||||
| void mixeng_volume (struct st_sample *buf, int len, struct mixeng_volume *vol); |  | ||||||
|  |  | ||||||
| #endif /* QEMU_MIXENG_H */ | #endif  /* mixeng.h */ | ||||||
|   | |||||||
| @@ -31,30 +31,39 @@ | |||||||
| #define HALF (IN_MAX >> 1) | #define HALF (IN_MAX >> 1) | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #define ET glue (ENDIAN_CONVERSION, glue (glue (glue (_, ITYPE), BSIZE), _t)) | #ifdef NOVOL | ||||||
| #define IN_T glue (glue (ITYPE, BSIZE), _t) | #define VOL(a, b) a | ||||||
|  | #else | ||||||
|  | #ifdef FLOAT_MIXENG | ||||||
|  | #define VOL(a, b) ((a) * (b)) | ||||||
|  | #else | ||||||
|  | #define VOL(a, b) ((a) * (b)) >> 32 | ||||||
|  | #endif | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #define ET glue (ENDIAN_CONVERSION, glue (_, IN_T)) | ||||||
|  |  | ||||||
| #ifdef FLOAT_MIXENG | #ifdef FLOAT_MIXENG | ||||||
| static inline mixeng_real glue (conv_, ET) (IN_T v) | static real_t inline glue (conv_, ET) (IN_T v) | ||||||
| { | { | ||||||
|     IN_T nv = ENDIAN_CONVERT (v); |     IN_T nv = ENDIAN_CONVERT (v); | ||||||
|  |  | ||||||
| #ifdef RECIPROCAL | #ifdef RECIPROCAL | ||||||
| #ifdef SIGNED | #ifdef SIGNED | ||||||
|     return nv * (1.f / (mixeng_real) (IN_MAX - IN_MIN)); |     return nv * (1.f / (real_t) (IN_MAX - IN_MIN)); | ||||||
| #else | #else | ||||||
|     return (nv - HALF) * (1.f / (mixeng_real) IN_MAX); |     return (nv - HALF) * (1.f / (real_t) IN_MAX); | ||||||
| #endif | #endif | ||||||
| #else  /* !RECIPROCAL */ | #else  /* !RECIPROCAL */ | ||||||
| #ifdef SIGNED | #ifdef SIGNED | ||||||
|     return nv / (mixeng_real) ((mixeng_real) IN_MAX - IN_MIN); |     return nv / (real_t) (IN_MAX - IN_MIN); | ||||||
| #else | #else | ||||||
|     return (nv - HALF) / (mixeng_real) IN_MAX; |     return (nv - HALF) / (real_t) IN_MAX; | ||||||
| #endif | #endif | ||||||
| #endif | #endif | ||||||
| } | } | ||||||
|  |  | ||||||
| static inline IN_T glue (clip_, ET) (mixeng_real v) | static IN_T inline glue (clip_, ET) (real_t v) | ||||||
| { | { | ||||||
|     if (v >= 0.5) { |     if (v >= 0.5) { | ||||||
|         return IN_MAX; |         return IN_MAX; | ||||||
| @@ -64,7 +73,7 @@ static inline IN_T glue (clip_, ET) (mixeng_real v) | |||||||
|     } |     } | ||||||
|  |  | ||||||
| #ifdef SIGNED | #ifdef SIGNED | ||||||
|     return ENDIAN_CONVERT ((IN_T) (v * ((mixeng_real) IN_MAX - IN_MIN))); |     return ENDIAN_CONVERT ((IN_T) (v * (IN_MAX - IN_MIN))); | ||||||
| #else | #else | ||||||
|     return ENDIAN_CONVERT ((IN_T) ((v * IN_MAX) + HALF)); |     return ENDIAN_CONVERT ((IN_T) ((v * IN_MAX) + HALF)); | ||||||
| #endif | #endif | ||||||
| @@ -100,26 +109,40 @@ static inline IN_T glue (clip_, ET) (int64_t v) | |||||||
| #endif | #endif | ||||||
|  |  | ||||||
| static void glue (glue (conv_, ET), _to_stereo) | static void glue (glue (conv_, ET), _to_stereo) | ||||||
|     (struct st_sample *dst, const void *src, int samples) |     (st_sample_t *dst, const void *src, int samples, volume_t *vol) | ||||||
| { | { | ||||||
|     struct st_sample *out = dst; |     st_sample_t *out = dst; | ||||||
|     IN_T *in = (IN_T *) src; |     IN_T *in = (IN_T *) src; | ||||||
|  | #ifndef NOVOL | ||||||
|  |     if (vol->mute) { | ||||||
|  |         mixeng_clear (dst, samples); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | #else | ||||||
|  |     (void) vol; | ||||||
|  | #endif | ||||||
|     while (samples--) { |     while (samples--) { | ||||||
|         out->l = glue (conv_, ET) (*in++); |         out->l = VOL (glue (conv_, ET) (*in++), vol->l); | ||||||
|         out->r = glue (conv_, ET) (*in++); |         out->r = VOL (glue (conv_, ET) (*in++), vol->r); | ||||||
|         out += 1; |         out += 1; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| static void glue (glue (conv_, ET), _to_mono) | static void glue (glue (conv_, ET), _to_mono) | ||||||
|     (struct st_sample *dst, const void *src, int samples) |     (st_sample_t *dst, const void *src, int samples, volume_t *vol) | ||||||
| { | { | ||||||
|     struct st_sample *out = dst; |     st_sample_t *out = dst; | ||||||
|     IN_T *in = (IN_T *) src; |     IN_T *in = (IN_T *) src; | ||||||
|  | #ifndef NOVOL | ||||||
|  |     if (vol->mute) { | ||||||
|  |         mixeng_clear (dst, samples); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | #else | ||||||
|  |     (void) vol; | ||||||
|  | #endif | ||||||
|     while (samples--) { |     while (samples--) { | ||||||
|         out->l = glue (conv_, ET) (in[0]); |         out->l = VOL (glue (conv_, ET) (in[0]), vol->l); | ||||||
|         out->r = out->l; |         out->r = out->l; | ||||||
|         out += 1; |         out += 1; | ||||||
|         in += 1; |         in += 1; | ||||||
| @@ -127,9 +150,9 @@ static void glue (glue (conv_, ET), _to_mono) | |||||||
| } | } | ||||||
|  |  | ||||||
| static void glue (glue (clip_, ET), _from_stereo) | static void glue (glue (clip_, ET), _from_stereo) | ||||||
|     (void *dst, const struct st_sample *src, int samples) |     (void *dst, const st_sample_t *src, int samples) | ||||||
| { | { | ||||||
|     const struct st_sample *in = src; |     const st_sample_t *in = src; | ||||||
|     IN_T *out = (IN_T *) dst; |     IN_T *out = (IN_T *) dst; | ||||||
|     while (samples--) { |     while (samples--) { | ||||||
|         *out++ = glue (clip_, ET) (in->l); |         *out++ = glue (clip_, ET) (in->l); | ||||||
| @@ -139,9 +162,9 @@ static void glue (glue (clip_, ET), _from_stereo) | |||||||
| } | } | ||||||
|  |  | ||||||
| static void glue (glue (clip_, ET), _from_mono) | static void glue (glue (clip_, ET), _from_mono) | ||||||
|     (void *dst, const struct st_sample *src, int samples) |     (void *dst, const st_sample_t *src, int samples) | ||||||
| { | { | ||||||
|     const struct st_sample *in = src; |     const st_sample_t *in = src; | ||||||
|     IN_T *out = (IN_T *) dst; |     IN_T *out = (IN_T *) dst; | ||||||
|     while (samples--) { |     while (samples--) { | ||||||
|         *out++ = glue (clip_, ET) (in->l + in->r); |         *out++ = glue (clip_, ET) (in->l + in->r); | ||||||
| @@ -151,4 +174,4 @@ static void glue (glue (clip_, ET), _from_mono) | |||||||
|  |  | ||||||
| #undef ET | #undef ET | ||||||
| #undef HALF | #undef HALF | ||||||
| #undef IN_T | #undef VOL | ||||||
|   | |||||||
| @@ -21,11 +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-common.h" | #include "qemu-common.h" | ||||||
| #include "qemu/host-utils.h" |  | ||||||
| #include "audio.h" | #include "audio.h" | ||||||
| #include "qemu/timer.h" | #include "qemu-timer.h" | ||||||
|  |  | ||||||
| #define AUDIO_CAP "noaudio" | #define AUDIO_CAP "noaudio" | ||||||
| #include "audio_int.h" | #include "audio_int.h" | ||||||
| @@ -40,17 +38,22 @@ typedef struct NoVoiceIn { | |||||||
|     int64_t old_ticks; |     int64_t old_ticks; | ||||||
| } NoVoiceIn; | } NoVoiceIn; | ||||||
|  |  | ||||||
| static int no_run_out (HWVoiceOut *hw, int live) | static int no_run_out (HWVoiceOut *hw) | ||||||
| { | { | ||||||
|     NoVoiceOut *no = (NoVoiceOut *) hw; |     NoVoiceOut *no = (NoVoiceOut *) hw; | ||||||
|     int decr, samples; |     int live, decr, samples; | ||||||
|     int64_t now; |     int64_t now; | ||||||
|     int64_t ticks; |     int64_t ticks; | ||||||
|     int64_t bytes; |     int64_t bytes; | ||||||
|  |  | ||||||
|     now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); |     live = audio_pcm_hw_get_live_out (&no->hw); | ||||||
|  |     if (!live) { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     now = qemu_get_clock (vm_clock); | ||||||
|     ticks = now - no->old_ticks; |     ticks = now - no->old_ticks; | ||||||
|     bytes = muldiv64(ticks, hw->info.bytes_per_second, NANOSECONDS_PER_SECOND); |     bytes = (ticks * hw->info.bytes_per_second) / ticks_per_sec; | ||||||
|     bytes = audio_MIN (bytes, INT_MAX); |     bytes = audio_MIN (bytes, INT_MAX); | ||||||
|     samples = bytes >> hw->info.shift; |     samples = bytes >> hw->info.shift; | ||||||
|  |  | ||||||
| @@ -65,7 +68,7 @@ static int no_write (SWVoiceOut *sw, void *buf, int len) | |||||||
|     return audio_pcm_sw_write (sw, buf, 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, audsettings_t *as) | ||||||
| { | { | ||||||
|     audio_pcm_init_info (&hw->info, as); |     audio_pcm_init_info (&hw->info, as); | ||||||
|     hw->samples = 1024; |     hw->samples = 1024; | ||||||
| @@ -84,7 +87,7 @@ static int no_ctl_out (HWVoiceOut *hw, int cmd, ...) | |||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| static int no_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) | static int no_init_in (HWVoiceIn *hw, audsettings_t *as) | ||||||
| { | { | ||||||
|     audio_pcm_init_info (&hw->info, as); |     audio_pcm_init_info (&hw->info, as); | ||||||
|     hw->samples = 1024; |     hw->samples = 1024; | ||||||
| @@ -104,10 +107,9 @@ static int no_run_in (HWVoiceIn *hw) | |||||||
|     int samples = 0; |     int samples = 0; | ||||||
|  |  | ||||||
|     if (dead) { |     if (dead) { | ||||||
|         int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); |         int64_t now = qemu_get_clock (vm_clock); | ||||||
|         int64_t ticks = now - no->old_ticks; |         int64_t ticks = now - no->old_ticks; | ||||||
|         int64_t bytes = |         int64_t bytes = (ticks * hw->info.bytes_per_second) / ticks_per_sec; | ||||||
|             muldiv64(ticks, hw->info.bytes_per_second, NANOSECONDS_PER_SECOND); |  | ||||||
|  |  | ||||||
|         no->old_ticks = now; |         no->old_ticks = now; | ||||||
|         bytes = audio_MIN (bytes, INT_MAX); |         bytes = audio_MIN (bytes, INT_MAX); | ||||||
| @@ -119,14 +121,11 @@ static int no_run_in (HWVoiceIn *hw) | |||||||
|  |  | ||||||
| static int no_read (SWVoiceIn *sw, void *buf, int size) | 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 samples = size >> sw->info.shift; | ||||||
|     int total = sw->hw->total_samples_captured - sw->total_hw_samples_acquired; |     int total = sw->hw->total_samples_captured - sw->total_hw_samples_acquired; | ||||||
|     int to_clear = audio_MIN (samples, total); |     int to_clear = audio_MIN (samples, total); | ||||||
|     sw->total_hw_samples_acquired += total; |  | ||||||
|     audio_pcm_info_clear_buf (&sw->info, buf, to_clear); |     audio_pcm_info_clear_buf (&sw->info, buf, to_clear); | ||||||
|     return to_clear << sw->info.shift; |     return to_clear; | ||||||
| } | } | ||||||
|  |  | ||||||
| static int no_ctl_in (HWVoiceIn *hw, int cmd, ...) | static int no_ctl_in (HWVoiceIn *hw, int cmd, ...) | ||||||
| @@ -147,35 +146,29 @@ 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, |     no_init_out, | ||||||
|     .fini_out = no_fini_out, |     no_fini_out, | ||||||
|     .run_out  = no_run_out, |     no_run_out, | ||||||
|     .write    = no_write, |     no_write, | ||||||
|     .ctl_out  = no_ctl_out, |     no_ctl_out, | ||||||
|  |  | ||||||
|     .init_in  = no_init_in, |     no_init_in, | ||||||
|     .fini_in  = no_fini_in, |     no_fini_in, | ||||||
|     .run_in   = no_run_in, |     no_run_in, | ||||||
|     .read     = no_read, |     no_read, | ||||||
|     .ctl_in   = no_ctl_in |     no_ctl_in | ||||||
| }; | }; | ||||||
|  |  | ||||||
| static struct audio_driver no_audio_driver = { | struct audio_driver no_audio_driver = { | ||||||
|     .name           = "none", |     INIT_FIELD (name           = ) "none", | ||||||
|     .descr          = "Timer based audio emulation", |     INIT_FIELD (descr          = ) "Timer based audio emulation", | ||||||
|     .options        = NULL, |     INIT_FIELD (options        = ) NULL, | ||||||
|     .init           = no_audio_init, |     INIT_FIELD (init           = ) no_audio_init, | ||||||
|     .fini           = no_audio_fini, |     INIT_FIELD (fini           = ) no_audio_fini, | ||||||
|     .pcm_ops        = &no_pcm_ops, |     INIT_FIELD (pcm_ops        = ) &no_pcm_ops, | ||||||
|     .can_be_default = 1, |     INIT_FIELD (can_be_default = ) 1, | ||||||
|     .max_voices_out = INT_MAX, |     INIT_FIELD (max_voices_out = ) INT_MAX, | ||||||
|     .max_voices_in  = INT_MAX, |     INIT_FIELD (max_voices_in  = ) INT_MAX, | ||||||
|     .voice_size_out = sizeof (NoVoiceOut), |     INIT_FIELD (voice_size_out = ) sizeof (NoVoiceOut), | ||||||
|     .voice_size_in  = sizeof (NoVoiceIn) |     INIT_FIELD (voice_size_in  = ) sizeof (NoVoiceIn) | ||||||
| }; | }; | ||||||
|  |  | ||||||
| static void register_audio_none(void) |  | ||||||
| { |  | ||||||
|     audio_driver_register(&no_audio_driver); |  | ||||||
| } |  | ||||||
| type_init(register_audio_none); |  | ||||||
|   | |||||||
							
								
								
									
										537
									
								
								audio/ossaudio.c
									
									
									
									
									
								
							
							
						
						
									
										537
									
								
								audio/ossaudio.c
									
									
									
									
									
								
							| @@ -21,42 +21,29 @@ | |||||||
|  * 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 <stdlib.h> | ||||||
|  | #include <sys/mman.h> | ||||||
|  | #include <sys/types.h> | ||||||
| #include <sys/ioctl.h> | #include <sys/ioctl.h> | ||||||
|  | #ifdef __OpenBSD__ | ||||||
|  | #include <soundcard.h> | ||||||
|  | #else | ||||||
| #include <sys/soundcard.h> | #include <sys/soundcard.h> | ||||||
|  | #endif | ||||||
| #include "qemu-common.h" | #include "qemu-common.h" | ||||||
| #include "qemu/main-loop.h" |  | ||||||
| #include "qemu/host-utils.h" |  | ||||||
| #include "audio.h" | #include "audio.h" | ||||||
| #include "trace.h" |  | ||||||
|  |  | ||||||
| #define AUDIO_CAP "oss" | #define AUDIO_CAP "oss" | ||||||
| #include "audio_int.h" | #include "audio_int.h" | ||||||
|  |  | ||||||
| #if defined OSS_GETVERSION && defined SNDCTL_DSP_POLICY |  | ||||||
| #define USE_DSP_POLICY |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| typedef struct OSSConf { |  | ||||||
|     int try_mmap; |  | ||||||
|     int nfrags; |  | ||||||
|     int fragsize; |  | ||||||
|     const char *devpath_out; |  | ||||||
|     const char *devpath_in; |  | ||||||
|     int exclusive; |  | ||||||
|     int policy; |  | ||||||
| } OSSConf; |  | ||||||
|  |  | ||||||
| typedef struct OSSVoiceOut { | typedef struct OSSVoiceOut { | ||||||
|     HWVoiceOut hw; |     HWVoiceOut hw; | ||||||
|     void *pcm_buf; |     void *pcm_buf; | ||||||
|     int fd; |     int fd; | ||||||
|     int wpos; |  | ||||||
|     int nfrags; |     int nfrags; | ||||||
|     int fragsize; |     int fragsize; | ||||||
|     int mmapped; |     int mmapped; | ||||||
|     int pending; |     int old_optr; | ||||||
|     OSSConf *conf; |  | ||||||
| } OSSVoiceOut; | } OSSVoiceOut; | ||||||
|  |  | ||||||
| typedef struct OSSVoiceIn { | typedef struct OSSVoiceIn { | ||||||
| @@ -65,9 +52,25 @@ typedef struct OSSVoiceIn { | |||||||
|     int fd; |     int fd; | ||||||
|     int nfrags; |     int nfrags; | ||||||
|     int fragsize; |     int fragsize; | ||||||
|     OSSConf *conf; |     int old_optr; | ||||||
| } OSSVoiceIn; | } OSSVoiceIn; | ||||||
|  |  | ||||||
|  | static struct { | ||||||
|  |     int try_mmap; | ||||||
|  |     int nfrags; | ||||||
|  |     int fragsize; | ||||||
|  |     const char *devpath_out; | ||||||
|  |     const char *devpath_in; | ||||||
|  |     int debug; | ||||||
|  | } conf = { | ||||||
|  |     .try_mmap = 0, | ||||||
|  |     .nfrags = 4, | ||||||
|  |     .fragsize = 4096, | ||||||
|  |     .devpath_out = "/dev/dsp", | ||||||
|  |     .devpath_in = "/dev/dsp", | ||||||
|  |     .debug = 0 | ||||||
|  | }; | ||||||
|  |  | ||||||
| struct oss_params { | struct oss_params { | ||||||
|     int freq; |     int freq; | ||||||
|     audfmt_e fmt; |     audfmt_e fmt; | ||||||
| @@ -107,48 +110,19 @@ static void GCC_FMT_ATTR (3, 4) oss_logerr2 ( | |||||||
|  |  | ||||||
| static void oss_anal_close (int *fdp) | static void oss_anal_close (int *fdp) | ||||||
| { | { | ||||||
|     int err; |     int err = close (*fdp); | ||||||
|  |  | ||||||
|     qemu_set_fd_handler (*fdp, NULL, NULL, NULL); |  | ||||||
|     err = close (*fdp); |  | ||||||
|     if (err) { |     if (err) { | ||||||
|         oss_logerr (errno, "Failed to close file(fd=%d)\n", *fdp); |         oss_logerr (errno, "Failed to close file(fd=%d)\n", *fdp); | ||||||
|     } |     } | ||||||
|     *fdp = -1; |     *fdp = -1; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void oss_helper_poll_out (void *opaque) |  | ||||||
| { |  | ||||||
|     (void) opaque; |  | ||||||
|     audio_run ("oss_poll_out"); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void oss_helper_poll_in (void *opaque) |  | ||||||
| { |  | ||||||
|     (void) opaque; |  | ||||||
|     audio_run ("oss_poll_in"); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void oss_poll_out (HWVoiceOut *hw) |  | ||||||
| { |  | ||||||
|     OSSVoiceOut *oss = (OSSVoiceOut *) hw; |  | ||||||
|  |  | ||||||
|     qemu_set_fd_handler (oss->fd, NULL, oss_helper_poll_out, NULL); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void oss_poll_in (HWVoiceIn *hw) |  | ||||||
| { |  | ||||||
|     OSSVoiceIn *oss = (OSSVoiceIn *) hw; |  | ||||||
|  |  | ||||||
|     qemu_set_fd_handler (oss->fd, oss_helper_poll_in, NULL, NULL); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int oss_write (SWVoiceOut *sw, void *buf, int len) | static int oss_write (SWVoiceOut *sw, void *buf, int len) | ||||||
| { | { | ||||||
|     return audio_pcm_sw_write (sw, buf, len); |     return audio_pcm_sw_write (sw, buf, len); | ||||||
| } | } | ||||||
|  |  | ||||||
| static int aud_to_ossfmt (audfmt_e fmt, int endianness) | static int aud_to_ossfmt (audfmt_e fmt) | ||||||
| { | { | ||||||
|     switch (fmt) { |     switch (fmt) { | ||||||
|     case AUD_FMT_S8: |     case AUD_FMT_S8: | ||||||
| @@ -158,20 +132,10 @@ static int aud_to_ossfmt (audfmt_e fmt, int endianness) | |||||||
|         return AFMT_U8; |         return AFMT_U8; | ||||||
|  |  | ||||||
|     case AUD_FMT_S16: |     case AUD_FMT_S16: | ||||||
|         if (endianness) { |  | ||||||
|             return AFMT_S16_BE; |  | ||||||
|         } |  | ||||||
|         else { |  | ||||||
|         return AFMT_S16_LE; |         return AFMT_S16_LE; | ||||||
|         } |  | ||||||
|  |  | ||||||
|     case AUD_FMT_U16: |     case AUD_FMT_U16: | ||||||
|         if (endianness) { |  | ||||||
|             return AFMT_U16_BE; |  | ||||||
|         } |  | ||||||
|         else { |  | ||||||
|         return AFMT_U16_LE; |         return AFMT_U16_LE; | ||||||
|         } |  | ||||||
|  |  | ||||||
|     default: |     default: | ||||||
|         dolog ("Internal logic error: Bad audio format %d\n", fmt); |         dolog ("Internal logic error: Bad audio format %d\n", fmt); | ||||||
| @@ -237,46 +201,17 @@ static void oss_dump_info (struct oss_params *req, struct oss_params *obt) | |||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #ifdef USE_DSP_POLICY |  | ||||||
| static int oss_get_version (int fd, int *version, const char *typ) |  | ||||||
| { |  | ||||||
|     if (ioctl (fd, OSS_GETVERSION, &version)) { |  | ||||||
| #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) |  | ||||||
|         /* |  | ||||||
|          * Looks like atm (20100109) FreeBSD knows OSS_GETVERSION |  | ||||||
|          * since 7.x, but currently only on the mixer device (or in |  | ||||||
|          * the Linuxolator), and in the native version that part of |  | ||||||
|          * the code is in fact never reached so the ioctl fails anyway. |  | ||||||
|          * Until this is fixed, just check the errno and if its what |  | ||||||
|          * FreeBSD's sound drivers return atm assume they are new enough. |  | ||||||
|          */ |  | ||||||
|         if (errno == EINVAL) { |  | ||||||
|             *version = 0x040000; |  | ||||||
|             return 0; |  | ||||||
|         } |  | ||||||
| #endif |  | ||||||
|         oss_logerr2 (errno, typ, "Failed to get OSS version\n"); |  | ||||||
|         return -1; |  | ||||||
|     } |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| static int oss_open (int in, struct oss_params *req, | static int oss_open (int in, struct oss_params *req, | ||||||
|                      struct oss_params *obt, int *pfd, OSSConf* conf) |                      struct oss_params *obt, int *pfd) | ||||||
| { | { | ||||||
|     int fd; |     int fd; | ||||||
|     int oflags = conf->exclusive ? O_EXCL : 0; |     int mmmmssss; | ||||||
|     audio_buf_info abinfo; |     audio_buf_info abinfo; | ||||||
|     int fmt, freq, nchannels; |     int fmt, freq, nchannels; | ||||||
|     int setfragment = 1; |     const char *dspname = in ? conf.devpath_in : conf.devpath_out; | ||||||
|     const char *dspname = in ? conf->devpath_in : conf->devpath_out; |  | ||||||
|     const char *typ = in ? "ADC" : "DAC"; |     const char *typ = in ? "ADC" : "DAC"; | ||||||
|  |  | ||||||
|     /* Kludge needed to have working mmap on Linux */ |     fd = open (dspname, (in ? O_RDONLY : O_WRONLY) | O_NONBLOCK); | ||||||
|     oflags |= conf->try_mmap ? O_RDWR : (in ? O_RDONLY : O_WRONLY); |  | ||||||
|  |  | ||||||
|     fd = open (dspname, oflags | O_NONBLOCK); |  | ||||||
|     if (-1 == fd) { |     if (-1 == fd) { | ||||||
|         oss_logerr2 (errno, typ, "Failed to open `%s'\n", dspname); |         oss_logerr2 (errno, typ, "Failed to open `%s'\n", dspname); | ||||||
|         return -1; |         return -1; | ||||||
| @@ -302,52 +237,23 @@ static int oss_open (int in, struct oss_params *req, | |||||||
|         goto err; |         goto err; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (ioctl (fd, SNDCTL_DSP_NONBLOCK, NULL)) { |     if (ioctl (fd, SNDCTL_DSP_NONBLOCK)) { | ||||||
|         oss_logerr2 (errno, typ, "Failed to set non-blocking mode\n"); |         oss_logerr2 (errno, typ, "Failed to set non-blocking mode\n"); | ||||||
|         goto err; |         goto err; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| #ifdef USE_DSP_POLICY |     mmmmssss = (req->nfrags << 16) | lsbindex (req->fragsize); | ||||||
|     if (conf->policy >= 0) { |  | ||||||
|         int version; |  | ||||||
|  |  | ||||||
|         if (!oss_get_version (fd, &version, typ)) { |  | ||||||
|             trace_oss_version(version); |  | ||||||
|  |  | ||||||
|             if (version >= 0x040000) { |  | ||||||
|                 int policy = conf->policy; |  | ||||||
|                 if (ioctl (fd, SNDCTL_DSP_POLICY, &policy)) { |  | ||||||
|                     oss_logerr2 (errno, typ, |  | ||||||
|                                  "Failed to set timing policy to %d\n", |  | ||||||
|                                  conf->policy); |  | ||||||
|                     goto err; |  | ||||||
|                 } |  | ||||||
|                 setfragment = 0; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
|     if (setfragment) { |  | ||||||
|         int mmmmssss = (req->nfrags << 16) | ctz32 (req->fragsize); |  | ||||||
|     if (ioctl (fd, SNDCTL_DSP_SETFRAGMENT, &mmmmssss)) { |     if (ioctl (fd, SNDCTL_DSP_SETFRAGMENT, &mmmmssss)) { | ||||||
|         oss_logerr2 (errno, typ, "Failed to set buffer length (%d, %d)\n", |         oss_logerr2 (errno, typ, "Failed to set buffer length (%d, %d)\n", | ||||||
|                      req->nfrags, req->fragsize); |                      req->nfrags, req->fragsize); | ||||||
|         goto err; |         goto err; | ||||||
|     } |     } | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (ioctl (fd, in ? SNDCTL_DSP_GETISPACE : SNDCTL_DSP_GETOSPACE, &abinfo)) { |     if (ioctl (fd, in ? SNDCTL_DSP_GETISPACE : SNDCTL_DSP_GETOSPACE, &abinfo)) { | ||||||
|         oss_logerr2 (errno, typ, "Failed to get buffer length\n"); |         oss_logerr2 (errno, typ, "Failed to get buffer length\n"); | ||||||
|         goto err; |         goto err; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (!abinfo.fragstotal || !abinfo.fragsize) { |  | ||||||
|         AUD_log (AUDIO_CAP, "Returned bogus buffer information(%d, %d) for %s\n", |  | ||||||
|                  abinfo.fragstotal, abinfo.fragsize, typ); |  | ||||||
|         goto err; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     obt->fmt = fmt; |     obt->fmt = fmt; | ||||||
|     obt->nchannels = nchannels; |     obt->nchannels = nchannels; | ||||||
|     obt->freq = freq; |     obt->freq = freq; | ||||||
| @@ -376,58 +282,26 @@ static int oss_open (int in, struct oss_params *req, | |||||||
|     return -1; |     return -1; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void oss_write_pending (OSSVoiceOut *oss) | static int oss_run_out (HWVoiceOut *hw) | ||||||
| { |  | ||||||
|     HWVoiceOut *hw = &oss->hw; |  | ||||||
|  |  | ||||||
|     if (oss->mmapped) { |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     while (oss->pending) { |  | ||||||
|         int samples_written; |  | ||||||
|         ssize_t bytes_written; |  | ||||||
|         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, bytes_to_write); |  | ||||||
|         if (bytes_written < 0) { |  | ||||||
|             if (errno != EAGAIN) { |  | ||||||
|                 oss_logerr (errno, "failed to write %d bytes\n", |  | ||||||
|                             bytes_to_write); |  | ||||||
|             } |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         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; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int oss_run_out (HWVoiceOut *hw, int live) |  | ||||||
| { | { | ||||||
|     OSSVoiceOut *oss = (OSSVoiceOut *) hw; |     OSSVoiceOut *oss = (OSSVoiceOut *) hw; | ||||||
|     int err, decr; |     int err, rpos, live, decr; | ||||||
|  |     int samples; | ||||||
|  |     uint8_t *dst; | ||||||
|  |     st_sample_t *src; | ||||||
|     struct audio_buf_info abinfo; |     struct audio_buf_info abinfo; | ||||||
|     struct count_info cntinfo; |     struct count_info cntinfo; | ||||||
|     int bufsize; |     int bufsize; | ||||||
|  |  | ||||||
|  |     live = audio_pcm_hw_get_live_out (hw); | ||||||
|  |     if (!live) { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     bufsize = hw->samples << hw->info.shift; |     bufsize = hw->samples << hw->info.shift; | ||||||
|  |  | ||||||
|     if (oss->mmapped) { |     if (oss->mmapped) { | ||||||
|         int bytes, pos; |         int bytes; | ||||||
|  |  | ||||||
|         err = ioctl (oss->fd, SNDCTL_DSP_GETOPTR, &cntinfo); |         err = ioctl (oss->fd, SNDCTL_DSP_GETOPTR, &cntinfo); | ||||||
|         if (err < 0) { |         if (err < 0) { | ||||||
| @@ -435,8 +309,20 @@ static int oss_run_out (HWVoiceOut *hw, int live) | |||||||
|             return 0; |             return 0; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         pos = hw->rpos << hw->info.shift; |         if (cntinfo.ptr == oss->old_optr) { | ||||||
|         bytes = audio_ring_dist (cntinfo.ptr, pos, bufsize); |             if (abs (hw->samples - live) < 64) { | ||||||
|  |                 dolog ("warning: Overrun\n"); | ||||||
|  |             } | ||||||
|  |             return 0; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (cntinfo.ptr > oss->old_optr) { | ||||||
|  |             bytes = cntinfo.ptr - oss->old_optr; | ||||||
|  |         } | ||||||
|  |         else { | ||||||
|  |             bytes = bufsize + cntinfo.ptr - oss->old_optr; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         decr = audio_MIN (bytes >> hw->info.shift, live); |         decr = audio_MIN (bytes >> hw->info.shift, live); | ||||||
|     } |     } | ||||||
|     else { |     else { | ||||||
| @@ -447,12 +333,19 @@ static int oss_run_out (HWVoiceOut *hw, int live) | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (abinfo.bytes > bufsize) { |         if (abinfo.bytes > bufsize) { | ||||||
|             trace_oss_invalid_available_size(abinfo.bytes, bufsize); |             if (conf.debug) { | ||||||
|  |                 dolog ("warning: Invalid available size, size=%d bufsize=%d\n" | ||||||
|  |                        "please report your OS/audio hw to malc@pulsesoft.com\n", | ||||||
|  |                        abinfo.bytes, bufsize); | ||||||
|  |             } | ||||||
|             abinfo.bytes = bufsize; |             abinfo.bytes = bufsize; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (abinfo.bytes < 0) { |         if (abinfo.bytes < 0) { | ||||||
|             trace_oss_invalid_available_size(abinfo.bytes, bufsize); |             if (conf.debug) { | ||||||
|  |                 dolog ("warning: Invalid available size, size=%d bufsize=%d\n", | ||||||
|  |                        abinfo.bytes, bufsize); | ||||||
|  |             } | ||||||
|             return 0; |             return 0; | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -462,10 +355,53 @@ static int oss_run_out (HWVoiceOut *hw, int live) | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     decr = audio_pcm_hw_clip_out (hw, oss->pcm_buf, decr, oss->pending); |     samples = decr; | ||||||
|     oss->pending += decr; |     rpos = hw->rpos; | ||||||
|     oss_write_pending (oss); |     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 (oss->pcm_buf, rpos << hw->info.shift); | ||||||
|  |  | ||||||
|  |         hw->clip (dst, src, convert_samples); | ||||||
|  |         if (!oss->mmapped) { | ||||||
|  |             int written; | ||||||
|  |  | ||||||
|  |             written = write (oss->fd, dst, convert_samples << hw->info.shift); | ||||||
|  |             /* XXX: follow errno recommendations ? */ | ||||||
|  |             if (written == -1) { | ||||||
|  |                 oss_logerr ( | ||||||
|  |                     errno, | ||||||
|  |                     "Failed to write %d bytes of audio data from %p\n", | ||||||
|  |                     convert_samples << hw->info.shift, | ||||||
|  |                     dst | ||||||
|  |                     ); | ||||||
|  |                 continue; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             if (written != convert_samples << hw->info.shift) { | ||||||
|  |                 int wsamples = written >> hw->info.shift; | ||||||
|  |                 int wbytes = wsamples << hw->info.shift; | ||||||
|  |                 if (wbytes != written) { | ||||||
|  |                     dolog ("warning: Misaligned write %d (requested %d), " | ||||||
|  |                            "alignment %d\n", | ||||||
|  |                            wbytes, written, hw->info.align + 1); | ||||||
|  |                 } | ||||||
|  |                 decr -= wsamples; | ||||||
|  |                 rpos = (rpos + wsamples) % hw->samples; | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         rpos = (rpos + convert_samples) % hw->samples; | ||||||
|  |         samples -= convert_samples; | ||||||
|  |     } | ||||||
|  |     if (oss->mmapped) { | ||||||
|  |         oss->old_optr = cntinfo.ptr; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     hw->rpos = rpos; | ||||||
|     return decr; |     return decr; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -486,14 +422,13 @@ static void oss_fini_out (HWVoiceOut *hw) | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         else { |         else { | ||||||
|             g_free (oss->pcm_buf); |             qemu_free (oss->pcm_buf); | ||||||
|         } |         } | ||||||
|         oss->pcm_buf = NULL; |         oss->pcm_buf = NULL; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| static int oss_init_out(HWVoiceOut *hw, struct audsettings *as, | static int oss_init_out (HWVoiceOut *hw, audsettings_t *as) | ||||||
|                         void *drv_opaque) |  | ||||||
| { | { | ||||||
|     OSSVoiceOut *oss = (OSSVoiceOut *) hw; |     OSSVoiceOut *oss = (OSSVoiceOut *) hw; | ||||||
|     struct oss_params req, obt; |     struct oss_params req, obt; | ||||||
| @@ -501,18 +436,17 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as, | |||||||
|     int err; |     int err; | ||||||
|     int fd; |     int fd; | ||||||
|     audfmt_e effective_fmt; |     audfmt_e effective_fmt; | ||||||
|     struct audsettings obt_as; |     audsettings_t obt_as; | ||||||
|     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); | ||||||
|     req.freq = as->freq; |     req.freq = as->freq; | ||||||
|     req.nchannels = as->nchannels; |     req.nchannels = as->nchannels; | ||||||
|     req.fragsize = conf->fragsize; |     req.fragsize = conf.fragsize; | ||||||
|     req.nfrags = conf->nfrags; |     req.nfrags = conf.nfrags; | ||||||
|  |  | ||||||
|     if (oss_open (0, &req, &obt, &fd, conf)) { |     if (oss_open (0, &req, &obt, &fd)) { | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -539,9 +473,9 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as, | |||||||
|     hw->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift; |     hw->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift; | ||||||
|  |  | ||||||
|     oss->mmapped = 0; |     oss->mmapped = 0; | ||||||
|     if (conf->try_mmap) { |     if (conf.try_mmap) { | ||||||
|         oss->pcm_buf = mmap ( |         oss->pcm_buf = mmap ( | ||||||
|             NULL, |             0, | ||||||
|             hw->samples << hw->info.shift, |             hw->samples << hw->info.shift, | ||||||
|             PROT_READ | PROT_WRITE, |             PROT_READ | PROT_WRITE, | ||||||
|             MAP_SHARED, |             MAP_SHARED, | ||||||
| @@ -551,8 +485,7 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as, | |||||||
|         if (oss->pcm_buf == MAP_FAILED) { |         if (oss->pcm_buf == MAP_FAILED) { | ||||||
|             oss_logerr (errno, "Failed to map %d bytes of DAC\n", |             oss_logerr (errno, "Failed to map %d bytes of DAC\n", | ||||||
|                         hw->samples << hw->info.shift); |                         hw->samples << hw->info.shift); | ||||||
|         } |         } 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) { | ||||||
| @@ -582,9 +515,11 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as, | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (!oss->mmapped) { |     if (!oss->mmapped) { | ||||||
|         oss->pcm_buf = audio_calloc(__func__, |         oss->pcm_buf = audio_calloc ( | ||||||
|  |             AUDIO_FUNC, | ||||||
|             hw->samples, |             hw->samples, | ||||||
|                                     1 << hw->info.shift); |             1 << hw->info.shift | ||||||
|  |             ); | ||||||
|         if (!oss->pcm_buf) { |         if (!oss->pcm_buf) { | ||||||
|             dolog ( |             dolog ( | ||||||
|                 "Could not allocate DAC buffer (%d samples, each %d bytes)\n", |                 "Could not allocate DAC buffer (%d samples, each %d bytes)\n", | ||||||
| @@ -597,7 +532,6 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as, | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     oss->fd = fd; |     oss->fd = fd; | ||||||
|     oss->conf = conf; |  | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -606,27 +540,13 @@ static int oss_ctl_out (HWVoiceOut *hw, int cmd, ...) | |||||||
|     int trig; |     int trig; | ||||||
|     OSSVoiceOut *oss = (OSSVoiceOut *) hw; |     OSSVoiceOut *oss = (OSSVoiceOut *) hw; | ||||||
|  |  | ||||||
|     switch (cmd) { |  | ||||||
|     case VOICE_ENABLE: |  | ||||||
|         { |  | ||||||
|             va_list ap; |  | ||||||
|             int poll_mode; |  | ||||||
|  |  | ||||||
|             va_start (ap, cmd); |  | ||||||
|             poll_mode = va_arg (ap, int); |  | ||||||
|             va_end (ap); |  | ||||||
|  |  | ||||||
|             ldebug ("enabling voice\n"); |  | ||||||
|             if (poll_mode) { |  | ||||||
|                 oss_poll_out (hw); |  | ||||||
|                 poll_mode = 0; |  | ||||||
|             } |  | ||||||
|             hw->poll_mode = poll_mode; |  | ||||||
|  |  | ||||||
|     if (!oss->mmapped) { |     if (!oss->mmapped) { | ||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     switch (cmd) { | ||||||
|  |     case VOICE_ENABLE: | ||||||
|  |         ldebug ("enabling voice\n"); | ||||||
|         audio_pcm_info_clear_buf (&hw->info, oss->pcm_buf, hw->samples); |         audio_pcm_info_clear_buf (&hw->info, oss->pcm_buf, hw->samples); | ||||||
|         trig = PCM_ENABLE_OUTPUT; |         trig = PCM_ENABLE_OUTPUT; | ||||||
|         if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) { |         if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) { | ||||||
| @@ -636,19 +556,9 @@ static int oss_ctl_out (HWVoiceOut *hw, int cmd, ...) | |||||||
|                 ); |                 ); | ||||||
|             return -1; |             return -1; | ||||||
|         } |         } | ||||||
|         } |  | ||||||
|         break; |         break; | ||||||
|  |  | ||||||
|     case VOICE_DISABLE: |     case VOICE_DISABLE: | ||||||
|         if (hw->poll_mode) { |  | ||||||
|             qemu_set_fd_handler (oss->fd, NULL, NULL, NULL); |  | ||||||
|             hw->poll_mode = 0; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if (!oss->mmapped) { |  | ||||||
|             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) { | ||||||
| @@ -660,7 +570,7 @@ static int oss_ctl_out (HWVoiceOut *hw, int cmd, ...) | |||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) | static int oss_init_in (HWVoiceIn *hw, audsettings_t *as) | ||||||
| { | { | ||||||
|     OSSVoiceIn *oss = (OSSVoiceIn *) hw; |     OSSVoiceIn *oss = (OSSVoiceIn *) hw; | ||||||
|     struct oss_params req, obt; |     struct oss_params req, obt; | ||||||
| @@ -668,17 +578,16 @@ static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) | |||||||
|     int err; |     int err; | ||||||
|     int fd; |     int fd; | ||||||
|     audfmt_e effective_fmt; |     audfmt_e effective_fmt; | ||||||
|     struct audsettings obt_as; |     audsettings_t obt_as; | ||||||
|     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); | ||||||
|     req.freq = as->freq; |     req.freq = as->freq; | ||||||
|     req.nchannels = as->nchannels; |     req.nchannels = as->nchannels; | ||||||
|     req.fragsize = conf->fragsize; |     req.fragsize = conf.fragsize; | ||||||
|     req.nfrags = conf->nfrags; |     req.nfrags = conf.nfrags; | ||||||
|     if (oss_open (1, &req, &obt, &fd, conf)) { |     if (oss_open (1, &req, &obt, &fd)) { | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -703,7 +612,7 @@ static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     hw->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift; |     hw->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift; | ||||||
|     oss->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift); |     oss->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift); | ||||||
|     if (!oss->pcm_buf) { |     if (!oss->pcm_buf) { | ||||||
|         dolog ("Could not allocate ADC buffer (%d samples, each %d bytes)\n", |         dolog ("Could not allocate ADC buffer (%d samples, each %d bytes)\n", | ||||||
|                hw->samples, 1 << hw->info.shift); |                hw->samples, 1 << hw->info.shift); | ||||||
| @@ -712,7 +621,6 @@ static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     oss->fd = fd; |     oss->fd = fd; | ||||||
|     oss->conf = conf; |  | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -722,9 +630,11 @@ static void oss_fini_in (HWVoiceIn *hw) | |||||||
|  |  | ||||||
|     oss_anal_close (&oss->fd); |     oss_anal_close (&oss->fd); | ||||||
|  |  | ||||||
|     g_free(oss->pcm_buf); |     if (oss->pcm_buf) { | ||||||
|  |         qemu_free (oss->pcm_buf); | ||||||
|         oss->pcm_buf = NULL; |         oss->pcm_buf = NULL; | ||||||
|     } |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| static int oss_run_in (HWVoiceIn *hw) | static int oss_run_in (HWVoiceIn *hw) | ||||||
| { | { | ||||||
| @@ -738,8 +648,8 @@ static int oss_run_in (HWVoiceIn *hw) | |||||||
|         int add; |         int add; | ||||||
|         int len; |         int len; | ||||||
|     } bufs[2] = { |     } bufs[2] = { | ||||||
|         { .add = hw->wpos, .len = 0 }, |         { hw->wpos, 0 }, | ||||||
|         { .add = 0,        .len = 0 } |         { 0, 0 } | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     if (!dead) { |     if (!dead) { | ||||||
| @@ -754,6 +664,7 @@ static int oss_run_in (HWVoiceIn *hw) | |||||||
|         bufs[0].len = dead << hwshift; |         bufs[0].len = dead << hwshift; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|     for (i = 0; i < 2; ++i) { |     for (i = 0; i < 2; ++i) { | ||||||
|         ssize_t nread; |         ssize_t nread; | ||||||
|  |  | ||||||
| @@ -768,7 +679,8 @@ static int oss_run_in (HWVoiceIn *hw) | |||||||
|                            hw->info.align + 1); |                            hw->info.align + 1); | ||||||
|                 } |                 } | ||||||
|                 read_samples += nread >> hwshift; |                 read_samples += nread >> hwshift; | ||||||
|                 hw->conv (hw->conv_buf + bufs[i].add, p, nread >> hwshift); |                 hw->conv (hw->conv_buf + bufs[i].add, p, nread >> hwshift, | ||||||
|  |                           &nominal_volume); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             if (bufs[i].len - nread) { |             if (bufs[i].len - nread) { | ||||||
| @@ -802,142 +714,61 @@ static int oss_read (SWVoiceIn *sw, void *buf, int size) | |||||||
|  |  | ||||||
| static int oss_ctl_in (HWVoiceIn *hw, int cmd, ...) | static int oss_ctl_in (HWVoiceIn *hw, int cmd, ...) | ||||||
| { | { | ||||||
|     OSSVoiceIn *oss = (OSSVoiceIn *) hw; |     (void) hw; | ||||||
|  |     (void) cmd; | ||||||
|     switch (cmd) { |  | ||||||
|     case VOICE_ENABLE: |  | ||||||
|         { |  | ||||||
|             va_list ap; |  | ||||||
|             int poll_mode; |  | ||||||
|  |  | ||||||
|             va_start (ap, cmd); |  | ||||||
|             poll_mode = va_arg (ap, int); |  | ||||||
|             va_end (ap); |  | ||||||
|  |  | ||||||
|             if (poll_mode) { |  | ||||||
|                 oss_poll_in (hw); |  | ||||||
|                 poll_mode = 0; |  | ||||||
|             } |  | ||||||
|             hw->poll_mode = poll_mode; |  | ||||||
|         } |  | ||||||
|         break; |  | ||||||
|  |  | ||||||
|     case VOICE_DISABLE: |  | ||||||
|         if (hw->poll_mode) { |  | ||||||
|             hw->poll_mode = 0; |  | ||||||
|             qemu_set_fd_handler (oss->fd, NULL, NULL, NULL); |  | ||||||
|         } |  | ||||||
|         break; |  | ||||||
|     } |  | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| 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) | static void *oss_audio_init (void) | ||||||
| { | { | ||||||
|     OSSConf *conf = g_malloc(sizeof(OSSConf)); |     return &conf; | ||||||
|     *conf = glob_conf; |  | ||||||
|  |  | ||||||
|     if (access(conf->devpath_in, R_OK | W_OK) < 0 || |  | ||||||
|         access(conf->devpath_out, R_OK | W_OK) < 0) { |  | ||||||
|         g_free(conf); |  | ||||||
|         return NULL; |  | ||||||
|     } |  | ||||||
|     return conf; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static void oss_audio_fini (void *opaque) | static void oss_audio_fini (void *opaque) | ||||||
| { | { | ||||||
|     g_free(opaque); |     (void) opaque; | ||||||
| } | } | ||||||
|  |  | ||||||
| static struct audio_option oss_options[] = { | static struct audio_option oss_options[] = { | ||||||
|     { |     {"FRAGSIZE", AUD_OPT_INT, &conf.fragsize, | ||||||
|         .name  = "FRAGSIZE", |      "Fragment size in bytes", NULL, 0}, | ||||||
|         .tag   = AUD_OPT_INT, |     {"NFRAGS", AUD_OPT_INT, &conf.nfrags, | ||||||
|         .valp  = &glob_conf.fragsize, |      "Number of fragments", NULL, 0}, | ||||||
|         .descr = "Fragment size in bytes" |     {"MMAP", AUD_OPT_BOOL, &conf.try_mmap, | ||||||
|     }, |      "Try using memory mapped access", NULL, 0}, | ||||||
|     { |     {"DAC_DEV", AUD_OPT_STR, &conf.devpath_out, | ||||||
|         .name  = "NFRAGS", |      "Path to DAC device", NULL, 0}, | ||||||
|         .tag   = AUD_OPT_INT, |     {"ADC_DEV", AUD_OPT_STR, &conf.devpath_in, | ||||||
|         .valp  = &glob_conf.nfrags, |      "Path to ADC device", NULL, 0}, | ||||||
|         .descr = "Number of fragments" |     {"DEBUG", AUD_OPT_BOOL, &conf.debug, | ||||||
|     }, |      "Turn on some debugging messages", NULL, 0}, | ||||||
|     { |     {NULL, 0, NULL, NULL, NULL, 0} | ||||||
|         .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, |     oss_init_out, | ||||||
|     .fini_out = oss_fini_out, |     oss_fini_out, | ||||||
|     .run_out  = oss_run_out, |     oss_run_out, | ||||||
|     .write    = oss_write, |     oss_write, | ||||||
|     .ctl_out  = oss_ctl_out, |     oss_ctl_out, | ||||||
|  |  | ||||||
|     .init_in  = oss_init_in, |     oss_init_in, | ||||||
|     .fini_in  = oss_fini_in, |     oss_fini_in, | ||||||
|     .run_in   = oss_run_in, |     oss_run_in, | ||||||
|     .read     = oss_read, |     oss_read, | ||||||
|     .ctl_in   = oss_ctl_in |     oss_ctl_in | ||||||
| }; | }; | ||||||
|  |  | ||||||
| static struct audio_driver oss_audio_driver = { | struct audio_driver oss_audio_driver = { | ||||||
|     .name           = "oss", |     INIT_FIELD (name           = ) "oss", | ||||||
|     .descr          = "OSS http://www.opensound.com", |     INIT_FIELD (descr          = ) "OSS http://www.opensound.com", | ||||||
|     .options        = oss_options, |     INIT_FIELD (options        = ) oss_options, | ||||||
|     .init           = oss_audio_init, |     INIT_FIELD (init           = ) oss_audio_init, | ||||||
|     .fini           = oss_audio_fini, |     INIT_FIELD (fini           = ) oss_audio_fini, | ||||||
|     .pcm_ops        = &oss_pcm_ops, |     INIT_FIELD (pcm_ops        = ) &oss_pcm_ops, | ||||||
|     .can_be_default = 1, |     INIT_FIELD (can_be_default = ) 1, | ||||||
|     .max_voices_out = INT_MAX, |     INIT_FIELD (max_voices_out = ) INT_MAX, | ||||||
|     .max_voices_in  = INT_MAX, |     INIT_FIELD (max_voices_in  = ) INT_MAX, | ||||||
|     .voice_size_out = sizeof (OSSVoiceOut), |     INIT_FIELD (voice_size_out = ) sizeof (OSSVoiceOut), | ||||||
|     .voice_size_in  = sizeof (OSSVoiceIn) |     INIT_FIELD (voice_size_in  = ) sizeof (OSSVoiceIn) | ||||||
| }; | }; | ||||||
|  |  | ||||||
| static void register_audio_oss(void) |  | ||||||
| { |  | ||||||
|     audio_driver_register(&oss_audio_driver); |  | ||||||
| } |  | ||||||
| type_init(register_audio_oss); |  | ||||||
|   | |||||||
							
								
								
									
										959
									
								
								audio/paaudio.c
									
									
									
									
									
								
							
							
						
						
									
										959
									
								
								audio/paaudio.c
									
									
									
									
									
								
							| @@ -1,959 +0,0 @@ | |||||||
| /* public domain */ |  | ||||||
| #include "qemu/osdep.h" |  | ||||||
| #include "qemu-common.h" |  | ||||||
| #include "audio.h" |  | ||||||
|  |  | ||||||
| #include <pulse/pulseaudio.h> |  | ||||||
|  |  | ||||||
| #define AUDIO_CAP "pulseaudio" |  | ||||||
| #include "audio_int.h" |  | ||||||
| #include "audio_pt_int.h" |  | ||||||
|  |  | ||||||
| typedef struct { |  | ||||||
|     int samples; |  | ||||||
|     char *server; |  | ||||||
|     char *sink; |  | ||||||
|     char *source; |  | ||||||
| } PAConf; |  | ||||||
|  |  | ||||||
| typedef struct { |  | ||||||
|     PAConf conf; |  | ||||||
|     pa_threaded_mainloop *mainloop; |  | ||||||
|     pa_context *context; |  | ||||||
| } paaudio; |  | ||||||
|  |  | ||||||
| typedef struct { |  | ||||||
|     HWVoiceOut hw; |  | ||||||
|     int done; |  | ||||||
|     int live; |  | ||||||
|     int decr; |  | ||||||
|     int rpos; |  | ||||||
|     pa_stream *stream; |  | ||||||
|     void *pcm_buf; |  | ||||||
|     struct audio_pt pt; |  | ||||||
|     paaudio *g; |  | ||||||
| } PAVoiceOut; |  | ||||||
|  |  | ||||||
| typedef struct { |  | ||||||
|     HWVoiceIn hw; |  | ||||||
|     int done; |  | ||||||
|     int dead; |  | ||||||
|     int incr; |  | ||||||
|     int wpos; |  | ||||||
|     pa_stream *stream; |  | ||||||
|     void *pcm_buf; |  | ||||||
|     struct audio_pt pt; |  | ||||||
|     const void *read_data; |  | ||||||
|     size_t read_index, read_length; |  | ||||||
|     paaudio *g; |  | ||||||
| } PAVoiceIn; |  | ||||||
|  |  | ||||||
| static void qpa_audio_fini(void *opaque); |  | ||||||
|  |  | ||||||
| static void GCC_FMT_ATTR (2, 3) qpa_logerr (int err, const char *fmt, ...) |  | ||||||
| { |  | ||||||
|     va_list ap; |  | ||||||
|  |  | ||||||
|     va_start (ap, fmt); |  | ||||||
|     AUD_vlog (AUDIO_CAP, fmt, ap); |  | ||||||
|     va_end (ap); |  | ||||||
|  |  | ||||||
|     AUD_log (AUDIO_CAP, "Reason: %s\n", pa_strerror (err)); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #ifndef PA_CONTEXT_IS_GOOD |  | ||||||
| static inline int PA_CONTEXT_IS_GOOD(pa_context_state_t x) |  | ||||||
| { |  | ||||||
|     return |  | ||||||
|         x == PA_CONTEXT_CONNECTING || |  | ||||||
|         x == PA_CONTEXT_AUTHORIZING || |  | ||||||
|         x == PA_CONTEXT_SETTING_NAME || |  | ||||||
|         x == PA_CONTEXT_READY; |  | ||||||
| } |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| #ifndef PA_STREAM_IS_GOOD |  | ||||||
| static inline int PA_STREAM_IS_GOOD(pa_stream_state_t x) |  | ||||||
| { |  | ||||||
|     return |  | ||||||
|         x == PA_STREAM_CREATING || |  | ||||||
|         x == PA_STREAM_READY; |  | ||||||
| } |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| #define CHECK_SUCCESS_GOTO(c, rerror, expression, label)        \ |  | ||||||
|     do {                                                        \ |  | ||||||
|         if (!(expression)) {                                    \ |  | ||||||
|             if (rerror) {                                       \ |  | ||||||
|                 *(rerror) = pa_context_errno ((c)->context);    \ |  | ||||||
|             }                                                   \ |  | ||||||
|             goto label;                                         \ |  | ||||||
|         }                                                       \ |  | ||||||
|     } while (0) |  | ||||||
|  |  | ||||||
| #define CHECK_DEAD_GOTO(c, stream, rerror, label)                       \ |  | ||||||
|     do {                                                                \ |  | ||||||
|         if (!(c)->context || !PA_CONTEXT_IS_GOOD (pa_context_get_state((c)->context)) || \ |  | ||||||
|             !(stream) || !PA_STREAM_IS_GOOD (pa_stream_get_state ((stream)))) { \ |  | ||||||
|             if (((c)->context && pa_context_get_state ((c)->context) == PA_CONTEXT_FAILED) || \ |  | ||||||
|                 ((stream) && pa_stream_get_state ((stream)) == PA_STREAM_FAILED)) { \ |  | ||||||
|                 if (rerror) {                                           \ |  | ||||||
|                     *(rerror) = pa_context_errno ((c)->context);        \ |  | ||||||
|                 }                                                       \ |  | ||||||
|             } else {                                                    \ |  | ||||||
|                 if (rerror) {                                           \ |  | ||||||
|                     *(rerror) = PA_ERR_BADSTATE;                        \ |  | ||||||
|                 }                                                       \ |  | ||||||
|             }                                                           \ |  | ||||||
|             goto label;                                                 \ |  | ||||||
|         }                                                               \ |  | ||||||
|     } while (0) |  | ||||||
|  |  | ||||||
| static int qpa_simple_read (PAVoiceIn *p, void *data, size_t length, int *rerror) |  | ||||||
| { |  | ||||||
|     paaudio *g = p->g; |  | ||||||
|  |  | ||||||
|     pa_threaded_mainloop_lock (g->mainloop); |  | ||||||
|  |  | ||||||
|     CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail); |  | ||||||
|  |  | ||||||
|     while (length > 0) { |  | ||||||
|         size_t l; |  | ||||||
|  |  | ||||||
|         while (!p->read_data) { |  | ||||||
|             int r; |  | ||||||
|  |  | ||||||
|             r = pa_stream_peek (p->stream, &p->read_data, &p->read_length); |  | ||||||
|             CHECK_SUCCESS_GOTO (g, rerror, r == 0, unlock_and_fail); |  | ||||||
|  |  | ||||||
|             if (!p->read_data) { |  | ||||||
|                 pa_threaded_mainloop_wait (g->mainloop); |  | ||||||
|                 CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail); |  | ||||||
|             } else { |  | ||||||
|                 p->read_index = 0; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         l = p->read_length < length ? p->read_length : length; |  | ||||||
|         memcpy (data, (const uint8_t *) p->read_data+p->read_index, l); |  | ||||||
|  |  | ||||||
|         data = (uint8_t *) data + l; |  | ||||||
|         length -= l; |  | ||||||
|  |  | ||||||
|         p->read_index += l; |  | ||||||
|         p->read_length -= l; |  | ||||||
|  |  | ||||||
|         if (!p->read_length) { |  | ||||||
|             int r; |  | ||||||
|  |  | ||||||
|             r = pa_stream_drop (p->stream); |  | ||||||
|             p->read_data = NULL; |  | ||||||
|             p->read_length = 0; |  | ||||||
|             p->read_index = 0; |  | ||||||
|  |  | ||||||
|             CHECK_SUCCESS_GOTO (g, rerror, r == 0, unlock_and_fail); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     pa_threaded_mainloop_unlock (g->mainloop); |  | ||||||
|     return 0; |  | ||||||
|  |  | ||||||
| unlock_and_fail: |  | ||||||
|     pa_threaded_mainloop_unlock (g->mainloop); |  | ||||||
|     return -1; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int qpa_simple_write (PAVoiceOut *p, const void *data, size_t length, int *rerror) |  | ||||||
| { |  | ||||||
|     paaudio *g = p->g; |  | ||||||
|  |  | ||||||
|     pa_threaded_mainloop_lock (g->mainloop); |  | ||||||
|  |  | ||||||
|     CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail); |  | ||||||
|  |  | ||||||
|     while (length > 0) { |  | ||||||
|         size_t l; |  | ||||||
|         int r; |  | ||||||
|  |  | ||||||
|         while (!(l = pa_stream_writable_size (p->stream))) { |  | ||||||
|             pa_threaded_mainloop_wait (g->mainloop); |  | ||||||
|             CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         CHECK_SUCCESS_GOTO (g, rerror, l != (size_t) -1, unlock_and_fail); |  | ||||||
|  |  | ||||||
|         if (l > length) { |  | ||||||
|             l = length; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         r = pa_stream_write (p->stream, data, l, NULL, 0LL, PA_SEEK_RELATIVE); |  | ||||||
|         CHECK_SUCCESS_GOTO (g, rerror, r >= 0, unlock_and_fail); |  | ||||||
|  |  | ||||||
|         data = (const uint8_t *) data + l; |  | ||||||
|         length -= l; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     pa_threaded_mainloop_unlock (g->mainloop); |  | ||||||
|     return 0; |  | ||||||
|  |  | ||||||
| unlock_and_fail: |  | ||||||
|     pa_threaded_mainloop_unlock (g->mainloop); |  | ||||||
|     return -1; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void *qpa_thread_out (void *arg) |  | ||||||
| { |  | ||||||
|     PAVoiceOut *pa = arg; |  | ||||||
|     HWVoiceOut *hw = &pa->hw; |  | ||||||
|  |  | ||||||
|     if (audio_pt_lock(&pa->pt, __func__)) { |  | ||||||
|         return NULL; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     for (;;) { |  | ||||||
|         int decr, to_mix, rpos; |  | ||||||
|  |  | ||||||
|         for (;;) { |  | ||||||
|             if (pa->done) { |  | ||||||
|                 goto exit; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             if (pa->live > 0) { |  | ||||||
|                 break; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             if (audio_pt_wait(&pa->pt, __func__)) { |  | ||||||
|                 goto exit; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         decr = to_mix = audio_MIN (pa->live, pa->g->conf.samples >> 2); |  | ||||||
|         rpos = pa->rpos; |  | ||||||
|  |  | ||||||
|         if (audio_pt_unlock(&pa->pt, __func__)) { |  | ||||||
|             return NULL; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         while (to_mix) { |  | ||||||
|             int error; |  | ||||||
|             int chunk = audio_MIN (to_mix, hw->samples - rpos); |  | ||||||
|             struct st_sample *src = hw->mix_buf + rpos; |  | ||||||
|  |  | ||||||
|             hw->clip (pa->pcm_buf, src, chunk); |  | ||||||
|  |  | ||||||
|             if (qpa_simple_write (pa, pa->pcm_buf, |  | ||||||
|                                   chunk << hw->info.shift, &error) < 0) { |  | ||||||
|                 qpa_logerr (error, "pa_simple_write failed\n"); |  | ||||||
|                 return NULL; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             rpos = (rpos + chunk) % hw->samples; |  | ||||||
|             to_mix -= chunk; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if (audio_pt_lock(&pa->pt, __func__)) { |  | ||||||
|             return NULL; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         pa->rpos = rpos; |  | ||||||
|         pa->live -= decr; |  | ||||||
|         pa->decr += decr; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|  exit: |  | ||||||
|     audio_pt_unlock(&pa->pt, __func__); |  | ||||||
|     return NULL; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int qpa_run_out (HWVoiceOut *hw, int live) |  | ||||||
| { |  | ||||||
|     int decr; |  | ||||||
|     PAVoiceOut *pa = (PAVoiceOut *) hw; |  | ||||||
|  |  | ||||||
|     if (audio_pt_lock(&pa->pt, __func__)) { |  | ||||||
|         return 0; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     decr = audio_MIN (live, pa->decr); |  | ||||||
|     pa->decr -= decr; |  | ||||||
|     pa->live = live - decr; |  | ||||||
|     hw->rpos = pa->rpos; |  | ||||||
|     if (pa->live > 0) { |  | ||||||
|         audio_pt_unlock_and_signal(&pa->pt, __func__); |  | ||||||
|     } |  | ||||||
|     else { |  | ||||||
|         audio_pt_unlock(&pa->pt, __func__); |  | ||||||
|     } |  | ||||||
|     return decr; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int qpa_write (SWVoiceOut *sw, void *buf, int len) |  | ||||||
| { |  | ||||||
|     return audio_pcm_sw_write (sw, buf, len); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* capture */ |  | ||||||
| static void *qpa_thread_in (void *arg) |  | ||||||
| { |  | ||||||
|     PAVoiceIn *pa = arg; |  | ||||||
|     HWVoiceIn *hw = &pa->hw; |  | ||||||
|  |  | ||||||
|     if (audio_pt_lock(&pa->pt, __func__)) { |  | ||||||
|         return NULL; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     for (;;) { |  | ||||||
|         int incr, to_grab, wpos; |  | ||||||
|  |  | ||||||
|         for (;;) { |  | ||||||
|             if (pa->done) { |  | ||||||
|                 goto exit; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             if (pa->dead > 0) { |  | ||||||
|                 break; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             if (audio_pt_wait(&pa->pt, __func__)) { |  | ||||||
|                 goto exit; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         incr = to_grab = audio_MIN (pa->dead, pa->g->conf.samples >> 2); |  | ||||||
|         wpos = pa->wpos; |  | ||||||
|  |  | ||||||
|         if (audio_pt_unlock(&pa->pt, __func__)) { |  | ||||||
|             return NULL; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         while (to_grab) { |  | ||||||
|             int error; |  | ||||||
|             int chunk = audio_MIN (to_grab, hw->samples - wpos); |  | ||||||
|             void *buf = advance (pa->pcm_buf, wpos); |  | ||||||
|  |  | ||||||
|             if (qpa_simple_read (pa, buf, |  | ||||||
|                                  chunk << hw->info.shift, &error) < 0) { |  | ||||||
|                 qpa_logerr (error, "pa_simple_read failed\n"); |  | ||||||
|                 return NULL; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             hw->conv (hw->conv_buf + wpos, buf, chunk); |  | ||||||
|             wpos = (wpos + chunk) % hw->samples; |  | ||||||
|             to_grab -= chunk; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if (audio_pt_lock(&pa->pt, __func__)) { |  | ||||||
|             return NULL; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         pa->wpos = wpos; |  | ||||||
|         pa->dead -= incr; |  | ||||||
|         pa->incr += incr; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|  exit: |  | ||||||
|     audio_pt_unlock(&pa->pt, __func__); |  | ||||||
|     return NULL; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int qpa_run_in (HWVoiceIn *hw) |  | ||||||
| { |  | ||||||
|     int live, incr, dead; |  | ||||||
|     PAVoiceIn *pa = (PAVoiceIn *) hw; |  | ||||||
|  |  | ||||||
|     if (audio_pt_lock(&pa->pt, __func__)) { |  | ||||||
|         return 0; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     live = audio_pcm_hw_get_live_in (hw); |  | ||||||
|     dead = hw->samples - live; |  | ||||||
|     incr = audio_MIN (dead, pa->incr); |  | ||||||
|     pa->incr -= incr; |  | ||||||
|     pa->dead = dead - incr; |  | ||||||
|     hw->wpos = pa->wpos; |  | ||||||
|     if (pa->dead > 0) { |  | ||||||
|         audio_pt_unlock_and_signal(&pa->pt, __func__); |  | ||||||
|     } |  | ||||||
|     else { |  | ||||||
|         audio_pt_unlock(&pa->pt, __func__); |  | ||||||
|     } |  | ||||||
|     return incr; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int qpa_read (SWVoiceIn *sw, void *buf, int len) |  | ||||||
| { |  | ||||||
|     return audio_pcm_sw_read (sw, buf, len); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static pa_sample_format_t audfmt_to_pa (audfmt_e afmt, int endianness) |  | ||||||
| { |  | ||||||
|     int format; |  | ||||||
|  |  | ||||||
|     switch (afmt) { |  | ||||||
|     case AUD_FMT_S8: |  | ||||||
|     case AUD_FMT_U8: |  | ||||||
|         format = PA_SAMPLE_U8; |  | ||||||
|         break; |  | ||||||
|     case AUD_FMT_S16: |  | ||||||
|     case AUD_FMT_U16: |  | ||||||
|         format = endianness ? PA_SAMPLE_S16BE : PA_SAMPLE_S16LE; |  | ||||||
|         break; |  | ||||||
|     case AUD_FMT_S32: |  | ||||||
|     case AUD_FMT_U32: |  | ||||||
|         format = endianness ? PA_SAMPLE_S32BE : PA_SAMPLE_S32LE; |  | ||||||
|         break; |  | ||||||
|     default: |  | ||||||
|         dolog ("Internal logic error: Bad audio format %d\n", afmt); |  | ||||||
|         format = PA_SAMPLE_U8; |  | ||||||
|         break; |  | ||||||
|     } |  | ||||||
|     return format; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static audfmt_e pa_to_audfmt (pa_sample_format_t fmt, int *endianness) |  | ||||||
| { |  | ||||||
|     switch (fmt) { |  | ||||||
|     case PA_SAMPLE_U8: |  | ||||||
|         return AUD_FMT_U8; |  | ||||||
|     case PA_SAMPLE_S16BE: |  | ||||||
|         *endianness = 1; |  | ||||||
|         return AUD_FMT_S16; |  | ||||||
|     case PA_SAMPLE_S16LE: |  | ||||||
|         *endianness = 0; |  | ||||||
|         return AUD_FMT_S16; |  | ||||||
|     case PA_SAMPLE_S32BE: |  | ||||||
|         *endianness = 1; |  | ||||||
|         return AUD_FMT_S32; |  | ||||||
|     case PA_SAMPLE_S32LE: |  | ||||||
|         *endianness = 0; |  | ||||||
|         return AUD_FMT_S32; |  | ||||||
|     default: |  | ||||||
|         dolog ("Internal logic error: Bad pa_sample_format %d\n", fmt); |  | ||||||
|         return AUD_FMT_U8; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void context_state_cb (pa_context *c, void *userdata) |  | ||||||
| { |  | ||||||
|     paaudio *g = userdata; |  | ||||||
|  |  | ||||||
|     switch (pa_context_get_state(c)) { |  | ||||||
|     case PA_CONTEXT_READY: |  | ||||||
|     case PA_CONTEXT_TERMINATED: |  | ||||||
|     case PA_CONTEXT_FAILED: |  | ||||||
|         pa_threaded_mainloop_signal (g->mainloop, 0); |  | ||||||
|         break; |  | ||||||
|  |  | ||||||
|     case PA_CONTEXT_UNCONNECTED: |  | ||||||
|     case PA_CONTEXT_CONNECTING: |  | ||||||
|     case PA_CONTEXT_AUTHORIZING: |  | ||||||
|     case PA_CONTEXT_SETTING_NAME: |  | ||||||
|         break; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void stream_state_cb (pa_stream *s, void * userdata) |  | ||||||
| { |  | ||||||
|     paaudio *g = userdata; |  | ||||||
|  |  | ||||||
|     switch (pa_stream_get_state (s)) { |  | ||||||
|  |  | ||||||
|     case PA_STREAM_READY: |  | ||||||
|     case PA_STREAM_FAILED: |  | ||||||
|     case PA_STREAM_TERMINATED: |  | ||||||
|         pa_threaded_mainloop_signal (g->mainloop, 0); |  | ||||||
|         break; |  | ||||||
|  |  | ||||||
|     case PA_STREAM_UNCONNECTED: |  | ||||||
|     case PA_STREAM_CREATING: |  | ||||||
|         break; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void stream_request_cb (pa_stream *s, size_t length, void *userdata) |  | ||||||
| { |  | ||||||
|     paaudio *g = userdata; |  | ||||||
|  |  | ||||||
|     pa_threaded_mainloop_signal (g->mainloop, 0); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static pa_stream *qpa_simple_new ( |  | ||||||
|         paaudio *g, |  | ||||||
|         const char *name, |  | ||||||
|         pa_stream_direction_t dir, |  | ||||||
|         const char *dev, |  | ||||||
|         const pa_sample_spec *ss, |  | ||||||
|         const pa_channel_map *map, |  | ||||||
|         const pa_buffer_attr *attr, |  | ||||||
|         int *rerror) |  | ||||||
| { |  | ||||||
|     int r; |  | ||||||
|     pa_stream *stream; |  | ||||||
|  |  | ||||||
|     pa_threaded_mainloop_lock (g->mainloop); |  | ||||||
|  |  | ||||||
|     stream = pa_stream_new (g->context, name, ss, map); |  | ||||||
|     if (!stream) { |  | ||||||
|         goto fail; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     pa_stream_set_state_callback (stream, stream_state_cb, g); |  | ||||||
|     pa_stream_set_read_callback (stream, stream_request_cb, g); |  | ||||||
|     pa_stream_set_write_callback (stream, stream_request_cb, g); |  | ||||||
|  |  | ||||||
|     if (dir == PA_STREAM_PLAYBACK) { |  | ||||||
|         r = pa_stream_connect_playback (stream, dev, attr, |  | ||||||
|                                         PA_STREAM_INTERPOLATE_TIMING |  | ||||||
| #ifdef PA_STREAM_ADJUST_LATENCY |  | ||||||
|                                         |PA_STREAM_ADJUST_LATENCY |  | ||||||
| #endif |  | ||||||
|                                         |PA_STREAM_AUTO_TIMING_UPDATE, NULL, NULL); |  | ||||||
|     } else { |  | ||||||
|         r = pa_stream_connect_record (stream, dev, attr, |  | ||||||
|                                       PA_STREAM_INTERPOLATE_TIMING |  | ||||||
| #ifdef PA_STREAM_ADJUST_LATENCY |  | ||||||
|                                       |PA_STREAM_ADJUST_LATENCY |  | ||||||
| #endif |  | ||||||
|                                       |PA_STREAM_AUTO_TIMING_UPDATE); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (r < 0) { |  | ||||||
|       goto fail; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     pa_threaded_mainloop_unlock (g->mainloop); |  | ||||||
|  |  | ||||||
|     return stream; |  | ||||||
|  |  | ||||||
| fail: |  | ||||||
|     pa_threaded_mainloop_unlock (g->mainloop); |  | ||||||
|  |  | ||||||
|     if (stream) { |  | ||||||
|         pa_stream_unref (stream); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     *rerror = pa_context_errno (g->context); |  | ||||||
|  |  | ||||||
|     return NULL; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as, |  | ||||||
|                         void *drv_opaque) |  | ||||||
| { |  | ||||||
|     int error; |  | ||||||
|     pa_sample_spec ss; |  | ||||||
|     pa_buffer_attr ba; |  | ||||||
|     struct audsettings obt_as = *as; |  | ||||||
|     PAVoiceOut *pa = (PAVoiceOut *) hw; |  | ||||||
|     paaudio *g = pa->g = drv_opaque; |  | ||||||
|  |  | ||||||
|     ss.format = audfmt_to_pa (as->fmt, as->endianness); |  | ||||||
|     ss.channels = as->nchannels; |  | ||||||
|     ss.rate = as->freq; |  | ||||||
|  |  | ||||||
|     /* |  | ||||||
|      * qemu audio tick runs at 100 Hz (by default), so processing |  | ||||||
|      * data chunks worth 10 ms of sound should be a good fit. |  | ||||||
|      */ |  | ||||||
|     ba.tlength = pa_usec_to_bytes (10 * 1000, &ss); |  | ||||||
|     ba.minreq = pa_usec_to_bytes (5 * 1000, &ss); |  | ||||||
|     ba.maxlength = -1; |  | ||||||
|     ba.prebuf = -1; |  | ||||||
|  |  | ||||||
|     obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness); |  | ||||||
|  |  | ||||||
|     pa->stream = qpa_simple_new ( |  | ||||||
|         g, |  | ||||||
|         "qemu", |  | ||||||
|         PA_STREAM_PLAYBACK, |  | ||||||
|         g->conf.sink, |  | ||||||
|         &ss, |  | ||||||
|         NULL,                   /* channel map */ |  | ||||||
|         &ba,                    /* buffering attributes */ |  | ||||||
|         &error |  | ||||||
|         ); |  | ||||||
|     if (!pa->stream) { |  | ||||||
|         qpa_logerr (error, "pa_simple_new for playback failed\n"); |  | ||||||
|         goto fail1; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     audio_pcm_init_info (&hw->info, &obt_as); |  | ||||||
|     hw->samples = g->conf.samples; |  | ||||||
|     pa->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift); |  | ||||||
|     pa->rpos = hw->rpos; |  | ||||||
|     if (!pa->pcm_buf) { |  | ||||||
|         dolog ("Could not allocate buffer (%d bytes)\n", |  | ||||||
|                hw->samples << hw->info.shift); |  | ||||||
|         goto fail2; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (audio_pt_init(&pa->pt, qpa_thread_out, hw, AUDIO_CAP, __func__)) { |  | ||||||
|         goto fail3; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return 0; |  | ||||||
|  |  | ||||||
|  fail3: |  | ||||||
|     g_free (pa->pcm_buf); |  | ||||||
|     pa->pcm_buf = NULL; |  | ||||||
|  fail2: |  | ||||||
|     if (pa->stream) { |  | ||||||
|         pa_stream_unref (pa->stream); |  | ||||||
|         pa->stream = NULL; |  | ||||||
|     } |  | ||||||
|  fail1: |  | ||||||
|     return -1; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) |  | ||||||
| { |  | ||||||
|     int error; |  | ||||||
|     pa_sample_spec ss; |  | ||||||
|     struct audsettings obt_as = *as; |  | ||||||
|     PAVoiceIn *pa = (PAVoiceIn *) hw; |  | ||||||
|     paaudio *g = pa->g = drv_opaque; |  | ||||||
|  |  | ||||||
|     ss.format = audfmt_to_pa (as->fmt, as->endianness); |  | ||||||
|     ss.channels = as->nchannels; |  | ||||||
|     ss.rate = as->freq; |  | ||||||
|  |  | ||||||
|     obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness); |  | ||||||
|  |  | ||||||
|     pa->stream = qpa_simple_new ( |  | ||||||
|         g, |  | ||||||
|         "qemu", |  | ||||||
|         PA_STREAM_RECORD, |  | ||||||
|         g->conf.source, |  | ||||||
|         &ss, |  | ||||||
|         NULL,                   /* channel map */ |  | ||||||
|         NULL,                   /* buffering attributes */ |  | ||||||
|         &error |  | ||||||
|         ); |  | ||||||
|     if (!pa->stream) { |  | ||||||
|         qpa_logerr (error, "pa_simple_new for capture failed\n"); |  | ||||||
|         goto fail1; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     audio_pcm_init_info (&hw->info, &obt_as); |  | ||||||
|     hw->samples = g->conf.samples; |  | ||||||
|     pa->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift); |  | ||||||
|     pa->wpos = hw->wpos; |  | ||||||
|     if (!pa->pcm_buf) { |  | ||||||
|         dolog ("Could not allocate buffer (%d bytes)\n", |  | ||||||
|                hw->samples << hw->info.shift); |  | ||||||
|         goto fail2; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (audio_pt_init(&pa->pt, qpa_thread_in, hw, AUDIO_CAP, __func__)) { |  | ||||||
|         goto fail3; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return 0; |  | ||||||
|  |  | ||||||
|  fail3: |  | ||||||
|     g_free (pa->pcm_buf); |  | ||||||
|     pa->pcm_buf = NULL; |  | ||||||
|  fail2: |  | ||||||
|     if (pa->stream) { |  | ||||||
|         pa_stream_unref (pa->stream); |  | ||||||
|         pa->stream = NULL; |  | ||||||
|     } |  | ||||||
|  fail1: |  | ||||||
|     return -1; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void qpa_fini_out (HWVoiceOut *hw) |  | ||||||
| { |  | ||||||
|     void *ret; |  | ||||||
|     PAVoiceOut *pa = (PAVoiceOut *) hw; |  | ||||||
|  |  | ||||||
|     audio_pt_lock(&pa->pt, __func__); |  | ||||||
|     pa->done = 1; |  | ||||||
|     audio_pt_unlock_and_signal(&pa->pt, __func__); |  | ||||||
|     audio_pt_join(&pa->pt, &ret, __func__); |  | ||||||
|  |  | ||||||
|     if (pa->stream) { |  | ||||||
|         pa_stream_unref (pa->stream); |  | ||||||
|         pa->stream = NULL; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     audio_pt_fini(&pa->pt, __func__); |  | ||||||
|     g_free (pa->pcm_buf); |  | ||||||
|     pa->pcm_buf = NULL; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void qpa_fini_in (HWVoiceIn *hw) |  | ||||||
| { |  | ||||||
|     void *ret; |  | ||||||
|     PAVoiceIn *pa = (PAVoiceIn *) hw; |  | ||||||
|  |  | ||||||
|     audio_pt_lock(&pa->pt, __func__); |  | ||||||
|     pa->done = 1; |  | ||||||
|     audio_pt_unlock_and_signal(&pa->pt, __func__); |  | ||||||
|     audio_pt_join(&pa->pt, &ret, __func__); |  | ||||||
|  |  | ||||||
|     if (pa->stream) { |  | ||||||
|         pa_stream_unref (pa->stream); |  | ||||||
|         pa->stream = NULL; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     audio_pt_fini(&pa->pt, __func__); |  | ||||||
|     g_free (pa->pcm_buf); |  | ||||||
|     pa->pcm_buf = NULL; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int qpa_ctl_out (HWVoiceOut *hw, int cmd, ...) |  | ||||||
| { |  | ||||||
|     PAVoiceOut *pa = (PAVoiceOut *) hw; |  | ||||||
|     pa_operation *op; |  | ||||||
|     pa_cvolume v; |  | ||||||
|     paaudio *g = pa->g; |  | ||||||
|  |  | ||||||
| #ifdef PA_CHECK_VERSION    /* macro is present in 0.9.16+ */ |  | ||||||
|     pa_cvolume_init (&v);  /* function is present in 0.9.13+ */ |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
|     switch (cmd) { |  | ||||||
|     case VOICE_VOLUME: |  | ||||||
|         { |  | ||||||
|             SWVoiceOut *sw; |  | ||||||
|             va_list ap; |  | ||||||
|  |  | ||||||
|             va_start (ap, cmd); |  | ||||||
|             sw = va_arg (ap, SWVoiceOut *); |  | ||||||
|             va_end (ap); |  | ||||||
|  |  | ||||||
|             v.channels = 2; |  | ||||||
|             v.values[0] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.l) / UINT32_MAX; |  | ||||||
|             v.values[1] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.r) / UINT32_MAX; |  | ||||||
|  |  | ||||||
|             pa_threaded_mainloop_lock (g->mainloop); |  | ||||||
|  |  | ||||||
|             op = pa_context_set_sink_input_volume (g->context, |  | ||||||
|                 pa_stream_get_index (pa->stream), |  | ||||||
|                 &v, NULL, NULL); |  | ||||||
|             if (!op) |  | ||||||
|                 qpa_logerr (pa_context_errno (g->context), |  | ||||||
|                             "set_sink_input_volume() failed\n"); |  | ||||||
|             else |  | ||||||
|                 pa_operation_unref (op); |  | ||||||
|  |  | ||||||
|             op = pa_context_set_sink_input_mute (g->context, |  | ||||||
|                 pa_stream_get_index (pa->stream), |  | ||||||
|                sw->vol.mute, NULL, NULL); |  | ||||||
|             if (!op) { |  | ||||||
|                 qpa_logerr (pa_context_errno (g->context), |  | ||||||
|                             "set_sink_input_mute() failed\n"); |  | ||||||
|             } else { |  | ||||||
|                 pa_operation_unref (op); |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             pa_threaded_mainloop_unlock (g->mainloop); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...) |  | ||||||
| { |  | ||||||
|     PAVoiceIn *pa = (PAVoiceIn *) hw; |  | ||||||
|     pa_operation *op; |  | ||||||
|     pa_cvolume v; |  | ||||||
|     paaudio *g = pa->g; |  | ||||||
|  |  | ||||||
| #ifdef PA_CHECK_VERSION |  | ||||||
|     pa_cvolume_init (&v); |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
|     switch (cmd) { |  | ||||||
|     case VOICE_VOLUME: |  | ||||||
|         { |  | ||||||
|             SWVoiceIn *sw; |  | ||||||
|             va_list ap; |  | ||||||
|  |  | ||||||
|             va_start (ap, cmd); |  | ||||||
|             sw = va_arg (ap, SWVoiceIn *); |  | ||||||
|             va_end (ap); |  | ||||||
|  |  | ||||||
|             v.channels = 2; |  | ||||||
|             v.values[0] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.l) / UINT32_MAX; |  | ||||||
|             v.values[1] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.r) / UINT32_MAX; |  | ||||||
|  |  | ||||||
|             pa_threaded_mainloop_lock (g->mainloop); |  | ||||||
|  |  | ||||||
|             op = pa_context_set_source_output_volume (g->context, |  | ||||||
|                 pa_stream_get_index (pa->stream), |  | ||||||
|                 &v, NULL, NULL); |  | ||||||
|             if (!op) { |  | ||||||
|                 qpa_logerr (pa_context_errno (g->context), |  | ||||||
|                             "set_source_output_volume() failed\n"); |  | ||||||
|             } else { |  | ||||||
|                 pa_operation_unref(op); |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             op = pa_context_set_source_output_mute (g->context, |  | ||||||
|                 pa_stream_get_index (pa->stream), |  | ||||||
|                 sw->vol.mute, NULL, NULL); |  | ||||||
|             if (!op) { |  | ||||||
|                 qpa_logerr (pa_context_errno (g->context), |  | ||||||
|                             "set_source_output_mute() failed\n"); |  | ||||||
|             } else { |  | ||||||
|                 pa_operation_unref (op); |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             pa_threaded_mainloop_unlock (g->mainloop); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* common */ |  | ||||||
| static PAConf glob_conf = { |  | ||||||
|     .samples = 4096, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static void *qpa_audio_init (void) |  | ||||||
| { |  | ||||||
|     paaudio *g = g_malloc(sizeof(paaudio)); |  | ||||||
|     g->conf = glob_conf; |  | ||||||
|     g->mainloop = NULL; |  | ||||||
|     g->context = NULL; |  | ||||||
|  |  | ||||||
|     g->mainloop = pa_threaded_mainloop_new (); |  | ||||||
|     if (!g->mainloop) { |  | ||||||
|         goto fail; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     g->context = pa_context_new (pa_threaded_mainloop_get_api (g->mainloop), |  | ||||||
|                                  g->conf.server); |  | ||||||
|     if (!g->context) { |  | ||||||
|         goto fail; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     pa_context_set_state_callback (g->context, context_state_cb, g); |  | ||||||
|  |  | ||||||
|     if (pa_context_connect (g->context, g->conf.server, 0, NULL) < 0) { |  | ||||||
|         qpa_logerr (pa_context_errno (g->context), |  | ||||||
|                     "pa_context_connect() failed\n"); |  | ||||||
|         goto fail; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     pa_threaded_mainloop_lock (g->mainloop); |  | ||||||
|  |  | ||||||
|     if (pa_threaded_mainloop_start (g->mainloop) < 0) { |  | ||||||
|         goto unlock_and_fail; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     for (;;) { |  | ||||||
|         pa_context_state_t state; |  | ||||||
|  |  | ||||||
|         state = pa_context_get_state (g->context); |  | ||||||
|  |  | ||||||
|         if (state == PA_CONTEXT_READY) { |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if (!PA_CONTEXT_IS_GOOD (state)) { |  | ||||||
|             qpa_logerr (pa_context_errno (g->context), |  | ||||||
|                         "Wrong context state\n"); |  | ||||||
|             goto unlock_and_fail; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         /* Wait until the context is ready */ |  | ||||||
|         pa_threaded_mainloop_wait (g->mainloop); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     pa_threaded_mainloop_unlock (g->mainloop); |  | ||||||
|  |  | ||||||
|     return g; |  | ||||||
|  |  | ||||||
| unlock_and_fail: |  | ||||||
|     pa_threaded_mainloop_unlock (g->mainloop); |  | ||||||
| fail: |  | ||||||
|     AUD_log (AUDIO_CAP, "Failed to initialize PA context"); |  | ||||||
|     qpa_audio_fini(g); |  | ||||||
|     return NULL; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void qpa_audio_fini (void *opaque) |  | ||||||
| { |  | ||||||
|     paaudio *g = opaque; |  | ||||||
|  |  | ||||||
|     if (g->mainloop) { |  | ||||||
|         pa_threaded_mainloop_stop (g->mainloop); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (g->context) { |  | ||||||
|         pa_context_disconnect (g->context); |  | ||||||
|         pa_context_unref (g->context); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (g->mainloop) { |  | ||||||
|         pa_threaded_mainloop_free (g->mainloop); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     g_free(g); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| struct audio_option qpa_options[] = { |  | ||||||
|     { |  | ||||||
|         .name  = "SAMPLES", |  | ||||||
|         .tag   = AUD_OPT_INT, |  | ||||||
|         .valp  = &glob_conf.samples, |  | ||||||
|         .descr = "buffer size in samples" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         .name  = "SERVER", |  | ||||||
|         .tag   = AUD_OPT_STR, |  | ||||||
|         .valp  = &glob_conf.server, |  | ||||||
|         .descr = "server address" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         .name  = "SINK", |  | ||||||
|         .tag   = AUD_OPT_STR, |  | ||||||
|         .valp  = &glob_conf.sink, |  | ||||||
|         .descr = "sink device name" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         .name  = "SOURCE", |  | ||||||
|         .tag   = AUD_OPT_STR, |  | ||||||
|         .valp  = &glob_conf.source, |  | ||||||
|         .descr = "source device name" |  | ||||||
|     }, |  | ||||||
|     { /* End of list */ } |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static struct audio_pcm_ops qpa_pcm_ops = { |  | ||||||
|     .init_out = qpa_init_out, |  | ||||||
|     .fini_out = qpa_fini_out, |  | ||||||
|     .run_out  = qpa_run_out, |  | ||||||
|     .write    = qpa_write, |  | ||||||
|     .ctl_out  = qpa_ctl_out, |  | ||||||
|  |  | ||||||
|     .init_in  = qpa_init_in, |  | ||||||
|     .fini_in  = qpa_fini_in, |  | ||||||
|     .run_in   = qpa_run_in, |  | ||||||
|     .read     = qpa_read, |  | ||||||
|     .ctl_in   = qpa_ctl_in |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static struct audio_driver pa_audio_driver = { |  | ||||||
|     .name           = "pa", |  | ||||||
|     .descr          = "http://www.pulseaudio.org/", |  | ||||||
|     .options        = qpa_options, |  | ||||||
|     .init           = qpa_audio_init, |  | ||||||
|     .fini           = qpa_audio_fini, |  | ||||||
|     .pcm_ops        = &qpa_pcm_ops, |  | ||||||
|     .can_be_default = 1, |  | ||||||
|     .max_voices_out = INT_MAX, |  | ||||||
|     .max_voices_in  = INT_MAX, |  | ||||||
|     .voice_size_out = sizeof (PAVoiceOut), |  | ||||||
|     .voice_size_in  = sizeof (PAVoiceIn), |  | ||||||
|     .ctl_caps       = VOICE_VOLUME_CAP |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static void register_audio_pa(void) |  | ||||||
| { |  | ||||||
|     audio_driver_register(&pa_audio_driver); |  | ||||||
| } |  | ||||||
| type_init(register_audio_pa); |  | ||||||
| @@ -27,15 +27,15 @@ | |||||||
|  * Processed signed long samples from ibuf to obuf. |  * Processed signed long samples from ibuf to obuf. | ||||||
|  * 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, st_sample_t *ibuf, st_sample_t *obuf, | ||||||
|            int *isamp, int *osamp) |            int *isamp, int *osamp) | ||||||
| { | { | ||||||
|     struct rate *rate = opaque; |     struct rate *rate = opaque; | ||||||
|     struct st_sample *istart, *iend; |     st_sample_t *istart, *iend; | ||||||
|     struct st_sample *ostart, *oend; |     st_sample_t *ostart, *oend; | ||||||
|     struct st_sample ilast, icur, out; |     st_sample_t ilast, icur, out; | ||||||
| #ifdef FLOAT_MIXENG | #ifdef FLOAT_MIXENG | ||||||
|     mixeng_real t; |     real_t t; | ||||||
| #else | #else | ||||||
|     int64_t t; |     int64_t t; | ||||||
| #endif | #endif | ||||||
| @@ -71,12 +71,6 @@ void NAME (void *opaque, struct st_sample *ibuf, struct st_sample *obuf, | |||||||
|         while (rate->ipos <= (rate->opos >> 32)) { |         while (rate->ipos <= (rate->opos >> 32)) { | ||||||
|             ilast = *ibuf++; |             ilast = *ibuf++; | ||||||
|             rate->ipos++; |             rate->ipos++; | ||||||
|  |  | ||||||
|             /* if ipos overflow, there is  a infinite loop */ |  | ||||||
|             if (rate->ipos == 0xffffffff) { |  | ||||||
|                 rate->ipos = 1; |  | ||||||
|                 rate->opos = rate->opos & 0xffffffff; |  | ||||||
|             } |  | ||||||
|             /* See if we finished the input buffer yet */ |             /* See if we finished the input buffer yet */ | ||||||
|             if (ibuf >= iend) { |             if (ibuf >= iend) { | ||||||
|                 goto the_end; |                 goto the_end; | ||||||
| @@ -90,7 +84,7 @@ void NAME (void *opaque, struct st_sample *ibuf, struct st_sample *obuf, | |||||||
| #ifdef RECIPROCAL | #ifdef RECIPROCAL | ||||||
|         t = (rate->opos & UINT_MAX) * (1.f / UINT_MAX); |         t = (rate->opos & UINT_MAX) * (1.f / UINT_MAX); | ||||||
| #else | #else | ||||||
|         t = (rate->opos & UINT_MAX) / (mixeng_real) UINT_MAX; |         t = (rate->opos & UINT_MAX) / (real_t) UINT_MAX; | ||||||
| #endif | #endif | ||||||
|         out.l = (ilast.l * (1.0 - t)) + icur.l * t; |         out.l = (ilast.l * (1.0 - t)) + icur.l * t; | ||||||
|         out.r = (ilast.r * (1.0 - t)) + icur.r * t; |         out.r = (ilast.r * (1.0 - t)) + icur.r * t; | ||||||
|   | |||||||
							
								
								
									
										193
									
								
								audio/sdlaudio.c
									
									
									
									
									
								
							
							
						
						
									
										193
									
								
								audio/sdlaudio.c
									
									
									
									
									
								
							| @@ -21,7 +21,6 @@ | |||||||
|  * 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 <SDL.h> | #include <SDL.h> | ||||||
| #include <SDL_thread.h> | #include <SDL_thread.h> | ||||||
| #include "qemu-common.h" | #include "qemu-common.h" | ||||||
| @@ -30,39 +29,31 @@ | |||||||
| #ifndef _WIN32 | #ifndef _WIN32 | ||||||
| #ifdef __sun__ | #ifdef __sun__ | ||||||
| #define _POSIX_PTHREAD_SEMANTICS 1 | #define _POSIX_PTHREAD_SEMANTICS 1 | ||||||
| #elif defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__DragonFly__) |  | ||||||
| #include <pthread.h> |  | ||||||
| #endif | #endif | ||||||
|  | #include <signal.h> | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #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; |     int live; | ||||||
| #if USE_SEMAPHORE |  | ||||||
|     int rpos; |     int rpos; | ||||||
| #endif |  | ||||||
|     int decr; |     int decr; | ||||||
| } SDLVoiceOut; | } SDLVoiceOut; | ||||||
|  |  | ||||||
| static struct { | static struct { | ||||||
|     int nb_samples; |     int nb_samples; | ||||||
| } conf = { | } conf = { | ||||||
|     .nb_samples = 1024 |     1024 | ||||||
| }; | }; | ||||||
|  |  | ||||||
| static struct SDLAudioState { | struct SDLAudioState { | ||||||
|     int exit; |     int exit; | ||||||
| #if USE_SEMAPHORE |  | ||||||
|     SDL_mutex *mutex; |     SDL_mutex *mutex; | ||||||
|     SDL_sem *sem; |     SDL_sem *sem; | ||||||
| #endif |  | ||||||
|     int initialized; |     int initialized; | ||||||
|     bool driver_created; |  | ||||||
| } glob_sdl; | } glob_sdl; | ||||||
| typedef struct SDLAudioState SDLAudioState; | typedef struct SDLAudioState SDLAudioState; | ||||||
|  |  | ||||||
| @@ -79,45 +70,31 @@ static void GCC_FMT_ATTR (1, 2) sdl_logerr (const char *fmt, ...) | |||||||
|  |  | ||||||
| static int sdl_lock (SDLAudioState *s, const char *forfn) | static int sdl_lock (SDLAudioState *s, const char *forfn) | ||||||
| { | { | ||||||
| #if USE_SEMAPHORE |  | ||||||
|     if (SDL_LockMutex (s->mutex)) { |     if (SDL_LockMutex (s->mutex)) { | ||||||
|         sdl_logerr ("SDL_LockMutex for %s failed\n", forfn); |         sdl_logerr ("SDL_LockMutex for %s failed\n", forfn); | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
| #else |  | ||||||
|     SDL_LockAudio(); |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| static int sdl_unlock (SDLAudioState *s, const char *forfn) | static int sdl_unlock (SDLAudioState *s, const char *forfn) | ||||||
| { | { | ||||||
| #if USE_SEMAPHORE |  | ||||||
|     if (SDL_UnlockMutex (s->mutex)) { |     if (SDL_UnlockMutex (s->mutex)) { | ||||||
|         sdl_logerr ("SDL_UnlockMutex for %s failed\n", forfn); |         sdl_logerr ("SDL_UnlockMutex for %s failed\n", forfn); | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
| #else |  | ||||||
|     SDL_UnlockAudio(); |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| static int sdl_post (SDLAudioState *s, const char *forfn) | static int sdl_post (SDLAudioState *s, const char *forfn) | ||||||
| { | { | ||||||
| #if USE_SEMAPHORE |  | ||||||
|     if (SDL_SemPost (s->sem)) { |     if (SDL_SemPost (s->sem)) { | ||||||
|         sdl_logerr ("SDL_SemPost for %s failed\n", forfn); |         sdl_logerr ("SDL_SemPost for %s failed\n", forfn); | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
| #endif |  | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| #if USE_SEMAPHORE |  | ||||||
| static int sdl_wait (SDLAudioState *s, const char *forfn) | static int sdl_wait (SDLAudioState *s, const char *forfn) | ||||||
| { | { | ||||||
|     if (SDL_SemWait (s->sem)) { |     if (SDL_SemWait (s->sem)) { | ||||||
| @@ -126,7 +103,6 @@ static int sdl_wait (SDLAudioState *s, const char *forfn) | |||||||
|     } |     } | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
| #endif |  | ||||||
|  |  | ||||||
| static int sdl_unlock_and_post (SDLAudioState *s, const char *forfn) | static int sdl_unlock_and_post (SDLAudioState *s, const char *forfn) | ||||||
| { | { | ||||||
| @@ -137,19 +113,23 @@ static int sdl_unlock_and_post (SDLAudioState *s, const char *forfn) | |||||||
|     return sdl_post (s, forfn); |     return sdl_post (s, forfn); | ||||||
| } | } | ||||||
|  |  | ||||||
| static int aud_to_sdlfmt (audfmt_e fmt) | static int aud_to_sdlfmt (audfmt_e fmt, int *shift) | ||||||
| { | { | ||||||
|     switch (fmt) { |     switch (fmt) { | ||||||
|     case AUD_FMT_S8: |     case AUD_FMT_S8: | ||||||
|  |         *shift = 0; | ||||||
|         return AUDIO_S8; |         return AUDIO_S8; | ||||||
|  |  | ||||||
|     case AUD_FMT_U8: |     case AUD_FMT_U8: | ||||||
|  |         *shift = 0; | ||||||
|         return AUDIO_U8; |         return AUDIO_U8; | ||||||
|  |  | ||||||
|     case AUD_FMT_S16: |     case AUD_FMT_S16: | ||||||
|  |         *shift = 1; | ||||||
|         return AUDIO_S16LSB; |         return AUDIO_S16LSB; | ||||||
|  |  | ||||||
|     case AUD_FMT_U16: |     case AUD_FMT_U16: | ||||||
|  |         *shift = 1; | ||||||
|         return AUDIO_U16LSB; |         return AUDIO_U16LSB; | ||||||
|  |  | ||||||
|     default: |     default: | ||||||
| @@ -161,36 +141,36 @@ static int aud_to_sdlfmt (audfmt_e fmt) | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| static int sdl_to_audfmt(int sdlfmt, audfmt_e *fmt, int *endianness) | static int sdl_to_audfmt (int sdlfmt, audfmt_e *fmt, int *endianess) | ||||||
| { | { | ||||||
|     switch (sdlfmt) { |     switch (sdlfmt) { | ||||||
|     case AUDIO_S8: |     case AUDIO_S8: | ||||||
|         *endianness = 0; |         *endianess = 0; | ||||||
|         *fmt = AUD_FMT_S8; |         *fmt = AUD_FMT_S8; | ||||||
|         break; |         break; | ||||||
|  |  | ||||||
|     case AUDIO_U8: |     case AUDIO_U8: | ||||||
|         *endianness = 0; |         *endianess = 0; | ||||||
|         *fmt = AUD_FMT_U8; |         *fmt = AUD_FMT_U8; | ||||||
|         break; |         break; | ||||||
|  |  | ||||||
|     case AUDIO_S16LSB: |     case AUDIO_S16LSB: | ||||||
|         *endianness = 0; |         *endianess = 0; | ||||||
|         *fmt = AUD_FMT_S16; |         *fmt = AUD_FMT_S16; | ||||||
|         break; |         break; | ||||||
|  |  | ||||||
|     case AUDIO_U16LSB: |     case AUDIO_U16LSB: | ||||||
|         *endianness = 0; |         *endianess = 0; | ||||||
|         *fmt = AUD_FMT_U16; |         *fmt = AUD_FMT_U16; | ||||||
|         break; |         break; | ||||||
|  |  | ||||||
|     case AUDIO_S16MSB: |     case AUDIO_S16MSB: | ||||||
|         *endianness = 1; |         *endianess = 1; | ||||||
|         *fmt = AUD_FMT_S16; |         *fmt = AUD_FMT_S16; | ||||||
|         break; |         break; | ||||||
|  |  | ||||||
|     case AUDIO_U16MSB: |     case AUDIO_U16MSB: | ||||||
|         *endianness = 1; |         *endianess = 1; | ||||||
|         *fmt = AUD_FMT_U16; |         *fmt = AUD_FMT_U16; | ||||||
|         break; |         break; | ||||||
|  |  | ||||||
| @@ -206,20 +186,11 @@ static int sdl_open (SDL_AudioSpec *req, SDL_AudioSpec *obt) | |||||||
| { | { | ||||||
|     int status; |     int status; | ||||||
| #ifndef _WIN32 | #ifndef _WIN32 | ||||||
|     int err; |  | ||||||
|     sigset_t new, old; |     sigset_t new, old; | ||||||
|  |  | ||||||
|     /* Make sure potential threads created by SDL don't hog signals.  */ |     /* Make sure potential threads created by SDL don't hog signals.  */ | ||||||
|     err = sigfillset (&new); |     sigfillset (&new); | ||||||
|     if (err) { |     pthread_sigmask (SIG_BLOCK, &new, &old); | ||||||
|         dolog ("sdl_open: sigfillset failed: %s\n", strerror (errno)); |  | ||||||
|         return -1; |  | ||||||
|     } |  | ||||||
|     err = pthread_sigmask (SIG_BLOCK, &new, &old); |  | ||||||
|     if (err) { |  | ||||||
|         dolog ("sdl_open: pthread_sigmask failed: %s\n", strerror (err)); |  | ||||||
|         return -1; |  | ||||||
|     } |  | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|     status = SDL_OpenAudio (req, obt); |     status = SDL_OpenAudio (req, obt); | ||||||
| @@ -228,14 +199,7 @@ static int sdl_open (SDL_AudioSpec *req, SDL_AudioSpec *obt) | |||||||
|     } |     } | ||||||
|  |  | ||||||
| #ifndef _WIN32 | #ifndef _WIN32 | ||||||
|     err = pthread_sigmask (SIG_SETMASK, &old, NULL); |     pthread_sigmask (SIG_SETMASK, &old, 0); | ||||||
|     if (err) { |  | ||||||
|         dolog ("sdl_open: pthread_sigmask (restore) failed: %s\n", |  | ||||||
|                strerror (errno)); |  | ||||||
|         /* We have failed to restore original signal mask, all bets are off, |  | ||||||
|            so exit the process */ |  | ||||||
|         exit (EXIT_FAILURE); |  | ||||||
|     } |  | ||||||
| #endif | #endif | ||||||
|     return status; |     return status; | ||||||
| } | } | ||||||
| @@ -267,7 +231,6 @@ static void sdl_callback (void *opaque, Uint8 *buf, int len) | |||||||
|         int to_mix, decr; |         int to_mix, decr; | ||||||
|  |  | ||||||
|         /* dolog ("in callback samples=%d\n", samples); */ |         /* dolog ("in callback samples=%d\n", samples); */ | ||||||
| #if USE_SEMAPHORE |  | ||||||
|         sdl_wait (s, "sdl_callback"); |         sdl_wait (s, "sdl_callback"); | ||||||
|         if (s->exit) { |         if (s->exit) { | ||||||
|             return; |             return; | ||||||
| @@ -277,7 +240,7 @@ static void sdl_callback (void *opaque, Uint8 *buf, int len) | |||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (audio_bug(__func__, sdl->live < 0 || sdl->live > hw->samples)) { |         if (audio_bug (AUDIO_FUNC, sdl->live < 0 || sdl->live > hw->samples)) { | ||||||
|             dolog ("sdl->live=%d hw->samples=%d\n", |             dolog ("sdl->live=%d hw->samples=%d\n", | ||||||
|                    sdl->live, hw->samples); |                    sdl->live, hw->samples); | ||||||
|             return; |             return; | ||||||
| @@ -286,26 +249,17 @@ static void sdl_callback (void *opaque, Uint8 *buf, int len) | |||||||
|         if (!sdl->live) { |         if (!sdl->live) { | ||||||
|             goto again; |             goto again; | ||||||
|         } |         } | ||||||
| #else |  | ||||||
|         if (s->exit || !sdl->live) { |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
|         /* dolog ("in callback live=%d\n", live); */ |         /* dolog ("in callback live=%d\n", live); */ | ||||||
|         to_mix = audio_MIN (samples, sdl->live); |         to_mix = audio_MIN (samples, sdl->live); | ||||||
|         decr = to_mix; |         decr = to_mix; | ||||||
|         while (to_mix) { |         while (to_mix) { | ||||||
|             int chunk = audio_MIN (to_mix, hw->samples - hw->rpos); |             int chunk = audio_MIN (to_mix, hw->samples - hw->rpos); | ||||||
|             struct st_sample *src = hw->mix_buf + hw->rpos; |             st_sample_t *src = hw->mix_buf + hw->rpos; | ||||||
|  |  | ||||||
|             /* dolog ("in callback to_mix %d, chunk %d\n", to_mix, chunk); */ |             /* dolog ("in callback to_mix %d, chunk %d\n", to_mix, chunk); */ | ||||||
|             hw->clip (buf, src, chunk); |             hw->clip (buf, src, chunk); | ||||||
| #if USE_SEMAPHORE |  | ||||||
|             sdl->rpos = (sdl->rpos + chunk) % hw->samples; |             sdl->rpos = (sdl->rpos + chunk) % hw->samples; | ||||||
| #else |  | ||||||
|             hw->rpos = (hw->rpos + chunk) % hw->samples; |  | ||||||
| #endif |  | ||||||
|             to_mix -= chunk; |             to_mix -= chunk; | ||||||
|             buf += chunk << hw->info.shift; |             buf += chunk << hw->info.shift; | ||||||
|         } |         } | ||||||
| @@ -313,21 +267,12 @@ static void sdl_callback (void *opaque, Uint8 *buf, int len) | |||||||
|         sdl->live -= decr; |         sdl->live -= decr; | ||||||
|         sdl->decr += decr; |         sdl->decr += decr; | ||||||
|  |  | ||||||
| #if USE_SEMAPHORE |  | ||||||
|     again: |     again: | ||||||
|         if (sdl_unlock (s, "sdl_callback")) { |         if (sdl_unlock (s, "sdl_callback")) { | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
| #endif |  | ||||||
|     } |     } | ||||||
|     /* dolog ("done len=%d\n", len); */ |     /* dolog ("done len=%d\n", len); */ | ||||||
|  |  | ||||||
| #if (SDL_MAJOR_VERSION >= 2) |  | ||||||
|     /* SDL2 does not clear the remaining buffer for us, so do it on our own */ |  | ||||||
|     if (samples) { |  | ||||||
|         memset(buf, 0, samples << hw->info.shift); |  | ||||||
|     } |  | ||||||
| #endif |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static int sdl_write_out (SWVoiceOut *sw, void *buf, int len) | static int sdl_write_out (SWVoiceOut *sw, void *buf, int len) | ||||||
| @@ -335,16 +280,18 @@ static int sdl_write_out (SWVoiceOut *sw, void *buf, int len) | |||||||
|     return audio_pcm_sw_write (sw, buf, len); |     return audio_pcm_sw_write (sw, buf, len); | ||||||
| } | } | ||||||
|  |  | ||||||
| static int sdl_run_out (HWVoiceOut *hw, int live) | static int sdl_run_out (HWVoiceOut *hw) | ||||||
| { | { | ||||||
|     int decr; |     int decr, live; | ||||||
|     SDLVoiceOut *sdl = (SDLVoiceOut *) hw; |     SDLVoiceOut *sdl = (SDLVoiceOut *) hw; | ||||||
|     SDLAudioState *s = &glob_sdl; |     SDLAudioState *s = &glob_sdl; | ||||||
|  |  | ||||||
|     if (sdl_lock (s, "sdl_run_out")) { |     if (sdl_lock (s, "sdl_callback")) { | ||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     live = audio_pcm_hw_get_live_out (hw); | ||||||
|  |  | ||||||
|     if (sdl->decr > live) { |     if (sdl->decr > live) { | ||||||
|         ldebug ("sdl->decr %d live %d sdl->live %d\n", |         ldebug ("sdl->decr %d live %d sdl->live %d\n", | ||||||
|                 sdl->decr, |                 sdl->decr, | ||||||
| @@ -355,18 +302,14 @@ static int sdl_run_out (HWVoiceOut *hw, int live) | |||||||
|     decr = audio_MIN (sdl->decr, live); |     decr = audio_MIN (sdl->decr, live); | ||||||
|     sdl->decr -= decr; |     sdl->decr -= decr; | ||||||
|  |  | ||||||
| #if USE_SEMAPHORE |  | ||||||
|     sdl->live = live - decr; |     sdl->live = live - decr; | ||||||
|     hw->rpos = sdl->rpos; |     hw->rpos = sdl->rpos; | ||||||
| #else |  | ||||||
|     sdl->live = live; |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
|     if (sdl->live > 0) { |     if (sdl->live > 0) { | ||||||
|         sdl_unlock_and_post (s, "sdl_run_out"); |         sdl_unlock_and_post (s, "sdl_callback"); | ||||||
|     } |     } | ||||||
|     else { |     else { | ||||||
|         sdl_unlock (s, "sdl_run_out"); |         sdl_unlock (s, "sdl_callback"); | ||||||
|     } |     } | ||||||
|     return decr; |     return decr; | ||||||
| } | } | ||||||
| @@ -378,19 +321,21 @@ static void sdl_fini_out (HWVoiceOut *hw) | |||||||
|     sdl_close (&glob_sdl); |     sdl_close (&glob_sdl); | ||||||
| } | } | ||||||
|  |  | ||||||
| static int sdl_init_out(HWVoiceOut *hw, struct audsettings *as, | static int sdl_init_out (HWVoiceOut *hw, audsettings_t *as) | ||||||
|                         void *drv_opaque) |  | ||||||
| { | { | ||||||
|     SDLVoiceOut *sdl = (SDLVoiceOut *) hw; |     SDLVoiceOut *sdl = (SDLVoiceOut *) hw; | ||||||
|     SDLAudioState *s = &glob_sdl; |     SDLAudioState *s = &glob_sdl; | ||||||
|     SDL_AudioSpec req, obt; |     SDL_AudioSpec req, obt; | ||||||
|     int endianness; |     int shift; | ||||||
|  |     int endianess; | ||||||
|     int err; |     int err; | ||||||
|     audfmt_e effective_fmt; |     audfmt_e effective_fmt; | ||||||
|     struct audsettings obt_as; |     audsettings_t obt_as; | ||||||
|  |  | ||||||
|  |     shift <<= as->nchannels == 2; | ||||||
|  |  | ||||||
|     req.freq = as->freq; |     req.freq = as->freq; | ||||||
|     req.format = aud_to_sdlfmt (as->fmt); |     req.format = aud_to_sdlfmt (as->fmt, &shift); | ||||||
|     req.channels = as->nchannels; |     req.channels = as->nchannels; | ||||||
|     req.samples = conf.nb_samples; |     req.samples = conf.nb_samples; | ||||||
|     req.callback = sdl_callback; |     req.callback = sdl_callback; | ||||||
| @@ -400,7 +345,7 @@ static int sdl_init_out(HWVoiceOut *hw, struct audsettings *as, | |||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     err = sdl_to_audfmt(obt.format, &effective_fmt, &endianness); |     err = sdl_to_audfmt (obt.format, &effective_fmt, &endianess); | ||||||
|     if (err) { |     if (err) { | ||||||
|         sdl_close (s); |         sdl_close (s); | ||||||
|         return -1; |         return -1; | ||||||
| @@ -409,7 +354,7 @@ static int sdl_init_out(HWVoiceOut *hw, struct audsettings *as, | |||||||
|     obt_as.freq = obt.freq; |     obt_as.freq = obt.freq; | ||||||
|     obt_as.nchannels = obt.channels; |     obt_as.nchannels = obt.channels; | ||||||
|     obt_as.fmt = effective_fmt; |     obt_as.fmt = effective_fmt; | ||||||
|     obt_as.endianness = endianness; |     obt_as.endianness = endianess; | ||||||
|  |  | ||||||
|     audio_pcm_init_info (&hw->info, &obt_as); |     audio_pcm_init_info (&hw->info, &obt_as); | ||||||
|     hw->samples = obt.samples; |     hw->samples = obt.samples; | ||||||
| @@ -439,17 +384,12 @@ static int sdl_ctl_out (HWVoiceOut *hw, int cmd, ...) | |||||||
| static void *sdl_audio_init (void) | static void *sdl_audio_init (void) | ||||||
| { | { | ||||||
|     SDLAudioState *s = &glob_sdl; |     SDLAudioState *s = &glob_sdl; | ||||||
|     if (s->driver_created) { |  | ||||||
|         sdl_logerr("Can't create multiple sdl backends\n"); |  | ||||||
|         return NULL; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (SDL_InitSubSystem (SDL_INIT_AUDIO)) { |     if (SDL_InitSubSystem (SDL_INIT_AUDIO)) { | ||||||
|         sdl_logerr ("SDL failed to initialize audio subsystem\n"); |         sdl_logerr ("SDL failed to initialize audio subsystem\n"); | ||||||
|         return NULL; |         return NULL; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| #if USE_SEMAPHORE |  | ||||||
|     s->mutex = SDL_CreateMutex (); |     s->mutex = SDL_CreateMutex (); | ||||||
|     if (!s->mutex) { |     if (!s->mutex) { | ||||||
|         sdl_logerr ("Failed to create SDL mutex\n"); |         sdl_logerr ("Failed to create SDL mutex\n"); | ||||||
| @@ -464,9 +404,7 @@ static void *sdl_audio_init (void) | |||||||
|         SDL_QuitSubSystem (SDL_INIT_AUDIO); |         SDL_QuitSubSystem (SDL_INIT_AUDIO); | ||||||
|         return NULL; |         return NULL; | ||||||
|     } |     } | ||||||
| #endif |  | ||||||
|  |  | ||||||
|     s->driver_created = true; |  | ||||||
|     return s; |     return s; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -474,48 +412,41 @@ 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_DestroySemaphore (s->sem); | ||||||
|     SDL_DestroyMutex (s->mutex); |     SDL_DestroyMutex (s->mutex); | ||||||
| #endif |  | ||||||
|     SDL_QuitSubSystem (SDL_INIT_AUDIO); |     SDL_QuitSubSystem (SDL_INIT_AUDIO); | ||||||
|     s->driver_created = false; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static struct audio_option sdl_options[] = { | static struct audio_option sdl_options[] = { | ||||||
|     { |     {"SAMPLES", AUD_OPT_INT, &conf.nb_samples, | ||||||
|         .name  = "SAMPLES", |      "Size of SDL buffer in samples", NULL, 0}, | ||||||
|         .tag   = AUD_OPT_INT, |     {NULL, 0, NULL, NULL, NULL, 0} | ||||||
|         .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, |     sdl_init_out, | ||||||
|     .fini_out = sdl_fini_out, |     sdl_fini_out, | ||||||
|     .run_out  = sdl_run_out, |     sdl_run_out, | ||||||
|     .write    = sdl_write_out, |     sdl_write_out, | ||||||
|     .ctl_out  = sdl_ctl_out, |     sdl_ctl_out, | ||||||
|  |  | ||||||
|  |     NULL, | ||||||
|  |     NULL, | ||||||
|  |     NULL, | ||||||
|  |     NULL, | ||||||
|  |     NULL | ||||||
| }; | }; | ||||||
|  |  | ||||||
| static struct audio_driver sdl_audio_driver = { | struct audio_driver sdl_audio_driver = { | ||||||
|     .name           = "sdl", |     INIT_FIELD (name           = ) "sdl", | ||||||
|     .descr          = "SDL http://www.libsdl.org", |     INIT_FIELD (descr          = ) "SDL http://www.libsdl.org", | ||||||
|     .options        = sdl_options, |     INIT_FIELD (options        = ) sdl_options, | ||||||
|     .init           = sdl_audio_init, |     INIT_FIELD (init           = ) sdl_audio_init, | ||||||
|     .fini           = sdl_audio_fini, |     INIT_FIELD (fini           = ) sdl_audio_fini, | ||||||
|     .pcm_ops        = &sdl_pcm_ops, |     INIT_FIELD (pcm_ops        = ) &sdl_pcm_ops, | ||||||
|     .can_be_default = 1, |     INIT_FIELD (can_be_default = ) 1, | ||||||
|     .max_voices_out = 1, |     INIT_FIELD (max_voices_out = ) 1, | ||||||
|     .max_voices_in  = 0, |     INIT_FIELD (max_voices_in  = ) 0, | ||||||
|     .voice_size_out = sizeof (SDLVoiceOut), |     INIT_FIELD (voice_size_out = ) sizeof (SDLVoiceOut), | ||||||
|     .voice_size_in  = 0 |     INIT_FIELD (voice_size_in  = ) 0 | ||||||
| }; | }; | ||||||
|  |  | ||||||
| static void register_audio_sdl(void) |  | ||||||
| { |  | ||||||
|     audio_driver_register(&sdl_audio_driver); |  | ||||||
| } |  | ||||||
| type_init(register_audio_sdl); |  | ||||||
|   | |||||||
| @@ -1,419 +0,0 @@ | |||||||
| /* |  | ||||||
|  * Copyright (C) 2010 Red Hat, Inc. |  | ||||||
|  * |  | ||||||
|  * maintained by Gerd Hoffmann <kraxel@redhat.com> |  | ||||||
|  * |  | ||||||
|  * This program is free software; you can redistribute it and/or |  | ||||||
|  * modify it under the terms of the GNU General Public License as |  | ||||||
|  * published by the Free Software Foundation; either version 2 or |  | ||||||
|  * (at your option) version 3 of the License. |  | ||||||
|  * |  | ||||||
|  * This program is distributed in the hope that it will be useful, |  | ||||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of |  | ||||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the |  | ||||||
|  * GNU General Public License for more details. |  | ||||||
|  * |  | ||||||
|  * You should have received a copy of the GNU General Public License |  | ||||||
|  * along with this program; if not, see <http://www.gnu.org/licenses/>. |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #include "qemu/osdep.h" |  | ||||||
| #include "hw/hw.h" |  | ||||||
| #include "qemu/host-utils.h" |  | ||||||
| #include "qemu/error-report.h" |  | ||||||
| #include "qemu/timer.h" |  | ||||||
| #include "ui/qemu-spice.h" |  | ||||||
|  |  | ||||||
| #define AUDIO_CAP "spice" |  | ||||||
| #include "audio.h" |  | ||||||
| #include "audio_int.h" |  | ||||||
|  |  | ||||||
| #if SPICE_INTERFACE_PLAYBACK_MAJOR > 1 || SPICE_INTERFACE_PLAYBACK_MINOR >= 3 |  | ||||||
| #define LINE_OUT_SAMPLES (480 * 4) |  | ||||||
| #else |  | ||||||
| #define LINE_OUT_SAMPLES (256 * 4) |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| #if SPICE_INTERFACE_RECORD_MAJOR > 2 || SPICE_INTERFACE_RECORD_MINOR >= 3 |  | ||||||
| #define LINE_IN_SAMPLES (480 * 4) |  | ||||||
| #else |  | ||||||
| #define LINE_IN_SAMPLES (256 * 4) |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| typedef struct SpiceRateCtl { |  | ||||||
|     int64_t               start_ticks; |  | ||||||
|     int64_t               bytes_sent; |  | ||||||
| } SpiceRateCtl; |  | ||||||
|  |  | ||||||
| typedef struct SpiceVoiceOut { |  | ||||||
|     HWVoiceOut            hw; |  | ||||||
|     SpicePlaybackInstance sin; |  | ||||||
|     SpiceRateCtl          rate; |  | ||||||
|     int                   active; |  | ||||||
|     uint32_t              *frame; |  | ||||||
|     uint32_t              *fpos; |  | ||||||
|     uint32_t              fsize; |  | ||||||
| } SpiceVoiceOut; |  | ||||||
|  |  | ||||||
| typedef struct SpiceVoiceIn { |  | ||||||
|     HWVoiceIn             hw; |  | ||||||
|     SpiceRecordInstance   sin; |  | ||||||
|     SpiceRateCtl          rate; |  | ||||||
|     int                   active; |  | ||||||
|     uint32_t              samples[LINE_IN_SAMPLES]; |  | ||||||
| } SpiceVoiceIn; |  | ||||||
|  |  | ||||||
| static const SpicePlaybackInterface playback_sif = { |  | ||||||
|     .base.type          = SPICE_INTERFACE_PLAYBACK, |  | ||||||
|     .base.description   = "playback", |  | ||||||
|     .base.major_version = SPICE_INTERFACE_PLAYBACK_MAJOR, |  | ||||||
|     .base.minor_version = SPICE_INTERFACE_PLAYBACK_MINOR, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static const SpiceRecordInterface record_sif = { |  | ||||||
|     .base.type          = SPICE_INTERFACE_RECORD, |  | ||||||
|     .base.description   = "record", |  | ||||||
|     .base.major_version = SPICE_INTERFACE_RECORD_MAJOR, |  | ||||||
|     .base.minor_version = SPICE_INTERFACE_RECORD_MINOR, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static void *spice_audio_init (void) |  | ||||||
| { |  | ||||||
|     if (!using_spice) { |  | ||||||
|         return NULL; |  | ||||||
|     } |  | ||||||
|     return &spice_audio_init; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void spice_audio_fini (void *opaque) |  | ||||||
| { |  | ||||||
|     /* nothing */ |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void rate_start (SpiceRateCtl *rate) |  | ||||||
| { |  | ||||||
|     memset (rate, 0, sizeof (*rate)); |  | ||||||
|     rate->start_ticks = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int rate_get_samples (struct audio_pcm_info *info, SpiceRateCtl *rate) |  | ||||||
| { |  | ||||||
|     int64_t now; |  | ||||||
|     int64_t ticks; |  | ||||||
|     int64_t bytes; |  | ||||||
|     int64_t samples; |  | ||||||
|  |  | ||||||
|     now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); |  | ||||||
|     ticks = now - rate->start_ticks; |  | ||||||
|     bytes = muldiv64(ticks, info->bytes_per_second, NANOSECONDS_PER_SECOND); |  | ||||||
|     samples = (bytes - rate->bytes_sent) >> info->shift; |  | ||||||
|     if (samples < 0 || samples > 65536) { |  | ||||||
|         error_report("Resetting rate control (%" PRId64 " samples)", samples); |  | ||||||
|         rate_start(rate); |  | ||||||
|         samples = 0; |  | ||||||
|     } |  | ||||||
|     rate->bytes_sent += samples << info->shift; |  | ||||||
|     return samples; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* playback */ |  | ||||||
|  |  | ||||||
| static int line_out_init(HWVoiceOut *hw, struct audsettings *as, |  | ||||||
|                          void *drv_opaque) |  | ||||||
| { |  | ||||||
|     SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw); |  | ||||||
|     struct audsettings settings; |  | ||||||
|  |  | ||||||
| #if SPICE_INTERFACE_PLAYBACK_MAJOR > 1 || SPICE_INTERFACE_PLAYBACK_MINOR >= 3 |  | ||||||
|     settings.freq       = spice_server_get_best_playback_rate(NULL); |  | ||||||
| #else |  | ||||||
|     settings.freq       = SPICE_INTERFACE_PLAYBACK_FREQ; |  | ||||||
| #endif |  | ||||||
|     settings.nchannels  = SPICE_INTERFACE_PLAYBACK_CHAN; |  | ||||||
|     settings.fmt        = AUD_FMT_S16; |  | ||||||
|     settings.endianness = AUDIO_HOST_ENDIANNESS; |  | ||||||
|  |  | ||||||
|     audio_pcm_init_info (&hw->info, &settings); |  | ||||||
|     hw->samples = LINE_OUT_SAMPLES; |  | ||||||
|     out->active = 0; |  | ||||||
|  |  | ||||||
|     out->sin.base.sif = &playback_sif.base; |  | ||||||
|     qemu_spice_add_interface (&out->sin.base); |  | ||||||
| #if SPICE_INTERFACE_PLAYBACK_MAJOR > 1 || SPICE_INTERFACE_PLAYBACK_MINOR >= 3 |  | ||||||
|     spice_server_set_playback_rate(&out->sin, settings.freq); |  | ||||||
| #endif |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void line_out_fini (HWVoiceOut *hw) |  | ||||||
| { |  | ||||||
|     SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw); |  | ||||||
|  |  | ||||||
|     spice_server_remove_interface (&out->sin.base); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int line_out_run (HWVoiceOut *hw, int live) |  | ||||||
| { |  | ||||||
|     SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw); |  | ||||||
|     int rpos, decr; |  | ||||||
|     int samples; |  | ||||||
|  |  | ||||||
|     if (!live) { |  | ||||||
|         return 0; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     decr = rate_get_samples (&hw->info, &out->rate); |  | ||||||
|     decr = audio_MIN (live, decr); |  | ||||||
|  |  | ||||||
|     samples = decr; |  | ||||||
|     rpos = hw->rpos; |  | ||||||
|     while (samples) { |  | ||||||
|         int left_till_end_samples = hw->samples - rpos; |  | ||||||
|         int len = audio_MIN (samples, left_till_end_samples); |  | ||||||
|  |  | ||||||
|         if (!out->frame) { |  | ||||||
|             spice_server_playback_get_buffer (&out->sin, &out->frame, &out->fsize); |  | ||||||
|             out->fpos = out->frame; |  | ||||||
|         } |  | ||||||
|         if (out->frame) { |  | ||||||
|             len = audio_MIN (len, out->fsize); |  | ||||||
|             hw->clip (out->fpos, hw->mix_buf + rpos, len); |  | ||||||
|             out->fsize -= len; |  | ||||||
|             out->fpos  += len; |  | ||||||
|             if (out->fsize == 0) { |  | ||||||
|                 spice_server_playback_put_samples (&out->sin, out->frame); |  | ||||||
|                 out->frame = out->fpos = NULL; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         rpos = (rpos + len) % hw->samples; |  | ||||||
|         samples -= len; |  | ||||||
|     } |  | ||||||
|     hw->rpos = rpos; |  | ||||||
|     return decr; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int line_out_write (SWVoiceOut *sw, void *buf, int len) |  | ||||||
| { |  | ||||||
|     return audio_pcm_sw_write (sw, buf, len); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int line_out_ctl (HWVoiceOut *hw, int cmd, ...) |  | ||||||
| { |  | ||||||
|     SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw); |  | ||||||
|  |  | ||||||
|     switch (cmd) { |  | ||||||
|     case VOICE_ENABLE: |  | ||||||
|         if (out->active) { |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|         out->active = 1; |  | ||||||
|         rate_start (&out->rate); |  | ||||||
|         spice_server_playback_start (&out->sin); |  | ||||||
|         break; |  | ||||||
|     case VOICE_DISABLE: |  | ||||||
|         if (!out->active) { |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|         out->active = 0; |  | ||||||
|         if (out->frame) { |  | ||||||
|             memset (out->fpos, 0, out->fsize << 2); |  | ||||||
|             spice_server_playback_put_samples (&out->sin, out->frame); |  | ||||||
|             out->frame = out->fpos = NULL; |  | ||||||
|         } |  | ||||||
|         spice_server_playback_stop (&out->sin); |  | ||||||
|         break; |  | ||||||
|     case VOICE_VOLUME: |  | ||||||
|         { |  | ||||||
| #if ((SPICE_INTERFACE_PLAYBACK_MAJOR >= 1) && (SPICE_INTERFACE_PLAYBACK_MINOR >= 2)) |  | ||||||
|             SWVoiceOut *sw; |  | ||||||
|             va_list ap; |  | ||||||
|             uint16_t vol[2]; |  | ||||||
|  |  | ||||||
|             va_start (ap, cmd); |  | ||||||
|             sw = va_arg (ap, SWVoiceOut *); |  | ||||||
|             va_end (ap); |  | ||||||
|  |  | ||||||
|             vol[0] = sw->vol.l / ((1ULL << 16) + 1); |  | ||||||
|             vol[1] = sw->vol.r / ((1ULL << 16) + 1); |  | ||||||
|             spice_server_playback_set_volume (&out->sin, 2, vol); |  | ||||||
|             spice_server_playback_set_mute (&out->sin, sw->vol.mute); |  | ||||||
| #endif |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* record */ |  | ||||||
|  |  | ||||||
| static int line_in_init(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) |  | ||||||
| { |  | ||||||
|     SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw); |  | ||||||
|     struct audsettings settings; |  | ||||||
|  |  | ||||||
| #if SPICE_INTERFACE_RECORD_MAJOR > 2 || SPICE_INTERFACE_RECORD_MINOR >= 3 |  | ||||||
|     settings.freq       = spice_server_get_best_record_rate(NULL); |  | ||||||
| #else |  | ||||||
|     settings.freq       = SPICE_INTERFACE_RECORD_FREQ; |  | ||||||
| #endif |  | ||||||
|     settings.nchannels  = SPICE_INTERFACE_RECORD_CHAN; |  | ||||||
|     settings.fmt        = AUD_FMT_S16; |  | ||||||
|     settings.endianness = AUDIO_HOST_ENDIANNESS; |  | ||||||
|  |  | ||||||
|     audio_pcm_init_info (&hw->info, &settings); |  | ||||||
|     hw->samples = LINE_IN_SAMPLES; |  | ||||||
|     in->active = 0; |  | ||||||
|  |  | ||||||
|     in->sin.base.sif = &record_sif.base; |  | ||||||
|     qemu_spice_add_interface (&in->sin.base); |  | ||||||
| #if SPICE_INTERFACE_RECORD_MAJOR > 2 || SPICE_INTERFACE_RECORD_MINOR >= 3 |  | ||||||
|     spice_server_set_record_rate(&in->sin, settings.freq); |  | ||||||
| #endif |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void line_in_fini (HWVoiceIn *hw) |  | ||||||
| { |  | ||||||
|     SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw); |  | ||||||
|  |  | ||||||
|     spice_server_remove_interface (&in->sin.base); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int line_in_run (HWVoiceIn *hw) |  | ||||||
| { |  | ||||||
|     SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw); |  | ||||||
|     int num_samples; |  | ||||||
|     int ready; |  | ||||||
|     int len[2]; |  | ||||||
|     uint64_t delta_samp; |  | ||||||
|     const uint32_t *samples; |  | ||||||
|  |  | ||||||
|     if (!(num_samples = hw->samples - audio_pcm_hw_get_live_in (hw))) { |  | ||||||
|         return 0; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     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 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); |  | ||||||
|  |  | ||||||
|     switch (cmd) { |  | ||||||
|     case VOICE_ENABLE: |  | ||||||
|         if (in->active) { |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|         in->active = 1; |  | ||||||
|         rate_start (&in->rate); |  | ||||||
|         spice_server_record_start (&in->sin); |  | ||||||
|         break; |  | ||||||
|     case VOICE_DISABLE: |  | ||||||
|         if (!in->active) { |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|         in->active = 0; |  | ||||||
|         spice_server_record_stop (&in->sin); |  | ||||||
|         break; |  | ||||||
|     case VOICE_VOLUME: |  | ||||||
|         { |  | ||||||
| #if ((SPICE_INTERFACE_RECORD_MAJOR >= 2) && (SPICE_INTERFACE_RECORD_MINOR >= 2)) |  | ||||||
|             SWVoiceIn *sw; |  | ||||||
|             va_list ap; |  | ||||||
|             uint16_t vol[2]; |  | ||||||
|  |  | ||||||
|             va_start (ap, cmd); |  | ||||||
|             sw = va_arg (ap, SWVoiceIn *); |  | ||||||
|             va_end (ap); |  | ||||||
|  |  | ||||||
|             vol[0] = sw->vol.l / ((1ULL << 16) + 1); |  | ||||||
|             vol[1] = sw->vol.r / ((1ULL << 16) + 1); |  | ||||||
|             spice_server_record_set_volume (&in->sin, 2, vol); |  | ||||||
|             spice_server_record_set_mute (&in->sin, sw->vol.mute); |  | ||||||
| #endif |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static struct audio_option audio_options[] = { |  | ||||||
|     { /* end of list */ }, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static struct audio_pcm_ops audio_callbacks = { |  | ||||||
|     .init_out = line_out_init, |  | ||||||
|     .fini_out = line_out_fini, |  | ||||||
|     .run_out  = line_out_run, |  | ||||||
|     .write    = line_out_write, |  | ||||||
|     .ctl_out  = line_out_ctl, |  | ||||||
|  |  | ||||||
|     .init_in  = line_in_init, |  | ||||||
|     .fini_in  = line_in_fini, |  | ||||||
|     .run_in   = line_in_run, |  | ||||||
|     .read     = line_in_read, |  | ||||||
|     .ctl_in   = line_in_ctl, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static struct audio_driver spice_audio_driver = { |  | ||||||
|     .name           = "spice", |  | ||||||
|     .descr          = "spice audio driver", |  | ||||||
|     .options        = audio_options, |  | ||||||
|     .init           = spice_audio_init, |  | ||||||
|     .fini           = spice_audio_fini, |  | ||||||
|     .pcm_ops        = &audio_callbacks, |  | ||||||
|     .max_voices_out = 1, |  | ||||||
|     .max_voices_in  = 1, |  | ||||||
|     .voice_size_out = sizeof (SpiceVoiceOut), |  | ||||||
|     .voice_size_in  = sizeof (SpiceVoiceIn), |  | ||||||
| #if ((SPICE_INTERFACE_PLAYBACK_MAJOR >= 1) && (SPICE_INTERFACE_PLAYBACK_MINOR >= 2)) |  | ||||||
|     .ctl_caps       = VOICE_VOLUME_CAP |  | ||||||
| #endif |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| void qemu_spice_audio_init (void) |  | ||||||
| { |  | ||||||
|     spice_audio_driver.can_be_default = 1; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void register_audio_spice(void) |  | ||||||
| { |  | ||||||
|     audio_driver_register(&spice_audio_driver); |  | ||||||
| } |  | ||||||
| type_init(register_audio_spice); |  | ||||||
							
								
								
									
										241
									
								
								audio/sys-queue.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										241
									
								
								audio/sys-queue.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,241 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright (c) 1991, 1993 | ||||||
|  |  *	The Regents of the University of California.  All rights reserved. | ||||||
|  |  * | ||||||
|  |  * Redistribution and use in source and binary forms, with or without | ||||||
|  |  * modification, are permitted provided that the following conditions | ||||||
|  |  * are met: | ||||||
|  |  * 1. Redistributions of source code must retain the above copyright | ||||||
|  |  *    notice, this list of conditions and the following disclaimer. | ||||||
|  |  * 2. Redistributions in binary form must reproduce the above copyright | ||||||
|  |  *    notice, this list of conditions and the following disclaimer in the | ||||||
|  |  *    documentation and/or other materials provided with the distribution. | ||||||
|  |  * 4. Neither the name of the University nor the names of its contributors | ||||||
|  |  *    may be used to endorse or promote products derived from this software | ||||||
|  |  *    without specific prior written permission. | ||||||
|  |  * | ||||||
|  |  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | ||||||
|  |  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||||||
|  |  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||||||
|  |  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | ||||||
|  |  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||||||
|  |  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | ||||||
|  |  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||||||
|  |  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||||||
|  |  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||||||
|  |  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | ||||||
|  |  * SUCH DAMAGE. | ||||||
|  |  * | ||||||
|  |  *	@(#)queue.h	8.3 (Berkeley) 12/13/93 | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #ifndef	_SYS_QUEUE_H | ||||||
|  | #define	_SYS_QUEUE_H 1 | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * This file defines three types of data structures: lists, tail queues, | ||||||
|  |  * and circular queues. | ||||||
|  |  * | ||||||
|  |  * A list is headed by a single forward pointer (or an array of forward | ||||||
|  |  * pointers for a hash table header). The elements are doubly linked | ||||||
|  |  * so that an arbitrary element can be removed without a need to | ||||||
|  |  * traverse the list. New elements can be added to the list after | ||||||
|  |  * an existing element or at the head of the list. A list may only be | ||||||
|  |  * traversed in the forward direction. | ||||||
|  |  * | ||||||
|  |  * A tail queue is headed by a pair of pointers, one to the head of the | ||||||
|  |  * list and the other to the tail of the list. The elements are doubly | ||||||
|  |  * linked so that an arbitrary element can be removed without a need to | ||||||
|  |  * traverse the list. New elements can be added to the list after | ||||||
|  |  * an existing element, at the head of the list, or at the end of the | ||||||
|  |  * list. A tail queue may only be traversed in the forward direction. | ||||||
|  |  * | ||||||
|  |  * A circle queue is headed by a pair of pointers, one to the head of the | ||||||
|  |  * list and the other to the tail of the list. The elements are doubly | ||||||
|  |  * linked so that an arbitrary element can be removed without a need to | ||||||
|  |  * traverse the list. New elements can be added to the list before or after | ||||||
|  |  * an existing element, at the head of the list, or at the end of the list. | ||||||
|  |  * A circle queue may be traversed in either direction, but has a more | ||||||
|  |  * complex end of list detection. | ||||||
|  |  * | ||||||
|  |  * For details on the use of these macros, see the queue(3) manual page. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * List definitions. | ||||||
|  |  */ | ||||||
|  | #define LIST_HEAD(name, type)						\ | ||||||
|  | struct name {								\ | ||||||
|  | 	struct type *lh_first;	/* first element */			\ | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #define LIST_ENTRY(type)						\ | ||||||
|  | struct {								\ | ||||||
|  | 	struct type *le_next;	/* next element */			\ | ||||||
|  | 	struct type **le_prev;	/* address of previous next element */	\ | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * List functions. | ||||||
|  |  */ | ||||||
|  | #define	LIST_INIT(head) {						\ | ||||||
|  | 	(head)->lh_first = NULL;					\ | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #define LIST_INSERT_AFTER(listelm, elm, field) {			\ | ||||||
|  | 	if (((elm)->field.le_next = (listelm)->field.le_next) != NULL)	\ | ||||||
|  | 		(listelm)->field.le_next->field.le_prev =		\ | ||||||
|  | 		    &(elm)->field.le_next;				\ | ||||||
|  | 	(listelm)->field.le_next = (elm);				\ | ||||||
|  | 	(elm)->field.le_prev = &(listelm)->field.le_next;		\ | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #define LIST_INSERT_HEAD(head, elm, field) {				\ | ||||||
|  | 	if (((elm)->field.le_next = (head)->lh_first) != NULL)		\ | ||||||
|  | 		(head)->lh_first->field.le_prev = &(elm)->field.le_next;\ | ||||||
|  | 	(head)->lh_first = (elm);					\ | ||||||
|  | 	(elm)->field.le_prev = &(head)->lh_first;			\ | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #define LIST_REMOVE(elm, field) {					\ | ||||||
|  | 	if ((elm)->field.le_next != NULL)				\ | ||||||
|  | 		(elm)->field.le_next->field.le_prev = 			\ | ||||||
|  | 		    (elm)->field.le_prev;				\ | ||||||
|  | 	*(elm)->field.le_prev = (elm)->field.le_next;			\ | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Tail queue definitions. | ||||||
|  |  */ | ||||||
|  | #define TAILQ_HEAD(name, type)						\ | ||||||
|  | struct name {								\ | ||||||
|  | 	struct type *tqh_first;	/* first element */			\ | ||||||
|  | 	struct type **tqh_last;	/* addr of last next element */		\ | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #define TAILQ_ENTRY(type)						\ | ||||||
|  | struct {								\ | ||||||
|  | 	struct type *tqe_next;	/* next element */			\ | ||||||
|  | 	struct type **tqe_prev;	/* address of previous next element */	\ | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Tail queue functions. | ||||||
|  |  */ | ||||||
|  | #define	TAILQ_INIT(head) {						\ | ||||||
|  | 	(head)->tqh_first = NULL;					\ | ||||||
|  | 	(head)->tqh_last = &(head)->tqh_first;				\ | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #define TAILQ_INSERT_HEAD(head, elm, field) {				\ | ||||||
|  | 	if (((elm)->field.tqe_next = (head)->tqh_first) != NULL)	\ | ||||||
|  | 		(elm)->field.tqe_next->field.tqe_prev =			\ | ||||||
|  | 		    &(elm)->field.tqe_next;				\ | ||||||
|  | 	else								\ | ||||||
|  | 		(head)->tqh_last = &(elm)->field.tqe_next;		\ | ||||||
|  | 	(head)->tqh_first = (elm);					\ | ||||||
|  | 	(elm)->field.tqe_prev = &(head)->tqh_first;			\ | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #define TAILQ_INSERT_TAIL(head, elm, field) {				\ | ||||||
|  | 	(elm)->field.tqe_next = NULL;					\ | ||||||
|  | 	(elm)->field.tqe_prev = (head)->tqh_last;			\ | ||||||
|  | 	*(head)->tqh_last = (elm);					\ | ||||||
|  | 	(head)->tqh_last = &(elm)->field.tqe_next;			\ | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #define TAILQ_INSERT_AFTER(head, listelm, elm, field) {			\ | ||||||
|  | 	if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ | ||||||
|  | 		(elm)->field.tqe_next->field.tqe_prev = 		\ | ||||||
|  | 		    &(elm)->field.tqe_next;				\ | ||||||
|  | 	else								\ | ||||||
|  | 		(head)->tqh_last = &(elm)->field.tqe_next;		\ | ||||||
|  | 	(listelm)->field.tqe_next = (elm);				\ | ||||||
|  | 	(elm)->field.tqe_prev = &(listelm)->field.tqe_next;		\ | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #define TAILQ_REMOVE(head, elm, field) {				\ | ||||||
|  | 	if (((elm)->field.tqe_next) != NULL)				\ | ||||||
|  | 		(elm)->field.tqe_next->field.tqe_prev = 		\ | ||||||
|  | 		    (elm)->field.tqe_prev;				\ | ||||||
|  | 	else								\ | ||||||
|  | 		(head)->tqh_last = (elm)->field.tqe_prev;		\ | ||||||
|  | 	*(elm)->field.tqe_prev = (elm)->field.tqe_next;			\ | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Circular queue definitions. | ||||||
|  |  */ | ||||||
|  | #define CIRCLEQ_HEAD(name, type)					\ | ||||||
|  | struct name {								\ | ||||||
|  | 	struct type *cqh_first;		/* first element */		\ | ||||||
|  | 	struct type *cqh_last;		/* last element */		\ | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #define CIRCLEQ_ENTRY(type)						\ | ||||||
|  | struct {								\ | ||||||
|  | 	struct type *cqe_next;		/* next element */		\ | ||||||
|  | 	struct type *cqe_prev;		/* previous element */		\ | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Circular queue functions. | ||||||
|  |  */ | ||||||
|  | #define	CIRCLEQ_INIT(head) {						\ | ||||||
|  | 	(head)->cqh_first = (void *)(head);				\ | ||||||
|  | 	(head)->cqh_last = (void *)(head);				\ | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) {		\ | ||||||
|  | 	(elm)->field.cqe_next = (listelm)->field.cqe_next;		\ | ||||||
|  | 	(elm)->field.cqe_prev = (listelm);				\ | ||||||
|  | 	if ((listelm)->field.cqe_next == (void *)(head))		\ | ||||||
|  | 		(head)->cqh_last = (elm);				\ | ||||||
|  | 	else								\ | ||||||
|  | 		(listelm)->field.cqe_next->field.cqe_prev = (elm);	\ | ||||||
|  | 	(listelm)->field.cqe_next = (elm);				\ | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) {		\ | ||||||
|  | 	(elm)->field.cqe_next = (listelm);				\ | ||||||
|  | 	(elm)->field.cqe_prev = (listelm)->field.cqe_prev;		\ | ||||||
|  | 	if ((listelm)->field.cqe_prev == (void *)(head))		\ | ||||||
|  | 		(head)->cqh_first = (elm);				\ | ||||||
|  | 	else								\ | ||||||
|  | 		(listelm)->field.cqe_prev->field.cqe_next = (elm);	\ | ||||||
|  | 	(listelm)->field.cqe_prev = (elm);				\ | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #define CIRCLEQ_INSERT_HEAD(head, elm, field) {				\ | ||||||
|  | 	(elm)->field.cqe_next = (head)->cqh_first;			\ | ||||||
|  | 	(elm)->field.cqe_prev = (void *)(head);				\ | ||||||
|  | 	if ((head)->cqh_last == (void *)(head))				\ | ||||||
|  | 		(head)->cqh_last = (elm);				\ | ||||||
|  | 	else								\ | ||||||
|  | 		(head)->cqh_first->field.cqe_prev = (elm);		\ | ||||||
|  | 	(head)->cqh_first = (elm);					\ | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #define CIRCLEQ_INSERT_TAIL(head, elm, field) {				\ | ||||||
|  | 	(elm)->field.cqe_next = (void *)(head);				\ | ||||||
|  | 	(elm)->field.cqe_prev = (head)->cqh_last;			\ | ||||||
|  | 	if ((head)->cqh_first == (void *)(head))			\ | ||||||
|  | 		(head)->cqh_first = (elm);				\ | ||||||
|  | 	else								\ | ||||||
|  | 		(head)->cqh_last->field.cqe_next = (elm);		\ | ||||||
|  | 	(head)->cqh_last = (elm);					\ | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #define	CIRCLEQ_REMOVE(head, elm, field) {				\ | ||||||
|  | 	if ((elm)->field.cqe_next == (void *)(head))			\ | ||||||
|  | 		(head)->cqh_last = (elm)->field.cqe_prev;		\ | ||||||
|  | 	else								\ | ||||||
|  | 		(elm)->field.cqe_next->field.cqe_prev =			\ | ||||||
|  | 		    (elm)->field.cqe_prev;				\ | ||||||
|  | 	if ((elm)->field.cqe_prev == (void *)(head))			\ | ||||||
|  | 		(head)->cqh_first = (elm)->field.cqe_next;		\ | ||||||
|  | 	else								\ | ||||||
|  | 		(elm)->field.cqe_prev->field.cqe_next =			\ | ||||||
|  | 		    (elm)->field.cqe_next;				\ | ||||||
|  | } | ||||||
|  | #endif	/* sys/queue.h */ | ||||||
| @@ -1,17 +0,0 @@ | |||||||
| # See docs/devel/tracing.txt for syntax documentation. |  | ||||||
|  |  | ||||||
| # audio/alsaaudio.c |  | ||||||
| alsa_revents(int revents) "revents = %d" |  | ||||||
| alsa_pollout(int i, int fd) "i = %d fd = %d" |  | ||||||
| alsa_set_handler(int events, int index, int fd, int err) "events=0x%x index=%d fd=%d err=%d" |  | ||||||
| alsa_wrote_zero(int len) "Failed to write %d frames (wrote zero)" |  | ||||||
| alsa_read_zero(long len) "Failed to read %ld frames (read zero)" |  | ||||||
| alsa_xrun_out(void) "Recovering from playback xrun" |  | ||||||
| alsa_xrun_in(void) "Recovering from capture xrun" |  | ||||||
| alsa_resume_out(void) "Resuming suspended output stream" |  | ||||||
| alsa_resume_in(void) "Resuming suspended input stream" |  | ||||||
| alsa_no_frames(int state) "No frames available and ALSA state is %d" |  | ||||||
|  |  | ||||||
| # audio/ossaudio.c |  | ||||||
| oss_version(int version) "OSS version = 0x%x" |  | ||||||
| oss_invalid_available_size(int size, int bufsize) "Invalid available size, size=%d bufsize=%d" |  | ||||||
							
								
								
									
										198
									
								
								audio/wavaudio.c
									
									
									
									
									
								
							
							
						
						
									
										198
									
								
								audio/wavaudio.c
									
									
									
									
									
								
							| @@ -21,9 +21,8 @@ | |||||||
|  * 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 "hw/hw.h" | ||||||
| #include "qemu/host-utils.h" | #include "qemu-timer.h" | ||||||
| #include "qemu/timer.h" |  | ||||||
| #include "audio.h" | #include "audio.h" | ||||||
|  |  | ||||||
| #define AUDIO_CAP "wav" | #define AUDIO_CAP "wav" | ||||||
| @@ -31,27 +30,34 @@ | |||||||
|  |  | ||||||
| typedef struct WAVVoiceOut { | typedef struct WAVVoiceOut { | ||||||
|     HWVoiceOut hw; |     HWVoiceOut hw; | ||||||
|     FILE *f; |     QEMUFile *f; | ||||||
|     int64_t old_ticks; |     int64_t old_ticks; | ||||||
|     void *pcm_buf; |     void *pcm_buf; | ||||||
|     int total_samples; |     int total_samples; | ||||||
| } WAVVoiceOut; | } WAVVoiceOut; | ||||||
|  |  | ||||||
| typedef struct { | static struct { | ||||||
|     struct audsettings settings; |     audsettings_t settings; | ||||||
|     const char *wav_path; |     const char *wav_path; | ||||||
| } WAVConf; | } conf = { | ||||||
|  |     { | ||||||
|  |         44100, | ||||||
|  |         2, | ||||||
|  |         AUD_FMT_S16, | ||||||
|  |         AUDIO_HOST_ENDIANNESS | ||||||
|  |     }, | ||||||
|  |     "qemu.wav" | ||||||
|  | }; | ||||||
|  |  | ||||||
| static int wav_run_out (HWVoiceOut *hw, int live) | static int wav_run_out (HWVoiceOut *hw) | ||||||
| { | { | ||||||
|     WAVVoiceOut *wav = (WAVVoiceOut *) hw; |     WAVVoiceOut *wav = (WAVVoiceOut *) hw; | ||||||
|     int rpos, decr, samples; |     int rpos, live, decr, samples; | ||||||
|     uint8_t *dst; |     uint8_t *dst; | ||||||
|     struct st_sample *src; |     st_sample_t *src; | ||||||
|     int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); |     int64_t now = qemu_get_clock (vm_clock); | ||||||
|     int64_t ticks = now - wav->old_ticks; |     int64_t ticks = now - wav->old_ticks; | ||||||
|     int64_t bytes = |     int64_t bytes = (ticks * hw->info.bytes_per_second) / ticks_per_sec; | ||||||
|         muldiv64(ticks, hw->info.bytes_per_second, NANOSECONDS_PER_SECOND); |  | ||||||
|  |  | ||||||
|     if (bytes > INT_MAX) { |     if (bytes > INT_MAX) { | ||||||
|         samples = INT_MAX >> hw->info.shift; |         samples = INT_MAX >> hw->info.shift; | ||||||
| @@ -60,6 +66,11 @@ static int wav_run_out (HWVoiceOut *hw, int live) | |||||||
|         samples = bytes >> hw->info.shift; |         samples = bytes >> hw->info.shift; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     live = audio_pcm_hw_get_live_out (hw); | ||||||
|  |     if (!live) { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     wav->old_ticks = now; |     wav->old_ticks = now; | ||||||
|     decr = audio_MIN (live, samples); |     decr = audio_MIN (live, samples); | ||||||
|     samples = decr; |     samples = decr; | ||||||
| @@ -72,10 +83,7 @@ static int wav_run_out (HWVoiceOut *hw, int live) | |||||||
|         dst = advance (wav->pcm_buf, rpos << hw->info.shift); |         dst = advance (wav->pcm_buf, rpos << hw->info.shift); | ||||||
|  |  | ||||||
|         hw->clip (dst, src, convert_samples); |         hw->clip (dst, src, convert_samples); | ||||||
|         if (fwrite (dst, convert_samples << hw->info.shift, 1, wav->f) != 1) { |         qemu_put_buffer (wav->f, dst, convert_samples << hw->info.shift); | ||||||
|             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; |         rpos = (rpos + convert_samples) % hw->samples; | ||||||
|         samples -= convert_samples; |         samples -= convert_samples; | ||||||
| @@ -101,8 +109,7 @@ static void le_store (uint8_t *buf, uint32_t val, int len) | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| static int wav_init_out(HWVoiceOut *hw, struct audsettings *as, | static int wav_init_out (HWVoiceOut *hw, audsettings_t *as) | ||||||
|                         void *drv_opaque) |  | ||||||
| { | { | ||||||
|     WAVVoiceOut *wav = (WAVVoiceOut *) hw; |     WAVVoiceOut *wav = (WAVVoiceOut *) hw; | ||||||
|     int bits16 = 0, stereo = 0; |     int bits16 = 0, stereo = 0; | ||||||
| @@ -112,8 +119,9 @@ 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 | ||||||
|     }; |     }; | ||||||
|     WAVConf *conf = drv_opaque; |     audsettings_t wav_as = conf.settings; | ||||||
|     struct audsettings wav_as = conf->settings; |  | ||||||
|  |     (void) as; | ||||||
|  |  | ||||||
|     stereo = wav_as.nchannels == 2; |     stereo = wav_as.nchannels == 2; | ||||||
|     switch (wav_as.fmt) { |     switch (wav_as.fmt) { | ||||||
| @@ -139,7 +147,7 @@ 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); |     wav->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift); | ||||||
|     if (!wav->pcm_buf) { |     if (!wav->pcm_buf) { | ||||||
|         dolog ("Could not allocate buffer (%d bytes)\n", |         dolog ("Could not allocate buffer (%d bytes)\n", | ||||||
|                hw->samples << hw->info.shift); |                hw->samples << hw->info.shift); | ||||||
| @@ -151,20 +159,16 @@ static int wav_init_out(HWVoiceOut *hw, struct audsettings *as, | |||||||
|     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 (conf->wav_path, "wb"); |     wav->f = qemu_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", | ||||||
|                conf->wav_path, strerror (errno)); |                conf.wav_path, strerror (errno)); | ||||||
|         g_free (wav->pcm_buf); |         qemu_free (wav->pcm_buf); | ||||||
|         wav->pcm_buf = NULL; |         wav->pcm_buf = NULL; | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (fwrite (hdr, sizeof (hdr), 1, wav->f) != 1) { |     qemu_put_buffer (wav->f, hdr, sizeof (hdr)); | ||||||
|         dolog ("wav_init_out: failed to write header\nReason: %s\n", |  | ||||||
|                strerror(errno)); |  | ||||||
|         return -1; |  | ||||||
|     } |  | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -183,35 +187,16 @@ static void wav_fini_out (HWVoiceOut *hw) | |||||||
|     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)) { |     qemu_fseek (wav->f, 4, SEEK_SET); | ||||||
|         dolog ("wav_fini_out: fseek to rlen failed\nReason: %s\n", |     qemu_put_buffer (wav->f, rlen, 4); | ||||||
|                strerror(errno)); |  | ||||||
|         goto doclose; |  | ||||||
|     } |  | ||||||
|     if (fwrite (rlen, 4, 1, wav->f) != 1) { |  | ||||||
|         dolog ("wav_fini_out: failed to write rlen\nReason: %s\n", |  | ||||||
|                strerror (errno)); |  | ||||||
|         goto doclose; |  | ||||||
|     } |  | ||||||
|     if (fseek (wav->f, 32, SEEK_CUR)) { |  | ||||||
|         dolog ("wav_fini_out: fseek to dlen failed\nReason: %s\n", |  | ||||||
|                strerror (errno)); |  | ||||||
|         goto doclose; |  | ||||||
|     } |  | ||||||
|     if (fwrite (dlen, 4, 1, wav->f) != 1) { |  | ||||||
|         dolog ("wav_fini_out: failed to write dlen\nReaons: %s\n", |  | ||||||
|                strerror (errno)); |  | ||||||
|         goto doclose; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|  doclose: |     qemu_fseek (wav->f, 32, SEEK_CUR); | ||||||
|     if (fclose (wav->f))  { |     qemu_put_buffer (wav->f, dlen, 4); | ||||||
|         dolog ("wav_fini_out: fclose %p failed\nReason: %s\n", |  | ||||||
|                wav->f, strerror (errno)); |     qemu_fclose (wav->f); | ||||||
|     } |  | ||||||
|     wav->f = NULL; |     wav->f = NULL; | ||||||
|  |  | ||||||
|     g_free (wav->pcm_buf); |     qemu_free (wav->pcm_buf); | ||||||
|     wav->pcm_buf = NULL; |     wav->pcm_buf = NULL; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -222,78 +207,57 @@ static int wav_ctl_out (HWVoiceOut *hw, int cmd, ...) | |||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| 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) | static void *wav_audio_init (void) | ||||||
| { | { | ||||||
|     WAVConf *conf = g_malloc(sizeof(WAVConf)); |     return &conf; | ||||||
|     *conf = glob_conf; |  | ||||||
|     return conf; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static void wav_audio_fini (void *opaque) | static void wav_audio_fini (void *opaque) | ||||||
| { | { | ||||||
|  |     (void) opaque; | ||||||
|     ldebug ("wav_fini"); |     ldebug ("wav_fini"); | ||||||
|     g_free(opaque); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static struct audio_option wav_options[] = { | struct audio_option wav_options[] = { | ||||||
|     { |     {"FREQUENCY", AUD_OPT_INT, &conf.settings.freq, | ||||||
|         .name  = "FREQUENCY", |      "Frequency", NULL, 0}, | ||||||
|         .tag   = AUD_OPT_INT, |  | ||||||
|         .valp  = &glob_conf.settings.freq, |     {"FORMAT", AUD_OPT_FMT, &conf.settings.fmt, | ||||||
|         .descr = "Frequency" |      "Format", NULL, 0}, | ||||||
|     }, |  | ||||||
|     { |     {"DAC_FIXED_CHANNELS", AUD_OPT_INT, &conf.settings.nchannels, | ||||||
|         .name  = "FORMAT", |      "Number of channels (1 - mono, 2 - stereo)", NULL, 0}, | ||||||
|         .tag   = AUD_OPT_FMT, |  | ||||||
|         .valp  = &glob_conf.settings.fmt, |     {"PATH", AUD_OPT_STR, &conf.wav_path, | ||||||
|         .descr = "Format" |      "Path to wave file", NULL, 0}, | ||||||
|     }, |     {NULL, 0, NULL, NULL, NULL, 0} | ||||||
|     { |  | ||||||
|         .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 = { | struct audio_pcm_ops wav_pcm_ops = { | ||||||
|     .init_out = wav_init_out, |     wav_init_out, | ||||||
|     .fini_out = wav_fini_out, |     wav_fini_out, | ||||||
|     .run_out  = wav_run_out, |     wav_run_out, | ||||||
|     .write    = wav_write_out, |     wav_write_out, | ||||||
|     .ctl_out  = wav_ctl_out, |     wav_ctl_out, | ||||||
|  |  | ||||||
|  |     NULL, | ||||||
|  |     NULL, | ||||||
|  |     NULL, | ||||||
|  |     NULL, | ||||||
|  |     NULL | ||||||
| }; | }; | ||||||
|  |  | ||||||
| static struct audio_driver wav_audio_driver = { | struct audio_driver wav_audio_driver = { | ||||||
|     .name           = "wav", |     INIT_FIELD (name           = ) "wav", | ||||||
|     .descr          = "WAV renderer http://wikipedia.org/wiki/WAV", |     INIT_FIELD (descr          = ) | ||||||
|     .options        = wav_options, |     "WAV renderer http://wikipedia.org/wiki/WAV", | ||||||
|     .init           = wav_audio_init, |     INIT_FIELD (options        = ) wav_options, | ||||||
|     .fini           = wav_audio_fini, |     INIT_FIELD (init           = ) wav_audio_init, | ||||||
|     .pcm_ops        = &wav_pcm_ops, |     INIT_FIELD (fini           = ) wav_audio_fini, | ||||||
|     .can_be_default = 0, |     INIT_FIELD (pcm_ops        = ) &wav_pcm_ops, | ||||||
|     .max_voices_out = 1, |     INIT_FIELD (can_be_default = ) 0, | ||||||
|     .max_voices_in  = 0, |     INIT_FIELD (max_voices_out = ) 1, | ||||||
|     .voice_size_out = sizeof (WAVVoiceOut), |     INIT_FIELD (max_voices_in  = ) 0, | ||||||
|     .voice_size_in  = 0 |     INIT_FIELD (voice_size_out = ) sizeof (WAVVoiceOut), | ||||||
|  |     INIT_FIELD (voice_size_in  = ) 0 | ||||||
| }; | }; | ||||||
|  |  | ||||||
| static void register_audio_wav(void) |  | ||||||
| { |  | ||||||
|     audio_driver_register(&wav_audio_driver); |  | ||||||
| } |  | ||||||
| type_init(register_audio_wav); |  | ||||||
|   | |||||||
| @@ -1,12 +1,9 @@ | |||||||
| #include "qemu/osdep.h" |  | ||||||
| #include "hw/hw.h" | #include "hw/hw.h" | ||||||
| #include "monitor/monitor.h" | #include "console.h" | ||||||
| #include "qapi/error.h" |  | ||||||
| #include "qemu/error-report.h" |  | ||||||
| #include "audio.h" | #include "audio.h" | ||||||
|  |  | ||||||
| typedef struct { | typedef struct { | ||||||
|     FILE *f; |     QEMUFile *f; | ||||||
|     int bytes; |     int bytes; | ||||||
|     char *path; |     char *path; | ||||||
|     int freq; |     int freq; | ||||||
| @@ -38,49 +35,27 @@ 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)) { |         qemu_fseek (wav->f, 4, SEEK_SET); | ||||||
|             monitor_printf (mon, "wav_destroy: rlen fseek failed\nReason: %s\n", |         qemu_put_buffer (wav->f, rlen, 4); | ||||||
|                             strerror (errno)); |  | ||||||
|             goto doclose; |         qemu_fseek (wav->f, 32, SEEK_CUR); | ||||||
|         } |         qemu_put_buffer (wav->f, dlen, 4); | ||||||
|         if (fwrite (rlen, 4, 1, wav->f) != 1) { |         qemu_fclose (wav->f); | ||||||
|             monitor_printf (mon, "wav_destroy: rlen fwrite failed\nReason %s\n", |  | ||||||
|                             strerror (errno)); |  | ||||||
|             goto doclose; |  | ||||||
|         } |  | ||||||
|         if (fseek (wav->f, 32, SEEK_CUR)) { |  | ||||||
|             monitor_printf (mon, "wav_destroy: dlen fseek failed\nReason %s\n", |  | ||||||
|                             strerror (errno)); |  | ||||||
|             goto doclose; |  | ||||||
|         } |  | ||||||
|         if (fwrite (dlen, 1, 4, wav->f) != 4) { |  | ||||||
|             monitor_printf (mon, "wav_destroy: dlen fwrite failed\nReason %s\n", |  | ||||||
|                             strerror (errno)); |  | ||||||
|             goto doclose; |  | ||||||
|         } |  | ||||||
|     doclose: |  | ||||||
|         if (fclose (wav->f)) { |  | ||||||
|             error_report("wav_destroy: fclose failed: %s", strerror(errno)); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     g_free (wav->path); |     qemu_free (wav->path); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void wav_capture (void *opaque, void *buf, int size) | static void wav_capture (void *opaque, void *buf, int size) | ||||||
| { | { | ||||||
|     WAVState *wav = opaque; |     WAVState *wav = opaque; | ||||||
|  |  | ||||||
|     if (fwrite (buf, size, 1, wav->f) != 1) { |     qemu_put_buffer (wav->f, buf, size); | ||||||
|         monitor_printf (cur_mon, "wav_capture: fwrite error\nReason: %s", |  | ||||||
|                         strerror (errno)); |  | ||||||
|     } |  | ||||||
|     wav->bytes += size; |     wav->bytes += size; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -89,7 +64,6 @@ static void wav_capture_destroy (void *opaque) | |||||||
|     WAVState *wav = opaque; |     WAVState *wav = opaque; | ||||||
|  |  | ||||||
|     AUD_del_capture (wav->cap, wav); |     AUD_del_capture (wav->cap, wav); | ||||||
|     g_free (wav); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static void wav_capture_info (void *opaque) | static void wav_capture_info (void *opaque) | ||||||
| @@ -97,7 +71,7 @@ static void wav_capture_info (void *opaque) | |||||||
|     WAVState *wav = opaque; |     WAVState *wav = opaque; | ||||||
|     char *path = wav->path; |     char *path = wav->path; | ||||||
|  |  | ||||||
|     monitor_printf (cur_mon, "Capturing audio(%d,%d,%d) to %s: %d bytes\n", |     term_printf ("Capturing audio(%d,%d,%d) to %s: %d bytes\n", | ||||||
|                  wav->freq, wav->bits, wav->nchannels, |                  wav->freq, wav->bits, wav->nchannels, | ||||||
|                  path ? path : "<not available>", wav->bytes); |                  path ? path : "<not available>", wav->bytes); | ||||||
| } | } | ||||||
| @@ -110,7 +84,6 @@ static struct capture_ops wav_capture_ops = { | |||||||
| int wav_start_capture (CaptureState *s, const char *path, int freq, | int wav_start_capture (CaptureState *s, const char *path, 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, | ||||||
| @@ -118,18 +91,18 @@ int wav_start_capture (CaptureState *s, const char *path, int freq, | |||||||
|         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 | ||||||
|     }; |     }; | ||||||
|     struct audsettings as; |     audsettings_t as; | ||||||
|     struct audio_capture_ops ops; |     struct audio_capture_ops ops; | ||||||
|     int stereo, bits16, shift; |     int stereo, bits16, shift; | ||||||
|     CaptureVoiceOut *cap; |     CaptureVoiceOut *cap; | ||||||
|  |  | ||||||
|     if (bits != 8 && bits != 16) { |     if (bits != 8 && bits != 16) { | ||||||
|         monitor_printf (mon, "incorrect bit count %d, must be 8 or 16\n", bits); |         term_printf ("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) { | ||||||
|         monitor_printf (mon, "incorrect channel count %d, must be 1 or 2\n", |         term_printf ("incorrect channel count %d, must be 1 or 2\n", | ||||||
|                      nchannels); |                      nchannels); | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
| @@ -146,7 +119,12 @@ int wav_start_capture (CaptureState *s, const char *path, int freq, | |||||||
|     ops.capture = wav_capture; |     ops.capture = wav_capture; | ||||||
|     ops.destroy = wav_destroy; |     ops.destroy = wav_destroy; | ||||||
|  |  | ||||||
|     wav = g_malloc0 (sizeof (*wav)); |     wav = qemu_mallocz (sizeof (*wav)); | ||||||
|  |     if (!wav) { | ||||||
|  |         term_printf ("Could not allocate memory for wav capture (%zu bytes)", | ||||||
|  |                      sizeof (*wav)); | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     shift = bits16 + stereo; |     shift = bits16 + stereo; | ||||||
|     hdr[34] = bits16 ? 0x10 : 0x08; |     hdr[34] = bits16 ? 0x10 : 0x08; | ||||||
| @@ -156,42 +134,32 @@ int wav_start_capture (CaptureState *s, const char *path, int freq, | |||||||
|     le_store (hdr + 28, freq << shift, 4); |     le_store (hdr + 28, freq << shift, 4); | ||||||
|     le_store (hdr + 32, 1 << shift, 2); |     le_store (hdr + 32, 1 << shift, 2); | ||||||
|  |  | ||||||
|     wav->f = fopen (path, "wb"); |     wav->f = qemu_fopen (path, "wb"); | ||||||
|     if (!wav->f) { |     if (!wav->f) { | ||||||
|         monitor_printf (mon, "Failed to open wave file `%s'\nReason: %s\n", |         term_printf ("Failed to open wave file `%s'\nReason: %s\n", | ||||||
|                      path, strerror (errno)); |                      path, strerror (errno)); | ||||||
|         g_free (wav); |         qemu_free (wav); | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     wav->path = g_strdup (path); |     wav->path = qemu_strdup (path); | ||||||
|     wav->bits = bits; |     wav->bits = bits; | ||||||
|     wav->nchannels = nchannels; |     wav->nchannels = nchannels; | ||||||
|     wav->freq = freq; |     wav->freq = freq; | ||||||
|  |  | ||||||
|     if (fwrite (hdr, sizeof (hdr), 1, wav->f) != 1) { |     qemu_put_buffer (wav->f, hdr, sizeof (hdr)); | ||||||
|         monitor_printf (mon, "Failed to write header\nReason: %s\n", |  | ||||||
|                         strerror (errno)); |  | ||||||
|         goto error_free; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     cap = AUD_add_capture (&as, &ops, wav); |     cap = AUD_add_capture (NULL, &as, &ops, wav); | ||||||
|     if (!cap) { |     if (!cap) { | ||||||
|         monitor_printf (mon, "Failed to add audio capture\n"); |         term_printf ("Failed to add audio capture\n"); | ||||||
|         goto error_free; |         qemu_free (wav->path); | ||||||
|  |         qemu_fclose (wav->f); | ||||||
|  |         qemu_free (wav); | ||||||
|  |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     wav->cap = cap; |     wav->cap = cap; | ||||||
|     s->opaque = wav; |     s->opaque = wav; | ||||||
|     s->ops = wav_capture_ops; |     s->ops = wav_capture_ops; | ||||||
|     return 0; |     return 0; | ||||||
|  |  | ||||||
| error_free: |  | ||||||
|     g_free (wav->path); |  | ||||||
|     if (fclose (wav->f)) { |  | ||||||
|         monitor_printf (mon, "Failed to close wave file\nReason: %s\n", |  | ||||||
|                         strerror (errno)); |  | ||||||
|     } |  | ||||||
|     g_free (wav); |  | ||||||
|     return -1; |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,18 +0,0 @@ | |||||||
| common-obj-y += rng.o rng-egd.o |  | ||||||
| common-obj-$(CONFIG_POSIX) += rng-random.o |  | ||||||
|  |  | ||||||
| common-obj-$(CONFIG_TPM) += tpm.o |  | ||||||
|  |  | ||||||
| common-obj-y += hostmem.o hostmem-ram.o |  | ||||||
| common-obj-$(CONFIG_LINUX) += hostmem-file.o |  | ||||||
|  |  | ||||||
| common-obj-y += cryptodev.o |  | ||||||
| common-obj-y += cryptodev-builtin.o |  | ||||||
|  |  | ||||||
| ifeq ($(CONFIG_VIRTIO),y) |  | ||||||
| common-obj-y += cryptodev-vhost.o |  | ||||||
| common-obj-$(call land,$(CONFIG_VHOST_USER),$(CONFIG_LINUX)) += \ |  | ||||||
|     cryptodev-vhost-user.o |  | ||||||
| endif |  | ||||||
|  |  | ||||||
| common-obj-$(CONFIG_LINUX) += hostmem-memfd.o |  | ||||||
| @@ -1,401 +0,0 @@ | |||||||
| /* |  | ||||||
|  * QEMU Cryptodev backend for QEMU cipher APIs |  | ||||||
|  * |  | ||||||
|  * Copyright (c) 2016 HUAWEI TECHNOLOGIES CO., LTD. |  | ||||||
|  * |  | ||||||
|  * Authors: |  | ||||||
|  *    Gonglei <arei.gonglei@huawei.com> |  | ||||||
|  * |  | ||||||
|  * 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 "sysemu/cryptodev.h" |  | ||||||
| #include "hw/boards.h" |  | ||||||
| #include "qapi/error.h" |  | ||||||
| #include "standard-headers/linux/virtio_crypto.h" |  | ||||||
| #include "crypto/cipher.h" |  | ||||||
|  |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * @TYPE_CRYPTODEV_BACKEND_BUILTIN: |  | ||||||
|  * name of backend that uses QEMU cipher API |  | ||||||
|  */ |  | ||||||
| #define TYPE_CRYPTODEV_BACKEND_BUILTIN "cryptodev-backend-builtin" |  | ||||||
|  |  | ||||||
| #define CRYPTODEV_BACKEND_BUILTIN(obj) \ |  | ||||||
|     OBJECT_CHECK(CryptoDevBackendBuiltin, \ |  | ||||||
|                  (obj), TYPE_CRYPTODEV_BACKEND_BUILTIN) |  | ||||||
|  |  | ||||||
| typedef struct CryptoDevBackendBuiltin |  | ||||||
|                          CryptoDevBackendBuiltin; |  | ||||||
|  |  | ||||||
| typedef struct CryptoDevBackendBuiltinSession { |  | ||||||
|     QCryptoCipher *cipher; |  | ||||||
|     uint8_t direction; /* encryption or decryption */ |  | ||||||
|     uint8_t type; /* cipher? hash? aead? */ |  | ||||||
|     QTAILQ_ENTRY(CryptoDevBackendBuiltinSession) next; |  | ||||||
| } CryptoDevBackendBuiltinSession; |  | ||||||
|  |  | ||||||
| /* Max number of symmetric sessions */ |  | ||||||
| #define MAX_NUM_SESSIONS 256 |  | ||||||
|  |  | ||||||
| #define CRYPTODEV_BUITLIN_MAX_AUTH_KEY_LEN    512 |  | ||||||
| #define CRYPTODEV_BUITLIN_MAX_CIPHER_KEY_LEN  64 |  | ||||||
|  |  | ||||||
| struct CryptoDevBackendBuiltin { |  | ||||||
|     CryptoDevBackend parent_obj; |  | ||||||
|  |  | ||||||
|     CryptoDevBackendBuiltinSession *sessions[MAX_NUM_SESSIONS]; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static void cryptodev_builtin_init( |  | ||||||
|              CryptoDevBackend *backend, Error **errp) |  | ||||||
| { |  | ||||||
|     /* Only support one queue */ |  | ||||||
|     int queues = backend->conf.peers.queues; |  | ||||||
|     CryptoDevBackendClient *cc; |  | ||||||
|  |  | ||||||
|     if (queues != 1) { |  | ||||||
|         error_setg(errp, |  | ||||||
|                   "Only support one queue in cryptdov-builtin backend"); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     cc = cryptodev_backend_new_client( |  | ||||||
|               "cryptodev-builtin", NULL); |  | ||||||
|     cc->info_str = g_strdup_printf("cryptodev-builtin0"); |  | ||||||
|     cc->queue_index = 0; |  | ||||||
|     cc->type = CRYPTODEV_BACKEND_TYPE_BUILTIN; |  | ||||||
|     backend->conf.peers.ccs[0] = cc; |  | ||||||
|  |  | ||||||
|     backend->conf.crypto_services = |  | ||||||
|                          1u << VIRTIO_CRYPTO_SERVICE_CIPHER | |  | ||||||
|                          1u << VIRTIO_CRYPTO_SERVICE_HASH | |  | ||||||
|                          1u << VIRTIO_CRYPTO_SERVICE_MAC; |  | ||||||
|     backend->conf.cipher_algo_l = 1u << VIRTIO_CRYPTO_CIPHER_AES_CBC; |  | ||||||
|     backend->conf.hash_algo = 1u << VIRTIO_CRYPTO_HASH_SHA1; |  | ||||||
|     /* |  | ||||||
|      * Set the Maximum length of crypto request. |  | ||||||
|      * Why this value? Just avoid to overflow when |  | ||||||
|      * memory allocation for each crypto request. |  | ||||||
|      */ |  | ||||||
|     backend->conf.max_size = LONG_MAX - sizeof(CryptoDevBackendSymOpInfo); |  | ||||||
|     backend->conf.max_cipher_key_len = CRYPTODEV_BUITLIN_MAX_CIPHER_KEY_LEN; |  | ||||||
|     backend->conf.max_auth_key_len = CRYPTODEV_BUITLIN_MAX_AUTH_KEY_LEN; |  | ||||||
|  |  | ||||||
|     cryptodev_backend_set_ready(backend, true); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int |  | ||||||
| cryptodev_builtin_get_unused_session_index( |  | ||||||
|                  CryptoDevBackendBuiltin *builtin) |  | ||||||
| { |  | ||||||
|     size_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < MAX_NUM_SESSIONS; i++) { |  | ||||||
|         if (builtin->sessions[i] == NULL) { |  | ||||||
|             return i; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return -1; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #define AES_KEYSIZE_128 16 |  | ||||||
| #define AES_KEYSIZE_192 24 |  | ||||||
| #define AES_KEYSIZE_256 32 |  | ||||||
| #define AES_KEYSIZE_128_XTS AES_KEYSIZE_256 |  | ||||||
| #define AES_KEYSIZE_256_XTS 64 |  | ||||||
|  |  | ||||||
| static int |  | ||||||
| cryptodev_builtin_get_aes_algo(uint32_t key_len, int mode, Error **errp) |  | ||||||
| { |  | ||||||
|     int algo; |  | ||||||
|  |  | ||||||
|     if (key_len == AES_KEYSIZE_128) { |  | ||||||
|         algo = QCRYPTO_CIPHER_ALG_AES_128; |  | ||||||
|     } else if (key_len == AES_KEYSIZE_192) { |  | ||||||
|         algo = QCRYPTO_CIPHER_ALG_AES_192; |  | ||||||
|     } else if (key_len == AES_KEYSIZE_256) { /* equals AES_KEYSIZE_128_XTS */ |  | ||||||
|         if (mode == QCRYPTO_CIPHER_MODE_XTS) { |  | ||||||
|             algo = QCRYPTO_CIPHER_ALG_AES_128; |  | ||||||
|         } else { |  | ||||||
|             algo = QCRYPTO_CIPHER_ALG_AES_256; |  | ||||||
|         } |  | ||||||
|     } else if (key_len == AES_KEYSIZE_256_XTS) { |  | ||||||
|         if (mode == QCRYPTO_CIPHER_MODE_XTS) { |  | ||||||
|             algo = QCRYPTO_CIPHER_ALG_AES_256; |  | ||||||
|         } else { |  | ||||||
|             goto err; |  | ||||||
|         } |  | ||||||
|     } else { |  | ||||||
|         goto err; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return algo; |  | ||||||
|  |  | ||||||
| err: |  | ||||||
|    error_setg(errp, "Unsupported key length :%u", key_len); |  | ||||||
|    return -1; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int cryptodev_builtin_create_cipher_session( |  | ||||||
|                     CryptoDevBackendBuiltin *builtin, |  | ||||||
|                     CryptoDevBackendSymSessionInfo *sess_info, |  | ||||||
|                     Error **errp) |  | ||||||
| { |  | ||||||
|     int algo; |  | ||||||
|     int mode; |  | ||||||
|     QCryptoCipher *cipher; |  | ||||||
|     int index; |  | ||||||
|     CryptoDevBackendBuiltinSession *sess; |  | ||||||
|  |  | ||||||
|     if (sess_info->op_type != VIRTIO_CRYPTO_SYM_OP_CIPHER) { |  | ||||||
|         error_setg(errp, "Unsupported optype :%u", sess_info->op_type); |  | ||||||
|         return -1; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     index = cryptodev_builtin_get_unused_session_index(builtin); |  | ||||||
|     if (index < 0) { |  | ||||||
|         error_setg(errp, "Total number of sessions created exceeds %u", |  | ||||||
|                   MAX_NUM_SESSIONS); |  | ||||||
|         return -1; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     switch (sess_info->cipher_alg) { |  | ||||||
|     case VIRTIO_CRYPTO_CIPHER_AES_ECB: |  | ||||||
|         mode = QCRYPTO_CIPHER_MODE_ECB; |  | ||||||
|         algo = cryptodev_builtin_get_aes_algo(sess_info->key_len, |  | ||||||
|                                                     mode, errp); |  | ||||||
|         if (algo < 0)  { |  | ||||||
|             return -1; |  | ||||||
|         } |  | ||||||
|         break; |  | ||||||
|     case VIRTIO_CRYPTO_CIPHER_AES_CBC: |  | ||||||
|         mode = QCRYPTO_CIPHER_MODE_CBC; |  | ||||||
|         algo = cryptodev_builtin_get_aes_algo(sess_info->key_len, |  | ||||||
|                                                     mode, errp); |  | ||||||
|         if (algo < 0)  { |  | ||||||
|             return -1; |  | ||||||
|         } |  | ||||||
|         break; |  | ||||||
|     case VIRTIO_CRYPTO_CIPHER_AES_CTR: |  | ||||||
|         mode = QCRYPTO_CIPHER_MODE_CTR; |  | ||||||
|         algo = cryptodev_builtin_get_aes_algo(sess_info->key_len, |  | ||||||
|                                                     mode, errp); |  | ||||||
|         if (algo < 0)  { |  | ||||||
|             return -1; |  | ||||||
|         } |  | ||||||
|         break; |  | ||||||
|     case VIRTIO_CRYPTO_CIPHER_AES_XTS: |  | ||||||
|         mode = QCRYPTO_CIPHER_MODE_XTS; |  | ||||||
|         algo = cryptodev_builtin_get_aes_algo(sess_info->key_len, |  | ||||||
|                                                     mode, errp); |  | ||||||
|         if (algo < 0)  { |  | ||||||
|             return -1; |  | ||||||
|         } |  | ||||||
|         break; |  | ||||||
|     case VIRTIO_CRYPTO_CIPHER_3DES_ECB: |  | ||||||
|         mode = QCRYPTO_CIPHER_MODE_ECB; |  | ||||||
|         algo = QCRYPTO_CIPHER_ALG_3DES; |  | ||||||
|         break; |  | ||||||
|     case VIRTIO_CRYPTO_CIPHER_3DES_CBC: |  | ||||||
|         mode = QCRYPTO_CIPHER_MODE_CBC; |  | ||||||
|         algo = QCRYPTO_CIPHER_ALG_3DES; |  | ||||||
|         break; |  | ||||||
|     case VIRTIO_CRYPTO_CIPHER_3DES_CTR: |  | ||||||
|         mode = QCRYPTO_CIPHER_MODE_CTR; |  | ||||||
|         algo = QCRYPTO_CIPHER_ALG_3DES; |  | ||||||
|         break; |  | ||||||
|     default: |  | ||||||
|         error_setg(errp, "Unsupported cipher alg :%u", |  | ||||||
|                    sess_info->cipher_alg); |  | ||||||
|         return -1; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     cipher = qcrypto_cipher_new(algo, mode, |  | ||||||
|                                sess_info->cipher_key, |  | ||||||
|                                sess_info->key_len, |  | ||||||
|                                errp); |  | ||||||
|     if (!cipher) { |  | ||||||
|         return -1; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     sess = g_new0(CryptoDevBackendBuiltinSession, 1); |  | ||||||
|     sess->cipher = cipher; |  | ||||||
|     sess->direction = sess_info->direction; |  | ||||||
|     sess->type = sess_info->op_type; |  | ||||||
|  |  | ||||||
|     builtin->sessions[index] = sess; |  | ||||||
|  |  | ||||||
|     return index; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int64_t cryptodev_builtin_sym_create_session( |  | ||||||
|            CryptoDevBackend *backend, |  | ||||||
|            CryptoDevBackendSymSessionInfo *sess_info, |  | ||||||
|            uint32_t queue_index, Error **errp) |  | ||||||
| { |  | ||||||
|     CryptoDevBackendBuiltin *builtin = |  | ||||||
|                       CRYPTODEV_BACKEND_BUILTIN(backend); |  | ||||||
|     int64_t session_id = -1; |  | ||||||
|     int ret; |  | ||||||
|  |  | ||||||
|     switch (sess_info->op_code) { |  | ||||||
|     case VIRTIO_CRYPTO_CIPHER_CREATE_SESSION: |  | ||||||
|         ret = cryptodev_builtin_create_cipher_session( |  | ||||||
|                            builtin, sess_info, errp); |  | ||||||
|         if (ret < 0) { |  | ||||||
|             return ret; |  | ||||||
|         } else { |  | ||||||
|             session_id = ret; |  | ||||||
|         } |  | ||||||
|         break; |  | ||||||
|     case VIRTIO_CRYPTO_HASH_CREATE_SESSION: |  | ||||||
|     case VIRTIO_CRYPTO_MAC_CREATE_SESSION: |  | ||||||
|     default: |  | ||||||
|         error_setg(errp, "Unsupported opcode :%" PRIu32 "", |  | ||||||
|                    sess_info->op_code); |  | ||||||
|         return -1; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return session_id; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int cryptodev_builtin_sym_close_session( |  | ||||||
|            CryptoDevBackend *backend, |  | ||||||
|            uint64_t session_id, |  | ||||||
|            uint32_t queue_index, Error **errp) |  | ||||||
| { |  | ||||||
|     CryptoDevBackendBuiltin *builtin = |  | ||||||
|                       CRYPTODEV_BACKEND_BUILTIN(backend); |  | ||||||
|  |  | ||||||
|     if (session_id >= MAX_NUM_SESSIONS || |  | ||||||
|               builtin->sessions[session_id] == NULL) { |  | ||||||
|         error_setg(errp, "Cannot find a valid session id: %" PRIu64 "", |  | ||||||
|                       session_id); |  | ||||||
|         return -1; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     qcrypto_cipher_free(builtin->sessions[session_id]->cipher); |  | ||||||
|     g_free(builtin->sessions[session_id]); |  | ||||||
|     builtin->sessions[session_id] = NULL; |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int cryptodev_builtin_sym_operation( |  | ||||||
|                  CryptoDevBackend *backend, |  | ||||||
|                  CryptoDevBackendSymOpInfo *op_info, |  | ||||||
|                  uint32_t queue_index, Error **errp) |  | ||||||
| { |  | ||||||
|     CryptoDevBackendBuiltin *builtin = |  | ||||||
|                       CRYPTODEV_BACKEND_BUILTIN(backend); |  | ||||||
|     CryptoDevBackendBuiltinSession *sess; |  | ||||||
|     int ret; |  | ||||||
|  |  | ||||||
|     if (op_info->session_id >= MAX_NUM_SESSIONS || |  | ||||||
|               builtin->sessions[op_info->session_id] == NULL) { |  | ||||||
|         error_setg(errp, "Cannot find a valid session id: %" PRIu64 "", |  | ||||||
|                    op_info->session_id); |  | ||||||
|         return -VIRTIO_CRYPTO_INVSESS; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (op_info->op_type == VIRTIO_CRYPTO_SYM_OP_ALGORITHM_CHAINING) { |  | ||||||
|         error_setg(errp, |  | ||||||
|                "Algorithm chain is unsupported for cryptdoev-builtin"); |  | ||||||
|         return -VIRTIO_CRYPTO_NOTSUPP; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     sess = builtin->sessions[op_info->session_id]; |  | ||||||
|  |  | ||||||
|     if (op_info->iv_len > 0) { |  | ||||||
|         ret = qcrypto_cipher_setiv(sess->cipher, op_info->iv, |  | ||||||
|                                    op_info->iv_len, errp); |  | ||||||
|         if (ret < 0) { |  | ||||||
|             return -VIRTIO_CRYPTO_ERR; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (sess->direction == VIRTIO_CRYPTO_OP_ENCRYPT) { |  | ||||||
|         ret = qcrypto_cipher_encrypt(sess->cipher, op_info->src, |  | ||||||
|                                      op_info->dst, op_info->src_len, errp); |  | ||||||
|         if (ret < 0) { |  | ||||||
|             return -VIRTIO_CRYPTO_ERR; |  | ||||||
|         } |  | ||||||
|     } else { |  | ||||||
|         ret = qcrypto_cipher_decrypt(sess->cipher, op_info->src, |  | ||||||
|                                      op_info->dst, op_info->src_len, errp); |  | ||||||
|         if (ret < 0) { |  | ||||||
|             return -VIRTIO_CRYPTO_ERR; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     return VIRTIO_CRYPTO_OK; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void cryptodev_builtin_cleanup( |  | ||||||
|              CryptoDevBackend *backend, |  | ||||||
|              Error **errp) |  | ||||||
| { |  | ||||||
|     CryptoDevBackendBuiltin *builtin = |  | ||||||
|                       CRYPTODEV_BACKEND_BUILTIN(backend); |  | ||||||
|     size_t i; |  | ||||||
|     int queues = backend->conf.peers.queues; |  | ||||||
|     CryptoDevBackendClient *cc; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < MAX_NUM_SESSIONS; i++) { |  | ||||||
|         if (builtin->sessions[i] != NULL) { |  | ||||||
|             cryptodev_builtin_sym_close_session( |  | ||||||
|                     backend, i, 0, errp); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     for (i = 0; i < queues; i++) { |  | ||||||
|         cc = backend->conf.peers.ccs[i]; |  | ||||||
|         if (cc) { |  | ||||||
|             cryptodev_backend_free_client(cc); |  | ||||||
|             backend->conf.peers.ccs[i] = NULL; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     cryptodev_backend_set_ready(backend, false); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void |  | ||||||
| cryptodev_builtin_class_init(ObjectClass *oc, void *data) |  | ||||||
| { |  | ||||||
|     CryptoDevBackendClass *bc = CRYPTODEV_BACKEND_CLASS(oc); |  | ||||||
|  |  | ||||||
|     bc->init = cryptodev_builtin_init; |  | ||||||
|     bc->cleanup = cryptodev_builtin_cleanup; |  | ||||||
|     bc->create_session = cryptodev_builtin_sym_create_session; |  | ||||||
|     bc->close_session = cryptodev_builtin_sym_close_session; |  | ||||||
|     bc->do_sym_op = cryptodev_builtin_sym_operation; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static const TypeInfo cryptodev_builtin_info = { |  | ||||||
|     .name = TYPE_CRYPTODEV_BACKEND_BUILTIN, |  | ||||||
|     .parent = TYPE_CRYPTODEV_BACKEND, |  | ||||||
|     .class_init = cryptodev_builtin_class_init, |  | ||||||
|     .instance_size = sizeof(CryptoDevBackendBuiltin), |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static void |  | ||||||
| cryptodev_builtin_register_types(void) |  | ||||||
| { |  | ||||||
|     type_register_static(&cryptodev_builtin_info); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type_init(cryptodev_builtin_register_types); |  | ||||||
| @@ -1,377 +0,0 @@ | |||||||
| /* |  | ||||||
|  * QEMU Cryptodev backend for QEMU cipher APIs |  | ||||||
|  * |  | ||||||
|  * Copyright (c) 2016 HUAWEI TECHNOLOGIES CO., LTD. |  | ||||||
|  * |  | ||||||
|  * Authors: |  | ||||||
|  *    Gonglei <arei.gonglei@huawei.com> |  | ||||||
|  * |  | ||||||
|  * 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 "hw/boards.h" |  | ||||||
| #include "qapi/error.h" |  | ||||||
| #include "qapi/qmp/qerror.h" |  | ||||||
| #include "qemu/error-report.h" |  | ||||||
| #include "standard-headers/linux/virtio_crypto.h" |  | ||||||
| #include "sysemu/cryptodev-vhost.h" |  | ||||||
| #include "chardev/char-fe.h" |  | ||||||
| #include "sysemu/cryptodev-vhost-user.h" |  | ||||||
|  |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * @TYPE_CRYPTODEV_BACKEND_VHOST_USER: |  | ||||||
|  * name of backend that uses vhost user server |  | ||||||
|  */ |  | ||||||
| #define TYPE_CRYPTODEV_BACKEND_VHOST_USER "cryptodev-vhost-user" |  | ||||||
|  |  | ||||||
| #define CRYPTODEV_BACKEND_VHOST_USER(obj) \ |  | ||||||
|     OBJECT_CHECK(CryptoDevBackendVhostUser, \ |  | ||||||
|                  (obj), TYPE_CRYPTODEV_BACKEND_VHOST_USER) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| typedef struct CryptoDevBackendVhostUser { |  | ||||||
|     CryptoDevBackend parent_obj; |  | ||||||
|  |  | ||||||
|     CharBackend chr; |  | ||||||
|     char *chr_name; |  | ||||||
|     bool opened; |  | ||||||
|     CryptoDevBackendVhost *vhost_crypto[MAX_CRYPTO_QUEUE_NUM]; |  | ||||||
| } CryptoDevBackendVhostUser; |  | ||||||
|  |  | ||||||
| static int |  | ||||||
| cryptodev_vhost_user_running( |  | ||||||
|              CryptoDevBackendVhost *crypto) |  | ||||||
| { |  | ||||||
|     return crypto ? 1 : 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| CryptoDevBackendVhost * |  | ||||||
| cryptodev_vhost_user_get_vhost( |  | ||||||
|                          CryptoDevBackendClient *cc, |  | ||||||
|                          CryptoDevBackend *b, |  | ||||||
|                          uint16_t queue) |  | ||||||
| { |  | ||||||
|     CryptoDevBackendVhostUser *s = |  | ||||||
|                       CRYPTODEV_BACKEND_VHOST_USER(b); |  | ||||||
|     assert(cc->type == CRYPTODEV_BACKEND_TYPE_VHOST_USER); |  | ||||||
|     assert(queue < MAX_CRYPTO_QUEUE_NUM); |  | ||||||
|  |  | ||||||
|     return s->vhost_crypto[queue]; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void cryptodev_vhost_user_stop(int queues, |  | ||||||
|                           CryptoDevBackendVhostUser *s) |  | ||||||
| { |  | ||||||
|     size_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < queues; i++) { |  | ||||||
|         if (!cryptodev_vhost_user_running(s->vhost_crypto[i])) { |  | ||||||
|             continue; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         cryptodev_vhost_cleanup(s->vhost_crypto[i]); |  | ||||||
|         s->vhost_crypto[i] = NULL; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int |  | ||||||
| cryptodev_vhost_user_start(int queues, |  | ||||||
|                          CryptoDevBackendVhostUser *s) |  | ||||||
| { |  | ||||||
|     CryptoDevBackendVhostOptions options; |  | ||||||
|     CryptoDevBackend *b = CRYPTODEV_BACKEND(s); |  | ||||||
|     int max_queues; |  | ||||||
|     size_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < queues; i++) { |  | ||||||
|         if (cryptodev_vhost_user_running(s->vhost_crypto[i])) { |  | ||||||
|             continue; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         options.opaque = &s->chr; |  | ||||||
|         options.backend_type = VHOST_BACKEND_TYPE_USER; |  | ||||||
|         options.cc = b->conf.peers.ccs[i]; |  | ||||||
|         s->vhost_crypto[i] = cryptodev_vhost_init(&options); |  | ||||||
|         if (!s->vhost_crypto[i]) { |  | ||||||
|             error_report("failed to init vhost_crypto for queue %zu", i); |  | ||||||
|             goto err; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if (i == 0) { |  | ||||||
|             max_queues = |  | ||||||
|               cryptodev_vhost_get_max_queues(s->vhost_crypto[i]); |  | ||||||
|             if (queues > max_queues) { |  | ||||||
|                 error_report("you are asking more queues than supported: %d", |  | ||||||
|                              max_queues); |  | ||||||
|                 goto err; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return 0; |  | ||||||
|  |  | ||||||
| err: |  | ||||||
|     cryptodev_vhost_user_stop(i + 1, s); |  | ||||||
|     return -1; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static Chardev * |  | ||||||
| cryptodev_vhost_claim_chardev(CryptoDevBackendVhostUser *s, |  | ||||||
|                                     Error **errp) |  | ||||||
| { |  | ||||||
|     Chardev *chr; |  | ||||||
|  |  | ||||||
|     if (s->chr_name == NULL) { |  | ||||||
|         error_setg(errp, QERR_INVALID_PARAMETER_VALUE, |  | ||||||
|                    "chardev", "a valid character device"); |  | ||||||
|         return NULL; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     chr = qemu_chr_find(s->chr_name); |  | ||||||
|     if (chr == NULL) { |  | ||||||
|         error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, |  | ||||||
|                   "Device '%s' not found", s->chr_name); |  | ||||||
|         return NULL; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return chr; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void cryptodev_vhost_user_event(void *opaque, int event) |  | ||||||
| { |  | ||||||
|     CryptoDevBackendVhostUser *s = opaque; |  | ||||||
|     CryptoDevBackend *b = CRYPTODEV_BACKEND(s); |  | ||||||
|     Error *err = NULL; |  | ||||||
|     int queues = b->conf.peers.queues; |  | ||||||
|  |  | ||||||
|     assert(queues < MAX_CRYPTO_QUEUE_NUM); |  | ||||||
|  |  | ||||||
|     switch (event) { |  | ||||||
|     case CHR_EVENT_OPENED: |  | ||||||
|         if (cryptodev_vhost_user_start(queues, s) < 0) { |  | ||||||
|             exit(1); |  | ||||||
|         } |  | ||||||
|         b->ready = true; |  | ||||||
|         break; |  | ||||||
|     case CHR_EVENT_CLOSED: |  | ||||||
|         b->ready = false; |  | ||||||
|         cryptodev_vhost_user_stop(queues, s); |  | ||||||
|         break; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (err) { |  | ||||||
|         error_report_err(err); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void cryptodev_vhost_user_init( |  | ||||||
|              CryptoDevBackend *backend, Error **errp) |  | ||||||
| { |  | ||||||
|     int queues = backend->conf.peers.queues; |  | ||||||
|     size_t i; |  | ||||||
|     Error *local_err = NULL; |  | ||||||
|     Chardev *chr; |  | ||||||
|     CryptoDevBackendClient *cc; |  | ||||||
|     CryptoDevBackendVhostUser *s = |  | ||||||
|                       CRYPTODEV_BACKEND_VHOST_USER(backend); |  | ||||||
|  |  | ||||||
|     chr = cryptodev_vhost_claim_chardev(s, &local_err); |  | ||||||
|     if (local_err) { |  | ||||||
|         error_propagate(errp, local_err); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     s->opened = true; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < queues; i++) { |  | ||||||
|         cc = cryptodev_backend_new_client( |  | ||||||
|                   "cryptodev-vhost-user", NULL); |  | ||||||
|         cc->info_str = g_strdup_printf("cryptodev-vhost-user%zu to %s ", |  | ||||||
|                                        i, chr->label); |  | ||||||
|         cc->queue_index = i; |  | ||||||
|         cc->type = CRYPTODEV_BACKEND_TYPE_VHOST_USER; |  | ||||||
|  |  | ||||||
|         backend->conf.peers.ccs[i] = cc; |  | ||||||
|  |  | ||||||
|         if (i == 0) { |  | ||||||
|             if (!qemu_chr_fe_init(&s->chr, chr, &local_err)) { |  | ||||||
|                 error_propagate(errp, local_err); |  | ||||||
|                 return; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     qemu_chr_fe_set_handlers(&s->chr, NULL, NULL, |  | ||||||
|                      cryptodev_vhost_user_event, NULL, s, NULL, true); |  | ||||||
|  |  | ||||||
|     backend->conf.crypto_services = |  | ||||||
|                          1u << VIRTIO_CRYPTO_SERVICE_CIPHER | |  | ||||||
|                          1u << VIRTIO_CRYPTO_SERVICE_HASH | |  | ||||||
|                          1u << VIRTIO_CRYPTO_SERVICE_MAC; |  | ||||||
|     backend->conf.cipher_algo_l = 1u << VIRTIO_CRYPTO_CIPHER_AES_CBC; |  | ||||||
|     backend->conf.hash_algo = 1u << VIRTIO_CRYPTO_HASH_SHA1; |  | ||||||
|  |  | ||||||
|     backend->conf.max_size = UINT64_MAX; |  | ||||||
|     backend->conf.max_cipher_key_len = VHOST_USER_MAX_CIPHER_KEY_LEN; |  | ||||||
|     backend->conf.max_auth_key_len = VHOST_USER_MAX_AUTH_KEY_LEN; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int64_t cryptodev_vhost_user_sym_create_session( |  | ||||||
|            CryptoDevBackend *backend, |  | ||||||
|            CryptoDevBackendSymSessionInfo *sess_info, |  | ||||||
|            uint32_t queue_index, Error **errp) |  | ||||||
| { |  | ||||||
|     CryptoDevBackendClient *cc = |  | ||||||
|                    backend->conf.peers.ccs[queue_index]; |  | ||||||
|     CryptoDevBackendVhost *vhost_crypto; |  | ||||||
|     uint64_t session_id = 0; |  | ||||||
|     int ret; |  | ||||||
|  |  | ||||||
|     vhost_crypto = cryptodev_vhost_user_get_vhost(cc, backend, queue_index); |  | ||||||
|     if (vhost_crypto) { |  | ||||||
|         struct vhost_dev *dev = &(vhost_crypto->dev); |  | ||||||
|         ret = dev->vhost_ops->vhost_crypto_create_session(dev, |  | ||||||
|                                                           sess_info, |  | ||||||
|                                                           &session_id); |  | ||||||
|         if (ret < 0) { |  | ||||||
|             return -1; |  | ||||||
|         } else { |  | ||||||
|             return session_id; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     return -1; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int cryptodev_vhost_user_sym_close_session( |  | ||||||
|            CryptoDevBackend *backend, |  | ||||||
|            uint64_t session_id, |  | ||||||
|            uint32_t queue_index, Error **errp) |  | ||||||
| { |  | ||||||
|     CryptoDevBackendClient *cc = |  | ||||||
|                   backend->conf.peers.ccs[queue_index]; |  | ||||||
|     CryptoDevBackendVhost *vhost_crypto; |  | ||||||
|     int ret; |  | ||||||
|  |  | ||||||
|     vhost_crypto = cryptodev_vhost_user_get_vhost(cc, backend, queue_index); |  | ||||||
|     if (vhost_crypto) { |  | ||||||
|         struct vhost_dev *dev = &(vhost_crypto->dev); |  | ||||||
|         ret = dev->vhost_ops->vhost_crypto_close_session(dev, |  | ||||||
|                                                          session_id); |  | ||||||
|         if (ret < 0) { |  | ||||||
|             return -1; |  | ||||||
|         } else { |  | ||||||
|             return 0; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     return -1; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void cryptodev_vhost_user_cleanup( |  | ||||||
|              CryptoDevBackend *backend, |  | ||||||
|              Error **errp) |  | ||||||
| { |  | ||||||
|     CryptoDevBackendVhostUser *s = |  | ||||||
|                       CRYPTODEV_BACKEND_VHOST_USER(backend); |  | ||||||
|     size_t i; |  | ||||||
|     int queues = backend->conf.peers.queues; |  | ||||||
|     CryptoDevBackendClient *cc; |  | ||||||
|  |  | ||||||
|     cryptodev_vhost_user_stop(queues, s); |  | ||||||
|  |  | ||||||
|     for (i = 0; i < queues; i++) { |  | ||||||
|         cc = backend->conf.peers.ccs[i]; |  | ||||||
|         if (cc) { |  | ||||||
|             cryptodev_backend_free_client(cc); |  | ||||||
|             backend->conf.peers.ccs[i] = NULL; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void cryptodev_vhost_user_set_chardev(Object *obj, |  | ||||||
|                                     const char *value, Error **errp) |  | ||||||
| { |  | ||||||
|     CryptoDevBackendVhostUser *s = |  | ||||||
|                       CRYPTODEV_BACKEND_VHOST_USER(obj); |  | ||||||
|  |  | ||||||
|     if (s->opened) { |  | ||||||
|         error_setg(errp, QERR_PERMISSION_DENIED); |  | ||||||
|     } else { |  | ||||||
|         g_free(s->chr_name); |  | ||||||
|         s->chr_name = g_strdup(value); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static char * |  | ||||||
| cryptodev_vhost_user_get_chardev(Object *obj, Error **errp) |  | ||||||
| { |  | ||||||
|     CryptoDevBackendVhostUser *s = |  | ||||||
|                       CRYPTODEV_BACKEND_VHOST_USER(obj); |  | ||||||
|     Chardev *chr = qemu_chr_fe_get_driver(&s->chr); |  | ||||||
|  |  | ||||||
|     if (chr && chr->label) { |  | ||||||
|         return g_strdup(chr->label); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return NULL; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void cryptodev_vhost_user_instance_int(Object *obj) |  | ||||||
| { |  | ||||||
|     object_property_add_str(obj, "chardev", |  | ||||||
|                             cryptodev_vhost_user_get_chardev, |  | ||||||
|                             cryptodev_vhost_user_set_chardev, |  | ||||||
|                             NULL); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void cryptodev_vhost_user_finalize(Object *obj) |  | ||||||
| { |  | ||||||
|     CryptoDevBackendVhostUser *s = |  | ||||||
|                       CRYPTODEV_BACKEND_VHOST_USER(obj); |  | ||||||
|  |  | ||||||
|     qemu_chr_fe_deinit(&s->chr, false); |  | ||||||
|  |  | ||||||
|     g_free(s->chr_name); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void |  | ||||||
| cryptodev_vhost_user_class_init(ObjectClass *oc, void *data) |  | ||||||
| { |  | ||||||
|     CryptoDevBackendClass *bc = CRYPTODEV_BACKEND_CLASS(oc); |  | ||||||
|  |  | ||||||
|     bc->init = cryptodev_vhost_user_init; |  | ||||||
|     bc->cleanup = cryptodev_vhost_user_cleanup; |  | ||||||
|     bc->create_session = cryptodev_vhost_user_sym_create_session; |  | ||||||
|     bc->close_session = cryptodev_vhost_user_sym_close_session; |  | ||||||
|     bc->do_sym_op = NULL; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static const TypeInfo cryptodev_vhost_user_info = { |  | ||||||
|     .name = TYPE_CRYPTODEV_BACKEND_VHOST_USER, |  | ||||||
|     .parent = TYPE_CRYPTODEV_BACKEND, |  | ||||||
|     .class_init = cryptodev_vhost_user_class_init, |  | ||||||
|     .instance_init = cryptodev_vhost_user_instance_int, |  | ||||||
|     .instance_finalize = cryptodev_vhost_user_finalize, |  | ||||||
|     .instance_size = sizeof(CryptoDevBackendVhostUser), |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static void |  | ||||||
| cryptodev_vhost_user_register_types(void) |  | ||||||
| { |  | ||||||
|     type_register_static(&cryptodev_vhost_user_info); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type_init(cryptodev_vhost_user_register_types); |  | ||||||
| @@ -1,347 +0,0 @@ | |||||||
| /* |  | ||||||
|  * QEMU Cryptodev backend for QEMU cipher APIs |  | ||||||
|  * |  | ||||||
|  * Copyright (c) 2016 HUAWEI TECHNOLOGIES CO., LTD. |  | ||||||
|  * |  | ||||||
|  * Authors: |  | ||||||
|  *    Gonglei <arei.gonglei@huawei.com> |  | ||||||
|  *    Jay Zhou <jianjay.zhou@huawei.com> |  | ||||||
|  * |  | ||||||
|  * 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 "hw/virtio/virtio-bus.h" |  | ||||||
| #include "sysemu/cryptodev-vhost.h" |  | ||||||
|  |  | ||||||
| #ifdef CONFIG_VHOST_CRYPTO |  | ||||||
| #include "qapi/error.h" |  | ||||||
| #include "qapi/qmp/qerror.h" |  | ||||||
| #include "qemu/error-report.h" |  | ||||||
| #include "hw/virtio/virtio-crypto.h" |  | ||||||
| #include "sysemu/cryptodev-vhost-user.h" |  | ||||||
|  |  | ||||||
| uint64_t |  | ||||||
| cryptodev_vhost_get_max_queues( |  | ||||||
|                         CryptoDevBackendVhost *crypto) |  | ||||||
| { |  | ||||||
|     return crypto->dev.max_queues; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void cryptodev_vhost_cleanup(CryptoDevBackendVhost *crypto) |  | ||||||
| { |  | ||||||
|     vhost_dev_cleanup(&crypto->dev); |  | ||||||
|     g_free(crypto); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| struct CryptoDevBackendVhost * |  | ||||||
| cryptodev_vhost_init( |  | ||||||
|              CryptoDevBackendVhostOptions *options) |  | ||||||
| { |  | ||||||
|     int r; |  | ||||||
|     CryptoDevBackendVhost *crypto; |  | ||||||
|  |  | ||||||
|     crypto = g_new(CryptoDevBackendVhost, 1); |  | ||||||
|     crypto->dev.max_queues = 1; |  | ||||||
|     crypto->dev.nvqs = 1; |  | ||||||
|     crypto->dev.vqs = crypto->vqs; |  | ||||||
|  |  | ||||||
|     crypto->cc = options->cc; |  | ||||||
|  |  | ||||||
|     crypto->dev.protocol_features = 0; |  | ||||||
|     crypto->backend = -1; |  | ||||||
|  |  | ||||||
|     /* vhost-user needs vq_index to initiate a specific queue pair */ |  | ||||||
|     crypto->dev.vq_index = crypto->cc->queue_index * crypto->dev.nvqs; |  | ||||||
|  |  | ||||||
|     r = vhost_dev_init(&crypto->dev, options->opaque, options->backend_type, 0); |  | ||||||
|     if (r < 0) { |  | ||||||
|         goto fail; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return crypto; |  | ||||||
| fail: |  | ||||||
|     g_free(crypto); |  | ||||||
|     return NULL; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int |  | ||||||
| cryptodev_vhost_start_one(CryptoDevBackendVhost *crypto, |  | ||||||
|                                   VirtIODevice *dev) |  | ||||||
| { |  | ||||||
|     int r; |  | ||||||
|  |  | ||||||
|     crypto->dev.nvqs = 1; |  | ||||||
|     crypto->dev.vqs = crypto->vqs; |  | ||||||
|  |  | ||||||
|     r = vhost_dev_enable_notifiers(&crypto->dev, dev); |  | ||||||
|     if (r < 0) { |  | ||||||
|         goto fail_notifiers; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     r = vhost_dev_start(&crypto->dev, dev); |  | ||||||
|     if (r < 0) { |  | ||||||
|         goto fail_start; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return 0; |  | ||||||
|  |  | ||||||
| fail_start: |  | ||||||
|     vhost_dev_disable_notifiers(&crypto->dev, dev); |  | ||||||
| fail_notifiers: |  | ||||||
|     return r; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void |  | ||||||
| cryptodev_vhost_stop_one(CryptoDevBackendVhost *crypto, |  | ||||||
|                                  VirtIODevice *dev) |  | ||||||
| { |  | ||||||
|     vhost_dev_stop(&crypto->dev, dev); |  | ||||||
|     vhost_dev_disable_notifiers(&crypto->dev, dev); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| CryptoDevBackendVhost * |  | ||||||
| cryptodev_get_vhost(CryptoDevBackendClient *cc, |  | ||||||
|                             CryptoDevBackend *b, |  | ||||||
|                             uint16_t queue) |  | ||||||
| { |  | ||||||
|     CryptoDevBackendVhost *vhost_crypto = NULL; |  | ||||||
|  |  | ||||||
|     if (!cc) { |  | ||||||
|         return NULL; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     switch (cc->type) { |  | ||||||
| #if defined(CONFIG_VHOST_USER) && defined(CONFIG_LINUX) |  | ||||||
|     case CRYPTODEV_BACKEND_TYPE_VHOST_USER: |  | ||||||
|         vhost_crypto = cryptodev_vhost_user_get_vhost(cc, b, queue); |  | ||||||
|         break; |  | ||||||
| #endif |  | ||||||
|     default: |  | ||||||
|         break; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return vhost_crypto; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void |  | ||||||
| cryptodev_vhost_set_vq_index(CryptoDevBackendVhost *crypto, |  | ||||||
|                                      int vq_index) |  | ||||||
| { |  | ||||||
|     crypto->dev.vq_index = vq_index; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int |  | ||||||
| vhost_set_vring_enable(CryptoDevBackendClient *cc, |  | ||||||
|                             CryptoDevBackend *b, |  | ||||||
|                             uint16_t queue, int enable) |  | ||||||
| { |  | ||||||
|     CryptoDevBackendVhost *crypto = |  | ||||||
|                        cryptodev_get_vhost(cc, b, queue); |  | ||||||
|     const VhostOps *vhost_ops; |  | ||||||
|  |  | ||||||
|     cc->vring_enable = enable; |  | ||||||
|  |  | ||||||
|     if (!crypto) { |  | ||||||
|         return 0; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     vhost_ops = crypto->dev.vhost_ops; |  | ||||||
|     if (vhost_ops->vhost_set_vring_enable) { |  | ||||||
|         return vhost_ops->vhost_set_vring_enable(&crypto->dev, enable); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int cryptodev_vhost_start(VirtIODevice *dev, int total_queues) |  | ||||||
| { |  | ||||||
|     VirtIOCrypto *vcrypto = VIRTIO_CRYPTO(dev); |  | ||||||
|     BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(dev))); |  | ||||||
|     VirtioBusState *vbus = VIRTIO_BUS(qbus); |  | ||||||
|     VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(vbus); |  | ||||||
|     int r, e; |  | ||||||
|     int i; |  | ||||||
|     CryptoDevBackend *b = vcrypto->cryptodev; |  | ||||||
|     CryptoDevBackendVhost *vhost_crypto; |  | ||||||
|     CryptoDevBackendClient *cc; |  | ||||||
|  |  | ||||||
|     if (!k->set_guest_notifiers) { |  | ||||||
|         error_report("binding does not support guest notifiers"); |  | ||||||
|         return -ENOSYS; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     for (i = 0; i < total_queues; i++) { |  | ||||||
|         cc = b->conf.peers.ccs[i]; |  | ||||||
|  |  | ||||||
|         vhost_crypto = cryptodev_get_vhost(cc, b, i); |  | ||||||
|         cryptodev_vhost_set_vq_index(vhost_crypto, i); |  | ||||||
|  |  | ||||||
|         /* Suppress the masking guest notifiers on vhost user |  | ||||||
|          * because vhost user doesn't interrupt masking/unmasking |  | ||||||
|          * properly. |  | ||||||
|          */ |  | ||||||
|         if (cc->type == CRYPTODEV_BACKEND_TYPE_VHOST_USER) { |  | ||||||
|             dev->use_guest_notifier_mask = false; |  | ||||||
|         } |  | ||||||
|      } |  | ||||||
|  |  | ||||||
|     r = k->set_guest_notifiers(qbus->parent, total_queues, true); |  | ||||||
|     if (r < 0) { |  | ||||||
|         error_report("error binding guest notifier: %d", -r); |  | ||||||
|         goto err; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     for (i = 0; i < total_queues; i++) { |  | ||||||
|         cc = b->conf.peers.ccs[i]; |  | ||||||
|  |  | ||||||
|         vhost_crypto = cryptodev_get_vhost(cc, b, i); |  | ||||||
|         r = cryptodev_vhost_start_one(vhost_crypto, dev); |  | ||||||
|  |  | ||||||
|         if (r < 0) { |  | ||||||
|             goto err_start; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if (cc->vring_enable) { |  | ||||||
|             /* restore vring enable state */ |  | ||||||
|             r = vhost_set_vring_enable(cc, b, i, cc->vring_enable); |  | ||||||
|  |  | ||||||
|             if (r < 0) { |  | ||||||
|                 goto err_start; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return 0; |  | ||||||
|  |  | ||||||
| err_start: |  | ||||||
|     while (--i >= 0) { |  | ||||||
|         cc = b->conf.peers.ccs[i]; |  | ||||||
|         vhost_crypto = cryptodev_get_vhost(cc, b, i); |  | ||||||
|         cryptodev_vhost_stop_one(vhost_crypto, dev); |  | ||||||
|     } |  | ||||||
|     e = k->set_guest_notifiers(qbus->parent, total_queues, false); |  | ||||||
|     if (e < 0) { |  | ||||||
|         error_report("vhost guest notifier cleanup failed: %d", e); |  | ||||||
|     } |  | ||||||
| err: |  | ||||||
|     return r; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void cryptodev_vhost_stop(VirtIODevice *dev, int total_queues) |  | ||||||
| { |  | ||||||
|     BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(dev))); |  | ||||||
|     VirtioBusState *vbus = VIRTIO_BUS(qbus); |  | ||||||
|     VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(vbus); |  | ||||||
|     VirtIOCrypto *vcrypto = VIRTIO_CRYPTO(dev); |  | ||||||
|     CryptoDevBackend *b = vcrypto->cryptodev; |  | ||||||
|     CryptoDevBackendVhost *vhost_crypto; |  | ||||||
|     CryptoDevBackendClient *cc; |  | ||||||
|     size_t i; |  | ||||||
|     int r; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < total_queues; i++) { |  | ||||||
|         cc = b->conf.peers.ccs[i]; |  | ||||||
|  |  | ||||||
|         vhost_crypto = cryptodev_get_vhost(cc, b, i); |  | ||||||
|         cryptodev_vhost_stop_one(vhost_crypto, dev); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     r = k->set_guest_notifiers(qbus->parent, total_queues, false); |  | ||||||
|     if (r < 0) { |  | ||||||
|         error_report("vhost guest notifier cleanup failed: %d", r); |  | ||||||
|     } |  | ||||||
|     assert(r >= 0); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void cryptodev_vhost_virtqueue_mask(VirtIODevice *dev, |  | ||||||
|                                            int queue, |  | ||||||
|                                            int idx, bool mask) |  | ||||||
| { |  | ||||||
|     VirtIOCrypto *vcrypto = VIRTIO_CRYPTO(dev); |  | ||||||
|     CryptoDevBackend *b = vcrypto->cryptodev; |  | ||||||
|     CryptoDevBackendVhost *vhost_crypto; |  | ||||||
|     CryptoDevBackendClient *cc; |  | ||||||
|  |  | ||||||
|     assert(queue < MAX_CRYPTO_QUEUE_NUM); |  | ||||||
|  |  | ||||||
|     cc = b->conf.peers.ccs[queue]; |  | ||||||
|     vhost_crypto = cryptodev_get_vhost(cc, b, queue); |  | ||||||
|  |  | ||||||
|     vhost_virtqueue_mask(&vhost_crypto->dev, dev, idx, mask); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| bool cryptodev_vhost_virtqueue_pending(VirtIODevice *dev, |  | ||||||
|                                               int queue, int idx) |  | ||||||
| { |  | ||||||
|     VirtIOCrypto *vcrypto = VIRTIO_CRYPTO(dev); |  | ||||||
|     CryptoDevBackend *b = vcrypto->cryptodev; |  | ||||||
|     CryptoDevBackendVhost *vhost_crypto; |  | ||||||
|     CryptoDevBackendClient *cc; |  | ||||||
|  |  | ||||||
|     assert(queue < MAX_CRYPTO_QUEUE_NUM); |  | ||||||
|  |  | ||||||
|     cc = b->conf.peers.ccs[queue]; |  | ||||||
|     vhost_crypto = cryptodev_get_vhost(cc, b, queue); |  | ||||||
|  |  | ||||||
|     return vhost_virtqueue_pending(&vhost_crypto->dev, idx); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #else |  | ||||||
| uint64_t |  | ||||||
| cryptodev_vhost_get_max_queues(CryptoDevBackendVhost *crypto) |  | ||||||
| { |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void cryptodev_vhost_cleanup(CryptoDevBackendVhost *crypto) |  | ||||||
| { |  | ||||||
| } |  | ||||||
|  |  | ||||||
| struct CryptoDevBackendVhost * |  | ||||||
| cryptodev_vhost_init(CryptoDevBackendVhostOptions *options) |  | ||||||
| { |  | ||||||
|     return NULL; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| CryptoDevBackendVhost * |  | ||||||
| cryptodev_get_vhost(CryptoDevBackendClient *cc, |  | ||||||
|                     CryptoDevBackend *b, |  | ||||||
|                     uint16_t queue) |  | ||||||
| { |  | ||||||
|     return NULL; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int cryptodev_vhost_start(VirtIODevice *dev, int total_queues) |  | ||||||
| { |  | ||||||
|     return -1; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void cryptodev_vhost_stop(VirtIODevice *dev, int total_queues) |  | ||||||
| { |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void cryptodev_vhost_virtqueue_mask(VirtIODevice *dev, |  | ||||||
|                                     int queue, |  | ||||||
|                                     int idx, bool mask) |  | ||||||
| { |  | ||||||
| } |  | ||||||
|  |  | ||||||
| bool cryptodev_vhost_virtqueue_pending(VirtIODevice *dev, |  | ||||||
|                                        int queue, int idx) |  | ||||||
| { |  | ||||||
|     return false; |  | ||||||
| } |  | ||||||
| #endif |  | ||||||
| @@ -1,269 +0,0 @@ | |||||||
| /* |  | ||||||
|  * QEMU Crypto Device Implementation |  | ||||||
|  * |  | ||||||
|  * Copyright (c) 2016 HUAWEI TECHNOLOGIES CO., LTD. |  | ||||||
|  * |  | ||||||
|  * Authors: |  | ||||||
|  *    Gonglei <arei.gonglei@huawei.com> |  | ||||||
|  * |  | ||||||
|  * 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 "sysemu/cryptodev.h" |  | ||||||
| #include "hw/boards.h" |  | ||||||
| #include "qapi/error.h" |  | ||||||
| #include "qapi/visitor.h" |  | ||||||
| #include "qemu/config-file.h" |  | ||||||
| #include "qom/object_interfaces.h" |  | ||||||
| #include "hw/virtio/virtio-crypto.h" |  | ||||||
|  |  | ||||||
|  |  | ||||||
| static QTAILQ_HEAD(, CryptoDevBackendClient) crypto_clients; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| CryptoDevBackendClient * |  | ||||||
| cryptodev_backend_new_client(const char *model, |  | ||||||
|                                     const char *name) |  | ||||||
| { |  | ||||||
|     CryptoDevBackendClient *cc; |  | ||||||
|  |  | ||||||
|     cc = g_malloc0(sizeof(CryptoDevBackendClient)); |  | ||||||
|     cc->model = g_strdup(model); |  | ||||||
|     if (name) { |  | ||||||
|         cc->name = g_strdup(name); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     QTAILQ_INSERT_TAIL(&crypto_clients, cc, next); |  | ||||||
|  |  | ||||||
|     return cc; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void cryptodev_backend_free_client( |  | ||||||
|                   CryptoDevBackendClient *cc) |  | ||||||
| { |  | ||||||
|     QTAILQ_REMOVE(&crypto_clients, cc, next); |  | ||||||
|     g_free(cc->name); |  | ||||||
|     g_free(cc->model); |  | ||||||
|     g_free(cc->info_str); |  | ||||||
|     g_free(cc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void cryptodev_backend_cleanup( |  | ||||||
|              CryptoDevBackend *backend, |  | ||||||
|              Error **errp) |  | ||||||
| { |  | ||||||
|     CryptoDevBackendClass *bc = |  | ||||||
|                   CRYPTODEV_BACKEND_GET_CLASS(backend); |  | ||||||
|  |  | ||||||
|     if (bc->cleanup) { |  | ||||||
|         bc->cleanup(backend, errp); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int64_t cryptodev_backend_sym_create_session( |  | ||||||
|            CryptoDevBackend *backend, |  | ||||||
|            CryptoDevBackendSymSessionInfo *sess_info, |  | ||||||
|            uint32_t queue_index, Error **errp) |  | ||||||
| { |  | ||||||
|     CryptoDevBackendClass *bc = |  | ||||||
|                       CRYPTODEV_BACKEND_GET_CLASS(backend); |  | ||||||
|  |  | ||||||
|     if (bc->create_session) { |  | ||||||
|         return bc->create_session(backend, sess_info, queue_index, errp); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return -1; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int cryptodev_backend_sym_close_session( |  | ||||||
|            CryptoDevBackend *backend, |  | ||||||
|            uint64_t session_id, |  | ||||||
|            uint32_t queue_index, Error **errp) |  | ||||||
| { |  | ||||||
|     CryptoDevBackendClass *bc = |  | ||||||
|                       CRYPTODEV_BACKEND_GET_CLASS(backend); |  | ||||||
|  |  | ||||||
|     if (bc->close_session) { |  | ||||||
|         return bc->close_session(backend, session_id, queue_index, errp); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return -1; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int cryptodev_backend_sym_operation( |  | ||||||
|                  CryptoDevBackend *backend, |  | ||||||
|                  CryptoDevBackendSymOpInfo *op_info, |  | ||||||
|                  uint32_t queue_index, Error **errp) |  | ||||||
| { |  | ||||||
|     CryptoDevBackendClass *bc = |  | ||||||
|                       CRYPTODEV_BACKEND_GET_CLASS(backend); |  | ||||||
|  |  | ||||||
|     if (bc->do_sym_op) { |  | ||||||
|         return bc->do_sym_op(backend, op_info, queue_index, errp); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return -VIRTIO_CRYPTO_ERR; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int cryptodev_backend_crypto_operation( |  | ||||||
|                  CryptoDevBackend *backend, |  | ||||||
|                  void *opaque, |  | ||||||
|                  uint32_t queue_index, Error **errp) |  | ||||||
| { |  | ||||||
|     VirtIOCryptoReq *req = opaque; |  | ||||||
|  |  | ||||||
|     if (req->flags == CRYPTODEV_BACKEND_ALG_SYM) { |  | ||||||
|         CryptoDevBackendSymOpInfo *op_info; |  | ||||||
|         op_info = req->u.sym_op_info; |  | ||||||
|  |  | ||||||
|         return cryptodev_backend_sym_operation(backend, |  | ||||||
|                          op_info, queue_index, errp); |  | ||||||
|     } else { |  | ||||||
|         error_setg(errp, "Unsupported cryptodev alg type: %" PRIu32 "", |  | ||||||
|                    req->flags); |  | ||||||
|        return -VIRTIO_CRYPTO_NOTSUPP; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return -VIRTIO_CRYPTO_ERR; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void |  | ||||||
| cryptodev_backend_get_queues(Object *obj, Visitor *v, const char *name, |  | ||||||
|                              void *opaque, Error **errp) |  | ||||||
| { |  | ||||||
|     CryptoDevBackend *backend = CRYPTODEV_BACKEND(obj); |  | ||||||
|     uint32_t value = backend->conf.peers.queues; |  | ||||||
|  |  | ||||||
|     visit_type_uint32(v, name, &value, errp); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void |  | ||||||
| cryptodev_backend_set_queues(Object *obj, Visitor *v, const char *name, |  | ||||||
|                              void *opaque, Error **errp) |  | ||||||
| { |  | ||||||
|     CryptoDevBackend *backend = CRYPTODEV_BACKEND(obj); |  | ||||||
|     Error *local_err = NULL; |  | ||||||
|     uint32_t value; |  | ||||||
|  |  | ||||||
|     visit_type_uint32(v, name, &value, &local_err); |  | ||||||
|     if (local_err) { |  | ||||||
|         goto out; |  | ||||||
|     } |  | ||||||
|     if (!value) { |  | ||||||
|         error_setg(&local_err, "Property '%s.%s' doesn't take value '%" |  | ||||||
|                    PRIu32 "'", object_get_typename(obj), name, value); |  | ||||||
|         goto out; |  | ||||||
|     } |  | ||||||
|     backend->conf.peers.queues = value; |  | ||||||
| out: |  | ||||||
|     error_propagate(errp, local_err); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void |  | ||||||
| cryptodev_backend_complete(UserCreatable *uc, Error **errp) |  | ||||||
| { |  | ||||||
|     CryptoDevBackend *backend = CRYPTODEV_BACKEND(uc); |  | ||||||
|     CryptoDevBackendClass *bc = CRYPTODEV_BACKEND_GET_CLASS(uc); |  | ||||||
|     Error *local_err = NULL; |  | ||||||
|  |  | ||||||
|     if (bc->init) { |  | ||||||
|         bc->init(backend, &local_err); |  | ||||||
|         if (local_err) { |  | ||||||
|             goto out; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return; |  | ||||||
|  |  | ||||||
| out: |  | ||||||
|     error_propagate(errp, local_err); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void cryptodev_backend_set_used(CryptoDevBackend *backend, bool used) |  | ||||||
| { |  | ||||||
|     backend->is_used = used; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| bool cryptodev_backend_is_used(CryptoDevBackend *backend) |  | ||||||
| { |  | ||||||
|     return backend->is_used; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void cryptodev_backend_set_ready(CryptoDevBackend *backend, bool ready) |  | ||||||
| { |  | ||||||
|     backend->ready = ready; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| bool cryptodev_backend_is_ready(CryptoDevBackend *backend) |  | ||||||
| { |  | ||||||
|     return backend->ready; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static bool |  | ||||||
| cryptodev_backend_can_be_deleted(UserCreatable *uc) |  | ||||||
| { |  | ||||||
|     return !cryptodev_backend_is_used(CRYPTODEV_BACKEND(uc)); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void cryptodev_backend_instance_init(Object *obj) |  | ||||||
| { |  | ||||||
|     object_property_add(obj, "queues", "uint32", |  | ||||||
|                           cryptodev_backend_get_queues, |  | ||||||
|                           cryptodev_backend_set_queues, |  | ||||||
|                           NULL, NULL, NULL); |  | ||||||
|     /* Initialize devices' queues property to 1 */ |  | ||||||
|     object_property_set_int(obj, 1, "queues", NULL); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void cryptodev_backend_finalize(Object *obj) |  | ||||||
| { |  | ||||||
|     CryptoDevBackend *backend = CRYPTODEV_BACKEND(obj); |  | ||||||
|  |  | ||||||
|     cryptodev_backend_cleanup(backend, NULL); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void |  | ||||||
| cryptodev_backend_class_init(ObjectClass *oc, void *data) |  | ||||||
| { |  | ||||||
|     UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); |  | ||||||
|  |  | ||||||
|     ucc->complete = cryptodev_backend_complete; |  | ||||||
|     ucc->can_be_deleted = cryptodev_backend_can_be_deleted; |  | ||||||
|  |  | ||||||
|     QTAILQ_INIT(&crypto_clients); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static const TypeInfo cryptodev_backend_info = { |  | ||||||
|     .name = TYPE_CRYPTODEV_BACKEND, |  | ||||||
|     .parent = TYPE_OBJECT, |  | ||||||
|     .instance_size = sizeof(CryptoDevBackend), |  | ||||||
|     .instance_init = cryptodev_backend_instance_init, |  | ||||||
|     .instance_finalize = cryptodev_backend_finalize, |  | ||||||
|     .class_size = sizeof(CryptoDevBackendClass), |  | ||||||
|     .class_init = cryptodev_backend_class_init, |  | ||||||
|     .interfaces = (InterfaceInfo[]) { |  | ||||||
|         { TYPE_USER_CREATABLE }, |  | ||||||
|         { } |  | ||||||
|     } |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static void |  | ||||||
| cryptodev_backend_register_types(void) |  | ||||||
| { |  | ||||||
|     type_register_static(&cryptodev_backend_info); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type_init(cryptodev_backend_register_types); |  | ||||||
| @@ -1,186 +0,0 @@ | |||||||
| /* |  | ||||||
|  * QEMU Host Memory Backend for hugetlbfs |  | ||||||
|  * |  | ||||||
|  * Copyright (C) 2013-2014 Red Hat Inc |  | ||||||
|  * |  | ||||||
|  * Authors: |  | ||||||
|  *   Paolo Bonzini <pbonzini@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 "qemu-common.h" |  | ||||||
| #include "sysemu/hostmem.h" |  | ||||||
| #include "sysemu/sysemu.h" |  | ||||||
| #include "qom/object_interfaces.h" |  | ||||||
|  |  | ||||||
| /* hostmem-file.c */ |  | ||||||
| /** |  | ||||||
|  * @TYPE_MEMORY_BACKEND_FILE: |  | ||||||
|  * name of backend that uses mmap on a file descriptor |  | ||||||
|  */ |  | ||||||
| #define TYPE_MEMORY_BACKEND_FILE "memory-backend-file" |  | ||||||
|  |  | ||||||
| #define MEMORY_BACKEND_FILE(obj) \ |  | ||||||
|     OBJECT_CHECK(HostMemoryBackendFile, (obj), TYPE_MEMORY_BACKEND_FILE) |  | ||||||
|  |  | ||||||
| typedef struct HostMemoryBackendFile HostMemoryBackendFile; |  | ||||||
|  |  | ||||||
| struct HostMemoryBackendFile { |  | ||||||
|     HostMemoryBackend parent_obj; |  | ||||||
|  |  | ||||||
|     bool discard_data; |  | ||||||
|     char *mem_path; |  | ||||||
|     uint64_t align; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static void |  | ||||||
| file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) |  | ||||||
| { |  | ||||||
|     HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(backend); |  | ||||||
|  |  | ||||||
|     if (!backend->size) { |  | ||||||
|         error_setg(errp, "can't create backend with size 0"); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|     if (!fb->mem_path) { |  | ||||||
|         error_setg(errp, "mem-path property not set"); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
| #ifndef CONFIG_LINUX |  | ||||||
|     error_setg(errp, "-mem-path not supported on this host"); |  | ||||||
| #else |  | ||||||
|     if (!host_memory_backend_mr_inited(backend)) { |  | ||||||
|         gchar *path; |  | ||||||
|         backend->force_prealloc = mem_prealloc; |  | ||||||
|         path = object_get_canonical_path(OBJECT(backend)); |  | ||||||
|         memory_region_init_ram_from_file(&backend->mr, OBJECT(backend), |  | ||||||
|                                  path, |  | ||||||
|                                  backend->size, fb->align, backend->share, |  | ||||||
|                                  fb->mem_path, errp); |  | ||||||
|         g_free(path); |  | ||||||
|     } |  | ||||||
| #endif |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static char *get_mem_path(Object *o, Error **errp) |  | ||||||
| { |  | ||||||
|     HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o); |  | ||||||
|  |  | ||||||
|     return g_strdup(fb->mem_path); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void set_mem_path(Object *o, const char *str, 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 value"); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|     g_free(fb->mem_path); |  | ||||||
|     fb->mem_path = g_strdup(str); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static bool file_memory_backend_get_discard_data(Object *o, Error **errp) |  | ||||||
| { |  | ||||||
|     return MEMORY_BACKEND_FILE(o)->discard_data; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void file_memory_backend_set_discard_data(Object *o, bool value, |  | ||||||
|                                                Error **errp) |  | ||||||
| { |  | ||||||
|     MEMORY_BACKEND_FILE(o)->discard_data = value; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void file_memory_backend_get_align(Object *o, Visitor *v, |  | ||||||
|                                           const char *name, void *opaque, |  | ||||||
|                                           Error **errp) |  | ||||||
| { |  | ||||||
|     HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o); |  | ||||||
|     uint64_t val = fb->align; |  | ||||||
|  |  | ||||||
|     visit_type_size(v, name, &val, errp); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void file_memory_backend_set_align(Object *o, Visitor *v, |  | ||||||
|                                           const char *name, void *opaque, |  | ||||||
|                                           Error **errp) |  | ||||||
| { |  | ||||||
|     HostMemoryBackend *backend = MEMORY_BACKEND(o); |  | ||||||
|     HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o); |  | ||||||
|     Error *local_err = NULL; |  | ||||||
|     uint64_t val; |  | ||||||
|  |  | ||||||
|     if (host_memory_backend_mr_inited(backend)) { |  | ||||||
|         error_setg(&local_err, "cannot change property value"); |  | ||||||
|         goto out; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     visit_type_size(v, name, &val, &local_err); |  | ||||||
|     if (local_err) { |  | ||||||
|         goto out; |  | ||||||
|     } |  | ||||||
|     fb->align = val; |  | ||||||
|  |  | ||||||
|  out: |  | ||||||
|     error_propagate(errp, local_err); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void file_backend_unparent(Object *obj) |  | ||||||
| { |  | ||||||
|     HostMemoryBackend *backend = MEMORY_BACKEND(obj); |  | ||||||
|     HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(obj); |  | ||||||
|  |  | ||||||
|     if (host_memory_backend_mr_inited(backend) && fb->discard_data) { |  | ||||||
|         void *ptr = memory_region_get_ram_ptr(&backend->mr); |  | ||||||
|         uint64_t sz = memory_region_size(&backend->mr); |  | ||||||
|  |  | ||||||
|         qemu_madvise(ptr, sz, QEMU_MADV_REMOVE); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void |  | ||||||
| file_backend_class_init(ObjectClass *oc, void *data) |  | ||||||
| { |  | ||||||
|     HostMemoryBackendClass *bc = MEMORY_BACKEND_CLASS(oc); |  | ||||||
|  |  | ||||||
|     bc->alloc = file_backend_memory_alloc; |  | ||||||
|     oc->unparent = file_backend_unparent; |  | ||||||
|  |  | ||||||
|     object_class_property_add_bool(oc, "discard-data", |  | ||||||
|         file_memory_backend_get_discard_data, file_memory_backend_set_discard_data, |  | ||||||
|         &error_abort); |  | ||||||
|     object_class_property_add_str(oc, "mem-path", |  | ||||||
|         get_mem_path, set_mem_path, |  | ||||||
|         &error_abort); |  | ||||||
|     object_class_property_add(oc, "align", "int", |  | ||||||
|         file_memory_backend_get_align, |  | ||||||
|         file_memory_backend_set_align, |  | ||||||
|         NULL, NULL, &error_abort); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void file_backend_instance_finalize(Object *o) |  | ||||||
| { |  | ||||||
|     HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o); |  | ||||||
|  |  | ||||||
|     g_free(fb->mem_path); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static const TypeInfo file_backend_info = { |  | ||||||
|     .name = TYPE_MEMORY_BACKEND_FILE, |  | ||||||
|     .parent = TYPE_MEMORY_BACKEND, |  | ||||||
|     .class_init = file_backend_class_init, |  | ||||||
|     .instance_finalize = file_backend_instance_finalize, |  | ||||||
|     .instance_size = sizeof(HostMemoryBackendFile), |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static void register_types(void) |  | ||||||
| { |  | ||||||
|     type_register_static(&file_backend_info); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type_init(register_types); |  | ||||||
| @@ -1,170 +0,0 @@ | |||||||
| /* |  | ||||||
|  * QEMU host memfd memory 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 "qemu-common.h" |  | ||||||
| #include "sysemu/hostmem.h" |  | ||||||
| #include "sysemu/sysemu.h" |  | ||||||
| #include "qom/object_interfaces.h" |  | ||||||
| #include "qemu/memfd.h" |  | ||||||
| #include "qapi/error.h" |  | ||||||
|  |  | ||||||
| #define TYPE_MEMORY_BACKEND_MEMFD "memory-backend-memfd" |  | ||||||
|  |  | ||||||
| #define MEMORY_BACKEND_MEMFD(obj)                                        \ |  | ||||||
|     OBJECT_CHECK(HostMemoryBackendMemfd, (obj), TYPE_MEMORY_BACKEND_MEMFD) |  | ||||||
|  |  | ||||||
| typedef struct HostMemoryBackendMemfd HostMemoryBackendMemfd; |  | ||||||
|  |  | ||||||
| struct HostMemoryBackendMemfd { |  | ||||||
|     HostMemoryBackend parent_obj; |  | ||||||
|  |  | ||||||
|     bool hugetlb; |  | ||||||
|     uint64_t hugetlbsize; |  | ||||||
|     bool seal; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static void |  | ||||||
| memfd_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) |  | ||||||
| { |  | ||||||
|     HostMemoryBackendMemfd *m = MEMORY_BACKEND_MEMFD(backend); |  | ||||||
|     char *name; |  | ||||||
|     int fd; |  | ||||||
|  |  | ||||||
|     if (!backend->size) { |  | ||||||
|         error_setg(errp, "can't create backend with size 0"); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (host_memory_backend_mr_inited(backend)) { |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     backend->force_prealloc = mem_prealloc; |  | ||||||
|     fd = qemu_memfd_create(TYPE_MEMORY_BACKEND_MEMFD, backend->size, |  | ||||||
|                            m->hugetlb, m->hugetlbsize, m->seal ? |  | ||||||
|                            F_SEAL_GROW | F_SEAL_SHRINK | F_SEAL_SEAL : 0, |  | ||||||
|                            errp); |  | ||||||
|     if (fd == -1) { |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     name = object_get_canonical_path(OBJECT(backend)); |  | ||||||
|     memory_region_init_ram_from_fd(&backend->mr, OBJECT(backend), |  | ||||||
|                                    name, backend->size, true, fd, errp); |  | ||||||
|     g_free(name); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static bool |  | ||||||
| memfd_backend_get_hugetlb(Object *o, Error **errp) |  | ||||||
| { |  | ||||||
|     return MEMORY_BACKEND_MEMFD(o)->hugetlb; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void |  | ||||||
| memfd_backend_set_hugetlb(Object *o, bool value, Error **errp) |  | ||||||
| { |  | ||||||
|     MEMORY_BACKEND_MEMFD(o)->hugetlb = value; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void |  | ||||||
| memfd_backend_set_hugetlbsize(Object *obj, Visitor *v, const char *name, |  | ||||||
|                               void *opaque, Error **errp) |  | ||||||
| { |  | ||||||
|     HostMemoryBackendMemfd *m = MEMORY_BACKEND_MEMFD(obj); |  | ||||||
|     Error *local_err = NULL; |  | ||||||
|     uint64_t value; |  | ||||||
|  |  | ||||||
|     if (host_memory_backend_mr_inited(MEMORY_BACKEND(obj))) { |  | ||||||
|         error_setg(&local_err, "cannot change property value"); |  | ||||||
|         goto out; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     visit_type_size(v, name, &value, &local_err); |  | ||||||
|     if (local_err) { |  | ||||||
|         goto out; |  | ||||||
|     } |  | ||||||
|     if (!value) { |  | ||||||
|         error_setg(&local_err, "Property '%s.%s' doesn't take value '%" |  | ||||||
|                    PRIu64 "'", object_get_typename(obj), name, value); |  | ||||||
|         goto out; |  | ||||||
|     } |  | ||||||
|     m->hugetlbsize = value; |  | ||||||
| out: |  | ||||||
|     error_propagate(errp, local_err); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void |  | ||||||
| memfd_backend_get_hugetlbsize(Object *obj, Visitor *v, const char *name, |  | ||||||
|                               void *opaque, Error **errp) |  | ||||||
| { |  | ||||||
|     HostMemoryBackendMemfd *m = MEMORY_BACKEND_MEMFD(obj); |  | ||||||
|     uint64_t value = m->hugetlbsize; |  | ||||||
|  |  | ||||||
|     visit_type_size(v, name, &value, errp); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static bool |  | ||||||
| memfd_backend_get_seal(Object *o, Error **errp) |  | ||||||
| { |  | ||||||
|     return MEMORY_BACKEND_MEMFD(o)->seal; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void |  | ||||||
| memfd_backend_set_seal(Object *o, bool value, Error **errp) |  | ||||||
| { |  | ||||||
|     MEMORY_BACKEND_MEMFD(o)->seal = value; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void |  | ||||||
| memfd_backend_instance_init(Object *obj) |  | ||||||
| { |  | ||||||
|     HostMemoryBackendMemfd *m = MEMORY_BACKEND_MEMFD(obj); |  | ||||||
|  |  | ||||||
|     /* default to sealed file */ |  | ||||||
|     m->seal = true; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void |  | ||||||
| memfd_backend_class_init(ObjectClass *oc, void *data) |  | ||||||
| { |  | ||||||
|     HostMemoryBackendClass *bc = MEMORY_BACKEND_CLASS(oc); |  | ||||||
|  |  | ||||||
|     bc->alloc = memfd_backend_memory_alloc; |  | ||||||
|  |  | ||||||
|     object_class_property_add_bool(oc, "hugetlb", |  | ||||||
|                                    memfd_backend_get_hugetlb, |  | ||||||
|                                    memfd_backend_set_hugetlb, |  | ||||||
|                                    &error_abort); |  | ||||||
|     object_class_property_add(oc, "hugetlbsize", "int", |  | ||||||
|                               memfd_backend_get_hugetlbsize, |  | ||||||
|                               memfd_backend_set_hugetlbsize, |  | ||||||
|                               NULL, NULL, &error_abort); |  | ||||||
|     object_class_property_add_bool(oc, "seal", |  | ||||||
|                                    memfd_backend_get_seal, |  | ||||||
|                                    memfd_backend_set_seal, |  | ||||||
|                                    &error_abort); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static const TypeInfo memfd_backend_info = { |  | ||||||
|     .name = TYPE_MEMORY_BACKEND_MEMFD, |  | ||||||
|     .parent = TYPE_MEMORY_BACKEND, |  | ||||||
|     .instance_init = memfd_backend_instance_init, |  | ||||||
|     .class_init = memfd_backend_class_init, |  | ||||||
|     .instance_size = sizeof(HostMemoryBackendMemfd), |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static void register_types(void) |  | ||||||
| { |  | ||||||
|     type_register_static(&memfd_backend_info); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type_init(register_types); |  | ||||||
| @@ -1,55 +0,0 @@ | |||||||
| /* |  | ||||||
|  * QEMU Host Memory Backend |  | ||||||
|  * |  | ||||||
|  * Copyright (C) 2013-2014 Red Hat Inc |  | ||||||
|  * |  | ||||||
|  * Authors: |  | ||||||
|  *   Igor Mammedov <imammedo@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 "sysemu/hostmem.h" |  | ||||||
| #include "qapi/error.h" |  | ||||||
| #include "qom/object_interfaces.h" |  | ||||||
|  |  | ||||||
| #define TYPE_MEMORY_BACKEND_RAM "memory-backend-ram" |  | ||||||
|  |  | ||||||
|  |  | ||||||
| static void |  | ||||||
| ram_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) |  | ||||||
| { |  | ||||||
|     char *path; |  | ||||||
|  |  | ||||||
|     if (!backend->size) { |  | ||||||
|         error_setg(errp, "can't create backend with size 0"); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     path = object_get_canonical_path_component(OBJECT(backend)); |  | ||||||
|     memory_region_init_ram_shared_nomigrate(&backend->mr, OBJECT(backend), path, |  | ||||||
|                            backend->size, backend->share, errp); |  | ||||||
|     g_free(path); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void |  | ||||||
| ram_backend_class_init(ObjectClass *oc, void *data) |  | ||||||
| { |  | ||||||
|     HostMemoryBackendClass *bc = MEMORY_BACKEND_CLASS(oc); |  | ||||||
|  |  | ||||||
|     bc->alloc = ram_backend_memory_alloc; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static const TypeInfo ram_backend_info = { |  | ||||||
|     .name = TYPE_MEMORY_BACKEND_RAM, |  | ||||||
|     .parent = TYPE_MEMORY_BACKEND, |  | ||||||
|     .class_init = ram_backend_class_init, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static void register_types(void) |  | ||||||
| { |  | ||||||
|     type_register_static(&ram_backend_info); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type_init(register_types); |  | ||||||
| @@ -1,451 +0,0 @@ | |||||||
| /* |  | ||||||
|  * QEMU Host Memory Backend |  | ||||||
|  * |  | ||||||
|  * Copyright (C) 2013-2014 Red Hat Inc |  | ||||||
|  * |  | ||||||
|  * Authors: |  | ||||||
|  *   Igor Mammedov <imammedo@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 "sysemu/hostmem.h" |  | ||||||
| #include "hw/boards.h" |  | ||||||
| #include "qapi/error.h" |  | ||||||
| #include "qapi/qapi-builtin-visit.h" |  | ||||||
| #include "qapi/visitor.h" |  | ||||||
| #include "qemu/config-file.h" |  | ||||||
| #include "qom/object_interfaces.h" |  | ||||||
|  |  | ||||||
| #ifdef CONFIG_NUMA |  | ||||||
| #include <numaif.h> |  | ||||||
| QEMU_BUILD_BUG_ON(HOST_MEM_POLICY_DEFAULT != MPOL_DEFAULT); |  | ||||||
| QEMU_BUILD_BUG_ON(HOST_MEM_POLICY_PREFERRED != MPOL_PREFERRED); |  | ||||||
| QEMU_BUILD_BUG_ON(HOST_MEM_POLICY_BIND != MPOL_BIND); |  | ||||||
| QEMU_BUILD_BUG_ON(HOST_MEM_POLICY_INTERLEAVE != MPOL_INTERLEAVE); |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| static void |  | ||||||
| host_memory_backend_get_size(Object *obj, Visitor *v, const char *name, |  | ||||||
|                              void *opaque, Error **errp) |  | ||||||
| { |  | ||||||
|     HostMemoryBackend *backend = MEMORY_BACKEND(obj); |  | ||||||
|     uint64_t value = backend->size; |  | ||||||
|  |  | ||||||
|     visit_type_size(v, name, &value, errp); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void |  | ||||||
| host_memory_backend_set_size(Object *obj, Visitor *v, const char *name, |  | ||||||
|                              void *opaque, Error **errp) |  | ||||||
| { |  | ||||||
|     HostMemoryBackend *backend = MEMORY_BACKEND(obj); |  | ||||||
|     Error *local_err = NULL; |  | ||||||
|     uint64_t value; |  | ||||||
|  |  | ||||||
|     if (host_memory_backend_mr_inited(backend)) { |  | ||||||
|         error_setg(&local_err, "cannot change property value"); |  | ||||||
|         goto out; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     visit_type_size(v, name, &value, &local_err); |  | ||||||
|     if (local_err) { |  | ||||||
|         goto out; |  | ||||||
|     } |  | ||||||
|     if (!value) { |  | ||||||
|         error_setg(&local_err, "Property '%s.%s' doesn't take value '%" |  | ||||||
|                    PRIu64 "'", object_get_typename(obj), name, value); |  | ||||||
|         goto out; |  | ||||||
|     } |  | ||||||
|     backend->size = value; |  | ||||||
| out: |  | ||||||
|     error_propagate(errp, local_err); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void |  | ||||||
| host_memory_backend_get_host_nodes(Object *obj, Visitor *v, const char *name, |  | ||||||
|                                    void *opaque, Error **errp) |  | ||||||
| { |  | ||||||
|     HostMemoryBackend *backend = MEMORY_BACKEND(obj); |  | ||||||
|     uint16List *host_nodes = NULL; |  | ||||||
|     uint16List **node = &host_nodes; |  | ||||||
|     unsigned long value; |  | ||||||
|  |  | ||||||
|     value = find_first_bit(backend->host_nodes, MAX_NODES); |  | ||||||
|     if (value == MAX_NODES) { |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     *node = g_malloc0(sizeof(**node)); |  | ||||||
|     (*node)->value = value; |  | ||||||
|     node = &(*node)->next; |  | ||||||
|  |  | ||||||
|     do { |  | ||||||
|         value = find_next_bit(backend->host_nodes, MAX_NODES, value + 1); |  | ||||||
|         if (value == MAX_NODES) { |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         *node = g_malloc0(sizeof(**node)); |  | ||||||
|         (*node)->value = value; |  | ||||||
|         node = &(*node)->next; |  | ||||||
|     } while (true); |  | ||||||
|  |  | ||||||
|     visit_type_uint16List(v, name, &host_nodes, errp); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void |  | ||||||
| host_memory_backend_set_host_nodes(Object *obj, Visitor *v, const char *name, |  | ||||||
|                                    void *opaque, Error **errp) |  | ||||||
| { |  | ||||||
| #ifdef CONFIG_NUMA |  | ||||||
|     HostMemoryBackend *backend = MEMORY_BACKEND(obj); |  | ||||||
|     uint16List *l = NULL; |  | ||||||
|  |  | ||||||
|     visit_type_uint16List(v, name, &l, errp); |  | ||||||
|  |  | ||||||
|     while (l) { |  | ||||||
|         bitmap_set(backend->host_nodes, l->value, 1); |  | ||||||
|         l = l->next; |  | ||||||
|     } |  | ||||||
| #else |  | ||||||
|     error_setg(errp, "NUMA node binding are not supported by this QEMU"); |  | ||||||
| #endif |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int |  | ||||||
| host_memory_backend_get_policy(Object *obj, Error **errp G_GNUC_UNUSED) |  | ||||||
| { |  | ||||||
|     HostMemoryBackend *backend = MEMORY_BACKEND(obj); |  | ||||||
|     return backend->policy; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void |  | ||||||
| host_memory_backend_set_policy(Object *obj, int policy, Error **errp) |  | ||||||
| { |  | ||||||
|     HostMemoryBackend *backend = MEMORY_BACKEND(obj); |  | ||||||
|     backend->policy = policy; |  | ||||||
|  |  | ||||||
| #ifndef CONFIG_NUMA |  | ||||||
|     if (policy != HOST_MEM_POLICY_DEFAULT) { |  | ||||||
|         error_setg(errp, "NUMA policies are not supported by this QEMU"); |  | ||||||
|     } |  | ||||||
| #endif |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static bool host_memory_backend_get_merge(Object *obj, Error **errp) |  | ||||||
| { |  | ||||||
|     HostMemoryBackend *backend = MEMORY_BACKEND(obj); |  | ||||||
|  |  | ||||||
|     return backend->merge; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void host_memory_backend_set_merge(Object *obj, bool value, Error **errp) |  | ||||||
| { |  | ||||||
|     HostMemoryBackend *backend = MEMORY_BACKEND(obj); |  | ||||||
|  |  | ||||||
|     if (!host_memory_backend_mr_inited(backend)) { |  | ||||||
|         backend->merge = value; |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (value != backend->merge) { |  | ||||||
|         void *ptr = memory_region_get_ram_ptr(&backend->mr); |  | ||||||
|         uint64_t sz = memory_region_size(&backend->mr); |  | ||||||
|  |  | ||||||
|         qemu_madvise(ptr, sz, |  | ||||||
|                      value ? QEMU_MADV_MERGEABLE : QEMU_MADV_UNMERGEABLE); |  | ||||||
|         backend->merge = value; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static bool host_memory_backend_get_dump(Object *obj, Error **errp) |  | ||||||
| { |  | ||||||
|     HostMemoryBackend *backend = MEMORY_BACKEND(obj); |  | ||||||
|  |  | ||||||
|     return backend->dump; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void host_memory_backend_set_dump(Object *obj, bool value, Error **errp) |  | ||||||
| { |  | ||||||
|     HostMemoryBackend *backend = MEMORY_BACKEND(obj); |  | ||||||
|  |  | ||||||
|     if (!host_memory_backend_mr_inited(backend)) { |  | ||||||
|         backend->dump = value; |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (value != backend->dump) { |  | ||||||
|         void *ptr = memory_region_get_ram_ptr(&backend->mr); |  | ||||||
|         uint64_t sz = memory_region_size(&backend->mr); |  | ||||||
|  |  | ||||||
|         qemu_madvise(ptr, sz, |  | ||||||
|                      value ? QEMU_MADV_DODUMP : QEMU_MADV_DONTDUMP); |  | ||||||
|         backend->dump = value; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static bool host_memory_backend_get_prealloc(Object *obj, Error **errp) |  | ||||||
| { |  | ||||||
|     HostMemoryBackend *backend = MEMORY_BACKEND(obj); |  | ||||||
|  |  | ||||||
|     return backend->prealloc || backend->force_prealloc; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void host_memory_backend_set_prealloc(Object *obj, bool value, |  | ||||||
|                                              Error **errp) |  | ||||||
| { |  | ||||||
|     Error *local_err = NULL; |  | ||||||
|     HostMemoryBackend *backend = MEMORY_BACKEND(obj); |  | ||||||
|  |  | ||||||
|     if (backend->force_prealloc) { |  | ||||||
|         if (value) { |  | ||||||
|             error_setg(errp, |  | ||||||
|                        "remove -mem-prealloc to use the prealloc property"); |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (!host_memory_backend_mr_inited(backend)) { |  | ||||||
|         backend->prealloc = value; |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (value && !backend->prealloc) { |  | ||||||
|         int fd = memory_region_get_fd(&backend->mr); |  | ||||||
|         void *ptr = memory_region_get_ram_ptr(&backend->mr); |  | ||||||
|         uint64_t sz = memory_region_size(&backend->mr); |  | ||||||
|  |  | ||||||
|         os_mem_prealloc(fd, ptr, sz, smp_cpus, &local_err); |  | ||||||
|         if (local_err) { |  | ||||||
|             error_propagate(errp, local_err); |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|         backend->prealloc = true; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void host_memory_backend_init(Object *obj) |  | ||||||
| { |  | ||||||
|     HostMemoryBackend *backend = MEMORY_BACKEND(obj); |  | ||||||
|     MachineState *machine = MACHINE(qdev_get_machine()); |  | ||||||
|  |  | ||||||
|     backend->merge = machine_mem_merge(machine); |  | ||||||
|     backend->dump = machine_dump_guest_core(machine); |  | ||||||
|     backend->prealloc = mem_prealloc; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| bool host_memory_backend_mr_inited(HostMemoryBackend *backend) |  | ||||||
| { |  | ||||||
|     /* |  | ||||||
|      * NOTE: We forbid zero-length memory backend, so here zero means |  | ||||||
|      * "we haven't inited the backend memory region yet". |  | ||||||
|      */ |  | ||||||
|     return memory_region_size(&backend->mr) != 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| MemoryRegion * |  | ||||||
| host_memory_backend_get_memory(HostMemoryBackend *backend, Error **errp) |  | ||||||
| { |  | ||||||
|     return host_memory_backend_mr_inited(backend) ? &backend->mr : NULL; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void host_memory_backend_set_mapped(HostMemoryBackend *backend, bool mapped) |  | ||||||
| { |  | ||||||
|     backend->is_mapped = mapped; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| bool host_memory_backend_is_mapped(HostMemoryBackend *backend) |  | ||||||
| { |  | ||||||
|     return backend->is_mapped; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void |  | ||||||
| host_memory_backend_memory_complete(UserCreatable *uc, Error **errp) |  | ||||||
| { |  | ||||||
|     HostMemoryBackend *backend = MEMORY_BACKEND(uc); |  | ||||||
|     HostMemoryBackendClass *bc = MEMORY_BACKEND_GET_CLASS(uc); |  | ||||||
|     Error *local_err = NULL; |  | ||||||
|     void *ptr; |  | ||||||
|     uint64_t sz; |  | ||||||
|  |  | ||||||
|     if (bc->alloc) { |  | ||||||
|         bc->alloc(backend, &local_err); |  | ||||||
|         if (local_err) { |  | ||||||
|             goto out; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         ptr = memory_region_get_ram_ptr(&backend->mr); |  | ||||||
|         sz = memory_region_size(&backend->mr); |  | ||||||
|  |  | ||||||
|         if (backend->merge) { |  | ||||||
|             qemu_madvise(ptr, sz, QEMU_MADV_MERGEABLE); |  | ||||||
|         } |  | ||||||
|         if (!backend->dump) { |  | ||||||
|             qemu_madvise(ptr, sz, QEMU_MADV_DONTDUMP); |  | ||||||
|         } |  | ||||||
| #ifdef CONFIG_NUMA |  | ||||||
|         unsigned long lastbit = find_last_bit(backend->host_nodes, MAX_NODES); |  | ||||||
|         /* lastbit == MAX_NODES means maxnode = 0 */ |  | ||||||
|         unsigned long maxnode = (lastbit + 1) % (MAX_NODES + 1); |  | ||||||
|         /* ensure policy won't be ignored in case memory is preallocated |  | ||||||
|          * before mbind(). note: MPOL_MF_STRICT is ignored on hugepages so |  | ||||||
|          * this doesn't catch hugepage case. */ |  | ||||||
|         unsigned flags = MPOL_MF_STRICT | MPOL_MF_MOVE; |  | ||||||
|  |  | ||||||
|         /* check for invalid host-nodes and policies and give more verbose |  | ||||||
|          * error messages than mbind(). */ |  | ||||||
|         if (maxnode && backend->policy == MPOL_DEFAULT) { |  | ||||||
|             error_setg(errp, "host-nodes must be empty for policy default," |  | ||||||
|                        " or you should explicitly specify a policy other" |  | ||||||
|                        " than default"); |  | ||||||
|             return; |  | ||||||
|         } else if (maxnode == 0 && backend->policy != MPOL_DEFAULT) { |  | ||||||
|             error_setg(errp, "host-nodes must be set for policy %s", |  | ||||||
|                        HostMemPolicy_str(backend->policy)); |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         /* We can have up to MAX_NODES nodes, but we need to pass maxnode+1 |  | ||||||
|          * as argument to mbind() due to an old Linux bug (feature?) which |  | ||||||
|          * cuts off the last specified node. This means backend->host_nodes |  | ||||||
|          * must have MAX_NODES+1 bits available. |  | ||||||
|          */ |  | ||||||
|         assert(sizeof(backend->host_nodes) >= |  | ||||||
|                BITS_TO_LONGS(MAX_NODES + 1) * sizeof(unsigned long)); |  | ||||||
|         assert(maxnode <= MAX_NODES); |  | ||||||
|         if (mbind(ptr, sz, backend->policy, |  | ||||||
|                   maxnode ? backend->host_nodes : NULL, maxnode + 1, flags)) { |  | ||||||
|             if (backend->policy != MPOL_DEFAULT || errno != ENOSYS) { |  | ||||||
|                 error_setg_errno(errp, errno, |  | ||||||
|                                  "cannot bind memory to host NUMA nodes"); |  | ||||||
|                 return; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| #endif |  | ||||||
|         /* Preallocate memory after the NUMA policy has been instantiated. |  | ||||||
|          * This is necessary to guarantee memory is allocated with |  | ||||||
|          * specified NUMA policy in place. |  | ||||||
|          */ |  | ||||||
|         if (backend->prealloc) { |  | ||||||
|             os_mem_prealloc(memory_region_get_fd(&backend->mr), ptr, sz, |  | ||||||
|                             smp_cpus, &local_err); |  | ||||||
|             if (local_err) { |  | ||||||
|                 goto out; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| out: |  | ||||||
|     error_propagate(errp, local_err); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static bool |  | ||||||
| host_memory_backend_can_be_deleted(UserCreatable *uc) |  | ||||||
| { |  | ||||||
|     if (host_memory_backend_is_mapped(MEMORY_BACKEND(uc))) { |  | ||||||
|         return false; |  | ||||||
|     } else { |  | ||||||
|         return true; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| 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) |  | ||||||
| { |  | ||||||
|     HostMemoryBackend *backend = MEMORY_BACKEND(o); |  | ||||||
|  |  | ||||||
|     return backend->share; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void host_memory_backend_set_share(Object *o, bool value, Error **errp) |  | ||||||
| { |  | ||||||
|     HostMemoryBackend *backend = MEMORY_BACKEND(o); |  | ||||||
|  |  | ||||||
|     if (host_memory_backend_mr_inited(backend)) { |  | ||||||
|         error_setg(errp, "cannot change property value"); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|     backend->share = value; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void |  | ||||||
| host_memory_backend_class_init(ObjectClass *oc, void *data) |  | ||||||
| { |  | ||||||
|     UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); |  | ||||||
|  |  | ||||||
|     ucc->complete = host_memory_backend_memory_complete; |  | ||||||
|     ucc->can_be_deleted = host_memory_backend_can_be_deleted; |  | ||||||
|  |  | ||||||
|     object_class_property_add_bool(oc, "merge", |  | ||||||
|         host_memory_backend_get_merge, |  | ||||||
|         host_memory_backend_set_merge, &error_abort); |  | ||||||
|     object_class_property_add_bool(oc, "dump", |  | ||||||
|         host_memory_backend_get_dump, |  | ||||||
|         host_memory_backend_set_dump, &error_abort); |  | ||||||
|     object_class_property_add_bool(oc, "prealloc", |  | ||||||
|         host_memory_backend_get_prealloc, |  | ||||||
|         host_memory_backend_set_prealloc, &error_abort); |  | ||||||
|     object_class_property_add(oc, "size", "int", |  | ||||||
|         host_memory_backend_get_size, |  | ||||||
|         host_memory_backend_set_size, |  | ||||||
|         NULL, NULL, &error_abort); |  | ||||||
|     object_class_property_add(oc, "host-nodes", "int", |  | ||||||
|         host_memory_backend_get_host_nodes, |  | ||||||
|         host_memory_backend_set_host_nodes, |  | ||||||
|         NULL, NULL, &error_abort); |  | ||||||
|     object_class_property_add_enum(oc, "policy", "HostMemPolicy", |  | ||||||
|         &HostMemPolicy_lookup, |  | ||||||
|         host_memory_backend_get_policy, |  | ||||||
|         host_memory_backend_set_policy, &error_abort); |  | ||||||
|     object_class_property_add_str(oc, "id", get_id, set_id, &error_abort); |  | ||||||
|     object_class_property_add_bool(oc, "share", |  | ||||||
|         host_memory_backend_get_share, host_memory_backend_set_share, |  | ||||||
|         &error_abort); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void host_memory_backend_finalize(Object *o) |  | ||||||
| { |  | ||||||
|     HostMemoryBackend *backend = MEMORY_BACKEND(o); |  | ||||||
|     g_free(backend->id); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static const TypeInfo host_memory_backend_info = { |  | ||||||
|     .name = TYPE_MEMORY_BACKEND, |  | ||||||
|     .parent = TYPE_OBJECT, |  | ||||||
|     .abstract = true, |  | ||||||
|     .class_size = sizeof(HostMemoryBackendClass), |  | ||||||
|     .class_init = host_memory_backend_class_init, |  | ||||||
|     .instance_size = sizeof(HostMemoryBackend), |  | ||||||
|     .instance_init = host_memory_backend_init, |  | ||||||
|     .instance_finalize = host_memory_backend_finalize, |  | ||||||
|     .interfaces = (InterfaceInfo[]) { |  | ||||||
|         { TYPE_USER_CREATABLE }, |  | ||||||
|         { } |  | ||||||
|     } |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static void register_types(void) |  | ||||||
| { |  | ||||||
|     type_register_static(&host_memory_backend_info); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type_init(register_types); |  | ||||||
| @@ -1,174 +0,0 @@ | |||||||
| /* |  | ||||||
|  * QEMU Random Number Generator Backend |  | ||||||
|  * |  | ||||||
|  * Copyright IBM, Corp. 2012 |  | ||||||
|  * |  | ||||||
|  * 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 "sysemu/rng.h" |  | ||||||
| #include "chardev/char-fe.h" |  | ||||||
| #include "qapi/error.h" |  | ||||||
| #include "qapi/qmp/qerror.h" |  | ||||||
|  |  | ||||||
| #define TYPE_RNG_EGD "rng-egd" |  | ||||||
| #define RNG_EGD(obj) OBJECT_CHECK(RngEgd, (obj), TYPE_RNG_EGD) |  | ||||||
|  |  | ||||||
| typedef struct RngEgd |  | ||||||
| { |  | ||||||
|     RngBackend parent; |  | ||||||
|  |  | ||||||
|     CharBackend chr; |  | ||||||
|     char *chr_name; |  | ||||||
| } RngEgd; |  | ||||||
|  |  | ||||||
| static void rng_egd_request_entropy(RngBackend *b, RngRequest *req) |  | ||||||
| { |  | ||||||
|     RngEgd *s = RNG_EGD(b); |  | ||||||
|     size_t size = req->size; |  | ||||||
|  |  | ||||||
|     while (size > 0) { |  | ||||||
|         uint8_t header[2]; |  | ||||||
|         uint8_t len = MIN(size, 255); |  | ||||||
|  |  | ||||||
|         /* synchronous entropy request */ |  | ||||||
|         header[0] = 0x02; |  | ||||||
|         header[1] = len; |  | ||||||
|  |  | ||||||
|         /* XXX this blocks entire thread. Rewrite to use |  | ||||||
|          * qemu_chr_fe_write and background I/O callbacks */ |  | ||||||
|         qemu_chr_fe_write_all(&s->chr, header, sizeof(header)); |  | ||||||
|  |  | ||||||
|         size -= len; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int rng_egd_chr_can_read(void *opaque) |  | ||||||
| { |  | ||||||
|     RngEgd *s = RNG_EGD(opaque); |  | ||||||
|     RngRequest *req; |  | ||||||
|     int size = 0; |  | ||||||
|  |  | ||||||
|     QSIMPLEQ_FOREACH(req, &s->parent.requests, next) { |  | ||||||
|         size += req->size - req->offset; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return size; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void rng_egd_chr_read(void *opaque, const uint8_t *buf, int size) |  | ||||||
| { |  | ||||||
|     RngEgd *s = RNG_EGD(opaque); |  | ||||||
|     size_t buf_offset = 0; |  | ||||||
|  |  | ||||||
|     while (size > 0 && !QSIMPLEQ_EMPTY(&s->parent.requests)) { |  | ||||||
|         RngRequest *req = QSIMPLEQ_FIRST(&s->parent.requests); |  | ||||||
|         int len = MIN(size, req->size - req->offset); |  | ||||||
|  |  | ||||||
|         memcpy(req->data + req->offset, buf + buf_offset, len); |  | ||||||
|         buf_offset += len; |  | ||||||
|         req->offset += len; |  | ||||||
|         size -= len; |  | ||||||
|  |  | ||||||
|         if (req->offset == req->size) { |  | ||||||
|             req->receive_entropy(req->opaque, req->data, req->size); |  | ||||||
|  |  | ||||||
|             rng_backend_finalize_request(&s->parent, req); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void rng_egd_opened(RngBackend *b, Error **errp) |  | ||||||
| { |  | ||||||
|     RngEgd *s = RNG_EGD(b); |  | ||||||
|     Chardev *chr; |  | ||||||
|  |  | ||||||
|     if (s->chr_name == NULL) { |  | ||||||
|         error_setg(errp, QERR_INVALID_PARAMETER_VALUE, |  | ||||||
|                    "chardev", "a valid character device"); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     chr = qemu_chr_find(s->chr_name); |  | ||||||
|     if (chr == NULL) { |  | ||||||
|         error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, |  | ||||||
|                   "Device '%s' not found", s->chr_name); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|     if (!qemu_chr_fe_init(&s->chr, chr, errp)) { |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /* FIXME we should resubmit pending requests when the CDS reconnects. */ |  | ||||||
|     qemu_chr_fe_set_handlers(&s->chr, rng_egd_chr_can_read, |  | ||||||
|                              rng_egd_chr_read, NULL, NULL, s, NULL, true); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void rng_egd_set_chardev(Object *obj, const char *value, Error **errp) |  | ||||||
| { |  | ||||||
|     RngBackend *b = RNG_BACKEND(obj); |  | ||||||
|     RngEgd *s = RNG_EGD(b); |  | ||||||
|  |  | ||||||
|     if (b->opened) { |  | ||||||
|         error_setg(errp, QERR_PERMISSION_DENIED); |  | ||||||
|     } else { |  | ||||||
|         g_free(s->chr_name); |  | ||||||
|         s->chr_name = g_strdup(value); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static char *rng_egd_get_chardev(Object *obj, Error **errp) |  | ||||||
| { |  | ||||||
|     RngEgd *s = RNG_EGD(obj); |  | ||||||
|     Chardev *chr = qemu_chr_fe_get_driver(&s->chr); |  | ||||||
|  |  | ||||||
|     if (chr && chr->label) { |  | ||||||
|         return g_strdup(chr->label); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return NULL; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void rng_egd_init(Object *obj) |  | ||||||
| { |  | ||||||
|     object_property_add_str(obj, "chardev", |  | ||||||
|                             rng_egd_get_chardev, rng_egd_set_chardev, |  | ||||||
|                             NULL); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void rng_egd_finalize(Object *obj) |  | ||||||
| { |  | ||||||
|     RngEgd *s = RNG_EGD(obj); |  | ||||||
|  |  | ||||||
|     qemu_chr_fe_deinit(&s->chr, false); |  | ||||||
|     g_free(s->chr_name); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void rng_egd_class_init(ObjectClass *klass, void *data) |  | ||||||
| { |  | ||||||
|     RngBackendClass *rbc = RNG_BACKEND_CLASS(klass); |  | ||||||
|  |  | ||||||
|     rbc->request_entropy = rng_egd_request_entropy; |  | ||||||
|     rbc->opened = rng_egd_opened; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static const TypeInfo rng_egd_info = { |  | ||||||
|     .name = TYPE_RNG_EGD, |  | ||||||
|     .parent = TYPE_RNG_BACKEND, |  | ||||||
|     .instance_size = sizeof(RngEgd), |  | ||||||
|     .class_init = rng_egd_class_init, |  | ||||||
|     .instance_init = rng_egd_init, |  | ||||||
|     .instance_finalize = rng_egd_finalize, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static void register_types(void) |  | ||||||
| { |  | ||||||
|     type_register_static(&rng_egd_info); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type_init(register_types); |  | ||||||
| @@ -1,153 +0,0 @@ | |||||||
| /* |  | ||||||
|  * QEMU Random Number Generator Backend |  | ||||||
|  * |  | ||||||
|  * Copyright IBM, Corp. 2012 |  | ||||||
|  * |  | ||||||
|  * 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 "sysemu/rng-random.h" |  | ||||||
| #include "sysemu/rng.h" |  | ||||||
| #include "qapi/error.h" |  | ||||||
| #include "qapi/qmp/qerror.h" |  | ||||||
| #include "qemu/main-loop.h" |  | ||||||
|  |  | ||||||
| struct RngRandom |  | ||||||
| { |  | ||||||
|     RngBackend parent; |  | ||||||
|  |  | ||||||
|     int fd; |  | ||||||
|     char *filename; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * A simple and incomplete backend to request entropy from /dev/random. |  | ||||||
|  * |  | ||||||
|  * This backend exposes an additional "filename" property that can be used to |  | ||||||
|  * set the filename to use to open the backend. |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| static void entropy_available(void *opaque) |  | ||||||
| { |  | ||||||
|     RngRandom *s = RNG_RANDOM(opaque); |  | ||||||
|  |  | ||||||
|     while (!QSIMPLEQ_EMPTY(&s->parent.requests)) { |  | ||||||
|         RngRequest *req = QSIMPLEQ_FIRST(&s->parent.requests); |  | ||||||
|         ssize_t len; |  | ||||||
|  |  | ||||||
|         len = read(s->fd, req->data, req->size); |  | ||||||
|         if (len < 0 && errno == EAGAIN) { |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|         g_assert(len != -1); |  | ||||||
|  |  | ||||||
|         req->receive_entropy(req->opaque, req->data, len); |  | ||||||
|  |  | ||||||
|         rng_backend_finalize_request(&s->parent, req); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /* We've drained all requests, the fd handler can be reset. */ |  | ||||||
|     qemu_set_fd_handler(s->fd, NULL, NULL, NULL); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void rng_random_request_entropy(RngBackend *b, RngRequest *req) |  | ||||||
| { |  | ||||||
|     RngRandom *s = RNG_RANDOM(b); |  | ||||||
|  |  | ||||||
|     if (QSIMPLEQ_EMPTY(&s->parent.requests)) { |  | ||||||
|         /* If there are no pending requests yet, we need to |  | ||||||
|          * install our fd handler. */ |  | ||||||
|         qemu_set_fd_handler(s->fd, entropy_available, NULL, s); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void rng_random_opened(RngBackend *b, Error **errp) |  | ||||||
| { |  | ||||||
|     RngRandom *s = RNG_RANDOM(b); |  | ||||||
|  |  | ||||||
|     if (s->filename == NULL) { |  | ||||||
|         error_setg(errp, QERR_INVALID_PARAMETER_VALUE, |  | ||||||
|                    "filename", "a valid filename"); |  | ||||||
|     } else { |  | ||||||
|         s->fd = qemu_open(s->filename, O_RDONLY | O_NONBLOCK); |  | ||||||
|         if (s->fd == -1) { |  | ||||||
|             error_setg_file_open(errp, errno, s->filename); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static char *rng_random_get_filename(Object *obj, Error **errp) |  | ||||||
| { |  | ||||||
|     RngRandom *s = RNG_RANDOM(obj); |  | ||||||
|  |  | ||||||
|     return g_strdup(s->filename); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void rng_random_set_filename(Object *obj, const char *filename, |  | ||||||
|                                  Error **errp) |  | ||||||
| { |  | ||||||
|     RngBackend *b = RNG_BACKEND(obj); |  | ||||||
|     RngRandom *s = RNG_RANDOM(obj); |  | ||||||
|  |  | ||||||
|     if (b->opened) { |  | ||||||
|         error_setg(errp, QERR_PERMISSION_DENIED); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     g_free(s->filename); |  | ||||||
|     s->filename = g_strdup(filename); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void rng_random_init(Object *obj) |  | ||||||
| { |  | ||||||
|     RngRandom *s = RNG_RANDOM(obj); |  | ||||||
|  |  | ||||||
|     object_property_add_str(obj, "filename", |  | ||||||
|                             rng_random_get_filename, |  | ||||||
|                             rng_random_set_filename, |  | ||||||
|                             NULL); |  | ||||||
|  |  | ||||||
|     s->filename = g_strdup("/dev/random"); |  | ||||||
|     s->fd = -1; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void rng_random_finalize(Object *obj) |  | ||||||
| { |  | ||||||
|     RngRandom *s = RNG_RANDOM(obj); |  | ||||||
|  |  | ||||||
|     if (s->fd != -1) { |  | ||||||
|         qemu_set_fd_handler(s->fd, NULL, NULL, NULL); |  | ||||||
|         qemu_close(s->fd); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     g_free(s->filename); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void rng_random_class_init(ObjectClass *klass, void *data) |  | ||||||
| { |  | ||||||
|     RngBackendClass *rbc = RNG_BACKEND_CLASS(klass); |  | ||||||
|  |  | ||||||
|     rbc->request_entropy = rng_random_request_entropy; |  | ||||||
|     rbc->opened = rng_random_opened; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static const TypeInfo rng_random_info = { |  | ||||||
|     .name = TYPE_RNG_RANDOM, |  | ||||||
|     .parent = TYPE_RNG_BACKEND, |  | ||||||
|     .instance_size = sizeof(RngRandom), |  | ||||||
|     .class_init = rng_random_class_init, |  | ||||||
|     .instance_init = rng_random_init, |  | ||||||
|     .instance_finalize = rng_random_finalize, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static void register_types(void) |  | ||||||
| { |  | ||||||
|     type_register_static(&rng_random_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