Compare commits
	
		
			1 Commits
		
	
	
		
			stable-0.1
			...
			release_0_
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					bebe39c387 | 
							
								
								
									
										29
									
								
								.cvsignore
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								.cvsignore
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,29 @@
 | 
			
		||||
arm-user
 | 
			
		||||
arm-softmmu
 | 
			
		||||
armeb-user
 | 
			
		||||
config-host.*
 | 
			
		||||
dyngen
 | 
			
		||||
i386
 | 
			
		||||
i386-softmmu
 | 
			
		||||
i386-user
 | 
			
		||||
ppc-softmmu
 | 
			
		||||
ppc64-softmmu
 | 
			
		||||
ppc-user
 | 
			
		||||
qemu-doc.html
 | 
			
		||||
qemu-tech.html
 | 
			
		||||
qemu.1
 | 
			
		||||
qemu.pod
 | 
			
		||||
qemu-img.1
 | 
			
		||||
qemu-img.pod
 | 
			
		||||
sparc-user
 | 
			
		||||
qemu-img
 | 
			
		||||
sparc-softmmu
 | 
			
		||||
x86_64-softmmu
 | 
			
		||||
sparc64-user
 | 
			
		||||
sparc64-softmmu
 | 
			
		||||
mips-softmmu
 | 
			
		||||
mipsel-softmmu
 | 
			
		||||
mips-user
 | 
			
		||||
mipsel-user
 | 
			
		||||
sh4-user
 | 
			
		||||
sh4-softmmu
 | 
			
		||||
							
								
								
									
										56
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										56
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -1,56 +0,0 @@
 | 
			
		||||
config-devices.*
 | 
			
		||||
config-all-devices.*
 | 
			
		||||
config-host.*
 | 
			
		||||
config-target.*
 | 
			
		||||
*-softmmu
 | 
			
		||||
*-darwin-user
 | 
			
		||||
*-linux-user
 | 
			
		||||
*-bsd-user
 | 
			
		||||
libdis*
 | 
			
		||||
libhw32
 | 
			
		||||
libhw64
 | 
			
		||||
libuser
 | 
			
		||||
qemu-doc.html
 | 
			
		||||
qemu-tech.html
 | 
			
		||||
qemu-doc.info
 | 
			
		||||
qemu-tech.info
 | 
			
		||||
qemu.1
 | 
			
		||||
qemu.pod
 | 
			
		||||
qemu-img.1
 | 
			
		||||
qemu-img.pod
 | 
			
		||||
qemu-img
 | 
			
		||||
qemu-nbd
 | 
			
		||||
qemu-nbd.8
 | 
			
		||||
qemu-nbd.pod
 | 
			
		||||
qemu-options.def
 | 
			
		||||
qemu-options.texi
 | 
			
		||||
qemu-img-cmds.texi
 | 
			
		||||
qemu-img-cmds.h
 | 
			
		||||
qemu-io
 | 
			
		||||
qemu-monitor.texi
 | 
			
		||||
QMP/qmp-commands.txt
 | 
			
		||||
.gdbinit
 | 
			
		||||
*.a
 | 
			
		||||
*.aux
 | 
			
		||||
*.cp
 | 
			
		||||
*.dvi
 | 
			
		||||
*.exe
 | 
			
		||||
*.fn
 | 
			
		||||
*.ky
 | 
			
		||||
*.log
 | 
			
		||||
*.pdf
 | 
			
		||||
*.pg
 | 
			
		||||
*.toc
 | 
			
		||||
*.tp
 | 
			
		||||
*.vr
 | 
			
		||||
*.d
 | 
			
		||||
*.o
 | 
			
		||||
.pc
 | 
			
		||||
patches
 | 
			
		||||
pc-bios/bios-pq/status
 | 
			
		||||
pc-bios/vgabios-pq/status
 | 
			
		||||
pc-bios/optionrom/linuxboot.bin
 | 
			
		||||
pc-bios/optionrom/multiboot.bin
 | 
			
		||||
pc-bios/optionrom/multiboot.raw
 | 
			
		||||
.stgit-*
 | 
			
		||||
cscope.*
 | 
			
		||||
							
								
								
									
										6
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							@@ -1,6 +0,0 @@
 | 
			
		||||
[submodule "roms/vgabios"]
 | 
			
		||||
	path = roms/vgabios
 | 
			
		||||
	url = git://git.qemu.org/vgabios.git/
 | 
			
		||||
[submodule "roms/seabios"]
 | 
			
		||||
	path = roms/seabios
 | 
			
		||||
	url = git://git.qemu.org/seabios.git/
 | 
			
		||||
							
								
								
									
										81
									
								
								CODING_STYLE
									
									
									
									
									
								
							
							
						
						
									
										81
									
								
								CODING_STYLE
									
									
									
									
									
								
							@@ -1,81 +0,0 @@
 | 
			
		||||
Qemu Coding Style
 | 
			
		||||
=================
 | 
			
		||||
 | 
			
		||||
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 have 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 are 80 characters; not longer.
 | 
			
		||||
 | 
			
		||||
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.
 | 
			
		||||
 - 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.  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.
 | 
			
		||||
 | 
			
		||||
Typedefs are used to eliminate the redundant 'struct' keyword.  It is the
 | 
			
		||||
QEMU coding style.
 | 
			
		||||
 | 
			
		||||
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");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
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.
 | 
			
		||||
							
								
								
									
										18
									
								
								COPYING
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								COPYING
									
									
									
									
									
								
							@@ -1,8 +1,8 @@
 | 
			
		||||
		    GNU GENERAL PUBLIC LICENSE
 | 
			
		||||
		       Version 2, June 1991
 | 
			
		||||
 | 
			
		||||
 Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
 | 
			
		||||
 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 | 
			
		||||
 Copyright (C) 1989, 1991 Free Software Foundation, Inc.
 | 
			
		||||
                          675 Mass Ave, Cambridge, MA 02139, USA
 | 
			
		||||
 Everyone is permitted to copy and distribute verbatim copies
 | 
			
		||||
 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
 | 
			
		||||
Foundation's software and to any other program whose authors commit to
 | 
			
		||||
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.
 | 
			
		||||
 | 
			
		||||
  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.
 | 
			
		||||
 | 
			
		||||
    <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
 | 
			
		||||
    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
 | 
			
		||||
    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.,
 | 
			
		||||
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 | 
			
		||||
    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.
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
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'.
 | 
			
		||||
    This is free software, and you are welcome to redistribute it
 | 
			
		||||
    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
 | 
			
		||||
proprietary programs.  If your program is a subroutine library, you may
 | 
			
		||||
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.
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@
 | 
			
		||||
		       Version 2.1, February 1999
 | 
			
		||||
 | 
			
		||||
 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
 | 
			
		||||
 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
 | 
			
		||||
    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.
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										202
									
								
								Changelog
									
									
									
									
									
								
							
							
						
						
									
										202
									
								
								Changelog
									
									
									
									
									
								
							@@ -1,203 +1,3 @@
 | 
			
		||||
See git history for Changelogs of recent releases.
 | 
			
		||||
 | 
			
		||||
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 propogation (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:
 | 
			
		||||
 | 
			
		||||
  - TFTP booting from host directory (Anthony Liguori, Erwan Velu)
 | 
			
		||||
  - Tap device emulation for Solaris (Sittichai Palanisong)
 | 
			
		||||
  - Monitor multiplexing to several I/O channels (Jason Wessel)
 | 
			
		||||
  - ds1225y nvram support (Herve Poussineau)
 | 
			
		||||
  - CPU model selection support (J. Mayer, Paul Brook, Herve Poussineau)
 | 
			
		||||
  - Several Sparc fixes (Aurelien Jarno, Blue Swirl, Robert Reif)
 | 
			
		||||
  - MIPS 64-bit FPU support (Thiemo Seufer)
 | 
			
		||||
  - Xscale PDA emulation (Andrzej Zaborowski)
 | 
			
		||||
  - ColdFire system emulation (Paul Brook)
 | 
			
		||||
  - Improved SH4 support (Magnus Damm)
 | 
			
		||||
  - MIPS64 support (Aurelien Jarno, Thiemo Seufer)
 | 
			
		||||
  - Preliminary Alpha guest support (J. Mayer)
 | 
			
		||||
  - Read-only support for Parallels disk images (Alex Beregszaszi)
 | 
			
		||||
  - SVM (x86 virtualization) support (Alexander Graf)
 | 
			
		||||
  - CRIS emulation (Edgar E. Iglesias)
 | 
			
		||||
  - SPARC32PLUS execution support (Blue Swirl)
 | 
			
		||||
  - MIPS mipssim pseudo machine (Thiemo Seufer)
 | 
			
		||||
  - Strace for Linux userland emulation (Stuart Anderson, Thayne Harbaugh)
 | 
			
		||||
  - OMAP310 MPU emulation plus Palm T|E machine (Andrzej Zaborowski)
 | 
			
		||||
  - ARM v6, v7, NEON SIMD and SMP emulation (Paul Brook/CodeSourcery)
 | 
			
		||||
  - Gumstix boards: connex and verdex emulation (Thorsten Zitterell)
 | 
			
		||||
  - Intel mainstone II board emulation (Armin Kuster)
 | 
			
		||||
  - VMware SVGA II graphics card support (Andrzej Zaborowski)
 | 
			
		||||
 | 
			
		||||
version 0.9.0:
 | 
			
		||||
 | 
			
		||||
  - Support for relative paths in backing files for disk images
 | 
			
		||||
  - Async file I/O API
 | 
			
		||||
  - New qcow2 disk image format
 | 
			
		||||
  - Support of multiple VM snapshots
 | 
			
		||||
  - Linux: specific host CDROM and floppy support
 | 
			
		||||
  - SMM support
 | 
			
		||||
  - Moved PCI init, MP table init and ACPI table init to Bochs BIOS
 | 
			
		||||
  - Support for MIPS32 Release 2 instruction set (Thiemo Seufer)
 | 
			
		||||
  - MIPS Malta system emulation (Aurelien Jarno, Stefan Weil)
 | 
			
		||||
  - Darwin userspace emulation (Pierre d'Herbemont)
 | 
			
		||||
  - m68k user support (Paul Brook)
 | 
			
		||||
  - several x86 and x86_64 emulation fixes
 | 
			
		||||
  - Mouse relative offset VNC extension (Anthony Liguori)
 | 
			
		||||
  - PXE boot support (Anthony Liguori)
 | 
			
		||||
  - '-daemonize' option (Anthony Liguori)
 | 
			
		||||
 | 
			
		||||
version 0.8.2:
 | 
			
		||||
 | 
			
		||||
  - ACPI support
 | 
			
		||||
  - PC VGA BIOS fixes
 | 
			
		||||
  - switch to OpenBios for SPARC targets (Blue Swirl)
 | 
			
		||||
  - VNC server fixes
 | 
			
		||||
  - MIPS FPU support (Marius Groeger)
 | 
			
		||||
  - Solaris/SPARC host support (Juergen Keil)
 | 
			
		||||
  - PPC breakpoints and single stepping (Jason Wessel)
 | 
			
		||||
  - USB updates (Paul Brook)
 | 
			
		||||
  - UDP/TCP/telnet character devices (Jason Wessel)
 | 
			
		||||
  - Windows sparse file support (Frediano Ziglio)
 | 
			
		||||
  - RTL8139 NIC TCP segmentation offloading (Igor Kovalenko)
 | 
			
		||||
  - PCNET NIC support (Antony T Curtis)
 | 
			
		||||
  - Support for variable frequency host CPUs
 | 
			
		||||
  - Workaround for win32 SMP hosts
 | 
			
		||||
  - Support for AMD Flash memories (Jocelyn Mayer)
 | 
			
		||||
  - Audio capture to WAV files support (malc)
 | 
			
		||||
 | 
			
		||||
version 0.8.1:
 | 
			
		||||
 | 
			
		||||
  - USB tablet support (Brad Campbell, Anthony Liguori)
 | 
			
		||||
@@ -205,7 +5,7 @@ version 0.8.1:
 | 
			
		||||
  - PC speaker support (Joachim Henke)
 | 
			
		||||
  - IDE LBA48 support (Jens Axboe)
 | 
			
		||||
  - SSE3 support
 | 
			
		||||
  - Solaris port (Juergen Keil)
 | 
			
		||||
  - Solaris port (Ben Taylor)
 | 
			
		||||
  - Preliminary SH4 target (Samuel Tardieu)
 | 
			
		||||
  - VNC server (Anthony Liguori)
 | 
			
		||||
  - slirp fixes (Ed Swierk et al.)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										20
									
								
								LICENSE
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								LICENSE
									
									
									
									
									
								
							@@ -1,18 +1,12 @@
 | 
			
		||||
The following points clarify the QEMU license:
 | 
			
		||||
The following points clarify the QEMU licenses:
 | 
			
		||||
 | 
			
		||||
1) QEMU as a whole is released under the GNU General Public License
 | 
			
		||||
1) The QEMU virtual CPU core library (libqemu.a) and the QEMU PC
 | 
			
		||||
   system emulator are released under the GNU Lesser General Public
 | 
			
		||||
   License.
 | 
			
		||||
 | 
			
		||||
2) Parts of QEMU have specific licenses which are compatible with the
 | 
			
		||||
GNU General Public License. Hence each source file contains its own
 | 
			
		||||
licensing information.
 | 
			
		||||
2) The Linux user mode QEMU emulator is released under the GNU General
 | 
			
		||||
   Public License.
 | 
			
		||||
 | 
			
		||||
In particular, the QEMU virtual CPU core library (libqemu.a) is
 | 
			
		||||
released under the GNU Lesser General Public License. Many hardware
 | 
			
		||||
device emulation sources are released under the BSD license.
 | 
			
		||||
 | 
			
		||||
3) The Tiny Code Generator (TCG) is released under the BSD license
 | 
			
		||||
   (see license headers in files).
 | 
			
		||||
 | 
			
		||||
4) QEMU is a trademark of Fabrice Bellard.
 | 
			
		||||
3) QEMU is a trademark of Fabrice Bellard.
 | 
			
		||||
 | 
			
		||||
Fabrice Bellard.
 | 
			
		||||
							
								
								
									
										88
									
								
								MAINTAINERS
									
									
									
									
									
								
							
							
						
						
									
										88
									
								
								MAINTAINERS
									
									
									
									
									
								
							@@ -1,88 +0,0 @@
 | 
			
		||||
QEMU Maintainers
 | 
			
		||||
================
 | 
			
		||||
 | 
			
		||||
Project leaders:
 | 
			
		||||
----------------
 | 
			
		||||
 | 
			
		||||
Fabrice Bellard
 | 
			
		||||
Paul Brook
 | 
			
		||||
 | 
			
		||||
CPU cores:
 | 
			
		||||
----------
 | 
			
		||||
 | 
			
		||||
x86                Fabrice Bellard
 | 
			
		||||
ARM                Paul Brook
 | 
			
		||||
SPARC              Blue Swirl
 | 
			
		||||
MIPS               ?
 | 
			
		||||
PowerPC            ?
 | 
			
		||||
M68K               Paul Brook
 | 
			
		||||
SH4                ?
 | 
			
		||||
CRIS               Edgar E. Iglesias
 | 
			
		||||
Alpha              ?
 | 
			
		||||
MicroBlaze         Edgar E. Iglesias
 | 
			
		||||
S390               ?
 | 
			
		||||
 | 
			
		||||
Machines (sorted by CPU):
 | 
			
		||||
-------------------------
 | 
			
		||||
 | 
			
		||||
x86
 | 
			
		||||
  pc.c                    Fabrice Bellard (new maintainer needed)
 | 
			
		||||
ARM
 | 
			
		||||
  integratorcp.c          Paul Brook
 | 
			
		||||
  versatilepb.c           Paul Brook
 | 
			
		||||
  Real View               Paul Brook
 | 
			
		||||
  spitz.c                 Andrzej Zaborowski
 | 
			
		||||
  palm.c                  Andrzej Zaborowski
 | 
			
		||||
  nseries.c               Andrzej Zaborowski
 | 
			
		||||
  stellaris.c             Paul Brook
 | 
			
		||||
  gumstix.c               Thorsten Zitterell
 | 
			
		||||
  mainstone.c             Armin Kuster
 | 
			
		||||
  musicpal.c              Jan Kiszka
 | 
			
		||||
SPARC
 | 
			
		||||
  sun4u.c                 Blue Swirl
 | 
			
		||||
  sun4m.c                 Blue Swirl
 | 
			
		||||
MIPS
 | 
			
		||||
  mips_r4k.c              Aurelien Jarno
 | 
			
		||||
  mips_malta.c            Aurelien Jarno
 | 
			
		||||
  mips_jazz.c             Hervé Poussineau
 | 
			
		||||
  mips_mipssim.c          ?
 | 
			
		||||
PowerPC
 | 
			
		||||
  ppc_prep.c              ?
 | 
			
		||||
  ppc_oldworld.c          Fabrice Bellard
 | 
			
		||||
  ppc_chrp.c              Fabrice Bellard
 | 
			
		||||
  ppc405_boards.c         ?
 | 
			
		||||
M86K
 | 
			
		||||
  mcf5208.c               Paul Brook
 | 
			
		||||
  an5206.c                Paul Brook
 | 
			
		||||
  dummy_m68k.c            Paul Brook
 | 
			
		||||
SH4
 | 
			
		||||
  shix.c                  ?
 | 
			
		||||
  r2d.c                   Magnus Damm
 | 
			
		||||
CRIS
 | 
			
		||||
  etraxfs.c               Edgar E. Iglesias
 | 
			
		||||
  axis_dev88.c            Edgar E. Iglesias
 | 
			
		||||
Alpha
 | 
			
		||||
MicroBlaze
 | 
			
		||||
  petalogix_s3adsp1800.c  Edgar E. Iglesias
 | 
			
		||||
S390
 | 
			
		||||
  s390-*.c                Alexander Graf
 | 
			
		||||
 | 
			
		||||
Generic Subsystems:
 | 
			
		||||
-------------------  
 | 
			
		||||
 | 
			
		||||
Dynamic translator        Fabrice Bellard
 | 
			
		||||
Main loop                 Fabrice Bellard (new maintainer needed)
 | 
			
		||||
TCG                       Fabrice Bellard
 | 
			
		||||
IDE device                ?
 | 
			
		||||
SCSI device               Paul Brook
 | 
			
		||||
PCI layer                 Michael S. Tsirkin
 | 
			
		||||
USB layer                 ?
 | 
			
		||||
Block layer               ?
 | 
			
		||||
Graphic layer             ?
 | 
			
		||||
Audio device layer        Vassili Karpov (malc)
 | 
			
		||||
Character device layer    ?
 | 
			
		||||
Network device layer      ?
 | 
			
		||||
GDB stub                  ?
 | 
			
		||||
Linux user                ?
 | 
			
		||||
Darwin user               ?
 | 
			
		||||
SLIRP                     ?
 | 
			
		||||
							
								
								
									
										332
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										332
									
								
								Makefile
									
									
									
									
									
								
							@@ -1,151 +1,45 @@
 | 
			
		||||
# Makefile for QEMU.
 | 
			
		||||
 | 
			
		||||
GENERATED_HEADERS = config-host.h
 | 
			
		||||
 | 
			
		||||
ifneq ($(wildcard config-host.mak),)
 | 
			
		||||
# Put the all: rule here so that config-host.mak can contain dependencies.
 | 
			
		||||
all: build-all
 | 
			
		||||
include config-host.mak
 | 
			
		||||
include $(SRC_PATH)/rules.mak
 | 
			
		||||
config-host.mak: $(SRC_PATH)/configure
 | 
			
		||||
	@echo $@ is out-of-date, running configure
 | 
			
		||||
	@sed -n "/.*Configured with/s/[^:]*: //p" $@ | sh
 | 
			
		||||
else
 | 
			
		||||
config-host.mak:
 | 
			
		||||
	@echo "Please call configure before running make!"
 | 
			
		||||
	@exit 1
 | 
			
		||||
 | 
			
		||||
CFLAGS=-Wall -O2 -g -fno-strict-aliasing -I.
 | 
			
		||||
ifdef CONFIG_DARWIN
 | 
			
		||||
CFLAGS+= -mdynamic-no-pic
 | 
			
		||||
endif
 | 
			
		||||
LDFLAGS=-g
 | 
			
		||||
LIBS=
 | 
			
		||||
DEFINES+=-D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE
 | 
			
		||||
TOOLS=qemu-img$(EXESUF)
 | 
			
		||||
ifdef CONFIG_STATIC
 | 
			
		||||
LDFLAGS+=-static
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
# Don't try to regenerate Makefile or configure
 | 
			
		||||
# We don't generate any of them
 | 
			
		||||
Makefile: ;
 | 
			
		||||
configure: ;
 | 
			
		||||
 | 
			
		||||
.PHONY: all clean cscope distclean dvi html info install install-doc \
 | 
			
		||||
	pdf recurse-all speed tar tarbin test build-all
 | 
			
		||||
 | 
			
		||||
$(call set-vpath, $(SRC_PATH):$(SRC_PATH)/hw)
 | 
			
		||||
 | 
			
		||||
LIBS+=-lz $(LIBS_TOOLS)
 | 
			
		||||
 | 
			
		||||
ifdef BUILD_DOCS
 | 
			
		||||
DOCS=qemu-doc.html qemu-tech.html qemu.1 qemu-img.1 qemu-nbd.8 QMP/qmp-commands.txt
 | 
			
		||||
DOCS=qemu-doc.html qemu-tech.html qemu.1 qemu-img.1
 | 
			
		||||
else
 | 
			
		||||
DOCS=
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
SUBDIR_MAKEFLAGS=$(if $(V),,--no-print-directory)
 | 
			
		||||
SUBDIR_DEVICES_MAK=$(patsubst %, %/config-devices.mak, $(TARGET_DIRS))
 | 
			
		||||
all: dyngen$(EXESUF) $(TOOLS) $(DOCS)
 | 
			
		||||
	for d in $(TARGET_DIRS); do \
 | 
			
		||||
	$(MAKE) -C $$d $@ || exit 1 ; \
 | 
			
		||||
        done
 | 
			
		||||
 | 
			
		||||
config-all-devices.mak: $(SUBDIR_DEVICES_MAK)
 | 
			
		||||
	$(call quiet-command,cat $(SUBDIR_DEVICES_MAK) | grep =y | sort -u > $@,"  GEN   $@")
 | 
			
		||||
qemu-img$(EXESUF): qemu-img.c block.c block-cow.c block-qcow.c aes.c block-vmdk.c block-cloop.c block-dmg.c block-bochs.c block-vpc.c block-vvfat.c
 | 
			
		||||
	$(CC) -DQEMU_TOOL $(CFLAGS) $(LDFLAGS) $(DEFINES) -o $@ $^ -lz $(LIBS)
 | 
			
		||||
 | 
			
		||||
%/config-devices.mak: default-configs/%.mak
 | 
			
		||||
	$(call quiet-command,cat $< > $@.tmp, "  GEN   $@")
 | 
			
		||||
	@if test -f $@; then \
 | 
			
		||||
	  if cmp -s $@.old $@ || cmp -s $@ $@.tmp; then \
 | 
			
		||||
	    mv $@.tmp $@; \
 | 
			
		||||
	    cp -p $@ $@.old; \
 | 
			
		||||
	  else \
 | 
			
		||||
	    if test -f $@.old; then \
 | 
			
		||||
	      echo "WARNING: $@ (user modified) out of date.";\
 | 
			
		||||
	    else \
 | 
			
		||||
	      echo "WARNING: $@ out of date.";\
 | 
			
		||||
	    fi; \
 | 
			
		||||
	    echo "Run \"make defconfig\" to regenerate."; \
 | 
			
		||||
	    rm $@.tmp; \
 | 
			
		||||
	  fi; \
 | 
			
		||||
	 else \
 | 
			
		||||
	  mv $@.tmp $@; \
 | 
			
		||||
	  cp -p $@ $@.old; \
 | 
			
		||||
	 fi
 | 
			
		||||
 | 
			
		||||
defconfig:
 | 
			
		||||
	rm -f config-all-devices.mak $(SUBDIR_DEVICES_MAK)
 | 
			
		||||
 | 
			
		||||
-include config-all-devices.mak
 | 
			
		||||
 | 
			
		||||
build-all: $(DOCS) $(TOOLS) recurse-all
 | 
			
		||||
 | 
			
		||||
config-host.h: config-host.h-timestamp
 | 
			
		||||
config-host.h-timestamp: config-host.mak
 | 
			
		||||
 | 
			
		||||
SUBDIR_RULES=$(patsubst %,subdir-%, $(TARGET_DIRS))
 | 
			
		||||
 | 
			
		||||
subdir-%: $(GENERATED_HEADERS)
 | 
			
		||||
	$(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C $* V="$(V)" TARGET_DIR="$*/" all,)
 | 
			
		||||
 | 
			
		||||
ifneq ($(wildcard config-host.mak),)
 | 
			
		||||
include $(SRC_PATH)/Makefile.objs
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
$(common-obj-y): $(GENERATED_HEADERS)
 | 
			
		||||
$(filter %-softmmu,$(SUBDIR_RULES)): $(common-obj-y) subdir-libdis
 | 
			
		||||
 | 
			
		||||
$(filter %-user,$(SUBDIR_RULES)): $(GENERATED_HEADERS) subdir-libdis-user subdir-libuser
 | 
			
		||||
 | 
			
		||||
ROMSUBDIR_RULES=$(patsubst %,romsubdir-%, $(ROMS))
 | 
			
		||||
romsubdir-%:
 | 
			
		||||
	$(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C pc-bios/$* V="$(V)" TARGET_DIR="$*/",)
 | 
			
		||||
 | 
			
		||||
ALL_SUBDIRS=$(TARGET_DIRS) $(patsubst %,pc-bios/%, $(ROMS))
 | 
			
		||||
 | 
			
		||||
recurse-all: $(SUBDIR_RULES) $(ROMSUBDIR_RULES)
 | 
			
		||||
 | 
			
		||||
audio/audio.o audio/fmodaudio.o: QEMU_CFLAGS += $(FMOD_CFLAGS)
 | 
			
		||||
 | 
			
		||||
QEMU_CFLAGS+=$(CURL_CFLAGS)
 | 
			
		||||
 | 
			
		||||
ui/cocoa.o: ui/cocoa.m
 | 
			
		||||
 | 
			
		||||
ui/sdl.o audio/sdlaudio.o ui/sdl_zoom.o baum.o: QEMU_CFLAGS += $(SDL_CFLAGS)
 | 
			
		||||
 | 
			
		||||
ui/vnc.o: QEMU_CFLAGS += $(VNC_TLS_CFLAGS)
 | 
			
		||||
 | 
			
		||||
bt-host.o: QEMU_CFLAGS += $(BLUEZ_CFLAGS)
 | 
			
		||||
 | 
			
		||||
######################################################################
 | 
			
		||||
 | 
			
		||||
qemu-img.o: qemu-img-cmds.h
 | 
			
		||||
qemu-img.o qemu-tool.o qemu-nbd.o qemu-io.o cmd.o: $(GENERATED_HEADERS)
 | 
			
		||||
 | 
			
		||||
qemu-img$(EXESUF): qemu-img.o qemu-tool.o qemu-error.o $(block-obj-y) $(qobject-obj-y)
 | 
			
		||||
 | 
			
		||||
qemu-nbd$(EXESUF): qemu-nbd.o qemu-tool.o qemu-error.o $(block-obj-y) $(qobject-obj-y)
 | 
			
		||||
 | 
			
		||||
qemu-io$(EXESUF): qemu-io.o cmd.o qemu-tool.o qemu-error.o $(block-obj-y) $(qobject-obj-y)
 | 
			
		||||
 | 
			
		||||
qemu-img-cmds.h: $(SRC_PATH)/qemu-img-cmds.hx
 | 
			
		||||
	$(call quiet-command,sh $(SRC_PATH)/hxtool -h < $< > $@,"  GEN   $@")
 | 
			
		||||
 | 
			
		||||
check-qint.o check-qstring.o check-qdict.o check-qlist.o check-qfloat.o check-qjson.o: $(GENERATED_HEADERS)
 | 
			
		||||
 | 
			
		||||
check-qint: check-qint.o qint.o qemu-malloc.o
 | 
			
		||||
check-qstring: check-qstring.o qstring.o qemu-malloc.o
 | 
			
		||||
check-qdict: check-qdict.o qdict.o qfloat.o qint.o qstring.o qbool.o qemu-malloc.o qlist.o
 | 
			
		||||
check-qlist: check-qlist.o qlist.o qint.o qemu-malloc.o
 | 
			
		||||
check-qfloat: check-qfloat.o qfloat.o qemu-malloc.o
 | 
			
		||||
check-qjson: check-qjson.o qfloat.o qint.o qdict.o qstring.o qlist.o qbool.o qjson.o json-streamer.o json-lexer.o json-parser.o qemu-malloc.o
 | 
			
		||||
dyngen$(EXESUF): dyngen.c
 | 
			
		||||
	$(HOST_CC) $(CFLAGS) $(DEFINES) -o $@ $^
 | 
			
		||||
 | 
			
		||||
clean:
 | 
			
		||||
# avoid old build problems by removing potentially incorrect old files
 | 
			
		||||
	rm -f config.mak op-i386.h opc-i386.h gen-op-i386.h op-arm.h opc-arm.h gen-op-arm.h
 | 
			
		||||
	rm -f *.o *.d *.a $(TOOLS) TAGS cscope.* *.pod *~ */*~
 | 
			
		||||
	rm -f slirp/*.o slirp/*.d audio/*.o audio/*.d block/*.o block/*.d net/*.o net/*.d fsdev/*.o fsdev/*.d ui/*.o ui/*.d
 | 
			
		||||
	rm -f qemu-img-cmds.h
 | 
			
		||||
	rm -f config.mak config.h op-i386.h opc-i386.h gen-op-i386.h op-arm.h opc-arm.h gen-op-arm.h 
 | 
			
		||||
	rm -f *.o *.a $(TOOLS) dyngen$(EXESUF) TAGS *.pod *~ */*~
 | 
			
		||||
	$(MAKE) -C tests clean
 | 
			
		||||
	for d in $(ALL_SUBDIRS) libhw32 libhw64 libuser libdis libdis-user; do \
 | 
			
		||||
	if test -d $$d; then $(MAKE) -C $$d $@ || exit 1; fi; \
 | 
			
		||||
	for d in $(TARGET_DIRS); do \
 | 
			
		||||
	$(MAKE) -C $$d $@ || exit 1 ; \
 | 
			
		||||
        done
 | 
			
		||||
 | 
			
		||||
distclean: clean
 | 
			
		||||
	rm -f config-host.mak config-host.h* config-host.ld $(DOCS) qemu-options.texi qemu-img-cmds.texi qemu-monitor.texi
 | 
			
		||||
	rm -f qemu-options.def
 | 
			
		||||
	rm -f config-all-devices.mak
 | 
			
		||||
	rm -f roms/seabios/config.mak roms/vgabios/config.mak
 | 
			
		||||
	rm -f qemu-doc.info qemu-doc.aux qemu-doc.cp qemu-doc.dvi qemu-doc.fn qemu-doc.info qemu-doc.ky qemu-doc.log qemu-doc.pdf qemu-doc.pg qemu-doc.toc qemu-doc.tp qemu-doc.vr
 | 
			
		||||
	rm -f qemu-tech.info qemu-tech.aux qemu-tech.cp qemu-tech.dvi qemu-tech.fn qemu-tech.info qemu-tech.ky qemu-tech.log qemu-tech.pdf qemu-tech.pg qemu-tech.toc qemu-tech.tp qemu-tech.vr
 | 
			
		||||
	for d in $(TARGET_DIRS) libhw32 libhw64 libuser libdis libdis-user; do \
 | 
			
		||||
	rm -f config-host.mak config-host.h $(DOCS)
 | 
			
		||||
	for d in $(TARGET_DIRS); do \
 | 
			
		||||
	rm -rf $$d || exit 1 ; \
 | 
			
		||||
        done
 | 
			
		||||
 | 
			
		||||
@@ -153,168 +47,100 @@ KEYMAPS=da     en-gb  et  fr     fr-ch  is  lt  modifiers  no  pt-br  sv \
 | 
			
		||||
ar      de     en-us  fi  fr-be  hr     it  lv  nl         pl  ru     th \
 | 
			
		||||
common  de-ch  es     fo  fr-ca  hu     ja  mk  nl-be      pt  sl     tr
 | 
			
		||||
 | 
			
		||||
ifdef INSTALL_BLOBS
 | 
			
		||||
BLOBS=bios.bin vgabios.bin vgabios-cirrus.bin ppc_rom.bin \
 | 
			
		||||
video.x openbios-sparc32 openbios-sparc64 openbios-ppc \
 | 
			
		||||
gpxe-eepro100-80861209.rom \
 | 
			
		||||
gpxe-eepro100-80861229.rom \
 | 
			
		||||
pxe-e1000.bin \
 | 
			
		||||
pxe-ne2k_pci.bin pxe-pcnet.bin \
 | 
			
		||||
pxe-rtl8139.bin pxe-virtio.bin \
 | 
			
		||||
bamboo.dtb petalogix-s3adsp1800.dtb \
 | 
			
		||||
multiboot.bin linuxboot.bin \
 | 
			
		||||
s390-zipl.rom
 | 
			
		||||
else
 | 
			
		||||
BLOBS=
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
install-doc: $(DOCS)
 | 
			
		||||
	$(INSTALL_DIR) "$(DESTDIR)$(docdir)"
 | 
			
		||||
	$(INSTALL_DATA) qemu-doc.html  qemu-tech.html "$(DESTDIR)$(docdir)"
 | 
			
		||||
ifdef CONFIG_POSIX
 | 
			
		||||
	$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man1"
 | 
			
		||||
	$(INSTALL_DATA) qemu.1 qemu-img.1 "$(DESTDIR)$(mandir)/man1"
 | 
			
		||||
	$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man8"
 | 
			
		||||
	$(INSTALL_DATA) qemu-nbd.8 "$(DESTDIR)$(mandir)/man8"
 | 
			
		||||
	mkdir -p "$(DESTDIR)$(docdir)"
 | 
			
		||||
	$(INSTALL) -m 644 qemu-doc.html  qemu-tech.html "$(DESTDIR)$(docdir)"
 | 
			
		||||
ifndef CONFIG_WIN32
 | 
			
		||||
	mkdir -p "$(DESTDIR)$(mandir)/man1"
 | 
			
		||||
	$(INSTALL) qemu.1 qemu-img.1 "$(DESTDIR)$(mandir)/man1"
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
install-sysconfig:
 | 
			
		||||
	$(INSTALL_DIR) "$(DESTDIR)$(sysconfdir)/qemu"
 | 
			
		||||
	$(INSTALL_DATA) $(SRC_PATH)/sysconfigs/target/target-x86_64.conf "$(DESTDIR)$(sysconfdir)/qemu"
 | 
			
		||||
 | 
			
		||||
install: all $(if $(BUILD_DOCS),install-doc) install-sysconfig
 | 
			
		||||
	$(INSTALL_DIR) "$(DESTDIR)$(bindir)"
 | 
			
		||||
ifneq ($(TOOLS),)
 | 
			
		||||
	$(INSTALL_PROG) $(STRIP_OPT) $(TOOLS) "$(DESTDIR)$(bindir)"
 | 
			
		||||
endif
 | 
			
		||||
ifneq ($(BLOBS),)
 | 
			
		||||
	$(INSTALL_DIR) "$(DESTDIR)$(datadir)"
 | 
			
		||||
	set -e; for x in $(BLOBS); do \
 | 
			
		||||
		$(INSTALL_DATA) $(SRC_PATH)/pc-bios/$$x "$(DESTDIR)$(datadir)"; \
 | 
			
		||||
install: all $(if $(BUILD_DOCS),install-doc)
 | 
			
		||||
	mkdir -p "$(DESTDIR)$(bindir)"
 | 
			
		||||
	$(INSTALL) -m 755 -s $(TOOLS) "$(DESTDIR)$(bindir)"
 | 
			
		||||
	mkdir -p "$(DESTDIR)$(datadir)"
 | 
			
		||||
	for x in bios.bin vgabios.bin vgabios-cirrus.bin ppc_rom.bin \
 | 
			
		||||
			video.x proll.elf linux_boot.bin; do \
 | 
			
		||||
		$(INSTALL) -m 644 $(SRC_PATH)/pc-bios/$$x "$(DESTDIR)$(datadir)"; \
 | 
			
		||||
	done
 | 
			
		||||
ifndef CONFIG_WIN32
 | 
			
		||||
	mkdir -p "$(DESTDIR)$(datadir)/keymaps"
 | 
			
		||||
	for x in $(KEYMAPS); do \
 | 
			
		||||
		$(INSTALL) -m 644 $(SRC_PATH)/keymaps/$$x "$(DESTDIR)$(datadir)/keymaps"; \
 | 
			
		||||
	done
 | 
			
		||||
endif
 | 
			
		||||
	$(INSTALL_DIR) "$(DESTDIR)$(datadir)/keymaps"
 | 
			
		||||
	set -e; for x in $(KEYMAPS); do \
 | 
			
		||||
		$(INSTALL_DATA) $(SRC_PATH)/pc-bios/keymaps/$$x "$(DESTDIR)$(datadir)/keymaps"; \
 | 
			
		||||
	done
 | 
			
		||||
	for d in $(TARGET_DIRS); do \
 | 
			
		||||
	$(MAKE) -C $$d $@ || exit 1 ; \
 | 
			
		||||
        done
 | 
			
		||||
 | 
			
		||||
# various test targets
 | 
			
		||||
test speed: all
 | 
			
		||||
test speed test2: all
 | 
			
		||||
	$(MAKE) -C tests $@
 | 
			
		||||
 | 
			
		||||
.PHONY: TAGS
 | 
			
		||||
TAGS: 
 | 
			
		||||
	find "$(SRC_PATH)" -name '*.[hc]' -print0 | xargs -0 etags
 | 
			
		||||
	etags *.[ch] tests/*.[ch]
 | 
			
		||||
 | 
			
		||||
cscope:
 | 
			
		||||
	rm -f ./cscope.*
 | 
			
		||||
	find . -name "*.[ch]" -print | sed 's,^\./,,' > ./cscope.files
 | 
			
		||||
	find . -name "*.[ch]" -print > ./cscope.files
 | 
			
		||||
	cscope -b
 | 
			
		||||
 | 
			
		||||
# documentation
 | 
			
		||||
MAKEINFO=makeinfo
 | 
			
		||||
MAKEINFOFLAGS=--no-headers --no-split --number-sections
 | 
			
		||||
TEXIFLAG=$(if $(V),,--quiet)
 | 
			
		||||
%.dvi: %.texi
 | 
			
		||||
	$(call quiet-command,texi2dvi $(TEXIFLAG) -I . $<,"  GEN   $@")
 | 
			
		||||
 | 
			
		||||
%.html: %.texi
 | 
			
		||||
	$(call quiet-command,$(MAKEINFO) $(MAKEINFOFLAGS) --html $< -o $@, \
 | 
			
		||||
	"  GEN   $@")
 | 
			
		||||
	texi2html -monolithic -number $<
 | 
			
		||||
 | 
			
		||||
%.info: %.texi
 | 
			
		||||
	$(call quiet-command,$(MAKEINFO) $< -o $@,"  GEN   $@")
 | 
			
		||||
	makeinfo $< -o $@
 | 
			
		||||
 | 
			
		||||
%.pdf: %.texi
 | 
			
		||||
	$(call quiet-command,texi2pdf $(TEXIFLAG) -I . $<,"  GEN   $@")
 | 
			
		||||
%.dvi: %.texi
 | 
			
		||||
	texi2dvi $<
 | 
			
		||||
 | 
			
		||||
qemu-options.texi: $(SRC_PATH)/qemu-options.hx
 | 
			
		||||
	$(call quiet-command,sh $(SRC_PATH)/hxtool -t < $< > $@,"  GEN   $@")
 | 
			
		||||
qemu.1: qemu-doc.texi
 | 
			
		||||
	$(SRC_PATH)/texi2pod.pl $< qemu.pod
 | 
			
		||||
	pod2man --section=1 --center=" " --release=" " qemu.pod > $@
 | 
			
		||||
 | 
			
		||||
qemu-monitor.texi: $(SRC_PATH)/qemu-monitor.hx
 | 
			
		||||
	$(call quiet-command,sh $(SRC_PATH)/hxtool -t < $< > $@,"  GEN   $@")
 | 
			
		||||
qemu-img.1: qemu-img.texi
 | 
			
		||||
	$(SRC_PATH)/texi2pod.pl $< qemu-img.pod
 | 
			
		||||
	pod2man --section=1 --center=" " --release=" " qemu-img.pod > $@
 | 
			
		||||
 | 
			
		||||
QMP/qmp-commands.txt: $(SRC_PATH)/qemu-monitor.hx
 | 
			
		||||
	$(call quiet-command,sh $(SRC_PATH)/hxtool -q < $< > $@,"  GEN   $@")
 | 
			
		||||
 | 
			
		||||
qemu-img-cmds.texi: $(SRC_PATH)/qemu-img-cmds.hx
 | 
			
		||||
	$(call quiet-command,sh $(SRC_PATH)/hxtool -t < $< > $@,"  GEN   $@")
 | 
			
		||||
 | 
			
		||||
qemu.1: qemu-doc.texi qemu-options.texi qemu-monitor.texi
 | 
			
		||||
	$(call quiet-command, \
 | 
			
		||||
	  perl -Ww -- $(SRC_PATH)/texi2pod.pl $< qemu.pod && \
 | 
			
		||||
	  pod2man --section=1 --center=" " --release=" " qemu.pod > $@, \
 | 
			
		||||
	  "  GEN   $@")
 | 
			
		||||
 | 
			
		||||
qemu-img.1: qemu-img.texi qemu-img-cmds.texi
 | 
			
		||||
	$(call quiet-command, \
 | 
			
		||||
	  perl -Ww -- $(SRC_PATH)/texi2pod.pl $< qemu-img.pod && \
 | 
			
		||||
	  pod2man --section=1 --center=" " --release=" " qemu-img.pod > $@, \
 | 
			
		||||
	  "  GEN   $@")
 | 
			
		||||
 | 
			
		||||
qemu-nbd.8: qemu-nbd.texi
 | 
			
		||||
	$(call quiet-command, \
 | 
			
		||||
	  perl -Ww -- $(SRC_PATH)/texi2pod.pl $< qemu-nbd.pod && \
 | 
			
		||||
	  pod2man --section=8 --center=" " --release=" " qemu-nbd.pod > $@, \
 | 
			
		||||
	  "  GEN   $@")
 | 
			
		||||
 | 
			
		||||
dvi: qemu-doc.dvi qemu-tech.dvi
 | 
			
		||||
html: qemu-doc.html qemu-tech.html
 | 
			
		||||
info: qemu-doc.info qemu-tech.info
 | 
			
		||||
pdf: qemu-doc.pdf qemu-tech.pdf
 | 
			
		||||
 | 
			
		||||
qemu-doc.dvi qemu-doc.html qemu-doc.info qemu-doc.pdf: \
 | 
			
		||||
	qemu-img.texi qemu-nbd.texi qemu-options.texi \
 | 
			
		||||
	qemu-monitor.texi qemu-img-cmds.texi
 | 
			
		||||
 | 
			
		||||
VERSION ?= $(shell cat VERSION)
 | 
			
		||||
FILE = qemu-$(VERSION)
 | 
			
		||||
FILE=qemu-$(shell cat VERSION)
 | 
			
		||||
 | 
			
		||||
# tar release (use 'make -k tar' on a checkouted tree)
 | 
			
		||||
tar:
 | 
			
		||||
	rm -rf /tmp/$(FILE)
 | 
			
		||||
	cp -r . /tmp/$(FILE)
 | 
			
		||||
	cd /tmp && tar zcvf ~/$(FILE).tar.gz $(FILE) --exclude CVS --exclude .git --exclude .svn
 | 
			
		||||
	( cd /tmp ; tar zcvf ~/$(FILE).tar.gz $(FILE) --exclude CVS )
 | 
			
		||||
	rm -rf /tmp/$(FILE)
 | 
			
		||||
 | 
			
		||||
SYSTEM_TARGETS=$(filter %-softmmu,$(TARGET_DIRS))
 | 
			
		||||
SYSTEM_PROGS=$(patsubst qemu-system-i386,qemu, \
 | 
			
		||||
             $(patsubst %-softmmu,qemu-system-%, \
 | 
			
		||||
             $(SYSTEM_TARGETS)))
 | 
			
		||||
 | 
			
		||||
USER_TARGETS=$(filter %-user,$(TARGET_DIRS))
 | 
			
		||||
USER_PROGS=$(patsubst %-bsd-user,qemu-%, \
 | 
			
		||||
           $(patsubst %-darwin-user,qemu-%, \
 | 
			
		||||
           $(patsubst %-linux-user,qemu-%, \
 | 
			
		||||
           $(USER_TARGETS))))
 | 
			
		||||
 | 
			
		||||
# generate a binary distribution
 | 
			
		||||
tarbin:
 | 
			
		||||
	cd / && tar zcvf ~/qemu-$(VERSION)-$(ARCH).tar.gz \
 | 
			
		||||
	$(patsubst %,$(bindir)/%, $(SYSTEM_PROGS)) \
 | 
			
		||||
	$(patsubst %,$(bindir)/%, $(USER_PROGS)) \
 | 
			
		||||
	( cd / ; tar zcvf ~/qemu-$(VERSION)-i386.tar.gz \
 | 
			
		||||
	$(bindir)/qemu \
 | 
			
		||||
	$(bindir)/qemu-system-ppc \
 | 
			
		||||
	$(bindir)/qemu-system-sparc \
 | 
			
		||||
	$(bindir)/qemu-system-x86_64 \
 | 
			
		||||
	$(bindir)/qemu-system-mips \
 | 
			
		||||
	$(bindir)/qemu-system-mipsel \
 | 
			
		||||
	$(bindir)/qemu-system-arm \
 | 
			
		||||
	$(bindir)/qemu-i386 \
 | 
			
		||||
        $(bindir)/qemu-arm \
 | 
			
		||||
        $(bindir)/qemu-armeb \
 | 
			
		||||
        $(bindir)/qemu-sparc \
 | 
			
		||||
        $(bindir)/qemu-ppc \
 | 
			
		||||
        $(bindir)/qemu-mips \
 | 
			
		||||
        $(bindir)/qemu-mipsel \
 | 
			
		||||
        $(bindir)/qemu-img \
 | 
			
		||||
	$(bindir)/qemu-nbd \
 | 
			
		||||
	$(datadir)/bios.bin \
 | 
			
		||||
	$(datadir)/vgabios.bin \
 | 
			
		||||
	$(datadir)/vgabios-cirrus.bin \
 | 
			
		||||
	$(datadir)/ppc_rom.bin \
 | 
			
		||||
	$(datadir)/video.x \
 | 
			
		||||
	$(datadir)/openbios-sparc32 \
 | 
			
		||||
	$(datadir)/openbios-sparc64 \
 | 
			
		||||
	$(datadir)/openbios-ppc \
 | 
			
		||||
	$(datadir)/pxe-ne2k_pci.bin \
 | 
			
		||||
	$(datadir)/pxe-rtl8139.bin \
 | 
			
		||||
	$(datadir)/pxe-pcnet.bin \
 | 
			
		||||
	$(datadir)/pxe-e1000.bin \
 | 
			
		||||
	$(datadir)/proll.elf \
 | 
			
		||||
	$(datadir)/linux_boot.bin \
 | 
			
		||||
	$(docdir)/qemu-doc.html \
 | 
			
		||||
	$(docdir)/qemu-tech.html \
 | 
			
		||||
	$(mandir)/man1/qemu.1 \
 | 
			
		||||
	$(mandir)/man1/qemu-img.1 \
 | 
			
		||||
	$(mandir)/man8/qemu-nbd.8
 | 
			
		||||
	$(mandir)/man1/qemu.1 $(mandir)/man1/qemu-img.1 )
 | 
			
		||||
 | 
			
		||||
# Include automatically generated dependency files
 | 
			
		||||
-include $(wildcard *.d audio/*.d slirp/*.d block/*.d net/*.d ui/*.d)
 | 
			
		||||
ifneq ($(wildcard .depend),)
 | 
			
		||||
include .depend
 | 
			
		||||
endif
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										23
									
								
								Makefile.dis
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								Makefile.dis
									
									
									
									
									
								
							@@ -1,23 +0,0 @@
 | 
			
		||||
# Makefile for disassemblers.
 | 
			
		||||
 | 
			
		||||
include ../config-host.mak
 | 
			
		||||
include config.mak
 | 
			
		||||
include $(SRC_PATH)/rules.mak
 | 
			
		||||
 | 
			
		||||
.PHONY: all
 | 
			
		||||
 | 
			
		||||
$(call set-vpath, $(SRC_PATH))
 | 
			
		||||
 | 
			
		||||
QEMU_CFLAGS+=-I..
 | 
			
		||||
 | 
			
		||||
include $(SRC_PATH)/Makefile.objs
 | 
			
		||||
 | 
			
		||||
all: $(libdis-y)
 | 
			
		||||
# Dummy command so that make thinks it has done something
 | 
			
		||||
	@true
 | 
			
		||||
 | 
			
		||||
clean:
 | 
			
		||||
	rm -f *.o *.d *.a *~
 | 
			
		||||
 | 
			
		||||
# Include automatically generated dependency files
 | 
			
		||||
-include $(wildcard *.d */*.d)
 | 
			
		||||
							
								
								
									
										24
									
								
								Makefile.hw
									
									
									
									
									
								
							
							
						
						
									
										24
									
								
								Makefile.hw
									
									
									
									
									
								
							@@ -1,24 +0,0 @@
 | 
			
		||||
# Makefile for qemu target independent devices.
 | 
			
		||||
 | 
			
		||||
include ../config-host.mak
 | 
			
		||||
include ../config-all-devices.mak
 | 
			
		||||
include config.mak
 | 
			
		||||
include $(SRC_PATH)/rules.mak
 | 
			
		||||
 | 
			
		||||
.PHONY: all
 | 
			
		||||
 | 
			
		||||
$(call set-vpath, $(SRC_PATH):$(SRC_PATH)/hw)
 | 
			
		||||
 | 
			
		||||
QEMU_CFLAGS+=-I.. -I$(SRC_PATH)/fpu
 | 
			
		||||
 | 
			
		||||
include $(SRC_PATH)/Makefile.objs
 | 
			
		||||
 | 
			
		||||
all: $(hw-obj-y)
 | 
			
		||||
# Dummy command so that make thinks it has done something
 | 
			
		||||
	@true
 | 
			
		||||
 | 
			
		||||
clean:
 | 
			
		||||
	rm -f *.o */*.o *.d */*.d *.a */*.a *~ */*~
 | 
			
		||||
 | 
			
		||||
# Include automatically generated dependency files
 | 
			
		||||
-include $(wildcard *.d */*.d)
 | 
			
		||||
							
								
								
									
										278
									
								
								Makefile.objs
									
									
									
									
									
								
							
							
						
						
									
										278
									
								
								Makefile.objs
									
									
									
									
									
								
							@@ -1,278 +0,0 @@
 | 
			
		||||
#######################################################################
 | 
			
		||||
# QObject
 | 
			
		||||
qobject-obj-y = qint.o qstring.o qdict.o qlist.o qfloat.o qbool.o
 | 
			
		||||
qobject-obj-y += qjson.o json-lexer.o json-streamer.o json-parser.o
 | 
			
		||||
qobject-obj-y += qerror.o
 | 
			
		||||
 | 
			
		||||
#######################################################################
 | 
			
		||||
# block-obj-y is code used by both qemu system emulation and qemu-img
 | 
			
		||||
 | 
			
		||||
block-obj-y = cutils.o cache-utils.o qemu-malloc.o qemu-option.o module.o
 | 
			
		||||
block-obj-y += nbd.o block.o aio.o aes.o osdep.o qemu-config.o
 | 
			
		||||
block-obj-$(CONFIG_POSIX) += posix-aio-compat.o
 | 
			
		||||
block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o
 | 
			
		||||
 | 
			
		||||
block-nested-y += raw.o cow.o qcow.o vdi.o vmdk.o cloop.o dmg.o bochs.o vpc.o vvfat.o
 | 
			
		||||
block-nested-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o
 | 
			
		||||
block-nested-y += parallels.o nbd.o blkdebug.o sheepdog.o
 | 
			
		||||
block-nested-$(CONFIG_WIN32) += raw-win32.o
 | 
			
		||||
block-nested-$(CONFIG_POSIX) += raw-posix.o
 | 
			
		||||
block-nested-$(CONFIG_CURL) += curl.o
 | 
			
		||||
 | 
			
		||||
block-obj-y +=  $(addprefix block/, $(block-nested-y))
 | 
			
		||||
 | 
			
		||||
net-obj-y = net.o
 | 
			
		||||
net-nested-y = queue.o checksum.o util.o
 | 
			
		||||
net-nested-y += socket.o
 | 
			
		||||
net-nested-y += dump.o
 | 
			
		||||
net-nested-$(CONFIG_POSIX) += tap.o
 | 
			
		||||
net-nested-$(CONFIG_LINUX) += tap-linux.o
 | 
			
		||||
net-nested-$(CONFIG_WIN32) += tap-win32.o
 | 
			
		||||
net-nested-$(CONFIG_BSD) += tap-bsd.o
 | 
			
		||||
net-nested-$(CONFIG_SOLARIS) += tap-solaris.o
 | 
			
		||||
net-nested-$(CONFIG_AIX) += tap-aix.o
 | 
			
		||||
net-nested-$(CONFIG_SLIRP) += slirp.o
 | 
			
		||||
net-nested-$(CONFIG_VDE) += vde.o
 | 
			
		||||
net-obj-y += $(addprefix net/, $(net-nested-y))
 | 
			
		||||
 | 
			
		||||
fsdev-nested-$(CONFIG_VIRTFS) = qemu-fsdev.o
 | 
			
		||||
fsdev-obj-$(CONFIG_VIRTFS) += $(addprefix fsdev/, $(fsdev-nested-y))
 | 
			
		||||
 | 
			
		||||
######################################################################
 | 
			
		||||
# libqemu_common.a: 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.
 | 
			
		||||
 | 
			
		||||
common-obj-y = $(block-obj-y) blockdev.o
 | 
			
		||||
common-obj-y += $(net-obj-y)
 | 
			
		||||
common-obj-y += $(qobject-obj-y)
 | 
			
		||||
common-obj-$(CONFIG_LINUX) += $(fsdev-obj-$(CONFIG_LINUX))
 | 
			
		||||
common-obj-y += readline.o console.o cursor.o async.o qemu-error.o
 | 
			
		||||
common-obj-$(CONFIG_WIN32) += os-win32.o
 | 
			
		||||
common-obj-$(CONFIG_POSIX) += os-posix.o
 | 
			
		||||
 | 
			
		||||
common-obj-y += tcg-runtime.o host-utils.o
 | 
			
		||||
common-obj-y += irq.o ioport.o input.o
 | 
			
		||||
common-obj-$(CONFIG_PTIMER) += ptimer.o
 | 
			
		||||
common-obj-$(CONFIG_MAX7310) += max7310.o
 | 
			
		||||
common-obj-$(CONFIG_WM8750) += wm8750.o
 | 
			
		||||
common-obj-$(CONFIG_TWL92230) += twl92230.o
 | 
			
		||||
common-obj-$(CONFIG_TSC2005) += tsc2005.o
 | 
			
		||||
common-obj-$(CONFIG_LM832X) += lm832x.o
 | 
			
		||||
common-obj-$(CONFIG_TMP105) += tmp105.o
 | 
			
		||||
common-obj-$(CONFIG_STELLARIS_INPUT) += stellaris_input.o
 | 
			
		||||
common-obj-$(CONFIG_SSD0303) += ssd0303.o
 | 
			
		||||
common-obj-$(CONFIG_SSD0323) += ssd0323.o
 | 
			
		||||
common-obj-$(CONFIG_ADS7846) += ads7846.o
 | 
			
		||||
common-obj-$(CONFIG_MAX111X) += max111x.o
 | 
			
		||||
common-obj-$(CONFIG_DS1338) += ds1338.o
 | 
			
		||||
common-obj-y += i2c.o smbus.o smbus_eeprom.o
 | 
			
		||||
common-obj-y += eeprom93xx.o
 | 
			
		||||
common-obj-y += scsi-disk.o cdrom.o
 | 
			
		||||
common-obj-y += scsi-generic.o scsi-bus.o
 | 
			
		||||
common-obj-y += usb.o usb-hub.o usb-$(HOST_USB).o usb-hid.o usb-msd.o usb-wacom.o
 | 
			
		||||
common-obj-y += usb-serial.o usb-net.o usb-bus.o
 | 
			
		||||
common-obj-$(CONFIG_SSI) += ssi.o
 | 
			
		||||
common-obj-$(CONFIG_SSI_SD) += ssi-sd.o
 | 
			
		||||
common-obj-$(CONFIG_SD) += sd.o
 | 
			
		||||
common-obj-y += bt.o bt-host.o bt-vhci.o bt-l2cap.o bt-sdp.o bt-hci.o bt-hid.o usb-bt.o
 | 
			
		||||
common-obj-y += bt-hci-csr.o
 | 
			
		||||
common-obj-y += buffered_file.o migration.o migration-tcp.o qemu-sockets.o
 | 
			
		||||
common-obj-y += qemu-char.o savevm.o #aio.o
 | 
			
		||||
common-obj-y += msmouse.o ps2.o
 | 
			
		||||
common-obj-y += qdev.o qdev-properties.o
 | 
			
		||||
common-obj-y += block-migration.o
 | 
			
		||||
 | 
			
		||||
common-obj-$(CONFIG_BRLAPI) += baum.o
 | 
			
		||||
common-obj-$(CONFIG_POSIX) += migration-exec.o migration-unix.o migration-fd.o
 | 
			
		||||
 | 
			
		||||
audio-obj-y = audio.o noaudio.o wavaudio.o mixeng.o
 | 
			
		||||
audio-obj-$(CONFIG_SDL) += sdlaudio.o
 | 
			
		||||
audio-obj-$(CONFIG_OSS) += ossaudio.o
 | 
			
		||||
audio-obj-$(CONFIG_COREAUDIO) += coreaudio.o
 | 
			
		||||
audio-obj-$(CONFIG_ALSA) += alsaaudio.o
 | 
			
		||||
audio-obj-$(CONFIG_DSOUND) += dsoundaudio.o
 | 
			
		||||
audio-obj-$(CONFIG_FMOD) += fmodaudio.o
 | 
			
		||||
audio-obj-$(CONFIG_ESD) += esdaudio.o
 | 
			
		||||
audio-obj-$(CONFIG_PA) += paaudio.o
 | 
			
		||||
audio-obj-$(CONFIG_WINWAVE) += winwaveaudio.o
 | 
			
		||||
audio-obj-$(CONFIG_AUDIO_PT_INT) += audio_pt_int.o
 | 
			
		||||
audio-obj-$(CONFIG_AUDIO_WIN_INT) += audio_win_int.o
 | 
			
		||||
audio-obj-y += wavcapture.o
 | 
			
		||||
common-obj-y += $(addprefix audio/, $(audio-obj-y))
 | 
			
		||||
 | 
			
		||||
ui-obj-y += keymaps.o
 | 
			
		||||
ui-obj-$(CONFIG_SDL) += sdl.o sdl_zoom.o x_keymap.o
 | 
			
		||||
ui-obj-$(CONFIG_CURSES) += curses.o
 | 
			
		||||
ui-obj-y += vnc.o d3des.o
 | 
			
		||||
ui-obj-y += vnc-enc-zlib.o vnc-enc-hextile.o
 | 
			
		||||
ui-obj-y += vnc-enc-tight.o vnc-palette.o
 | 
			
		||||
ui-obj-$(CONFIG_VNC_TLS) += vnc-tls.o vnc-auth-vencrypt.o
 | 
			
		||||
ui-obj-$(CONFIG_VNC_SASL) += vnc-auth-sasl.o
 | 
			
		||||
ui-obj-$(CONFIG_COCOA) += cocoa.o
 | 
			
		||||
ifdef CONFIG_VNC_THREAD
 | 
			
		||||
ui-obj-y += vnc-jobs-async.o
 | 
			
		||||
else
 | 
			
		||||
ui-obj-y += vnc-jobs-sync.o
 | 
			
		||||
endif
 | 
			
		||||
common-obj-y += $(addprefix ui/, $(ui-obj-y))
 | 
			
		||||
 | 
			
		||||
common-obj-y += iov.o acl.o
 | 
			
		||||
common-obj-$(CONFIG_THREAD) += qemu-thread.o
 | 
			
		||||
common-obj-y += notify.o event_notifier.o
 | 
			
		||||
common-obj-y += qemu-timer.o
 | 
			
		||||
 | 
			
		||||
slirp-obj-y = cksum.o if.o ip_icmp.o ip_input.o ip_output.o
 | 
			
		||||
slirp-obj-y += slirp.o mbuf.o misc.o sbuf.o socket.o tcp_input.o tcp_output.o
 | 
			
		||||
slirp-obj-y += tcp_subr.o tcp_timer.o udp.o bootp.o tftp.o
 | 
			
		||||
common-obj-$(CONFIG_SLIRP) += $(addprefix slirp/, $(slirp-obj-y))
 | 
			
		||||
 | 
			
		||||
# xen backend driver support
 | 
			
		||||
common-obj-$(CONFIG_XEN) += xen_backend.o xen_devconfig.o
 | 
			
		||||
common-obj-$(CONFIG_XEN) += xen_console.o xenfb.o xen_disk.o xen_nic.o
 | 
			
		||||
 | 
			
		||||
######################################################################
 | 
			
		||||
# libuser
 | 
			
		||||
 | 
			
		||||
user-obj-y =
 | 
			
		||||
user-obj-y += envlist.o path.o
 | 
			
		||||
user-obj-y += tcg-runtime.o host-utils.o
 | 
			
		||||
user-obj-y += cutils.o cache-utils.o
 | 
			
		||||
 | 
			
		||||
######################################################################
 | 
			
		||||
# libhw
 | 
			
		||||
 | 
			
		||||
hw-obj-y =
 | 
			
		||||
hw-obj-y += vl.o loader.o
 | 
			
		||||
hw-obj-y += virtio.o virtio-console.o
 | 
			
		||||
hw-obj-y += fw_cfg.o pci.o pci_host.o pcie_host.o
 | 
			
		||||
hw-obj-y += watchdog.o
 | 
			
		||||
hw-obj-$(CONFIG_ISA_MMIO) += isa_mmio.o
 | 
			
		||||
hw-obj-$(CONFIG_ECC) += ecc.o
 | 
			
		||||
hw-obj-$(CONFIG_NAND) += nand.o
 | 
			
		||||
hw-obj-$(CONFIG_PFLASH_CFI01) += pflash_cfi01.o
 | 
			
		||||
hw-obj-$(CONFIG_PFLASH_CFI02) += pflash_cfi02.o
 | 
			
		||||
 | 
			
		||||
hw-obj-$(CONFIG_M48T59) += m48t59.o
 | 
			
		||||
hw-obj-$(CONFIG_ESCC) += escc.o
 | 
			
		||||
hw-obj-$(CONFIG_EMPTY_SLOT) += empty_slot.o
 | 
			
		||||
 | 
			
		||||
hw-obj-$(CONFIG_SERIAL) += serial.o
 | 
			
		||||
hw-obj-$(CONFIG_PARALLEL) += parallel.o
 | 
			
		||||
hw-obj-$(CONFIG_I8254) += i8254.o
 | 
			
		||||
hw-obj-$(CONFIG_PCSPK) += pcspk.o
 | 
			
		||||
hw-obj-$(CONFIG_PCKBD) += pckbd.o
 | 
			
		||||
hw-obj-$(CONFIG_USB_UHCI) += usb-uhci.o
 | 
			
		||||
hw-obj-$(CONFIG_FDC) += fdc.o
 | 
			
		||||
hw-obj-$(CONFIG_ACPI) += acpi.o acpi_piix4.o
 | 
			
		||||
hw-obj-$(CONFIG_APM) += pm_smbus.o apm.o
 | 
			
		||||
hw-obj-$(CONFIG_DMA) += dma.o
 | 
			
		||||
 | 
			
		||||
# PPC devices
 | 
			
		||||
hw-obj-$(CONFIG_OPENPIC) += openpic.o
 | 
			
		||||
hw-obj-$(CONFIG_PREP_PCI) += prep_pci.o
 | 
			
		||||
# Mac shared devices
 | 
			
		||||
hw-obj-$(CONFIG_MACIO) += macio.o
 | 
			
		||||
hw-obj-$(CONFIG_CUDA) += cuda.o
 | 
			
		||||
hw-obj-$(CONFIG_ADB) += adb.o
 | 
			
		||||
hw-obj-$(CONFIG_MAC_NVRAM) += mac_nvram.o
 | 
			
		||||
hw-obj-$(CONFIG_MAC_DBDMA) += mac_dbdma.o
 | 
			
		||||
# OldWorld PowerMac
 | 
			
		||||
hw-obj-$(CONFIG_HEATHROW_PIC) += heathrow_pic.o
 | 
			
		||||
hw-obj-$(CONFIG_GRACKLE_PCI) += grackle_pci.o
 | 
			
		||||
# NewWorld PowerMac
 | 
			
		||||
hw-obj-$(CONFIG_UNIN_PCI) += unin_pci.o
 | 
			
		||||
hw-obj-$(CONFIG_DEC_PCI) += dec_pci.o
 | 
			
		||||
# PowerPC E500 boards
 | 
			
		||||
hw-obj-$(CONFIG_PPCE500_PCI) += ppce500_pci.o
 | 
			
		||||
 | 
			
		||||
# MIPS devices
 | 
			
		||||
hw-obj-$(CONFIG_PIIX4) += piix4.o
 | 
			
		||||
 | 
			
		||||
# PCI watchdog devices
 | 
			
		||||
hw-obj-y += wdt_i6300esb.o
 | 
			
		||||
 | 
			
		||||
hw-obj-y += msix.o
 | 
			
		||||
 | 
			
		||||
# PCI network cards
 | 
			
		||||
hw-obj-y += ne2000.o
 | 
			
		||||
hw-obj-y += eepro100.o
 | 
			
		||||
hw-obj-y += pcnet.o
 | 
			
		||||
 | 
			
		||||
hw-obj-$(CONFIG_SMC91C111) += smc91c111.o
 | 
			
		||||
hw-obj-$(CONFIG_LAN9118) += lan9118.o
 | 
			
		||||
hw-obj-$(CONFIG_NE2000_ISA) += ne2000-isa.o
 | 
			
		||||
 | 
			
		||||
# IDE
 | 
			
		||||
hw-obj-$(CONFIG_IDE_CORE) += ide/core.o
 | 
			
		||||
hw-obj-$(CONFIG_IDE_QDEV) += ide/qdev.o
 | 
			
		||||
hw-obj-$(CONFIG_IDE_PCI) += ide/pci.o
 | 
			
		||||
hw-obj-$(CONFIG_IDE_ISA) += ide/isa.o
 | 
			
		||||
hw-obj-$(CONFIG_IDE_PIIX) += ide/piix.o
 | 
			
		||||
hw-obj-$(CONFIG_IDE_CMD646) += ide/cmd646.o
 | 
			
		||||
hw-obj-$(CONFIG_IDE_MACIO) += ide/macio.o
 | 
			
		||||
hw-obj-$(CONFIG_IDE_VIA) += ide/via.o
 | 
			
		||||
 | 
			
		||||
# SCSI layer
 | 
			
		||||
hw-obj-y += lsi53c895a.o
 | 
			
		||||
hw-obj-$(CONFIG_ESP) += esp.o
 | 
			
		||||
 | 
			
		||||
hw-obj-y += dma-helpers.o sysbus.o isa-bus.o
 | 
			
		||||
hw-obj-y += qdev-addr.o
 | 
			
		||||
 | 
			
		||||
# VGA
 | 
			
		||||
hw-obj-$(CONFIG_VGA_PCI) += vga-pci.o
 | 
			
		||||
hw-obj-$(CONFIG_VGA_ISA) += vga-isa.o
 | 
			
		||||
hw-obj-$(CONFIG_VGA_ISA_MM) += vga-isa-mm.o
 | 
			
		||||
hw-obj-$(CONFIG_VMWARE_VGA) += vmware_vga.o
 | 
			
		||||
 | 
			
		||||
hw-obj-$(CONFIG_RC4030) += rc4030.o
 | 
			
		||||
hw-obj-$(CONFIG_DP8393X) += dp8393x.o
 | 
			
		||||
hw-obj-$(CONFIG_DS1225Y) += ds1225y.o
 | 
			
		||||
hw-obj-$(CONFIG_MIPSNET) += mipsnet.o
 | 
			
		||||
 | 
			
		||||
# Sound
 | 
			
		||||
sound-obj-y =
 | 
			
		||||
sound-obj-$(CONFIG_SB16) += sb16.o
 | 
			
		||||
sound-obj-$(CONFIG_ES1370) += es1370.o
 | 
			
		||||
sound-obj-$(CONFIG_AC97) += ac97.o
 | 
			
		||||
sound-obj-$(CONFIG_ADLIB) += fmopl.o adlib.o
 | 
			
		||||
sound-obj-$(CONFIG_GUS) += gus.o gusemu_hal.o gusemu_mixer.o
 | 
			
		||||
sound-obj-$(CONFIG_CS4231A) += cs4231a.o
 | 
			
		||||
 | 
			
		||||
adlib.o fmopl.o: QEMU_CFLAGS += -DBUILD_Y8950=0
 | 
			
		||||
hw-obj-$(CONFIG_SOUND) += $(sound-obj-y)
 | 
			
		||||
 | 
			
		||||
hw-obj-$(CONFIG_VIRTFS) += virtio-9p-debug.o virtio-9p-local.o
 | 
			
		||||
 | 
			
		||||
######################################################################
 | 
			
		||||
# libdis
 | 
			
		||||
# NOTE: the disassembler code is only needed for debugging
 | 
			
		||||
 | 
			
		||||
libdis-y =
 | 
			
		||||
libdis-$(CONFIG_ALPHA_DIS) += alpha-dis.o
 | 
			
		||||
libdis-$(CONFIG_ARM_DIS) += arm-dis.o
 | 
			
		||||
libdis-$(CONFIG_CRIS_DIS) += cris-dis.o
 | 
			
		||||
libdis-$(CONFIG_HPPA_DIS) += hppa-dis.o
 | 
			
		||||
libdis-$(CONFIG_I386_DIS) += i386-dis.o
 | 
			
		||||
libdis-$(CONFIG_IA64_DIS) += ia64-dis.o
 | 
			
		||||
libdis-$(CONFIG_M68K_DIS) += m68k-dis.o
 | 
			
		||||
libdis-$(CONFIG_MICROBLAZE_DIS) += microblaze-dis.o
 | 
			
		||||
libdis-$(CONFIG_MIPS_DIS) += mips-dis.o
 | 
			
		||||
libdis-$(CONFIG_PPC_DIS) += ppc-dis.o
 | 
			
		||||
libdis-$(CONFIG_S390_DIS) += s390-dis.o
 | 
			
		||||
libdis-$(CONFIG_SH4_DIS) += sh4-dis.o
 | 
			
		||||
libdis-$(CONFIG_SPARC_DIS) += sparc-dis.o
 | 
			
		||||
 | 
			
		||||
vl.o: QEMU_CFLAGS+=$(GPROF_CFLAGS)
 | 
			
		||||
 | 
			
		||||
vl.o: QEMU_CFLAGS+=$(SDL_CFLAGS)
 | 
			
		||||
 | 
			
		||||
vl.o: qemu-options.def
 | 
			
		||||
os-posix.o: qemu-options.def
 | 
			
		||||
os-win32.o: qemu-options.def
 | 
			
		||||
 | 
			
		||||
qemu-options.def: $(SRC_PATH)/qemu-options.hx
 | 
			
		||||
	$(call quiet-command,sh $(SRC_PATH)/hxtool -h < $< > $@,"  GEN   $(TARGET_DIR)$@")
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										746
									
								
								Makefile.target
									
									
									
									
									
								
							
							
						
						
									
										746
									
								
								Makefile.target
									
									
									
									
									
								
							@@ -1,336 +1,506 @@
 | 
			
		||||
# -*- Mode: makefile -*-
 | 
			
		||||
include config.mak
 | 
			
		||||
 | 
			
		||||
GENERATED_HEADERS = config-target.h
 | 
			
		||||
CONFIG_NO_KVM = $(if $(subst n,,$(CONFIG_KVM)),n,y)
 | 
			
		||||
 | 
			
		||||
include ../config-host.mak
 | 
			
		||||
include config-devices.mak
 | 
			
		||||
include config-target.mak
 | 
			
		||||
include $(SRC_PATH)/rules.mak
 | 
			
		||||
ifneq ($(HWDIR),)
 | 
			
		||||
include $(HWDIR)/config.mak
 | 
			
		||||
TARGET_BASE_ARCH:=$(TARGET_ARCH)
 | 
			
		||||
ifeq ($(TARGET_ARCH), x86_64)
 | 
			
		||||
TARGET_BASE_ARCH:=i386
 | 
			
		||||
endif
 | 
			
		||||
ifeq ($(TARGET_ARCH), ppc64)
 | 
			
		||||
TARGET_BASE_ARCH:=ppc
 | 
			
		||||
endif
 | 
			
		||||
ifeq ($(TARGET_ARCH), sparc64)
 | 
			
		||||
TARGET_BASE_ARCH:=sparc
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
TARGET_PATH=$(SRC_PATH)/target-$(TARGET_BASE_ARCH)
 | 
			
		||||
$(call set-vpath, $(SRC_PATH):$(TARGET_PATH):$(SRC_PATH)/hw)
 | 
			
		||||
QEMU_CFLAGS+= -I.. -I$(TARGET_PATH) -DNEED_CPU_H
 | 
			
		||||
 | 
			
		||||
include $(SRC_PATH)/Makefile.objs
 | 
			
		||||
VPATH=$(SRC_PATH):$(TARGET_PATH):$(SRC_PATH)/hw:$(SRC_PATH)/audio
 | 
			
		||||
DEFINES=-I. -I.. -I$(TARGET_PATH) -I$(SRC_PATH)
 | 
			
		||||
ifdef CONFIG_USER_ONLY
 | 
			
		||||
VPATH+=:$(SRC_PATH)/linux-user
 | 
			
		||||
DEFINES+=-I$(SRC_PATH)/linux-user -I$(SRC_PATH)/linux-user/$(TARGET_ARCH)
 | 
			
		||||
endif
 | 
			
		||||
CFLAGS=-Wall -O2 -g -fno-strict-aliasing
 | 
			
		||||
#CFLAGS+=-Werror
 | 
			
		||||
LDFLAGS=-g
 | 
			
		||||
LIBS=
 | 
			
		||||
HELPER_CFLAGS=$(CFLAGS)
 | 
			
		||||
DYNGEN=../dyngen$(EXESUF)
 | 
			
		||||
# user emulator name
 | 
			
		||||
TARGET_ARCH2=$(TARGET_ARCH)
 | 
			
		||||
ifeq ($(TARGET_ARCH),arm)
 | 
			
		||||
  ifeq ($(TARGET_WORDS_BIGENDIAN),yes)
 | 
			
		||||
    TARGET_ARCH2=armeb
 | 
			
		||||
  endif
 | 
			
		||||
endif
 | 
			
		||||
ifeq ($(TARGET_ARCH),mips)
 | 
			
		||||
  ifneq ($(TARGET_WORDS_BIGENDIAN),yes)
 | 
			
		||||
    TARGET_ARCH2=mipsel
 | 
			
		||||
  endif
 | 
			
		||||
endif
 | 
			
		||||
QEMU_USER=qemu-$(TARGET_ARCH2)
 | 
			
		||||
# system emulator name
 | 
			
		||||
ifdef CONFIG_SOFTMMU
 | 
			
		||||
ifeq ($(TARGET_ARCH), i386)
 | 
			
		||||
QEMU_SYSTEM=qemu$(EXESUF)
 | 
			
		||||
else
 | 
			
		||||
QEMU_SYSTEM=qemu-system-$(TARGET_ARCH2)$(EXESUF)
 | 
			
		||||
endif
 | 
			
		||||
else
 | 
			
		||||
QEMU_SYSTEM=qemu-fast
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
ifdef CONFIG_USER_ONLY
 | 
			
		||||
# user emulator name
 | 
			
		||||
QEMU_PROG=qemu-$(TARGET_ARCH2)
 | 
			
		||||
PROGS=$(QEMU_USER)
 | 
			
		||||
else
 | 
			
		||||
# system emulator name
 | 
			
		||||
ifeq ($(TARGET_ARCH), i386)
 | 
			
		||||
QEMU_PROG=qemu$(EXESUF)
 | 
			
		||||
PROGS+=$(QEMU_SYSTEM)
 | 
			
		||||
ifndef CONFIG_SOFTMMU
 | 
			
		||||
CONFIG_STATIC=y
 | 
			
		||||
endif
 | 
			
		||||
endif # !CONFIG_USER_ONLY
 | 
			
		||||
 | 
			
		||||
ifdef CONFIG_STATIC
 | 
			
		||||
LDFLAGS+=-static
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
ifeq ($(ARCH),i386)
 | 
			
		||||
CFLAGS+=-fomit-frame-pointer
 | 
			
		||||
OP_CFLAGS=$(CFLAGS) -mpreferred-stack-boundary=2
 | 
			
		||||
ifeq ($(HAVE_GCC3_OPTIONS),yes)
 | 
			
		||||
OP_CFLAGS+= -falign-functions=0 -fno-gcse
 | 
			
		||||
else
 | 
			
		||||
QEMU_PROG=qemu-system-$(TARGET_ARCH2)$(EXESUF)
 | 
			
		||||
OP_CFLAGS+= -malign-functions=0
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
ifdef TARGET_GPROF
 | 
			
		||||
USE_I386_LD=y
 | 
			
		||||
endif
 | 
			
		||||
ifdef CONFIG_STATIC
 | 
			
		||||
USE_I386_LD=y
 | 
			
		||||
endif
 | 
			
		||||
ifdef USE_I386_LD
 | 
			
		||||
LDFLAGS+=-Wl,-T,$(SRC_PATH)/i386.ld
 | 
			
		||||
else
 | 
			
		||||
# 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!
 | 
			
		||||
LDFLAGS+=-Wl,-shared
 | 
			
		||||
endif
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
PROGS=$(QEMU_PROG)
 | 
			
		||||
ifeq ($(ARCH),x86_64)
 | 
			
		||||
OP_CFLAGS=$(CFLAGS) -falign-functions=0
 | 
			
		||||
LDFLAGS+=-Wl,-T,$(SRC_PATH)/x86_64.ld
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
ifeq ($(ARCH),ppc)
 | 
			
		||||
CFLAGS+= -D__powerpc__
 | 
			
		||||
OP_CFLAGS=$(CFLAGS)
 | 
			
		||||
LDFLAGS+=-Wl,-T,$(SRC_PATH)/ppc.ld
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
ifeq ($(ARCH),s390)
 | 
			
		||||
OP_CFLAGS=$(CFLAGS)
 | 
			
		||||
LDFLAGS+=-Wl,-T,$(SRC_PATH)/s390.ld
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
ifeq ($(ARCH),sparc)
 | 
			
		||||
CFLAGS+=-m32 -ffixed-g1 -ffixed-g2 -ffixed-g3 -ffixed-g6
 | 
			
		||||
LDFLAGS+=-m32
 | 
			
		||||
OP_CFLAGS=$(CFLAGS) -fno-delayed-branch -ffixed-i0
 | 
			
		||||
HELPER_CFLAGS=$(CFLAGS) -ffixed-i0 -mflat
 | 
			
		||||
# -static is used to avoid g1/g3 usage by the dynamic linker
 | 
			
		||||
LDFLAGS+=-Wl,-T,$(SRC_PATH)/sparc.ld -static
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
ifeq ($(ARCH),sparc64)
 | 
			
		||||
CFLAGS+=-m64 -ffixed-g1 -ffixed-g2 -ffixed-g3 -ffixed-g6
 | 
			
		||||
LDFLAGS+=-m64
 | 
			
		||||
OP_CFLAGS=$(CFLAGS) -fno-delayed-branch -ffixed-i0
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
ifeq ($(ARCH),alpha)
 | 
			
		||||
# -msmall-data is not used because we want two-instruction relocations
 | 
			
		||||
# for the constant constructions
 | 
			
		||||
OP_CFLAGS=-Wall -O2 -g
 | 
			
		||||
# Ensure there's only a single GP
 | 
			
		||||
CFLAGS += -msmall-data
 | 
			
		||||
LDFLAGS+=-Wl,-T,$(SRC_PATH)/alpha.ld
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
ifeq ($(ARCH),ia64)
 | 
			
		||||
CFLAGS += -mno-sdata
 | 
			
		||||
OP_CFLAGS=$(CFLAGS)
 | 
			
		||||
LDFLAGS+=-Wl,-G0 -Wl,-T,$(SRC_PATH)/ia64.ld
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
ifeq ($(ARCH),arm)
 | 
			
		||||
OP_CFLAGS=$(CFLAGS) -mno-sched-prolog -fno-omit-frame-pointer
 | 
			
		||||
LDFLAGS+=-Wl,-T,$(SRC_PATH)/arm.ld
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
ifeq ($(ARCH),m68k)
 | 
			
		||||
OP_CFLAGS=$(CFLAGS) -fomit-frame-pointer
 | 
			
		||||
LDFLAGS+=-Wl,-T,m68k.ld
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
ifeq ($(HAVE_GCC3_OPTIONS),yes)
 | 
			
		||||
# very important to generate a return at the end of every operation
 | 
			
		||||
OP_CFLAGS+=-fno-reorder-blocks -fno-optimize-sibling-calls
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
ifeq ($(CONFIG_DARWIN),yes)
 | 
			
		||||
OP_CFLAGS+= -mdynamic-no-pic
 | 
			
		||||
LIBS+=-lmx
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
#########################################################
 | 
			
		||||
 | 
			
		||||
DEFINES+=-D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE
 | 
			
		||||
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
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
kvm.o kvm-all.o vhost.o vhost_net.o: QEMU_CFLAGS+=$(KVM_CFLAGS)
 | 
			
		||||
# profiling code
 | 
			
		||||
ifdef TARGET_GPROF
 | 
			
		||||
LDFLAGS+=-p
 | 
			
		||||
main.o: CFLAGS+=-p
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
config-target.h: config-target.h-timestamp
 | 
			
		||||
config-target.h-timestamp: config-target.mak
 | 
			
		||||
OBJS= elfload.o main.o syscall.o mmap.o signal.o path.o osdep.o thunk.o 
 | 
			
		||||
ifeq ($(TARGET_ARCH), i386)
 | 
			
		||||
OBJS+= vm86.o
 | 
			
		||||
endif
 | 
			
		||||
ifeq ($(TARGET_ARCH), arm)
 | 
			
		||||
OBJS+=nwfpe/fpa11.o nwfpe/fpa11_cpdo.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
 | 
			
		||||
SRCS:= $(OBJS:.o=.c)
 | 
			
		||||
OBJS+= libqemu.a
 | 
			
		||||
 | 
			
		||||
# cpu emulator library
 | 
			
		||||
LIBOBJS=exec.o kqemu.o translate-op.o translate-all.o cpu-exec.o\
 | 
			
		||||
        translate.o op.o 
 | 
			
		||||
ifdef CONFIG_SOFTFLOAT
 | 
			
		||||
LIBOBJS+=fpu/softfloat.o
 | 
			
		||||
else
 | 
			
		||||
LIBOBJS+=fpu/softfloat-native.o
 | 
			
		||||
endif
 | 
			
		||||
DEFINES+=-I$(SRC_PATH)/fpu
 | 
			
		||||
 | 
			
		||||
ifeq ($(TARGET_ARCH), i386)
 | 
			
		||||
LIBOBJS+=helper.o helper2.o
 | 
			
		||||
ifeq ($(ARCH), i386)
 | 
			
		||||
LIBOBJS+=translate-copy.o
 | 
			
		||||
endif
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
ifeq ($(TARGET_ARCH), x86_64)
 | 
			
		||||
LIBOBJS+=helper.o helper2.o
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
ifeq ($(TARGET_BASE_ARCH), ppc)
 | 
			
		||||
LIBOBJS+= op_helper.o helper.o
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
ifeq ($(TARGET_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
 | 
			
		||||
 | 
			
		||||
# 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_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
 | 
			
		||||
 | 
			
		||||
ifdef CONFIG_GDBSTUB
 | 
			
		||||
OBJS+=gdbstub.o
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
all: $(PROGS)
 | 
			
		||||
 | 
			
		||||
# Dummy command so that make thinks it has done something
 | 
			
		||||
	@true
 | 
			
		||||
 | 
			
		||||
#########################################################
 | 
			
		||||
# cpu emulator library
 | 
			
		||||
libobj-y = exec.o translate-all.o cpu-exec.o translate.o
 | 
			
		||||
libobj-y += tcg/tcg.o
 | 
			
		||||
libobj-$(CONFIG_SOFTFLOAT) += fpu/softfloat.o
 | 
			
		||||
libobj-$(CONFIG_NOSOFTFLOAT) += fpu/softfloat-native.o
 | 
			
		||||
libobj-y += op_helper.o helper.o
 | 
			
		||||
ifeq ($(TARGET_BASE_ARCH), i386)
 | 
			
		||||
libobj-y += cpuid.o
 | 
			
		||||
$(QEMU_USER): $(OBJS)
 | 
			
		||||
	$(CC) $(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
 | 
			
		||||
libobj-$(CONFIG_NEED_MMU) += mmu.o
 | 
			
		||||
libobj-$(TARGET_ARM) += neon_helper.o iwmmxt_helper.o
 | 
			
		||||
 | 
			
		||||
libobj-y += disas.o
 | 
			
		||||
# must use static linking to avoid leaving stuff in virtual address space
 | 
			
		||||
VL_OBJS=vl.o osdep.o block.o readline.o monitor.o pci.o console.o loader.o
 | 
			
		||||
VL_OBJS+=block-cow.o block-qcow.o aes.o block-vmdk.o block-cloop.o block-dmg.o block-bochs.o block-vpc.o block-vvfat.o
 | 
			
		||||
ifdef CONFIG_WIN32
 | 
			
		||||
VL_OBJS+=tap-win32.o
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
$(libobj-y): $(GENERATED_HEADERS)
 | 
			
		||||
SOUND_HW = sb16.o es1370.o
 | 
			
		||||
AUDIODRV = audio.o noaudio.o wavaudio.o
 | 
			
		||||
ifdef CONFIG_SDL
 | 
			
		||||
AUDIODRV += sdlaudio.o
 | 
			
		||||
endif
 | 
			
		||||
ifdef CONFIG_OSS
 | 
			
		||||
AUDIODRV += ossaudio.o
 | 
			
		||||
endif
 | 
			
		||||
ifdef CONFIG_COREAUDIO
 | 
			
		||||
AUDIODRV += coreaudio.o
 | 
			
		||||
endif
 | 
			
		||||
ifdef CONFIG_ALSA
 | 
			
		||||
AUDIODRV += alsaaudio.o
 | 
			
		||||
LIBS += -lasound
 | 
			
		||||
endif
 | 
			
		||||
ifdef CONFIG_DSOUND
 | 
			
		||||
AUDIODRV += dsoundaudio.o
 | 
			
		||||
LIBS += -lole32 -ldxguid
 | 
			
		||||
endif
 | 
			
		||||
ifdef CONFIG_FMOD
 | 
			
		||||
AUDIODRV += fmodaudio.o
 | 
			
		||||
audio.o fmodaudio.o: DEFINES := -I$(CONFIG_FMOD_INC) $(DEFINES)
 | 
			
		||||
LIBS += $(CONFIG_FMOD_LIB)
 | 
			
		||||
endif
 | 
			
		||||
ifdef CONFIG_ADLIB
 | 
			
		||||
SOUND_HW += fmopl.o adlib.o
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
# USB layer
 | 
			
		||||
VL_OBJS+= usb.o usb-hub.o usb-uhci.o usb-linux.o usb-hid.o
 | 
			
		||||
 | 
			
		||||
# PCI network cards
 | 
			
		||||
VL_OBJS+= ne2000.o rtl8139.o
 | 
			
		||||
 | 
			
		||||
ifeq ($(TARGET_BASE_ARCH), i386)
 | 
			
		||||
# Hardware support
 | 
			
		||||
VL_OBJS+= ide.o pckbd.o ps2.o vga.o $(SOUND_HW) dma.o $(AUDIODRV)
 | 
			
		||||
VL_OBJS+= fdc.o mc146818rtc.o serial.o i8259.o i8254.o pcspk.o pc.o
 | 
			
		||||
VL_OBJS+= cirrus_vga.o mixeng.o apic.o parallel.o
 | 
			
		||||
DEFINES += -DHAS_AUDIO
 | 
			
		||||
endif
 | 
			
		||||
ifeq ($(TARGET_BASE_ARCH), ppc)
 | 
			
		||||
VL_OBJS+= ppc.o ide.o pckbd.o ps2.o vga.o $(SOUND_HW) dma.o $(AUDIODRV)
 | 
			
		||||
VL_OBJS+= mc146818rtc.o serial.o i8259.o i8254.o fdc.o m48t59.o
 | 
			
		||||
VL_OBJS+= ppc_prep.o ppc_chrp.o cuda.o adb.o openpic.o heathrow_pic.o mixeng.o
 | 
			
		||||
DEFINES += -DHAS_AUDIO
 | 
			
		||||
endif
 | 
			
		||||
ifeq ($(TARGET_ARCH), mips)
 | 
			
		||||
VL_OBJS+= mips_r4k.o dma.o vga.o serial.o i8254.o i8259.o
 | 
			
		||||
#VL_OBJS+= #ide.o pckbd.o fdc.o m48t59.o
 | 
			
		||||
endif
 | 
			
		||||
ifeq ($(TARGET_BASE_ARCH), sparc)
 | 
			
		||||
ifeq ($(TARGET_ARCH), sparc64)
 | 
			
		||||
VL_OBJS+= sun4u.o ide.o pckbd.o ps2.o vga.o
 | 
			
		||||
VL_OBJS+= fdc.o mc146818rtc.o serial.o m48t59.o
 | 
			
		||||
VL_OBJS+= cirrus_vga.o parallel.o
 | 
			
		||||
else
 | 
			
		||||
VL_OBJS+= sun4m.o tcx.o lance.o iommu.o m48t59.o slavio_intctl.o
 | 
			
		||||
VL_OBJS+= slavio_timer.o slavio_serial.o slavio_misc.o fdc.o esp.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 pl050.o pl080.o pl110.o pl190.o
 | 
			
		||||
endif
 | 
			
		||||
ifeq ($(TARGET_BASE_ARCH), sh4)
 | 
			
		||||
VL_OBJS+= shix.o sh7750.o sh7750_regnames.o tc58128.o
 | 
			
		||||
endif
 | 
			
		||||
ifdef CONFIG_GDBSTUB
 | 
			
		||||
VL_OBJS+=gdbstub.o 
 | 
			
		||||
endif
 | 
			
		||||
ifdef CONFIG_SDL
 | 
			
		||||
VL_OBJS+=sdl.o
 | 
			
		||||
endif
 | 
			
		||||
VL_OBJS+=vnc.o
 | 
			
		||||
ifdef CONFIG_COCOA
 | 
			
		||||
VL_OBJS+=cocoa.o
 | 
			
		||||
COCOA_LIBS=-F/System/Library/Frameworks -framework Cocoa -framework IOKit
 | 
			
		||||
ifdef CONFIG_COREAUDIO
 | 
			
		||||
COCOA_LIBS+=-framework CoreAudio
 | 
			
		||||
endif
 | 
			
		||||
endif
 | 
			
		||||
ifdef CONFIG_SLIRP
 | 
			
		||||
DEFINES+=-I$(SRC_PATH)/slirp
 | 
			
		||||
SLIRP_OBJS=cksum.o if.o ip_icmp.o ip_input.o ip_output.o \
 | 
			
		||||
slirp.o mbuf.o misc.o sbuf.o socket.o tcp_input.o tcp_output.o \
 | 
			
		||||
tcp_subr.o tcp_timer.o udp.o bootp.o debug.o tftp.o
 | 
			
		||||
VL_OBJS+=$(addprefix slirp/, $(SLIRP_OBJS))
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
VL_LDFLAGS=
 | 
			
		||||
# 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: CFLAGS+=-p
 | 
			
		||||
VL_LDFLAGS+=-p
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
ifeq ($(ARCH),ia64)
 | 
			
		||||
VL_LDFLAGS+=-Wl,-G0 -Wl,-T,$(SRC_PATH)/ia64.ld
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
ifdef CONFIG_WIN32
 | 
			
		||||
SDL_LIBS := $(filter-out -mwindows, $(SDL_LIBS)) -mconsole
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
$(QEMU_SYSTEM): $(VL_OBJS) libqemu.a
 | 
			
		||||
	$(CC) $(VL_LDFLAGS) -o $@ $^ $(LIBS) $(SDL_LIBS) $(COCOA_LIBS) $(VL_LIBS)
 | 
			
		||||
 | 
			
		||||
cocoa.o: cocoa.m
 | 
			
		||||
	$(CC) $(CFLAGS) $(DEFINES) -c -o $@ $<
 | 
			
		||||
 | 
			
		||||
sdl.o: sdl.c keymaps.c sdl_keysym.h
 | 
			
		||||
	$(CC) $(CFLAGS) $(DEFINES) $(SDL_CFLAGS) -c -o $@ $<
 | 
			
		||||
 | 
			
		||||
vnc.o: vnc.c keymaps.c sdl_keysym.h vnchextile.h
 | 
			
		||||
	$(CC) $(CFLAGS) $(DEFINES) -c -o $@ $<
 | 
			
		||||
 | 
			
		||||
sdlaudio.o: sdlaudio.c
 | 
			
		||||
	$(CC) $(CFLAGS) $(DEFINES) $(SDL_CFLAGS) -c -o $@ $<
 | 
			
		||||
 | 
			
		||||
depend: $(SRCS)
 | 
			
		||||
	$(CC) -MM $(CFLAGS) $(DEFINES) $^ 1>.depend
 | 
			
		||||
 | 
			
		||||
vldepend: $(VL_OBJS:.o=.c)
 | 
			
		||||
	$(CC) -MM $(CFLAGS) $(DEFINES) $^ 1>.depend
 | 
			
		||||
 | 
			
		||||
# libqemu 
 | 
			
		||||
 | 
			
		||||
translate.o: translate.c cpu.h
 | 
			
		||||
libqemu.a: $(LIBOBJS)
 | 
			
		||||
	rm -f $@
 | 
			
		||||
	$(AR) rcs $@ $(LIBOBJS)
 | 
			
		||||
 | 
			
		||||
translate-all.o: translate-all.c cpu.h
 | 
			
		||||
translate.o: translate.c gen-op.h opc.h cpu.h
 | 
			
		||||
 | 
			
		||||
tcg/tcg.o: cpu.h
 | 
			
		||||
translate-all.o: translate-all.c opc.h cpu.h
 | 
			
		||||
 | 
			
		||||
# HELPER_CFLAGS is used for all the code compiled with static register
 | 
			
		||||
# variables
 | 
			
		||||
op_helper.o cpu-exec.o: QEMU_CFLAGS += $(HELPER_CFLAGS)
 | 
			
		||||
translate-op.o: translate-all.c op.h opc.h cpu.h
 | 
			
		||||
 | 
			
		||||
# Note: this is a workaround. The real fix is to avoid compiling
 | 
			
		||||
# cpu_signal_handler() in cpu-exec.c.
 | 
			
		||||
signal.o: QEMU_CFLAGS += $(HELPER_CFLAGS)
 | 
			
		||||
op.h: op.o $(DYNGEN)
 | 
			
		||||
	$(DYNGEN) -o $@ $<
 | 
			
		||||
 | 
			
		||||
#########################################################
 | 
			
		||||
# Linux user emulator target
 | 
			
		||||
opc.h: op.o $(DYNGEN)
 | 
			
		||||
	$(DYNGEN) -c -o $@ $<
 | 
			
		||||
 | 
			
		||||
ifdef CONFIG_LINUX_USER
 | 
			
		||||
gen-op.h: op.o $(DYNGEN)
 | 
			
		||||
	$(DYNGEN) -g -o $@ $<
 | 
			
		||||
 | 
			
		||||
$(call set-vpath, $(SRC_PATH)/linux-user:$(SRC_PATH)/linux-user/$(TARGET_ABI_DIR))
 | 
			
		||||
op.o: op.c
 | 
			
		||||
	$(CC) $(OP_CFLAGS) $(DEFINES) -c -o $@ $<
 | 
			
		||||
 | 
			
		||||
QEMU_CFLAGS+=-I$(SRC_PATH)/linux-user -I$(SRC_PATH)/linux-user/$(TARGET_ABI_DIR)
 | 
			
		||||
obj-y = main.o syscall.o strace.o mmap.o signal.o thunk.o \
 | 
			
		||||
      elfload.o linuxload.o uaccess.o gdbstub.o cpu-uname.o \
 | 
			
		||||
      qemu-malloc.o
 | 
			
		||||
helper.o: helper.c
 | 
			
		||||
	$(CC) $(HELPER_CFLAGS) $(DEFINES) -c -o $@ $<
 | 
			
		||||
 | 
			
		||||
obj-$(TARGET_HAS_BFLT) += flatload.o
 | 
			
		||||
 | 
			
		||||
obj-$(TARGET_I386) += vm86.o
 | 
			
		||||
 | 
			
		||||
obj-i386-y += ioport-user.o
 | 
			
		||||
 | 
			
		||||
nwfpe-obj-y = fpa11.o fpa11_cpdo.o fpa11_cpdt.o fpa11_cprt.o fpopcode.o
 | 
			
		||||
nwfpe-obj-y += single_cpdo.o double_cpdo.o extended_cpdo.o
 | 
			
		||||
obj-arm-y +=  $(addprefix nwfpe/, $(nwfpe-obj-y))
 | 
			
		||||
obj-arm-y += arm-semi.o
 | 
			
		||||
 | 
			
		||||
obj-m68k-y += m68k-sim.o m68k-semi.o
 | 
			
		||||
 | 
			
		||||
$(obj-y) $(obj-$(TARGET_BASE_ARCH)-y): $(GENERATED_HEADERS)
 | 
			
		||||
 | 
			
		||||
obj-y += $(addprefix ../libuser/, $(user-obj-y))
 | 
			
		||||
obj-y += $(addprefix ../libdis-user/, $(libdis-y))
 | 
			
		||||
obj-y += $(libobj-y)
 | 
			
		||||
 | 
			
		||||
endif #CONFIG_LINUX_USER
 | 
			
		||||
 | 
			
		||||
#########################################################
 | 
			
		||||
# Darwin user emulator target
 | 
			
		||||
 | 
			
		||||
ifdef CONFIG_DARWIN_USER
 | 
			
		||||
 | 
			
		||||
$(call set-vpath, $(SRC_PATH)/darwin-user)
 | 
			
		||||
 | 
			
		||||
QEMU_CFLAGS+=-I$(SRC_PATH)/darwin-user -I$(SRC_PATH)/darwin-user/$(TARGET_ARCH)
 | 
			
		||||
 | 
			
		||||
# Leave some space for the regular program loading zone
 | 
			
		||||
LDFLAGS+=-Wl,-segaddr,__STD_PROG_ZONE,0x1000 -image_base 0x0e000000
 | 
			
		||||
 | 
			
		||||
LIBS+=-lmx
 | 
			
		||||
 | 
			
		||||
obj-y = main.o commpage.o machload.o mmap.o signal.o syscall.o thunk.o \
 | 
			
		||||
        gdbstub.o
 | 
			
		||||
 | 
			
		||||
obj-i386-y += ioport-user.o
 | 
			
		||||
 | 
			
		||||
$(obj-y) $(obj-$(TARGET_BASE_ARCH)-y): $(GENERATED_HEADERS)
 | 
			
		||||
 | 
			
		||||
obj-y += $(addprefix ../libuser/, $(user-obj-y))
 | 
			
		||||
obj-y += $(addprefix ../libdis-user/, $(libdis-y))
 | 
			
		||||
obj-y += $(libobj-y)
 | 
			
		||||
 | 
			
		||||
endif #CONFIG_DARWIN_USER
 | 
			
		||||
 | 
			
		||||
#########################################################
 | 
			
		||||
# BSD user emulator target
 | 
			
		||||
 | 
			
		||||
ifdef CONFIG_BSD_USER
 | 
			
		||||
 | 
			
		||||
$(call set-vpath, $(SRC_PATH)/bsd-user)
 | 
			
		||||
 | 
			
		||||
QEMU_CFLAGS+=-I$(SRC_PATH)/bsd-user -I$(SRC_PATH)/bsd-user/$(TARGET_ARCH)
 | 
			
		||||
 | 
			
		||||
obj-y = main.o bsdload.o elfload.o mmap.o signal.o strace.o syscall.o \
 | 
			
		||||
        gdbstub.o uaccess.o
 | 
			
		||||
 | 
			
		||||
obj-i386-y += ioport-user.o
 | 
			
		||||
 | 
			
		||||
$(obj-y) $(obj-$(TARGET_BASE_ARCH)-y): $(GENERATED_HEADERS)
 | 
			
		||||
 | 
			
		||||
obj-y += $(addprefix ../libuser/, $(user-obj-y))
 | 
			
		||||
obj-y += $(addprefix ../libdis-user/, $(libdis-y))
 | 
			
		||||
obj-y += $(libobj-y)
 | 
			
		||||
 | 
			
		||||
endif #CONFIG_BSD_USER
 | 
			
		||||
 | 
			
		||||
#########################################################
 | 
			
		||||
# System emulator target
 | 
			
		||||
ifdef CONFIG_SOFTMMU
 | 
			
		||||
 | 
			
		||||
obj-y = arch_init.o cpus.o monitor.o machine.o gdbstub.o balloon.o
 | 
			
		||||
# virtio has to be here due to weird dependency between PCI and virtio-net.
 | 
			
		||||
# need to fix this properly
 | 
			
		||||
obj-y += virtio-blk.o virtio-balloon.o virtio-net.o virtio-serial-bus.o
 | 
			
		||||
obj-$(CONFIG_VIRTIO_PCI) += virtio-pci.o
 | 
			
		||||
obj-y += vhost_net.o
 | 
			
		||||
obj-$(CONFIG_VHOST_NET) += vhost.o
 | 
			
		||||
obj-$(CONFIG_VIRTFS) += virtio-9p.o
 | 
			
		||||
obj-y += rwhandler.o
 | 
			
		||||
obj-$(CONFIG_KVM) += kvm.o kvm-all.o
 | 
			
		||||
obj-$(CONFIG_NO_KVM) += kvm-stub.o
 | 
			
		||||
LIBS+=-lz
 | 
			
		||||
 | 
			
		||||
QEMU_CFLAGS += $(VNC_TLS_CFLAGS)
 | 
			
		||||
QEMU_CFLAGS += $(VNC_SASL_CFLAGS)
 | 
			
		||||
QEMU_CFLAGS += $(VNC_JPEG_CFLAGS)
 | 
			
		||||
QEMU_CFLAGS += $(VNC_PNG_CFLAGS)
 | 
			
		||||
 | 
			
		||||
# xen backend driver support
 | 
			
		||||
obj-$(CONFIG_XEN) += xen_machine_pv.o xen_domainbuild.o
 | 
			
		||||
 | 
			
		||||
# USB layer
 | 
			
		||||
obj-$(CONFIG_USB_OHCI) += usb-ohci.o
 | 
			
		||||
 | 
			
		||||
# PCI network cards
 | 
			
		||||
obj-y += rtl8139.o
 | 
			
		||||
obj-y += e1000.o
 | 
			
		||||
 | 
			
		||||
# Inter-VM PCI shared memory
 | 
			
		||||
obj-$(CONFIG_KVM) += ivshmem.o
 | 
			
		||||
 | 
			
		||||
# Hardware support
 | 
			
		||||
obj-i386-y += vga.o
 | 
			
		||||
obj-i386-y += mc146818rtc.o i8259.o pc.o
 | 
			
		||||
obj-i386-y += cirrus_vga.o apic.o ioapic.o piix_pci.o
 | 
			
		||||
obj-i386-y += vmmouse.o vmport.o hpet.o applesmc.o
 | 
			
		||||
obj-i386-y += device-hotplug.o pci-hotplug.o smbios.o wdt_ib700.o
 | 
			
		||||
obj-i386-y += debugcon.o multiboot.o
 | 
			
		||||
obj-i386-y += pc_piix.o
 | 
			
		||||
 | 
			
		||||
# shared objects
 | 
			
		||||
obj-ppc-y = ppc.o
 | 
			
		||||
obj-ppc-y += vga.o
 | 
			
		||||
# PREP target
 | 
			
		||||
obj-ppc-y += i8259.o mc146818rtc.o
 | 
			
		||||
obj-ppc-y += ppc_prep.o
 | 
			
		||||
# OldWorld PowerMac
 | 
			
		||||
obj-ppc-y += ppc_oldworld.o
 | 
			
		||||
# NewWorld PowerMac
 | 
			
		||||
obj-ppc-y += ppc_newworld.o
 | 
			
		||||
# PowerPC 4xx boards
 | 
			
		||||
obj-ppc-y += ppc4xx_devs.o ppc4xx_pci.o ppc405_uc.o ppc405_boards.o
 | 
			
		||||
obj-ppc-y += ppc440.o ppc440_bamboo.o
 | 
			
		||||
# PowerPC E500 boards
 | 
			
		||||
obj-ppc-y += ppce500_mpc8544ds.o
 | 
			
		||||
obj-ppc-$(CONFIG_KVM) += kvm_ppc.o
 | 
			
		||||
obj-ppc-$(CONFIG_FDT) += device_tree.o
 | 
			
		||||
 | 
			
		||||
obj-mips-y = mips_r4k.o mips_jazz.o mips_malta.o mips_mipssim.o
 | 
			
		||||
obj-mips-y += mips_addr.o mips_timer.o mips_int.o
 | 
			
		||||
obj-mips-y += vga.o i8259.o
 | 
			
		||||
obj-mips-y += g364fb.o jazz_led.o
 | 
			
		||||
obj-mips-y += gt64xxx.o mc146818rtc.o
 | 
			
		||||
obj-mips-y += cirrus_vga.o
 | 
			
		||||
obj-mips-$(CONFIG_FULONG) += bonito.o vt82c686.o mips_fulong2e.o
 | 
			
		||||
 | 
			
		||||
obj-microblaze-y = petalogix_s3adsp1800_mmu.o
 | 
			
		||||
 | 
			
		||||
obj-microblaze-y += microblaze_pic_cpu.o
 | 
			
		||||
obj-microblaze-y += xilinx_intc.o
 | 
			
		||||
obj-microblaze-y += xilinx_timer.o
 | 
			
		||||
obj-microblaze-y += xilinx_uartlite.o
 | 
			
		||||
obj-microblaze-y += xilinx_ethlite.o
 | 
			
		||||
 | 
			
		||||
obj-microblaze-$(CONFIG_FDT) += device_tree.o
 | 
			
		||||
 | 
			
		||||
# Boards
 | 
			
		||||
obj-cris-y = cris_pic_cpu.o
 | 
			
		||||
obj-cris-y += cris-boot.o
 | 
			
		||||
obj-cris-y += etraxfs.o axis_dev88.o
 | 
			
		||||
obj-cris-y += axis_dev88.o
 | 
			
		||||
 | 
			
		||||
# IO blocks
 | 
			
		||||
obj-cris-y += etraxfs_dma.o
 | 
			
		||||
obj-cris-y += etraxfs_pic.o
 | 
			
		||||
obj-cris-y += etraxfs_eth.o
 | 
			
		||||
obj-cris-y += etraxfs_timer.o
 | 
			
		||||
obj-cris-y += etraxfs_ser.o
 | 
			
		||||
 | 
			
		||||
ifeq ($(TARGET_ARCH), sparc64)
 | 
			
		||||
obj-sparc-y = sun4u.o apb_pci.o
 | 
			
		||||
obj-sparc-y += vga.o
 | 
			
		||||
obj-sparc-y += mc146818rtc.o
 | 
			
		||||
obj-sparc-y += cirrus_vga.o
 | 
			
		||||
else
 | 
			
		||||
obj-sparc-y = sun4m.o lance.o tcx.o sun4m_iommu.o slavio_intctl.o
 | 
			
		||||
obj-sparc-y += slavio_timer.o slavio_misc.o sparc32_dma.o
 | 
			
		||||
obj-sparc-y += cs4231.o eccmemctl.o sbi.o sun4c_intctl.o
 | 
			
		||||
ifeq ($(TARGET_BASE_ARCH), i386)
 | 
			
		||||
op.o: op.c opreg_template.h ops_template.h ops_template_mem.h ops_mem.h ops_sse.h
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
obj-arm-y = integratorcp.o versatilepb.o arm_pic.o arm_timer.o
 | 
			
		||||
obj-arm-y += arm_boot.o pl011.o pl031.o pl050.o pl080.o pl110.o pl181.o pl190.o
 | 
			
		||||
obj-arm-y += versatile_pci.o
 | 
			
		||||
obj-arm-y += realview_gic.o realview.o arm_sysctl.o arm11mpcore.o a9mpcore.o
 | 
			
		||||
obj-arm-y += armv7m.o armv7m_nvic.o stellaris.o pl022.o stellaris_enet.o
 | 
			
		||||
obj-arm-y += pl061.o
 | 
			
		||||
obj-arm-y += arm-semi.o
 | 
			
		||||
obj-arm-y += pxa2xx.o pxa2xx_pic.o pxa2xx_gpio.o pxa2xx_timer.o pxa2xx_dma.o
 | 
			
		||||
obj-arm-y += pxa2xx_lcd.o pxa2xx_mmci.o pxa2xx_pcmcia.o pxa2xx_keypad.o
 | 
			
		||||
obj-arm-y += gumstix.o
 | 
			
		||||
obj-arm-y += zaurus.o ide/microdrive.o spitz.o tosa.o tc6393xb.o
 | 
			
		||||
obj-arm-y += omap1.o omap_lcdc.o omap_dma.o omap_clk.o omap_mmc.o omap_i2c.o \
 | 
			
		||||
		omap_gpio.o omap_intc.o omap_uart.o
 | 
			
		||||
obj-arm-y += omap2.o omap_dss.o soc_dma.o omap_gptimer.o omap_synctimer.o \
 | 
			
		||||
		omap_gpmc.o omap_sdrc.o omap_spi.o omap_tap.o omap_l4.o
 | 
			
		||||
obj-arm-y += omap_sx1.o palm.o tsc210x.o
 | 
			
		||||
obj-arm-y += nseries.o blizzard.o onenand.o vga.o cbus.o tusb6010.o usb-musb.o
 | 
			
		||||
obj-arm-y += mst_fpga.o mainstone.o
 | 
			
		||||
obj-arm-y += musicpal.o bitbang_i2c.o marvell_88w8618_audio.o
 | 
			
		||||
obj-arm-y += framebuffer.o
 | 
			
		||||
obj-arm-y += syborg.o syborg_fb.o syborg_interrupt.o syborg_keyboard.o
 | 
			
		||||
obj-arm-y += syborg_serial.o syborg_timer.o syborg_pointer.o syborg_rtc.o
 | 
			
		||||
obj-arm-y += syborg_virtio.o
 | 
			
		||||
ifeq ($(TARGET_ARCH), arm)
 | 
			
		||||
op.o: op.c op_template.h
 | 
			
		||||
pl110.o: pl110_template.h
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
obj-sh4-y = shix.o r2d.o sh7750.o sh7750_regnames.o tc58128.o
 | 
			
		||||
obj-sh4-y += sh_timer.o sh_serial.o sh_intc.o sh_pci.o sm501.o
 | 
			
		||||
obj-sh4-y += ide/mmio.o
 | 
			
		||||
ifeq ($(TARGET_BASE_ARCH), sparc)
 | 
			
		||||
op.o: op.c op_template.h op_mem.h fop_template.h fbranch_template.h
 | 
			
		||||
magic_load.o: elf_op.h
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
obj-m68k-y = an5206.o mcf5206.o mcf_uart.o mcf_intc.o mcf5208.o mcf_fec.o
 | 
			
		||||
obj-m68k-y += m68k-semi.o dummy_m68k.o
 | 
			
		||||
ifeq ($(TARGET_BASE_ARCH), ppc)
 | 
			
		||||
op.o: op.c op_template.h op_mem.h
 | 
			
		||||
op_helper.o: op_helper_mem.h
 | 
			
		||||
translate.o: translate.c translate_init.c
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
obj-s390x-y = s390-virtio-bus.o s390-virtio.o
 | 
			
		||||
ifeq ($(TARGET_ARCH), mips)
 | 
			
		||||
op.o: op.c op_template.c op_mem.c
 | 
			
		||||
op_helper.o: op_helper_mem.c
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
obj-alpha-y = alpha_palcode.o
 | 
			
		||||
loader.o: loader.c elf_ops.h
 | 
			
		||||
 | 
			
		||||
main.o: QEMU_CFLAGS+=$(GPROF_CFLAGS)
 | 
			
		||||
ifeq ($(TARGET_ARCH), sh4)
 | 
			
		||||
op.o: op.c op_mem.c cpu.h
 | 
			
		||||
op_helper.o: op_helper.c exec.h cpu.h
 | 
			
		||||
helper.o: helper.c exec.h cpu.h
 | 
			
		||||
sh7750.o: sh7750.c sh7750_regs.h sh7750_regnames.h cpu.h
 | 
			
		||||
shix.o: shix.c sh7750_regs.h sh7750_regnames.h
 | 
			
		||||
sh7750_regnames.o: sh7750_regnames.c sh7750_regnames.h sh7750_regs.h
 | 
			
		||||
tc58128.o: tc58128.c
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
monitor.o: qemu-monitor.h
 | 
			
		||||
%.o: %.c
 | 
			
		||||
	$(CC) $(CFLAGS) $(DEFINES) -c -o $@ $<
 | 
			
		||||
 | 
			
		||||
$(obj-y) $(obj-$(TARGET_BASE_ARCH)-y): $(GENERATED_HEADERS)
 | 
			
		||||
 | 
			
		||||
obj-y += $(addprefix ../, $(common-obj-y))
 | 
			
		||||
obj-y += $(addprefix ../libdis/, $(libdis-y))
 | 
			
		||||
obj-y += $(libobj-y)
 | 
			
		||||
obj-y += $(addprefix $(HWDIR)/, $(hw-obj-y))
 | 
			
		||||
 | 
			
		||||
endif # CONFIG_SOFTMMU
 | 
			
		||||
 | 
			
		||||
obj-$(CONFIG_GDBSTUB_XML) += gdbstub-xml.o
 | 
			
		||||
 | 
			
		||||
$(QEMU_PROG): $(obj-y) $(obj-$(TARGET_BASE_ARCH)-y)
 | 
			
		||||
	$(call LINK,$(obj-y) $(obj-$(TARGET_BASE_ARCH)-y))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
gdbstub-xml.c: $(TARGET_XML_FILES) $(SRC_PATH)/feature_to_c.sh
 | 
			
		||||
	$(call quiet-command,rm -f $@ && $(SHELL) $(SRC_PATH)/feature_to_c.sh $@ $(TARGET_XML_FILES),"  GEN   $(TARGET_DIR)$@")
 | 
			
		||||
 | 
			
		||||
qemu-monitor.h: $(SRC_PATH)/qemu-monitor.hx
 | 
			
		||||
	$(call quiet-command,sh $(SRC_PATH)/hxtool -h < $< > $@,"  GEN   $(TARGET_DIR)$@")
 | 
			
		||||
%.o: %.S
 | 
			
		||||
	$(CC) $(DEFINES) -c -o $@ $<
 | 
			
		||||
 | 
			
		||||
clean:
 | 
			
		||||
	rm -f *.o *.a *~ $(PROGS) nwfpe/*.o fpu/*.o
 | 
			
		||||
	rm -f *.d */*.d tcg/*.o ide/*.o
 | 
			
		||||
	rm -f qemu-monitor.h gdbstub-xml.c
 | 
			
		||||
	rm -f *.o  *.a *~ $(PROGS) gen-op.h opc.h op.h nwfpe/*.o slirp/*.o fpu/*.o
 | 
			
		||||
 | 
			
		||||
install: all 
 | 
			
		||||
ifneq ($(PROGS),)
 | 
			
		||||
	$(INSTALL) -m 755 $(STRIP_OPT) $(PROGS) "$(DESTDIR)$(bindir)"
 | 
			
		||||
	$(INSTALL) -m 755 -s $(PROGS) "$(DESTDIR)$(bindir)"
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
# Include automatically generated dependency files
 | 
			
		||||
-include $(wildcard *.d */*.d)
 | 
			
		||||
ifneq ($(wildcard .depend),)
 | 
			
		||||
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
 | 
			
		||||
 
 | 
			
		||||
@@ -1,23 +0,0 @@
 | 
			
		||||
# Makefile for qemu target independent user files.
 | 
			
		||||
 | 
			
		||||
include ../config-host.mak
 | 
			
		||||
include $(SRC_PATH)/rules.mak
 | 
			
		||||
-include config.mak
 | 
			
		||||
 | 
			
		||||
.PHONY: all
 | 
			
		||||
 | 
			
		||||
$(call set-vpath, $(SRC_PATH))
 | 
			
		||||
 | 
			
		||||
QEMU_CFLAGS+=-I..
 | 
			
		||||
 | 
			
		||||
include $(SRC_PATH)/Makefile.objs
 | 
			
		||||
 | 
			
		||||
all: $(user-obj-y)
 | 
			
		||||
# Dummy command so that make thinks it has done something
 | 
			
		||||
	@true
 | 
			
		||||
 | 
			
		||||
clean:
 | 
			
		||||
	rm -f *.o *.d *.a *~
 | 
			
		||||
 | 
			
		||||
# Include automatically generated dependency files
 | 
			
		||||
-include $(wildcard *.d */*.d)
 | 
			
		||||
							
								
								
									
										91
									
								
								QMP/README
									
									
									
									
									
								
							
							
						
						
									
										91
									
								
								QMP/README
									
									
									
									
									
								
							@@ -1,91 +0,0 @@
 | 
			
		||||
                          QEMU Monitor Protocol
 | 
			
		||||
                          =====================
 | 
			
		||||
 | 
			
		||||
Introduction
 | 
			
		||||
-------------
 | 
			
		||||
 | 
			
		||||
The QEMU Monitor Protocol (QMP) allows applications to communicate with
 | 
			
		||||
QEMU's Monitor.
 | 
			
		||||
 | 
			
		||||
QMP is JSON[1] based and currently has the following features:
 | 
			
		||||
 | 
			
		||||
- Lightweight, text-based, easy to parse data format
 | 
			
		||||
- Asynchronous messages support (ie. events)
 | 
			
		||||
- Capabilities Negotiation
 | 
			
		||||
 | 
			
		||||
For detailed information on QMP's usage, please, refer to the following files:
 | 
			
		||||
 | 
			
		||||
o qmp-spec.txt      QEMU Monitor Protocol current specification
 | 
			
		||||
o qmp-commands.txt  QMP supported commands (auto-generated at build-time)
 | 
			
		||||
o qmp-events.txt    List of available asynchronous events
 | 
			
		||||
 | 
			
		||||
There are also two simple Python scripts available:
 | 
			
		||||
 | 
			
		||||
o qmp-shell  A shell
 | 
			
		||||
o vm-info    Show some information about the Virtual Machine
 | 
			
		||||
 | 
			
		||||
IMPORTANT: It's strongly recommended to read the 'Stability Considerations'
 | 
			
		||||
section in the qmp-commands.txt file before making any serious use of QMP.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
[1] http://www.json.org
 | 
			
		||||
 | 
			
		||||
Usage
 | 
			
		||||
-----
 | 
			
		||||
 | 
			
		||||
To enable QMP, you need a QEMU monitor instance in "control mode". There are
 | 
			
		||||
two ways of doing this.
 | 
			
		||||
 | 
			
		||||
The simplest one is using the '-qmp' command-line option. The following
 | 
			
		||||
example makes QMP available on localhost port 4444:
 | 
			
		||||
 | 
			
		||||
  $ qemu [...] -qmp tcp:localhost:4444,server
 | 
			
		||||
 | 
			
		||||
However, in order to have more complex combinations, like multiple monitors,
 | 
			
		||||
the '-mon' command-line option should be used along with the '-chardev' one.
 | 
			
		||||
For instance, the following example creates one user monitor on stdio and one
 | 
			
		||||
QMP monitor on localhost port 4444.
 | 
			
		||||
 | 
			
		||||
   $ qemu [...] -chardev stdio,id=mon0 -mon chardev=mon0,mode=readline \
 | 
			
		||||
                -chardev socket,id=mon1,host=localhost,port=4444,server \
 | 
			
		||||
                -mon chardev=mon1,mode=control
 | 
			
		||||
 | 
			
		||||
Please, refer to QEMU's manpage for more information.
 | 
			
		||||
 | 
			
		||||
Simple Testing
 | 
			
		||||
--------------
 | 
			
		||||
 | 
			
		||||
To manually test QMP one can connect with telnet and issue commands by hand:
 | 
			
		||||
 | 
			
		||||
$ telnet localhost 4444
 | 
			
		||||
Trying 127.0.0.1...
 | 
			
		||||
Connected to localhost.
 | 
			
		||||
Escape character is '^]'.
 | 
			
		||||
{"QMP": {"version": {"qemu": {"micro": 50, "minor": 13, "major": 0}, "package": ""}, "capabilities": []}}
 | 
			
		||||
{ "execute": "qmp_capabilities" }
 | 
			
		||||
{"return": {}}
 | 
			
		||||
{ "execute": "query-version" }
 | 
			
		||||
{"return": {"qemu": {"micro": 50, "minor": 13, "major": 0}, "package": ""}}
 | 
			
		||||
 | 
			
		||||
Development Process
 | 
			
		||||
-------------------
 | 
			
		||||
 | 
			
		||||
When changing QMP's interface (by adding new commands, events or modifying
 | 
			
		||||
existing ones) it's mandatory to update the relevant documentation, which is
 | 
			
		||||
one (or more) of the files listed in the 'Introduction' section*.
 | 
			
		||||
 | 
			
		||||
Also, it's strongly recommended to send the documentation patch first, before
 | 
			
		||||
doing any code change. This is so because:
 | 
			
		||||
 | 
			
		||||
  1. Avoids the code dictating the interface
 | 
			
		||||
 | 
			
		||||
  2. Review can improve your interface.  Letting that happen before
 | 
			
		||||
     you implement it can save you work.
 | 
			
		||||
 | 
			
		||||
* The qmp-commands.txt file is generated from the qemu-monitor.hx one, which
 | 
			
		||||
  is the file that should be edited.
 | 
			
		||||
 | 
			
		||||
Homepage
 | 
			
		||||
--------
 | 
			
		||||
 | 
			
		||||
http://wiki.qemu.org/QMP
 | 
			
		||||
@@ -1,202 +0,0 @@
 | 
			
		||||
                   QEMU Monitor Protocol Events
 | 
			
		||||
                   ============================
 | 
			
		||||
 | 
			
		||||
BLOCK_IO_ERROR
 | 
			
		||||
--------------
 | 
			
		||||
 | 
			
		||||
Emitted when a disk I/O error occurs.
 | 
			
		||||
 | 
			
		||||
Data:
 | 
			
		||||
 | 
			
		||||
- "device": device name (json-string)
 | 
			
		||||
- "operation": I/O operation (json-string, "read" or "write")
 | 
			
		||||
- "action": action that has been taken, it's one of the following (json-string):
 | 
			
		||||
    "ignore": error has been ignored
 | 
			
		||||
    "report": error has been reported to the device
 | 
			
		||||
    "stop": error caused VM to be stopped
 | 
			
		||||
 | 
			
		||||
Example:
 | 
			
		||||
 | 
			
		||||
{ "event": "BLOCK_IO_ERROR",
 | 
			
		||||
    "data": { "device": "ide0-hd1",
 | 
			
		||||
              "operation": "write",
 | 
			
		||||
              "action": "stop" },
 | 
			
		||||
    "timestamp": { "seconds": 1265044230, "microseconds": 450486 } }
 | 
			
		||||
 | 
			
		||||
Note: If action is "stop", a STOP event will eventually follow the
 | 
			
		||||
BLOCK_IO_ERROR event.
 | 
			
		||||
 | 
			
		||||
RESET
 | 
			
		||||
-----
 | 
			
		||||
 | 
			
		||||
Emitted when the Virtual Machine is reseted.
 | 
			
		||||
 | 
			
		||||
Data: None.
 | 
			
		||||
 | 
			
		||||
Example:
 | 
			
		||||
 | 
			
		||||
{ "event": "RESET",
 | 
			
		||||
    "timestamp": { "seconds": 1267041653, "microseconds": 9518 } }
 | 
			
		||||
 | 
			
		||||
RESUME
 | 
			
		||||
------
 | 
			
		||||
 | 
			
		||||
Emitted when the Virtual Machine resumes execution.
 | 
			
		||||
 | 
			
		||||
Data: None.
 | 
			
		||||
 | 
			
		||||
Example:
 | 
			
		||||
 | 
			
		||||
{ "event": "RESUME",
 | 
			
		||||
    "timestamp": { "seconds": 1271770767, "microseconds": 582542 } }
 | 
			
		||||
 | 
			
		||||
RTC_CHANGE
 | 
			
		||||
----------
 | 
			
		||||
 | 
			
		||||
Emitted when the guest changes the RTC time.
 | 
			
		||||
 | 
			
		||||
Data:
 | 
			
		||||
 | 
			
		||||
- "offset": delta against the host UTC in seconds (json-number)
 | 
			
		||||
 | 
			
		||||
Example:
 | 
			
		||||
 | 
			
		||||
{ "event": "RTC_CHANGE",
 | 
			
		||||
    "data": { "offset": 78 },
 | 
			
		||||
    "timestamp": { "seconds": 1267020223, "microseconds": 435656 } }
 | 
			
		||||
 | 
			
		||||
SHUTDOWN
 | 
			
		||||
--------
 | 
			
		||||
 | 
			
		||||
Emitted when the Virtual Machine is powered down.
 | 
			
		||||
 | 
			
		||||
Data: None.
 | 
			
		||||
 | 
			
		||||
Example:
 | 
			
		||||
 | 
			
		||||
{ "event": "SHUTDOWN",
 | 
			
		||||
    "timestamp": { "seconds": 1267040730, "microseconds": 682951 } }
 | 
			
		||||
 | 
			
		||||
Note: If the command-line option "-no-shutdown" has been specified, a STOP
 | 
			
		||||
event will eventually follow the SHUTDOWN event.
 | 
			
		||||
 | 
			
		||||
STOP
 | 
			
		||||
----
 | 
			
		||||
 | 
			
		||||
Emitted when the Virtual Machine is stopped.
 | 
			
		||||
 | 
			
		||||
Data: None.
 | 
			
		||||
 | 
			
		||||
Example:
 | 
			
		||||
 | 
			
		||||
{ "event": "SHUTDOWN",
 | 
			
		||||
    "timestamp": { "seconds": 1267041730, "microseconds": 281295 } }
 | 
			
		||||
 | 
			
		||||
VNC_CONNECTED
 | 
			
		||||
-------------
 | 
			
		||||
 | 
			
		||||
Emitted when a VNC client establishes a connection.
 | 
			
		||||
 | 
			
		||||
Data:
 | 
			
		||||
 | 
			
		||||
- "server": Server information (json-object)
 | 
			
		||||
  - "host": IP address (json-string)
 | 
			
		||||
  - "service": port number (json-string)
 | 
			
		||||
  - "family": address family (json-string, "ipv4" or "ipv6")
 | 
			
		||||
  - "auth": authentication method (json-string, optional)
 | 
			
		||||
- "client": Client information (json-object)
 | 
			
		||||
  - "host": IP address (json-string)
 | 
			
		||||
  - "service": port number (json-string)
 | 
			
		||||
  - "family": address family (json-string, "ipv4" or "ipv6")
 | 
			
		||||
 | 
			
		||||
Example:
 | 
			
		||||
 | 
			
		||||
{ "event": "VNC_CONNECTED",
 | 
			
		||||
    "data": {
 | 
			
		||||
        "server": { "auth": "sasl", "family": "ipv4",
 | 
			
		||||
                    "service": "5901", "host": "0.0.0.0" },
 | 
			
		||||
        "client": { "family": "ipv4", "service": "58425",
 | 
			
		||||
                    "host": "127.0.0.1" } },
 | 
			
		||||
    "timestamp": { "seconds": 1262976601, "microseconds": 975795 } }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Note: This event is emitted before any authentication takes place, thus
 | 
			
		||||
the authentication ID is not provided.
 | 
			
		||||
 | 
			
		||||
VNC_DISCONNECTED
 | 
			
		||||
----------------
 | 
			
		||||
 | 
			
		||||
Emitted when the conection is closed.
 | 
			
		||||
 | 
			
		||||
Data:
 | 
			
		||||
 | 
			
		||||
- "server": Server information (json-object)
 | 
			
		||||
  - "host": IP address (json-string)
 | 
			
		||||
  - "service": port number (json-string)
 | 
			
		||||
  - "family": address family (json-string, "ipv4" or "ipv6")
 | 
			
		||||
  - "auth": authentication method (json-string, optional)
 | 
			
		||||
- "client": Client information (json-object)
 | 
			
		||||
  - "host": IP address (json-string)
 | 
			
		||||
  - "service": port number (json-string)
 | 
			
		||||
  - "family": address family (json-string, "ipv4" or "ipv6")
 | 
			
		||||
  - "x509_dname": TLS dname (json-string, optional)
 | 
			
		||||
  - "sasl_username": SASL username (json-string, optional)
 | 
			
		||||
 | 
			
		||||
Example:
 | 
			
		||||
 | 
			
		||||
{ "event": "VNC_DISCONNECTED",
 | 
			
		||||
    "data": {
 | 
			
		||||
        "server": { "auth": "sasl", "family": "ipv4",
 | 
			
		||||
                    "service": "5901", "host": "0.0.0.0" },
 | 
			
		||||
        "client": { "family": "ipv4", "service": "58425",
 | 
			
		||||
                    "host": "127.0.0.1", "sasl_username": "luiz" } },
 | 
			
		||||
    "timestamp": { "seconds": 1262976601, "microseconds": 975795 } }
 | 
			
		||||
 | 
			
		||||
VNC_INITIALIZED
 | 
			
		||||
---------------
 | 
			
		||||
 | 
			
		||||
Emitted after authentication takes place (if any) and the VNC session is
 | 
			
		||||
made active.
 | 
			
		||||
 | 
			
		||||
Data:
 | 
			
		||||
 | 
			
		||||
- "server": Server information (json-object)
 | 
			
		||||
  - "host": IP address (json-string)
 | 
			
		||||
  - "service": port number (json-string)
 | 
			
		||||
  - "family": address family (json-string, "ipv4" or "ipv6")
 | 
			
		||||
  - "auth": authentication method (json-string, optional)
 | 
			
		||||
- "client": Client information (json-object)
 | 
			
		||||
  - "host": IP address (json-string)
 | 
			
		||||
  - "service": port number (json-string)
 | 
			
		||||
  - "family": address family (json-string, "ipv4" or "ipv6")
 | 
			
		||||
  - "x509_dname": TLS dname (json-string, optional)
 | 
			
		||||
  - "sasl_username": SASL username (json-string, optional)
 | 
			
		||||
 | 
			
		||||
Example:
 | 
			
		||||
 | 
			
		||||
{ "event": "VNC_INITIALIZED",
 | 
			
		||||
    "data": {
 | 
			
		||||
        "server": { "auth": "sasl", "family": "ipv4",
 | 
			
		||||
                    "service": "5901", "host": "0.0.0.0"},
 | 
			
		||||
        "client": { "family": "ipv4", "service": "46089",
 | 
			
		||||
                    "host": "127.0.0.1", "sasl_username": "luiz" } },
 | 
			
		||||
        "timestamp": { "seconds": 1263475302, "microseconds": 150772 } }
 | 
			
		||||
 | 
			
		||||
WATCHDOG
 | 
			
		||||
--------
 | 
			
		||||
 | 
			
		||||
Emitted when the watchdog device's timer is expired.
 | 
			
		||||
 | 
			
		||||
Data:
 | 
			
		||||
 | 
			
		||||
- "action": Action that has been taken, it's one of the following (json-string):
 | 
			
		||||
            "reset", "shutdown", "poweroff", "pause", "debug", or "none"
 | 
			
		||||
 | 
			
		||||
Example:
 | 
			
		||||
 | 
			
		||||
{ "event": "WATCHDOG",
 | 
			
		||||
     "data": { "action": "reset" },
 | 
			
		||||
     "timestamp": { "seconds": 1267061043, "microseconds": 959568 } }
 | 
			
		||||
 | 
			
		||||
Note: If action is "reset", "shutdown", or "pause" the WATCHDOG event is
 | 
			
		||||
followed respectively by the RESET, SHUTDOWN, or STOP events.
 | 
			
		||||
@@ -1,73 +0,0 @@
 | 
			
		||||
#!/usr/bin/python
 | 
			
		||||
#
 | 
			
		||||
# Simple QEMU shell on top of QMP
 | 
			
		||||
#
 | 
			
		||||
# Copyright (C) 2009 Red Hat Inc.
 | 
			
		||||
#
 | 
			
		||||
# Authors:
 | 
			
		||||
#  Luiz Capitulino <lcapitulino@redhat.com>
 | 
			
		||||
#
 | 
			
		||||
# This work is licensed under the terms of the GNU GPL, version 2.  See
 | 
			
		||||
# the COPYING file in the top-level directory.
 | 
			
		||||
#
 | 
			
		||||
# Usage:
 | 
			
		||||
#
 | 
			
		||||
# Start QEMU with:
 | 
			
		||||
#
 | 
			
		||||
# $ qemu [...] -monitor control,unix:./qmp,server
 | 
			
		||||
#
 | 
			
		||||
# Run the shell:
 | 
			
		||||
#
 | 
			
		||||
# $ qmp-shell ./qmp
 | 
			
		||||
#
 | 
			
		||||
# Commands have the following format:
 | 
			
		||||
#
 | 
			
		||||
# < command-name > [ arg-name1=arg1 ] ... [ arg-nameN=argN ]
 | 
			
		||||
#
 | 
			
		||||
# For example:
 | 
			
		||||
#
 | 
			
		||||
# (QEMU) info item=network
 | 
			
		||||
 | 
			
		||||
import qmp
 | 
			
		||||
import readline
 | 
			
		||||
from sys import argv,exit
 | 
			
		||||
 | 
			
		||||
def shell_help():
 | 
			
		||||
    print 'bye  exit from the shell'
 | 
			
		||||
 | 
			
		||||
def main():
 | 
			
		||||
    if len(argv) != 2:
 | 
			
		||||
        print 'qemu-shell <unix-socket>'
 | 
			
		||||
        exit(1)
 | 
			
		||||
 | 
			
		||||
    qemu = qmp.QEMUMonitorProtocol(argv[1])
 | 
			
		||||
    qemu.connect()
 | 
			
		||||
    qemu.send("qmp_capabilities")
 | 
			
		||||
 | 
			
		||||
    print 'Connected!'
 | 
			
		||||
 | 
			
		||||
    while True:
 | 
			
		||||
        try:
 | 
			
		||||
            cmd = raw_input('(QEMU) ')
 | 
			
		||||
        except EOFError:
 | 
			
		||||
            print
 | 
			
		||||
            break
 | 
			
		||||
        if cmd == '':
 | 
			
		||||
            continue
 | 
			
		||||
        elif cmd == 'bye':
 | 
			
		||||
            break
 | 
			
		||||
        elif cmd == 'help':
 | 
			
		||||
            shell_help()
 | 
			
		||||
        else:
 | 
			
		||||
            try:
 | 
			
		||||
                resp = qemu.send(cmd)
 | 
			
		||||
                if resp == None:
 | 
			
		||||
                    print 'Disconnected'
 | 
			
		||||
                    break
 | 
			
		||||
                print resp
 | 
			
		||||
            except IndexError:
 | 
			
		||||
                print '-> command format: <command-name> ',
 | 
			
		||||
                print '[arg-name1=arg1] ... [arg-nameN=argN]'
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
    main()
 | 
			
		||||
							
								
								
									
										272
									
								
								QMP/qmp-spec.txt
									
									
									
									
									
								
							
							
						
						
									
										272
									
								
								QMP/qmp-spec.txt
									
									
									
									
									
								
							@@ -1,272 +0,0 @@
 | 
			
		||||
           QEMU Monitor Protocol Specification - Version 0.1
 | 
			
		||||
 | 
			
		||||
1. Introduction
 | 
			
		||||
===============
 | 
			
		||||
 | 
			
		||||
This document specifies the QEMU Monitor Protocol (QMP), a JSON-based protocol
 | 
			
		||||
which is available for applications to control QEMU at the machine-level.
 | 
			
		||||
 | 
			
		||||
To enable QMP support, QEMU has to be run in "control mode". This is done by
 | 
			
		||||
starting QEMU with the appropriate command-line options. Please, refer to the
 | 
			
		||||
QEMU manual page for more information.
 | 
			
		||||
 | 
			
		||||
2. Protocol Specification
 | 
			
		||||
=========================
 | 
			
		||||
 | 
			
		||||
This section details the protocol format. For the purpose of this document
 | 
			
		||||
"Client" is any application which is communicating with QEMU in control mode,
 | 
			
		||||
and "Server" is QEMU itself.
 | 
			
		||||
 | 
			
		||||
JSON data structures, when mentioned in this document, are always in the
 | 
			
		||||
following format:
 | 
			
		||||
 | 
			
		||||
    json-DATA-STRUCTURE-NAME
 | 
			
		||||
 | 
			
		||||
Where DATA-STRUCTURE-NAME is any valid JSON data structure, as defined by
 | 
			
		||||
the JSON standard:
 | 
			
		||||
 | 
			
		||||
http://www.ietf.org/rfc/rfc4627.txt
 | 
			
		||||
 | 
			
		||||
For convenience, json-object members and json-array elements mentioned in
 | 
			
		||||
this document will be in a certain order. However, in real protocol usage
 | 
			
		||||
they can be in ANY order, thus no particular order should be assumed.
 | 
			
		||||
 | 
			
		||||
2.1 General Definitions
 | 
			
		||||
-----------------------
 | 
			
		||||
 | 
			
		||||
2.1.1 All interactions transmitted by the Server are json-objects, always
 | 
			
		||||
      terminating with CRLF
 | 
			
		||||
 | 
			
		||||
2.1.2 All json-objects members are mandatory when not specified otherwise
 | 
			
		||||
 | 
			
		||||
2.2 Server Greeting
 | 
			
		||||
-------------------
 | 
			
		||||
 | 
			
		||||
Right when connected the Server will issue a greeting message, which signals
 | 
			
		||||
that the connection has been successfully established and that the Server is
 | 
			
		||||
ready for capabilities negotiation (for more information refer to section
 | 
			
		||||
'4. Capabilities Negotiation').
 | 
			
		||||
 | 
			
		||||
The format is:
 | 
			
		||||
 | 
			
		||||
{ "QMP": { "version": json-object, "capabilities": json-array } }
 | 
			
		||||
 | 
			
		||||
 Where,
 | 
			
		||||
 | 
			
		||||
- The "version" member contains the Server's version information (the format
 | 
			
		||||
  is the same of the 'query-version' command)
 | 
			
		||||
- The "capabilities" member specify the availability of features beyond the
 | 
			
		||||
  baseline specification
 | 
			
		||||
 | 
			
		||||
2.3 Issuing Commands
 | 
			
		||||
--------------------
 | 
			
		||||
 | 
			
		||||
The format for command execution is:
 | 
			
		||||
 | 
			
		||||
{ "execute": json-string, "arguments": json-object, "id": json-value }
 | 
			
		||||
 | 
			
		||||
 Where,
 | 
			
		||||
 | 
			
		||||
- The "execute" member identifies the command to be executed by the Server
 | 
			
		||||
- The "arguments" member is used to pass any arguments required for the
 | 
			
		||||
  execution of the command, it is optional when no arguments are required
 | 
			
		||||
- The "id" member is a transaction identification associated with the
 | 
			
		||||
  command execution, it is optional and will be part of the response if
 | 
			
		||||
  provided
 | 
			
		||||
 | 
			
		||||
2.4 Commands Responses
 | 
			
		||||
----------------------
 | 
			
		||||
 | 
			
		||||
There are two possible responses which the Server will issue as the result
 | 
			
		||||
of a command execution: success or error.
 | 
			
		||||
 | 
			
		||||
2.4.1 success
 | 
			
		||||
-------------
 | 
			
		||||
 | 
			
		||||
The success response is issued when the command execution has finished
 | 
			
		||||
without errors.
 | 
			
		||||
 | 
			
		||||
The format is:
 | 
			
		||||
 | 
			
		||||
{ "return": json-object, "id": json-value }
 | 
			
		||||
 | 
			
		||||
 Where,
 | 
			
		||||
 | 
			
		||||
- The "return" member contains the command returned data, which is defined
 | 
			
		||||
  in a per-command basis or an empty json-object if the command does not
 | 
			
		||||
  return data
 | 
			
		||||
- The "id" member contains the transaction identification associated
 | 
			
		||||
  with the command execution (if issued by the Client)
 | 
			
		||||
 | 
			
		||||
2.4.2 error
 | 
			
		||||
-----------
 | 
			
		||||
 | 
			
		||||
The error response is issued when the command execution could not be
 | 
			
		||||
completed because of an error condition.
 | 
			
		||||
 | 
			
		||||
The format is:
 | 
			
		||||
 | 
			
		||||
{ "error": { "class": json-string, "data": json-object, "desc": json-string },
 | 
			
		||||
  "id": json-value }
 | 
			
		||||
 | 
			
		||||
 Where,
 | 
			
		||||
 | 
			
		||||
- The "class" member contains the error class name (eg. "ServiceUnavailable")
 | 
			
		||||
- The "data" member contains specific error data and is defined in a
 | 
			
		||||
  per-command basis, it will be an empty json-object if the error has no data
 | 
			
		||||
- The "desc" member is a human-readable error message. Clients should
 | 
			
		||||
  not attempt to parse this message.
 | 
			
		||||
- The "id" member contains the transaction identification associated with
 | 
			
		||||
  the command execution (if issued by the Client)
 | 
			
		||||
 | 
			
		||||
NOTE: Some errors can occur before the Server is able to read the "id" member,
 | 
			
		||||
in these cases the "id" member will not be part of the error response, even
 | 
			
		||||
if provided by the client.
 | 
			
		||||
 | 
			
		||||
2.5 Asynchronous events
 | 
			
		||||
-----------------------
 | 
			
		||||
 | 
			
		||||
As a result of state changes, the Server may send messages unilaterally
 | 
			
		||||
to the Client at any time. They are called 'asynchronous events'.
 | 
			
		||||
 | 
			
		||||
The format is:
 | 
			
		||||
 | 
			
		||||
{ "event": json-string, "data": json-object,
 | 
			
		||||
  "timestamp": { "seconds": json-number, "microseconds": json-number } }
 | 
			
		||||
 | 
			
		||||
 Where,
 | 
			
		||||
 | 
			
		||||
- The "event" member contains the event's name
 | 
			
		||||
- The "data" member contains event specific data, which is defined in a
 | 
			
		||||
  per-event basis, it is optional
 | 
			
		||||
- The "timestamp" member contains the exact time of when the event occurred
 | 
			
		||||
  in the Server. It is a fixed json-object with time in seconds and
 | 
			
		||||
  microseconds
 | 
			
		||||
 | 
			
		||||
For a listing of supported asynchronous events, please, refer to the
 | 
			
		||||
qmp-events.txt file.
 | 
			
		||||
 | 
			
		||||
3. QMP Examples
 | 
			
		||||
===============
 | 
			
		||||
 | 
			
		||||
This section provides some examples of real QMP usage, in all of them
 | 
			
		||||
'C' stands for 'Client' and 'S' stands for 'Server'.
 | 
			
		||||
 | 
			
		||||
3.1 Server greeting
 | 
			
		||||
-------------------
 | 
			
		||||
 | 
			
		||||
S: {"QMP": {"version": {"qemu": "0.12.50", "package": ""}, "capabilities": []}}
 | 
			
		||||
 | 
			
		||||
3.2 Simple 'stop' execution
 | 
			
		||||
---------------------------
 | 
			
		||||
 | 
			
		||||
C: { "execute": "stop" }
 | 
			
		||||
S: {"return": {}}
 | 
			
		||||
 | 
			
		||||
3.3 KVM information
 | 
			
		||||
-------------------
 | 
			
		||||
 | 
			
		||||
C: { "execute": "query-kvm", "id": "example" }
 | 
			
		||||
S: {"return": {"enabled": true, "present": true}, "id": "example"}
 | 
			
		||||
 | 
			
		||||
3.4 Parsing error
 | 
			
		||||
------------------
 | 
			
		||||
 | 
			
		||||
C: { "execute": }
 | 
			
		||||
S: {"error": {"class": "JSONParsing", "desc": "Invalid JSON syntax", "data":
 | 
			
		||||
{}}}
 | 
			
		||||
 | 
			
		||||
3.5 Powerdown event
 | 
			
		||||
-------------------
 | 
			
		||||
 | 
			
		||||
S: {"timestamp": {"seconds": 1258551470, "microseconds": 802384}, "event":
 | 
			
		||||
"POWERDOWN"}
 | 
			
		||||
 | 
			
		||||
4. Capabilities Negotiation
 | 
			
		||||
----------------------------
 | 
			
		||||
 | 
			
		||||
When a Client successfully establishes a connection, the Server is in
 | 
			
		||||
Capabilities Negotiation mode.
 | 
			
		||||
 | 
			
		||||
In this mode only the 'qmp_capabilities' command is allowed to run, all
 | 
			
		||||
other commands will return the CommandNotFound error. Asynchronous messages
 | 
			
		||||
are not delivered either.
 | 
			
		||||
 | 
			
		||||
Clients should use the 'qmp_capabilities' command to enable capabilities
 | 
			
		||||
advertised in the Server's greeting (section '2.2 Server Greeting') they
 | 
			
		||||
support.
 | 
			
		||||
 | 
			
		||||
When the 'qmp_capabilities' command is issued, and if it does not return an
 | 
			
		||||
error, the Server enters in Command mode where capabilities changes take
 | 
			
		||||
effect, all commands (except 'qmp_capabilities') are allowed and asynchronous
 | 
			
		||||
messages are delivered.
 | 
			
		||||
 | 
			
		||||
5 Compatibility Considerations
 | 
			
		||||
------------------------------
 | 
			
		||||
 | 
			
		||||
All protocol changes or new features which modify the protocol format in an
 | 
			
		||||
incompatible way are disabled by default and will be advertised by the
 | 
			
		||||
capabilities array (section '2.2 Server Greeting'). Thus, Clients can check
 | 
			
		||||
that array and enable the capabilities they support.
 | 
			
		||||
 | 
			
		||||
Additionally, Clients must not assume any particular:
 | 
			
		||||
 | 
			
		||||
- Size of json-objects or length of json-arrays
 | 
			
		||||
- Order of json-object members or json-array elements
 | 
			
		||||
- Amount of errors generated by a command, that is, new errors can be added
 | 
			
		||||
  to any existing command in newer versions of the Server
 | 
			
		||||
 | 
			
		||||
6. Downstream extension of QMP
 | 
			
		||||
------------------------------
 | 
			
		||||
 | 
			
		||||
We recommend that downstream consumers of QEMU do *not* modify QMP.
 | 
			
		||||
Management tools should be able to support both upstream and downstream
 | 
			
		||||
versions of QMP without special logic, and downstream extensions are
 | 
			
		||||
inherently at odds with that.
 | 
			
		||||
 | 
			
		||||
However, we recognize that it is sometimes impossible for downstreams to
 | 
			
		||||
avoid modifying QMP.  Both upstream and downstream need to take care to
 | 
			
		||||
preserve long-term compatibility and interoperability.
 | 
			
		||||
 | 
			
		||||
To help with that, QMP reserves JSON object member names beginning with
 | 
			
		||||
'__' (double underscore) for downstream use ("downstream names").  This
 | 
			
		||||
means upstream will never use any downstream names for its commands,
 | 
			
		||||
arguments, errors, asynchronous events, and so forth.
 | 
			
		||||
 | 
			
		||||
Any new names downstream wishes to add must begin with '__'.  To
 | 
			
		||||
ensure compatibility with other downstreams, it is strongly
 | 
			
		||||
recommended that you prefix your downstram names with '__RFQDN_' where
 | 
			
		||||
RFQDN is a valid, reverse fully qualified domain name which you
 | 
			
		||||
control.  For example, a qemu-kvm specific monitor command would be:
 | 
			
		||||
 | 
			
		||||
    (qemu) __org.linux-kvm_enable_irqchip
 | 
			
		||||
 | 
			
		||||
Downstream must not change the server greeting (section 2.2) other than
 | 
			
		||||
to offer additional capabilities.  But see below for why even that is
 | 
			
		||||
discouraged.
 | 
			
		||||
 | 
			
		||||
Section '5 Compatibility Considerations' applies to downstream as well
 | 
			
		||||
as to upstream, obviously.  It follows that downstream must behave
 | 
			
		||||
exactly like upstream for any input not containing members with
 | 
			
		||||
downstream names ("downstream members"), except it may add members
 | 
			
		||||
with downstream names to its output.
 | 
			
		||||
 | 
			
		||||
Thus, a client should not be able to distinguish downstream from
 | 
			
		||||
upstream as long as it doesn't send input with downstream members, and
 | 
			
		||||
properly ignores any downstream members in the output it receives.
 | 
			
		||||
 | 
			
		||||
Advice on downstream modifications:
 | 
			
		||||
 | 
			
		||||
1. Introducing new commands is okay.  If you want to extend an existing
 | 
			
		||||
   command, consider introducing a new one with the new behaviour
 | 
			
		||||
   instead.
 | 
			
		||||
 | 
			
		||||
2. Introducing new asynchronous messages is okay.  If you want to extend
 | 
			
		||||
   an existing message, consider adding a new one instead.
 | 
			
		||||
 | 
			
		||||
3. Introducing new errors for use in new commands is okay.  Adding new
 | 
			
		||||
   errors to existing commands counts as extension, so 1. applies.
 | 
			
		||||
 | 
			
		||||
4. New capabilities are strongly discouraged.  Capabilities are for
 | 
			
		||||
   evolving the basic protocol, and multiple diverging basic protocol
 | 
			
		||||
   dialects are most undesirable.
 | 
			
		||||
							
								
								
									
										76
									
								
								QMP/qmp.py
									
									
									
									
									
								
							
							
						
						
									
										76
									
								
								QMP/qmp.py
									
									
									
									
									
								
							@@ -1,76 +0,0 @@
 | 
			
		||||
# QEMU Monitor Protocol Python class
 | 
			
		||||
# 
 | 
			
		||||
# Copyright (C) 2009 Red Hat Inc.
 | 
			
		||||
#
 | 
			
		||||
# Authors:
 | 
			
		||||
#  Luiz Capitulino <lcapitulino@redhat.com>
 | 
			
		||||
#
 | 
			
		||||
# This work is licensed under the terms of the GNU GPL, version 2.  See
 | 
			
		||||
# the COPYING file in the top-level directory.
 | 
			
		||||
 | 
			
		||||
import socket, json
 | 
			
		||||
 | 
			
		||||
class QMPError(Exception):
 | 
			
		||||
    pass
 | 
			
		||||
 | 
			
		||||
class QMPConnectError(QMPError):
 | 
			
		||||
    pass
 | 
			
		||||
 | 
			
		||||
class QEMUMonitorProtocol:
 | 
			
		||||
    def connect(self):
 | 
			
		||||
        self.sock.connect(self.filename)
 | 
			
		||||
        data = self.__json_read()
 | 
			
		||||
        if data == None:
 | 
			
		||||
            raise QMPConnectError
 | 
			
		||||
        if not data.has_key('QMP'):
 | 
			
		||||
            raise QMPConnectError
 | 
			
		||||
        return data['QMP']['capabilities']
 | 
			
		||||
 | 
			
		||||
    def close(self):
 | 
			
		||||
        self.sock.close()
 | 
			
		||||
 | 
			
		||||
    def send_raw(self, line):
 | 
			
		||||
        self.sock.send(str(line))
 | 
			
		||||
        return self.__json_read()
 | 
			
		||||
 | 
			
		||||
    def send(self, cmdline):
 | 
			
		||||
        cmd = self.__build_cmd(cmdline)
 | 
			
		||||
        self.__json_send(cmd)
 | 
			
		||||
        resp = self.__json_read()
 | 
			
		||||
        if resp == None:
 | 
			
		||||
            return
 | 
			
		||||
        elif resp.has_key('error'):
 | 
			
		||||
            return resp['error']
 | 
			
		||||
        else:
 | 
			
		||||
            return resp['return']
 | 
			
		||||
 | 
			
		||||
    def __build_cmd(self, cmdline):
 | 
			
		||||
        cmdargs = cmdline.split()
 | 
			
		||||
        qmpcmd = { 'execute': cmdargs[0], 'arguments': {} }
 | 
			
		||||
        for arg in cmdargs[1:]:
 | 
			
		||||
            opt = arg.split('=')
 | 
			
		||||
            try:
 | 
			
		||||
                value = int(opt[1])
 | 
			
		||||
            except ValueError:
 | 
			
		||||
                value = opt[1]
 | 
			
		||||
            qmpcmd['arguments'][opt[0]] = value
 | 
			
		||||
        return qmpcmd
 | 
			
		||||
 | 
			
		||||
    def __json_send(self, cmd):
 | 
			
		||||
        # XXX: We have to send any additional char, otherwise
 | 
			
		||||
        # the Server won't read our input
 | 
			
		||||
        self.sock.send(json.dumps(cmd) + ' ')
 | 
			
		||||
 | 
			
		||||
    def __json_read(self):
 | 
			
		||||
        try:
 | 
			
		||||
            while True:
 | 
			
		||||
                line = json.loads(self.sockfile.readline())
 | 
			
		||||
                if not 'event' in line:
 | 
			
		||||
                    return line
 | 
			
		||||
        except ValueError:
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
    def __init__(self, filename):
 | 
			
		||||
        self.filename = filename
 | 
			
		||||
        self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
 | 
			
		||||
        self.sockfile = self.sock.makefile()
 | 
			
		||||
							
								
								
									
										33
									
								
								QMP/vm-info
									
									
									
									
									
								
							
							
						
						
									
										33
									
								
								QMP/vm-info
									
									
									
									
									
								
							@@ -1,33 +0,0 @@
 | 
			
		||||
#!/usr/bin/python
 | 
			
		||||
#
 | 
			
		||||
# Print Virtual Machine information
 | 
			
		||||
#
 | 
			
		||||
# Usage:
 | 
			
		||||
#
 | 
			
		||||
# Start QEMU with:
 | 
			
		||||
#
 | 
			
		||||
# $ qemu [...] -monitor control,unix:./qmp,server
 | 
			
		||||
#
 | 
			
		||||
# Run vm-info:
 | 
			
		||||
#
 | 
			
		||||
# $ vm-info ./qmp
 | 
			
		||||
#
 | 
			
		||||
# Luiz Capitulino <lcapitulino@redhat.com>
 | 
			
		||||
 | 
			
		||||
import qmp
 | 
			
		||||
from sys import argv,exit
 | 
			
		||||
 | 
			
		||||
def main():
 | 
			
		||||
    if len(argv) != 2:
 | 
			
		||||
        print 'vm-info <unix-socket>'
 | 
			
		||||
        exit(1)
 | 
			
		||||
 | 
			
		||||
    qemu = qmp.QEMUMonitorProtocol(argv[1])
 | 
			
		||||
    qemu.connect()
 | 
			
		||||
    qemu.send("qmp_capabilities")
 | 
			
		||||
 | 
			
		||||
    for cmd in [ 'version', 'kvm', 'status', 'uuid', 'balloon' ]:
 | 
			
		||||
        print cmd + ': ' + str(qemu.send('query-' + cmd))
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
    main()
 | 
			
		||||
							
								
								
									
										16
									
								
								README.distrib
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								README.distrib
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
			
		||||
Information about the various packages used to build the current qemu
 | 
			
		||||
x86 binary distribution:
 | 
			
		||||
 | 
			
		||||
* gcc 2.95.2 was used for the build. A glibc 2.1.3 Debian distribution
 | 
			
		||||
  was used to get most of the binary packages.
 | 
			
		||||
 | 
			
		||||
* wine-20020411 tarball
 | 
			
		||||
 | 
			
		||||
  ./configure --prefix=/usr/local/wine-i386
 | 
			
		||||
  
 | 
			
		||||
  All exe and libs were stripped. Some compile time tools and the
 | 
			
		||||
  includes were deleted.
 | 
			
		||||
 | 
			
		||||
* ldconfig was launched to build the library links:
 | 
			
		||||
 | 
			
		||||
  qemu-i386 /usr/gnemul/qemu-i386/bin/ldconfig-i386 -C /usr/gnemul/qemu-i386/etc/ld.so.cache
 | 
			
		||||
							
								
								
									
										36
									
								
								TODO
									
									
									
									
									
								
							
							
						
						
									
										36
									
								
								TODO
									
									
									
									
									
								
							@@ -1,37 +1,61 @@
 | 
			
		||||
General:
 | 
			
		||||
-------
 | 
			
		||||
- cycle counter for all archs
 | 
			
		||||
short term:
 | 
			
		||||
----------
 | 
			
		||||
- support variable tsc freq
 | 
			
		||||
- cpu_interrupt() win32/SMP fix
 | 
			
		||||
- 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
 | 
			
		||||
- merge Solaris patch
 | 
			
		||||
- warning for OS/2: must not use 128 MB memory (merge bochs cmos patch ?)
 | 
			
		||||
- config file (at least for windows/Mac OS X)
 | 
			
		||||
- commit message if execution of code in IO memory
 | 
			
		||||
- update doc: PCI infos.
 | 
			
		||||
- VNC patch + Synaptic patch.
 | 
			
		||||
- basic VGA optimizations
 | 
			
		||||
- better code fetch
 | 
			
		||||
- physical memory cache (reduce qemu-fast address space size to about 32 MB)
 | 
			
		||||
- better code fetch (different exception handling + CS.limit support)
 | 
			
		||||
- do not resize vga if invalid size.
 | 
			
		||||
- avoid looping if only exceptions
 | 
			
		||||
- cycle counter for all archs
 | 
			
		||||
- 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()).
 | 
			
		||||
- fix arm fpu rounding (at least for float->integer conversions)
 | 
			
		||||
 | 
			
		||||
ppc specific:
 | 
			
		||||
------------
 | 
			
		||||
- TLB invalidate not needed if msr_pr changes
 | 
			
		||||
- SPR_ENCODE() not useful
 | 
			
		||||
- enable shift optimizations ?
 | 
			
		||||
 | 
			
		||||
linux-user specific:
 | 
			
		||||
-------------------
 | 
			
		||||
- remove threading support as it cannot work at this point
 | 
			
		||||
- improve IPC syscalls
 | 
			
		||||
- 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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										105
									
								
								a.out.h
									
									
									
									
									
								
							
							
						
						
									
										105
									
								
								a.out.h
									
									
									
									
									
								
							@@ -25,9 +25,9 @@ extern "C" {
 | 
			
		||||
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	*/
 | 
			
		||||
  unsigned long f_timdat;	/* time & date stamp		*/
 | 
			
		||||
  unsigned long f_symptr;	/* file pointer to symtab	*/
 | 
			
		||||
  unsigned long f_nsyms;	/* number of symtab entries	*/
 | 
			
		||||
  short f_opthdr;	/* sizeof(optional hdr)		*/
 | 
			
		||||
  short f_flags;	/* flags			*/
 | 
			
		||||
};
 | 
			
		||||
@@ -72,12 +72,12 @@ 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=
 | 
			
		||||
  unsigned long	tsize;		/* text size in bytes, padded to FW bdry*/
 | 
			
		||||
  unsigned long	dsize;		/* initialized data "  "		*/
 | 
			
		||||
  unsigned long	bsize;		/* uninitialized data "   "		*/
 | 
			
		||||
  unsigned long	entry;		/* entry pt.				*/
 | 
			
		||||
  unsigned long text_start;	/* base of text used for this file */
 | 
			
		||||
  unsigned long data_start;	/* base of data used for this file=
 | 
			
		||||
 */
 | 
			
		||||
}
 | 
			
		||||
AOUTHDR;
 | 
			
		||||
@@ -103,16 +103,16 @@ AOUTHDR;
 | 
			
		||||
 | 
			
		||||
struct external_scnhdr {
 | 
			
		||||
  char		s_name[8];	/* section name			*/
 | 
			
		||||
  host_ulong	s_paddr;	/* physical address, offset
 | 
			
		||||
  unsigned long	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 long	s_vaddr;	/* virtual address		*/
 | 
			
		||||
  unsigned long	s_size;		/* section size			*/
 | 
			
		||||
  unsigned long	s_scnptr;	/* file ptr to raw data for section */
 | 
			
		||||
  unsigned long	s_relptr;	/* file ptr to relocation	*/
 | 
			
		||||
  unsigned long	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			*/
 | 
			
		||||
  unsigned long	s_flags;	/* flags			*/
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define	SCNHDR	struct external_scnhdr
 | 
			
		||||
@@ -136,8 +136,8 @@ struct external_scnhdr {
 | 
			
		||||
 */
 | 
			
		||||
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	*/
 | 
			
		||||
    unsigned long l_symndx; /* function name symbol index, iff l_lnno 0 */
 | 
			
		||||
    unsigned long l_paddr;	/* (physical) address of line number	*/
 | 
			
		||||
  } l_addr;
 | 
			
		||||
  unsigned short l_lnno;	/* line number		*/
 | 
			
		||||
};
 | 
			
		||||
@@ -156,11 +156,11 @@ struct __attribute__((packed)) external_syment
 | 
			
		||||
  union {
 | 
			
		||||
    char e_name[E_SYMNMLEN];
 | 
			
		||||
    struct {
 | 
			
		||||
      host_ulong e_zeroes;
 | 
			
		||||
      host_ulong e_offset;
 | 
			
		||||
      unsigned long e_zeroes;
 | 
			
		||||
      unsigned long e_offset;
 | 
			
		||||
    } e;
 | 
			
		||||
  } e;
 | 
			
		||||
  host_ulong e_value;
 | 
			
		||||
  unsigned long e_value;
 | 
			
		||||
  unsigned short e_scnum;
 | 
			
		||||
  unsigned short e_type;
 | 
			
		||||
  char e_sclass[1];
 | 
			
		||||
@@ -174,18 +174,18 @@ struct __attribute__((packed)) external_syment
 | 
			
		||||
 | 
			
		||||
union external_auxent {
 | 
			
		||||
  struct {
 | 
			
		||||
    host_ulong x_tagndx;	/* str, un, or enum tag indx */
 | 
			
		||||
    unsigned long 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 */
 | 
			
		||||
      unsigned long 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 */
 | 
			
		||||
	unsigned long x_lnnoptr;/* ptr to fcn line # */
 | 
			
		||||
	unsigned long x_endndx;	/* entry ndx past block end */
 | 
			
		||||
      } x_fcn;
 | 
			
		||||
      struct {			/* if ISARY, up to 4 dimen. */
 | 
			
		||||
	char x_dimen[E_DIMNUM][2];
 | 
			
		||||
@@ -197,22 +197,22 @@ union external_auxent {
 | 
			
		||||
  union {
 | 
			
		||||
    char x_fname[E_FILNMLEN];
 | 
			
		||||
    struct {
 | 
			
		||||
      host_ulong x_zeroes;
 | 
			
		||||
      host_ulong x_offset;
 | 
			
		||||
      unsigned long x_zeroes;
 | 
			
		||||
      unsigned long x_offset;
 | 
			
		||||
    } x_n;
 | 
			
		||||
  } x_file;
 | 
			
		||||
 | 
			
		||||
  struct {
 | 
			
		||||
    host_ulong x_scnlen;	/* section length */
 | 
			
		||||
    unsigned long x_scnlen;	/* section length */
 | 
			
		||||
    unsigned short x_nreloc;	/* # relocation entries */
 | 
			
		||||
    unsigned short x_nlinno;	/* # line numbers */
 | 
			
		||||
    host_ulong x_checksum;	/* section COMDAT checksum */
 | 
			
		||||
    unsigned long 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 long 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)) */
 | 
			
		||||
@@ -344,7 +344,7 @@ struct external_PE_filehdr
 | 
			
		||||
  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 */
 | 
			
		||||
  unsigned long 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 */
 | 
			
		||||
 | 
			
		||||
@@ -352,9 +352,9 @@ struct external_PE_filehdr
 | 
			
		||||
 | 
			
		||||
  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 long f_timdat;	/* time & date stamp		*/
 | 
			
		||||
  unsigned long f_symptr;	/* file pointer to symtab	*/
 | 
			
		||||
  unsigned long f_nsyms;	/* number of symtab entries	*/
 | 
			
		||||
  unsigned short f_opthdr;	/* sizeof(optional hdr)		*/
 | 
			
		||||
  unsigned short f_flags;	/* flags			*/
 | 
			
		||||
};
 | 
			
		||||
@@ -370,17 +370,17 @@ 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 */
 | 
			
		||||
  unsigned long	tsize;		/* text size in bytes, padded to FW bdry*/
 | 
			
		||||
  unsigned long	dsize;		/* initialized data "  "		*/
 | 
			
		||||
  unsigned long	bsize;		/* uninitialized data "   "		*/
 | 
			
		||||
  unsigned long	entry;		/* entry pt.				*/
 | 
			
		||||
  unsigned long text_start;	/* base of text used for this file */
 | 
			
		||||
  unsigned long 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 long  ImageBase;
 | 
			
		||||
  unsigned long  SectionAlignment;
 | 
			
		||||
  unsigned long  FileAlignment;
 | 
			
		||||
  unsigned short  MajorOperatingSystemVersion;
 | 
			
		||||
  unsigned short  MinorOperatingSystemVersion;
 | 
			
		||||
  unsigned short  MajorImageVersion;
 | 
			
		||||
@@ -388,17 +388,17 @@ typedef struct
 | 
			
		||||
  unsigned short  MajorSubsystemVersion;
 | 
			
		||||
  unsigned short  MinorSubsystemVersion;
 | 
			
		||||
  char  Reserved1[4];
 | 
			
		||||
  host_ulong  SizeOfImage;
 | 
			
		||||
  host_ulong  SizeOfHeaders;
 | 
			
		||||
  host_ulong  CheckSum;
 | 
			
		||||
  unsigned long  SizeOfImage;
 | 
			
		||||
  unsigned long  SizeOfHeaders;
 | 
			
		||||
  unsigned long  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;
 | 
			
		||||
  unsigned long  SizeOfStackReserve;
 | 
			
		||||
  unsigned long  SizeOfStackCommit;
 | 
			
		||||
  unsigned long  SizeOfHeapReserve;
 | 
			
		||||
  unsigned long  SizeOfHeapCommit;
 | 
			
		||||
  unsigned long  LoaderFlags;
 | 
			
		||||
  unsigned long  NumberOfRvaAndSizes;
 | 
			
		||||
  /* IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; */
 | 
			
		||||
  char  DataDirectory[16][2][4]; /* 16 entries, 2 elements/entry, 4 chars */
 | 
			
		||||
 | 
			
		||||
@@ -428,3 +428,4 @@ typedef struct
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#endif /* _A_OUT_H_ */
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										185
									
								
								acl.c
									
									
									
									
									
								
							
							
						
						
									
										185
									
								
								acl.c
									
									
									
									
									
								
							@@ -1,185 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * QEMU access control list management
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2009 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-common.h"
 | 
			
		||||
#include "sysemu.h"
 | 
			
		||||
#include "acl.h"
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_FNMATCH
 | 
			
		||||
#include <fnmatch.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static unsigned int nacls = 0;
 | 
			
		||||
static qemu_acl **acls = NULL;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
qemu_acl *qemu_acl_find(const char *aclname)
 | 
			
		||||
{
 | 
			
		||||
    int i;
 | 
			
		||||
    for (i = 0 ; i < nacls ; i++) {
 | 
			
		||||
        if (strcmp(acls[i]->aclname, aclname) == 0)
 | 
			
		||||
            return acls[i];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
qemu_acl *qemu_acl_init(const char *aclname)
 | 
			
		||||
{
 | 
			
		||||
    qemu_acl *acl;
 | 
			
		||||
 | 
			
		||||
    acl = qemu_acl_find(aclname);
 | 
			
		||||
    if (acl)
 | 
			
		||||
        return acl;
 | 
			
		||||
 | 
			
		||||
    acl = qemu_malloc(sizeof(*acl));
 | 
			
		||||
    acl->aclname = qemu_strdup(aclname);
 | 
			
		||||
    /* Deny by default, so there is no window of "open
 | 
			
		||||
     * access" between QEMU starting, and the user setting
 | 
			
		||||
     * up ACLs in the monitor */
 | 
			
		||||
    acl->defaultDeny = 1;
 | 
			
		||||
 | 
			
		||||
    acl->nentries = 0;
 | 
			
		||||
    QTAILQ_INIT(&acl->entries);
 | 
			
		||||
 | 
			
		||||
    acls = qemu_realloc(acls, sizeof(*acls) * (nacls +1));
 | 
			
		||||
    acls[nacls] = acl;
 | 
			
		||||
    nacls++;
 | 
			
		||||
 | 
			
		||||
    return acl;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int qemu_acl_party_is_allowed(qemu_acl *acl,
 | 
			
		||||
                              const char *party)
 | 
			
		||||
{
 | 
			
		||||
    qemu_acl_entry *entry;
 | 
			
		||||
 | 
			
		||||
    QTAILQ_FOREACH(entry, &acl->entries, next) {
 | 
			
		||||
#ifdef CONFIG_FNMATCH
 | 
			
		||||
        if (fnmatch(entry->match, party, 0) == 0)
 | 
			
		||||
            return entry->deny ? 0 : 1;
 | 
			
		||||
#else
 | 
			
		||||
        /* No fnmatch, so fallback to exact string matching
 | 
			
		||||
         * instead of allowing wildcards */
 | 
			
		||||
        if (strcmp(entry->match, party) == 0)
 | 
			
		||||
            return entry->deny ? 0 : 1;
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return acl->defaultDeny ? 0 : 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void qemu_acl_reset(qemu_acl *acl)
 | 
			
		||||
{
 | 
			
		||||
    qemu_acl_entry *entry;
 | 
			
		||||
 | 
			
		||||
    /* Put back to deny by default, so there is no window
 | 
			
		||||
     * of "open access" while the user re-initializes the
 | 
			
		||||
     * access control list */
 | 
			
		||||
    acl->defaultDeny = 1;
 | 
			
		||||
    QTAILQ_FOREACH(entry, &acl->entries, next) {
 | 
			
		||||
        QTAILQ_REMOVE(&acl->entries, entry, next);
 | 
			
		||||
        free(entry->match);
 | 
			
		||||
        free(entry);
 | 
			
		||||
    }
 | 
			
		||||
    acl->nentries = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int qemu_acl_append(qemu_acl *acl,
 | 
			
		||||
                    int deny,
 | 
			
		||||
                    const char *match)
 | 
			
		||||
{
 | 
			
		||||
    qemu_acl_entry *entry;
 | 
			
		||||
 | 
			
		||||
    entry = qemu_malloc(sizeof(*entry));
 | 
			
		||||
    entry->match = qemu_strdup(match);
 | 
			
		||||
    entry->deny = deny;
 | 
			
		||||
 | 
			
		||||
    QTAILQ_INSERT_TAIL(&acl->entries, entry, next);
 | 
			
		||||
    acl->nentries++;
 | 
			
		||||
 | 
			
		||||
    return acl->nentries;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int qemu_acl_insert(qemu_acl *acl,
 | 
			
		||||
                    int deny,
 | 
			
		||||
                    const char *match,
 | 
			
		||||
                    int index)
 | 
			
		||||
{
 | 
			
		||||
    qemu_acl_entry *entry;
 | 
			
		||||
    qemu_acl_entry *tmp;
 | 
			
		||||
    int i = 0;
 | 
			
		||||
 | 
			
		||||
    if (index <= 0)
 | 
			
		||||
        return -1;
 | 
			
		||||
    if (index >= acl->nentries)
 | 
			
		||||
        return qemu_acl_append(acl, deny, match);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    entry = qemu_malloc(sizeof(*entry));
 | 
			
		||||
    entry->match = qemu_strdup(match);
 | 
			
		||||
    entry->deny = deny;
 | 
			
		||||
 | 
			
		||||
    QTAILQ_FOREACH(tmp, &acl->entries, next) {
 | 
			
		||||
        i++;
 | 
			
		||||
        if (i == index) {
 | 
			
		||||
            QTAILQ_INSERT_BEFORE(tmp, entry, next);
 | 
			
		||||
            acl->nentries++;
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return i;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int qemu_acl_remove(qemu_acl *acl,
 | 
			
		||||
                    const char *match)
 | 
			
		||||
{
 | 
			
		||||
    qemu_acl_entry *entry;
 | 
			
		||||
    int i = 0;
 | 
			
		||||
 | 
			
		||||
    QTAILQ_FOREACH(entry, &acl->entries, next) {
 | 
			
		||||
        i++;
 | 
			
		||||
        if (strcmp(entry->match, match) == 0) {
 | 
			
		||||
            QTAILQ_REMOVE(&acl->entries, entry, next);
 | 
			
		||||
            return i;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Local variables:
 | 
			
		||||
 *  c-indent-level: 4
 | 
			
		||||
 *  c-basic-offset: 4
 | 
			
		||||
 *  tab-width: 8
 | 
			
		||||
 * End:
 | 
			
		||||
 */
 | 
			
		||||
							
								
								
									
										74
									
								
								acl.h
									
									
									
									
									
								
							
							
						
						
									
										74
									
								
								acl.h
									
									
									
									
									
								
							@@ -1,74 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * QEMU access control list management
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2009 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.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef __QEMU_ACL_H__
 | 
			
		||||
#define __QEMU_ACL_H__
 | 
			
		||||
 | 
			
		||||
#include "qemu-queue.h"
 | 
			
		||||
 | 
			
		||||
typedef struct qemu_acl_entry qemu_acl_entry;
 | 
			
		||||
typedef struct qemu_acl qemu_acl;
 | 
			
		||||
 | 
			
		||||
struct qemu_acl_entry {
 | 
			
		||||
    char *match;
 | 
			
		||||
    int deny;
 | 
			
		||||
 | 
			
		||||
    QTAILQ_ENTRY(qemu_acl_entry) next;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct qemu_acl {
 | 
			
		||||
    char *aclname;
 | 
			
		||||
    unsigned int nentries;
 | 
			
		||||
    QTAILQ_HEAD(,qemu_acl_entry) entries;
 | 
			
		||||
    int defaultDeny;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
qemu_acl *qemu_acl_init(const char *aclname);
 | 
			
		||||
 | 
			
		||||
qemu_acl *qemu_acl_find(const char *aclname);
 | 
			
		||||
 | 
			
		||||
int qemu_acl_party_is_allowed(qemu_acl *acl,
 | 
			
		||||
			      const char *party);
 | 
			
		||||
 | 
			
		||||
void qemu_acl_reset(qemu_acl *acl);
 | 
			
		||||
 | 
			
		||||
int qemu_acl_append(qemu_acl *acl,
 | 
			
		||||
		    int deny,
 | 
			
		||||
		    const char *match);
 | 
			
		||||
int qemu_acl_insert(qemu_acl *acl,
 | 
			
		||||
		    int deny,
 | 
			
		||||
		    const char *match,
 | 
			
		||||
		    int index);
 | 
			
		||||
int qemu_acl_remove(qemu_acl *acl,
 | 
			
		||||
		    const char *match);
 | 
			
		||||
 | 
			
		||||
#endif /* __QEMU_ACL_H__ */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Local variables:
 | 
			
		||||
 *  c-indent-level: 4
 | 
			
		||||
 *  c-basic-offset: 4
 | 
			
		||||
 *  tab-width: 8
 | 
			
		||||
 * End:
 | 
			
		||||
 */
 | 
			
		||||
							
								
								
									
										9
									
								
								aes.c
									
									
									
									
									
								
							
							
						
						
									
										9
									
								
								aes.c
									
									
									
									
									
								
							@@ -27,17 +27,20 @@
 | 
			
		||||
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 | 
			
		||||
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
 */
 | 
			
		||||
#include "qemu-common.h"
 | 
			
		||||
#include "vl.h"
 | 
			
		||||
#include "aes.h"
 | 
			
		||||
 | 
			
		||||
#ifndef NDEBUG
 | 
			
		||||
#define NDEBUG
 | 
			
		||||
#endif
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
 | 
			
		||||
typedef uint32_t u32;
 | 
			
		||||
typedef uint16_t u16;
 | 
			
		||||
typedef uint8_t u8;
 | 
			
		||||
 | 
			
		||||
#define MAXKC   (256/32)
 | 
			
		||||
#define MAXKB   (256/8)
 | 
			
		||||
#define MAXNR   14
 | 
			
		||||
 | 
			
		||||
/* This controls loop-unrolling in aes_core.c */
 | 
			
		||||
#undef FULL_UNROLL
 | 
			
		||||
# define GETU32(pt) (((u32)(pt)[0] << 24) ^ ((u32)(pt)[1] << 16) ^ ((u32)(pt)[2] <<  8) ^ ((u32)(pt)[3]))
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										230
									
								
								aio.c
									
									
									
									
									
								
							
							
						
						
									
										230
									
								
								aio.c
									
									
									
									
									
								
							@@ -1,230 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * QEMU aio implementation
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright IBM, Corp. 2008
 | 
			
		||||
 *
 | 
			
		||||
 * Authors:
 | 
			
		||||
 *  Anthony Liguori   <aliguori@us.ibm.com>
 | 
			
		||||
 *
 | 
			
		||||
 * This work is licensed under the terms of the GNU GPL, version 2.  See
 | 
			
		||||
 * the COPYING file in the top-level directory.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "qemu-common.h"
 | 
			
		||||
#include "block.h"
 | 
			
		||||
#include "qemu-queue.h"
 | 
			
		||||
#include "qemu_socket.h"
 | 
			
		||||
 | 
			
		||||
typedef struct AioHandler AioHandler;
 | 
			
		||||
 | 
			
		||||
/* The list of registered AIO handlers */
 | 
			
		||||
static QLIST_HEAD(, AioHandler) aio_handlers;
 | 
			
		||||
 | 
			
		||||
/* This is a simple lock used to protect the aio_handlers list.  Specifically,
 | 
			
		||||
 * it's used to ensure that no callbacks are removed while we're walking and
 | 
			
		||||
 * dispatching callbacks.
 | 
			
		||||
 */
 | 
			
		||||
static int walking_handlers;
 | 
			
		||||
 | 
			
		||||
struct AioHandler
 | 
			
		||||
{
 | 
			
		||||
    int fd;
 | 
			
		||||
    IOHandler *io_read;
 | 
			
		||||
    IOHandler *io_write;
 | 
			
		||||
    AioFlushHandler *io_flush;
 | 
			
		||||
    AioProcessQueue *io_process_queue;
 | 
			
		||||
    int deleted;
 | 
			
		||||
    void *opaque;
 | 
			
		||||
    QLIST_ENTRY(AioHandler) node;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static AioHandler *find_aio_handler(int fd)
 | 
			
		||||
{
 | 
			
		||||
    AioHandler *node;
 | 
			
		||||
 | 
			
		||||
    QLIST_FOREACH(node, &aio_handlers, node) {
 | 
			
		||||
        if (node->fd == fd)
 | 
			
		||||
            if (!node->deleted)
 | 
			
		||||
                return node;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int qemu_aio_set_fd_handler(int fd,
 | 
			
		||||
                            IOHandler *io_read,
 | 
			
		||||
                            IOHandler *io_write,
 | 
			
		||||
                            AioFlushHandler *io_flush,
 | 
			
		||||
                            AioProcessQueue *io_process_queue,
 | 
			
		||||
                            void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    AioHandler *node;
 | 
			
		||||
 | 
			
		||||
    node = find_aio_handler(fd);
 | 
			
		||||
 | 
			
		||||
    /* Are we deleting the fd handler? */
 | 
			
		||||
    if (!io_read && !io_write) {
 | 
			
		||||
        if (node) {
 | 
			
		||||
            /* If the lock is held, just mark the node as deleted */
 | 
			
		||||
            if (walking_handlers)
 | 
			
		||||
                node->deleted = 1;
 | 
			
		||||
            else {
 | 
			
		||||
                /* Otherwise, delete it for real.  We can't just mark it as
 | 
			
		||||
                 * deleted because deleted nodes are only cleaned up after
 | 
			
		||||
                 * releasing the walking_handlers lock.
 | 
			
		||||
                 */
 | 
			
		||||
                QLIST_REMOVE(node, node);
 | 
			
		||||
                qemu_free(node);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        if (node == NULL) {
 | 
			
		||||
            /* Alloc and insert if it's not already there */
 | 
			
		||||
            node = qemu_mallocz(sizeof(AioHandler));
 | 
			
		||||
            node->fd = fd;
 | 
			
		||||
            QLIST_INSERT_HEAD(&aio_handlers, node, node);
 | 
			
		||||
        }
 | 
			
		||||
        /* Update handler with latest information */
 | 
			
		||||
        node->io_read = io_read;
 | 
			
		||||
        node->io_write = io_write;
 | 
			
		||||
        node->io_flush = io_flush;
 | 
			
		||||
        node->io_process_queue = io_process_queue;
 | 
			
		||||
        node->opaque = opaque;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    qemu_set_fd_handler2(fd, NULL, io_read, io_write, opaque);
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void qemu_aio_flush(void)
 | 
			
		||||
{
 | 
			
		||||
    AioHandler *node;
 | 
			
		||||
    int ret;
 | 
			
		||||
 | 
			
		||||
    do {
 | 
			
		||||
        ret = 0;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * If there are pending emulated aio start them now so flush
 | 
			
		||||
	 * will be able to return 1.
 | 
			
		||||
	 */
 | 
			
		||||
        qemu_aio_wait();
 | 
			
		||||
 | 
			
		||||
        QLIST_FOREACH(node, &aio_handlers, node) {
 | 
			
		||||
            if (node->io_flush) {
 | 
			
		||||
                ret |= node->io_flush(node->opaque);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    } while (qemu_bh_poll() || ret > 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int qemu_aio_process_queue(void)
 | 
			
		||||
{
 | 
			
		||||
    AioHandler *node;
 | 
			
		||||
    int ret = 0;
 | 
			
		||||
 | 
			
		||||
    walking_handlers = 1;
 | 
			
		||||
 | 
			
		||||
    QLIST_FOREACH(node, &aio_handlers, node) {
 | 
			
		||||
        if (node->io_process_queue) {
 | 
			
		||||
            if (node->io_process_queue(node->opaque)) {
 | 
			
		||||
                ret = 1;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    walking_handlers = 0;
 | 
			
		||||
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void qemu_aio_wait(void)
 | 
			
		||||
{
 | 
			
		||||
    int ret;
 | 
			
		||||
 | 
			
		||||
    if (qemu_bh_poll())
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
     * If there are callbacks left that have been queued, we need to call then.
 | 
			
		||||
     * Return afterwards to avoid waiting needlessly in select().
 | 
			
		||||
     */
 | 
			
		||||
    if (qemu_aio_process_queue())
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    do {
 | 
			
		||||
        AioHandler *node;
 | 
			
		||||
        fd_set rdfds, wrfds;
 | 
			
		||||
        int max_fd = -1;
 | 
			
		||||
 | 
			
		||||
        walking_handlers = 1;
 | 
			
		||||
 | 
			
		||||
        FD_ZERO(&rdfds);
 | 
			
		||||
        FD_ZERO(&wrfds);
 | 
			
		||||
 | 
			
		||||
        /* fill fd sets */
 | 
			
		||||
        QLIST_FOREACH(node, &aio_handlers, node) {
 | 
			
		||||
            /* If there aren't pending AIO operations, don't invoke callbacks.
 | 
			
		||||
             * Otherwise, if there are no AIO requests, qemu_aio_wait() would
 | 
			
		||||
             * wait indefinitely.
 | 
			
		||||
             */
 | 
			
		||||
            if (node->io_flush && node->io_flush(node->opaque) == 0)
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            if (!node->deleted && node->io_read) {
 | 
			
		||||
                FD_SET(node->fd, &rdfds);
 | 
			
		||||
                max_fd = MAX(max_fd, node->fd + 1);
 | 
			
		||||
            }
 | 
			
		||||
            if (!node->deleted && node->io_write) {
 | 
			
		||||
                FD_SET(node->fd, &wrfds);
 | 
			
		||||
                max_fd = MAX(max_fd, node->fd + 1);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        walking_handlers = 0;
 | 
			
		||||
 | 
			
		||||
        /* No AIO operations?  Get us out of here */
 | 
			
		||||
        if (max_fd == -1)
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        /* wait until next event */
 | 
			
		||||
        ret = select(max_fd, &rdfds, &wrfds, NULL, NULL);
 | 
			
		||||
        if (ret == -1 && errno == EINTR)
 | 
			
		||||
            continue;
 | 
			
		||||
 | 
			
		||||
        /* if we have any readable fds, dispatch event */
 | 
			
		||||
        if (ret > 0) {
 | 
			
		||||
            walking_handlers = 1;
 | 
			
		||||
 | 
			
		||||
            /* we have to walk very carefully in case
 | 
			
		||||
             * qemu_aio_set_fd_handler is called while we're walking */
 | 
			
		||||
            node = QLIST_FIRST(&aio_handlers);
 | 
			
		||||
            while (node) {
 | 
			
		||||
                AioHandler *tmp;
 | 
			
		||||
 | 
			
		||||
                if (!node->deleted &&
 | 
			
		||||
                    FD_ISSET(node->fd, &rdfds) &&
 | 
			
		||||
                    node->io_read) {
 | 
			
		||||
                    node->io_read(node->opaque);
 | 
			
		||||
                }
 | 
			
		||||
                if (!node->deleted &&
 | 
			
		||||
                    FD_ISSET(node->fd, &wrfds) &&
 | 
			
		||||
                    node->io_write) {
 | 
			
		||||
                    node->io_write(node->opaque);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                tmp = node;
 | 
			
		||||
                node = QLIST_NEXT(node, node);
 | 
			
		||||
 | 
			
		||||
                if (tmp->deleted) {
 | 
			
		||||
                    QLIST_REMOVE(tmp, node);
 | 
			
		||||
                    qemu_free(tmp);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            walking_handlers = 0;
 | 
			
		||||
        }
 | 
			
		||||
    } while (ret == 0);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										123
									
								
								alpha-dis.c
									
									
									
									
									
								
							
							
						
						
									
										123
									
								
								alpha-dis.c
									
									
									
									
									
								
							@@ -16,8 +16,9 @@ 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 file; see the file COPYING.  If not, see
 | 
			
		||||
<http://www.gnu.org/licenses/>. */
 | 
			
		||||
along with this file; see the file COPYING.  If not, write to the Free
 | 
			
		||||
Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 | 
			
		||||
02111-1307, USA.  */
 | 
			
		||||
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include "dis-asm.h"
 | 
			
		||||
@@ -108,8 +109,8 @@ struct alpha_operand
 | 
			
		||||
     string (the operand will be inserted in any case).  If the
 | 
			
		||||
     operand value is legal, *ERRMSG will be unchanged (most operands
 | 
			
		||||
     can accept any value).  */
 | 
			
		||||
  unsigned (*insert) (unsigned instruction, int op,
 | 
			
		||||
                      const char **errmsg);
 | 
			
		||||
  unsigned (*insert) PARAMS ((unsigned instruction, int op,
 | 
			
		||||
			      const char **errmsg));
 | 
			
		||||
 | 
			
		||||
  /* Extraction function.  This is used by the disassembler.  To
 | 
			
		||||
     extract this operand type from an instruction, check this field.
 | 
			
		||||
@@ -128,7 +129,7 @@ struct alpha_operand
 | 
			
		||||
     non-zero if this operand type can not actually be extracted from
 | 
			
		||||
     this operand (i.e., the instruction does not match).  If the
 | 
			
		||||
     operand is valid, *INVALID will not be changed.  */
 | 
			
		||||
  int (*extract) (unsigned instruction, int *invalid);
 | 
			
		||||
  int (*extract) PARAMS ((unsigned instruction, int *invalid));
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Elements in the table are retrieved by indexing with values from
 | 
			
		||||
@@ -158,7 +159,7 @@ extern const unsigned alpha_num_operands;
 | 
			
		||||
   instructions which want their operands to look like "Ra,disp(Rb)".  */
 | 
			
		||||
#define AXP_OPERAND_PARENS	02
 | 
			
		||||
 | 
			
		||||
/* Used in combination with PARENS, this suppresses the suppression of
 | 
			
		||||
/* Used in combination with PARENS, this supresses the supression of
 | 
			
		||||
   the comma.  This is used for "jmp Ra,(Rb),hint".  */
 | 
			
		||||
#define AXP_OPERAND_COMMA	04
 | 
			
		||||
 | 
			
		||||
@@ -179,7 +180,7 @@ extern const unsigned alpha_num_operands;
 | 
			
		||||
   a flags value of 0 can be treated as end-of-arguments.  */
 | 
			
		||||
#define AXP_OPERAND_UNSIGNED	0200
 | 
			
		||||
 | 
			
		||||
/* Suppress overflow detection on this field.  This is used for hints. */
 | 
			
		||||
/* Supress overflow detection on this field.  This is used for hints. */
 | 
			
		||||
#define AXP_OPERAND_NOOVERFLOW	0400
 | 
			
		||||
 | 
			
		||||
/* Mask for optional argument default value.  */
 | 
			
		||||
@@ -273,23 +274,23 @@ enum bfd_reloc_code_real {
 | 
			
		||||
 | 
			
		||||
/* Local insertion and extraction functions */
 | 
			
		||||
 | 
			
		||||
static unsigned insert_rba (unsigned, int, const char **);
 | 
			
		||||
static unsigned insert_rca (unsigned, int, const char **);
 | 
			
		||||
static unsigned insert_za (unsigned, int, const char **);
 | 
			
		||||
static unsigned insert_zb (unsigned, int, const char **);
 | 
			
		||||
static unsigned insert_zc (unsigned, int, const char **);
 | 
			
		||||
static unsigned insert_bdisp (unsigned, int, const char **);
 | 
			
		||||
static unsigned insert_jhint (unsigned, int, const char **);
 | 
			
		||||
static unsigned insert_ev6hwjhint (unsigned, int, const char **);
 | 
			
		||||
static unsigned insert_rba PARAMS((unsigned, int, const char **));
 | 
			
		||||
static unsigned insert_rca PARAMS((unsigned, int, const char **));
 | 
			
		||||
static unsigned insert_za PARAMS((unsigned, int, const char **));
 | 
			
		||||
static unsigned insert_zb PARAMS((unsigned, int, const char **));
 | 
			
		||||
static unsigned insert_zc PARAMS((unsigned, int, const char **));
 | 
			
		||||
static unsigned insert_bdisp PARAMS((unsigned, int, const char **));
 | 
			
		||||
static unsigned insert_jhint PARAMS((unsigned, int, const char **));
 | 
			
		||||
static unsigned insert_ev6hwjhint PARAMS((unsigned, int, const char **));
 | 
			
		||||
 | 
			
		||||
static int extract_rba (unsigned, int *);
 | 
			
		||||
static int extract_rca (unsigned, int *);
 | 
			
		||||
static int extract_za (unsigned, int *);
 | 
			
		||||
static int extract_zb (unsigned, int *);
 | 
			
		||||
static int extract_zc (unsigned, int *);
 | 
			
		||||
static int extract_bdisp (unsigned, int *);
 | 
			
		||||
static int extract_jhint (unsigned, int *);
 | 
			
		||||
static int extract_ev6hwjhint (unsigned, int *);
 | 
			
		||||
static int extract_rba PARAMS((unsigned, int *));
 | 
			
		||||
static int extract_rca PARAMS((unsigned, int *));
 | 
			
		||||
static int extract_za PARAMS((unsigned, int *));
 | 
			
		||||
static int extract_zb PARAMS((unsigned, int *));
 | 
			
		||||
static int extract_zc PARAMS((unsigned, int *));
 | 
			
		||||
static int extract_bdisp PARAMS((unsigned, int *));
 | 
			
		||||
static int extract_jhint PARAMS((unsigned, int *));
 | 
			
		||||
static int extract_ev6hwjhint PARAMS((unsigned, int *));
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* The operands table  */
 | 
			
		||||
@@ -434,13 +435,18 @@ const unsigned alpha_num_operands = sizeof(alpha_operands)/sizeof(*alpha_operand
 | 
			
		||||
 | 
			
		||||
/*ARGSUSED*/
 | 
			
		||||
static unsigned
 | 
			
		||||
insert_rba(unsigned insn, int value ATTRIBUTE_UNUSED, const char **errmsg ATTRIBUTE_UNUSED)
 | 
			
		||||
insert_rba(insn, value, errmsg)
 | 
			
		||||
     unsigned insn;
 | 
			
		||||
     int value ATTRIBUTE_UNUSED;
 | 
			
		||||
     const char **errmsg ATTRIBUTE_UNUSED;
 | 
			
		||||
{
 | 
			
		||||
  return insn | (((insn >> 21) & 0x1f) << 16);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
extract_rba(unsigned insn, int *invalid)
 | 
			
		||||
extract_rba(insn, invalid)
 | 
			
		||||
     unsigned insn;
 | 
			
		||||
     int *invalid;
 | 
			
		||||
{
 | 
			
		||||
  if (invalid != (int *) NULL
 | 
			
		||||
      && ((insn >> 21) & 0x1f) != ((insn >> 16) & 0x1f))
 | 
			
		||||
@@ -453,13 +459,18 @@ extract_rba(unsigned insn, int *invalid)
 | 
			
		||||
 | 
			
		||||
/*ARGSUSED*/
 | 
			
		||||
static unsigned
 | 
			
		||||
insert_rca(unsigned insn, int value ATTRIBUTE_UNUSED, const char **errmsg ATTRIBUTE_UNUSED)
 | 
			
		||||
insert_rca(insn, value, errmsg)
 | 
			
		||||
     unsigned insn;
 | 
			
		||||
     int value ATTRIBUTE_UNUSED;
 | 
			
		||||
     const char **errmsg ATTRIBUTE_UNUSED;
 | 
			
		||||
{
 | 
			
		||||
  return insn | ((insn >> 21) & 0x1f);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
extract_rca(unsigned insn, int *invalid)
 | 
			
		||||
extract_rca(insn, invalid)
 | 
			
		||||
     unsigned insn;
 | 
			
		||||
     int *invalid;
 | 
			
		||||
{
 | 
			
		||||
  if (invalid != (int *) NULL
 | 
			
		||||
      && ((insn >> 21) & 0x1f) != (insn & 0x1f))
 | 
			
		||||
@@ -472,13 +483,18 @@ extract_rca(unsigned insn, int *invalid)
 | 
			
		||||
 | 
			
		||||
/*ARGSUSED*/
 | 
			
		||||
static unsigned
 | 
			
		||||
insert_za(unsigned insn, int value ATTRIBUTE_UNUSED, const char **errmsg ATTRIBUTE_UNUSED)
 | 
			
		||||
insert_za(insn, value, errmsg)
 | 
			
		||||
     unsigned insn;
 | 
			
		||||
     int value ATTRIBUTE_UNUSED;
 | 
			
		||||
     const char **errmsg ATTRIBUTE_UNUSED;
 | 
			
		||||
{
 | 
			
		||||
  return insn | (31 << 21);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
extract_za(unsigned insn, int *invalid)
 | 
			
		||||
extract_za(insn, invalid)
 | 
			
		||||
     unsigned insn;
 | 
			
		||||
     int *invalid;
 | 
			
		||||
{
 | 
			
		||||
  if (invalid != (int *) NULL && ((insn >> 21) & 0x1f) != 31)
 | 
			
		||||
    *invalid = 1;
 | 
			
		||||
@@ -487,13 +503,18 @@ extract_za(unsigned insn, int *invalid)
 | 
			
		||||
 | 
			
		||||
/*ARGSUSED*/
 | 
			
		||||
static unsigned
 | 
			
		||||
insert_zb(unsigned insn, int value ATTRIBUTE_UNUSED, const char **errmsg ATTRIBUTE_UNUSED)
 | 
			
		||||
insert_zb(insn, value, errmsg)
 | 
			
		||||
     unsigned insn;
 | 
			
		||||
     int value ATTRIBUTE_UNUSED;
 | 
			
		||||
     const char **errmsg ATTRIBUTE_UNUSED;
 | 
			
		||||
{
 | 
			
		||||
  return insn | (31 << 16);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
extract_zb(unsigned insn, int *invalid)
 | 
			
		||||
extract_zb(insn, invalid)
 | 
			
		||||
     unsigned insn;
 | 
			
		||||
     int *invalid;
 | 
			
		||||
{
 | 
			
		||||
  if (invalid != (int *) NULL && ((insn >> 16) & 0x1f) != 31)
 | 
			
		||||
    *invalid = 1;
 | 
			
		||||
@@ -502,13 +523,18 @@ extract_zb(unsigned insn, int *invalid)
 | 
			
		||||
 | 
			
		||||
/*ARGSUSED*/
 | 
			
		||||
static unsigned
 | 
			
		||||
insert_zc(unsigned insn, int value ATTRIBUTE_UNUSED, const char **errmsg ATTRIBUTE_UNUSED)
 | 
			
		||||
insert_zc(insn, value, errmsg)
 | 
			
		||||
     unsigned insn;
 | 
			
		||||
     int value ATTRIBUTE_UNUSED;
 | 
			
		||||
     const char **errmsg ATTRIBUTE_UNUSED;
 | 
			
		||||
{
 | 
			
		||||
  return insn | 31;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
extract_zc(unsigned insn, int *invalid)
 | 
			
		||||
extract_zc(insn, invalid)
 | 
			
		||||
     unsigned insn;
 | 
			
		||||
     int *invalid;
 | 
			
		||||
{
 | 
			
		||||
  if (invalid != (int *) NULL && (insn & 0x1f) != 31)
 | 
			
		||||
    *invalid = 1;
 | 
			
		||||
@@ -519,7 +545,10 @@ extract_zc(unsigned insn, int *invalid)
 | 
			
		||||
/* The displacement field of a Branch format insn.  */
 | 
			
		||||
 | 
			
		||||
static unsigned
 | 
			
		||||
insert_bdisp(unsigned insn, int value, const char **errmsg)
 | 
			
		||||
insert_bdisp(insn, value, errmsg)
 | 
			
		||||
     unsigned insn;
 | 
			
		||||
     int value;
 | 
			
		||||
     const char **errmsg;
 | 
			
		||||
{
 | 
			
		||||
  if (errmsg != (const char **)NULL && (value & 3))
 | 
			
		||||
    *errmsg = _("branch operand unaligned");
 | 
			
		||||
@@ -528,7 +557,9 @@ insert_bdisp(unsigned insn, int value, const char **errmsg)
 | 
			
		||||
 | 
			
		||||
/*ARGSUSED*/
 | 
			
		||||
static int
 | 
			
		||||
extract_bdisp(unsigned insn, int *invalid ATTRIBUTE_UNUSED)
 | 
			
		||||
extract_bdisp(insn, invalid)
 | 
			
		||||
     unsigned insn;
 | 
			
		||||
     int *invalid ATTRIBUTE_UNUSED;
 | 
			
		||||
{
 | 
			
		||||
  return 4 * (((insn & 0x1FFFFF) ^ 0x100000) - 0x100000);
 | 
			
		||||
}
 | 
			
		||||
@@ -537,7 +568,10 @@ extract_bdisp(unsigned insn, int *invalid ATTRIBUTE_UNUSED)
 | 
			
		||||
/* The hint field of a JMP/JSR insn.  */
 | 
			
		||||
 | 
			
		||||
static unsigned
 | 
			
		||||
insert_jhint(unsigned insn, int value, const char **errmsg)
 | 
			
		||||
insert_jhint(insn, value, errmsg)
 | 
			
		||||
     unsigned insn;
 | 
			
		||||
     int value;
 | 
			
		||||
     const char **errmsg;
 | 
			
		||||
{
 | 
			
		||||
  if (errmsg != (const char **)NULL && (value & 3))
 | 
			
		||||
    *errmsg = _("jump hint unaligned");
 | 
			
		||||
@@ -546,7 +580,9 @@ insert_jhint(unsigned insn, int value, const char **errmsg)
 | 
			
		||||
 | 
			
		||||
/*ARGSUSED*/
 | 
			
		||||
static int
 | 
			
		||||
extract_jhint(unsigned insn, int *invalid ATTRIBUTE_UNUSED)
 | 
			
		||||
extract_jhint(insn, invalid)
 | 
			
		||||
     unsigned insn;
 | 
			
		||||
     int *invalid ATTRIBUTE_UNUSED;
 | 
			
		||||
{
 | 
			
		||||
  return 4 * (((insn & 0x3FFF) ^ 0x2000) - 0x2000);
 | 
			
		||||
}
 | 
			
		||||
@@ -554,7 +590,10 @@ extract_jhint(unsigned insn, int *invalid ATTRIBUTE_UNUSED)
 | 
			
		||||
/* The hint field of an EV6 HW_JMP/JSR insn.  */
 | 
			
		||||
 | 
			
		||||
static unsigned
 | 
			
		||||
insert_ev6hwjhint(unsigned insn, int value, const char **errmsg)
 | 
			
		||||
insert_ev6hwjhint(insn, value, errmsg)
 | 
			
		||||
     unsigned insn;
 | 
			
		||||
     int value;
 | 
			
		||||
     const char **errmsg;
 | 
			
		||||
{
 | 
			
		||||
  if (errmsg != (const char **)NULL && (value & 3))
 | 
			
		||||
    *errmsg = _("jump hint unaligned");
 | 
			
		||||
@@ -563,7 +602,9 @@ insert_ev6hwjhint(unsigned insn, int value, const char **errmsg)
 | 
			
		||||
 | 
			
		||||
/*ARGSUSED*/
 | 
			
		||||
static int
 | 
			
		||||
extract_ev6hwjhint(unsigned insn, int *invalid ATTRIBUTE_UNUSED)
 | 
			
		||||
extract_ev6hwjhint(insn, invalid)
 | 
			
		||||
     unsigned insn;
 | 
			
		||||
     int *invalid ATTRIBUTE_UNUSED;
 | 
			
		||||
{
 | 
			
		||||
  return 4 * (((insn & 0x1FFF) ^ 0x1000) - 0x1000);
 | 
			
		||||
}
 | 
			
		||||
@@ -1764,7 +1805,9 @@ static const char * const vms_regnames[64] = {
 | 
			
		||||
/* Disassemble Alpha instructions.  */
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
print_insn_alpha (bfd_vma memaddr, struct disassemble_info *info)
 | 
			
		||||
print_insn_alpha (memaddr, info)
 | 
			
		||||
     bfd_vma memaddr;
 | 
			
		||||
     struct disassemble_info *info;
 | 
			
		||||
{
 | 
			
		||||
  static const struct alpha_opcode *opcode_index[AXP_NOPS+1];
 | 
			
		||||
  const char * const * regnames;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1
									
								
								alpha.ld
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								alpha.ld
									
									
									
									
									
								
							@@ -2,6 +2,7 @@ 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: */
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										645
									
								
								arch_init.c
									
									
									
									
									
								
							
							
						
						
									
										645
									
								
								arch_init.c
									
									
									
									
									
								
							@@ -1,645 +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 <stdint.h>
 | 
			
		||||
#include <stdarg.h>
 | 
			
		||||
#ifndef _WIN32
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <sys/mman.h>
 | 
			
		||||
#endif
 | 
			
		||||
#include "config.h"
 | 
			
		||||
#include "monitor.h"
 | 
			
		||||
#include "sysemu.h"
 | 
			
		||||
#include "arch_init.h"
 | 
			
		||||
#include "audio/audio.h"
 | 
			
		||||
#include "hw/pc.h"
 | 
			
		||||
#include "hw/pci.h"
 | 
			
		||||
#include "hw/audiodev.h"
 | 
			
		||||
#include "kvm.h"
 | 
			
		||||
#include "migration.h"
 | 
			
		||||
#include "net.h"
 | 
			
		||||
#include "gdbstub.h"
 | 
			
		||||
#include "hw/smbios.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 = 15;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
const char arch_config_name[] = CONFIG_QEMU_CONFDIR "/target-" TARGET_ARCH ".conf";
 | 
			
		||||
 | 
			
		||||
#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_M68K)
 | 
			
		||||
#define QEMU_ARCH QEMU_ARCH_M68K
 | 
			
		||||
#elif defined(TARGET_MICROBLAZE)
 | 
			
		||||
#define QEMU_ARCH QEMU_ARCH_MICROBLAZE
 | 
			
		||||
#elif defined(TARGET_MIPS)
 | 
			
		||||
#define QEMU_ARCH QEMU_ARCH_MIPS
 | 
			
		||||
#elif defined(TARGET_PPC)
 | 
			
		||||
#define QEMU_ARCH QEMU_ARCH_PPC
 | 
			
		||||
#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
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
const uint32_t arch_type = QEMU_ARCH;
 | 
			
		||||
 | 
			
		||||
/***********************************************************/
 | 
			
		||||
/* ram save/restore */
 | 
			
		||||
 | 
			
		||||
#define RAM_SAVE_FLAG_FULL	0x01 /* Obsolete, not used anymore */
 | 
			
		||||
#define RAM_SAVE_FLAG_COMPRESS	0x02
 | 
			
		||||
#define RAM_SAVE_FLAG_MEM_SIZE	0x04
 | 
			
		||||
#define RAM_SAVE_FLAG_PAGE	0x08
 | 
			
		||||
#define RAM_SAVE_FLAG_EOS	0x10
 | 
			
		||||
#define RAM_SAVE_FLAG_CONTINUE	0x20
 | 
			
		||||
 | 
			
		||||
static int is_dup_page(uint8_t *page, uint8_t ch)
 | 
			
		||||
{
 | 
			
		||||
    uint32_t val = ch << 24 | ch << 16 | ch << 8 | ch;
 | 
			
		||||
    uint32_t *array = (uint32_t *)page;
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < (TARGET_PAGE_SIZE / 4); i++) {
 | 
			
		||||
        if (array[i] != val) {
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static RAMBlock *last_block;
 | 
			
		||||
static ram_addr_t last_offset;
 | 
			
		||||
 | 
			
		||||
static int ram_save_block(QEMUFile *f)
 | 
			
		||||
{
 | 
			
		||||
    RAMBlock *block = last_block;
 | 
			
		||||
    ram_addr_t offset = last_offset;
 | 
			
		||||
    ram_addr_t current_addr;
 | 
			
		||||
    int bytes_sent = 0;
 | 
			
		||||
 | 
			
		||||
    if (!block)
 | 
			
		||||
        block = QLIST_FIRST(&ram_list.blocks);
 | 
			
		||||
 | 
			
		||||
    current_addr = block->offset + offset;
 | 
			
		||||
 | 
			
		||||
    do {
 | 
			
		||||
        if (cpu_physical_memory_get_dirty(current_addr, MIGRATION_DIRTY_FLAG)) {
 | 
			
		||||
            uint8_t *p;
 | 
			
		||||
            int cont = (block == last_block) ? RAM_SAVE_FLAG_CONTINUE : 0;
 | 
			
		||||
 | 
			
		||||
            cpu_physical_memory_reset_dirty(current_addr,
 | 
			
		||||
                                            current_addr + TARGET_PAGE_SIZE,
 | 
			
		||||
                                            MIGRATION_DIRTY_FLAG);
 | 
			
		||||
 | 
			
		||||
            p = block->host + offset;
 | 
			
		||||
 | 
			
		||||
            if (is_dup_page(p, *p)) {
 | 
			
		||||
                qemu_put_be64(f, offset | cont | RAM_SAVE_FLAG_COMPRESS);
 | 
			
		||||
                if (!cont) {
 | 
			
		||||
                    qemu_put_byte(f, strlen(block->idstr));
 | 
			
		||||
                    qemu_put_buffer(f, (uint8_t *)block->idstr,
 | 
			
		||||
                                    strlen(block->idstr));
 | 
			
		||||
                }
 | 
			
		||||
                qemu_put_byte(f, *p);
 | 
			
		||||
                bytes_sent = 1;
 | 
			
		||||
            } else {
 | 
			
		||||
                qemu_put_be64(f, offset | cont | RAM_SAVE_FLAG_PAGE);
 | 
			
		||||
                if (!cont) {
 | 
			
		||||
                    qemu_put_byte(f, strlen(block->idstr));
 | 
			
		||||
                    qemu_put_buffer(f, (uint8_t *)block->idstr,
 | 
			
		||||
                                    strlen(block->idstr));
 | 
			
		||||
                }
 | 
			
		||||
                qemu_put_buffer(f, p, TARGET_PAGE_SIZE);
 | 
			
		||||
                bytes_sent = TARGET_PAGE_SIZE;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        offset += TARGET_PAGE_SIZE;
 | 
			
		||||
        if (offset >= block->length) {
 | 
			
		||||
            offset = 0;
 | 
			
		||||
            block = QLIST_NEXT(block, next);
 | 
			
		||||
            if (!block)
 | 
			
		||||
                block = QLIST_FIRST(&ram_list.blocks);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        current_addr = block->offset + offset;
 | 
			
		||||
 | 
			
		||||
    } while (current_addr != last_block->offset + last_offset);
 | 
			
		||||
 | 
			
		||||
    last_block = block;
 | 
			
		||||
    last_offset = offset;
 | 
			
		||||
 | 
			
		||||
    return bytes_sent;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static uint64_t bytes_transferred;
 | 
			
		||||
 | 
			
		||||
static ram_addr_t ram_save_remaining(void)
 | 
			
		||||
{
 | 
			
		||||
    RAMBlock *block;
 | 
			
		||||
    ram_addr_t count = 0;
 | 
			
		||||
 | 
			
		||||
    QLIST_FOREACH(block, &ram_list.blocks, next) {
 | 
			
		||||
        ram_addr_t addr;
 | 
			
		||||
        for (addr = block->offset; addr < block->offset + block->length;
 | 
			
		||||
             addr += TARGET_PAGE_SIZE) {
 | 
			
		||||
            if (cpu_physical_memory_get_dirty(addr, MIGRATION_DIRTY_FLAG)) {
 | 
			
		||||
                count++;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return count;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint64_t ram_bytes_remaining(void)
 | 
			
		||||
{
 | 
			
		||||
    return ram_save_remaining() * TARGET_PAGE_SIZE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint64_t ram_bytes_transferred(void)
 | 
			
		||||
{
 | 
			
		||||
    return bytes_transferred;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint64_t ram_bytes_total(void)
 | 
			
		||||
{
 | 
			
		||||
    RAMBlock *block;
 | 
			
		||||
    uint64_t total = 0;
 | 
			
		||||
 | 
			
		||||
    QLIST_FOREACH(block, &ram_list.blocks, next)
 | 
			
		||||
        total += block->length;
 | 
			
		||||
 | 
			
		||||
    return total;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int ram_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    ram_addr_t addr;
 | 
			
		||||
    uint64_t bytes_transferred_last;
 | 
			
		||||
    double bwidth = 0;
 | 
			
		||||
    uint64_t expected_time = 0;
 | 
			
		||||
 | 
			
		||||
    if (stage < 0) {
 | 
			
		||||
        cpu_physical_memory_set_dirty_tracking(0);
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (cpu_physical_sync_dirty_bitmap(0, TARGET_PHYS_ADDR_MAX) != 0) {
 | 
			
		||||
        qemu_file_set_error(f);
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (stage == 1) {
 | 
			
		||||
        RAMBlock *block;
 | 
			
		||||
        bytes_transferred = 0;
 | 
			
		||||
        last_block = NULL;
 | 
			
		||||
        last_offset = 0;
 | 
			
		||||
 | 
			
		||||
        /* Make sure all dirty bits are set */
 | 
			
		||||
        QLIST_FOREACH(block, &ram_list.blocks, next) {
 | 
			
		||||
            for (addr = block->offset; addr < block->offset + block->length;
 | 
			
		||||
                 addr += TARGET_PAGE_SIZE) {
 | 
			
		||||
                if (!cpu_physical_memory_get_dirty(addr,
 | 
			
		||||
                                                   MIGRATION_DIRTY_FLAG)) {
 | 
			
		||||
                    cpu_physical_memory_set_dirty(addr);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* Enable dirty memory tracking */
 | 
			
		||||
        cpu_physical_memory_set_dirty_tracking(1);
 | 
			
		||||
 | 
			
		||||
        qemu_put_be64(f, ram_bytes_total() | RAM_SAVE_FLAG_MEM_SIZE);
 | 
			
		||||
 | 
			
		||||
        QLIST_FOREACH(block, &ram_list.blocks, next) {
 | 
			
		||||
            qemu_put_byte(f, strlen(block->idstr));
 | 
			
		||||
            qemu_put_buffer(f, (uint8_t *)block->idstr, strlen(block->idstr));
 | 
			
		||||
            qemu_put_be64(f, block->length);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bytes_transferred_last = bytes_transferred;
 | 
			
		||||
    bwidth = qemu_get_clock_ns(rt_clock);
 | 
			
		||||
 | 
			
		||||
    while (!qemu_file_rate_limit(f)) {
 | 
			
		||||
        int bytes_sent;
 | 
			
		||||
 | 
			
		||||
        bytes_sent = ram_save_block(f);
 | 
			
		||||
        bytes_transferred += bytes_sent;
 | 
			
		||||
        if (bytes_sent == 0) { /* no more blocks */
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bwidth = qemu_get_clock_ns(rt_clock) - bwidth;
 | 
			
		||||
    bwidth = (bytes_transferred - bytes_transferred_last) / bwidth;
 | 
			
		||||
 | 
			
		||||
    /* if we haven't transferred anything this round, force expected_time to a
 | 
			
		||||
     * a very high value, but without crashing */
 | 
			
		||||
    if (bwidth == 0) {
 | 
			
		||||
        bwidth = 0.000001;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* try transferring iterative blocks of memory */
 | 
			
		||||
    if (stage == 3) {
 | 
			
		||||
        int bytes_sent;
 | 
			
		||||
 | 
			
		||||
        /* flush all remaining blocks regardless of rate limiting */
 | 
			
		||||
        while ((bytes_sent = ram_save_block(f)) != 0) {
 | 
			
		||||
            bytes_transferred += bytes_sent;
 | 
			
		||||
        }
 | 
			
		||||
        cpu_physical_memory_set_dirty_tracking(0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    qemu_put_be64(f, RAM_SAVE_FLAG_EOS);
 | 
			
		||||
 | 
			
		||||
    expected_time = ram_save_remaining() * TARGET_PAGE_SIZE / bwidth;
 | 
			
		||||
 | 
			
		||||
    return (stage == 2) && (expected_time <= migrate_max_downtime());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void *host_from_stream_offset(QEMUFile *f,
 | 
			
		||||
                                            ram_addr_t offset,
 | 
			
		||||
                                            int flags)
 | 
			
		||||
{
 | 
			
		||||
    static RAMBlock *block = NULL;
 | 
			
		||||
    char id[256];
 | 
			
		||||
    uint8_t len;
 | 
			
		||||
 | 
			
		||||
    if (flags & RAM_SAVE_FLAG_CONTINUE) {
 | 
			
		||||
        if (!block) {
 | 
			
		||||
            fprintf(stderr, "Ack, bad migration stream!\n");
 | 
			
		||||
            return NULL;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return block->host + offset;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    len = qemu_get_byte(f);
 | 
			
		||||
    qemu_get_buffer(f, (uint8_t *)id, len);
 | 
			
		||||
    id[len] = 0;
 | 
			
		||||
 | 
			
		||||
    QLIST_FOREACH(block, &ram_list.blocks, next) {
 | 
			
		||||
        if (!strncmp(id, block->idstr, sizeof(id)))
 | 
			
		||||
            return block->host + offset;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fprintf(stderr, "Can't find block %s!\n", id);
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int ram_load(QEMUFile *f, void *opaque, int version_id)
 | 
			
		||||
{
 | 
			
		||||
    ram_addr_t addr;
 | 
			
		||||
    int flags;
 | 
			
		||||
 | 
			
		||||
    if (version_id < 3 || version_id > 4) {
 | 
			
		||||
        return -EINVAL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    do {
 | 
			
		||||
        addr = qemu_get_be64(f);
 | 
			
		||||
 | 
			
		||||
        flags = addr & ~TARGET_PAGE_MASK;
 | 
			
		||||
        addr &= TARGET_PAGE_MASK;
 | 
			
		||||
 | 
			
		||||
        if (flags & RAM_SAVE_FLAG_MEM_SIZE) {
 | 
			
		||||
            if (version_id == 3) {
 | 
			
		||||
                if (addr != ram_bytes_total()) {
 | 
			
		||||
                    return -EINVAL;
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                /* Synchronize RAM block list */
 | 
			
		||||
                char id[256];
 | 
			
		||||
                ram_addr_t length;
 | 
			
		||||
                ram_addr_t total_ram_bytes = addr;
 | 
			
		||||
 | 
			
		||||
                while (total_ram_bytes) {
 | 
			
		||||
                    RAMBlock *block;
 | 
			
		||||
                    uint8_t len;
 | 
			
		||||
 | 
			
		||||
                    len = qemu_get_byte(f);
 | 
			
		||||
                    qemu_get_buffer(f, (uint8_t *)id, len);
 | 
			
		||||
                    id[len] = 0;
 | 
			
		||||
                    length = qemu_get_be64(f);
 | 
			
		||||
 | 
			
		||||
                    QLIST_FOREACH(block, &ram_list.blocks, next) {
 | 
			
		||||
                        if (!strncmp(id, block->idstr, sizeof(id))) {
 | 
			
		||||
                            if (block->length != length)
 | 
			
		||||
                                return -EINVAL;
 | 
			
		||||
                            break;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    if (!block) {
 | 
			
		||||
                        fprintf(stderr, "Unknown ramblock \"%s\", cannot "
 | 
			
		||||
                                "accept migration\n", id);
 | 
			
		||||
                        return -EINVAL;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    total_ram_bytes -= length;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (flags & RAM_SAVE_FLAG_COMPRESS) {
 | 
			
		||||
            void *host;
 | 
			
		||||
            uint8_t ch;
 | 
			
		||||
 | 
			
		||||
            if (version_id == 3)
 | 
			
		||||
                host = qemu_get_ram_ptr(addr);
 | 
			
		||||
            else
 | 
			
		||||
                host = host_from_stream_offset(f, addr, flags);
 | 
			
		||||
 | 
			
		||||
            ch = qemu_get_byte(f);
 | 
			
		||||
            memset(host, ch, TARGET_PAGE_SIZE);
 | 
			
		||||
#ifndef _WIN32
 | 
			
		||||
            if (ch == 0 &&
 | 
			
		||||
                (!kvm_enabled() || kvm_has_sync_mmu())) {
 | 
			
		||||
                madvise(host, TARGET_PAGE_SIZE, MADV_DONTNEED);
 | 
			
		||||
            }
 | 
			
		||||
#endif
 | 
			
		||||
        } else if (flags & RAM_SAVE_FLAG_PAGE) {
 | 
			
		||||
            void *host;
 | 
			
		||||
 | 
			
		||||
            if (version_id == 3)
 | 
			
		||||
                host = qemu_get_ram_ptr(addr);
 | 
			
		||||
            else
 | 
			
		||||
                host = host_from_stream_offset(f, addr, flags);
 | 
			
		||||
 | 
			
		||||
            qemu_get_buffer(f, host, TARGET_PAGE_SIZE);
 | 
			
		||||
        }
 | 
			
		||||
        if (qemu_file_has_error(f)) {
 | 
			
		||||
            return -EIO;
 | 
			
		||||
        }
 | 
			
		||||
    } while (!(flags & RAM_SAVE_FLAG_EOS));
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void qemu_service_io(void)
 | 
			
		||||
{
 | 
			
		||||
    qemu_notify_event();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef HAS_AUDIO
 | 
			
		||||
struct soundhw soundhw[] = {
 | 
			
		||||
#ifdef HAS_AUDIO_CHOICE
 | 
			
		||||
#if defined(TARGET_I386) || defined(TARGET_MIPS)
 | 
			
		||||
    {
 | 
			
		||||
        "pcspk",
 | 
			
		||||
        "PC speaker",
 | 
			
		||||
        0,
 | 
			
		||||
        1,
 | 
			
		||||
        { .init_isa = pcspk_audio_init }
 | 
			
		||||
    },
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_SB16
 | 
			
		||||
    {
 | 
			
		||||
        "sb16",
 | 
			
		||||
        "Creative Sound Blaster 16",
 | 
			
		||||
        0,
 | 
			
		||||
        1,
 | 
			
		||||
        { .init_isa = SB16_init }
 | 
			
		||||
    },
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_CS4231A
 | 
			
		||||
    {
 | 
			
		||||
        "cs4231a",
 | 
			
		||||
        "CS4231A",
 | 
			
		||||
        0,
 | 
			
		||||
        1,
 | 
			
		||||
        { .init_isa = cs4231a_init }
 | 
			
		||||
    },
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_ADLIB
 | 
			
		||||
    {
 | 
			
		||||
        "adlib",
 | 
			
		||||
#ifdef HAS_YMF262
 | 
			
		||||
        "Yamaha YMF262 (OPL3)",
 | 
			
		||||
#else
 | 
			
		||||
        "Yamaha YM3812 (OPL2)",
 | 
			
		||||
#endif
 | 
			
		||||
        0,
 | 
			
		||||
        1,
 | 
			
		||||
        { .init_isa = Adlib_init }
 | 
			
		||||
    },
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_GUS
 | 
			
		||||
    {
 | 
			
		||||
        "gus",
 | 
			
		||||
        "Gravis Ultrasound GF1",
 | 
			
		||||
        0,
 | 
			
		||||
        1,
 | 
			
		||||
        { .init_isa = GUS_init }
 | 
			
		||||
    },
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_AC97
 | 
			
		||||
    {
 | 
			
		||||
        "ac97",
 | 
			
		||||
        "Intel 82801AA AC97 Audio",
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
        { .init_pci = ac97_init }
 | 
			
		||||
    },
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_ES1370
 | 
			
		||||
    {
 | 
			
		||||
        "es1370",
 | 
			
		||||
        "ENSONIQ AudioPCI ES1370",
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
        { .init_pci = es1370_init }
 | 
			
		||||
    },
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#endif /* HAS_AUDIO_CHOICE */
 | 
			
		||||
 | 
			
		||||
    { NULL, NULL, 0, 0, { NULL } }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void select_soundhw(const char *optarg)
 | 
			
		||||
{
 | 
			
		||||
    struct soundhw *c;
 | 
			
		||||
 | 
			
		||||
    if (*optarg == '?') {
 | 
			
		||||
    show_valid_cards:
 | 
			
		||||
 | 
			
		||||
        printf("Valid sound card names (comma separated):\n");
 | 
			
		||||
        for (c = soundhw; c->name; ++c) {
 | 
			
		||||
            printf ("%-11s %s\n", c->name, c->descr);
 | 
			
		||||
        }
 | 
			
		||||
        printf("\n-soundhw all will enable all of the above\n");
 | 
			
		||||
        exit(*optarg != '?');
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
        size_t l;
 | 
			
		||||
        const char *p;
 | 
			
		||||
        char *e;
 | 
			
		||||
        int bad_card = 0;
 | 
			
		||||
 | 
			
		||||
        if (!strcmp(optarg, "all")) {
 | 
			
		||||
            for (c = soundhw; c->name; ++c) {
 | 
			
		||||
                c->enabled = 1;
 | 
			
		||||
            }
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        p = optarg;
 | 
			
		||||
        while (*p) {
 | 
			
		||||
            e = strchr(p, ',');
 | 
			
		||||
            l = !e ? strlen(p) : (size_t) (e - p);
 | 
			
		||||
 | 
			
		||||
            for (c = soundhw; c->name; ++c) {
 | 
			
		||||
                if (!strncmp(c->name, p, l) && !c->name[l]) {
 | 
			
		||||
                    c->enabled = 1;
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (!c->name) {
 | 
			
		||||
                if (l > 80) {
 | 
			
		||||
                    fprintf(stderr,
 | 
			
		||||
                            "Unknown sound card name (too big to show)\n");
 | 
			
		||||
                }
 | 
			
		||||
                else {
 | 
			
		||||
                    fprintf(stderr, "Unknown sound card name `%.*s'\n",
 | 
			
		||||
                            (int) l, p);
 | 
			
		||||
                }
 | 
			
		||||
                bad_card = 1;
 | 
			
		||||
            }
 | 
			
		||||
            p += l + (e != NULL);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (bad_card) {
 | 
			
		||||
            goto show_valid_cards;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
#else
 | 
			
		||||
void select_soundhw(const char *optarg)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
int qemu_uuid_parse(const char *str, uint8_t *uuid)
 | 
			
		||||
{
 | 
			
		||||
    int ret;
 | 
			
		||||
 | 
			
		||||
    if (strlen(str) != 36) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ret = sscanf(str, UUID_FMT, &uuid[0], &uuid[1], &uuid[2], &uuid[3],
 | 
			
		||||
                 &uuid[4], &uuid[5], &uuid[6], &uuid[7], &uuid[8], &uuid[9],
 | 
			
		||||
                 &uuid[10], &uuid[11], &uuid[12], &uuid[13], &uuid[14],
 | 
			
		||||
                 &uuid[15]);
 | 
			
		||||
 | 
			
		||||
    if (ret != 16) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
#ifdef TARGET_I386
 | 
			
		||||
    smbios_add_field(1, offsetof(struct smbios_type_1, uuid), 16, uuid);
 | 
			
		||||
#endif
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void do_acpitable_option(const char *optarg)
 | 
			
		||||
{
 | 
			
		||||
#ifdef TARGET_I386
 | 
			
		||||
    if (acpi_table_add(optarg) < 0) {
 | 
			
		||||
        fprintf(stderr, "Wrong acpi table provided\n");
 | 
			
		||||
        exit(1);
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void do_smbios_option(const char *optarg)
 | 
			
		||||
{
 | 
			
		||||
#ifdef TARGET_I386
 | 
			
		||||
    if (smbios_entry_add(optarg) < 0) {
 | 
			
		||||
        fprintf(stderr, "Wrong smbios provided\n");
 | 
			
		||||
        exit(1);
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cpudef_init(void)
 | 
			
		||||
{
 | 
			
		||||
#if defined(cpudef_setup)
 | 
			
		||||
    cpudef_setup(); /* parse cpu definitions in target config file */
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int audio_available(void)
 | 
			
		||||
{
 | 
			
		||||
#ifdef HAS_AUDIO
 | 
			
		||||
    return 1;
 | 
			
		||||
#else
 | 
			
		||||
    return 0;
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										33
									
								
								arch_init.h
									
									
									
									
									
								
							
							
						
						
									
										33
									
								
								arch_init.h
									
									
									
									
									
								
							@@ -1,33 +0,0 @@
 | 
			
		||||
#ifndef QEMU_ARCH_INIT_H
 | 
			
		||||
#define QEMU_ARCH_INIT_H
 | 
			
		||||
 | 
			
		||||
extern const char arch_config_name[];
 | 
			
		||||
 | 
			
		||||
enum {
 | 
			
		||||
    QEMU_ARCH_ALL = -1,
 | 
			
		||||
    QEMU_ARCH_ALPHA = 1,
 | 
			
		||||
    QEMU_ARCH_ARM = 2,
 | 
			
		||||
    QEMU_ARCH_CRIS = 4,
 | 
			
		||||
    QEMU_ARCH_I386 = 8,
 | 
			
		||||
    QEMU_ARCH_M68K = 16,
 | 
			
		||||
    QEMU_ARCH_MICROBLAZE = 32,
 | 
			
		||||
    QEMU_ARCH_MIPS = 64,
 | 
			
		||||
    QEMU_ARCH_PPC = 128,
 | 
			
		||||
    QEMU_ARCH_S390X = 256,
 | 
			
		||||
    QEMU_ARCH_SH4 = 512,
 | 
			
		||||
    QEMU_ARCH_SPARC = 1024,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
extern const uint32_t arch_type;
 | 
			
		||||
 | 
			
		||||
void select_soundhw(const char *optarg);
 | 
			
		||||
int ram_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque);
 | 
			
		||||
int ram_load(QEMUFile *f, void *opaque, int version_id);
 | 
			
		||||
void do_acpitable_option(const char *optarg);
 | 
			
		||||
void do_smbios_option(const char *optarg);
 | 
			
		||||
void cpudef_init(void);
 | 
			
		||||
int audio_available(void);
 | 
			
		||||
int kvm_available(void);
 | 
			
		||||
int xen_available(void);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										469
									
								
								arm-semi.c
									
									
									
									
									
								
							
							
						
						
									
										469
									
								
								arm-semi.c
									
									
									
									
									
								
							@@ -1,469 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  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, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#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);
 | 
			
		||||
            return ret;
 | 
			
		||||
        }
 | 
			
		||||
    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:
 | 
			
		||||
        gdb_exit(env, 0);
 | 
			
		||||
        exit(0);
 | 
			
		||||
    default:
 | 
			
		||||
        fprintf(stderr, "qemu: Unsupported SemiHosting SWI 0x%02x\n", nr);
 | 
			
		||||
        cpu_dump_state(env, stderr, fprintf, 0);
 | 
			
		||||
        abort();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										27
									
								
								arm.ld
									
									
									
									
									
								
							
							
						
						
									
										27
									
								
								arm.ld
									
									
									
									
									
								
							@@ -2,6 +2,7 @@ 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: */
 | 
			
		||||
@@ -52,43 +53,17 @@ SECTIONS
 | 
			
		||||
  .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    :
 | 
			
		||||
  {
 | 
			
		||||
    *(.gen_code)
 | 
			
		||||
    *(.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)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										216
									
								
								async.c
									
									
									
									
									
								
							
							
						
						
									
										216
									
								
								async.c
									
									
									
									
									
								
							@@ -1,216 +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-common.h"
 | 
			
		||||
#include "qemu-aio.h"
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * An AsyncContext protects the callbacks of AIO requests and Bottom Halves
 | 
			
		||||
 * against interfering with each other. A typical example is qcow2 that accepts
 | 
			
		||||
 * asynchronous requests, but relies for manipulation of its metadata on
 | 
			
		||||
 * synchronous bdrv_read/write that doesn't trigger any callbacks.
 | 
			
		||||
 *
 | 
			
		||||
 * However, these functions are often emulated using AIO which means that AIO
 | 
			
		||||
 * callbacks must be run - but at the same time we must not run callbacks of
 | 
			
		||||
 * other requests as they might start to modify metadata and corrupt the
 | 
			
		||||
 * internal state of the caller of bdrv_read/write.
 | 
			
		||||
 *
 | 
			
		||||
 * To achieve the desired semantics we switch into a new AsyncContext.
 | 
			
		||||
 * Callbacks must only be run if they belong to the current AsyncContext.
 | 
			
		||||
 * Otherwise they need to be queued until their own context is active again.
 | 
			
		||||
 * This is how you can make qemu_aio_wait() wait only for your own callbacks.
 | 
			
		||||
 *
 | 
			
		||||
 * The AsyncContexts form a stack. When you leave a AsyncContexts, you always
 | 
			
		||||
 * return to the old ("parent") context.
 | 
			
		||||
 */
 | 
			
		||||
struct AsyncContext {
 | 
			
		||||
    /* Consecutive number of the AsyncContext (position in the stack) */
 | 
			
		||||
    int id;
 | 
			
		||||
 | 
			
		||||
    /* Anchor of the list of Bottom Halves belonging to the context */
 | 
			
		||||
    struct QEMUBH *first_bh;
 | 
			
		||||
 | 
			
		||||
    /* Link to parent context */
 | 
			
		||||
    struct AsyncContext *parent;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* The currently active AsyncContext */
 | 
			
		||||
static struct AsyncContext *async_context = &(struct AsyncContext) { 0 };
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Enter a new AsyncContext. Already scheduled Bottom Halves and AIO callbacks
 | 
			
		||||
 * won't be called until this context is left again.
 | 
			
		||||
 */
 | 
			
		||||
void async_context_push(void)
 | 
			
		||||
{
 | 
			
		||||
    struct AsyncContext *new = qemu_mallocz(sizeof(*new));
 | 
			
		||||
    new->parent = async_context;
 | 
			
		||||
    new->id = async_context->id + 1;
 | 
			
		||||
    async_context = new;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Run queued AIO completions and destroy Bottom Half */
 | 
			
		||||
static void bh_run_aio_completions(void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    QEMUBH **bh = opaque;
 | 
			
		||||
    qemu_bh_delete(*bh);
 | 
			
		||||
    qemu_free(bh);
 | 
			
		||||
    qemu_aio_process_queue();
 | 
			
		||||
}
 | 
			
		||||
/*
 | 
			
		||||
 * Leave the currently active AsyncContext. All Bottom Halves belonging to the
 | 
			
		||||
 * old context are executed before changing the context.
 | 
			
		||||
 */
 | 
			
		||||
void async_context_pop(void)
 | 
			
		||||
{
 | 
			
		||||
    struct AsyncContext *old = async_context;
 | 
			
		||||
    QEMUBH **bh;
 | 
			
		||||
 | 
			
		||||
    /* Flush the bottom halves, we don't want to lose them */
 | 
			
		||||
    while (qemu_bh_poll());
 | 
			
		||||
 | 
			
		||||
    /* Switch back to the parent context */
 | 
			
		||||
    async_context = async_context->parent;
 | 
			
		||||
    qemu_free(old);
 | 
			
		||||
 | 
			
		||||
    if (async_context == NULL) {
 | 
			
		||||
        abort();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Schedule BH to run any queued AIO completions as soon as possible */
 | 
			
		||||
    bh = qemu_malloc(sizeof(*bh));
 | 
			
		||||
    *bh = qemu_bh_new(bh_run_aio_completions, bh);
 | 
			
		||||
    qemu_bh_schedule(*bh);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Returns the ID of the currently active AsyncContext
 | 
			
		||||
 */
 | 
			
		||||
int get_async_context_id(void)
 | 
			
		||||
{
 | 
			
		||||
    return async_context->id;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/***********************************************************/
 | 
			
		||||
/* bottom halves (can be seen as timers which expire ASAP) */
 | 
			
		||||
 | 
			
		||||
struct QEMUBH {
 | 
			
		||||
    QEMUBHFunc *cb;
 | 
			
		||||
    void *opaque;
 | 
			
		||||
    int scheduled;
 | 
			
		||||
    int idle;
 | 
			
		||||
    int deleted;
 | 
			
		||||
    QEMUBH *next;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
QEMUBH *qemu_bh_new(QEMUBHFunc *cb, void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    QEMUBH *bh;
 | 
			
		||||
    bh = qemu_mallocz(sizeof(QEMUBH));
 | 
			
		||||
    bh->cb = cb;
 | 
			
		||||
    bh->opaque = opaque;
 | 
			
		||||
    bh->next = async_context->first_bh;
 | 
			
		||||
    async_context->first_bh = bh;
 | 
			
		||||
    return bh;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int qemu_bh_poll(void)
 | 
			
		||||
{
 | 
			
		||||
    QEMUBH *bh, **bhp;
 | 
			
		||||
    int ret;
 | 
			
		||||
 | 
			
		||||
    ret = 0;
 | 
			
		||||
    for (bh = async_context->first_bh; bh; bh = bh->next) {
 | 
			
		||||
        if (!bh->deleted && bh->scheduled) {
 | 
			
		||||
            bh->scheduled = 0;
 | 
			
		||||
            if (!bh->idle)
 | 
			
		||||
                ret = 1;
 | 
			
		||||
            bh->idle = 0;
 | 
			
		||||
            bh->cb(bh->opaque);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* remove deleted bhs */
 | 
			
		||||
    bhp = &async_context->first_bh;
 | 
			
		||||
    while (*bhp) {
 | 
			
		||||
        bh = *bhp;
 | 
			
		||||
        if (bh->deleted) {
 | 
			
		||||
            *bhp = bh->next;
 | 
			
		||||
            qemu_free(bh);
 | 
			
		||||
        } else
 | 
			
		||||
            bhp = &bh->next;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void qemu_bh_schedule_idle(QEMUBH *bh)
 | 
			
		||||
{
 | 
			
		||||
    if (bh->scheduled)
 | 
			
		||||
        return;
 | 
			
		||||
    bh->scheduled = 1;
 | 
			
		||||
    bh->idle = 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void qemu_bh_schedule(QEMUBH *bh)
 | 
			
		||||
{
 | 
			
		||||
    if (bh->scheduled)
 | 
			
		||||
        return;
 | 
			
		||||
    bh->scheduled = 1;
 | 
			
		||||
    bh->idle = 0;
 | 
			
		||||
    /* stop the currently executing CPU to execute the BH ASAP */
 | 
			
		||||
    qemu_notify_event();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void qemu_bh_cancel(QEMUBH *bh)
 | 
			
		||||
{
 | 
			
		||||
    bh->scheduled = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void qemu_bh_delete(QEMUBH *bh)
 | 
			
		||||
{
 | 
			
		||||
    bh->scheduled = 0;
 | 
			
		||||
    bh->deleted = 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void qemu_bh_update_timeout(int *timeout)
 | 
			
		||||
{
 | 
			
		||||
    QEMUBH *bh;
 | 
			
		||||
 | 
			
		||||
    for (bh = async_context->first_bh; bh; bh = bh->next) {
 | 
			
		||||
        if (!bh->deleted && bh->scheduled) {
 | 
			
		||||
            if (bh->idle) {
 | 
			
		||||
                /* idle bottom halves will be polled at least
 | 
			
		||||
                 * every 10ms */
 | 
			
		||||
                *timeout = MIN(10, *timeout);
 | 
			
		||||
            } else {
 | 
			
		||||
                /* non-idle bottom halves will be executed
 | 
			
		||||
                 * immediately */
 | 
			
		||||
                *timeout = 0;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										1107
									
								
								audio/audio.c
									
									
									
									
									
								
							
							
						
						
									
										1107
									
								
								audio/audio.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -24,62 +24,31 @@
 | 
			
		||||
#ifndef QEMU_AUDIO_H
 | 
			
		||||
#define QEMU_AUDIO_H
 | 
			
		||||
 | 
			
		||||
#include "config-host.h"
 | 
			
		||||
#include "qemu-queue.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 {
 | 
			
		||||
    AUD_FMT_U8,
 | 
			
		||||
    AUD_FMT_S8,
 | 
			
		||||
    AUD_FMT_U16,
 | 
			
		||||
    AUD_FMT_S16,
 | 
			
		||||
    AUD_FMT_U32,
 | 
			
		||||
    AUD_FMT_S32
 | 
			
		||||
    AUD_FMT_S16
 | 
			
		||||
} audfmt_e;
 | 
			
		||||
 | 
			
		||||
#ifdef HOST_WORDS_BIGENDIAN
 | 
			
		||||
#define AUDIO_HOST_ENDIANNESS 1
 | 
			
		||||
#else
 | 
			
		||||
#define AUDIO_HOST_ENDIANNESS 0
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
struct audsettings {
 | 
			
		||||
typedef struct {
 | 
			
		||||
    int freq;
 | 
			
		||||
    int nchannels;
 | 
			
		||||
    audfmt_e fmt;
 | 
			
		||||
    int endianness;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef enum {
 | 
			
		||||
    AUD_CNOTIFY_ENABLE,
 | 
			
		||||
    AUD_CNOTIFY_DISABLE
 | 
			
		||||
} audcnotification_e;
 | 
			
		||||
 | 
			
		||||
struct audio_capture_ops {
 | 
			
		||||
    void (*notify) (void *opaque, audcnotification_e cmd);
 | 
			
		||||
    void (*capture) (void *opaque, void *buf, int size);
 | 
			
		||||
    void (*destroy) (void *opaque);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct capture_ops {
 | 
			
		||||
    void (*info) (void *opaque);
 | 
			
		||||
    void (*destroy) (void *opaque);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef struct CaptureState {
 | 
			
		||||
    void *opaque;
 | 
			
		||||
    struct capture_ops ops;
 | 
			
		||||
    QLIST_ENTRY (CaptureState) entries;
 | 
			
		||||
} CaptureState;
 | 
			
		||||
} audsettings_t;
 | 
			
		||||
 | 
			
		||||
typedef struct AudioState AudioState;
 | 
			
		||||
typedef struct SWVoiceOut SWVoiceOut;
 | 
			
		||||
typedef struct CaptureVoiceOut CaptureVoiceOut;
 | 
			
		||||
typedef struct SWVoiceIn SWVoiceIn;
 | 
			
		||||
 | 
			
		||||
typedef struct QEMUSoundCard {
 | 
			
		||||
    AudioState *audio;
 | 
			
		||||
    char *name;
 | 
			
		||||
    QLIST_ENTRY (QEMUSoundCard) entries;
 | 
			
		||||
    LIST_ENTRY (QEMUSoundCard) entries;
 | 
			
		||||
} QEMUSoundCard;
 | 
			
		||||
 | 
			
		||||
typedef struct QEMUAudioTimeStamp {
 | 
			
		||||
@@ -93,23 +62,19 @@ void AUD_log (const char *cap, const char *fmt, ...)
 | 
			
		||||
#endif
 | 
			
		||||
    ;
 | 
			
		||||
 | 
			
		||||
AudioState *AUD_init (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);
 | 
			
		||||
CaptureVoiceOut *AUD_add_capture (
 | 
			
		||||
    struct audsettings *as,
 | 
			
		||||
    struct audio_capture_ops *ops,
 | 
			
		||||
    void *opaque
 | 
			
		||||
    );
 | 
			
		||||
void AUD_del_capture (CaptureVoiceOut *cap, void *cb_opaque);
 | 
			
		||||
 | 
			
		||||
SWVoiceOut *AUD_open_out (
 | 
			
		||||
    QEMUSoundCard *card,
 | 
			
		||||
    SWVoiceOut *sw,
 | 
			
		||||
    const char *name,
 | 
			
		||||
    void *callback_opaque,
 | 
			
		||||
    audio_callback_fn callback_fn,
 | 
			
		||||
    struct audsettings *settings
 | 
			
		||||
    audio_callback_fn_t callback_fn,
 | 
			
		||||
    audsettings_t *settings,
 | 
			
		||||
    int sw_endian
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
void AUD_close_out (QEMUSoundCard *card, SWVoiceOut *sw);
 | 
			
		||||
@@ -121,16 +86,14 @@ int  AUD_is_active_out (SWVoiceOut *sw);
 | 
			
		||||
void     AUD_init_time_stamp_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 (
 | 
			
		||||
    QEMUSoundCard *card,
 | 
			
		||||
    SWVoiceIn *sw,
 | 
			
		||||
    const char *name,
 | 
			
		||||
    void *callback_opaque,
 | 
			
		||||
    audio_callback_fn callback_fn,
 | 
			
		||||
    struct audsettings *settings
 | 
			
		||||
    audio_callback_fn_t callback_fn,
 | 
			
		||||
    audsettings_t *settings,
 | 
			
		||||
    int sw_endian
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
void AUD_close_in (QEMUSoundCard *card, SWVoiceIn *sw);
 | 
			
		||||
@@ -147,6 +110,9 @@ static inline void *advance (void *p, int incr)
 | 
			
		||||
    return (d + incr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint32_t popcount (uint32_t u);
 | 
			
		||||
inline uint32_t lsbindex (uint32_t u);
 | 
			
		||||
 | 
			
		||||
#ifdef __GNUC__
 | 
			
		||||
#define audio_MIN(a, b) ( __extension__ ({      \
 | 
			
		||||
    __typeof (a) ta = a;                        \
 | 
			
		||||
@@ -164,7 +130,4 @@ static inline void *advance (void *p, int incr)
 | 
			
		||||
#define audio_MAX(a, b) ((a)<(b)?(b):(a))
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
int wav_start_capture (CaptureState *s, const char *path, int freq,
 | 
			
		||||
                       int bits, int nchannels);
 | 
			
		||||
 | 
			
		||||
#endif  /* audio.h */
 | 
			
		||||
 
 | 
			
		||||
@@ -44,13 +44,13 @@ struct audio_option {
 | 
			
		||||
    audio_option_tag_e tag;
 | 
			
		||||
    void *valp;
 | 
			
		||||
    const char *descr;
 | 
			
		||||
    int *overriddenp;
 | 
			
		||||
    int overridden;
 | 
			
		||||
    int *overridenp;
 | 
			
		||||
    int overriden;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct audio_callback {
 | 
			
		||||
    void *opaque;
 | 
			
		||||
    audio_callback_fn fn;
 | 
			
		||||
    audio_callback_fn_t fn;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct audio_pcm_info {
 | 
			
		||||
@@ -61,15 +61,13 @@ struct audio_pcm_info {
 | 
			
		||||
    int align;
 | 
			
		||||
    int shift;
 | 
			
		||||
    int bytes_per_second;
 | 
			
		||||
    int swap_endianness;
 | 
			
		||||
    int swap_endian;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef struct SWVoiceCap SWVoiceCap;
 | 
			
		||||
 | 
			
		||||
typedef struct HWVoiceOut {
 | 
			
		||||
    int enabled;
 | 
			
		||||
    int poll_mode;
 | 
			
		||||
    int pending_disable;
 | 
			
		||||
    int valid;
 | 
			
		||||
    struct audio_pcm_info info;
 | 
			
		||||
 | 
			
		||||
    f_sample *clip;
 | 
			
		||||
@@ -77,18 +75,16 @@ typedef struct HWVoiceOut {
 | 
			
		||||
    int rpos;
 | 
			
		||||
    uint64_t ts_helper;
 | 
			
		||||
 | 
			
		||||
    struct st_sample *mix_buf;
 | 
			
		||||
    st_sample_t *mix_buf;
 | 
			
		||||
 | 
			
		||||
    int samples;
 | 
			
		||||
    QLIST_HEAD (sw_out_listhead, SWVoiceOut) sw_head;
 | 
			
		||||
    QLIST_HEAD (sw_cap_listhead, SWVoiceCap) cap_head;
 | 
			
		||||
    LIST_HEAD (sw_out_listhead, SWVoiceOut) sw_head;
 | 
			
		||||
    struct audio_pcm_ops *pcm_ops;
 | 
			
		||||
    QLIST_ENTRY (HWVoiceOut) entries;
 | 
			
		||||
    LIST_ENTRY (HWVoiceOut) entries;
 | 
			
		||||
} HWVoiceOut;
 | 
			
		||||
 | 
			
		||||
typedef struct HWVoiceIn {
 | 
			
		||||
    int enabled;
 | 
			
		||||
    int poll_mode;
 | 
			
		||||
    struct audio_pcm_info info;
 | 
			
		||||
 | 
			
		||||
    t_sample *conv;
 | 
			
		||||
@@ -97,45 +93,43 @@ typedef struct HWVoiceIn {
 | 
			
		||||
    int total_samples_captured;
 | 
			
		||||
    uint64_t ts_helper;
 | 
			
		||||
 | 
			
		||||
    struct st_sample *conv_buf;
 | 
			
		||||
    st_sample_t *conv_buf;
 | 
			
		||||
 | 
			
		||||
    int samples;
 | 
			
		||||
    QLIST_HEAD (sw_in_listhead, SWVoiceIn) sw_head;
 | 
			
		||||
    LIST_HEAD (sw_in_listhead, SWVoiceIn) sw_head;
 | 
			
		||||
    struct audio_pcm_ops *pcm_ops;
 | 
			
		||||
    QLIST_ENTRY (HWVoiceIn) entries;
 | 
			
		||||
    LIST_ENTRY (HWVoiceIn) entries;
 | 
			
		||||
} HWVoiceIn;
 | 
			
		||||
 | 
			
		||||
struct SWVoiceOut {
 | 
			
		||||
    QEMUSoundCard *card;
 | 
			
		||||
    struct audio_pcm_info info;
 | 
			
		||||
    t_sample *conv;
 | 
			
		||||
    int64_t ratio;
 | 
			
		||||
    struct st_sample *buf;
 | 
			
		||||
    st_sample_t *buf;
 | 
			
		||||
    void *rate;
 | 
			
		||||
    int total_hw_samples_mixed;
 | 
			
		||||
    int active;
 | 
			
		||||
    int empty;
 | 
			
		||||
    HWVoiceOut *hw;
 | 
			
		||||
    char *name;
 | 
			
		||||
    struct mixeng_volume vol;
 | 
			
		||||
    volume_t vol;
 | 
			
		||||
    struct audio_callback callback;
 | 
			
		||||
    QLIST_ENTRY (SWVoiceOut) entries;
 | 
			
		||||
    LIST_ENTRY (SWVoiceOut) entries;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct SWVoiceIn {
 | 
			
		||||
    QEMUSoundCard *card;
 | 
			
		||||
    int active;
 | 
			
		||||
    struct audio_pcm_info info;
 | 
			
		||||
    int64_t ratio;
 | 
			
		||||
    void *rate;
 | 
			
		||||
    int total_hw_samples_acquired;
 | 
			
		||||
    struct st_sample *buf;
 | 
			
		||||
    st_sample_t *buf;
 | 
			
		||||
    f_sample *clip;
 | 
			
		||||
    HWVoiceIn *hw;
 | 
			
		||||
    char *name;
 | 
			
		||||
    struct mixeng_volume vol;
 | 
			
		||||
    volume_t vol;
 | 
			
		||||
    struct audio_callback callback;
 | 
			
		||||
    QLIST_ENTRY (SWVoiceIn) entries;
 | 
			
		||||
    LIST_ENTRY (SWVoiceIn) entries;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct audio_driver {
 | 
			
		||||
@@ -153,50 +147,29 @@ struct audio_driver {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct audio_pcm_ops {
 | 
			
		||||
    int  (*init_out)(HWVoiceOut *hw, struct audsettings *as);
 | 
			
		||||
    int  (*init_out)(HWVoiceOut *hw, audsettings_t *as);
 | 
			
		||||
    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  (*ctl_out) (HWVoiceOut *hw, int cmd, ...);
 | 
			
		||||
 | 
			
		||||
    int  (*init_in) (HWVoiceIn *hw, struct audsettings *as);
 | 
			
		||||
    int  (*init_in) (HWVoiceIn *hw, audsettings_t *as);
 | 
			
		||||
    void (*fini_in) (HWVoiceIn *hw);
 | 
			
		||||
    int  (*run_in)  (HWVoiceIn *hw);
 | 
			
		||||
    int  (*read)    (SWVoiceIn *sw, void *buf, int size);
 | 
			
		||||
    int  (*ctl_in)  (HWVoiceIn *hw, int cmd, ...);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct capture_callback {
 | 
			
		||||
    struct audio_capture_ops ops;
 | 
			
		||||
    void *opaque;
 | 
			
		||||
    QLIST_ENTRY (capture_callback) entries;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct CaptureVoiceOut {
 | 
			
		||||
    HWVoiceOut hw;
 | 
			
		||||
    void *buf;
 | 
			
		||||
    QLIST_HEAD (cb_listhead, capture_callback) cb_head;
 | 
			
		||||
    QLIST_ENTRY (CaptureVoiceOut) entries;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct SWVoiceCap {
 | 
			
		||||
    SWVoiceOut sw;
 | 
			
		||||
    CaptureVoiceOut *cap;
 | 
			
		||||
    QLIST_ENTRY (SWVoiceCap) entries;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct AudioState {
 | 
			
		||||
    struct audio_driver *drv;
 | 
			
		||||
    void *drv_opaque;
 | 
			
		||||
 | 
			
		||||
    QEMUTimer *ts;
 | 
			
		||||
    QLIST_HEAD (card_listhead, QEMUSoundCard) card_head;
 | 
			
		||||
    QLIST_HEAD (hw_in_listhead, HWVoiceIn) hw_head_in;
 | 
			
		||||
    QLIST_HEAD (hw_out_listhead, HWVoiceOut) hw_head_out;
 | 
			
		||||
    QLIST_HEAD (cap_listhead, CaptureVoiceOut) cap_head;
 | 
			
		||||
    LIST_HEAD (card_head, QEMUSoundCard) card_head;
 | 
			
		||||
    LIST_HEAD (hw_in_listhead, HWVoiceIn) hw_head_in;
 | 
			
		||||
    LIST_HEAD (hw_out_listhead, HWVoiceOut) hw_head_out;
 | 
			
		||||
    int nb_hw_voices_out;
 | 
			
		||||
    int nb_hw_voices_in;
 | 
			
		||||
    int vm_running;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
extern struct audio_driver no_audio_driver;
 | 
			
		||||
@@ -207,27 +180,22 @@ 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 struct audio_driver esd_audio_driver;
 | 
			
		||||
extern struct audio_driver pa_audio_driver;
 | 
			
		||||
extern struct audio_driver winwave_audio_driver;
 | 
			
		||||
extern struct mixeng_volume nominal_volume;
 | 
			
		||||
extern volume_t nominal_volume;
 | 
			
		||||
 | 
			
		||||
void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as);
 | 
			
		||||
void audio_pcm_init_info (struct audio_pcm_info *info, audsettings_t *as,
 | 
			
		||||
                          int swap_endian);
 | 
			
		||||
void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len);
 | 
			
		||||
 | 
			
		||||
int  audio_pcm_sw_write (SWVoiceOut *sw, void *buf, int len);
 | 
			
		||||
int  audio_pcm_hw_get_live_in (HWVoiceIn *hw);
 | 
			
		||||
 | 
			
		||||
int  audio_pcm_sw_read (SWVoiceIn *sw, void *buf, int len);
 | 
			
		||||
 | 
			
		||||
int audio_pcm_hw_clip_out (HWVoiceOut *hw, void *pcm_buf,
 | 
			
		||||
                           int live, int pending);
 | 
			
		||||
int  audio_pcm_hw_get_live_out (HWVoiceOut *hw);
 | 
			
		||||
int  audio_pcm_hw_get_live_out2 (HWVoiceOut *hw, int *nb_live);
 | 
			
		||||
 | 
			
		||||
int audio_bug (const char *funcname, int cond);
 | 
			
		||||
void *audio_calloc (const char *funcname, int nmemb, size_t size);
 | 
			
		||||
 | 
			
		||||
void audio_run (const char *msg);
 | 
			
		||||
 | 
			
		||||
#define VOICE_ENABLE 1
 | 
			
		||||
#define VOICE_DISABLE 2
 | 
			
		||||
 | 
			
		||||
@@ -236,11 +204,22 @@ static inline int audio_ring_dist (int dst, int src, int len)
 | 
			
		||||
    return (dst >= src) ? (dst - src) : (len - src + dst);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline int audio_need_to_swap_endian (int endianness)
 | 
			
		||||
{
 | 
			
		||||
#ifdef WORDS_BIGENDIAN
 | 
			
		||||
    return endianness != 1;
 | 
			
		||||
#else
 | 
			
		||||
    return endianness != 0;
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#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
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,149 +0,0 @@
 | 
			
		||||
#include "qemu-common.h"
 | 
			
		||||
#include "audio.h"
 | 
			
		||||
 | 
			
		||||
#define AUDIO_CAP "audio-pt"
 | 
			
		||||
 | 
			
		||||
#include "audio_int.h"
 | 
			
		||||
#include "audio_pt_int.h"
 | 
			
		||||
 | 
			
		||||
static void 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;
 | 
			
		||||
 | 
			
		||||
    p->drv = drv;
 | 
			
		||||
 | 
			
		||||
    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_create (&p->thread, NULL, func, opaque);
 | 
			
		||||
    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, AUDIO_FUNC);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 err1:
 | 
			
		||||
    err2 = pthread_mutex_destroy (&p->mutex);
 | 
			
		||||
    if (err2) {
 | 
			
		||||
        logerr (p, err2, "%s(%s): pthread_mutex_destroy failed", cap, AUDIO_FUNC);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 err0:
 | 
			
		||||
    logerr (p, err, "%s(%s): %s failed", cap, AUDIO_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, AUDIO_FUNC);
 | 
			
		||||
        ret = -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    err = pthread_mutex_destroy (&p->mutex);
 | 
			
		||||
    if (err) {
 | 
			
		||||
        logerr (p, err, "%s(%s): pthread_mutex_destroy failed", cap, AUDIO_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, AUDIO_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, AUDIO_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, AUDIO_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, AUDIO_FUNC);
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
    err = pthread_cond_signal (&p->cond);
 | 
			
		||||
    if (err) {
 | 
			
		||||
        logerr (p, err, "%s(%s): pthread_cond_signal failed", cap, AUDIO_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, AUDIO_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 /* audio_pt_int.h */
 | 
			
		||||
@@ -36,9 +36,11 @@
 | 
			
		||||
#define HWBUF hw->conv_buf
 | 
			
		||||
#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 voice_size = glue (drv->voice_size_, TYPE);
 | 
			
		||||
 | 
			
		||||
@@ -80,7 +82,7 @@ static void glue (audio_pcm_hw_free_resources_, TYPE) (HW *hw)
 | 
			
		||||
 | 
			
		||||
static int glue (audio_pcm_hw_alloc_resources_, TYPE) (HW *hw)
 | 
			
		||||
{
 | 
			
		||||
    HWBUF = audio_calloc (AUDIO_FUNC, hw->samples, sizeof (struct st_sample));
 | 
			
		||||
    HWBUF = audio_calloc (AUDIO_FUNC, hw->samples, sizeof (st_sample_t));
 | 
			
		||||
    if (!HWBUF) {
 | 
			
		||||
        dolog ("Could not allocate " NAME " buffer (%d samples)\n",
 | 
			
		||||
               hw->samples);
 | 
			
		||||
@@ -114,7 +116,7 @@ static int glue (audio_pcm_sw_alloc_resources_, TYPE) (SW *sw)
 | 
			
		||||
    samples = ((int64_t) sw->hw->samples << 32) / sw->ratio;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    sw->buf = audio_calloc (AUDIO_FUNC, samples, sizeof (struct st_sample));
 | 
			
		||||
    sw->buf = audio_calloc (AUDIO_FUNC, samples, sizeof (st_sample_t));
 | 
			
		||||
    if (!sw->buf) {
 | 
			
		||||
        dolog ("Could not allocate buffer for `%s' (%d samples)\n",
 | 
			
		||||
               SW_NAME (sw), samples);
 | 
			
		||||
@@ -138,12 +140,13 @@ static int glue (audio_pcm_sw_init_, TYPE) (
 | 
			
		||||
    SW *sw,
 | 
			
		||||
    HW *hw,
 | 
			
		||||
    const char *name,
 | 
			
		||||
    struct audsettings *as
 | 
			
		||||
    audsettings_t *as,
 | 
			
		||||
    int endian
 | 
			
		||||
    )
 | 
			
		||||
{
 | 
			
		||||
    int err;
 | 
			
		||||
 | 
			
		||||
    audio_pcm_init_info (&sw->info, as);
 | 
			
		||||
    audio_pcm_init_info (&sw->info, as, audio_need_to_swap_endian (endian));
 | 
			
		||||
    sw->hw = hw;
 | 
			
		||||
    sw->active = 0;
 | 
			
		||||
#ifdef DAC
 | 
			
		||||
@@ -161,8 +164,8 @@ static int glue (audio_pcm_sw_init_, TYPE) (
 | 
			
		||||
#endif
 | 
			
		||||
        [sw->info.nchannels == 2]
 | 
			
		||||
        [sw->info.sign]
 | 
			
		||||
        [sw->info.swap_endianness]
 | 
			
		||||
        [audio_bits_to_index (sw->info.bits)];
 | 
			
		||||
        [sw->info.swap_endian]
 | 
			
		||||
        [sw->info.bits == 16];
 | 
			
		||||
 | 
			
		||||
    sw->name = qemu_strdup (name);
 | 
			
		||||
    err = glue (audio_pcm_sw_alloc_resources_, TYPE) (sw);
 | 
			
		||||
@@ -184,24 +187,20 @@ static void glue (audio_pcm_sw_fini_, TYPE) (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)
 | 
			
		||||
{
 | 
			
		||||
    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;
 | 
			
		||||
 | 
			
		||||
    if (!hw->sw_head.lh_first) {
 | 
			
		||||
#ifdef DAC
 | 
			
		||||
        audio_detach_capture (hw);
 | 
			
		||||
#endif
 | 
			
		||||
        QLIST_REMOVE (hw, entries);
 | 
			
		||||
        LIST_REMOVE (hw, entries);
 | 
			
		||||
        glue (s->nb_hw_voices_, TYPE) += 1;
 | 
			
		||||
        glue (audio_pcm_hw_free_resources_ ,TYPE) (hw);
 | 
			
		||||
        glue (hw->pcm_ops->fini_, TYPE) (hw);
 | 
			
		||||
@@ -210,15 +209,14 @@ static void glue (audio_pcm_hw_gc_, TYPE) (HW **hwp)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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 : glue (s->hw_head_, TYPE).lh_first;
 | 
			
		||||
    return hw ? hw->entries.le_next : s->glue (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) {
 | 
			
		||||
            return hw;
 | 
			
		||||
        }
 | 
			
		||||
@@ -227,11 +225,12 @@ static HW *glue (audio_pcm_hw_find_any_enabled_, TYPE) (HW *hw)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static HW *glue (audio_pcm_hw_find_specific_, TYPE) (
 | 
			
		||||
    AudioState *s,
 | 
			
		||||
    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)) {
 | 
			
		||||
            return hw;
 | 
			
		||||
        }
 | 
			
		||||
@@ -239,10 +238,9 @@ static HW *glue (audio_pcm_hw_find_specific_, TYPE) (
 | 
			
		||||
    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;
 | 
			
		||||
    AudioState *s = &glob_audio_state;
 | 
			
		||||
    struct audio_driver *drv = s->drv;
 | 
			
		||||
 | 
			
		||||
    if (!glue (s->nb_hw_voices_, TYPE)) {
 | 
			
		||||
@@ -267,10 +265,8 @@ static HW *glue (audio_pcm_hw_add_new_, TYPE) (struct audsettings *as)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    hw->pcm_ops = drv->pcm_ops;
 | 
			
		||||
    QLIST_INIT (&hw->sw_head);
 | 
			
		||||
#ifdef DAC
 | 
			
		||||
    QLIST_INIT (&hw->cap_head);
 | 
			
		||||
#endif
 | 
			
		||||
    LIST_INIT (&hw->sw_head);
 | 
			
		||||
 | 
			
		||||
    if (glue (hw->pcm_ops->init_, TYPE) (hw, as)) {
 | 
			
		||||
        goto err0;
 | 
			
		||||
    }
 | 
			
		||||
@@ -287,18 +283,15 @@ static HW *glue (audio_pcm_hw_add_new_, TYPE) (struct audsettings *as)
 | 
			
		||||
#endif
 | 
			
		||||
        [hw->info.nchannels == 2]
 | 
			
		||||
        [hw->info.sign]
 | 
			
		||||
        [hw->info.swap_endianness]
 | 
			
		||||
        [audio_bits_to_index (hw->info.bits)];
 | 
			
		||||
        [hw->info.swap_endian]
 | 
			
		||||
        [hw->info.bits == 16];
 | 
			
		||||
 | 
			
		||||
    if (glue (audio_pcm_hw_alloc_resources_, TYPE) (hw)) {
 | 
			
		||||
        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;
 | 
			
		||||
#ifdef DAC
 | 
			
		||||
    audio_attach_capture (hw);
 | 
			
		||||
#endif
 | 
			
		||||
    return hw;
 | 
			
		||||
 | 
			
		||||
 err1:
 | 
			
		||||
@@ -308,38 +301,40 @@ static HW *glue (audio_pcm_hw_add_new_, TYPE) (struct audsettings *as)
 | 
			
		||||
    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;
 | 
			
		||||
 | 
			
		||||
    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) {
 | 
			
		||||
            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) {
 | 
			
		||||
        return hw;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    hw = glue (audio_pcm_hw_add_new_, TYPE) (as);
 | 
			
		||||
    hw = glue (audio_pcm_hw_add_new_, TYPE) (s, as);
 | 
			
		||||
    if (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) (
 | 
			
		||||
    AudioState *s,
 | 
			
		||||
    const char *sw_name,
 | 
			
		||||
    struct audsettings *as
 | 
			
		||||
    audsettings_t *as,
 | 
			
		||||
    int sw_endian
 | 
			
		||||
    )
 | 
			
		||||
{
 | 
			
		||||
    SW *sw;
 | 
			
		||||
    HW *hw;
 | 
			
		||||
    struct audsettings hw_as;
 | 
			
		||||
    audsettings_t hw_as;
 | 
			
		||||
 | 
			
		||||
    if (glue (conf.fixed_, TYPE).enabled) {
 | 
			
		||||
        hw_as = glue (conf.fixed_, TYPE).settings;
 | 
			
		||||
@@ -355,14 +350,14 @@ static SW *glue (audio_pcm_create_voice_pair_, TYPE) (
 | 
			
		||||
        goto err1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    hw = glue (audio_pcm_hw_add_, TYPE) (&hw_as);
 | 
			
		||||
    hw = glue (audio_pcm_hw_add_, TYPE) (s, &hw_as);
 | 
			
		||||
    if (!hw) {
 | 
			
		||||
        goto err2;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    glue (audio_pcm_hw_add_sw_, TYPE) (hw, sw);
 | 
			
		||||
 | 
			
		||||
    if (glue (audio_pcm_sw_init_, TYPE) (sw, hw, sw_name, as)) {
 | 
			
		||||
    if (glue (audio_pcm_sw_init_, TYPE) (sw, hw, sw_name, as, sw_endian)) {
 | 
			
		||||
        goto err3;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -370,30 +365,31 @@ static SW *glue (audio_pcm_create_voice_pair_, TYPE) (
 | 
			
		||||
 | 
			
		||||
err3:
 | 
			
		||||
    glue (audio_pcm_hw_del_sw_, TYPE) (sw);
 | 
			
		||||
    glue (audio_pcm_hw_gc_, TYPE) (&hw);
 | 
			
		||||
    glue (audio_pcm_hw_gc_, TYPE) (s, &hw);
 | 
			
		||||
err2:
 | 
			
		||||
    qemu_free (sw);
 | 
			
		||||
err1:
 | 
			
		||||
    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_hw_del_sw_, TYPE) (sw);
 | 
			
		||||
    glue (audio_pcm_hw_gc_, TYPE) (&sw->hw);
 | 
			
		||||
    glue (audio_pcm_hw_gc_, TYPE) (s, &sw->hw);
 | 
			
		||||
    qemu_free (sw);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void glue (AUD_close_, TYPE) (QEMUSoundCard *card, SW *sw)
 | 
			
		||||
{
 | 
			
		||||
    if (sw) {
 | 
			
		||||
        if (audio_bug (AUDIO_FUNC, !card)) {
 | 
			
		||||
            dolog ("card=%p\n", card);
 | 
			
		||||
        if (audio_bug (AUDIO_FUNC, !card || !card->audio)) {
 | 
			
		||||
            dolog ("card=%p card->audio=%p\n",
 | 
			
		||||
                   card, card ? card->audio : NULL);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        glue (audio_close_, TYPE) (sw);
 | 
			
		||||
        glue (audio_close_, TYPE) (card->audio, sw);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -402,11 +398,12 @@ SW *glue (AUD_open_, TYPE) (
 | 
			
		||||
    SW *sw,
 | 
			
		||||
    const char *name,
 | 
			
		||||
    void *callback_opaque ,
 | 
			
		||||
    audio_callback_fn callback_fn,
 | 
			
		||||
    struct audsettings *as
 | 
			
		||||
    audio_callback_fn_t callback_fn,
 | 
			
		||||
    audsettings_t *as,
 | 
			
		||||
    int sw_endian
 | 
			
		||||
    )
 | 
			
		||||
{
 | 
			
		||||
    AudioState *s = &glob_audio_state;
 | 
			
		||||
    AudioState *s;
 | 
			
		||||
#ifdef DAC
 | 
			
		||||
    int live = 0;
 | 
			
		||||
    SW *old_sw = NULL;
 | 
			
		||||
@@ -415,13 +412,16 @@ SW *glue (AUD_open_, TYPE) (
 | 
			
		||||
    ldebug ("open %s, freq %d, nchannels %d, fmt %d\n",
 | 
			
		||||
            name, as->freq, as->nchannels, as->fmt);
 | 
			
		||||
 | 
			
		||||
    if (audio_bug (AUDIO_FUNC, !card || !name || !callback_fn || !as)) {
 | 
			
		||||
        dolog ("card=%p name=%p callback_fn=%p as=%p\n",
 | 
			
		||||
               card, name, callback_fn, 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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (audio_bug (AUDIO_FUNC, audio_validate_settings (as))) {
 | 
			
		||||
    s = card->audio;
 | 
			
		||||
 | 
			
		||||
    if (audio_bug (AUDIO_FUNC, audio_validate_settigs (as))) {
 | 
			
		||||
        audio_print_settings (as);
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
@@ -445,9 +445,9 @@ SW *glue (AUD_open_, TYPE) (
 | 
			
		||||
               SW_NAME (sw), sw->info.freq, sw->info.bits, sw->info.nchannels);
 | 
			
		||||
        dolog ("New %s freq %d, bits %d, channels %d\n",
 | 
			
		||||
               name,
 | 
			
		||||
               as->freq,
 | 
			
		||||
               (as->fmt == AUD_FMT_S16 || as->fmt == AUD_FMT_U16) ? 16 : 8,
 | 
			
		||||
               as->nchannels);
 | 
			
		||||
               freq,
 | 
			
		||||
               (fmt == AUD_FMT_S16 || fmt == AUD_FMT_U16) ? 16 : 8,
 | 
			
		||||
               nchannels);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
        if (live) {
 | 
			
		||||
@@ -473,19 +473,19 @@ SW *glue (AUD_open_, TYPE) (
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        glue (audio_pcm_sw_fini_, TYPE) (sw);
 | 
			
		||||
        if (glue (audio_pcm_sw_init_, TYPE) (sw, hw, name, as)) {
 | 
			
		||||
        if (glue (audio_pcm_sw_init_, TYPE) (sw, hw, name, as, sw_endian)) {
 | 
			
		||||
            goto fail;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
        sw = glue (audio_pcm_create_voice_pair_, TYPE) (name, as);
 | 
			
		||||
        sw = glue (audio_pcm_create_voice_pair_, TYPE) (s, name, as, sw_endian);
 | 
			
		||||
        if (!sw) {
 | 
			
		||||
            dolog ("Failed to create voice `%s'\n", name);
 | 
			
		||||
            return NULL;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    sw->card = card;
 | 
			
		||||
    if (sw) {
 | 
			
		||||
        sw->vol = nominal_volume;
 | 
			
		||||
        sw->callback.fn = callback_fn;
 | 
			
		||||
        sw->callback.opaque = callback_opaque;
 | 
			
		||||
@@ -509,6 +509,7 @@ SW *glue (AUD_open_, TYPE) (
 | 
			
		||||
        audio_pcm_print_info ("hw", &sw->hw->info);
 | 
			
		||||
        audio_pcm_print_info ("sw", &sw->info);
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return sw;
 | 
			
		||||
 | 
			
		||||
@@ -541,7 +542,7 @@ uint64_t glue (AUD_get_elapsed_usec_, TYPE) (SW *sw, QEMUAudioTimeStamp *ts)
 | 
			
		||||
 | 
			
		||||
    cur_ts = sw->hw->ts_helper;
 | 
			
		||||
    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) {
 | 
			
		||||
        delta = cur_ts - old_ts;
 | 
			
		||||
@@ -554,7 +555,7 @@ uint64_t glue (AUD_get_elapsed_usec_, TYPE) (SW *sw, QEMUAudioTimeStamp *ts)
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return muldiv64 (delta, sw->hw->info.freq, 1000000);
 | 
			
		||||
    return (delta * sw->hw->info.freq) / 1000000;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#undef TYPE
 | 
			
		||||
 
 | 
			
		||||
@@ -1,108 +0,0 @@
 | 
			
		||||
/* public domain */
 | 
			
		||||
 | 
			
		||||
#include "qemu-common.h"
 | 
			
		||||
#include "audio.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 */
 | 
			
		||||
@@ -26,8 +26,7 @@
 | 
			
		||||
#include <string.h>             /* strerror */
 | 
			
		||||
#include <pthread.h>            /* pthread_X */
 | 
			
		||||
 | 
			
		||||
#include "qemu-common.h"
 | 
			
		||||
#include "audio.h"
 | 
			
		||||
#include "vl.h"
 | 
			
		||||
 | 
			
		||||
#define AUDIO_CAP "coreaudio"
 | 
			
		||||
#include "audio_int.h"
 | 
			
		||||
@@ -190,15 +189,17 @@ static int coreaudio_unlock (coreaudioVoiceOut *core, const char *fn_name)
 | 
			
		||||
    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;
 | 
			
		||||
 | 
			
		||||
    if (coreaudio_lock (core, "coreaudio_run_out")) {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    live = audio_pcm_hw_get_live_out (hw);
 | 
			
		||||
 | 
			
		||||
    if (core->decr > live) {
 | 
			
		||||
        ldebug ("core->decr %d live %d core->live %d\n",
 | 
			
		||||
                core->decr,
 | 
			
		||||
@@ -231,7 +232,7 @@ static OSStatus audioDeviceIOProc(
 | 
			
		||||
    HWVoiceOut *hw = hwptr;
 | 
			
		||||
    coreaudioVoiceOut *core = (coreaudioVoiceOut *) hwptr;
 | 
			
		||||
    int rpos, live;
 | 
			
		||||
    struct st_sample *src;
 | 
			
		||||
    st_sample_t *src;
 | 
			
		||||
#ifndef FLOAT_MIXENG
 | 
			
		||||
#ifdef RECIPROCAL
 | 
			
		||||
    const float scale = 1.f / UINT_MAX;
 | 
			
		||||
@@ -274,6 +275,8 @@ static OSStatus audioDeviceIOProc(
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* cleanup */
 | 
			
		||||
    mixeng_clear (src, frameCount);
 | 
			
		||||
    rpos = (rpos + frameCount) % hw->samples;
 | 
			
		||||
    core->decr += frameCount;
 | 
			
		||||
    core->rpos = rpos;
 | 
			
		||||
@@ -287,12 +290,14 @@ static int coreaudio_write (SWVoiceOut *sw, void *buf, int len)
 | 
			
		||||
    return audio_pcm_sw_write (sw, buf, len);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int coreaudio_init_out (HWVoiceOut *hw, struct audsettings *as)
 | 
			
		||||
static int coreaudio_init_out (HWVoiceOut *hw, audsettings_t *as)
 | 
			
		||||
{
 | 
			
		||||
    OSStatus status;
 | 
			
		||||
    coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
 | 
			
		||||
    UInt32 propertySize;
 | 
			
		||||
    int err;
 | 
			
		||||
    int bits = 8;
 | 
			
		||||
    int endianess = 0;
 | 
			
		||||
    const char *typ = "playback";
 | 
			
		||||
    AudioValueRange frameRange;
 | 
			
		||||
 | 
			
		||||
@@ -303,7 +308,18 @@ static int coreaudio_init_out (HWVoiceOut *hw, struct audsettings *as)
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    audio_pcm_init_info (&hw->info, as);
 | 
			
		||||
    if (as->fmt == AUD_FMT_S16 || as->fmt == AUD_FMT_U16) {
 | 
			
		||||
        bits = 16;
 | 
			
		||||
        endianess = 1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    audio_pcm_init_info (
 | 
			
		||||
        &hw->info,
 | 
			
		||||
        as,
 | 
			
		||||
        /* Following is irrelevant actually since we do not use
 | 
			
		||||
           mixengs clipping routines */
 | 
			
		||||
        audio_need_to_swap_endian (endianess)
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
    /* open default output device */
 | 
			
		||||
    propertySize = sizeof(core->outputDeviceID);
 | 
			
		||||
@@ -511,39 +527,38 @@ static void coreaudio_audio_fini (void *opaque)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct audio_option coreaudio_options[] = {
 | 
			
		||||
    {
 | 
			
		||||
        .name  = "BUFFER_SIZE",
 | 
			
		||||
        .tag   = AUD_OPT_INT,
 | 
			
		||||
        .valp  = &conf.buffer_frames,
 | 
			
		||||
        .descr = "Size of the buffer in frames"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        .name  = "BUFFER_COUNT",
 | 
			
		||||
        .tag   = AUD_OPT_INT,
 | 
			
		||||
        .valp  = &conf.nbuffers,
 | 
			
		||||
        .descr = "Number of buffers"
 | 
			
		||||
    },
 | 
			
		||||
    { /* End of list */ }
 | 
			
		||||
    {"BUFFER_SIZE", AUD_OPT_INT, &conf.buffer_frames,
 | 
			
		||||
     "Size of the buffer in frames", NULL, 0},
 | 
			
		||||
    {"BUFFER_COUNT", AUD_OPT_INT, &conf.nbuffers,
 | 
			
		||||
     "Number of buffers", NULL, 0},
 | 
			
		||||
    {NULL, 0, NULL, NULL, NULL, 0}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct audio_pcm_ops coreaudio_pcm_ops = {
 | 
			
		||||
    .init_out = coreaudio_init_out,
 | 
			
		||||
    .fini_out = coreaudio_fini_out,
 | 
			
		||||
    .run_out  = coreaudio_run_out,
 | 
			
		||||
    .write    = coreaudio_write,
 | 
			
		||||
    .ctl_out  = coreaudio_ctl_out
 | 
			
		||||
    coreaudio_init_out,
 | 
			
		||||
    coreaudio_fini_out,
 | 
			
		||||
    coreaudio_run_out,
 | 
			
		||||
    coreaudio_write,
 | 
			
		||||
    coreaudio_ctl_out,
 | 
			
		||||
 | 
			
		||||
    NULL,
 | 
			
		||||
    NULL,
 | 
			
		||||
    NULL,
 | 
			
		||||
    NULL,
 | 
			
		||||
    NULL
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct audio_driver coreaudio_audio_driver = {
 | 
			
		||||
    .name           = "coreaudio",
 | 
			
		||||
    .descr          = "CoreAudio http://developer.apple.com/audio/coreaudio.html",
 | 
			
		||||
    .options        = coreaudio_options,
 | 
			
		||||
    .init           = coreaudio_audio_init,
 | 
			
		||||
    .fini           = coreaudio_audio_fini,
 | 
			
		||||
    .pcm_ops        = &coreaudio_pcm_ops,
 | 
			
		||||
    .can_be_default = 1,
 | 
			
		||||
    .max_voices_out = 1,
 | 
			
		||||
    .max_voices_in  = 0,
 | 
			
		||||
    .voice_size_out = sizeof (coreaudioVoiceOut),
 | 
			
		||||
    .voice_size_in  = 0
 | 
			
		||||
    INIT_FIELD (name           = ) "coreaudio",
 | 
			
		||||
    INIT_FIELD (descr          = )
 | 
			
		||||
    "CoreAudio http://developer.apple.com/audio/coreaudio.html",
 | 
			
		||||
    INIT_FIELD (options        = ) coreaudio_options,
 | 
			
		||||
    INIT_FIELD (init           = ) coreaudio_audio_init,
 | 
			
		||||
    INIT_FIELD (fini           = ) coreaudio_audio_fini,
 | 
			
		||||
    INIT_FIELD (pcm_ops        = ) &coreaudio_pcm_ops,
 | 
			
		||||
    INIT_FIELD (can_be_default = ) 1,
 | 
			
		||||
    INIT_FIELD (max_voices_out = ) 1,
 | 
			
		||||
    INIT_FIELD (max_voices_in  = ) 0,
 | 
			
		||||
    INIT_FIELD (voice_size_out = ) sizeof (coreaudioVoiceOut),
 | 
			
		||||
    INIT_FIELD (voice_size_in  = ) 0
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -23,20 +23,16 @@
 | 
			
		||||
 */
 | 
			
		||||
#ifdef DSBTYPE_IN
 | 
			
		||||
#define NAME "capture buffer"
 | 
			
		||||
#define NAME2 "DirectSoundCapture"
 | 
			
		||||
#define TYPE in
 | 
			
		||||
#define IFACE IDirectSoundCaptureBuffer
 | 
			
		||||
#define BUFPTR LPDIRECTSOUNDCAPTUREBUFFER
 | 
			
		||||
#define FIELD dsound_capture_buffer
 | 
			
		||||
#define FIELD2 dsound_capture
 | 
			
		||||
#else
 | 
			
		||||
#define NAME "playback buffer"
 | 
			
		||||
#define NAME2 "DirectSound"
 | 
			
		||||
#define TYPE out
 | 
			
		||||
#define IFACE IDirectSoundBuffer
 | 
			
		||||
#define BUFPTR LPDIRECTSOUNDBUFFER
 | 
			
		||||
#define FIELD dsound_buffer
 | 
			
		||||
#define FIELD2 dsound
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static int glue (dsound_unlock_, TYPE) (
 | 
			
		||||
@@ -74,13 +70,7 @@ static int glue (dsound_lock_, TYPE) (
 | 
			
		||||
    int i;
 | 
			
		||||
    LPVOID p1 = NULL, p2 = NULL;
 | 
			
		||||
    DWORD blen1 = 0, blen2 = 0;
 | 
			
		||||
    DWORD flag;
 | 
			
		||||
 | 
			
		||||
#ifdef DSBTYPE_IN
 | 
			
		||||
    flag = entire ? DSCBLOCK_ENTIREBUFFER : 0;
 | 
			
		||||
#else
 | 
			
		||||
    flag = entire ? DSBLOCK_ENTIREBUFFER : 0;
 | 
			
		||||
#endif
 | 
			
		||||
    for (i = 0; i < conf.lock_retries; ++i) {
 | 
			
		||||
        hr = glue (IFACE, _Lock) (
 | 
			
		||||
            buf,
 | 
			
		||||
@@ -90,7 +80,13 @@ static int glue (dsound_lock_, TYPE) (
 | 
			
		||||
            &blen1,
 | 
			
		||||
            &p2,
 | 
			
		||||
            &blen2,
 | 
			
		||||
            flag
 | 
			
		||||
            (entire
 | 
			
		||||
#ifdef DSBTYPE_IN
 | 
			
		||||
             ? DSCBLOCK_ENTIREBUFFER
 | 
			
		||||
#else
 | 
			
		||||
             ? DSBLOCK_ENTIREBUFFER
 | 
			
		||||
#endif
 | 
			
		||||
             : 0)
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
        if (FAILED (hr)) {
 | 
			
		||||
@@ -174,16 +170,16 @@ static void dsound_fini_out (HWVoiceOut *hw)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef DSBTYPE_IN
 | 
			
		||||
static int dsound_init_in (HWVoiceIn *hw, struct audsettings *as)
 | 
			
		||||
static int dsound_init_in (HWVoiceIn *hw, audsettings_t *as)
 | 
			
		||||
#else
 | 
			
		||||
static int dsound_init_out (HWVoiceOut *hw, struct audsettings *as)
 | 
			
		||||
static int dsound_init_out (HWVoiceOut *hw, audsettings_t *as)
 | 
			
		||||
#endif
 | 
			
		||||
{
 | 
			
		||||
    int err;
 | 
			
		||||
    HRESULT hr;
 | 
			
		||||
    dsound *s = &glob_dsound;
 | 
			
		||||
    WAVEFORMATEX wfx;
 | 
			
		||||
    struct audsettings obt_as;
 | 
			
		||||
    audsettings_t obt_as;
 | 
			
		||||
#ifdef DSBTYPE_IN
 | 
			
		||||
    const char *typ = "ADC";
 | 
			
		||||
    DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
 | 
			
		||||
@@ -196,11 +192,6 @@ static int dsound_init_out (HWVoiceOut *hw, struct audsettings *as)
 | 
			
		||||
    DSBCAPS bc;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    if (!s->FIELD2) {
 | 
			
		||||
        dolog ("Attempt to initialize voice without " NAME2 " object\n");
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    err = waveformat_from_audio_settings (&wfx, as);
 | 
			
		||||
    if (err) {
 | 
			
		||||
        return -1;
 | 
			
		||||
@@ -259,8 +250,8 @@ static int dsound_init_out (HWVoiceOut *hw, struct audsettings *as)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ds->first_time = 1;
 | 
			
		||||
    obt_as.endianness = 0;
 | 
			
		||||
    audio_pcm_init_info (&hw->info, &obt_as);
 | 
			
		||||
 | 
			
		||||
    audio_pcm_init_info (&hw->info, &obt_as, audio_need_to_swap_endian (0));
 | 
			
		||||
 | 
			
		||||
    if (bc.dwBufferBytes & hw->info.align) {
 | 
			
		||||
        dolog (
 | 
			
		||||
@@ -285,9 +276,7 @@ static int dsound_init_out (HWVoiceOut *hw, struct audsettings *as)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#undef NAME
 | 
			
		||||
#undef NAME2
 | 
			
		||||
#undef TYPE
 | 
			
		||||
#undef IFACE
 | 
			
		||||
#undef BUFPTR
 | 
			
		||||
#undef FIELD
 | 
			
		||||
#undef FIELD2
 | 
			
		||||
 
 | 
			
		||||
@@ -26,19 +26,15 @@
 | 
			
		||||
 * SEAL 1.07 by Carlos 'pel' Hasan was used as documentation
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "qemu-common.h"
 | 
			
		||||
#include "audio.h"
 | 
			
		||||
#include "vl.h"
 | 
			
		||||
 | 
			
		||||
#define AUDIO_CAP "dsound"
 | 
			
		||||
#include "audio_int.h"
 | 
			
		||||
 | 
			
		||||
#include <windows.h>
 | 
			
		||||
#include <mmsystem.h>
 | 
			
		||||
#include <objbase.h>
 | 
			
		||||
#include <dsound.h>
 | 
			
		||||
 | 
			
		||||
#include "audio_win_int.h"
 | 
			
		||||
 | 
			
		||||
/* #define DEBUG_DSOUND */
 | 
			
		||||
 | 
			
		||||
static struct {
 | 
			
		||||
@@ -48,26 +44,28 @@ static struct {
 | 
			
		||||
    int set_primary;
 | 
			
		||||
    int bufsize_in;
 | 
			
		||||
    int bufsize_out;
 | 
			
		||||
    struct audsettings settings;
 | 
			
		||||
    audsettings_t settings;
 | 
			
		||||
    int latency_millis;
 | 
			
		||||
} conf = {
 | 
			
		||||
    .lock_retries       = 1,
 | 
			
		||||
    .restore_retries    = 1,
 | 
			
		||||
    .getstatus_retries  = 1,
 | 
			
		||||
    .set_primary        = 0,
 | 
			
		||||
    .bufsize_in         = 16384,
 | 
			
		||||
    .bufsize_out        = 16384,
 | 
			
		||||
    .settings.freq      = 44100,
 | 
			
		||||
    .settings.nchannels = 2,
 | 
			
		||||
    .settings.fmt       = AUD_FMT_S16,
 | 
			
		||||
    .latency_millis     = 10
 | 
			
		||||
    1,
 | 
			
		||||
    1,
 | 
			
		||||
    1,
 | 
			
		||||
    0,
 | 
			
		||||
    16384,
 | 
			
		||||
    16384,
 | 
			
		||||
    {
 | 
			
		||||
        44100,
 | 
			
		||||
        2,
 | 
			
		||||
        AUD_FMT_S16
 | 
			
		||||
    },
 | 
			
		||||
    10
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    LPDIRECTSOUND dsound;
 | 
			
		||||
    LPDIRECTSOUNDCAPTURE dsound_capture;
 | 
			
		||||
    LPDIRECTSOUNDBUFFER dsound_primary_buffer;
 | 
			
		||||
    struct audsettings settings;
 | 
			
		||||
    audsettings_t settings;
 | 
			
		||||
} dsound;
 | 
			
		||||
 | 
			
		||||
static dsound glob_dsound;
 | 
			
		||||
@@ -306,6 +304,95 @@ static int dsound_restore_out (LPDIRECTSOUNDBUFFER dsb)
 | 
			
		||||
    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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#include "dsound_template.h"
 | 
			
		||||
#define DSBTYPE_IN
 | 
			
		||||
#include "dsound_template.h"
 | 
			
		||||
@@ -354,8 +441,8 @@ static void dsound_write_sample (HWVoiceOut *hw, uint8_t *dst, int dst_len)
 | 
			
		||||
    int src_len1 = dst_len;
 | 
			
		||||
    int src_len2 = 0;
 | 
			
		||||
    int pos = hw->rpos + dst_len;
 | 
			
		||||
    struct st_sample *src1 = hw->mix_buf + hw->rpos;
 | 
			
		||||
    struct st_sample *src2 = NULL;
 | 
			
		||||
    st_sample_t *src1 = hw->mix_buf + hw->rpos;
 | 
			
		||||
    st_sample_t *src2 = NULL;
 | 
			
		||||
 | 
			
		||||
    if (pos > hw->samples) {
 | 
			
		||||
        src_len1 = hw->samples - hw->rpos;
 | 
			
		||||
@@ -366,11 +453,13 @@ static void dsound_write_sample (HWVoiceOut *hw, uint8_t *dst, int dst_len)
 | 
			
		||||
 | 
			
		||||
    if (src_len1) {
 | 
			
		||||
        hw->clip (dst, src1, src_len1);
 | 
			
		||||
        mixeng_clear (src1, src_len1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (src_len2) {
 | 
			
		||||
        dst = advance (dst, src_len1 << hw->info.shift);
 | 
			
		||||
        hw->clip (dst, src2, src_len2);
 | 
			
		||||
        mixeng_clear (src2, src_len2);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    hw->rpos = pos % hw->samples;
 | 
			
		||||
@@ -565,13 +654,13 @@ static int dsound_write (SWVoiceOut *sw, void *buf, int len)
 | 
			
		||||
    return audio_pcm_sw_write (sw, buf, len);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int dsound_run_out (HWVoiceOut *hw, int live)
 | 
			
		||||
static int dsound_run_out (HWVoiceOut *hw)
 | 
			
		||||
{
 | 
			
		||||
    int err;
 | 
			
		||||
    HRESULT hr;
 | 
			
		||||
    DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
 | 
			
		||||
    LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer;
 | 
			
		||||
    int len, hwshift;
 | 
			
		||||
    int live, len, hwshift;
 | 
			
		||||
    DWORD blen1, blen2;
 | 
			
		||||
    DWORD len1, len2;
 | 
			
		||||
    DWORD decr;
 | 
			
		||||
@@ -587,6 +676,8 @@ static int dsound_run_out (HWVoiceOut *hw, int live)
 | 
			
		||||
    hwshift = hw->info.shift;
 | 
			
		||||
    bufsize = hw->samples << hwshift;
 | 
			
		||||
 | 
			
		||||
    live = audio_pcm_hw_get_live_out (hw);
 | 
			
		||||
 | 
			
		||||
    hr = IDirectSoundBuffer_GetCurrentPosition (
 | 
			
		||||
        dsb,
 | 
			
		||||
        &ppos,
 | 
			
		||||
@@ -896,12 +987,6 @@ static void *dsound_audio_init (void)
 | 
			
		||||
    hr = IDirectSound_Initialize (s->dsound, NULL);
 | 
			
		||||
    if (FAILED (hr)) {
 | 
			
		||||
        dsound_logerr (hr, "Could not initialize DirectSound\n");
 | 
			
		||||
 | 
			
		||||
        hr = IDirectSound_Release (s->dsound);
 | 
			
		||||
        if (FAILED (hr)) {
 | 
			
		||||
            dsound_logerr (hr, "Could not release DirectSound\n");
 | 
			
		||||
        }
 | 
			
		||||
        s->dsound = NULL;
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -938,93 +1023,54 @@ static void *dsound_audio_init (void)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct audio_option dsound_options[] = {
 | 
			
		||||
    {
 | 
			
		||||
        .name  = "LOCK_RETRIES",
 | 
			
		||||
        .tag   = AUD_OPT_INT,
 | 
			
		||||
        .valp  = &conf.lock_retries,
 | 
			
		||||
        .descr = "Number of times to attempt locking the buffer"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        .name  = "RESTOURE_RETRIES",
 | 
			
		||||
        .tag   = AUD_OPT_INT,
 | 
			
		||||
        .valp  = &conf.restore_retries,
 | 
			
		||||
        .descr = "Number of times to attempt restoring the buffer"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        .name  = "GETSTATUS_RETRIES",
 | 
			
		||||
        .tag   = AUD_OPT_INT,
 | 
			
		||||
        .valp  = &conf.getstatus_retries,
 | 
			
		||||
        .descr = "Number of times to attempt getting status of the buffer"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        .name  = "SET_PRIMARY",
 | 
			
		||||
        .tag   = AUD_OPT_BOOL,
 | 
			
		||||
        .valp  = &conf.set_primary,
 | 
			
		||||
        .descr = "Set the parameters of primary buffer"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        .name  = "LATENCY_MILLIS",
 | 
			
		||||
        .tag   = AUD_OPT_INT,
 | 
			
		||||
        .valp  = &conf.latency_millis,
 | 
			
		||||
        .descr = "(undocumented)"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        .name  = "PRIMARY_FREQ",
 | 
			
		||||
        .tag   = AUD_OPT_INT,
 | 
			
		||||
        .valp  = &conf.settings.freq,
 | 
			
		||||
        .descr = "Primary buffer frequency"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        .name  = "PRIMARY_CHANNELS",
 | 
			
		||||
        .tag   = AUD_OPT_INT,
 | 
			
		||||
        .valp  = &conf.settings.nchannels,
 | 
			
		||||
        .descr = "Primary buffer number of channels (1 - mono, 2 - stereo)"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        .name  = "PRIMARY_FMT",
 | 
			
		||||
        .tag   = AUD_OPT_FMT,
 | 
			
		||||
        .valp  = &conf.settings.fmt,
 | 
			
		||||
        .descr = "Primary buffer format"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        .name  = "BUFSIZE_OUT",
 | 
			
		||||
        .tag   = AUD_OPT_INT,
 | 
			
		||||
        .valp  = &conf.bufsize_out,
 | 
			
		||||
        .descr = "(undocumented)"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        .name  = "BUFSIZE_IN",
 | 
			
		||||
        .tag   = AUD_OPT_INT,
 | 
			
		||||
        .valp  = &conf.bufsize_in,
 | 
			
		||||
        .descr = "(undocumented)"
 | 
			
		||||
    },
 | 
			
		||||
    { /* End of list */ }
 | 
			
		||||
    {"LOCK_RETRIES", AUD_OPT_INT, &conf.lock_retries,
 | 
			
		||||
     "Number of times to attempt locking the buffer", NULL, 0},
 | 
			
		||||
    {"RESTOURE_RETRIES", AUD_OPT_INT, &conf.restore_retries,
 | 
			
		||||
     "Number of times to attempt restoring the buffer", NULL, 0},
 | 
			
		||||
    {"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,
 | 
			
		||||
     "Set the parameters of primary buffer", NULL, 0},
 | 
			
		||||
    {"LATENCY_MILLIS", AUD_OPT_INT, &conf.latency_millis,
 | 
			
		||||
     "(undocumented)", NULL, 0},
 | 
			
		||||
    {"PRIMARY_FREQ", AUD_OPT_INT, &conf.settings.freq,
 | 
			
		||||
     "Primary buffer frequency", NULL, 0},
 | 
			
		||||
    {"PRIMARY_CHANNELS", AUD_OPT_INT, &conf.settings.nchannels,
 | 
			
		||||
     "Primary buffer number of channels (1 - mono, 2 - stereo)", NULL, 0},
 | 
			
		||||
    {"PRIMARY_FMT", AUD_OPT_FMT, &conf.settings.fmt,
 | 
			
		||||
     "Primary buffer format", NULL, 0},
 | 
			
		||||
    {"BUFSIZE_OUT", AUD_OPT_INT, &conf.bufsize_out,
 | 
			
		||||
     "(undocumented)", NULL, 0},
 | 
			
		||||
    {"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 = {
 | 
			
		||||
    .init_out = dsound_init_out,
 | 
			
		||||
    .fini_out = dsound_fini_out,
 | 
			
		||||
    .run_out  = dsound_run_out,
 | 
			
		||||
    .write    = dsound_write,
 | 
			
		||||
    .ctl_out  = dsound_ctl_out,
 | 
			
		||||
    dsound_init_out,
 | 
			
		||||
    dsound_fini_out,
 | 
			
		||||
    dsound_run_out,
 | 
			
		||||
    dsound_write,
 | 
			
		||||
    dsound_ctl_out,
 | 
			
		||||
 | 
			
		||||
    .init_in  = dsound_init_in,
 | 
			
		||||
    .fini_in  = dsound_fini_in,
 | 
			
		||||
    .run_in   = dsound_run_in,
 | 
			
		||||
    .read     = dsound_read,
 | 
			
		||||
    .ctl_in   = dsound_ctl_in
 | 
			
		||||
    dsound_init_in,
 | 
			
		||||
    dsound_fini_in,
 | 
			
		||||
    dsound_run_in,
 | 
			
		||||
    dsound_read,
 | 
			
		||||
    dsound_ctl_in
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct audio_driver dsound_audio_driver = {
 | 
			
		||||
    .name           = "dsound",
 | 
			
		||||
    .descr          = "DirectSound http://wikipedia.org/wiki/DirectSound",
 | 
			
		||||
    .options        = dsound_options,
 | 
			
		||||
    .init           = dsound_audio_init,
 | 
			
		||||
    .fini           = dsound_audio_fini,
 | 
			
		||||
    .pcm_ops        = &dsound_pcm_ops,
 | 
			
		||||
    .can_be_default = 1,
 | 
			
		||||
    .max_voices_out = INT_MAX,
 | 
			
		||||
    .max_voices_in  = 1,
 | 
			
		||||
    .voice_size_out = sizeof (DSoundVoiceOut),
 | 
			
		||||
    .voice_size_in  = sizeof (DSoundVoiceIn)
 | 
			
		||||
    INIT_FIELD (name           = ) "dsound",
 | 
			
		||||
    INIT_FIELD (descr          = )
 | 
			
		||||
    "DirectSound http://wikipedia.org/wiki/DirectSound",
 | 
			
		||||
    INIT_FIELD (options        = ) dsound_options,
 | 
			
		||||
    INIT_FIELD (init           = ) dsound_audio_init,
 | 
			
		||||
    INIT_FIELD (fini           = ) dsound_audio_fini,
 | 
			
		||||
    INIT_FIELD (pcm_ops        = ) &dsound_pcm_ops,
 | 
			
		||||
    INIT_FIELD (can_be_default = ) 1,
 | 
			
		||||
    INIT_FIELD (max_voices_out = ) INT_MAX,
 | 
			
		||||
    INIT_FIELD (max_voices_in  = ) 1,
 | 
			
		||||
    INIT_FIELD (voice_size_out = ) sizeof (DSoundVoiceOut),
 | 
			
		||||
    INIT_FIELD (voice_size_in  = ) sizeof (DSoundVoiceIn)
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										604
									
								
								audio/esdaudio.c
									
									
									
									
									
								
							
							
						
						
									
										604
									
								
								audio/esdaudio.c
									
									
									
									
									
								
							@@ -1,604 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * QEMU ESD audio driver
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2006 Frederick Reeve (brushed up by 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 <esd.h>
 | 
			
		||||
#include "qemu-common.h"
 | 
			
		||||
#include "audio.h"
 | 
			
		||||
#include <signal.h>
 | 
			
		||||
 | 
			
		||||
#define AUDIO_CAP "esd"
 | 
			
		||||
#include "audio_int.h"
 | 
			
		||||
#include "audio_pt_int.h"
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    HWVoiceOut hw;
 | 
			
		||||
    int done;
 | 
			
		||||
    int live;
 | 
			
		||||
    int decr;
 | 
			
		||||
    int rpos;
 | 
			
		||||
    void *pcm_buf;
 | 
			
		||||
    int fd;
 | 
			
		||||
    struct audio_pt pt;
 | 
			
		||||
} ESDVoiceOut;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    HWVoiceIn hw;
 | 
			
		||||
    int done;
 | 
			
		||||
    int dead;
 | 
			
		||||
    int incr;
 | 
			
		||||
    int wpos;
 | 
			
		||||
    void *pcm_buf;
 | 
			
		||||
    int fd;
 | 
			
		||||
    struct audio_pt pt;
 | 
			
		||||
} ESDVoiceIn;
 | 
			
		||||
 | 
			
		||||
static struct {
 | 
			
		||||
    int samples;
 | 
			
		||||
    int divisor;
 | 
			
		||||
    char *dac_host;
 | 
			
		||||
    char *adc_host;
 | 
			
		||||
} conf = {
 | 
			
		||||
    .samples = 1024,
 | 
			
		||||
    .divisor = 2,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void GCC_FMT_ATTR (2, 3) qesd_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", strerror (err));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* playback */
 | 
			
		||||
static void *qesd_thread_out (void *arg)
 | 
			
		||||
{
 | 
			
		||||
    ESDVoiceOut *esd = arg;
 | 
			
		||||
    HWVoiceOut *hw = &esd->hw;
 | 
			
		||||
    int threshold;
 | 
			
		||||
 | 
			
		||||
    threshold = conf.divisor ? hw->samples / conf.divisor : 0;
 | 
			
		||||
 | 
			
		||||
    if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (;;) {
 | 
			
		||||
        int decr, to_mix, rpos;
 | 
			
		||||
 | 
			
		||||
        for (;;) {
 | 
			
		||||
            if (esd->done) {
 | 
			
		||||
                goto exit;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (esd->live > threshold) {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (audio_pt_wait (&esd->pt, AUDIO_FUNC)) {
 | 
			
		||||
                goto exit;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        decr = to_mix = esd->live;
 | 
			
		||||
        rpos = hw->rpos;
 | 
			
		||||
 | 
			
		||||
        if (audio_pt_unlock (&esd->pt, AUDIO_FUNC)) {
 | 
			
		||||
            return NULL;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        while (to_mix) {
 | 
			
		||||
            ssize_t written;
 | 
			
		||||
            int chunk = audio_MIN (to_mix, hw->samples - rpos);
 | 
			
		||||
            struct st_sample *src = hw->mix_buf + rpos;
 | 
			
		||||
 | 
			
		||||
            hw->clip (esd->pcm_buf, src, chunk);
 | 
			
		||||
 | 
			
		||||
        again:
 | 
			
		||||
            written = write (esd->fd, esd->pcm_buf, chunk << hw->info.shift);
 | 
			
		||||
            if (written == -1) {
 | 
			
		||||
                if (errno == EINTR || errno == EAGAIN) {
 | 
			
		||||
                    goto again;
 | 
			
		||||
                }
 | 
			
		||||
                qesd_logerr (errno, "write failed\n");
 | 
			
		||||
                return NULL;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (written != chunk << hw->info.shift) {
 | 
			
		||||
                int wsamples = written >> hw->info.shift;
 | 
			
		||||
                int wbytes = wsamples << hw->info.shift;
 | 
			
		||||
                if (wbytes != written) {
 | 
			
		||||
                    dolog ("warning: Misaligned write %d (requested %zd), "
 | 
			
		||||
                           "alignment %d\n",
 | 
			
		||||
                           wbytes, written, hw->info.align + 1);
 | 
			
		||||
                }
 | 
			
		||||
                to_mix -= wsamples;
 | 
			
		||||
                rpos = (rpos + wsamples) % hw->samples;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            rpos = (rpos + chunk) % hw->samples;
 | 
			
		||||
            to_mix -= chunk;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
 | 
			
		||||
            return NULL;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        esd->rpos = rpos;
 | 
			
		||||
        esd->live -= decr;
 | 
			
		||||
        esd->decr += decr;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 exit:
 | 
			
		||||
    audio_pt_unlock (&esd->pt, AUDIO_FUNC);
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int qesd_run_out (HWVoiceOut *hw, int live)
 | 
			
		||||
{
 | 
			
		||||
    int decr;
 | 
			
		||||
    ESDVoiceOut *esd = (ESDVoiceOut *) hw;
 | 
			
		||||
 | 
			
		||||
    if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    decr = audio_MIN (live, esd->decr);
 | 
			
		||||
    esd->decr -= decr;
 | 
			
		||||
    esd->live = live - decr;
 | 
			
		||||
    hw->rpos = esd->rpos;
 | 
			
		||||
    if (esd->live > 0) {
 | 
			
		||||
        audio_pt_unlock_and_signal (&esd->pt, AUDIO_FUNC);
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
        audio_pt_unlock (&esd->pt, AUDIO_FUNC);
 | 
			
		||||
    }
 | 
			
		||||
    return decr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int qesd_write (SWVoiceOut *sw, void *buf, int len)
 | 
			
		||||
{
 | 
			
		||||
    return audio_pcm_sw_write (sw, buf, len);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int qesd_init_out (HWVoiceOut *hw, struct audsettings *as)
 | 
			
		||||
{
 | 
			
		||||
    ESDVoiceOut *esd = (ESDVoiceOut *) hw;
 | 
			
		||||
    struct audsettings obt_as = *as;
 | 
			
		||||
    int esdfmt = ESD_STREAM | ESD_PLAY;
 | 
			
		||||
    int err;
 | 
			
		||||
    sigset_t set, old_set;
 | 
			
		||||
 | 
			
		||||
    sigfillset (&set);
 | 
			
		||||
 | 
			
		||||
    esdfmt |= (as->nchannels == 2) ? ESD_STEREO : ESD_MONO;
 | 
			
		||||
    switch (as->fmt) {
 | 
			
		||||
    case AUD_FMT_S8:
 | 
			
		||||
    case AUD_FMT_U8:
 | 
			
		||||
        esdfmt |= ESD_BITS8;
 | 
			
		||||
        obt_as.fmt = AUD_FMT_U8;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case AUD_FMT_S32:
 | 
			
		||||
    case AUD_FMT_U32:
 | 
			
		||||
        dolog ("Will use 16 instead of 32 bit samples\n");
 | 
			
		||||
 | 
			
		||||
    case AUD_FMT_S16:
 | 
			
		||||
    case AUD_FMT_U16:
 | 
			
		||||
    deffmt:
 | 
			
		||||
        esdfmt |= ESD_BITS16;
 | 
			
		||||
        obt_as.fmt = AUD_FMT_S16;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    default:
 | 
			
		||||
        dolog ("Internal logic error: Bad audio format %d\n", as->fmt);
 | 
			
		||||
        goto deffmt;
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
    obt_as.endianness = AUDIO_HOST_ENDIANNESS;
 | 
			
		||||
 | 
			
		||||
    audio_pcm_init_info (&hw->info, &obt_as);
 | 
			
		||||
 | 
			
		||||
    hw->samples = conf.samples;
 | 
			
		||||
    esd->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
 | 
			
		||||
    if (!esd->pcm_buf) {
 | 
			
		||||
        dolog ("Could not allocate buffer (%d bytes)\n",
 | 
			
		||||
               hw->samples << hw->info.shift);
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    esd->fd = -1;
 | 
			
		||||
    err = pthread_sigmask (SIG_BLOCK, &set, &old_set);
 | 
			
		||||
    if (err) {
 | 
			
		||||
        qesd_logerr (err, "pthread_sigmask failed\n");
 | 
			
		||||
        goto fail1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    esd->fd = esd_play_stream (esdfmt, as->freq, conf.dac_host, NULL);
 | 
			
		||||
    if (esd->fd < 0) {
 | 
			
		||||
        qesd_logerr (errno, "esd_play_stream failed\n");
 | 
			
		||||
        goto fail2;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (audio_pt_init (&esd->pt, qesd_thread_out, esd, AUDIO_CAP, AUDIO_FUNC)) {
 | 
			
		||||
        goto fail3;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    err = pthread_sigmask (SIG_SETMASK, &old_set, NULL);
 | 
			
		||||
    if (err) {
 | 
			
		||||
        qesd_logerr (err, "pthread_sigmask(restore) failed\n");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
 | 
			
		||||
 fail3:
 | 
			
		||||
    if (close (esd->fd)) {
 | 
			
		||||
        qesd_logerr (errno, "%s: close on esd socket(%d) failed\n",
 | 
			
		||||
                     AUDIO_FUNC, esd->fd);
 | 
			
		||||
    }
 | 
			
		||||
    esd->fd = -1;
 | 
			
		||||
 | 
			
		||||
 fail2:
 | 
			
		||||
    err = pthread_sigmask (SIG_SETMASK, &old_set, NULL);
 | 
			
		||||
    if (err) {
 | 
			
		||||
        qesd_logerr (err, "pthread_sigmask(restore) failed\n");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 fail1:
 | 
			
		||||
    qemu_free (esd->pcm_buf);
 | 
			
		||||
    esd->pcm_buf = NULL;
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void qesd_fini_out (HWVoiceOut *hw)
 | 
			
		||||
{
 | 
			
		||||
    void *ret;
 | 
			
		||||
    ESDVoiceOut *esd = (ESDVoiceOut *) hw;
 | 
			
		||||
 | 
			
		||||
    audio_pt_lock (&esd->pt, AUDIO_FUNC);
 | 
			
		||||
    esd->done = 1;
 | 
			
		||||
    audio_pt_unlock_and_signal (&esd->pt, AUDIO_FUNC);
 | 
			
		||||
    audio_pt_join (&esd->pt, &ret, AUDIO_FUNC);
 | 
			
		||||
 | 
			
		||||
    if (esd->fd >= 0) {
 | 
			
		||||
        if (close (esd->fd)) {
 | 
			
		||||
            qesd_logerr (errno, "failed to close esd socket\n");
 | 
			
		||||
        }
 | 
			
		||||
        esd->fd = -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    audio_pt_fini (&esd->pt, AUDIO_FUNC);
 | 
			
		||||
 | 
			
		||||
    qemu_free (esd->pcm_buf);
 | 
			
		||||
    esd->pcm_buf = NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int qesd_ctl_out (HWVoiceOut *hw, int cmd, ...)
 | 
			
		||||
{
 | 
			
		||||
    (void) hw;
 | 
			
		||||
    (void) cmd;
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* capture */
 | 
			
		||||
static void *qesd_thread_in (void *arg)
 | 
			
		||||
{
 | 
			
		||||
    ESDVoiceIn *esd = arg;
 | 
			
		||||
    HWVoiceIn *hw = &esd->hw;
 | 
			
		||||
    int threshold;
 | 
			
		||||
 | 
			
		||||
    threshold = conf.divisor ? hw->samples / conf.divisor : 0;
 | 
			
		||||
 | 
			
		||||
    if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (;;) {
 | 
			
		||||
        int incr, to_grab, wpos;
 | 
			
		||||
 | 
			
		||||
        for (;;) {
 | 
			
		||||
            if (esd->done) {
 | 
			
		||||
                goto exit;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (esd->dead > threshold) {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (audio_pt_wait (&esd->pt, AUDIO_FUNC)) {
 | 
			
		||||
                goto exit;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        incr = to_grab = esd->dead;
 | 
			
		||||
        wpos = hw->wpos;
 | 
			
		||||
 | 
			
		||||
        if (audio_pt_unlock (&esd->pt, AUDIO_FUNC)) {
 | 
			
		||||
            return NULL;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        while (to_grab) {
 | 
			
		||||
            ssize_t nread;
 | 
			
		||||
            int chunk = audio_MIN (to_grab, hw->samples - wpos);
 | 
			
		||||
            void *buf = advance (esd->pcm_buf, wpos);
 | 
			
		||||
 | 
			
		||||
        again:
 | 
			
		||||
            nread = read (esd->fd, buf, chunk << hw->info.shift);
 | 
			
		||||
            if (nread == -1) {
 | 
			
		||||
                if (errno == EINTR || errno == EAGAIN) {
 | 
			
		||||
                    goto again;
 | 
			
		||||
                }
 | 
			
		||||
                qesd_logerr (errno, "read failed\n");
 | 
			
		||||
                return NULL;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (nread != chunk << hw->info.shift) {
 | 
			
		||||
                int rsamples = nread >> hw->info.shift;
 | 
			
		||||
                int rbytes = rsamples << hw->info.shift;
 | 
			
		||||
                if (rbytes != nread) {
 | 
			
		||||
                    dolog ("warning: Misaligned write %d (requested %zd), "
 | 
			
		||||
                           "alignment %d\n",
 | 
			
		||||
                           rbytes, nread, hw->info.align + 1);
 | 
			
		||||
                }
 | 
			
		||||
                to_grab -= rsamples;
 | 
			
		||||
                wpos = (wpos + rsamples) % hw->samples;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            hw->conv (hw->conv_buf + wpos, buf, nread >> hw->info.shift,
 | 
			
		||||
                      &nominal_volume);
 | 
			
		||||
            wpos = (wpos + chunk) % hw->samples;
 | 
			
		||||
            to_grab -= chunk;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
 | 
			
		||||
            return NULL;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        esd->wpos = wpos;
 | 
			
		||||
        esd->dead -= incr;
 | 
			
		||||
        esd->incr += incr;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 exit:
 | 
			
		||||
    audio_pt_unlock (&esd->pt, AUDIO_FUNC);
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int qesd_run_in (HWVoiceIn *hw)
 | 
			
		||||
{
 | 
			
		||||
    int live, incr, dead;
 | 
			
		||||
    ESDVoiceIn *esd = (ESDVoiceIn *) hw;
 | 
			
		||||
 | 
			
		||||
    if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    live = audio_pcm_hw_get_live_in (hw);
 | 
			
		||||
    dead = hw->samples - live;
 | 
			
		||||
    incr = audio_MIN (dead, esd->incr);
 | 
			
		||||
    esd->incr -= incr;
 | 
			
		||||
    esd->dead = dead - incr;
 | 
			
		||||
    hw->wpos = esd->wpos;
 | 
			
		||||
    if (esd->dead > 0) {
 | 
			
		||||
        audio_pt_unlock_and_signal (&esd->pt, AUDIO_FUNC);
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
        audio_pt_unlock (&esd->pt, AUDIO_FUNC);
 | 
			
		||||
    }
 | 
			
		||||
    return incr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int qesd_read (SWVoiceIn *sw, void *buf, int len)
 | 
			
		||||
{
 | 
			
		||||
    return audio_pcm_sw_read (sw, buf, len);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int qesd_init_in (HWVoiceIn *hw, struct audsettings *as)
 | 
			
		||||
{
 | 
			
		||||
    ESDVoiceIn *esd = (ESDVoiceIn *) hw;
 | 
			
		||||
    struct audsettings obt_as = *as;
 | 
			
		||||
    int esdfmt = ESD_STREAM | ESD_RECORD;
 | 
			
		||||
    int err;
 | 
			
		||||
    sigset_t set, old_set;
 | 
			
		||||
 | 
			
		||||
    sigfillset (&set);
 | 
			
		||||
 | 
			
		||||
    esdfmt |= (as->nchannels == 2) ? ESD_STEREO : ESD_MONO;
 | 
			
		||||
    switch (as->fmt) {
 | 
			
		||||
    case AUD_FMT_S8:
 | 
			
		||||
    case AUD_FMT_U8:
 | 
			
		||||
        esdfmt |= ESD_BITS8;
 | 
			
		||||
        obt_as.fmt = AUD_FMT_U8;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case AUD_FMT_S16:
 | 
			
		||||
    case AUD_FMT_U16:
 | 
			
		||||
        esdfmt |= ESD_BITS16;
 | 
			
		||||
        obt_as.fmt = AUD_FMT_S16;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case AUD_FMT_S32:
 | 
			
		||||
    case AUD_FMT_U32:
 | 
			
		||||
        dolog ("Will use 16 instead of 32 bit samples\n");
 | 
			
		||||
        esdfmt |= ESD_BITS16;
 | 
			
		||||
        obt_as.fmt = AUD_FMT_S16;
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
    obt_as.endianness = AUDIO_HOST_ENDIANNESS;
 | 
			
		||||
 | 
			
		||||
    audio_pcm_init_info (&hw->info, &obt_as);
 | 
			
		||||
 | 
			
		||||
    hw->samples = conf.samples;
 | 
			
		||||
    esd->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
 | 
			
		||||
    if (!esd->pcm_buf) {
 | 
			
		||||
        dolog ("Could not allocate buffer (%d bytes)\n",
 | 
			
		||||
               hw->samples << hw->info.shift);
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    esd->fd = -1;
 | 
			
		||||
 | 
			
		||||
    err = pthread_sigmask (SIG_BLOCK, &set, &old_set);
 | 
			
		||||
    if (err) {
 | 
			
		||||
        qesd_logerr (err, "pthread_sigmask failed\n");
 | 
			
		||||
        goto fail1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    esd->fd = esd_record_stream (esdfmt, as->freq, conf.adc_host, NULL);
 | 
			
		||||
    if (esd->fd < 0) {
 | 
			
		||||
        qesd_logerr (errno, "esd_record_stream failed\n");
 | 
			
		||||
        goto fail2;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (audio_pt_init (&esd->pt, qesd_thread_in, esd, AUDIO_CAP, AUDIO_FUNC)) {
 | 
			
		||||
        goto fail3;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    err = pthread_sigmask (SIG_SETMASK, &old_set, NULL);
 | 
			
		||||
    if (err) {
 | 
			
		||||
        qesd_logerr (err, "pthread_sigmask(restore) failed\n");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
 | 
			
		||||
 fail3:
 | 
			
		||||
    if (close (esd->fd)) {
 | 
			
		||||
        qesd_logerr (errno, "%s: close on esd socket(%d) failed\n",
 | 
			
		||||
                     AUDIO_FUNC, esd->fd);
 | 
			
		||||
    }
 | 
			
		||||
    esd->fd = -1;
 | 
			
		||||
 | 
			
		||||
 fail2:
 | 
			
		||||
    err = pthread_sigmask (SIG_SETMASK, &old_set, NULL);
 | 
			
		||||
    if (err) {
 | 
			
		||||
        qesd_logerr (err, "pthread_sigmask(restore) failed\n");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 fail1:
 | 
			
		||||
    qemu_free (esd->pcm_buf);
 | 
			
		||||
    esd->pcm_buf = NULL;
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void qesd_fini_in (HWVoiceIn *hw)
 | 
			
		||||
{
 | 
			
		||||
    void *ret;
 | 
			
		||||
    ESDVoiceIn *esd = (ESDVoiceIn *) hw;
 | 
			
		||||
 | 
			
		||||
    audio_pt_lock (&esd->pt, AUDIO_FUNC);
 | 
			
		||||
    esd->done = 1;
 | 
			
		||||
    audio_pt_unlock_and_signal (&esd->pt, AUDIO_FUNC);
 | 
			
		||||
    audio_pt_join (&esd->pt, &ret, AUDIO_FUNC);
 | 
			
		||||
 | 
			
		||||
    if (esd->fd >= 0) {
 | 
			
		||||
        if (close (esd->fd)) {
 | 
			
		||||
            qesd_logerr (errno, "failed to close esd socket\n");
 | 
			
		||||
        }
 | 
			
		||||
        esd->fd = -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    audio_pt_fini (&esd->pt, AUDIO_FUNC);
 | 
			
		||||
 | 
			
		||||
    qemu_free (esd->pcm_buf);
 | 
			
		||||
    esd->pcm_buf = NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int qesd_ctl_in (HWVoiceIn *hw, int cmd, ...)
 | 
			
		||||
{
 | 
			
		||||
    (void) hw;
 | 
			
		||||
    (void) cmd;
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* common */
 | 
			
		||||
static void *qesd_audio_init (void)
 | 
			
		||||
{
 | 
			
		||||
    return &conf;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void qesd_audio_fini (void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    (void) opaque;
 | 
			
		||||
    ldebug ("esd_fini");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct audio_option qesd_options[] = {
 | 
			
		||||
    {
 | 
			
		||||
        .name  = "SAMPLES",
 | 
			
		||||
        .tag   = AUD_OPT_INT,
 | 
			
		||||
        .valp  = &conf.samples,
 | 
			
		||||
        .descr = "buffer size in samples"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        .name  = "DIVISOR",
 | 
			
		||||
        .tag   = AUD_OPT_INT,
 | 
			
		||||
        .valp  = &conf.divisor,
 | 
			
		||||
        .descr = "threshold divisor"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        .name  = "DAC_HOST",
 | 
			
		||||
        .tag   = AUD_OPT_STR,
 | 
			
		||||
        .valp  = &conf.dac_host,
 | 
			
		||||
        .descr = "playback host"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        .name  = "ADC_HOST",
 | 
			
		||||
        .tag   = AUD_OPT_STR,
 | 
			
		||||
        .valp  = &conf.adc_host,
 | 
			
		||||
        .descr = "capture host"
 | 
			
		||||
    },
 | 
			
		||||
    { /* End of list */ }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct audio_pcm_ops qesd_pcm_ops = {
 | 
			
		||||
    .init_out = qesd_init_out,
 | 
			
		||||
    .fini_out = qesd_fini_out,
 | 
			
		||||
    .run_out  = qesd_run_out,
 | 
			
		||||
    .write    = qesd_write,
 | 
			
		||||
    .ctl_out  = qesd_ctl_out,
 | 
			
		||||
 | 
			
		||||
    .init_in  = qesd_init_in,
 | 
			
		||||
    .fini_in  = qesd_fini_in,
 | 
			
		||||
    .run_in   = qesd_run_in,
 | 
			
		||||
    .read     = qesd_read,
 | 
			
		||||
    .ctl_in   = qesd_ctl_in,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct audio_driver esd_audio_driver = {
 | 
			
		||||
    .name           = "esd",
 | 
			
		||||
    .descr          = "http://en.wikipedia.org/wiki/Esound",
 | 
			
		||||
    .options        = qesd_options,
 | 
			
		||||
    .init           = qesd_audio_init,
 | 
			
		||||
    .fini           = qesd_audio_fini,
 | 
			
		||||
    .pcm_ops        = &qesd_pcm_ops,
 | 
			
		||||
    .can_be_default = 0,
 | 
			
		||||
    .max_voices_out = INT_MAX,
 | 
			
		||||
    .max_voices_in  = INT_MAX,
 | 
			
		||||
    .voice_size_out = sizeof (ESDVoiceOut),
 | 
			
		||||
    .voice_size_in  = sizeof (ESDVoiceIn)
 | 
			
		||||
};
 | 
			
		||||
@@ -23,8 +23,7 @@
 | 
			
		||||
 */
 | 
			
		||||
#include <fmod.h>
 | 
			
		||||
#include <fmod_errors.h>
 | 
			
		||||
#include "qemu-common.h"
 | 
			
		||||
#include "audio.h"
 | 
			
		||||
#include "vl.h"
 | 
			
		||||
 | 
			
		||||
#define AUDIO_CAP "fmod"
 | 
			
		||||
#include "audio_int.h"
 | 
			
		||||
@@ -47,11 +46,16 @@ static struct {
 | 
			
		||||
    int freq;
 | 
			
		||||
    int nb_channels;
 | 
			
		||||
    int bufsize;
 | 
			
		||||
    int threshold;
 | 
			
		||||
    int broken_adc;
 | 
			
		||||
} conf = {
 | 
			
		||||
    .nb_samples  = 2048 * 2,
 | 
			
		||||
    .freq        = 44100,
 | 
			
		||||
    .nb_channels = 2,
 | 
			
		||||
    NULL,
 | 
			
		||||
    2048 * 2,
 | 
			
		||||
    44100,
 | 
			
		||||
    2,
 | 
			
		||||
    0,
 | 
			
		||||
    0,
 | 
			
		||||
    0
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void GCC_FMT_ATTR (1, 2) fmod_logerr (const char *fmt, ...)
 | 
			
		||||
@@ -137,8 +141,8 @@ 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;
 | 
			
		||||
    struct st_sample *src1 = hw->mix_buf + hw->rpos;
 | 
			
		||||
    struct st_sample *src2 = NULL;
 | 
			
		||||
    st_sample_t *src1 = hw->mix_buf + hw->rpos;
 | 
			
		||||
    st_sample_t *src2 = NULL;
 | 
			
		||||
 | 
			
		||||
    if (pos > hw->samples) {
 | 
			
		||||
        src_len1 = hw->samples - hw->rpos;
 | 
			
		||||
@@ -149,11 +153,13 @@ static void fmod_write_sample (HWVoiceOut *hw, uint8_t *dst, int dst_len)
 | 
			
		||||
 | 
			
		||||
    if (src_len1) {
 | 
			
		||||
        hw->clip (dst, src1, src_len1);
 | 
			
		||||
        mixeng_clear (src1, src_len1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (src_len2) {
 | 
			
		||||
        dst = advance (dst, src_len1 << hw->info.shift);
 | 
			
		||||
        hw->clip (dst, src2, src_len2);
 | 
			
		||||
        mixeng_clear (src2, src_len2);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    hw->rpos = pos % hw->samples;
 | 
			
		||||
@@ -224,15 +230,24 @@ static int fmod_lock_sample (
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int fmod_run_out (HWVoiceOut *hw, int live)
 | 
			
		||||
static int fmod_run_out (HWVoiceOut *hw)
 | 
			
		||||
{
 | 
			
		||||
    FMODVoiceOut *fmd = (FMODVoiceOut *) hw;
 | 
			
		||||
    int decr;
 | 
			
		||||
    int live, decr;
 | 
			
		||||
    void *p1 = 0, *p2 = 0;
 | 
			
		||||
    unsigned int blen1 = 0, blen2 = 0;
 | 
			
		||||
    unsigned int len1 = 0, len2 = 0;
 | 
			
		||||
    int nb_live;
 | 
			
		||||
 | 
			
		||||
    if (!hw->pending_disable) {
 | 
			
		||||
    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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -341,11 +356,10 @@ static void fmod_fini_out (HWVoiceOut *hw)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int fmod_init_out (HWVoiceOut *hw, struct audsettings *as)
 | 
			
		||||
static int fmod_init_out (HWVoiceOut *hw, audsettings_t *as)
 | 
			
		||||
{
 | 
			
		||||
    int bits16, mode, channel;
 | 
			
		||||
    FMODVoiceOut *fmd = (FMODVoiceOut *) hw;
 | 
			
		||||
    struct audsettings obt_as = *as;
 | 
			
		||||
 | 
			
		||||
    mode = aud_to_fmodfmt (as->fmt, as->nchannels == 2 ? 1 : 0);
 | 
			
		||||
    fmd->fmod_sample = FSOUND_Sample_Alloc (
 | 
			
		||||
@@ -372,8 +386,7 @@ static int fmod_init_out (HWVoiceOut *hw, struct audsettings *as)
 | 
			
		||||
    fmd->channel = channel;
 | 
			
		||||
 | 
			
		||||
    /* FMOD always operates on little endian frames? */
 | 
			
		||||
    obt_as.endianness = 0;
 | 
			
		||||
    audio_pcm_init_info (&hw->info, &obt_as);
 | 
			
		||||
    audio_pcm_init_info (&hw->info, as, audio_need_to_swap_endian (0));
 | 
			
		||||
    bits16 = (mode & FSOUND_16BITS) != 0;
 | 
			
		||||
    hw->samples = conf.nb_samples;
 | 
			
		||||
    return 0;
 | 
			
		||||
@@ -403,11 +416,10 @@ static int fmod_ctl_out (HWVoiceOut *hw, int cmd, ...)
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int fmod_init_in (HWVoiceIn *hw, struct audsettings *as)
 | 
			
		||||
static int fmod_init_in (HWVoiceIn *hw, audsettings_t *as)
 | 
			
		||||
{
 | 
			
		||||
    int bits16, mode;
 | 
			
		||||
    FMODVoiceIn *fmd = (FMODVoiceIn *) hw;
 | 
			
		||||
    struct audsettings obt_as = *as;
 | 
			
		||||
 | 
			
		||||
    if (conf.broken_adc) {
 | 
			
		||||
        return -1;
 | 
			
		||||
@@ -430,8 +442,7 @@ static int fmod_init_in (HWVoiceIn *hw, struct audsettings *as)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* FMOD always operates on little endian frames? */
 | 
			
		||||
    obt_as.endianness = 0;
 | 
			
		||||
    audio_pcm_init_info (&hw->info, &obt_as);
 | 
			
		||||
    audio_pcm_init_info (&hw->info, as, audio_need_to_swap_endian (0));
 | 
			
		||||
    bits16 = (mode & FSOUND_16BITS) != 0;
 | 
			
		||||
    hw->samples = conf.nb_samples;
 | 
			
		||||
    return 0;
 | 
			
		||||
@@ -503,27 +514,27 @@ static struct {
 | 
			
		||||
    const char *name;
 | 
			
		||||
    int type;
 | 
			
		||||
} drvtab[] = {
 | 
			
		||||
    { .name = "none",   .type = FSOUND_OUTPUT_NOSOUND },
 | 
			
		||||
    {"none", FSOUND_OUTPUT_NOSOUND},
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
    { .name = "winmm",  .type = FSOUND_OUTPUT_WINMM   },
 | 
			
		||||
    { .name = "dsound", .type = FSOUND_OUTPUT_DSOUND  },
 | 
			
		||||
    { .name = "a3d",    .type = FSOUND_OUTPUT_A3D     },
 | 
			
		||||
    { .name = "asio",   .type = FSOUND_OUTPUT_ASIO    },
 | 
			
		||||
    {"winmm", FSOUND_OUTPUT_WINMM},
 | 
			
		||||
    {"dsound", FSOUND_OUTPUT_DSOUND},
 | 
			
		||||
    {"a3d", FSOUND_OUTPUT_A3D},
 | 
			
		||||
    {"asio", FSOUND_OUTPUT_ASIO},
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef __linux__
 | 
			
		||||
    { .name = "oss",    .type = FSOUND_OUTPUT_OSS     },
 | 
			
		||||
    { .name = "alsa",   .type = FSOUND_OUTPUT_ALSA    },
 | 
			
		||||
    { .name = "esd",    .type = FSOUND_OUTPUT_ESD     },
 | 
			
		||||
    {"oss", FSOUND_OUTPUT_OSS},
 | 
			
		||||
    {"alsa", FSOUND_OUTPUT_ALSA},
 | 
			
		||||
    {"esd", FSOUND_OUTPUT_ESD},
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef __APPLE__
 | 
			
		||||
    { .name = "mac",    .type = FSOUND_OUTPUT_MAC     },
 | 
			
		||||
    {"mac", FSOUND_OUTPUT_MAC},
 | 
			
		||||
#endif
 | 
			
		||||
#if 0
 | 
			
		||||
    { .name = "xbox",   .type = FSOUND_OUTPUT_XBOX    },
 | 
			
		||||
    { .name = "ps2",    .type = FSOUND_OUTPUT_PS2     },
 | 
			
		||||
    { .name = "gcube",  .type = FSOUND_OUTPUT_GC      },
 | 
			
		||||
    {"xbox", FSOUND_OUTPUT_XBOX},
 | 
			
		||||
    {"ps2", FSOUND_OUTPUT_PS2},
 | 
			
		||||
    {"gcube", FSOUND_OUTPUT_GC},
 | 
			
		||||
#endif
 | 
			
		||||
    { .name = "none-realtime", .type = FSOUND_OUTPUT_NOSOUND_NONREALTIME }
 | 
			
		||||
    {"none-realtime", FSOUND_OUTPUT_NOSOUND_NONREALTIME}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void *fmod_audio_init (void)
 | 
			
		||||
@@ -550,7 +561,7 @@ static void *fmod_audio_init (void)
 | 
			
		||||
 | 
			
		||||
    if (drv) {
 | 
			
		||||
        int found = 0;
 | 
			
		||||
        for (i = 0; i < ARRAY_SIZE (drvtab); i++) {
 | 
			
		||||
        for (i = 0; i < sizeof (drvtab) / sizeof (drvtab[0]); i++) {
 | 
			
		||||
            if (!strcmp (drv, drvtab[i].name)) {
 | 
			
		||||
                output_type = drvtab[i].type;
 | 
			
		||||
                found = 1;
 | 
			
		||||
@@ -560,7 +571,7 @@ static void *fmod_audio_init (void)
 | 
			
		||||
        if (!found) {
 | 
			
		||||
            dolog ("Unknown FMOD driver `%s'\n", drv);
 | 
			
		||||
            dolog ("Valid drivers:\n");
 | 
			
		||||
            for (i = 0; i < ARRAY_SIZE (drvtab); i++) {
 | 
			
		||||
            for (i = 0; i < sizeof (drvtab) / sizeof (drvtab[0]); i++) {
 | 
			
		||||
                dolog ("  %s\n", drvtab[i].name);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
@@ -625,63 +636,48 @@ static void fmod_audio_fini (void *opaque)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct audio_option fmod_options[] = {
 | 
			
		||||
    {
 | 
			
		||||
        .name  = "DRV",
 | 
			
		||||
        .tag   = AUD_OPT_STR,
 | 
			
		||||
        .valp  = &conf.drvname,
 | 
			
		||||
        .descr = "FMOD driver"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        .name  = "FREQ",
 | 
			
		||||
        .tag   = AUD_OPT_INT,
 | 
			
		||||
        .valp  = &conf.freq,
 | 
			
		||||
        .descr = "Default frequency"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        .name  = "SAMPLES",
 | 
			
		||||
        .tag   = AUD_OPT_INT,
 | 
			
		||||
        .valp  = &conf.nb_samples,
 | 
			
		||||
        .descr = "Buffer size in samples"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        .name  = "CHANNELS",
 | 
			
		||||
        .tag   = AUD_OPT_INT,
 | 
			
		||||
        .valp  = &conf.nb_channels,
 | 
			
		||||
        .descr = "Number of default channels (1 - mono, 2 - stereo)"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        .name  = "BUFSIZE",
 | 
			
		||||
        .tag   = AUD_OPT_INT,
 | 
			
		||||
        .valp  = &conf.bufsize,
 | 
			
		||||
        .descr = "(undocumented)"
 | 
			
		||||
    },
 | 
			
		||||
    { /* End of list */ }
 | 
			
		||||
    {"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 = {
 | 
			
		||||
    .init_out = fmod_init_out,
 | 
			
		||||
    .fini_out = fmod_fini_out,
 | 
			
		||||
    .run_out  = fmod_run_out,
 | 
			
		||||
    .write    = fmod_write,
 | 
			
		||||
    .ctl_out  = fmod_ctl_out,
 | 
			
		||||
    fmod_init_out,
 | 
			
		||||
    fmod_fini_out,
 | 
			
		||||
    fmod_run_out,
 | 
			
		||||
    fmod_write,
 | 
			
		||||
    fmod_ctl_out,
 | 
			
		||||
 | 
			
		||||
    .init_in  = fmod_init_in,
 | 
			
		||||
    .fini_in  = fmod_fini_in,
 | 
			
		||||
    .run_in   = fmod_run_in,
 | 
			
		||||
    .read     = fmod_read,
 | 
			
		||||
    .ctl_in   = fmod_ctl_in
 | 
			
		||||
    fmod_init_in,
 | 
			
		||||
    fmod_fini_in,
 | 
			
		||||
    fmod_run_in,
 | 
			
		||||
    fmod_read,
 | 
			
		||||
    fmod_ctl_in
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct audio_driver fmod_audio_driver = {
 | 
			
		||||
    .name           = "fmod",
 | 
			
		||||
    .descr          = "FMOD 3.xx http://www.fmod.org",
 | 
			
		||||
    .options        = fmod_options,
 | 
			
		||||
    .init           = fmod_audio_init,
 | 
			
		||||
    .fini           = fmod_audio_fini,
 | 
			
		||||
    .pcm_ops        = &fmod_pcm_ops,
 | 
			
		||||
    .can_be_default = 1,
 | 
			
		||||
    .max_voices_out = INT_MAX,
 | 
			
		||||
    .max_voices_in  = INT_MAX,
 | 
			
		||||
    .voice_size_out = sizeof (FMODVoiceOut),
 | 
			
		||||
    .voice_size_in  = sizeof (FMODVoiceIn)
 | 
			
		||||
    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)
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										106
									
								
								audio/mixeng.c
									
									
									
									
									
								
							
							
						
						
									
										106
									
								
								audio/mixeng.c
									
									
									
									
									
								
							@@ -22,12 +22,13 @@
 | 
			
		||||
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
			
		||||
 * THE SOFTWARE.
 | 
			
		||||
 */
 | 
			
		||||
#include "qemu-common.h"
 | 
			
		||||
#include "audio.h"
 | 
			
		||||
#include "vl.h"
 | 
			
		||||
 | 
			
		||||
#define AUDIO_CAP "mixeng"
 | 
			
		||||
#include "audio_int.h"
 | 
			
		||||
 | 
			
		||||
#define NOVOL
 | 
			
		||||
 | 
			
		||||
/* 8 bit */
 | 
			
		||||
#define ENDIAN_CONVERSION natural
 | 
			
		||||
#define ENDIAN_CONVERT(v) (v)
 | 
			
		||||
@@ -81,7 +82,6 @@
 | 
			
		||||
#undef IN_T
 | 
			
		||||
#undef SHIFT
 | 
			
		||||
 | 
			
		||||
/* Unsigned 16 bit */
 | 
			
		||||
#define IN_T uint16_t
 | 
			
		||||
#define IN_MIN 0
 | 
			
		||||
#define IN_MAX USHRT_MAX
 | 
			
		||||
@@ -101,72 +101,26 @@
 | 
			
		||||
#undef IN_T
 | 
			
		||||
#undef SHIFT
 | 
			
		||||
 | 
			
		||||
/* Signed 32 bit */
 | 
			
		||||
#define IN_T int32_t
 | 
			
		||||
#define IN_MIN INT32_MIN
 | 
			
		||||
#define IN_MAX INT32_MAX
 | 
			
		||||
#define SIGNED
 | 
			
		||||
#define SHIFT 32
 | 
			
		||||
#define ENDIAN_CONVERSION natural
 | 
			
		||||
#define ENDIAN_CONVERT(v) (v)
 | 
			
		||||
#include "mixeng_template.h"
 | 
			
		||||
#undef ENDIAN_CONVERT
 | 
			
		||||
#undef ENDIAN_CONVERSION
 | 
			
		||||
#define ENDIAN_CONVERSION swap
 | 
			
		||||
#define ENDIAN_CONVERT(v) bswap32 (v)
 | 
			
		||||
#include "mixeng_template.h"
 | 
			
		||||
#undef ENDIAN_CONVERT
 | 
			
		||||
#undef ENDIAN_CONVERSION
 | 
			
		||||
#undef SIGNED
 | 
			
		||||
#undef IN_MAX
 | 
			
		||||
#undef IN_MIN
 | 
			
		||||
#undef IN_T
 | 
			
		||||
#undef SHIFT
 | 
			
		||||
 | 
			
		||||
/* Unsigned 32 bit */
 | 
			
		||||
#define IN_T uint32_t
 | 
			
		||||
#define IN_MIN 0
 | 
			
		||||
#define IN_MAX UINT32_MAX
 | 
			
		||||
#define SHIFT 32
 | 
			
		||||
#define ENDIAN_CONVERSION natural
 | 
			
		||||
#define ENDIAN_CONVERT(v) (v)
 | 
			
		||||
#include "mixeng_template.h"
 | 
			
		||||
#undef ENDIAN_CONVERT
 | 
			
		||||
#undef ENDIAN_CONVERSION
 | 
			
		||||
#define ENDIAN_CONVERSION swap
 | 
			
		||||
#define ENDIAN_CONVERT(v) bswap32 (v)
 | 
			
		||||
#include "mixeng_template.h"
 | 
			
		||||
#undef ENDIAN_CONVERT
 | 
			
		||||
#undef ENDIAN_CONVERSION
 | 
			
		||||
#undef IN_MAX
 | 
			
		||||
#undef IN_MIN
 | 
			
		||||
#undef IN_T
 | 
			
		||||
#undef SHIFT
 | 
			
		||||
 | 
			
		||||
t_sample *mixeng_conv[2][2][2][3] = {
 | 
			
		||||
t_sample *mixeng_conv[2][2][2][2] = {
 | 
			
		||||
    {
 | 
			
		||||
        {
 | 
			
		||||
            {
 | 
			
		||||
                conv_natural_uint8_t_to_mono,
 | 
			
		||||
                conv_natural_uint16_t_to_mono,
 | 
			
		||||
                conv_natural_uint32_t_to_mono
 | 
			
		||||
                conv_natural_uint16_t_to_mono
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                conv_natural_uint8_t_to_mono,
 | 
			
		||||
                conv_swap_uint16_t_to_mono,
 | 
			
		||||
                conv_swap_uint32_t_to_mono,
 | 
			
		||||
                conv_swap_uint16_t_to_mono
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            {
 | 
			
		||||
                conv_natural_int8_t_to_mono,
 | 
			
		||||
                conv_natural_int16_t_to_mono,
 | 
			
		||||
                conv_natural_int32_t_to_mono
 | 
			
		||||
                conv_natural_int16_t_to_mono
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                conv_natural_int8_t_to_mono,
 | 
			
		||||
                conv_swap_int16_t_to_mono,
 | 
			
		||||
                conv_swap_int32_t_to_mono
 | 
			
		||||
                conv_swap_int16_t_to_mono
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
@@ -174,54 +128,46 @@ t_sample *mixeng_conv[2][2][2][3] = {
 | 
			
		||||
        {
 | 
			
		||||
            {
 | 
			
		||||
                conv_natural_uint8_t_to_stereo,
 | 
			
		||||
                conv_natural_uint16_t_to_stereo,
 | 
			
		||||
                conv_natural_uint32_t_to_stereo
 | 
			
		||||
                conv_natural_uint16_t_to_stereo
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                conv_natural_uint8_t_to_stereo,
 | 
			
		||||
                conv_swap_uint16_t_to_stereo,
 | 
			
		||||
                conv_swap_uint32_t_to_stereo
 | 
			
		||||
                conv_swap_uint16_t_to_stereo
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            {
 | 
			
		||||
                conv_natural_int8_t_to_stereo,
 | 
			
		||||
                conv_natural_int16_t_to_stereo,
 | 
			
		||||
                conv_natural_int32_t_to_stereo
 | 
			
		||||
                conv_natural_int16_t_to_stereo
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                conv_natural_int8_t_to_stereo,
 | 
			
		||||
                conv_swap_int16_t_to_stereo,
 | 
			
		||||
                conv_swap_int32_t_to_stereo,
 | 
			
		||||
                conv_swap_int16_t_to_stereo
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
f_sample *mixeng_clip[2][2][2][3] = {
 | 
			
		||||
f_sample *mixeng_clip[2][2][2][2] = {
 | 
			
		||||
    {
 | 
			
		||||
        {
 | 
			
		||||
            {
 | 
			
		||||
                clip_natural_uint8_t_from_mono,
 | 
			
		||||
                clip_natural_uint16_t_from_mono,
 | 
			
		||||
                clip_natural_uint32_t_from_mono
 | 
			
		||||
                clip_natural_uint16_t_from_mono
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                clip_natural_uint8_t_from_mono,
 | 
			
		||||
                clip_swap_uint16_t_from_mono,
 | 
			
		||||
                clip_swap_uint32_t_from_mono
 | 
			
		||||
                clip_swap_uint16_t_from_mono
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            {
 | 
			
		||||
                clip_natural_int8_t_from_mono,
 | 
			
		||||
                clip_natural_int16_t_from_mono,
 | 
			
		||||
                clip_natural_int32_t_from_mono
 | 
			
		||||
                clip_natural_int16_t_from_mono
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                clip_natural_int8_t_from_mono,
 | 
			
		||||
                clip_swap_int16_t_from_mono,
 | 
			
		||||
                clip_swap_int32_t_from_mono
 | 
			
		||||
                clip_swap_int16_t_from_mono
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
@@ -229,25 +175,21 @@ f_sample *mixeng_clip[2][2][2][3] = {
 | 
			
		||||
        {
 | 
			
		||||
            {
 | 
			
		||||
                clip_natural_uint8_t_from_stereo,
 | 
			
		||||
                clip_natural_uint16_t_from_stereo,
 | 
			
		||||
                clip_natural_uint32_t_from_stereo
 | 
			
		||||
                clip_natural_uint16_t_from_stereo
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                clip_natural_uint8_t_from_stereo,
 | 
			
		||||
                clip_swap_uint16_t_from_stereo,
 | 
			
		||||
                clip_swap_uint32_t_from_stereo
 | 
			
		||||
                clip_swap_uint16_t_from_stereo
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            {
 | 
			
		||||
                clip_natural_int8_t_from_stereo,
 | 
			
		||||
                clip_natural_int16_t_from_stereo,
 | 
			
		||||
                clip_natural_int32_t_from_stereo
 | 
			
		||||
                clip_natural_int16_t_from_stereo
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                clip_natural_int8_t_from_stereo,
 | 
			
		||||
                clip_swap_int16_t_from_stereo,
 | 
			
		||||
                clip_swap_int32_t_from_stereo
 | 
			
		||||
                clip_swap_int16_t_from_stereo
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -290,7 +232,7 @@ struct rate {
 | 
			
		||||
    uint64_t opos;
 | 
			
		||||
    uint64_t opos_inc;
 | 
			
		||||
    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 */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
@@ -329,7 +271,7 @@ void st_rate_stop (void *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));
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -25,27 +25,27 @@
 | 
			
		||||
#define QEMU_MIXENG_H
 | 
			
		||||
 | 
			
		||||
#ifdef FLOAT_MIXENG
 | 
			
		||||
typedef float mixeng_real;
 | 
			
		||||
struct mixeng_volume { int mute; mixeng_real r; mixeng_real l; };
 | 
			
		||||
struct st_sample { mixeng_real l; mixeng_real r; };
 | 
			
		||||
typedef float real_t;
 | 
			
		||||
typedef struct { int mute; real_t r; real_t l; } volume_t;
 | 
			
		||||
typedef struct { real_t l; real_t r; } st_sample_t;
 | 
			
		||||
#else
 | 
			
		||||
struct mixeng_volume { int mute; int64_t r; int64_t l; };
 | 
			
		||||
struct st_sample { int64_t l; int64_t r; };
 | 
			
		||||
typedef struct { int mute; int64_t r; int64_t l; } volume_t;
 | 
			
		||||
typedef struct { int64_t l; int64_t r; } st_sample_t;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
typedef void (t_sample) (struct st_sample *dst, const void *src,
 | 
			
		||||
                         int samples, struct mixeng_volume *vol);
 | 
			
		||||
typedef void (f_sample) (void *dst, const struct st_sample *src, int samples);
 | 
			
		||||
typedef void (t_sample) (st_sample_t *dst, const void *src,
 | 
			
		||||
                         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 f_sample *mixeng_clip[2][2][2][3];
 | 
			
		||||
extern t_sample *mixeng_conv[2][2][2][2];
 | 
			
		||||
extern f_sample *mixeng_clip[2][2][2][2];
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
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);
 | 
			
		||||
void st_rate_stop (void *opaque);
 | 
			
		||||
void mixeng_clear (struct st_sample *buf, int len);
 | 
			
		||||
void mixeng_clear (st_sample_t *buf, int len);
 | 
			
		||||
 | 
			
		||||
#endif  /* mixeng.h */
 | 
			
		||||
 
 | 
			
		||||
@@ -31,39 +31,39 @@
 | 
			
		||||
#define HALF (IN_MAX >> 1)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_MIXEMU
 | 
			
		||||
#ifdef NOVOL
 | 
			
		||||
#define VOL(a, b) a
 | 
			
		||||
#else
 | 
			
		||||
#ifdef FLOAT_MIXENG
 | 
			
		||||
#define VOL(a, b) ((a) * (b))
 | 
			
		||||
#else
 | 
			
		||||
#define VOL(a, b) ((a) * (b)) >> 32
 | 
			
		||||
#endif
 | 
			
		||||
#else
 | 
			
		||||
#define VOL(a, b) a
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define ET glue (ENDIAN_CONVERSION, glue (_, IN_T))
 | 
			
		||||
 | 
			
		||||
#ifdef FLOAT_MIXENG
 | 
			
		||||
static mixeng_real inline glue (conv_, ET) (IN_T v)
 | 
			
		||||
static real_t inline glue (conv_, ET) (IN_T v)
 | 
			
		||||
{
 | 
			
		||||
    IN_T nv = ENDIAN_CONVERT (v);
 | 
			
		||||
 | 
			
		||||
#ifdef RECIPROCAL
 | 
			
		||||
#ifdef SIGNED
 | 
			
		||||
    return nv * (1.f / (mixeng_real) (IN_MAX - IN_MIN));
 | 
			
		||||
    return nv * (1.f / (real_t) (IN_MAX - IN_MIN));
 | 
			
		||||
#else
 | 
			
		||||
    return (nv - HALF) * (1.f / (mixeng_real) IN_MAX);
 | 
			
		||||
    return (nv - HALF) * (1.f / (real_t) IN_MAX);
 | 
			
		||||
#endif
 | 
			
		||||
#else  /* !RECIPROCAL */
 | 
			
		||||
#ifdef SIGNED
 | 
			
		||||
    return nv / (mixeng_real) (IN_MAX - IN_MIN);
 | 
			
		||||
    return nv / (real_t) (IN_MAX - IN_MIN);
 | 
			
		||||
#else
 | 
			
		||||
    return (nv - HALF) / (mixeng_real) IN_MAX;
 | 
			
		||||
    return (nv - HALF) / (real_t) IN_MAX;
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static IN_T inline glue (clip_, ET) (mixeng_real v)
 | 
			
		||||
static IN_T inline glue (clip_, ET) (real_t v)
 | 
			
		||||
{
 | 
			
		||||
    if (v >= 0.5) {
 | 
			
		||||
        return IN_MAX;
 | 
			
		||||
@@ -109,11 +109,11 @@ static inline IN_T glue (clip_, ET) (int64_t v)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static void glue (glue (conv_, ET), _to_stereo)
 | 
			
		||||
    (struct st_sample *dst, const void *src, int samples, struct mixeng_volume *vol)
 | 
			
		||||
    (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;
 | 
			
		||||
#ifdef CONFIG_MIXEMU
 | 
			
		||||
#ifndef NOVOL
 | 
			
		||||
    if (vol->mute) {
 | 
			
		||||
        mixeng_clear (dst, samples);
 | 
			
		||||
        return;
 | 
			
		||||
@@ -129,11 +129,11 @@ static void glue (glue (conv_, ET), _to_stereo)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void glue (glue (conv_, ET), _to_mono)
 | 
			
		||||
    (struct st_sample *dst, const void *src, int samples, struct mixeng_volume *vol)
 | 
			
		||||
    (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;
 | 
			
		||||
#ifdef CONFIG_MIXEMU
 | 
			
		||||
#ifndef NOVOL
 | 
			
		||||
    if (vol->mute) {
 | 
			
		||||
        mixeng_clear (dst, samples);
 | 
			
		||||
        return;
 | 
			
		||||
@@ -150,9 +150,9 @@ static void glue (glue (conv_, ET), _to_mono)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
    while (samples--) {
 | 
			
		||||
        *out++ = glue (clip_, ET) (in->l);
 | 
			
		||||
@@ -162,9 +162,9 @@ static void glue (glue (clip_, ET), _from_stereo)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
    while (samples--) {
 | 
			
		||||
        *out++ = glue (clip_, ET) (in->l + in->r);
 | 
			
		||||
 
 | 
			
		||||
@@ -21,9 +21,7 @@
 | 
			
		||||
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
			
		||||
 * THE SOFTWARE.
 | 
			
		||||
 */
 | 
			
		||||
#include "qemu-common.h"
 | 
			
		||||
#include "audio.h"
 | 
			
		||||
#include "qemu-timer.h"
 | 
			
		||||
#include "vl.h"
 | 
			
		||||
 | 
			
		||||
#define AUDIO_CAP "noaudio"
 | 
			
		||||
#include "audio_int.h"
 | 
			
		||||
@@ -38,19 +36,25 @@ typedef struct NoVoiceIn {
 | 
			
		||||
    int64_t old_ticks;
 | 
			
		||||
} NoVoiceIn;
 | 
			
		||||
 | 
			
		||||
static int no_run_out (HWVoiceOut *hw, int live)
 | 
			
		||||
static int no_run_out (HWVoiceOut *hw)
 | 
			
		||||
{
 | 
			
		||||
    NoVoiceOut *no = (NoVoiceOut *) hw;
 | 
			
		||||
    int decr, samples;
 | 
			
		||||
    int64_t now;
 | 
			
		||||
    int64_t ticks;
 | 
			
		||||
    int64_t bytes;
 | 
			
		||||
    int live, decr, samples;
 | 
			
		||||
    int64_t now = qemu_get_clock (vm_clock);
 | 
			
		||||
    int64_t ticks = now - no->old_ticks;
 | 
			
		||||
    int64_t bytes = (ticks * hw->info.bytes_per_second) / ticks_per_sec;
 | 
			
		||||
 | 
			
		||||
    now = qemu_get_clock (vm_clock);
 | 
			
		||||
    ticks = now - no->old_ticks;
 | 
			
		||||
    bytes = muldiv64 (ticks, hw->info.bytes_per_second, get_ticks_per_sec ());
 | 
			
		||||
    bytes = audio_MIN (bytes, INT_MAX);
 | 
			
		||||
    if (bytes > INT_MAX) {
 | 
			
		||||
        samples = INT_MAX >> hw->info.shift;
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
        samples = bytes >> hw->info.shift;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    live = audio_pcm_hw_get_live_out (&no->hw);
 | 
			
		||||
    if (!live) {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    no->old_ticks = now;
 | 
			
		||||
    decr = audio_MIN (live, samples);
 | 
			
		||||
@@ -63,9 +67,9 @@ static int no_write (SWVoiceOut *sw, void *buf, int len)
 | 
			
		||||
    return audio_pcm_sw_write (sw, buf, len);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int no_init_out (HWVoiceOut *hw, struct audsettings *as)
 | 
			
		||||
static int no_init_out (HWVoiceOut *hw, audsettings_t *as)
 | 
			
		||||
{
 | 
			
		||||
    audio_pcm_init_info (&hw->info, as);
 | 
			
		||||
    audio_pcm_init_info (&hw->info, as, 0);
 | 
			
		||||
    hw->samples = 1024;
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
@@ -82,9 +86,9 @@ static int no_ctl_out (HWVoiceOut *hw, int cmd, ...)
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int no_init_in (HWVoiceIn *hw, struct audsettings *as)
 | 
			
		||||
static int no_init_in (HWVoiceIn *hw, audsettings_t *as)
 | 
			
		||||
{
 | 
			
		||||
    audio_pcm_init_info (&hw->info, as);
 | 
			
		||||
    audio_pcm_init_info (&hw->info, as, 0);
 | 
			
		||||
    hw->samples = 1024;
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
@@ -97,21 +101,17 @@ static void no_fini_in (HWVoiceIn *hw)
 | 
			
		||||
static int no_run_in (HWVoiceIn *hw)
 | 
			
		||||
{
 | 
			
		||||
    NoVoiceIn *no = (NoVoiceIn *) hw;
 | 
			
		||||
    int live = audio_pcm_hw_get_live_in (hw);
 | 
			
		||||
    int dead = hw->samples - live;
 | 
			
		||||
    int samples = 0;
 | 
			
		||||
 | 
			
		||||
    if (dead) {
 | 
			
		||||
    int64_t now = qemu_get_clock (vm_clock);
 | 
			
		||||
    int64_t ticks = now - no->old_ticks;
 | 
			
		||||
        int64_t bytes =
 | 
			
		||||
            muldiv64 (ticks, hw->info.bytes_per_second, get_ticks_per_sec ());
 | 
			
		||||
    int64_t bytes = (ticks * hw->info.bytes_per_second) / ticks_per_sec;
 | 
			
		||||
    int live = audio_pcm_hw_get_live_in (hw);
 | 
			
		||||
    int dead = hw->samples - live;
 | 
			
		||||
    int samples;
 | 
			
		||||
 | 
			
		||||
        no->old_ticks = now;
 | 
			
		||||
    bytes = audio_MIN (bytes, INT_MAX);
 | 
			
		||||
    samples = bytes >> hw->info.shift;
 | 
			
		||||
    samples = audio_MIN (samples, dead);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return samples;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -142,29 +142,29 @@ static void no_audio_fini (void *opaque)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct audio_pcm_ops no_pcm_ops = {
 | 
			
		||||
    .init_out = no_init_out,
 | 
			
		||||
    .fini_out = no_fini_out,
 | 
			
		||||
    .run_out  = no_run_out,
 | 
			
		||||
    .write    = no_write,
 | 
			
		||||
    .ctl_out  = no_ctl_out,
 | 
			
		||||
    no_init_out,
 | 
			
		||||
    no_fini_out,
 | 
			
		||||
    no_run_out,
 | 
			
		||||
    no_write,
 | 
			
		||||
    no_ctl_out,
 | 
			
		||||
 | 
			
		||||
    .init_in  = no_init_in,
 | 
			
		||||
    .fini_in  = no_fini_in,
 | 
			
		||||
    .run_in   = no_run_in,
 | 
			
		||||
    .read     = no_read,
 | 
			
		||||
    .ctl_in   = no_ctl_in
 | 
			
		||||
    no_init_in,
 | 
			
		||||
    no_fini_in,
 | 
			
		||||
    no_run_in,
 | 
			
		||||
    no_read,
 | 
			
		||||
    no_ctl_in
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct audio_driver no_audio_driver = {
 | 
			
		||||
    .name           = "none",
 | 
			
		||||
    .descr          = "Timer based audio emulation",
 | 
			
		||||
    .options        = NULL,
 | 
			
		||||
    .init           = no_audio_init,
 | 
			
		||||
    .fini           = no_audio_fini,
 | 
			
		||||
    .pcm_ops        = &no_pcm_ops,
 | 
			
		||||
    .can_be_default = 1,
 | 
			
		||||
    .max_voices_out = INT_MAX,
 | 
			
		||||
    .max_voices_in  = INT_MAX,
 | 
			
		||||
    .voice_size_out = sizeof (NoVoiceOut),
 | 
			
		||||
    .voice_size_in  = sizeof (NoVoiceIn)
 | 
			
		||||
    INIT_FIELD (name           = ) "none",
 | 
			
		||||
    INIT_FIELD (descr          = ) "Timer based audio emulation",
 | 
			
		||||
    INIT_FIELD (options        = ) NULL,
 | 
			
		||||
    INIT_FIELD (init           = ) no_audio_init,
 | 
			
		||||
    INIT_FIELD (fini           = ) no_audio_fini,
 | 
			
		||||
    INIT_FIELD (pcm_ops        = ) &no_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 (NoVoiceOut),
 | 
			
		||||
    INIT_FIELD (voice_size_in  = ) sizeof (NoVoiceIn)
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										455
									
								
								audio/ossaudio.c
									
									
									
									
									
								
							
							
						
						
									
										455
									
								
								audio/ossaudio.c
									
									
									
									
									
								
							@@ -21,36 +21,23 @@
 | 
			
		||||
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
			
		||||
 * THE SOFTWARE.
 | 
			
		||||
 */
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <sys/mman.h>
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <sys/ioctl.h>
 | 
			
		||||
#ifdef __OpenBSD__
 | 
			
		||||
#include <soundcard.h>
 | 
			
		||||
#else
 | 
			
		||||
#include <sys/soundcard.h>
 | 
			
		||||
#endif
 | 
			
		||||
#include "qemu-common.h"
 | 
			
		||||
#include "host-utils.h"
 | 
			
		||||
#include "qemu-char.h"
 | 
			
		||||
#include "audio.h"
 | 
			
		||||
#include "vl.h"
 | 
			
		||||
 | 
			
		||||
#define AUDIO_CAP "oss"
 | 
			
		||||
#include "audio_int.h"
 | 
			
		||||
 | 
			
		||||
#if defined OSS_GETVERSION && defined SNDCTL_DSP_POLICY
 | 
			
		||||
#define USE_DSP_POLICY
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
typedef struct OSSVoiceOut {
 | 
			
		||||
    HWVoiceOut hw;
 | 
			
		||||
    void *pcm_buf;
 | 
			
		||||
    int fd;
 | 
			
		||||
    int wpos;
 | 
			
		||||
    int nfrags;
 | 
			
		||||
    int fragsize;
 | 
			
		||||
    int mmapped;
 | 
			
		||||
    int pending;
 | 
			
		||||
    int old_optr;
 | 
			
		||||
} OSSVoiceOut;
 | 
			
		||||
 | 
			
		||||
typedef struct OSSVoiceIn {
 | 
			
		||||
@@ -59,6 +46,7 @@ typedef struct OSSVoiceIn {
 | 
			
		||||
    int fd;
 | 
			
		||||
    int nfrags;
 | 
			
		||||
    int fragsize;
 | 
			
		||||
    int old_optr;
 | 
			
		||||
} OSSVoiceIn;
 | 
			
		||||
 | 
			
		||||
static struct {
 | 
			
		||||
@@ -67,18 +55,12 @@ static struct {
 | 
			
		||||
    int fragsize;
 | 
			
		||||
    const char *devpath_out;
 | 
			
		||||
    const char *devpath_in;
 | 
			
		||||
    int debug;
 | 
			
		||||
    int exclusive;
 | 
			
		||||
    int policy;
 | 
			
		||||
} conf = {
 | 
			
		||||
    .try_mmap = 0,
 | 
			
		||||
    .nfrags = 4,
 | 
			
		||||
    .fragsize = 4096,
 | 
			
		||||
    .devpath_out = "/dev/dsp",
 | 
			
		||||
    .devpath_in = "/dev/dsp",
 | 
			
		||||
    .debug = 0,
 | 
			
		||||
    .exclusive = 0,
 | 
			
		||||
    .policy = 5
 | 
			
		||||
    .devpath_in = "/dev/dsp"
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct oss_params {
 | 
			
		||||
@@ -120,42 +102,13 @@ static void GCC_FMT_ATTR (3, 4) oss_logerr2 (
 | 
			
		||||
 | 
			
		||||
static void oss_anal_close (int *fdp)
 | 
			
		||||
{
 | 
			
		||||
    int err;
 | 
			
		||||
 | 
			
		||||
    qemu_set_fd_handler (*fdp, NULL, NULL, NULL);
 | 
			
		||||
    err = close (*fdp);
 | 
			
		||||
    int err = close (*fdp);
 | 
			
		||||
    if (err) {
 | 
			
		||||
        oss_logerr (errno, "Failed to close file(fd=%d)\n", *fdp);
 | 
			
		||||
    }
 | 
			
		||||
    *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 int oss_poll_out (HWVoiceOut *hw)
 | 
			
		||||
{
 | 
			
		||||
    OSSVoiceOut *oss = (OSSVoiceOut *) hw;
 | 
			
		||||
 | 
			
		||||
    return qemu_set_fd_handler (oss->fd, NULL, oss_helper_poll_out, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int oss_poll_in (HWVoiceIn *hw)
 | 
			
		||||
{
 | 
			
		||||
    OSSVoiceIn *oss = (OSSVoiceIn *) hw;
 | 
			
		||||
 | 
			
		||||
    return qemu_set_fd_handler (oss->fd, oss_helper_poll_in, NULL, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int oss_write (SWVoiceOut *sw, void *buf, int len)
 | 
			
		||||
{
 | 
			
		||||
    return audio_pcm_sw_write (sw, buf, len);
 | 
			
		||||
@@ -240,46 +193,17 @@ static void oss_dump_info (struct oss_params *req, struct oss_params *obt)
 | 
			
		||||
}
 | 
			
		||||
#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,
 | 
			
		||||
                     struct oss_params *obt, int *pfd)
 | 
			
		||||
{
 | 
			
		||||
    int fd;
 | 
			
		||||
    int oflags = conf.exclusive ? O_EXCL : 0;
 | 
			
		||||
    int mmmmssss;
 | 
			
		||||
    audio_buf_info abinfo;
 | 
			
		||||
    int fmt, freq, nchannels;
 | 
			
		||||
    int setfragment = 1;
 | 
			
		||||
    const char *dspname = in ? conf.devpath_in : conf.devpath_out;
 | 
			
		||||
    const char *typ = in ? "ADC" : "DAC";
 | 
			
		||||
 | 
			
		||||
    /* Kludge needed to have working mmap on Linux */
 | 
			
		||||
    oflags |= conf.try_mmap ? O_RDWR : (in ? O_RDONLY : O_WRONLY);
 | 
			
		||||
 | 
			
		||||
    fd = open (dspname, oflags | O_NONBLOCK);
 | 
			
		||||
    fd = open (dspname, (in ? O_RDONLY : O_WRONLY) | O_NONBLOCK);
 | 
			
		||||
    if (-1 == fd) {
 | 
			
		||||
        oss_logerr2 (errno, typ, "Failed to open `%s'\n", dspname);
 | 
			
		||||
        return -1;
 | 
			
		||||
@@ -305,54 +229,23 @@ static int oss_open (int in, struct oss_params *req,
 | 
			
		||||
        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");
 | 
			
		||||
        goto err;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#ifdef USE_DSP_POLICY
 | 
			
		||||
    if (conf.policy >= 0) {
 | 
			
		||||
        int version;
 | 
			
		||||
 | 
			
		||||
        if (!oss_get_version (fd, &version, typ)) {
 | 
			
		||||
            if (conf.debug) {
 | 
			
		||||
                dolog ("OSS version = %#x\n", 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);
 | 
			
		||||
    mmmmssss = (req->nfrags << 16) | lsbindex (req->fragsize);
 | 
			
		||||
    if (ioctl (fd, SNDCTL_DSP_SETFRAGMENT, &mmmmssss)) {
 | 
			
		||||
        oss_logerr2 (errno, typ, "Failed to set buffer length (%d, %d)\n",
 | 
			
		||||
                     req->nfrags, req->fragsize);
 | 
			
		||||
        goto err;
 | 
			
		||||
    }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (ioctl (fd, in ? SNDCTL_DSP_GETISPACE : SNDCTL_DSP_GETOSPACE, &abinfo)) {
 | 
			
		||||
        oss_logerr2 (errno, typ, "Failed to get buffer length\n");
 | 
			
		||||
        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->nchannels = nchannels;
 | 
			
		||||
    obt->freq = freq;
 | 
			
		||||
@@ -381,58 +274,26 @@ static int oss_open (int in, struct oss_params *req,
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void oss_write_pending (OSSVoiceOut *oss)
 | 
			
		||||
{
 | 
			
		||||
    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)
 | 
			
		||||
static int oss_run_out (HWVoiceOut *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 count_info cntinfo;
 | 
			
		||||
    int bufsize;
 | 
			
		||||
 | 
			
		||||
    live = audio_pcm_hw_get_live_out (hw);
 | 
			
		||||
    if (!live) {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bufsize = hw->samples << hw->info.shift;
 | 
			
		||||
 | 
			
		||||
    if (oss->mmapped) {
 | 
			
		||||
        int bytes, pos;
 | 
			
		||||
        int bytes;
 | 
			
		||||
 | 
			
		||||
        err = ioctl (oss->fd, SNDCTL_DSP_GETOPTR, &cntinfo);
 | 
			
		||||
        if (err < 0) {
 | 
			
		||||
@@ -440,8 +301,20 @@ static int oss_run_out (HWVoiceOut *hw, int live)
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        pos = hw->rpos << hw->info.shift;
 | 
			
		||||
        bytes = audio_ring_dist (cntinfo.ptr, pos, bufsize);
 | 
			
		||||
        if (cntinfo.ptr == oss->old_optr) {
 | 
			
		||||
            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);
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
@@ -451,20 +324,9 @@ static int oss_run_out (HWVoiceOut *hw, int live)
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (abinfo.bytes > bufsize) {
 | 
			
		||||
            if (conf.debug) {
 | 
			
		||||
                dolog ("warning: Invalid available size, size=%d bufsize=%d\n"
 | 
			
		||||
                       "please report your OS/audio hw to av1474@comtv.ru\n",
 | 
			
		||||
        if (abinfo.bytes < 0 || abinfo.bytes > bufsize) {
 | 
			
		||||
            ldebug ("warning: Invalid available size, size=%d bufsize=%d\n",
 | 
			
		||||
                    abinfo.bytes, bufsize);
 | 
			
		||||
            }
 | 
			
		||||
            abinfo.bytes = bufsize;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (abinfo.bytes < 0) {
 | 
			
		||||
            if (conf.debug) {
 | 
			
		||||
                dolog ("warning: Invalid available size, size=%d bufsize=%d\n",
 | 
			
		||||
                       abinfo.bytes, bufsize);
 | 
			
		||||
            }
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -474,10 +336,56 @@ static int oss_run_out (HWVoiceOut *hw, int live)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    decr = audio_pcm_hw_clip_out (hw, oss->pcm_buf, decr, oss->pending);
 | 
			
		||||
    oss->pending += decr;
 | 
			
		||||
    oss_write_pending (oss);
 | 
			
		||||
    samples = decr;
 | 
			
		||||
    rpos = hw->rpos;
 | 
			
		||||
    while (samples) {
 | 
			
		||||
        int left_till_end_samples = hw->samples - rpos;
 | 
			
		||||
        int convert_samples = audio_MIN (samples, left_till_end_samples);
 | 
			
		||||
 | 
			
		||||
        src = hw->mix_buf + rpos;
 | 
			
		||||
        dst = advance (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);
 | 
			
		||||
                }
 | 
			
		||||
                mixeng_clear (src, wsamples);
 | 
			
		||||
                decr -= wsamples;
 | 
			
		||||
                rpos = (rpos + wsamples) % hw->samples;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        mixeng_clear (src, convert_samples);
 | 
			
		||||
 | 
			
		||||
        rpos = (rpos + convert_samples) % hw->samples;
 | 
			
		||||
        samples -= convert_samples;
 | 
			
		||||
    }
 | 
			
		||||
    if (oss->mmapped) {
 | 
			
		||||
        oss->old_optr = cntinfo.ptr;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    hw->rpos = rpos;
 | 
			
		||||
    return decr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -504,7 +412,7 @@ static void oss_fini_out (HWVoiceOut *hw)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int oss_init_out (HWVoiceOut *hw, struct audsettings *as)
 | 
			
		||||
static int oss_init_out (HWVoiceOut *hw, audsettings_t *as)
 | 
			
		||||
{
 | 
			
		||||
    OSSVoiceOut *oss = (OSSVoiceOut *) hw;
 | 
			
		||||
    struct oss_params req, obt;
 | 
			
		||||
@@ -512,7 +420,7 @@ static int oss_init_out (HWVoiceOut *hw, struct audsettings *as)
 | 
			
		||||
    int err;
 | 
			
		||||
    int fd;
 | 
			
		||||
    audfmt_e effective_fmt;
 | 
			
		||||
    struct audsettings obt_as;
 | 
			
		||||
    audsettings_t obt_as;
 | 
			
		||||
 | 
			
		||||
    oss->fd = -1;
 | 
			
		||||
 | 
			
		||||
@@ -535,9 +443,12 @@ static int oss_init_out (HWVoiceOut *hw, struct audsettings *as)
 | 
			
		||||
    obt_as.freq = obt.freq;
 | 
			
		||||
    obt_as.nchannels = obt.nchannels;
 | 
			
		||||
    obt_as.fmt = effective_fmt;
 | 
			
		||||
    obt_as.endianness = endianness;
 | 
			
		||||
 | 
			
		||||
    audio_pcm_init_info (&hw->info, &obt_as);
 | 
			
		||||
    audio_pcm_init_info (
 | 
			
		||||
        &hw->info,
 | 
			
		||||
        &obt_as,
 | 
			
		||||
        audio_need_to_swap_endian (endianness)
 | 
			
		||||
        );
 | 
			
		||||
    oss->nfrags = obt.nfrags;
 | 
			
		||||
    oss->fragsize = obt.fragsize;
 | 
			
		||||
 | 
			
		||||
@@ -551,7 +462,7 @@ static int oss_init_out (HWVoiceOut *hw, struct audsettings *as)
 | 
			
		||||
    oss->mmapped = 0;
 | 
			
		||||
    if (conf.try_mmap) {
 | 
			
		||||
        oss->pcm_buf = mmap (
 | 
			
		||||
            NULL,
 | 
			
		||||
            0,
 | 
			
		||||
            hw->samples << hw->info.shift,
 | 
			
		||||
            PROT_READ | PROT_WRITE,
 | 
			
		||||
            MAP_SHARED,
 | 
			
		||||
@@ -561,8 +472,7 @@ static int oss_init_out (HWVoiceOut *hw, struct audsettings *as)
 | 
			
		||||
        if (oss->pcm_buf == MAP_FAILED) {
 | 
			
		||||
            oss_logerr (errno, "Failed to map %d bytes of DAC\n",
 | 
			
		||||
                        hw->samples << hw->info.shift);
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
        } else {
 | 
			
		||||
            int err;
 | 
			
		||||
            int trig = 0;
 | 
			
		||||
            if (ioctl (fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
 | 
			
		||||
@@ -617,26 +527,13 @@ static int oss_ctl_out (HWVoiceOut *hw, int cmd, ...)
 | 
			
		||||
    int trig;
 | 
			
		||||
    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) {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    switch (cmd) {
 | 
			
		||||
    case VOICE_ENABLE:
 | 
			
		||||
        ldebug ("enabling voice\n");
 | 
			
		||||
        audio_pcm_info_clear_buf (&hw->info, oss->pcm_buf, hw->samples);
 | 
			
		||||
        trig = PCM_ENABLE_OUTPUT;
 | 
			
		||||
        if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
 | 
			
		||||
@@ -646,19 +543,9 @@ static int oss_ctl_out (HWVoiceOut *hw, int cmd, ...)
 | 
			
		||||
                );
 | 
			
		||||
            return -1;
 | 
			
		||||
        }
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case VOICE_DISABLE:
 | 
			
		||||
        if (hw->poll_mode) {
 | 
			
		||||
            qemu_set_fd_handler (oss->fd, NULL, NULL, NULL);
 | 
			
		||||
            hw->poll_mode = 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!oss->mmapped) {
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ldebug ("disabling voice\n");
 | 
			
		||||
        trig = 0;
 | 
			
		||||
        if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
 | 
			
		||||
@@ -670,7 +557,7 @@ static int oss_ctl_out (HWVoiceOut *hw, int cmd, ...)
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int oss_init_in (HWVoiceIn *hw, struct audsettings *as)
 | 
			
		||||
static int oss_init_in (HWVoiceIn *hw, audsettings_t *as)
 | 
			
		||||
{
 | 
			
		||||
    OSSVoiceIn *oss = (OSSVoiceIn *) hw;
 | 
			
		||||
    struct oss_params req, obt;
 | 
			
		||||
@@ -678,7 +565,7 @@ static int oss_init_in (HWVoiceIn *hw, struct audsettings *as)
 | 
			
		||||
    int err;
 | 
			
		||||
    int fd;
 | 
			
		||||
    audfmt_e effective_fmt;
 | 
			
		||||
    struct audsettings obt_as;
 | 
			
		||||
    audsettings_t obt_as;
 | 
			
		||||
 | 
			
		||||
    oss->fd = -1;
 | 
			
		||||
 | 
			
		||||
@@ -700,9 +587,12 @@ static int oss_init_in (HWVoiceIn *hw, struct audsettings *as)
 | 
			
		||||
    obt_as.freq = obt.freq;
 | 
			
		||||
    obt_as.nchannels = obt.nchannels;
 | 
			
		||||
    obt_as.fmt = effective_fmt;
 | 
			
		||||
    obt_as.endianness = endianness;
 | 
			
		||||
 | 
			
		||||
    audio_pcm_init_info (&hw->info, &obt_as);
 | 
			
		||||
    audio_pcm_init_info (
 | 
			
		||||
        &hw->info,
 | 
			
		||||
        &obt_as,
 | 
			
		||||
        audio_need_to_swap_endian (endianness)
 | 
			
		||||
        );
 | 
			
		||||
    oss->nfrags = obt.nfrags;
 | 
			
		||||
    oss->fragsize = obt.fragsize;
 | 
			
		||||
 | 
			
		||||
@@ -748,8 +638,8 @@ static int oss_run_in (HWVoiceIn *hw)
 | 
			
		||||
        int add;
 | 
			
		||||
        int len;
 | 
			
		||||
    } bufs[2] = {
 | 
			
		||||
        { .add = hw->wpos, .len = 0 },
 | 
			
		||||
        { .add = 0,        .len = 0 }
 | 
			
		||||
        { hw->wpos, 0 },
 | 
			
		||||
        { 0, 0 }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    if (!dead) {
 | 
			
		||||
@@ -764,6 +654,7 @@ static int oss_run_in (HWVoiceIn *hw)
 | 
			
		||||
        bufs[0].len = dead << hwshift;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < 2; ++i) {
 | 
			
		||||
        ssize_t nread;
 | 
			
		||||
 | 
			
		||||
@@ -813,32 +704,8 @@ static int oss_read (SWVoiceIn *sw, void *buf, int size)
 | 
			
		||||
 | 
			
		||||
static int oss_ctl_in (HWVoiceIn *hw, int cmd, ...)
 | 
			
		||||
{
 | 
			
		||||
    OSSVoiceIn *oss = (OSSVoiceIn *) 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);
 | 
			
		||||
 | 
			
		||||
            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;
 | 
			
		||||
    }
 | 
			
		||||
    (void) hw;
 | 
			
		||||
    (void) cmd;
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -853,83 +720,43 @@ static void oss_audio_fini (void *opaque)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct audio_option oss_options[] = {
 | 
			
		||||
    {
 | 
			
		||||
        .name  = "FRAGSIZE",
 | 
			
		||||
        .tag   = AUD_OPT_INT,
 | 
			
		||||
        .valp  = &conf.fragsize,
 | 
			
		||||
        .descr = "Fragment size in bytes"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        .name  = "NFRAGS",
 | 
			
		||||
        .tag   = AUD_OPT_INT,
 | 
			
		||||
        .valp  = &conf.nfrags,
 | 
			
		||||
        .descr = "Number of fragments"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        .name  = "MMAP",
 | 
			
		||||
        .tag   = AUD_OPT_BOOL,
 | 
			
		||||
        .valp  = &conf.try_mmap,
 | 
			
		||||
        .descr = "Try using memory mapped access"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        .name  = "DAC_DEV",
 | 
			
		||||
        .tag   = AUD_OPT_STR,
 | 
			
		||||
        .valp  = &conf.devpath_out,
 | 
			
		||||
        .descr = "Path to DAC device"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        .name  = "ADC_DEV",
 | 
			
		||||
        .tag   = AUD_OPT_STR,
 | 
			
		||||
        .valp  = &conf.devpath_in,
 | 
			
		||||
        .descr = "Path to ADC device"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        .name  = "EXCLUSIVE",
 | 
			
		||||
        .tag   = AUD_OPT_BOOL,
 | 
			
		||||
        .valp  = &conf.exclusive,
 | 
			
		||||
        .descr = "Open device in exclusive mode (vmix wont work)"
 | 
			
		||||
    },
 | 
			
		||||
#ifdef USE_DSP_POLICY
 | 
			
		||||
    {
 | 
			
		||||
        .name  = "POLICY",
 | 
			
		||||
        .tag   = AUD_OPT_INT,
 | 
			
		||||
        .valp  = &conf.policy,
 | 
			
		||||
        .descr = "Set the timing policy of the device, -1 to use fragment mode",
 | 
			
		||||
    },
 | 
			
		||||
#endif
 | 
			
		||||
    {
 | 
			
		||||
        .name  = "DEBUG",
 | 
			
		||||
        .tag   = AUD_OPT_BOOL,
 | 
			
		||||
        .valp  = &conf.debug,
 | 
			
		||||
        .descr = "Turn on some debugging messages"
 | 
			
		||||
    },
 | 
			
		||||
    { /* End of list */ }
 | 
			
		||||
    {"FRAGSIZE", AUD_OPT_INT, &conf.fragsize,
 | 
			
		||||
     "Fragment size in bytes", NULL, 0},
 | 
			
		||||
    {"NFRAGS", AUD_OPT_INT, &conf.nfrags,
 | 
			
		||||
     "Number of fragments", NULL, 0},
 | 
			
		||||
    {"MMAP", AUD_OPT_BOOL, &conf.try_mmap,
 | 
			
		||||
     "Try using memory mapped access", NULL, 0},
 | 
			
		||||
    {"DAC_DEV", AUD_OPT_STR, &conf.devpath_out,
 | 
			
		||||
     "Path to DAC device", NULL, 0},
 | 
			
		||||
    {"ADC_DEV", AUD_OPT_STR, &conf.devpath_in,
 | 
			
		||||
     "Path to ADC device", NULL, 0},
 | 
			
		||||
    {NULL, 0, NULL, NULL, NULL, 0}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct audio_pcm_ops oss_pcm_ops = {
 | 
			
		||||
    .init_out = oss_init_out,
 | 
			
		||||
    .fini_out = oss_fini_out,
 | 
			
		||||
    .run_out  = oss_run_out,
 | 
			
		||||
    .write    = oss_write,
 | 
			
		||||
    .ctl_out  = oss_ctl_out,
 | 
			
		||||
    oss_init_out,
 | 
			
		||||
    oss_fini_out,
 | 
			
		||||
    oss_run_out,
 | 
			
		||||
    oss_write,
 | 
			
		||||
    oss_ctl_out,
 | 
			
		||||
 | 
			
		||||
    .init_in  = oss_init_in,
 | 
			
		||||
    .fini_in  = oss_fini_in,
 | 
			
		||||
    .run_in   = oss_run_in,
 | 
			
		||||
    .read     = oss_read,
 | 
			
		||||
    .ctl_in   = oss_ctl_in
 | 
			
		||||
    oss_init_in,
 | 
			
		||||
    oss_fini_in,
 | 
			
		||||
    oss_run_in,
 | 
			
		||||
    oss_read,
 | 
			
		||||
    oss_ctl_in
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct audio_driver oss_audio_driver = {
 | 
			
		||||
    .name           = "oss",
 | 
			
		||||
    .descr          = "OSS http://www.opensound.com",
 | 
			
		||||
    .options        = oss_options,
 | 
			
		||||
    .init           = oss_audio_init,
 | 
			
		||||
    .fini           = oss_audio_fini,
 | 
			
		||||
    .pcm_ops        = &oss_pcm_ops,
 | 
			
		||||
    .can_be_default = 1,
 | 
			
		||||
    .max_voices_out = INT_MAX,
 | 
			
		||||
    .max_voices_in  = INT_MAX,
 | 
			
		||||
    .voice_size_out = sizeof (OSSVoiceOut),
 | 
			
		||||
    .voice_size_in  = sizeof (OSSVoiceIn)
 | 
			
		||||
    INIT_FIELD (name           = ) "oss",
 | 
			
		||||
    INIT_FIELD (descr          = ) "OSS http://www.opensound.com",
 | 
			
		||||
    INIT_FIELD (options        = ) oss_options,
 | 
			
		||||
    INIT_FIELD (init           = ) oss_audio_init,
 | 
			
		||||
    INIT_FIELD (fini           = ) oss_audio_fini,
 | 
			
		||||
    INIT_FIELD (pcm_ops        = ) &oss_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 (OSSVoiceOut),
 | 
			
		||||
    INIT_FIELD (voice_size_in  = ) sizeof (OSSVoiceIn)
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										527
									
								
								audio/paaudio.c
									
									
									
									
									
								
							
							
						
						
									
										527
									
								
								audio/paaudio.c
									
									
									
									
									
								
							@@ -1,527 +0,0 @@
 | 
			
		||||
/* public domain */
 | 
			
		||||
#include "qemu-common.h"
 | 
			
		||||
#include "audio.h"
 | 
			
		||||
 | 
			
		||||
#include <pulse/simple.h>
 | 
			
		||||
#include <pulse/error.h>
 | 
			
		||||
 | 
			
		||||
#define AUDIO_CAP "pulseaudio"
 | 
			
		||||
#include "audio_int.h"
 | 
			
		||||
#include "audio_pt_int.h"
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    HWVoiceOut hw;
 | 
			
		||||
    int done;
 | 
			
		||||
    int live;
 | 
			
		||||
    int decr;
 | 
			
		||||
    int rpos;
 | 
			
		||||
    pa_simple *s;
 | 
			
		||||
    void *pcm_buf;
 | 
			
		||||
    struct audio_pt pt;
 | 
			
		||||
} PAVoiceOut;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    HWVoiceIn hw;
 | 
			
		||||
    int done;
 | 
			
		||||
    int dead;
 | 
			
		||||
    int incr;
 | 
			
		||||
    int wpos;
 | 
			
		||||
    pa_simple *s;
 | 
			
		||||
    void *pcm_buf;
 | 
			
		||||
    struct audio_pt pt;
 | 
			
		||||
} PAVoiceIn;
 | 
			
		||||
 | 
			
		||||
static struct {
 | 
			
		||||
    int samples;
 | 
			
		||||
    int divisor;
 | 
			
		||||
    char *server;
 | 
			
		||||
    char *sink;
 | 
			
		||||
    char *source;
 | 
			
		||||
} conf = {
 | 
			
		||||
    .samples = 1024,
 | 
			
		||||
    .divisor = 2,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
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));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void *qpa_thread_out (void *arg)
 | 
			
		||||
{
 | 
			
		||||
    PAVoiceOut *pa = arg;
 | 
			
		||||
    HWVoiceOut *hw = &pa->hw;
 | 
			
		||||
    int threshold;
 | 
			
		||||
 | 
			
		||||
    threshold = conf.divisor ? hw->samples / conf.divisor : 0;
 | 
			
		||||
 | 
			
		||||
    if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (;;) {
 | 
			
		||||
        int decr, to_mix, rpos;
 | 
			
		||||
 | 
			
		||||
        for (;;) {
 | 
			
		||||
            if (pa->done) {
 | 
			
		||||
                goto exit;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (pa->live > threshold) {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (audio_pt_wait (&pa->pt, AUDIO_FUNC)) {
 | 
			
		||||
                goto exit;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        decr = to_mix = pa->live;
 | 
			
		||||
        rpos = hw->rpos;
 | 
			
		||||
 | 
			
		||||
        if (audio_pt_unlock (&pa->pt, AUDIO_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 (pa_simple_write (pa->s, 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, AUDIO_FUNC)) {
 | 
			
		||||
            return NULL;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        pa->rpos = rpos;
 | 
			
		||||
        pa->live -= decr;
 | 
			
		||||
        pa->decr += decr;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 exit:
 | 
			
		||||
    audio_pt_unlock (&pa->pt, AUDIO_FUNC);
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int qpa_run_out (HWVoiceOut *hw, int live)
 | 
			
		||||
{
 | 
			
		||||
    int decr;
 | 
			
		||||
    PAVoiceOut *pa = (PAVoiceOut *) hw;
 | 
			
		||||
 | 
			
		||||
    if (audio_pt_lock (&pa->pt, AUDIO_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, AUDIO_FUNC);
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
        audio_pt_unlock (&pa->pt, AUDIO_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;
 | 
			
		||||
    int threshold;
 | 
			
		||||
 | 
			
		||||
    threshold = conf.divisor ? hw->samples / conf.divisor : 0;
 | 
			
		||||
 | 
			
		||||
    if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (;;) {
 | 
			
		||||
        int incr, to_grab, wpos;
 | 
			
		||||
 | 
			
		||||
        for (;;) {
 | 
			
		||||
            if (pa->done) {
 | 
			
		||||
                goto exit;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (pa->dead > threshold) {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (audio_pt_wait (&pa->pt, AUDIO_FUNC)) {
 | 
			
		||||
                goto exit;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        incr = to_grab = pa->dead;
 | 
			
		||||
        wpos = hw->wpos;
 | 
			
		||||
 | 
			
		||||
        if (audio_pt_unlock (&pa->pt, AUDIO_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 (pa_simple_read (pa->s, 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, &nominal_volume);
 | 
			
		||||
            wpos = (wpos + chunk) % hw->samples;
 | 
			
		||||
            to_grab -= chunk;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
 | 
			
		||||
            return NULL;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        pa->wpos = wpos;
 | 
			
		||||
        pa->dead -= incr;
 | 
			
		||||
        pa->incr += incr;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 exit:
 | 
			
		||||
    audio_pt_unlock (&pa->pt, AUDIO_FUNC);
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int qpa_run_in (HWVoiceIn *hw)
 | 
			
		||||
{
 | 
			
		||||
    int live, incr, dead;
 | 
			
		||||
    PAVoiceIn *pa = (PAVoiceIn *) hw;
 | 
			
		||||
 | 
			
		||||
    if (audio_pt_lock (&pa->pt, AUDIO_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, AUDIO_FUNC);
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
        audio_pt_unlock (&pa->pt, AUDIO_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 int qpa_init_out (HWVoiceOut *hw, struct audsettings *as)
 | 
			
		||||
{
 | 
			
		||||
    int error;
 | 
			
		||||
    static pa_sample_spec ss;
 | 
			
		||||
    struct audsettings obt_as = *as;
 | 
			
		||||
    PAVoiceOut *pa = (PAVoiceOut *) hw;
 | 
			
		||||
 | 
			
		||||
    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->s = pa_simple_new (
 | 
			
		||||
        conf.server,
 | 
			
		||||
        "qemu",
 | 
			
		||||
        PA_STREAM_PLAYBACK,
 | 
			
		||||
        conf.sink,
 | 
			
		||||
        "pcm.playback",
 | 
			
		||||
        &ss,
 | 
			
		||||
        NULL,                   /* channel map */
 | 
			
		||||
        NULL,                   /* buffering attributes */
 | 
			
		||||
        &error
 | 
			
		||||
        );
 | 
			
		||||
    if (!pa->s) {
 | 
			
		||||
        qpa_logerr (error, "pa_simple_new for playback failed\n");
 | 
			
		||||
        goto fail1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    audio_pcm_init_info (&hw->info, &obt_as);
 | 
			
		||||
    hw->samples = conf.samples;
 | 
			
		||||
    pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
 | 
			
		||||
    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, AUDIO_FUNC)) {
 | 
			
		||||
        goto fail3;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
 | 
			
		||||
 fail3:
 | 
			
		||||
    qemu_free (pa->pcm_buf);
 | 
			
		||||
    pa->pcm_buf = NULL;
 | 
			
		||||
 fail2:
 | 
			
		||||
    pa_simple_free (pa->s);
 | 
			
		||||
    pa->s = NULL;
 | 
			
		||||
 fail1:
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int qpa_init_in (HWVoiceIn *hw, struct audsettings *as)
 | 
			
		||||
{
 | 
			
		||||
    int error;
 | 
			
		||||
    static pa_sample_spec ss;
 | 
			
		||||
    struct audsettings obt_as = *as;
 | 
			
		||||
    PAVoiceIn *pa = (PAVoiceIn *) hw;
 | 
			
		||||
 | 
			
		||||
    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->s = pa_simple_new (
 | 
			
		||||
        conf.server,
 | 
			
		||||
        "qemu",
 | 
			
		||||
        PA_STREAM_RECORD,
 | 
			
		||||
        conf.source,
 | 
			
		||||
        "pcm.capture",
 | 
			
		||||
        &ss,
 | 
			
		||||
        NULL,                   /* channel map */
 | 
			
		||||
        NULL,                   /* buffering attributes */
 | 
			
		||||
        &error
 | 
			
		||||
        );
 | 
			
		||||
    if (!pa->s) {
 | 
			
		||||
        qpa_logerr (error, "pa_simple_new for capture failed\n");
 | 
			
		||||
        goto fail1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    audio_pcm_init_info (&hw->info, &obt_as);
 | 
			
		||||
    hw->samples = conf.samples;
 | 
			
		||||
    pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
 | 
			
		||||
    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, AUDIO_FUNC)) {
 | 
			
		||||
        goto fail3;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
 | 
			
		||||
 fail3:
 | 
			
		||||
    qemu_free (pa->pcm_buf);
 | 
			
		||||
    pa->pcm_buf = NULL;
 | 
			
		||||
 fail2:
 | 
			
		||||
    pa_simple_free (pa->s);
 | 
			
		||||
    pa->s = NULL;
 | 
			
		||||
 fail1:
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void qpa_fini_out (HWVoiceOut *hw)
 | 
			
		||||
{
 | 
			
		||||
    void *ret;
 | 
			
		||||
    PAVoiceOut *pa = (PAVoiceOut *) hw;
 | 
			
		||||
 | 
			
		||||
    audio_pt_lock (&pa->pt, AUDIO_FUNC);
 | 
			
		||||
    pa->done = 1;
 | 
			
		||||
    audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC);
 | 
			
		||||
    audio_pt_join (&pa->pt, &ret, AUDIO_FUNC);
 | 
			
		||||
 | 
			
		||||
    if (pa->s) {
 | 
			
		||||
        pa_simple_free (pa->s);
 | 
			
		||||
        pa->s = NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    audio_pt_fini (&pa->pt, AUDIO_FUNC);
 | 
			
		||||
    qemu_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, AUDIO_FUNC);
 | 
			
		||||
    pa->done = 1;
 | 
			
		||||
    audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC);
 | 
			
		||||
    audio_pt_join (&pa->pt, &ret, AUDIO_FUNC);
 | 
			
		||||
 | 
			
		||||
    if (pa->s) {
 | 
			
		||||
        pa_simple_free (pa->s);
 | 
			
		||||
        pa->s = NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    audio_pt_fini (&pa->pt, AUDIO_FUNC);
 | 
			
		||||
    qemu_free (pa->pcm_buf);
 | 
			
		||||
    pa->pcm_buf = NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int qpa_ctl_out (HWVoiceOut *hw, int cmd, ...)
 | 
			
		||||
{
 | 
			
		||||
    (void) hw;
 | 
			
		||||
    (void) cmd;
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...)
 | 
			
		||||
{
 | 
			
		||||
    (void) hw;
 | 
			
		||||
    (void) cmd;
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* common */
 | 
			
		||||
static void *qpa_audio_init (void)
 | 
			
		||||
{
 | 
			
		||||
    return &conf;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void qpa_audio_fini (void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    (void) opaque;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct audio_option qpa_options[] = {
 | 
			
		||||
    {
 | 
			
		||||
        .name  = "SAMPLES",
 | 
			
		||||
        .tag   = AUD_OPT_INT,
 | 
			
		||||
        .valp  = &conf.samples,
 | 
			
		||||
        .descr = "buffer size in samples"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        .name  = "DIVISOR",
 | 
			
		||||
        .tag   = AUD_OPT_INT,
 | 
			
		||||
        .valp  = &conf.divisor,
 | 
			
		||||
        .descr = "threshold divisor"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        .name  = "SERVER",
 | 
			
		||||
        .tag   = AUD_OPT_STR,
 | 
			
		||||
        .valp  = &conf.server,
 | 
			
		||||
        .descr = "server address"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        .name  = "SINK",
 | 
			
		||||
        .tag   = AUD_OPT_STR,
 | 
			
		||||
        .valp  = &conf.sink,
 | 
			
		||||
        .descr = "sink device name"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        .name  = "SOURCE",
 | 
			
		||||
        .tag   = AUD_OPT_STR,
 | 
			
		||||
        .valp  = &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
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
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)
 | 
			
		||||
};
 | 
			
		||||
@@ -27,15 +27,15 @@
 | 
			
		||||
 * Processed signed long samples from ibuf to obuf.
 | 
			
		||||
 * 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)
 | 
			
		||||
{
 | 
			
		||||
    struct rate *rate = opaque;
 | 
			
		||||
    struct st_sample *istart, *iend;
 | 
			
		||||
    struct st_sample *ostart, *oend;
 | 
			
		||||
    struct st_sample ilast, icur, out;
 | 
			
		||||
    st_sample_t *istart, *iend;
 | 
			
		||||
    st_sample_t *ostart, *oend;
 | 
			
		||||
    st_sample_t ilast, icur, out;
 | 
			
		||||
#ifdef FLOAT_MIXENG
 | 
			
		||||
    mixeng_real t;
 | 
			
		||||
    real_t t;
 | 
			
		||||
#else
 | 
			
		||||
    int64_t t;
 | 
			
		||||
#endif
 | 
			
		||||
@@ -51,7 +51,7 @@ void NAME (void *opaque, struct st_sample *ibuf, struct st_sample *obuf,
 | 
			
		||||
    if (rate->opos_inc == (1ULL + UINT_MAX)) {
 | 
			
		||||
        int i, n = *isamp > *osamp ? *osamp : *isamp;
 | 
			
		||||
        for (i = 0; i < n; i++) {
 | 
			
		||||
            OP (obuf[i].l, ibuf[i].l);
 | 
			
		||||
            OP (obuf[i].l, ibuf[i].r);
 | 
			
		||||
            OP (obuf[i].r, ibuf[i].r);
 | 
			
		||||
        }
 | 
			
		||||
        *isamp = n;
 | 
			
		||||
@@ -84,7 +84,7 @@ void NAME (void *opaque, struct st_sample *ibuf, struct st_sample *obuf,
 | 
			
		||||
#ifdef RECIPROCAL
 | 
			
		||||
        t = (rate->opos & UINT_MAX) * (1.f / UINT_MAX);
 | 
			
		||||
#else
 | 
			
		||||
        t = (rate->opos & UINT_MAX) / (mixeng_real) UINT_MAX;
 | 
			
		||||
        t = (rate->opos & UINT_MAX) / (real_t) UINT_MAX;
 | 
			
		||||
#endif
 | 
			
		||||
        out.l = (ilast.l * (1.0 - t)) + icur.l * t;
 | 
			
		||||
        out.r = (ilast.r * (1.0 - t)) + icur.r * t;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										112
									
								
								audio/sdlaudio.c
									
									
									
									
									
								
							
							
						
						
									
										112
									
								
								audio/sdlaudio.c
									
									
									
									
									
								
							@@ -23,17 +23,7 @@
 | 
			
		||||
 */
 | 
			
		||||
#include <SDL.h>
 | 
			
		||||
#include <SDL_thread.h>
 | 
			
		||||
#include "qemu-common.h"
 | 
			
		||||
#include "audio.h"
 | 
			
		||||
 | 
			
		||||
#ifndef _WIN32
 | 
			
		||||
#ifdef __sun__
 | 
			
		||||
#define _POSIX_PTHREAD_SEMANTICS 1
 | 
			
		||||
#elif defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__DragonFly__)
 | 
			
		||||
#include <pthread.h>
 | 
			
		||||
#endif
 | 
			
		||||
#include <signal.h>
 | 
			
		||||
#endif
 | 
			
		||||
#include "vl.h"
 | 
			
		||||
 | 
			
		||||
#define AUDIO_CAP "sdl"
 | 
			
		||||
#include "audio_int.h"
 | 
			
		||||
@@ -48,10 +38,10 @@ typedef struct SDLVoiceOut {
 | 
			
		||||
static struct {
 | 
			
		||||
    int nb_samples;
 | 
			
		||||
} conf = {
 | 
			
		||||
    .nb_samples = 1024
 | 
			
		||||
    1024
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct SDLAudioState {
 | 
			
		||||
struct SDLAudioState {
 | 
			
		||||
    int exit;
 | 
			
		||||
    SDL_mutex *mutex;
 | 
			
		||||
    SDL_sem *sem;
 | 
			
		||||
@@ -115,19 +105,23 @@ static int sdl_unlock_and_post (SDLAudioState *s, const char *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) {
 | 
			
		||||
    case AUD_FMT_S8:
 | 
			
		||||
        *shift = 0;
 | 
			
		||||
        return AUDIO_S8;
 | 
			
		||||
 | 
			
		||||
    case AUD_FMT_U8:
 | 
			
		||||
        *shift = 0;
 | 
			
		||||
        return AUDIO_U8;
 | 
			
		||||
 | 
			
		||||
    case AUD_FMT_S16:
 | 
			
		||||
        *shift = 1;
 | 
			
		||||
        return AUDIO_S16LSB;
 | 
			
		||||
 | 
			
		||||
    case AUD_FMT_U16:
 | 
			
		||||
        *shift = 1;
 | 
			
		||||
        return AUDIO_U16LSB;
 | 
			
		||||
 | 
			
		||||
    default:
 | 
			
		||||
@@ -183,22 +177,11 @@ static int sdl_to_audfmt (int sdlfmt, audfmt_e *fmt, int *endianess)
 | 
			
		||||
static int sdl_open (SDL_AudioSpec *req, SDL_AudioSpec *obt)
 | 
			
		||||
{
 | 
			
		||||
    int status;
 | 
			
		||||
#ifndef _WIN32
 | 
			
		||||
    sigset_t new, old;
 | 
			
		||||
 | 
			
		||||
    /* Make sure potential threads created by SDL don't hog signals.  */
 | 
			
		||||
    sigfillset (&new);
 | 
			
		||||
    pthread_sigmask (SIG_BLOCK, &new, &old);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    status = SDL_OpenAudio (req, obt);
 | 
			
		||||
    if (status) {
 | 
			
		||||
        sdl_logerr ("SDL_OpenAudio failed\n");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#ifndef _WIN32
 | 
			
		||||
    pthread_sigmask (SIG_SETMASK, &old, NULL);
 | 
			
		||||
#endif
 | 
			
		||||
    return status;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -253,10 +236,11 @@ static void sdl_callback (void *opaque, Uint8 *buf, int len)
 | 
			
		||||
        decr = to_mix;
 | 
			
		||||
        while (to_mix) {
 | 
			
		||||
            int chunk = audio_MIN (to_mix, hw->samples - hw->rpos);
 | 
			
		||||
            struct st_sample *src = hw->mix_buf + hw->rpos;
 | 
			
		||||
            st_sample_t *src = hw->mix_buf + hw->rpos;
 | 
			
		||||
 | 
			
		||||
            /* dolog ("in callback to_mix %d, chunk %d\n", to_mix, chunk); */
 | 
			
		||||
            hw->clip (buf, src, chunk);
 | 
			
		||||
            mixeng_clear (src, chunk);
 | 
			
		||||
            sdl->rpos = (sdl->rpos + chunk) % hw->samples;
 | 
			
		||||
            to_mix -= chunk;
 | 
			
		||||
            buf += chunk << hw->info.shift;
 | 
			
		||||
@@ -278,16 +262,18 @@ static int sdl_write_out (SWVoiceOut *sw, void *buf, int len)
 | 
			
		||||
    return audio_pcm_sw_write (sw, buf, len);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int sdl_run_out (HWVoiceOut *hw, int live)
 | 
			
		||||
static int sdl_run_out (HWVoiceOut *hw)
 | 
			
		||||
{
 | 
			
		||||
    int decr;
 | 
			
		||||
    int decr, live;
 | 
			
		||||
    SDLVoiceOut *sdl = (SDLVoiceOut *) hw;
 | 
			
		||||
    SDLAudioState *s = &glob_sdl;
 | 
			
		||||
 | 
			
		||||
    if (sdl_lock (s, "sdl_run_out")) {
 | 
			
		||||
    if (sdl_lock (s, "sdl_callback")) {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    live = audio_pcm_hw_get_live_out (hw);
 | 
			
		||||
 | 
			
		||||
    if (sdl->decr > live) {
 | 
			
		||||
        ldebug ("sdl->decr %d live %d sdl->live %d\n",
 | 
			
		||||
                sdl->decr,
 | 
			
		||||
@@ -302,10 +288,10 @@ static int sdl_run_out (HWVoiceOut *hw, int live)
 | 
			
		||||
    hw->rpos = sdl->rpos;
 | 
			
		||||
 | 
			
		||||
    if (sdl->live > 0) {
 | 
			
		||||
        sdl_unlock_and_post (s, "sdl_run_out");
 | 
			
		||||
        sdl_unlock_and_post (s, "sdl_callback");
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
        sdl_unlock (s, "sdl_run_out");
 | 
			
		||||
        sdl_unlock (s, "sdl_callback");
 | 
			
		||||
    }
 | 
			
		||||
    return decr;
 | 
			
		||||
}
 | 
			
		||||
@@ -317,18 +303,21 @@ static void sdl_fini_out (HWVoiceOut *hw)
 | 
			
		||||
    sdl_close (&glob_sdl);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int sdl_init_out (HWVoiceOut *hw, struct audsettings *as)
 | 
			
		||||
static int sdl_init_out (HWVoiceOut *hw, audsettings_t *as)
 | 
			
		||||
{
 | 
			
		||||
    SDLVoiceOut *sdl = (SDLVoiceOut *) hw;
 | 
			
		||||
    SDLAudioState *s = &glob_sdl;
 | 
			
		||||
    SDL_AudioSpec req, obt;
 | 
			
		||||
    int shift;
 | 
			
		||||
    int endianess;
 | 
			
		||||
    int err;
 | 
			
		||||
    audfmt_e effective_fmt;
 | 
			
		||||
    struct audsettings obt_as;
 | 
			
		||||
    audsettings_t obt_as;
 | 
			
		||||
 | 
			
		||||
    shift <<= as->nchannels == 2;
 | 
			
		||||
 | 
			
		||||
    req.freq = as->freq;
 | 
			
		||||
    req.format = aud_to_sdlfmt (as->fmt);
 | 
			
		||||
    req.format = aud_to_sdlfmt (as->fmt, &shift);
 | 
			
		||||
    req.channels = as->nchannels;
 | 
			
		||||
    req.samples = conf.nb_samples;
 | 
			
		||||
    req.callback = sdl_callback;
 | 
			
		||||
@@ -347,9 +336,12 @@ static int sdl_init_out (HWVoiceOut *hw, struct audsettings *as)
 | 
			
		||||
    obt_as.freq = obt.freq;
 | 
			
		||||
    obt_as.nchannels = obt.channels;
 | 
			
		||||
    obt_as.fmt = effective_fmt;
 | 
			
		||||
    obt_as.endianness = endianess;
 | 
			
		||||
 | 
			
		||||
    audio_pcm_init_info (&hw->info, &obt_as);
 | 
			
		||||
    audio_pcm_init_info (
 | 
			
		||||
        &hw->info,
 | 
			
		||||
        &obt_as,
 | 
			
		||||
        audio_need_to_swap_endian (endianess)
 | 
			
		||||
        );
 | 
			
		||||
    hw->samples = obt.samples;
 | 
			
		||||
 | 
			
		||||
    s->initialized = 1;
 | 
			
		||||
@@ -411,33 +403,35 @@ static void sdl_audio_fini (void *opaque)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct audio_option sdl_options[] = {
 | 
			
		||||
    {
 | 
			
		||||
        .name  = "SAMPLES",
 | 
			
		||||
        .tag   = AUD_OPT_INT,
 | 
			
		||||
        .valp  = &conf.nb_samples,
 | 
			
		||||
        .descr = "Size of SDL buffer in samples"
 | 
			
		||||
    },
 | 
			
		||||
    { /* End of list */ }
 | 
			
		||||
    {"SAMPLES", AUD_OPT_INT, &conf.nb_samples,
 | 
			
		||||
     "Size of SDL buffer in samples", NULL, 0},
 | 
			
		||||
    {NULL, 0, NULL, NULL, NULL, 0}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct audio_pcm_ops sdl_pcm_ops = {
 | 
			
		||||
    .init_out = sdl_init_out,
 | 
			
		||||
    .fini_out = sdl_fini_out,
 | 
			
		||||
    .run_out  = sdl_run_out,
 | 
			
		||||
    .write    = sdl_write_out,
 | 
			
		||||
    .ctl_out  = sdl_ctl_out,
 | 
			
		||||
    sdl_init_out,
 | 
			
		||||
    sdl_fini_out,
 | 
			
		||||
    sdl_run_out,
 | 
			
		||||
    sdl_write_out,
 | 
			
		||||
    sdl_ctl_out,
 | 
			
		||||
 | 
			
		||||
    NULL,
 | 
			
		||||
    NULL,
 | 
			
		||||
    NULL,
 | 
			
		||||
    NULL,
 | 
			
		||||
    NULL
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct audio_driver sdl_audio_driver = {
 | 
			
		||||
    .name           = "sdl",
 | 
			
		||||
    .descr          = "SDL http://www.libsdl.org",
 | 
			
		||||
    .options        = sdl_options,
 | 
			
		||||
    .init           = sdl_audio_init,
 | 
			
		||||
    .fini           = sdl_audio_fini,
 | 
			
		||||
    .pcm_ops        = &sdl_pcm_ops,
 | 
			
		||||
    .can_be_default = 1,
 | 
			
		||||
    .max_voices_out = 1,
 | 
			
		||||
    .max_voices_in  = 0,
 | 
			
		||||
    .voice_size_out = sizeof (SDLVoiceOut),
 | 
			
		||||
    .voice_size_in  = 0
 | 
			
		||||
    INIT_FIELD (name           = ) "sdl",
 | 
			
		||||
    INIT_FIELD (descr          = ) "SDL http://www.libsdl.org",
 | 
			
		||||
    INIT_FIELD (options        = ) sdl_options,
 | 
			
		||||
    INIT_FIELD (init           = ) sdl_audio_init,
 | 
			
		||||
    INIT_FIELD (fini           = ) sdl_audio_fini,
 | 
			
		||||
    INIT_FIELD (pcm_ops        = ) &sdl_pcm_ops,
 | 
			
		||||
    INIT_FIELD (can_be_default = ) 1,
 | 
			
		||||
    INIT_FIELD (max_voices_out = ) 1,
 | 
			
		||||
    INIT_FIELD (max_voices_in  = ) 0,
 | 
			
		||||
    INIT_FIELD (voice_size_out = ) sizeof (SDLVoiceOut),
 | 
			
		||||
    INIT_FIELD (voice_size_in  = ) 0
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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 */
 | 
			
		||||
							
								
								
									
										127
									
								
								audio/wavaudio.c
									
									
									
									
									
								
							
							
						
						
									
										127
									
								
								audio/wavaudio.c
									
									
									
									
									
								
							@@ -21,9 +21,7 @@
 | 
			
		||||
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
			
		||||
 * THE SOFTWARE.
 | 
			
		||||
 */
 | 
			
		||||
#include "hw/hw.h"
 | 
			
		||||
#include "qemu-timer.h"
 | 
			
		||||
#include "audio.h"
 | 
			
		||||
#include "vl.h"
 | 
			
		||||
 | 
			
		||||
#define AUDIO_CAP "wav"
 | 
			
		||||
#include "audio_int.h"
 | 
			
		||||
@@ -37,25 +35,26 @@ typedef struct WAVVoiceOut {
 | 
			
		||||
} WAVVoiceOut;
 | 
			
		||||
 | 
			
		||||
static struct {
 | 
			
		||||
    struct audsettings settings;
 | 
			
		||||
    audsettings_t settings;
 | 
			
		||||
    const char *wav_path;
 | 
			
		||||
} conf = {
 | 
			
		||||
    .settings.freq      = 44100,
 | 
			
		||||
    .settings.nchannels = 2,
 | 
			
		||||
    .settings.fmt       = AUD_FMT_S16,
 | 
			
		||||
    .wav_path           = "qemu.wav"
 | 
			
		||||
    {
 | 
			
		||||
        44100,
 | 
			
		||||
        2,
 | 
			
		||||
        AUD_FMT_S16
 | 
			
		||||
    },
 | 
			
		||||
    "qemu.wav"
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int wav_run_out (HWVoiceOut *hw, int live)
 | 
			
		||||
static int wav_run_out (HWVoiceOut *hw)
 | 
			
		||||
{
 | 
			
		||||
    WAVVoiceOut *wav = (WAVVoiceOut *) hw;
 | 
			
		||||
    int rpos, decr, samples;
 | 
			
		||||
    int rpos, live, decr, samples;
 | 
			
		||||
    uint8_t *dst;
 | 
			
		||||
    struct st_sample *src;
 | 
			
		||||
    st_sample_t *src;
 | 
			
		||||
    int64_t now = qemu_get_clock (vm_clock);
 | 
			
		||||
    int64_t ticks = now - wav->old_ticks;
 | 
			
		||||
    int64_t bytes =
 | 
			
		||||
        muldiv64 (ticks, hw->info.bytes_per_second, get_ticks_per_sec ());
 | 
			
		||||
    int64_t bytes = (ticks * hw->info.bytes_per_second) / ticks_per_sec;
 | 
			
		||||
 | 
			
		||||
    if (bytes > INT_MAX) {
 | 
			
		||||
        samples = INT_MAX >> hw->info.shift;
 | 
			
		||||
@@ -64,6 +63,11 @@ static int wav_run_out (HWVoiceOut *hw, int live)
 | 
			
		||||
        samples = bytes >> hw->info.shift;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    live = audio_pcm_hw_get_live_out (hw);
 | 
			
		||||
    if (!live) {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    wav->old_ticks = now;
 | 
			
		||||
    decr = audio_MIN (live, samples);
 | 
			
		||||
    samples = decr;
 | 
			
		||||
@@ -77,6 +81,7 @@ static int wav_run_out (HWVoiceOut *hw, int live)
 | 
			
		||||
 | 
			
		||||
        hw->clip (dst, src, convert_samples);
 | 
			
		||||
        qemu_put_buffer (wav->f, dst, convert_samples << hw->info.shift);
 | 
			
		||||
        mixeng_clear (src, convert_samples);
 | 
			
		||||
 | 
			
		||||
        rpos = (rpos + convert_samples) % hw->samples;
 | 
			
		||||
        samples -= convert_samples;
 | 
			
		||||
@@ -102,7 +107,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)
 | 
			
		||||
{
 | 
			
		||||
    WAVVoiceOut *wav = (WAVVoiceOut *) hw;
 | 
			
		||||
    int bits16 = 0, stereo = 0;
 | 
			
		||||
@@ -112,7 +117,7 @@ static int wav_init_out (HWVoiceOut *hw, struct audsettings *as)
 | 
			
		||||
        0x02, 0x00, 0x44, 0xac, 0x00, 0x00, 0x10, 0xb1, 0x02, 0x00, 0x04,
 | 
			
		||||
        0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00
 | 
			
		||||
    };
 | 
			
		||||
    struct audsettings wav_as = conf.settings;
 | 
			
		||||
    audsettings_t wav_as = conf.settings;
 | 
			
		||||
 | 
			
		||||
    (void) as;
 | 
			
		||||
 | 
			
		||||
@@ -127,17 +132,11 @@ static int wav_init_out (HWVoiceOut *hw, struct audsettings *as)
 | 
			
		||||
    case AUD_FMT_U16:
 | 
			
		||||
        bits16 = 1;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case AUD_FMT_S32:
 | 
			
		||||
    case AUD_FMT_U32:
 | 
			
		||||
        dolog ("WAVE files can not handle 32bit formats\n");
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    hdr[34] = bits16 ? 0x10 : 0x08;
 | 
			
		||||
 | 
			
		||||
    wav_as.endianness = 0;
 | 
			
		||||
    audio_pcm_init_info (&hw->info, &wav_as);
 | 
			
		||||
    audio_pcm_init_info (&hw->info, &wav_as, audio_need_to_swap_endian (0));
 | 
			
		||||
 | 
			
		||||
    hw->samples = 1024;
 | 
			
		||||
    wav->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
 | 
			
		||||
@@ -152,7 +151,7 @@ static int wav_init_out (HWVoiceOut *hw, struct audsettings *as)
 | 
			
		||||
    le_store (hdr + 28, hw->info.freq << (bits16 + stereo), 4);
 | 
			
		||||
    le_store (hdr + 32, 1 << (bits16 + stereo), 2);
 | 
			
		||||
 | 
			
		||||
    wav->f = qemu_fopen (conf.wav_path, "wb");
 | 
			
		||||
    wav->f = fopen (conf.wav_path, "wb");
 | 
			
		||||
    if (!wav->f) {
 | 
			
		||||
        dolog ("Failed to open wave file `%s'\nReason: %s\n",
 | 
			
		||||
               conf.wav_path, strerror (errno));
 | 
			
		||||
@@ -186,7 +185,7 @@ static void wav_fini_out (HWVoiceOut *hw)
 | 
			
		||||
    qemu_fseek (wav->f, 32, SEEK_CUR);
 | 
			
		||||
    qemu_put_buffer (wav->f, dlen, 4);
 | 
			
		||||
 | 
			
		||||
    qemu_fclose (wav->f);
 | 
			
		||||
    fclose (wav->f);
 | 
			
		||||
    wav->f = NULL;
 | 
			
		||||
 | 
			
		||||
    qemu_free (wav->pcm_buf);
 | 
			
		||||
@@ -211,52 +210,46 @@ static void wav_audio_fini (void *opaque)
 | 
			
		||||
    ldebug ("wav_fini");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct audio_option wav_options[] = {
 | 
			
		||||
    {
 | 
			
		||||
        .name  = "FREQUENCY",
 | 
			
		||||
        .tag   = AUD_OPT_INT,
 | 
			
		||||
        .valp  = &conf.settings.freq,
 | 
			
		||||
        .descr = "Frequency"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        .name  = "FORMAT",
 | 
			
		||||
        .tag   = AUD_OPT_FMT,
 | 
			
		||||
        .valp  = &conf.settings.fmt,
 | 
			
		||||
        .descr = "Format"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        .name  = "DAC_FIXED_CHANNELS",
 | 
			
		||||
        .tag   = AUD_OPT_INT,
 | 
			
		||||
        .valp  = &conf.settings.nchannels,
 | 
			
		||||
        .descr = "Number of channels (1 - mono, 2 - stereo)"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        .name  = "PATH",
 | 
			
		||||
        .tag   = AUD_OPT_STR,
 | 
			
		||||
        .valp  = &conf.wav_path,
 | 
			
		||||
        .descr = "Path to wave file"
 | 
			
		||||
    },
 | 
			
		||||
    { /* End of list */ }
 | 
			
		||||
struct audio_option wav_options[] = {
 | 
			
		||||
    {"FREQUENCY", AUD_OPT_INT, &conf.settings.freq,
 | 
			
		||||
     "Frequency", NULL, 0},
 | 
			
		||||
 | 
			
		||||
    {"FORMAT", AUD_OPT_FMT, &conf.settings.fmt,
 | 
			
		||||
     "Format", NULL, 0},
 | 
			
		||||
 | 
			
		||||
    {"DAC_FIXED_CHANNELS", AUD_OPT_INT, &conf.settings.nchannels,
 | 
			
		||||
     "Number of channels (1 - mono, 2 - stereo)", NULL, 0},
 | 
			
		||||
 | 
			
		||||
    {"PATH", AUD_OPT_STR, &conf.wav_path,
 | 
			
		||||
     "Path to wave file", NULL, 0},
 | 
			
		||||
    {NULL, 0, NULL, NULL, NULL, 0}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct audio_pcm_ops wav_pcm_ops = {
 | 
			
		||||
    .init_out = wav_init_out,
 | 
			
		||||
    .fini_out = wav_fini_out,
 | 
			
		||||
    .run_out  = wav_run_out,
 | 
			
		||||
    .write    = wav_write_out,
 | 
			
		||||
    .ctl_out  = wav_ctl_out,
 | 
			
		||||
struct audio_pcm_ops wav_pcm_ops = {
 | 
			
		||||
    wav_init_out,
 | 
			
		||||
    wav_fini_out,
 | 
			
		||||
    wav_run_out,
 | 
			
		||||
    wav_write_out,
 | 
			
		||||
    wav_ctl_out,
 | 
			
		||||
 | 
			
		||||
    NULL,
 | 
			
		||||
    NULL,
 | 
			
		||||
    NULL,
 | 
			
		||||
    NULL,
 | 
			
		||||
    NULL
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct audio_driver wav_audio_driver = {
 | 
			
		||||
    .name           = "wav",
 | 
			
		||||
    .descr          = "WAV renderer http://wikipedia.org/wiki/WAV",
 | 
			
		||||
    .options        = wav_options,
 | 
			
		||||
    .init           = wav_audio_init,
 | 
			
		||||
    .fini           = wav_audio_fini,
 | 
			
		||||
    .pcm_ops        = &wav_pcm_ops,
 | 
			
		||||
    .can_be_default = 0,
 | 
			
		||||
    .max_voices_out = 1,
 | 
			
		||||
    .max_voices_in  = 0,
 | 
			
		||||
    .voice_size_out = sizeof (WAVVoiceOut),
 | 
			
		||||
    .voice_size_in  = 0
 | 
			
		||||
    INIT_FIELD (name           = ) "wav",
 | 
			
		||||
    INIT_FIELD (descr          = )
 | 
			
		||||
    "WAV renderer http://wikipedia.org/wiki/WAV",
 | 
			
		||||
    INIT_FIELD (options        = ) wav_options,
 | 
			
		||||
    INIT_FIELD (init           = ) wav_audio_init,
 | 
			
		||||
    INIT_FIELD (fini           = ) wav_audio_fini,
 | 
			
		||||
    INIT_FIELD (pcm_ops        = ) &wav_pcm_ops,
 | 
			
		||||
    INIT_FIELD (can_be_default = ) 0,
 | 
			
		||||
    INIT_FIELD (max_voices_out = ) 1,
 | 
			
		||||
    INIT_FIELD (max_voices_in  = ) 0,
 | 
			
		||||
    INIT_FIELD (voice_size_out = ) sizeof (WAVVoiceOut),
 | 
			
		||||
    INIT_FIELD (voice_size_in  = ) 0
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -1,161 +0,0 @@
 | 
			
		||||
#include "hw/hw.h"
 | 
			
		||||
#include "monitor.h"
 | 
			
		||||
#include "audio.h"
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    QEMUFile *f;
 | 
			
		||||
    int bytes;
 | 
			
		||||
    char *path;
 | 
			
		||||
    int freq;
 | 
			
		||||
    int bits;
 | 
			
		||||
    int nchannels;
 | 
			
		||||
    CaptureVoiceOut *cap;
 | 
			
		||||
} WAVState;
 | 
			
		||||
 | 
			
		||||
/* VICE code: Store number as little endian. */
 | 
			
		||||
static void le_store (uint8_t *buf, uint32_t val, int len)
 | 
			
		||||
{
 | 
			
		||||
    int i;
 | 
			
		||||
    for (i = 0; i < len; i++) {
 | 
			
		||||
        buf[i] = (uint8_t) (val & 0xff);
 | 
			
		||||
        val >>= 8;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void wav_notify (void *opaque, audcnotification_e cmd)
 | 
			
		||||
{
 | 
			
		||||
    (void) opaque;
 | 
			
		||||
    (void) cmd;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void wav_destroy (void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    WAVState *wav = opaque;
 | 
			
		||||
    uint8_t rlen[4];
 | 
			
		||||
    uint8_t dlen[4];
 | 
			
		||||
    uint32_t datalen = wav->bytes;
 | 
			
		||||
    uint32_t rifflen = datalen + 36;
 | 
			
		||||
 | 
			
		||||
    if (wav->f) {
 | 
			
		||||
        le_store (rlen, rifflen, 4);
 | 
			
		||||
        le_store (dlen, datalen, 4);
 | 
			
		||||
 | 
			
		||||
        qemu_fseek (wav->f, 4, SEEK_SET);
 | 
			
		||||
        qemu_put_buffer (wav->f, rlen, 4);
 | 
			
		||||
 | 
			
		||||
        qemu_fseek (wav->f, 32, SEEK_CUR);
 | 
			
		||||
        qemu_put_buffer (wav->f, dlen, 4);
 | 
			
		||||
        qemu_fclose (wav->f);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    qemu_free (wav->path);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void wav_capture (void *opaque, void *buf, int size)
 | 
			
		||||
{
 | 
			
		||||
    WAVState *wav = opaque;
 | 
			
		||||
 | 
			
		||||
    qemu_put_buffer (wav->f, buf, size);
 | 
			
		||||
    wav->bytes += size;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void wav_capture_destroy (void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    WAVState *wav = opaque;
 | 
			
		||||
 | 
			
		||||
    AUD_del_capture (wav->cap, wav);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void wav_capture_info (void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    WAVState *wav = opaque;
 | 
			
		||||
    char *path = wav->path;
 | 
			
		||||
 | 
			
		||||
    monitor_printf(cur_mon, "Capturing audio(%d,%d,%d) to %s: %d bytes\n",
 | 
			
		||||
                   wav->freq, wav->bits, wav->nchannels,
 | 
			
		||||
                   path ? path : "<not available>", wav->bytes);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct capture_ops wav_capture_ops = {
 | 
			
		||||
    .destroy = wav_capture_destroy,
 | 
			
		||||
    .info = wav_capture_info
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int wav_start_capture (CaptureState *s, const char *path, int freq,
 | 
			
		||||
                       int bits, int nchannels)
 | 
			
		||||
{
 | 
			
		||||
    Monitor *mon = cur_mon;
 | 
			
		||||
    WAVState *wav;
 | 
			
		||||
    uint8_t hdr[] = {
 | 
			
		||||
        0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56,
 | 
			
		||||
        0x45, 0x66, 0x6d, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00,
 | 
			
		||||
        0x02, 0x00, 0x44, 0xac, 0x00, 0x00, 0x10, 0xb1, 0x02, 0x00, 0x04,
 | 
			
		||||
        0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00
 | 
			
		||||
    };
 | 
			
		||||
    struct audsettings as;
 | 
			
		||||
    struct audio_capture_ops ops;
 | 
			
		||||
    int stereo, bits16, shift;
 | 
			
		||||
    CaptureVoiceOut *cap;
 | 
			
		||||
 | 
			
		||||
    if (bits != 8 && bits != 16) {
 | 
			
		||||
        monitor_printf(mon, "incorrect bit count %d, must be 8 or 16\n", bits);
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (nchannels != 1 && nchannels != 2) {
 | 
			
		||||
        monitor_printf(mon, "incorrect channel count %d, must be 1 or 2\n",
 | 
			
		||||
                       nchannels);
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    stereo = nchannels == 2;
 | 
			
		||||
    bits16 = bits == 16;
 | 
			
		||||
 | 
			
		||||
    as.freq = freq;
 | 
			
		||||
    as.nchannels = 1 << stereo;
 | 
			
		||||
    as.fmt = bits16 ? AUD_FMT_S16 : AUD_FMT_U8;
 | 
			
		||||
    as.endianness = 0;
 | 
			
		||||
 | 
			
		||||
    ops.notify = wav_notify;
 | 
			
		||||
    ops.capture = wav_capture;
 | 
			
		||||
    ops.destroy = wav_destroy;
 | 
			
		||||
 | 
			
		||||
    wav = qemu_mallocz (sizeof (*wav));
 | 
			
		||||
 | 
			
		||||
    shift = bits16 + stereo;
 | 
			
		||||
    hdr[34] = bits16 ? 0x10 : 0x08;
 | 
			
		||||
 | 
			
		||||
    le_store (hdr + 22, as.nchannels, 2);
 | 
			
		||||
    le_store (hdr + 24, freq, 4);
 | 
			
		||||
    le_store (hdr + 28, freq << shift, 4);
 | 
			
		||||
    le_store (hdr + 32, 1 << shift, 2);
 | 
			
		||||
 | 
			
		||||
    wav->f = qemu_fopen (path, "wb");
 | 
			
		||||
    if (!wav->f) {
 | 
			
		||||
        monitor_printf(mon, "Failed to open wave file `%s'\nReason: %s\n",
 | 
			
		||||
                       path, strerror (errno));
 | 
			
		||||
        qemu_free (wav);
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    wav->path = qemu_strdup (path);
 | 
			
		||||
    wav->bits = bits;
 | 
			
		||||
    wav->nchannels = nchannels;
 | 
			
		||||
    wav->freq = freq;
 | 
			
		||||
 | 
			
		||||
    qemu_put_buffer (wav->f, hdr, sizeof (hdr));
 | 
			
		||||
 | 
			
		||||
    cap = AUD_add_capture (&as, &ops, wav);
 | 
			
		||||
    if (!cap) {
 | 
			
		||||
        monitor_printf(mon, "Failed to add audio capture\n");
 | 
			
		||||
        qemu_free (wav->path);
 | 
			
		||||
        qemu_fclose (wav->f);
 | 
			
		||||
        qemu_free (wav);
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    wav->cap = cap;
 | 
			
		||||
    s->opaque = wav;
 | 
			
		||||
    s->ops = wav_capture_ops;
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
@@ -1,724 +0,0 @@
 | 
			
		||||
/* public domain */
 | 
			
		||||
 | 
			
		||||
#include "qemu-common.h"
 | 
			
		||||
#include "sysemu.h"
 | 
			
		||||
#include "audio.h"
 | 
			
		||||
 | 
			
		||||
#define AUDIO_CAP "winwave"
 | 
			
		||||
#include "audio_int.h"
 | 
			
		||||
 | 
			
		||||
#include <windows.h>
 | 
			
		||||
#include <mmsystem.h>
 | 
			
		||||
 | 
			
		||||
#include "audio_win_int.h"
 | 
			
		||||
 | 
			
		||||
static struct {
 | 
			
		||||
    int dac_headers;
 | 
			
		||||
    int dac_samples;
 | 
			
		||||
    int adc_headers;
 | 
			
		||||
    int adc_samples;
 | 
			
		||||
} conf = {
 | 
			
		||||
    .dac_headers = 4,
 | 
			
		||||
    .dac_samples = 1024,
 | 
			
		||||
    .adc_headers = 4,
 | 
			
		||||
    .adc_samples = 1024
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    HWVoiceOut hw;
 | 
			
		||||
    HWAVEOUT hwo;
 | 
			
		||||
    WAVEHDR *hdrs;
 | 
			
		||||
    HANDLE event;
 | 
			
		||||
    void *pcm_buf;
 | 
			
		||||
    int avail;
 | 
			
		||||
    int pending;
 | 
			
		||||
    int curhdr;
 | 
			
		||||
    int paused;
 | 
			
		||||
    CRITICAL_SECTION crit_sect;
 | 
			
		||||
} WaveVoiceOut;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    HWVoiceIn hw;
 | 
			
		||||
    HWAVEIN hwi;
 | 
			
		||||
    WAVEHDR *hdrs;
 | 
			
		||||
    HANDLE event;
 | 
			
		||||
    void *pcm_buf;
 | 
			
		||||
    int curhdr;
 | 
			
		||||
    int paused;
 | 
			
		||||
    int rpos;
 | 
			
		||||
    int avail;
 | 
			
		||||
    CRITICAL_SECTION crit_sect;
 | 
			
		||||
} WaveVoiceIn;
 | 
			
		||||
 | 
			
		||||
static void winwave_log_mmresult (MMRESULT mr)
 | 
			
		||||
{
 | 
			
		||||
    const char *str = "BUG";
 | 
			
		||||
 | 
			
		||||
    switch (mr) {
 | 
			
		||||
    case MMSYSERR_NOERROR:
 | 
			
		||||
        str = "Success";
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case MMSYSERR_INVALHANDLE:
 | 
			
		||||
        str = "Specified device handle is invalid";
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case MMSYSERR_BADDEVICEID:
 | 
			
		||||
        str = "Specified device id is out of range";
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case MMSYSERR_NODRIVER:
 | 
			
		||||
        str = "No device driver is present";
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case MMSYSERR_NOMEM:
 | 
			
		||||
        str = "Unable to allocate or locl memory";
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case WAVERR_SYNC:
 | 
			
		||||
        str = "Device is synchronous but waveOutOpen was called "
 | 
			
		||||
            "without using the WINWAVE_ALLOWSYNC flag";
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case WAVERR_UNPREPARED:
 | 
			
		||||
        str = "The data block pointed to by the pwh parameter "
 | 
			
		||||
            "hasn't been prepared";
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case WAVERR_STILLPLAYING:
 | 
			
		||||
        str = "There are still buffers in the queue";
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    default:
 | 
			
		||||
        dolog ("Reason: Unknown (MMRESULT %#x)\n", mr);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    dolog ("Reason: %s\n", str);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void GCC_FMT_ATTR (2, 3) winwave_logerr (
 | 
			
		||||
    MMRESULT mr,
 | 
			
		||||
    const char *fmt,
 | 
			
		||||
    ...
 | 
			
		||||
    )
 | 
			
		||||
{
 | 
			
		||||
    va_list ap;
 | 
			
		||||
 | 
			
		||||
    va_start (ap, fmt);
 | 
			
		||||
    AUD_vlog (AUDIO_CAP, fmt, ap);
 | 
			
		||||
    va_end (ap);
 | 
			
		||||
 | 
			
		||||
    AUD_log (NULL, " failed\n");
 | 
			
		||||
    winwave_log_mmresult (mr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void winwave_anal_close_out (WaveVoiceOut *wave)
 | 
			
		||||
{
 | 
			
		||||
    MMRESULT mr;
 | 
			
		||||
 | 
			
		||||
    mr = waveOutClose (wave->hwo);
 | 
			
		||||
    if (mr != MMSYSERR_NOERROR) {
 | 
			
		||||
        winwave_logerr (mr, "waveOutClose");
 | 
			
		||||
    }
 | 
			
		||||
    wave->hwo = NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void CALLBACK winwave_callback_out (
 | 
			
		||||
    HWAVEOUT hwo,
 | 
			
		||||
    UINT msg,
 | 
			
		||||
    DWORD_PTR dwInstance,
 | 
			
		||||
    DWORD_PTR dwParam1,
 | 
			
		||||
    DWORD_PTR dwParam2
 | 
			
		||||
    )
 | 
			
		||||
{
 | 
			
		||||
    WaveVoiceOut *wave = (WaveVoiceOut *) dwInstance;
 | 
			
		||||
 | 
			
		||||
    switch (msg) {
 | 
			
		||||
    case WOM_DONE:
 | 
			
		||||
        {
 | 
			
		||||
            WAVEHDR *h = (WAVEHDR *) dwParam1;
 | 
			
		||||
            if (!h->dwUser) {
 | 
			
		||||
                h->dwUser = 1;
 | 
			
		||||
                EnterCriticalSection (&wave->crit_sect);
 | 
			
		||||
                {
 | 
			
		||||
                    wave->avail += conf.dac_samples;
 | 
			
		||||
                }
 | 
			
		||||
                LeaveCriticalSection (&wave->crit_sect);
 | 
			
		||||
                if (wave->hw.poll_mode) {
 | 
			
		||||
                    if (!SetEvent (wave->event)) {
 | 
			
		||||
                        dolog ("DAC SetEvent failed %lx\n", GetLastError ());
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case WOM_CLOSE:
 | 
			
		||||
    case WOM_OPEN:
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    default:
 | 
			
		||||
        dolog ("unknown wave out callback msg %x\n", msg);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int winwave_init_out (HWVoiceOut *hw, struct audsettings *as)
 | 
			
		||||
{
 | 
			
		||||
    int i;
 | 
			
		||||
    int err;
 | 
			
		||||
    MMRESULT mr;
 | 
			
		||||
    WAVEFORMATEX wfx;
 | 
			
		||||
    WaveVoiceOut *wave;
 | 
			
		||||
 | 
			
		||||
    wave = (WaveVoiceOut *) hw;
 | 
			
		||||
 | 
			
		||||
    InitializeCriticalSection (&wave->crit_sect);
 | 
			
		||||
 | 
			
		||||
    err = waveformat_from_audio_settings (&wfx, as);
 | 
			
		||||
    if (err) {
 | 
			
		||||
        goto err0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    mr = waveOutOpen (&wave->hwo, WAVE_MAPPER, &wfx,
 | 
			
		||||
                      (DWORD_PTR) winwave_callback_out,
 | 
			
		||||
                      (DWORD_PTR) wave, CALLBACK_FUNCTION);
 | 
			
		||||
    if (mr != MMSYSERR_NOERROR) {
 | 
			
		||||
        winwave_logerr (mr, "waveOutOpen");
 | 
			
		||||
        goto err1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    wave->hdrs = audio_calloc (AUDIO_FUNC, conf.dac_headers,
 | 
			
		||||
                               sizeof (*wave->hdrs));
 | 
			
		||||
    if (!wave->hdrs) {
 | 
			
		||||
        goto err2;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    audio_pcm_init_info (&hw->info, as);
 | 
			
		||||
    hw->samples = conf.dac_samples * conf.dac_headers;
 | 
			
		||||
    wave->avail = hw->samples;
 | 
			
		||||
 | 
			
		||||
    wave->pcm_buf = audio_calloc (AUDIO_FUNC, conf.dac_samples,
 | 
			
		||||
                                  conf.dac_headers << hw->info.shift);
 | 
			
		||||
    if (!wave->pcm_buf) {
 | 
			
		||||
        goto err3;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < conf.dac_headers; ++i) {
 | 
			
		||||
        WAVEHDR *h = &wave->hdrs[i];
 | 
			
		||||
 | 
			
		||||
        h->dwUser = 0;
 | 
			
		||||
        h->dwBufferLength = conf.dac_samples << hw->info.shift;
 | 
			
		||||
        h->lpData = advance (wave->pcm_buf, i * h->dwBufferLength);
 | 
			
		||||
        h->dwFlags = 0;
 | 
			
		||||
 | 
			
		||||
        mr = waveOutPrepareHeader (wave->hwo, h, sizeof (*h));
 | 
			
		||||
        if (mr != MMSYSERR_NOERROR) {
 | 
			
		||||
            winwave_logerr (mr, "waveOutPrepareHeader(%d)", i);
 | 
			
		||||
            goto err4;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
 | 
			
		||||
 err4:
 | 
			
		||||
    qemu_free (wave->pcm_buf);
 | 
			
		||||
 err3:
 | 
			
		||||
    qemu_free (wave->hdrs);
 | 
			
		||||
 err2:
 | 
			
		||||
    winwave_anal_close_out (wave);
 | 
			
		||||
 err1:
 | 
			
		||||
 err0:
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int winwave_write (SWVoiceOut *sw, void *buf, int len)
 | 
			
		||||
{
 | 
			
		||||
    return audio_pcm_sw_write (sw, buf, len);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int winwave_run_out (HWVoiceOut *hw, int live)
 | 
			
		||||
{
 | 
			
		||||
    WaveVoiceOut *wave = (WaveVoiceOut *) hw;
 | 
			
		||||
    int decr;
 | 
			
		||||
    int doreset;
 | 
			
		||||
 | 
			
		||||
    EnterCriticalSection (&wave->crit_sect);
 | 
			
		||||
    {
 | 
			
		||||
        decr = audio_MIN (live, wave->avail);
 | 
			
		||||
        decr = audio_pcm_hw_clip_out (hw, wave->pcm_buf, decr, wave->pending);
 | 
			
		||||
        wave->pending += decr;
 | 
			
		||||
        wave->avail -= decr;
 | 
			
		||||
    }
 | 
			
		||||
    LeaveCriticalSection (&wave->crit_sect);
 | 
			
		||||
 | 
			
		||||
    doreset = hw->poll_mode && (wave->pending >= conf.dac_samples);
 | 
			
		||||
    if (doreset && !ResetEvent (wave->event)) {
 | 
			
		||||
        dolog ("DAC ResetEvent failed %lx\n", GetLastError ());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    while (wave->pending >= conf.dac_samples) {
 | 
			
		||||
        MMRESULT mr;
 | 
			
		||||
        WAVEHDR *h = &wave->hdrs[wave->curhdr];
 | 
			
		||||
 | 
			
		||||
        h->dwUser = 0;
 | 
			
		||||
        mr = waveOutWrite (wave->hwo, h, sizeof (*h));
 | 
			
		||||
        if (mr != MMSYSERR_NOERROR) {
 | 
			
		||||
            winwave_logerr (mr, "waveOutWrite(%d)", wave->curhdr);
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        wave->pending -= conf.dac_samples;
 | 
			
		||||
        wave->curhdr = (wave->curhdr + 1) % conf.dac_headers;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return decr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void winwave_poll (void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    (void) opaque;
 | 
			
		||||
    audio_run ("winwave_poll");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void winwave_fini_out (HWVoiceOut *hw)
 | 
			
		||||
{
 | 
			
		||||
    int i;
 | 
			
		||||
    MMRESULT mr;
 | 
			
		||||
    WaveVoiceOut *wave = (WaveVoiceOut *) hw;
 | 
			
		||||
 | 
			
		||||
    mr = waveOutReset (wave->hwo);
 | 
			
		||||
    if (mr != MMSYSERR_NOERROR) {
 | 
			
		||||
        winwave_logerr (mr, "waveOutReset");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < conf.dac_headers; ++i) {
 | 
			
		||||
        mr = waveOutUnprepareHeader (wave->hwo, &wave->hdrs[i],
 | 
			
		||||
                                     sizeof (wave->hdrs[i]));
 | 
			
		||||
        if (mr != MMSYSERR_NOERROR) {
 | 
			
		||||
            winwave_logerr (mr, "waveOutUnprepareHeader(%d)", i);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    winwave_anal_close_out (wave);
 | 
			
		||||
 | 
			
		||||
    if (wave->event) {
 | 
			
		||||
        qemu_del_wait_object (wave->event, winwave_poll, wave);
 | 
			
		||||
        if (!CloseHandle (wave->event)) {
 | 
			
		||||
            dolog ("DAC CloseHandle failed %lx\n", GetLastError ());
 | 
			
		||||
        }
 | 
			
		||||
        wave->event = NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    qemu_free (wave->pcm_buf);
 | 
			
		||||
    wave->pcm_buf = NULL;
 | 
			
		||||
 | 
			
		||||
    qemu_free (wave->hdrs);
 | 
			
		||||
    wave->hdrs = NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int winwave_ctl_out (HWVoiceOut *hw, int cmd, ...)
 | 
			
		||||
{
 | 
			
		||||
    MMRESULT mr;
 | 
			
		||||
    WaveVoiceOut *wave = (WaveVoiceOut *) 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);
 | 
			
		||||
 | 
			
		||||
            if (poll_mode && !wave->event) {
 | 
			
		||||
                wave->event = CreateEvent (NULL, TRUE, TRUE, NULL);
 | 
			
		||||
                if (!wave->event) {
 | 
			
		||||
                    dolog ("DAC CreateEvent: %lx, poll mode will be disabled\n",
 | 
			
		||||
                           GetLastError ());
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (wave->event) {
 | 
			
		||||
                int ret;
 | 
			
		||||
 | 
			
		||||
                ret = qemu_add_wait_object (wave->event, winwave_poll, wave);
 | 
			
		||||
                hw->poll_mode = (ret == 0);
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
                hw->poll_mode = 0;
 | 
			
		||||
            }
 | 
			
		||||
            if (wave->paused) {
 | 
			
		||||
                mr = waveOutRestart (wave->hwo);
 | 
			
		||||
                if (mr != MMSYSERR_NOERROR) {
 | 
			
		||||
                    winwave_logerr (mr, "waveOutRestart");
 | 
			
		||||
                }
 | 
			
		||||
                wave->paused = 0;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return 0;
 | 
			
		||||
 | 
			
		||||
    case VOICE_DISABLE:
 | 
			
		||||
        if (!wave->paused) {
 | 
			
		||||
            mr = waveOutPause (wave->hwo);
 | 
			
		||||
            if (mr != MMSYSERR_NOERROR) {
 | 
			
		||||
                winwave_logerr (mr, "waveOutPause");
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
                wave->paused = 1;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (wave->event) {
 | 
			
		||||
            qemu_del_wait_object (wave->event, winwave_poll, wave);
 | 
			
		||||
        }
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void winwave_anal_close_in (WaveVoiceIn *wave)
 | 
			
		||||
{
 | 
			
		||||
    MMRESULT mr;
 | 
			
		||||
 | 
			
		||||
    mr = waveInClose (wave->hwi);
 | 
			
		||||
    if (mr != MMSYSERR_NOERROR) {
 | 
			
		||||
        winwave_logerr (mr, "waveInClose");
 | 
			
		||||
    }
 | 
			
		||||
    wave->hwi = NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void CALLBACK winwave_callback_in (
 | 
			
		||||
    HWAVEIN *hwi,
 | 
			
		||||
    UINT msg,
 | 
			
		||||
    DWORD_PTR dwInstance,
 | 
			
		||||
    DWORD_PTR dwParam1,
 | 
			
		||||
    DWORD_PTR dwParam2
 | 
			
		||||
    )
 | 
			
		||||
{
 | 
			
		||||
    WaveVoiceIn *wave = (WaveVoiceIn *) dwInstance;
 | 
			
		||||
 | 
			
		||||
    switch (msg) {
 | 
			
		||||
    case WIM_DATA:
 | 
			
		||||
        {
 | 
			
		||||
            WAVEHDR *h = (WAVEHDR *) dwParam1;
 | 
			
		||||
            if (!h->dwUser) {
 | 
			
		||||
                h->dwUser = 1;
 | 
			
		||||
                EnterCriticalSection (&wave->crit_sect);
 | 
			
		||||
                {
 | 
			
		||||
                    wave->avail += conf.adc_samples;
 | 
			
		||||
                }
 | 
			
		||||
                LeaveCriticalSection (&wave->crit_sect);
 | 
			
		||||
                if (wave->hw.poll_mode) {
 | 
			
		||||
                    if (!SetEvent (wave->event)) {
 | 
			
		||||
                        dolog ("ADC SetEvent failed %lx\n", GetLastError ());
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case WIM_CLOSE:
 | 
			
		||||
    case WIM_OPEN:
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    default:
 | 
			
		||||
        dolog ("unknown wave in callback msg %x\n", msg);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void winwave_add_buffers (WaveVoiceIn *wave, int samples)
 | 
			
		||||
{
 | 
			
		||||
    int doreset;
 | 
			
		||||
 | 
			
		||||
    doreset = wave->hw.poll_mode && (samples >= conf.adc_samples);
 | 
			
		||||
    if (doreset && !ResetEvent (wave->event)) {
 | 
			
		||||
        dolog ("ADC ResetEvent failed %lx\n", GetLastError ());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    while (samples >= conf.adc_samples) {
 | 
			
		||||
        MMRESULT mr;
 | 
			
		||||
        WAVEHDR *h = &wave->hdrs[wave->curhdr];
 | 
			
		||||
 | 
			
		||||
        h->dwUser = 0;
 | 
			
		||||
        mr = waveInAddBuffer (wave->hwi, h, sizeof (*h));
 | 
			
		||||
        if (mr != MMSYSERR_NOERROR) {
 | 
			
		||||
            winwave_logerr (mr, "waveInAddBuffer(%d)", wave->curhdr);
 | 
			
		||||
        }
 | 
			
		||||
        wave->curhdr = (wave->curhdr + 1) % conf.adc_headers;
 | 
			
		||||
        samples -= conf.adc_samples;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int winwave_init_in (HWVoiceIn *hw, struct audsettings *as)
 | 
			
		||||
{
 | 
			
		||||
    int i;
 | 
			
		||||
    int err;
 | 
			
		||||
    MMRESULT mr;
 | 
			
		||||
    WAVEFORMATEX wfx;
 | 
			
		||||
    WaveVoiceIn *wave;
 | 
			
		||||
 | 
			
		||||
    wave = (WaveVoiceIn *) hw;
 | 
			
		||||
 | 
			
		||||
    InitializeCriticalSection (&wave->crit_sect);
 | 
			
		||||
 | 
			
		||||
    err = waveformat_from_audio_settings (&wfx, as);
 | 
			
		||||
    if (err) {
 | 
			
		||||
        goto err0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    mr = waveInOpen (&wave->hwi, WAVE_MAPPER, &wfx,
 | 
			
		||||
                     (DWORD_PTR) winwave_callback_in,
 | 
			
		||||
                     (DWORD_PTR) wave, CALLBACK_FUNCTION);
 | 
			
		||||
    if (mr != MMSYSERR_NOERROR) {
 | 
			
		||||
        winwave_logerr (mr, "waveInOpen");
 | 
			
		||||
        goto err1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    wave->hdrs = audio_calloc (AUDIO_FUNC, conf.dac_headers,
 | 
			
		||||
                               sizeof (*wave->hdrs));
 | 
			
		||||
    if (!wave->hdrs) {
 | 
			
		||||
        goto err2;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    audio_pcm_init_info (&hw->info, as);
 | 
			
		||||
    hw->samples = conf.adc_samples * conf.adc_headers;
 | 
			
		||||
    wave->avail = 0;
 | 
			
		||||
 | 
			
		||||
    wave->pcm_buf = audio_calloc (AUDIO_FUNC, conf.adc_samples,
 | 
			
		||||
                                  conf.adc_headers << hw->info.shift);
 | 
			
		||||
    if (!wave->pcm_buf) {
 | 
			
		||||
        goto err3;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < conf.adc_headers; ++i) {
 | 
			
		||||
        WAVEHDR *h = &wave->hdrs[i];
 | 
			
		||||
 | 
			
		||||
        h->dwUser = 0;
 | 
			
		||||
        h->dwBufferLength = conf.adc_samples << hw->info.shift;
 | 
			
		||||
        h->lpData = advance (wave->pcm_buf, i * h->dwBufferLength);
 | 
			
		||||
        h->dwFlags = 0;
 | 
			
		||||
 | 
			
		||||
        mr = waveInPrepareHeader (wave->hwi, h, sizeof (*h));
 | 
			
		||||
        if (mr != MMSYSERR_NOERROR) {
 | 
			
		||||
            winwave_logerr (mr, "waveInPrepareHeader(%d)", i);
 | 
			
		||||
            goto err4;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    wave->paused = 1;
 | 
			
		||||
    winwave_add_buffers (wave, hw->samples);
 | 
			
		||||
    return 0;
 | 
			
		||||
 | 
			
		||||
 err4:
 | 
			
		||||
    qemu_free (wave->pcm_buf);
 | 
			
		||||
 err3:
 | 
			
		||||
    qemu_free (wave->hdrs);
 | 
			
		||||
 err2:
 | 
			
		||||
    winwave_anal_close_in (wave);
 | 
			
		||||
 err1:
 | 
			
		||||
 err0:
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void winwave_fini_in (HWVoiceIn *hw)
 | 
			
		||||
{
 | 
			
		||||
    int i;
 | 
			
		||||
    MMRESULT mr;
 | 
			
		||||
    WaveVoiceIn *wave = (WaveVoiceIn *) hw;
 | 
			
		||||
 | 
			
		||||
    mr = waveInReset (wave->hwi);
 | 
			
		||||
    if (mr != MMSYSERR_NOERROR) {
 | 
			
		||||
        winwave_logerr (mr, "waveInReset");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < conf.adc_headers; ++i) {
 | 
			
		||||
        mr = waveInUnprepareHeader (wave->hwi, &wave->hdrs[i],
 | 
			
		||||
                                     sizeof (wave->hdrs[i]));
 | 
			
		||||
        if (mr != MMSYSERR_NOERROR) {
 | 
			
		||||
            winwave_logerr (mr, "waveInUnprepareHeader(%d)", i);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    winwave_anal_close_in (wave);
 | 
			
		||||
 | 
			
		||||
    if (wave->event) {
 | 
			
		||||
        qemu_del_wait_object (wave->event, winwave_poll, wave);
 | 
			
		||||
        if (!CloseHandle (wave->event)) {
 | 
			
		||||
            dolog ("ADC CloseHandle failed %lx\n", GetLastError ());
 | 
			
		||||
        }
 | 
			
		||||
        wave->event = NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    qemu_free (wave->pcm_buf);
 | 
			
		||||
    wave->pcm_buf = NULL;
 | 
			
		||||
 | 
			
		||||
    qemu_free (wave->hdrs);
 | 
			
		||||
    wave->hdrs = NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int winwave_run_in (HWVoiceIn *hw)
 | 
			
		||||
{
 | 
			
		||||
    WaveVoiceIn *wave = (WaveVoiceIn *) hw;
 | 
			
		||||
    int live = audio_pcm_hw_get_live_in (hw);
 | 
			
		||||
    int dead = hw->samples - live;
 | 
			
		||||
    int decr, ret;
 | 
			
		||||
 | 
			
		||||
    if (!dead) {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    EnterCriticalSection (&wave->crit_sect);
 | 
			
		||||
    {
 | 
			
		||||
        decr = audio_MIN (dead, wave->avail);
 | 
			
		||||
        wave->avail -= decr;
 | 
			
		||||
    }
 | 
			
		||||
    LeaveCriticalSection (&wave->crit_sect);
 | 
			
		||||
 | 
			
		||||
    ret = decr;
 | 
			
		||||
    while (decr) {
 | 
			
		||||
        int left = hw->samples - hw->wpos;
 | 
			
		||||
        int conv = audio_MIN (left, decr);
 | 
			
		||||
        hw->conv (hw->conv_buf + hw->wpos,
 | 
			
		||||
                  advance (wave->pcm_buf, wave->rpos << hw->info.shift),
 | 
			
		||||
                  conv,
 | 
			
		||||
                  &nominal_volume);
 | 
			
		||||
 | 
			
		||||
        wave->rpos = (wave->rpos + conv) % hw->samples;
 | 
			
		||||
        hw->wpos = (hw->wpos + conv) % hw->samples;
 | 
			
		||||
        decr -= conv;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    winwave_add_buffers (wave, ret);
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int winwave_read (SWVoiceIn *sw, void *buf, int size)
 | 
			
		||||
{
 | 
			
		||||
    return audio_pcm_sw_read (sw, buf, size);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int winwave_ctl_in (HWVoiceIn *hw, int cmd, ...)
 | 
			
		||||
{
 | 
			
		||||
    MMRESULT mr;
 | 
			
		||||
    WaveVoiceIn *wave = (WaveVoiceIn *) 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);
 | 
			
		||||
 | 
			
		||||
            if (poll_mode && !wave->event) {
 | 
			
		||||
                wave->event = CreateEvent (NULL, TRUE, TRUE, NULL);
 | 
			
		||||
                if (!wave->event) {
 | 
			
		||||
                    dolog ("ADC CreateEvent: %lx, poll mode will be disabled\n",
 | 
			
		||||
                           GetLastError ());
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (wave->event) {
 | 
			
		||||
                int ret;
 | 
			
		||||
 | 
			
		||||
                ret = qemu_add_wait_object (wave->event, winwave_poll, wave);
 | 
			
		||||
                hw->poll_mode = (ret == 0);
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
                hw->poll_mode = 0;
 | 
			
		||||
            }
 | 
			
		||||
            if (wave->paused) {
 | 
			
		||||
                mr = waveInStart (wave->hwi);
 | 
			
		||||
                if (mr != MMSYSERR_NOERROR) {
 | 
			
		||||
                    winwave_logerr (mr, "waveInStart");
 | 
			
		||||
                }
 | 
			
		||||
                wave->paused = 0;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return 0;
 | 
			
		||||
 | 
			
		||||
    case VOICE_DISABLE:
 | 
			
		||||
        if (!wave->paused) {
 | 
			
		||||
            mr = waveInStop (wave->hwi);
 | 
			
		||||
            if (mr != MMSYSERR_NOERROR) {
 | 
			
		||||
                winwave_logerr (mr, "waveInStop");
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
                wave->paused = 1;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (wave->event) {
 | 
			
		||||
            qemu_del_wait_object (wave->event, winwave_poll, wave);
 | 
			
		||||
        }
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void *winwave_audio_init (void)
 | 
			
		||||
{
 | 
			
		||||
    return &conf;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void winwave_audio_fini (void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    (void) opaque;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct audio_option winwave_options[] = {
 | 
			
		||||
    {
 | 
			
		||||
        .name        = "DAC_HEADERS",
 | 
			
		||||
        .tag         = AUD_OPT_INT,
 | 
			
		||||
        .valp        = &conf.dac_headers,
 | 
			
		||||
        .descr       = "DAC number of headers",
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        .name        = "DAC_SAMPLES",
 | 
			
		||||
        .tag         = AUD_OPT_INT,
 | 
			
		||||
        .valp        = &conf.dac_samples,
 | 
			
		||||
        .descr       = "DAC number of samples per header",
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        .name        = "ADC_HEADERS",
 | 
			
		||||
        .tag         = AUD_OPT_INT,
 | 
			
		||||
        .valp        = &conf.adc_headers,
 | 
			
		||||
        .descr       = "ADC number of headers",
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        .name        = "ADC_SAMPLES",
 | 
			
		||||
        .tag         = AUD_OPT_INT,
 | 
			
		||||
        .valp        = &conf.adc_samples,
 | 
			
		||||
        .descr       = "ADC number of samples per header",
 | 
			
		||||
    },
 | 
			
		||||
    { /* End of list */ }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct audio_pcm_ops winwave_pcm_ops = {
 | 
			
		||||
    .init_out = winwave_init_out,
 | 
			
		||||
    .fini_out = winwave_fini_out,
 | 
			
		||||
    .run_out  = winwave_run_out,
 | 
			
		||||
    .write    = winwave_write,
 | 
			
		||||
    .ctl_out  = winwave_ctl_out,
 | 
			
		||||
    .init_in  = winwave_init_in,
 | 
			
		||||
    .fini_in  = winwave_fini_in,
 | 
			
		||||
    .run_in   = winwave_run_in,
 | 
			
		||||
    .read     = winwave_read,
 | 
			
		||||
    .ctl_in   = winwave_ctl_in
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct audio_driver winwave_audio_driver = {
 | 
			
		||||
    .name           = "winwave",
 | 
			
		||||
    .descr          = "Windows Waveform Audio http://msdn.microsoft.com",
 | 
			
		||||
    .options        = winwave_options,
 | 
			
		||||
    .init           = winwave_audio_init,
 | 
			
		||||
    .fini           = winwave_audio_fini,
 | 
			
		||||
    .pcm_ops        = &winwave_pcm_ops,
 | 
			
		||||
    .can_be_default = 1,
 | 
			
		||||
    .max_voices_out = INT_MAX,
 | 
			
		||||
    .max_voices_in  = INT_MAX,
 | 
			
		||||
    .voice_size_out = sizeof (WaveVoiceOut),
 | 
			
		||||
    .voice_size_in  = sizeof (WaveVoiceIn)
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										146
									
								
								balloon.c
									
									
									
									
									
								
							
							
						
						
									
										146
									
								
								balloon.c
									
									
									
									
									
								
							@@ -1,146 +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 "sysemu.h"
 | 
			
		||||
#include "monitor.h"
 | 
			
		||||
#include "qjson.h"
 | 
			
		||||
#include "qint.h"
 | 
			
		||||
#include "cpu-common.h"
 | 
			
		||||
#include "kvm.h"
 | 
			
		||||
#include "balloon.h"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static QEMUBalloonEvent *qemu_balloon_event;
 | 
			
		||||
void *qemu_balloon_event_opaque;
 | 
			
		||||
 | 
			
		||||
void qemu_add_balloon_handler(QEMUBalloonEvent *func, void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    qemu_balloon_event = func;
 | 
			
		||||
    qemu_balloon_event_opaque = opaque;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int qemu_balloon(ram_addr_t target, MonitorCompletion cb, void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    if (qemu_balloon_event) {
 | 
			
		||||
        qemu_balloon_event(qemu_balloon_event_opaque, target, cb, opaque);
 | 
			
		||||
        return 1;
 | 
			
		||||
    } else {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int qemu_balloon_status(MonitorCompletion cb, void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    if (qemu_balloon_event) {
 | 
			
		||||
        qemu_balloon_event(qemu_balloon_event_opaque, 0, cb, opaque);
 | 
			
		||||
        return 1;
 | 
			
		||||
    } else {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void print_balloon_stat(const char *key, QObject *obj, void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    Monitor *mon = opaque;
 | 
			
		||||
 | 
			
		||||
    if (strcmp(key, "actual"))
 | 
			
		||||
        monitor_printf(mon, ",%s=%" PRId64, key,
 | 
			
		||||
                       qint_get_int(qobject_to_qint(obj)));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void monitor_print_balloon(Monitor *mon, const QObject *data)
 | 
			
		||||
{
 | 
			
		||||
    QDict *qdict;
 | 
			
		||||
 | 
			
		||||
    qdict = qobject_to_qdict(data);
 | 
			
		||||
    if (!qdict_haskey(qdict, "actual"))
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    monitor_printf(mon, "balloon: actual=%" PRId64,
 | 
			
		||||
                   qdict_get_int(qdict, "actual") >> 20);
 | 
			
		||||
    qdict_iter(qdict, print_balloon_stat, mon);
 | 
			
		||||
    monitor_printf(mon, "\n");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * do_info_balloon(): Balloon information
 | 
			
		||||
 *
 | 
			
		||||
 * Make an asynchronous request for balloon info.  When the request completes
 | 
			
		||||
 * a QDict will be returned according to the following specification:
 | 
			
		||||
 *
 | 
			
		||||
 * - "actual": current balloon value in bytes
 | 
			
		||||
 * The following fields may or may not be present:
 | 
			
		||||
 * - "mem_swapped_in": Amount of memory swapped in (bytes)
 | 
			
		||||
 * - "mem_swapped_out": Amount of memory swapped out (bytes)
 | 
			
		||||
 * - "major_page_faults": Number of major faults
 | 
			
		||||
 * - "minor_page_faults": Number of minor faults
 | 
			
		||||
 * - "free_mem": Total amount of free and unused memory (bytes)
 | 
			
		||||
 * - "total_mem": Total amount of available memory (bytes)
 | 
			
		||||
 *
 | 
			
		||||
 * Example:
 | 
			
		||||
 *
 | 
			
		||||
 * { "actual": 1073741824, "mem_swapped_in": 0, "mem_swapped_out": 0,
 | 
			
		||||
 *   "major_page_faults": 142, "minor_page_faults": 239245,
 | 
			
		||||
 *   "free_mem": 1014185984, "total_mem": 1044668416 }
 | 
			
		||||
 */
 | 
			
		||||
int do_info_balloon(Monitor *mon, MonitorCompletion cb, void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    int ret;
 | 
			
		||||
 | 
			
		||||
    if (kvm_enabled() && !kvm_has_sync_mmu()) {
 | 
			
		||||
        qerror_report(QERR_KVM_MISSING_CAP, "synchronous MMU", "balloon");
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ret = qemu_balloon_status(cb, opaque);
 | 
			
		||||
    if (!ret) {
 | 
			
		||||
        qerror_report(QERR_DEVICE_NOT_ACTIVE, "balloon");
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * do_balloon(): Request VM to change its memory allocation
 | 
			
		||||
 */
 | 
			
		||||
int do_balloon(Monitor *mon, const QDict *params,
 | 
			
		||||
	       MonitorCompletion cb, void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    int ret;
 | 
			
		||||
 | 
			
		||||
    if (kvm_enabled() && !kvm_has_sync_mmu()) {
 | 
			
		||||
        qerror_report(QERR_KVM_MISSING_CAP, "synchronous MMU", "balloon");
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ret = qemu_balloon(qdict_get_int(params, "value"), cb, opaque);
 | 
			
		||||
    if (ret == 0) {
 | 
			
		||||
        qerror_report(QERR_DEVICE_NOT_ACTIVE, "balloon");
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    cb(opaque, NULL);
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										33
									
								
								balloon.h
									
									
									
									
									
								
							
							
						
						
									
										33
									
								
								balloon.h
									
									
									
									
									
								
							@@ -1,33 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Balloon
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright IBM, Corp. 2008
 | 
			
		||||
 *
 | 
			
		||||
 * Authors:
 | 
			
		||||
 *  Anthony Liguori   <aliguori@us.ibm.com>
 | 
			
		||||
 *
 | 
			
		||||
 * This work is licensed under the terms of the GNU GPL, version 2.  See
 | 
			
		||||
 * the COPYING file in the top-level directory.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef _QEMU_BALLOON_H
 | 
			
		||||
#define _QEMU_BALLOON_H
 | 
			
		||||
 | 
			
		||||
#include "monitor.h"
 | 
			
		||||
 | 
			
		||||
typedef void (QEMUBalloonEvent)(void *opaque, ram_addr_t target,
 | 
			
		||||
                                MonitorCompletion cb, void *cb_data);
 | 
			
		||||
 | 
			
		||||
void qemu_add_balloon_handler(QEMUBalloonEvent *func, void *opaque);
 | 
			
		||||
 | 
			
		||||
int qemu_balloon(ram_addr_t target, MonitorCompletion cb, void *opaque);
 | 
			
		||||
 | 
			
		||||
int qemu_balloon_status(MonitorCompletion cb, void *opaque);
 | 
			
		||||
 | 
			
		||||
void monitor_print_balloon(Monitor *mon, const QObject *data);
 | 
			
		||||
int do_info_balloon(Monitor *mon, MonitorCompletion cb, void *opaque);
 | 
			
		||||
int do_balloon(Monitor *mon, const QDict *params,
 | 
			
		||||
               MonitorCompletion cb, void *opaque);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
@@ -22,15 +22,13 @@
 | 
			
		||||
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
			
		||||
 * THE SOFTWARE.
 | 
			
		||||
 */
 | 
			
		||||
#include "qemu-common.h"
 | 
			
		||||
#include "vl.h"
 | 
			
		||||
#include "block_int.h"
 | 
			
		||||
#include "module.h"
 | 
			
		||||
 | 
			
		||||
/**************************************************************/
 | 
			
		||||
 | 
			
		||||
#define HEADER_MAGIC "Bochs Virtual HD Image"
 | 
			
		||||
#define HEADER_VERSION 0x00020000
 | 
			
		||||
#define HEADER_V1 0x00010000
 | 
			
		||||
#define HEADER_VERSION 0x00010000
 | 
			
		||||
#define HEADER_SIZE 512
 | 
			
		||||
 | 
			
		||||
#define REDOLOG_TYPE "Redolog"
 | 
			
		||||
@@ -39,7 +37,7 @@
 | 
			
		||||
// not allocated: 0xffffffff
 | 
			
		||||
 | 
			
		||||
// always little-endian
 | 
			
		||||
struct bochs_header_v1 {
 | 
			
		||||
struct bochs_header {
 | 
			
		||||
    char magic[32]; // "Bochs Virtual HD Image"
 | 
			
		||||
    char type[16]; // "Redolog"
 | 
			
		||||
    char subtype[16]; // "Undoable" / "Volatile" / "Growing"
 | 
			
		||||
@@ -58,28 +56,9 @@ struct bochs_header_v1 {
 | 
			
		||||
    } extra;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// always little-endian
 | 
			
		||||
struct bochs_header {
 | 
			
		||||
    char magic[32]; // "Bochs Virtual HD Image"
 | 
			
		||||
    char type[16]; // "Redolog"
 | 
			
		||||
    char subtype[16]; // "Undoable" / "Volatile" / "Growing"
 | 
			
		||||
    uint32_t version;
 | 
			
		||||
    uint32_t header; // size of header
 | 
			
		||||
 | 
			
		||||
    union {
 | 
			
		||||
	struct {
 | 
			
		||||
	    uint32_t catalog; // num of entries
 | 
			
		||||
	    uint32_t bitmap; // bitmap size
 | 
			
		||||
	    uint32_t extent; // extent size
 | 
			
		||||
	    uint32_t reserved; // for ???
 | 
			
		||||
	    uint64_t disk; // disk size
 | 
			
		||||
	    char padding[HEADER_SIZE - 64 - 8 - 24];
 | 
			
		||||
	} redolog;
 | 
			
		||||
	char padding[HEADER_SIZE - 64 - 8];
 | 
			
		||||
    } extra;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef struct BDRVBochsState {
 | 
			
		||||
    int fd;
 | 
			
		||||
 | 
			
		||||
    uint32_t *catalog_bitmap;
 | 
			
		||||
    int catalog_size;
 | 
			
		||||
    
 | 
			
		||||
@@ -100,45 +79,50 @@ static int bochs_probe(const uint8_t *buf, int buf_size, const char *filename)
 | 
			
		||||
    if (!strcmp(bochs->magic, HEADER_MAGIC) &&
 | 
			
		||||
	!strcmp(bochs->type, REDOLOG_TYPE) &&
 | 
			
		||||
	!strcmp(bochs->subtype, GROWING_TYPE) &&
 | 
			
		||||
	((le32_to_cpu(bochs->version) == HEADER_VERSION) ||
 | 
			
		||||
	(le32_to_cpu(bochs->version) == HEADER_V1)))
 | 
			
		||||
	(le32_to_cpu(bochs->version) == HEADER_VERSION))
 | 
			
		||||
	return 100;
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int bochs_open(BlockDriverState *bs, int flags)
 | 
			
		||||
static int bochs_open(BlockDriverState *bs, const char *filename)
 | 
			
		||||
{
 | 
			
		||||
    BDRVBochsState *s = bs->opaque;
 | 
			
		||||
    int i;
 | 
			
		||||
    int fd, i;
 | 
			
		||||
    struct bochs_header bochs;
 | 
			
		||||
    struct bochs_header_v1 header_v1;
 | 
			
		||||
 | 
			
		||||
    fd = open(filename, O_RDWR | O_BINARY | O_LARGEFILE);
 | 
			
		||||
    if (fd < 0) {
 | 
			
		||||
        fd = open(filename, O_RDONLY | O_BINARY | O_LARGEFILE);
 | 
			
		||||
        if (fd < 0)
 | 
			
		||||
            return -1;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    bs->read_only = 1; // no write support yet
 | 
			
		||||
    
 | 
			
		||||
    if (bdrv_pread(bs->file, 0, &bochs, sizeof(bochs)) != sizeof(bochs)) {
 | 
			
		||||
    s->fd = fd;
 | 
			
		||||
 | 
			
		||||
    if (read(fd, &bochs, sizeof(bochs)) != sizeof(bochs)) {
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (strcmp(bochs.magic, HEADER_MAGIC) ||
 | 
			
		||||
        strcmp(bochs.type, REDOLOG_TYPE) ||
 | 
			
		||||
        strcmp(bochs.subtype, GROWING_TYPE) ||
 | 
			
		||||
	((le32_to_cpu(bochs.version) != HEADER_VERSION) &&
 | 
			
		||||
	(le32_to_cpu(bochs.version) != HEADER_V1))) {
 | 
			
		||||
	(le32_to_cpu(bochs.version) != HEADER_VERSION)) {
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (le32_to_cpu(bochs.version) == HEADER_V1) {
 | 
			
		||||
      memcpy(&header_v1, &bochs, sizeof(bochs));
 | 
			
		||||
      bs->total_sectors = le64_to_cpu(header_v1.extra.redolog.disk) / 512;
 | 
			
		||||
    } else {
 | 
			
		||||
    bs->total_sectors = le64_to_cpu(bochs.extra.redolog.disk) / 512;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    lseek(s->fd, le32_to_cpu(bochs.header), SEEK_SET);
 | 
			
		||||
 | 
			
		||||
    s->catalog_size = le32_to_cpu(bochs.extra.redolog.catalog);
 | 
			
		||||
    s->catalog_bitmap = qemu_malloc(s->catalog_size * 4);
 | 
			
		||||
    if (bdrv_pread(bs->file, le32_to_cpu(bochs.header), s->catalog_bitmap,
 | 
			
		||||
                   s->catalog_size * 4) != s->catalog_size * 4)
 | 
			
		||||
    if (!s->catalog_bitmap)
 | 
			
		||||
	goto fail;
 | 
			
		||||
    if (read(s->fd, s->catalog_bitmap, s->catalog_size * 4) !=
 | 
			
		||||
	s->catalog_size * 4)
 | 
			
		||||
	goto fail;
 | 
			
		||||
    for (i = 0; i < s->catalog_size; i++)
 | 
			
		||||
	le32_to_cpus(&s->catalog_bitmap[i]);
 | 
			
		||||
@@ -152,53 +136,68 @@ static int bochs_open(BlockDriverState *bs, int flags)
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
 fail:
 | 
			
		||||
    close(fd);
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int64_t seek_to_sector(BlockDriverState *bs, int64_t sector_num)
 | 
			
		||||
static inline int seek_to_sector(BlockDriverState *bs, int64_t sector_num)
 | 
			
		||||
{
 | 
			
		||||
    BDRVBochsState *s = bs->opaque;
 | 
			
		||||
    int64_t offset = sector_num * 512;
 | 
			
		||||
    int64_t extent_index, extent_offset, bitmap_offset;
 | 
			
		||||
    int64_t extent_index, extent_offset, bitmap_offset, block_offset;
 | 
			
		||||
    char bitmap_entry;
 | 
			
		||||
 | 
			
		||||
    // seek to sector
 | 
			
		||||
    extent_index = offset / s->extent_size;
 | 
			
		||||
    extent_offset = (offset % s->extent_size) / 512;
 | 
			
		||||
    
 | 
			
		||||
    if (s->catalog_bitmap[extent_index] == 0xffffffff) {
 | 
			
		||||
	return -1; /* not allocated */
 | 
			
		||||
    if (s->catalog_bitmap[extent_index] == 0xffffffff)
 | 
			
		||||
    {
 | 
			
		||||
//	fprintf(stderr, "page not allocated [%x - %x:%x]\n",
 | 
			
		||||
//	    sector_num, extent_index, extent_offset);
 | 
			
		||||
	return -1; // not allocated
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bitmap_offset = s->data_offset + (512 * s->catalog_bitmap[extent_index] *
 | 
			
		||||
	(s->extent_blocks + s->bitmap_blocks));
 | 
			
		||||
    block_offset = bitmap_offset + (512 * (s->bitmap_blocks + extent_offset));
 | 
			
		||||
    
 | 
			
		||||
    /* read in bitmap for current extent */
 | 
			
		||||
    if (bdrv_pread(bs->file, bitmap_offset + (extent_offset / 8),
 | 
			
		||||
                   &bitmap_entry, 1) != 1) {
 | 
			
		||||
        return -1;
 | 
			
		||||
//    fprintf(stderr, "sect: %x [ext i: %x o: %x] -> %x bitmap: %x block: %x\n",
 | 
			
		||||
//	sector_num, extent_index, extent_offset,
 | 
			
		||||
//	le32_to_cpu(s->catalog_bitmap[extent_index]),
 | 
			
		||||
//	bitmap_offset, block_offset);
 | 
			
		||||
    
 | 
			
		||||
    // read in bitmap for current extent
 | 
			
		||||
    lseek(s->fd, bitmap_offset + (extent_offset / 8), SEEK_SET);
 | 
			
		||||
    
 | 
			
		||||
    read(s->fd, &bitmap_entry, 1);
 | 
			
		||||
    
 | 
			
		||||
    if (!((bitmap_entry >> (extent_offset % 8)) & 1))
 | 
			
		||||
    {
 | 
			
		||||
//	fprintf(stderr, "sector (%x) in bitmap not allocated\n",
 | 
			
		||||
//	    sector_num);
 | 
			
		||||
	return -1; // not allocated
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!((bitmap_entry >> (extent_offset % 8)) & 1)) {
 | 
			
		||||
	return -1; /* not allocated */
 | 
			
		||||
    }
 | 
			
		||||
    lseek(s->fd, block_offset, SEEK_SET);
 | 
			
		||||
    
 | 
			
		||||
    return bitmap_offset + (512 * (s->bitmap_blocks + extent_offset));
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int bochs_read(BlockDriverState *bs, int64_t sector_num, 
 | 
			
		||||
                    uint8_t *buf, int nb_sectors)
 | 
			
		||||
{
 | 
			
		||||
    BDRVBochsState *s = bs->opaque;
 | 
			
		||||
    int ret;
 | 
			
		||||
 | 
			
		||||
    while (nb_sectors > 0) {
 | 
			
		||||
        int64_t block_offset = seek_to_sector(bs, sector_num);
 | 
			
		||||
        if (block_offset >= 0) {
 | 
			
		||||
            ret = bdrv_pread(bs->file, block_offset, buf, 512);
 | 
			
		||||
            if (ret != 512) {
 | 
			
		||||
	if (!seek_to_sector(bs, sector_num))
 | 
			
		||||
	{
 | 
			
		||||
	    ret = read(s->fd, buf, 512);
 | 
			
		||||
	    if (ret != 512)
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
        } else
 | 
			
		||||
	else
 | 
			
		||||
            memset(buf, 0, 512);
 | 
			
		||||
        nb_sectors--;
 | 
			
		||||
        sector_num++;
 | 
			
		||||
@@ -211,20 +210,15 @@ static void bochs_close(BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
    BDRVBochsState *s = bs->opaque;
 | 
			
		||||
    qemu_free(s->catalog_bitmap);
 | 
			
		||||
    close(s->fd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static BlockDriver bdrv_bochs = {
 | 
			
		||||
    .format_name	= "bochs",
 | 
			
		||||
    .instance_size	= sizeof(BDRVBochsState),
 | 
			
		||||
    .bdrv_probe		= bochs_probe,
 | 
			
		||||
    .bdrv_open		= bochs_open,
 | 
			
		||||
    .bdrv_read		= bochs_read,
 | 
			
		||||
    .bdrv_close		= bochs_close,
 | 
			
		||||
BlockDriver bdrv_bochs = {
 | 
			
		||||
    "bochs",
 | 
			
		||||
    sizeof(BDRVBochsState),
 | 
			
		||||
    bochs_probe,
 | 
			
		||||
    bochs_open,
 | 
			
		||||
    bochs_read,
 | 
			
		||||
    NULL,
 | 
			
		||||
    bochs_close,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void bdrv_bochs_init(void)
 | 
			
		||||
{
 | 
			
		||||
    bdrv_register(&bdrv_bochs);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
block_init(bdrv_bochs_init);
 | 
			
		||||
@@ -21,12 +21,12 @@
 | 
			
		||||
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
			
		||||
 * THE SOFTWARE.
 | 
			
		||||
 */
 | 
			
		||||
#include "qemu-common.h"
 | 
			
		||||
#include "vl.h"
 | 
			
		||||
#include "block_int.h"
 | 
			
		||||
#include "module.h"
 | 
			
		||||
#include <zlib.h>
 | 
			
		||||
 | 
			
		||||
typedef struct BDRVCloopState {
 | 
			
		||||
    int fd;
 | 
			
		||||
    uint32_t block_size;
 | 
			
		||||
    uint32_t n_blocks;
 | 
			
		||||
    uint64_t* offsets;
 | 
			
		||||
@@ -50,31 +50,35 @@ static int cloop_probe(const uint8_t *buf, int buf_size, const char *filename)
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int cloop_open(BlockDriverState *bs, int flags)
 | 
			
		||||
static int cloop_open(BlockDriverState *bs, const char *filename)
 | 
			
		||||
{
 | 
			
		||||
    BDRVCloopState *s = bs->opaque;
 | 
			
		||||
    uint32_t offsets_size,max_compressed_block_size=1,i;
 | 
			
		||||
 | 
			
		||||
    s->fd = open(filename, O_RDONLY | O_BINARY | O_LARGEFILE);
 | 
			
		||||
    if (s->fd < 0)
 | 
			
		||||
        return -1;
 | 
			
		||||
    bs->read_only = 1;
 | 
			
		||||
 | 
			
		||||
    /* read header */
 | 
			
		||||
    if (bdrv_pread(bs->file, 128, &s->block_size, 4) < 4) {
 | 
			
		||||
        goto cloop_close;
 | 
			
		||||
    if(lseek(s->fd,128,SEEK_SET)<0) {
 | 
			
		||||
cloop_close:
 | 
			
		||||
	close(s->fd);
 | 
			
		||||
	return -1;
 | 
			
		||||
    }
 | 
			
		||||
    if(read(s->fd,&s->block_size,4)<4)
 | 
			
		||||
	goto cloop_close;
 | 
			
		||||
    s->block_size=be32_to_cpu(s->block_size);
 | 
			
		||||
 | 
			
		||||
    if (bdrv_pread(bs->file, 128 + 4, &s->n_blocks, 4) < 4) {
 | 
			
		||||
    if(read(s->fd,&s->n_blocks,4)<4)
 | 
			
		||||
	goto cloop_close;
 | 
			
		||||
    }
 | 
			
		||||
    s->n_blocks=be32_to_cpu(s->n_blocks);
 | 
			
		||||
 | 
			
		||||
    /* read offsets */
 | 
			
		||||
    offsets_size=s->n_blocks*sizeof(uint64_t);
 | 
			
		||||
    s->offsets = qemu_malloc(offsets_size);
 | 
			
		||||
    if (bdrv_pread(bs->file, 128 + 4 + 4, s->offsets, offsets_size) <
 | 
			
		||||
            offsets_size) {
 | 
			
		||||
    if(!(s->offsets=(uint64_t*)malloc(offsets_size)))
 | 
			
		||||
	goto cloop_close;
 | 
			
		||||
    if(read(s->fd,s->offsets,offsets_size)<offsets_size)
 | 
			
		||||
	goto cloop_close;
 | 
			
		||||
    }
 | 
			
		||||
    for(i=0;i<s->n_blocks;i++) {
 | 
			
		||||
	s->offsets[i]=be64_to_cpu(s->offsets[i]);
 | 
			
		||||
	if(i>0) {
 | 
			
		||||
@@ -85,8 +89,10 @@ static int cloop_open(BlockDriverState *bs, int flags)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* initialize zlib engine */
 | 
			
		||||
    s->compressed_block = qemu_malloc(max_compressed_block_size+1);
 | 
			
		||||
    s->uncompressed_block = qemu_malloc(s->block_size);
 | 
			
		||||
    if(!(s->compressed_block = malloc(max_compressed_block_size+1)))
 | 
			
		||||
	goto cloop_close;
 | 
			
		||||
    if(!(s->uncompressed_block = malloc(s->block_size)))
 | 
			
		||||
	goto cloop_close;
 | 
			
		||||
    if(inflateInit(&s->zstream) != Z_OK)
 | 
			
		||||
	goto cloop_close;
 | 
			
		||||
    s->current_block=s->n_blocks;
 | 
			
		||||
@@ -94,21 +100,16 @@ static int cloop_open(BlockDriverState *bs, int flags)
 | 
			
		||||
    s->sectors_per_block = s->block_size/512;
 | 
			
		||||
    bs->total_sectors = s->n_blocks*s->sectors_per_block;
 | 
			
		||||
    return 0;
 | 
			
		||||
 | 
			
		||||
cloop_close:
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline int cloop_read_block(BlockDriverState *bs, int block_num)
 | 
			
		||||
static inline int cloop_read_block(BDRVCloopState *s,int block_num)
 | 
			
		||||
{
 | 
			
		||||
    BDRVCloopState *s = bs->opaque;
 | 
			
		||||
 | 
			
		||||
    if(s->current_block != block_num) {
 | 
			
		||||
	int ret;
 | 
			
		||||
        uint32_t bytes = s->offsets[block_num+1]-s->offsets[block_num];
 | 
			
		||||
	    
 | 
			
		||||
        ret = bdrv_pread(bs->file, s->offsets[block_num], s->compressed_block,
 | 
			
		||||
                         bytes);
 | 
			
		||||
	lseek(s->fd, s->offsets[block_num], SEEK_SET);
 | 
			
		||||
        ret = read(s->fd, s->compressed_block, bytes);
 | 
			
		||||
        if (ret != bytes) 
 | 
			
		||||
            return -1;
 | 
			
		||||
	
 | 
			
		||||
@@ -137,7 +138,7 @@ static int cloop_read(BlockDriverState *bs, int64_t sector_num,
 | 
			
		||||
    for(i=0;i<nb_sectors;i++) {
 | 
			
		||||
	uint32_t sector_offset_in_block=((sector_num+i)%s->sectors_per_block),
 | 
			
		||||
	    block_num=(sector_num+i)/s->sectors_per_block;
 | 
			
		||||
	if(cloop_read_block(bs, block_num) != 0)
 | 
			
		||||
	if(cloop_read_block(s, block_num) != 0)
 | 
			
		||||
	    return -1;
 | 
			
		||||
	memcpy(buf+i*512,s->uncompressed_block+sector_offset_in_block*512,512);
 | 
			
		||||
    }
 | 
			
		||||
@@ -147,6 +148,7 @@ static int cloop_read(BlockDriverState *bs, int64_t sector_num,
 | 
			
		||||
static void cloop_close(BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
    BDRVCloopState *s = bs->opaque;
 | 
			
		||||
    close(s->fd);
 | 
			
		||||
    if(s->n_blocks>0)
 | 
			
		||||
	free(s->offsets);
 | 
			
		||||
    free(s->compressed_block);
 | 
			
		||||
@@ -154,18 +156,14 @@ static void cloop_close(BlockDriverState *bs)
 | 
			
		||||
    inflateEnd(&s->zstream);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static BlockDriver bdrv_cloop = {
 | 
			
		||||
    .format_name	= "cloop",
 | 
			
		||||
    .instance_size	= sizeof(BDRVCloopState),
 | 
			
		||||
    .bdrv_probe		= cloop_probe,
 | 
			
		||||
    .bdrv_open		= cloop_open,
 | 
			
		||||
    .bdrv_read		= cloop_read,
 | 
			
		||||
    .bdrv_close		= cloop_close,
 | 
			
		||||
BlockDriver bdrv_cloop = {
 | 
			
		||||
    "cloop",
 | 
			
		||||
    sizeof(BDRVCloopState),
 | 
			
		||||
    cloop_probe,
 | 
			
		||||
    cloop_open,
 | 
			
		||||
    cloop_read,
 | 
			
		||||
    NULL,
 | 
			
		||||
    cloop_close,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void bdrv_cloop_init(void)
 | 
			
		||||
{
 | 
			
		||||
    bdrv_register(&bdrv_cloop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
block_init(bdrv_cloop_init);
 | 
			
		||||
							
								
								
									
										264
									
								
								block-cow.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										264
									
								
								block-cow.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,264 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Block driver for the COW format
 | 
			
		||||
 * 
 | 
			
		||||
 * Copyright (c) 2004 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.
 | 
			
		||||
 */
 | 
			
		||||
#ifndef _WIN32
 | 
			
		||||
#include "vl.h"
 | 
			
		||||
#include "block_int.h"
 | 
			
		||||
#include <sys/mman.h>
 | 
			
		||||
 | 
			
		||||
/**************************************************************/
 | 
			
		||||
/* COW block driver using file system holes */
 | 
			
		||||
 | 
			
		||||
/* user mode linux compatible COW file */
 | 
			
		||||
#define COW_MAGIC 0x4f4f4f4d  /* MOOO */
 | 
			
		||||
#define COW_VERSION 2
 | 
			
		||||
 | 
			
		||||
struct cow_header_v2 {
 | 
			
		||||
    uint32_t magic;
 | 
			
		||||
    uint32_t version;
 | 
			
		||||
    char backing_file[1024];
 | 
			
		||||
    int32_t mtime;
 | 
			
		||||
    uint64_t size;
 | 
			
		||||
    uint32_t sectorsize;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef struct BDRVCowState {
 | 
			
		||||
    int fd;
 | 
			
		||||
    uint8_t *cow_bitmap; /* if non NULL, COW mappings are used first */
 | 
			
		||||
    uint8_t *cow_bitmap_addr; /* mmap address of cow_bitmap */
 | 
			
		||||
    int cow_bitmap_size;
 | 
			
		||||
    int64_t cow_sectors_offset;
 | 
			
		||||
} BDRVCowState;
 | 
			
		||||
 | 
			
		||||
static int cow_probe(const uint8_t *buf, int buf_size, const char *filename)
 | 
			
		||||
{
 | 
			
		||||
    const struct cow_header_v2 *cow_header = (const void *)buf;
 | 
			
		||||
 | 
			
		||||
    if (buf_size >= sizeof(struct cow_header_v2) &&
 | 
			
		||||
        be32_to_cpu(cow_header->magic) == COW_MAGIC &&
 | 
			
		||||
        be32_to_cpu(cow_header->version) == COW_VERSION) 
 | 
			
		||||
        return 100;
 | 
			
		||||
    else
 | 
			
		||||
        return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int cow_open(BlockDriverState *bs, const char *filename)
 | 
			
		||||
{
 | 
			
		||||
    BDRVCowState *s = bs->opaque;
 | 
			
		||||
    int fd;
 | 
			
		||||
    struct cow_header_v2 cow_header;
 | 
			
		||||
    int64_t size;
 | 
			
		||||
 | 
			
		||||
    fd = open(filename, O_RDWR | O_BINARY | O_LARGEFILE);
 | 
			
		||||
    if (fd < 0) {
 | 
			
		||||
        fd = open(filename, O_RDONLY | O_BINARY | O_LARGEFILE);
 | 
			
		||||
        if (fd < 0)
 | 
			
		||||
            return -1;
 | 
			
		||||
    }
 | 
			
		||||
    s->fd = fd;
 | 
			
		||||
    /* see if it is a cow image */
 | 
			
		||||
    if (read(fd, &cow_header, sizeof(cow_header)) != sizeof(cow_header)) {
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (be32_to_cpu(cow_header.magic) != COW_MAGIC ||
 | 
			
		||||
        be32_to_cpu(cow_header.version) != COW_VERSION) {
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
        
 | 
			
		||||
    /* cow image found */
 | 
			
		||||
    size = be64_to_cpu(cow_header.size);
 | 
			
		||||
    bs->total_sectors = size / 512;
 | 
			
		||||
 | 
			
		||||
    pstrcpy(bs->backing_file, sizeof(bs->backing_file), 
 | 
			
		||||
            cow_header.backing_file);
 | 
			
		||||
    
 | 
			
		||||
#if 0
 | 
			
		||||
    if (cow_header.backing_file[0] != '\0') {
 | 
			
		||||
        if (stat(cow_header.backing_file, &st) != 0) {
 | 
			
		||||
            fprintf(stderr, "%s: could not find original disk image '%s'\n", filename, cow_header.backing_file);
 | 
			
		||||
            goto fail;
 | 
			
		||||
        }
 | 
			
		||||
        if (st.st_mtime != be32_to_cpu(cow_header.mtime)) {
 | 
			
		||||
            fprintf(stderr, "%s: original raw disk image '%s' does not match saved timestamp\n", filename, cow_header.backing_file);
 | 
			
		||||
            goto fail;
 | 
			
		||||
            }
 | 
			
		||||
        fd = open(cow_header.backing_file, O_RDONLY | O_LARGEFILE);
 | 
			
		||||
        if (fd < 0)
 | 
			
		||||
            goto fail;
 | 
			
		||||
        bs->fd = fd;
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
    /* mmap the bitmap */
 | 
			
		||||
    s->cow_bitmap_size = ((bs->total_sectors + 7) >> 3) + sizeof(cow_header);
 | 
			
		||||
    s->cow_bitmap_addr = mmap(get_mmap_addr(s->cow_bitmap_size), 
 | 
			
		||||
                              s->cow_bitmap_size, 
 | 
			
		||||
                              PROT_READ | PROT_WRITE,
 | 
			
		||||
                              MAP_SHARED, s->fd, 0);
 | 
			
		||||
    if (s->cow_bitmap_addr == MAP_FAILED)
 | 
			
		||||
        goto fail;
 | 
			
		||||
    s->cow_bitmap = s->cow_bitmap_addr + sizeof(cow_header);
 | 
			
		||||
    s->cow_sectors_offset = (s->cow_bitmap_size + 511) & ~511;
 | 
			
		||||
    return 0;
 | 
			
		||||
 fail:
 | 
			
		||||
    close(fd);
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void cow_set_bit(uint8_t *bitmap, int64_t bitnum)
 | 
			
		||||
{
 | 
			
		||||
    bitmap[bitnum / 8] |= (1 << (bitnum%8));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline int is_bit_set(const uint8_t *bitmap, int64_t bitnum)
 | 
			
		||||
{
 | 
			
		||||
    return !!(bitmap[bitnum / 8] & (1 << (bitnum%8)));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Return true if first block has been changed (ie. current version is
 | 
			
		||||
 * in COW file).  Set the number of continuous blocks for which that
 | 
			
		||||
 * is true. */
 | 
			
		||||
static inline int is_changed(uint8_t *bitmap,
 | 
			
		||||
                             int64_t sector_num, int nb_sectors,
 | 
			
		||||
                             int *num_same)
 | 
			
		||||
{
 | 
			
		||||
    int changed;
 | 
			
		||||
 | 
			
		||||
    if (!bitmap || nb_sectors == 0) {
 | 
			
		||||
	*num_same = nb_sectors;
 | 
			
		||||
	return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    changed = is_bit_set(bitmap, sector_num);
 | 
			
		||||
    for (*num_same = 1; *num_same < nb_sectors; (*num_same)++) {
 | 
			
		||||
	if (is_bit_set(bitmap, sector_num + *num_same) != changed)
 | 
			
		||||
	    break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return changed;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int cow_is_allocated(BlockDriverState *bs, int64_t sector_num, 
 | 
			
		||||
                            int nb_sectors, int *pnum)
 | 
			
		||||
{
 | 
			
		||||
    BDRVCowState *s = bs->opaque;
 | 
			
		||||
    return is_changed(s->cow_bitmap, sector_num, nb_sectors, pnum);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int cow_read(BlockDriverState *bs, int64_t sector_num, 
 | 
			
		||||
                    uint8_t *buf, int nb_sectors)
 | 
			
		||||
{
 | 
			
		||||
    BDRVCowState *s = bs->opaque;
 | 
			
		||||
    int ret, n;
 | 
			
		||||
    
 | 
			
		||||
    while (nb_sectors > 0) {
 | 
			
		||||
        if (is_changed(s->cow_bitmap, sector_num, nb_sectors, &n)) {
 | 
			
		||||
            lseek(s->fd, s->cow_sectors_offset + sector_num * 512, SEEK_SET);
 | 
			
		||||
            ret = read(s->fd, buf, n * 512);
 | 
			
		||||
            if (ret != n * 512) 
 | 
			
		||||
                return -1;
 | 
			
		||||
        } else {
 | 
			
		||||
            memset(buf, 0, n * 512);
 | 
			
		||||
        }
 | 
			
		||||
        nb_sectors -= n;
 | 
			
		||||
        sector_num += n;
 | 
			
		||||
        buf += n * 512;
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int cow_write(BlockDriverState *bs, int64_t sector_num, 
 | 
			
		||||
                     const uint8_t *buf, int nb_sectors)
 | 
			
		||||
{
 | 
			
		||||
    BDRVCowState *s = bs->opaque;
 | 
			
		||||
    int ret, i;
 | 
			
		||||
    
 | 
			
		||||
    lseek(s->fd, s->cow_sectors_offset + sector_num * 512, SEEK_SET);
 | 
			
		||||
    ret = write(s->fd, buf, nb_sectors * 512);
 | 
			
		||||
    if (ret != nb_sectors * 512) 
 | 
			
		||||
        return -1;
 | 
			
		||||
    for (i = 0; i < nb_sectors; i++)
 | 
			
		||||
        cow_set_bit(s->cow_bitmap, sector_num + i);
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void cow_close(BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
    BDRVCowState *s = bs->opaque;
 | 
			
		||||
    munmap(s->cow_bitmap_addr, s->cow_bitmap_size);
 | 
			
		||||
    close(s->fd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int cow_create(const char *filename, int64_t image_sectors,
 | 
			
		||||
                      const char *image_filename, int flags)
 | 
			
		||||
{
 | 
			
		||||
    int fd, cow_fd;
 | 
			
		||||
    struct cow_header_v2 cow_header;
 | 
			
		||||
    struct stat st;
 | 
			
		||||
 | 
			
		||||
    if (flags)
 | 
			
		||||
        return -ENOTSUP;
 | 
			
		||||
 | 
			
		||||
    cow_fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE, 
 | 
			
		||||
              0644);
 | 
			
		||||
    if (cow_fd < 0)
 | 
			
		||||
        return -1;
 | 
			
		||||
    memset(&cow_header, 0, sizeof(cow_header));
 | 
			
		||||
    cow_header.magic = cpu_to_be32(COW_MAGIC);
 | 
			
		||||
    cow_header.version = cpu_to_be32(COW_VERSION);
 | 
			
		||||
    if (image_filename) {
 | 
			
		||||
        fd = open(image_filename, O_RDONLY | O_BINARY);
 | 
			
		||||
        if (fd < 0) {
 | 
			
		||||
            close(cow_fd);
 | 
			
		||||
            return -1;
 | 
			
		||||
        }
 | 
			
		||||
        if (fstat(fd, &st) != 0) {
 | 
			
		||||
            close(fd);
 | 
			
		||||
            return -1;
 | 
			
		||||
        }
 | 
			
		||||
        close(fd);
 | 
			
		||||
        cow_header.mtime = cpu_to_be32(st.st_mtime);
 | 
			
		||||
        realpath(image_filename, cow_header.backing_file);
 | 
			
		||||
    }
 | 
			
		||||
    cow_header.sectorsize = cpu_to_be32(512);
 | 
			
		||||
    cow_header.size = cpu_to_be64(image_sectors * 512);
 | 
			
		||||
    write(cow_fd, &cow_header, sizeof(cow_header));
 | 
			
		||||
    /* resize to include at least all the bitmap */
 | 
			
		||||
    ftruncate(cow_fd, sizeof(cow_header) + ((image_sectors + 7) >> 3));
 | 
			
		||||
    close(cow_fd);
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
BlockDriver bdrv_cow = {
 | 
			
		||||
    "cow",
 | 
			
		||||
    sizeof(BDRVCowState),
 | 
			
		||||
    cow_probe,
 | 
			
		||||
    cow_open,
 | 
			
		||||
    cow_read,
 | 
			
		||||
    cow_write,
 | 
			
		||||
    cow_close,
 | 
			
		||||
    cow_create,
 | 
			
		||||
    cow_is_allocated,
 | 
			
		||||
};
 | 
			
		||||
#endif
 | 
			
		||||
@@ -21,13 +21,14 @@
 | 
			
		||||
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
			
		||||
 * THE SOFTWARE.
 | 
			
		||||
 */
 | 
			
		||||
#include "qemu-common.h"
 | 
			
		||||
#include "vl.h"
 | 
			
		||||
#include "block_int.h"
 | 
			
		||||
#include "bswap.h"
 | 
			
		||||
#include "module.h"
 | 
			
		||||
#include <zlib.h>
 | 
			
		||||
 | 
			
		||||
typedef struct BDRVDMGState {
 | 
			
		||||
    int fd;
 | 
			
		||||
    
 | 
			
		||||
    /* each chunk contains a certain number of sectors,
 | 
			
		||||
     * offsets[i] is the offset in the .dmg file,
 | 
			
		||||
     * lengths[i] is the length of the compressed chunk,
 | 
			
		||||
@@ -56,86 +57,82 @@ static int dmg_probe(const uint8_t *buf, int buf_size, const char *filename)
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static off_t read_off(BlockDriverState *bs, int64_t offset)
 | 
			
		||||
static off_t read_off(int fd)
 | 
			
		||||
{
 | 
			
		||||
	uint64_t buffer;
 | 
			
		||||
	if (bdrv_pread(bs->file, offset, &buffer, 8) < 8)
 | 
			
		||||
	if(read(fd,&buffer,8)<8)
 | 
			
		||||
		return 0;
 | 
			
		||||
	return be64_to_cpu(buffer);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static off_t read_uint32(BlockDriverState *bs, int64_t offset)
 | 
			
		||||
static off_t read_uint32(int fd)
 | 
			
		||||
{
 | 
			
		||||
	uint32_t buffer;
 | 
			
		||||
	if (bdrv_pread(bs->file, offset, &buffer, 4) < 4)
 | 
			
		||||
	if(read(fd,&buffer,4)<4)
 | 
			
		||||
		return 0;
 | 
			
		||||
	return be32_to_cpu(buffer);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int dmg_open(BlockDriverState *bs, int flags)
 | 
			
		||||
static int dmg_open(BlockDriverState *bs, const char *filename)
 | 
			
		||||
{
 | 
			
		||||
    BDRVDMGState *s = bs->opaque;
 | 
			
		||||
    off_t info_begin,info_end,last_in_offset,last_out_offset;
 | 
			
		||||
    uint32_t count;
 | 
			
		||||
    uint32_t max_compressed_size=1,max_sectors_per_chunk=1,i;
 | 
			
		||||
    int64_t offset;
 | 
			
		||||
 | 
			
		||||
    s->fd = open(filename, O_RDONLY | O_BINARY | O_LARGEFILE);
 | 
			
		||||
    if (s->fd < 0)
 | 
			
		||||
        return -1;
 | 
			
		||||
    bs->read_only = 1;
 | 
			
		||||
    s->n_chunks = 0;
 | 
			
		||||
    s->offsets = s->lengths = s->sectors = s->sectorcounts = NULL;
 | 
			
		||||
    s->offsets = s->lengths = s->sectors = s->sectorcounts = 0;
 | 
			
		||||
    
 | 
			
		||||
    /* read offset of info blocks */
 | 
			
		||||
    offset = bdrv_getlength(bs->file);
 | 
			
		||||
    if (offset < 0) {
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
    offset -= 0x1d8;
 | 
			
		||||
 | 
			
		||||
    info_begin = read_off(bs, offset);
 | 
			
		||||
    if (info_begin == 0) {
 | 
			
		||||
	goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (read_uint32(bs, info_begin) != 0x100) {
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    count = read_uint32(bs, info_begin + 4);
 | 
			
		||||
    if (count == 0) {
 | 
			
		||||
        goto fail;
 | 
			
		||||
    if(lseek(s->fd,-0x1d8,SEEK_END)<0) {
 | 
			
		||||
dmg_close:
 | 
			
		||||
	close(s->fd);
 | 
			
		||||
	/* open raw instead */
 | 
			
		||||
	bs->drv=&bdrv_raw;
 | 
			
		||||
	return bs->drv->bdrv_open(bs,filename);
 | 
			
		||||
    }
 | 
			
		||||
    info_begin=read_off(s->fd);
 | 
			
		||||
    if(info_begin==0)
 | 
			
		||||
	goto dmg_close;
 | 
			
		||||
    if(lseek(s->fd,info_begin,SEEK_SET)<0)
 | 
			
		||||
	goto dmg_close;
 | 
			
		||||
    if(read_uint32(s->fd)!=0x100)
 | 
			
		||||
	goto dmg_close;
 | 
			
		||||
    if((count = read_uint32(s->fd))==0)
 | 
			
		||||
	goto dmg_close;
 | 
			
		||||
    info_end = info_begin+count;
 | 
			
		||||
 | 
			
		||||
    offset = info_begin + 0x100;
 | 
			
		||||
    if(lseek(s->fd,0xf8,SEEK_CUR)<0)
 | 
			
		||||
	goto dmg_close;
 | 
			
		||||
 | 
			
		||||
    /* read offsets */
 | 
			
		||||
    last_in_offset = last_out_offset = 0;
 | 
			
		||||
    while (offset < info_end) {
 | 
			
		||||
    while(lseek(s->fd,0,SEEK_CUR)<info_end) {
 | 
			
		||||
        uint32_t type;
 | 
			
		||||
 | 
			
		||||
	count = read_uint32(bs, offset);
 | 
			
		||||
	count = read_uint32(s->fd);
 | 
			
		||||
	if(count==0)
 | 
			
		||||
	    goto fail;
 | 
			
		||||
        offset += 4;
 | 
			
		||||
 | 
			
		||||
	type = read_uint32(bs, offset);
 | 
			
		||||
	if (type == 0x6d697368 && count >= 244) {
 | 
			
		||||
	    goto dmg_close;
 | 
			
		||||
	type = read_uint32(s->fd);
 | 
			
		||||
	if(type!=0x6d697368 || count<244)
 | 
			
		||||
	    lseek(s->fd,count-4,SEEK_CUR);
 | 
			
		||||
	else {
 | 
			
		||||
	    int new_size, chunk_count;
 | 
			
		||||
 | 
			
		||||
            offset += 4;
 | 
			
		||||
            offset += 200;
 | 
			
		||||
 | 
			
		||||
	    if(lseek(s->fd,200,SEEK_CUR)<0)
 | 
			
		||||
	        goto dmg_close;
 | 
			
		||||
	    chunk_count = (count-204)/40;
 | 
			
		||||
	    new_size = sizeof(uint64_t) * (s->n_chunks + chunk_count);
 | 
			
		||||
	    s->types = qemu_realloc(s->types, new_size/2);
 | 
			
		||||
	    s->offsets = qemu_realloc(s->offsets, new_size);
 | 
			
		||||
	    s->lengths = qemu_realloc(s->lengths, new_size);
 | 
			
		||||
	    s->sectors = qemu_realloc(s->sectors, new_size);
 | 
			
		||||
	    s->sectorcounts = qemu_realloc(s->sectorcounts, new_size);
 | 
			
		||||
	    s->types = realloc(s->types, new_size/2);
 | 
			
		||||
	    s->offsets = realloc(s->offsets, new_size);
 | 
			
		||||
	    s->lengths = realloc(s->lengths, new_size);
 | 
			
		||||
	    s->sectors = realloc(s->sectors, new_size);
 | 
			
		||||
	    s->sectorcounts = realloc(s->sectorcounts, new_size);
 | 
			
		||||
 | 
			
		||||
	    for(i=s->n_chunks;i<s->n_chunks+chunk_count;i++) {
 | 
			
		||||
		s->types[i] = read_uint32(bs, offset);
 | 
			
		||||
		offset += 4;
 | 
			
		||||
		s->types[i] = read_uint32(s->fd);
 | 
			
		||||
		if(s->types[i]!=0x80000005 && s->types[i]!=1 && s->types[i]!=2) {
 | 
			
		||||
		    if(s->types[i]==0xffffffff) {
 | 
			
		||||
			last_in_offset = s->offsets[i-1]+s->lengths[i-1];
 | 
			
		||||
@@ -143,23 +140,15 @@ static int dmg_open(BlockDriverState *bs, int flags)
 | 
			
		||||
		    }
 | 
			
		||||
		    chunk_count--;
 | 
			
		||||
		    i--;
 | 
			
		||||
		    offset += 36;
 | 
			
		||||
		    if(lseek(s->fd,36,SEEK_CUR)<0)
 | 
			
		||||
			goto dmg_close;
 | 
			
		||||
		    continue;
 | 
			
		||||
		}
 | 
			
		||||
		offset += 4;
 | 
			
		||||
 | 
			
		||||
		s->sectors[i] = last_out_offset+read_off(bs, offset);
 | 
			
		||||
		offset += 8;
 | 
			
		||||
 | 
			
		||||
		s->sectorcounts[i] = read_off(bs, offset);
 | 
			
		||||
		offset += 8;
 | 
			
		||||
 | 
			
		||||
		s->offsets[i] = last_in_offset+read_off(bs, offset);
 | 
			
		||||
		offset += 8;
 | 
			
		||||
 | 
			
		||||
		s->lengths[i] = read_off(bs, offset);
 | 
			
		||||
		offset += 8;
 | 
			
		||||
 | 
			
		||||
		read_uint32(s->fd);
 | 
			
		||||
		s->sectors[i] = last_out_offset+read_off(s->fd);
 | 
			
		||||
		s->sectorcounts[i] = read_off(s->fd);
 | 
			
		||||
		s->offsets[i] = last_in_offset+read_off(s->fd);
 | 
			
		||||
		s->lengths[i] = read_off(s->fd);
 | 
			
		||||
		if(s->lengths[i]>max_compressed_size)
 | 
			
		||||
		    max_compressed_size = s->lengths[i];
 | 
			
		||||
		if(s->sectorcounts[i]>max_sectors_per_chunk)
 | 
			
		||||
@@ -170,16 +159,16 @@ static int dmg_open(BlockDriverState *bs, int flags)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* initialize zlib engine */
 | 
			
		||||
    s->compressed_chunk = qemu_malloc(max_compressed_size+1);
 | 
			
		||||
    s->uncompressed_chunk = qemu_malloc(512*max_sectors_per_chunk);
 | 
			
		||||
    if(!(s->compressed_chunk = malloc(max_compressed_size+1)))
 | 
			
		||||
	goto dmg_close;
 | 
			
		||||
    if(!(s->uncompressed_chunk = malloc(512*max_sectors_per_chunk)))
 | 
			
		||||
	goto dmg_close;
 | 
			
		||||
    if(inflateInit(&s->zstream) != Z_OK)
 | 
			
		||||
	goto fail;
 | 
			
		||||
	goto dmg_close;
 | 
			
		||||
 | 
			
		||||
    s->current_chunk = s->n_chunks;
 | 
			
		||||
    
 | 
			
		||||
    return 0;
 | 
			
		||||
fail:
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline int is_sector_in_chunk(BDRVDMGState* s,
 | 
			
		||||
@@ -208,10 +197,8 @@ static inline uint32_t search_chunk(BDRVDMGState* s,int sector_num)
 | 
			
		||||
    return s->n_chunks; /* error */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline int dmg_read_chunk(BlockDriverState *bs, int sector_num)
 | 
			
		||||
static inline int dmg_read_chunk(BDRVDMGState *s,int sector_num)
 | 
			
		||||
{
 | 
			
		||||
    BDRVDMGState *s = bs->opaque;
 | 
			
		||||
 | 
			
		||||
    if(!is_sector_in_chunk(s,s->current_chunk,sector_num)) {
 | 
			
		||||
	int ret;
 | 
			
		||||
	uint32_t chunk = search_chunk(s,sector_num);
 | 
			
		||||
@@ -224,12 +211,15 @@ static inline int dmg_read_chunk(BlockDriverState *bs, int sector_num)
 | 
			
		||||
	case 0x80000005: { /* zlib compressed */
 | 
			
		||||
	    int i;
 | 
			
		||||
 | 
			
		||||
	    ret = lseek(s->fd, s->offsets[chunk], SEEK_SET);
 | 
			
		||||
	    if(ret<0)
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	    /* we need to buffer, because only the chunk as whole can be
 | 
			
		||||
	     * inflated. */
 | 
			
		||||
	    i=0;
 | 
			
		||||
	    do {
 | 
			
		||||
                ret = bdrv_pread(bs->file, s->offsets[chunk] + i,
 | 
			
		||||
                                 s->compressed_chunk+i, s->lengths[chunk]-i);
 | 
			
		||||
		ret = read(s->fd, s->compressed_chunk+i, s->lengths[chunk]-i);
 | 
			
		||||
		if(ret<0 && errno==EINTR)
 | 
			
		||||
		    ret=0;
 | 
			
		||||
		i+=ret;
 | 
			
		||||
@@ -250,8 +240,7 @@ static inline int dmg_read_chunk(BlockDriverState *bs, int sector_num)
 | 
			
		||||
		return -1;
 | 
			
		||||
	    break; }
 | 
			
		||||
	case 1: /* copy */
 | 
			
		||||
	    ret = bdrv_pread(bs->file, s->offsets[chunk],
 | 
			
		||||
                             s->uncompressed_chunk, s->lengths[chunk]);
 | 
			
		||||
	    ret = read(s->fd, s->uncompressed_chunk, s->lengths[chunk]);
 | 
			
		||||
	    if (ret != s->lengths[chunk])
 | 
			
		||||
		return -1;
 | 
			
		||||
	    break;
 | 
			
		||||
@@ -272,7 +261,7 @@ static int dmg_read(BlockDriverState *bs, int64_t sector_num,
 | 
			
		||||
 | 
			
		||||
    for(i=0;i<nb_sectors;i++) {
 | 
			
		||||
	uint32_t sector_offset_in_chunk;
 | 
			
		||||
	if(dmg_read_chunk(bs, sector_num+i) != 0)
 | 
			
		||||
	if(dmg_read_chunk(s, sector_num+i) != 0)
 | 
			
		||||
	    return -1;
 | 
			
		||||
	sector_offset_in_chunk = sector_num+i-s->sectors[s->current_chunk];
 | 
			
		||||
	memcpy(buf+i*512,s->uncompressed_chunk+sector_offset_in_chunk*512,512);
 | 
			
		||||
@@ -283,6 +272,7 @@ static int dmg_read(BlockDriverState *bs, int64_t sector_num,
 | 
			
		||||
static void dmg_close(BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
    BDRVDMGState *s = bs->opaque;
 | 
			
		||||
    close(s->fd);
 | 
			
		||||
    if(s->n_chunks>0) {
 | 
			
		||||
	free(s->types);
 | 
			
		||||
	free(s->offsets);
 | 
			
		||||
@@ -295,18 +285,13 @@ static void dmg_close(BlockDriverState *bs)
 | 
			
		||||
    inflateEnd(&s->zstream);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static BlockDriver bdrv_dmg = {
 | 
			
		||||
    .format_name	= "dmg",
 | 
			
		||||
    .instance_size	= sizeof(BDRVDMGState),
 | 
			
		||||
    .bdrv_probe		= dmg_probe,
 | 
			
		||||
    .bdrv_open		= dmg_open,
 | 
			
		||||
    .bdrv_read		= dmg_read,
 | 
			
		||||
    .bdrv_close		= dmg_close,
 | 
			
		||||
BlockDriver bdrv_dmg = {
 | 
			
		||||
    "dmg",
 | 
			
		||||
    sizeof(BDRVDMGState),
 | 
			
		||||
    dmg_probe,
 | 
			
		||||
    dmg_open,
 | 
			
		||||
    dmg_read,
 | 
			
		||||
    NULL,
 | 
			
		||||
    dmg_close,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void bdrv_dmg_init(void)
 | 
			
		||||
{
 | 
			
		||||
    bdrv_register(&bdrv_dmg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
block_init(bdrv_dmg_init);
 | 
			
		||||
@@ -1,647 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * QEMU live block migration
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright IBM, Corp. 2009
 | 
			
		||||
 *
 | 
			
		||||
 * Authors:
 | 
			
		||||
 *  Liran Schour   <lirans@il.ibm.com>
 | 
			
		||||
 *
 | 
			
		||||
 * This work is licensed under the terms of the GNU GPL, version 2.  See
 | 
			
		||||
 * the COPYING file in the top-level directory.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "qemu-common.h"
 | 
			
		||||
#include "block_int.h"
 | 
			
		||||
#include "hw/hw.h"
 | 
			
		||||
#include "qemu-queue.h"
 | 
			
		||||
#include "qemu-timer.h"
 | 
			
		||||
#include "monitor.h"
 | 
			
		||||
#include "block-migration.h"
 | 
			
		||||
#include "migration.h"
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
 | 
			
		||||
#define BLOCK_SIZE (BDRV_SECTORS_PER_DIRTY_CHUNK << BDRV_SECTOR_BITS)
 | 
			
		||||
 | 
			
		||||
#define BLK_MIG_FLAG_DEVICE_BLOCK       0x01
 | 
			
		||||
#define BLK_MIG_FLAG_EOS                0x02
 | 
			
		||||
#define BLK_MIG_FLAG_PROGRESS           0x04
 | 
			
		||||
 | 
			
		||||
#define MAX_IS_ALLOCATED_SEARCH 65536
 | 
			
		||||
 | 
			
		||||
//#define DEBUG_BLK_MIGRATION
 | 
			
		||||
 | 
			
		||||
#ifdef DEBUG_BLK_MIGRATION
 | 
			
		||||
#define DPRINTF(fmt, ...) \
 | 
			
		||||
    do { printf("blk_migration: " fmt, ## __VA_ARGS__); } while (0)
 | 
			
		||||
#else
 | 
			
		||||
#define DPRINTF(fmt, ...) \
 | 
			
		||||
    do { } while (0)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
typedef struct BlkMigDevState {
 | 
			
		||||
    BlockDriverState *bs;
 | 
			
		||||
    int bulk_completed;
 | 
			
		||||
    int shared_base;
 | 
			
		||||
    int64_t cur_sector;
 | 
			
		||||
    int64_t cur_dirty;
 | 
			
		||||
    int64_t completed_sectors;
 | 
			
		||||
    int64_t total_sectors;
 | 
			
		||||
    int64_t dirty;
 | 
			
		||||
    QSIMPLEQ_ENTRY(BlkMigDevState) entry;
 | 
			
		||||
} BlkMigDevState;
 | 
			
		||||
 | 
			
		||||
typedef struct BlkMigBlock {
 | 
			
		||||
    uint8_t *buf;
 | 
			
		||||
    BlkMigDevState *bmds;
 | 
			
		||||
    int64_t sector;
 | 
			
		||||
    struct iovec iov;
 | 
			
		||||
    QEMUIOVector qiov;
 | 
			
		||||
    BlockDriverAIOCB *aiocb;
 | 
			
		||||
    int ret;
 | 
			
		||||
    int64_t time;
 | 
			
		||||
    QSIMPLEQ_ENTRY(BlkMigBlock) entry;
 | 
			
		||||
} BlkMigBlock;
 | 
			
		||||
 | 
			
		||||
typedef struct BlkMigState {
 | 
			
		||||
    int blk_enable;
 | 
			
		||||
    int shared_base;
 | 
			
		||||
    QSIMPLEQ_HEAD(bmds_list, BlkMigDevState) bmds_list;
 | 
			
		||||
    QSIMPLEQ_HEAD(blk_list, BlkMigBlock) blk_list;
 | 
			
		||||
    int submitted;
 | 
			
		||||
    int read_done;
 | 
			
		||||
    int transferred;
 | 
			
		||||
    int64_t total_sector_sum;
 | 
			
		||||
    int prev_progress;
 | 
			
		||||
    int bulk_completed;
 | 
			
		||||
    long double total_time;
 | 
			
		||||
    int reads;
 | 
			
		||||
} BlkMigState;
 | 
			
		||||
 | 
			
		||||
static BlkMigState block_mig_state;
 | 
			
		||||
 | 
			
		||||
static void blk_send(QEMUFile *f, BlkMigBlock * blk)
 | 
			
		||||
{
 | 
			
		||||
    int len;
 | 
			
		||||
 | 
			
		||||
    /* sector number and flags */
 | 
			
		||||
    qemu_put_be64(f, (blk->sector << BDRV_SECTOR_BITS)
 | 
			
		||||
                     | BLK_MIG_FLAG_DEVICE_BLOCK);
 | 
			
		||||
 | 
			
		||||
    /* device name */
 | 
			
		||||
    len = strlen(blk->bmds->bs->device_name);
 | 
			
		||||
    qemu_put_byte(f, len);
 | 
			
		||||
    qemu_put_buffer(f, (uint8_t *)blk->bmds->bs->device_name, len);
 | 
			
		||||
 | 
			
		||||
    qemu_put_buffer(f, blk->buf, BLOCK_SIZE);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int blk_mig_active(void)
 | 
			
		||||
{
 | 
			
		||||
    return !QSIMPLEQ_EMPTY(&block_mig_state.bmds_list);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint64_t blk_mig_bytes_transferred(void)
 | 
			
		||||
{
 | 
			
		||||
    BlkMigDevState *bmds;
 | 
			
		||||
    uint64_t sum = 0;
 | 
			
		||||
 | 
			
		||||
    QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) {
 | 
			
		||||
        sum += bmds->completed_sectors;
 | 
			
		||||
    }
 | 
			
		||||
    return sum << BDRV_SECTOR_BITS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint64_t blk_mig_bytes_remaining(void)
 | 
			
		||||
{
 | 
			
		||||
    return blk_mig_bytes_total() - blk_mig_bytes_transferred();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint64_t blk_mig_bytes_total(void)
 | 
			
		||||
{
 | 
			
		||||
    BlkMigDevState *bmds;
 | 
			
		||||
    uint64_t sum = 0;
 | 
			
		||||
 | 
			
		||||
    QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) {
 | 
			
		||||
        sum += bmds->total_sectors;
 | 
			
		||||
    }
 | 
			
		||||
    return sum << BDRV_SECTOR_BITS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void add_avg_read_time(int64_t time)
 | 
			
		||||
{
 | 
			
		||||
    block_mig_state.reads++;
 | 
			
		||||
    block_mig_state.total_time += time;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline long double compute_read_bwidth(void)
 | 
			
		||||
{
 | 
			
		||||
    assert(block_mig_state.total_time != 0);
 | 
			
		||||
    return  (block_mig_state.reads * BLOCK_SIZE)/ block_mig_state.total_time;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void blk_mig_read_cb(void *opaque, int ret)
 | 
			
		||||
{
 | 
			
		||||
    BlkMigBlock *blk = opaque;
 | 
			
		||||
 | 
			
		||||
    blk->ret = ret;
 | 
			
		||||
 | 
			
		||||
    blk->time = qemu_get_clock_ns(rt_clock) - blk->time;
 | 
			
		||||
 | 
			
		||||
    add_avg_read_time(blk->time);
 | 
			
		||||
 | 
			
		||||
    QSIMPLEQ_INSERT_TAIL(&block_mig_state.blk_list, blk, entry);
 | 
			
		||||
 | 
			
		||||
    block_mig_state.submitted--;
 | 
			
		||||
    block_mig_state.read_done++;
 | 
			
		||||
    assert(block_mig_state.submitted >= 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int mig_save_device_bulk(Monitor *mon, QEMUFile *f,
 | 
			
		||||
                                BlkMigDevState *bmds)
 | 
			
		||||
{
 | 
			
		||||
    int64_t total_sectors = bmds->total_sectors;
 | 
			
		||||
    int64_t cur_sector = bmds->cur_sector;
 | 
			
		||||
    BlockDriverState *bs = bmds->bs;
 | 
			
		||||
    BlkMigBlock *blk;
 | 
			
		||||
    int nr_sectors;
 | 
			
		||||
 | 
			
		||||
    if (bmds->shared_base) {
 | 
			
		||||
        while (cur_sector < total_sectors &&
 | 
			
		||||
               !bdrv_is_allocated(bs, cur_sector, MAX_IS_ALLOCATED_SEARCH,
 | 
			
		||||
                                  &nr_sectors)) {
 | 
			
		||||
            cur_sector += nr_sectors;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (cur_sector >= total_sectors) {
 | 
			
		||||
        bmds->cur_sector = bmds->completed_sectors = total_sectors;
 | 
			
		||||
        return 1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bmds->completed_sectors = cur_sector;
 | 
			
		||||
 | 
			
		||||
    cur_sector &= ~((int64_t)BDRV_SECTORS_PER_DIRTY_CHUNK - 1);
 | 
			
		||||
 | 
			
		||||
    /* we are going to transfer a full block even if it is not allocated */
 | 
			
		||||
    nr_sectors = BDRV_SECTORS_PER_DIRTY_CHUNK;
 | 
			
		||||
 | 
			
		||||
    if (total_sectors - cur_sector < BDRV_SECTORS_PER_DIRTY_CHUNK) {
 | 
			
		||||
        nr_sectors = total_sectors - cur_sector;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    blk = qemu_malloc(sizeof(BlkMigBlock));
 | 
			
		||||
    blk->buf = qemu_malloc(BLOCK_SIZE);
 | 
			
		||||
    blk->bmds = bmds;
 | 
			
		||||
    blk->sector = cur_sector;
 | 
			
		||||
 | 
			
		||||
    blk->iov.iov_base = blk->buf;
 | 
			
		||||
    blk->iov.iov_len = nr_sectors * BDRV_SECTOR_SIZE;
 | 
			
		||||
    qemu_iovec_init_external(&blk->qiov, &blk->iov, 1);
 | 
			
		||||
 | 
			
		||||
    blk->time = qemu_get_clock_ns(rt_clock);
 | 
			
		||||
 | 
			
		||||
    blk->aiocb = bdrv_aio_readv(bs, cur_sector, &blk->qiov,
 | 
			
		||||
                                nr_sectors, blk_mig_read_cb, blk);
 | 
			
		||||
    if (!blk->aiocb) {
 | 
			
		||||
        goto error;
 | 
			
		||||
    }
 | 
			
		||||
    block_mig_state.submitted++;
 | 
			
		||||
 | 
			
		||||
    bdrv_reset_dirty(bs, cur_sector, nr_sectors);
 | 
			
		||||
    bmds->cur_sector = cur_sector + nr_sectors;
 | 
			
		||||
 | 
			
		||||
    return (bmds->cur_sector >= total_sectors);
 | 
			
		||||
 | 
			
		||||
error:
 | 
			
		||||
    monitor_printf(mon, "Error reading sector %" PRId64 "\n", cur_sector);
 | 
			
		||||
    qemu_file_set_error(f);
 | 
			
		||||
    qemu_free(blk->buf);
 | 
			
		||||
    qemu_free(blk);
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void set_dirty_tracking(int enable)
 | 
			
		||||
{
 | 
			
		||||
    BlkMigDevState *bmds;
 | 
			
		||||
 | 
			
		||||
    QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) {
 | 
			
		||||
        bdrv_set_dirty_tracking(bmds->bs, enable);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void init_blk_migration_it(void *opaque, BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
    Monitor *mon = opaque;
 | 
			
		||||
    BlkMigDevState *bmds;
 | 
			
		||||
    int64_t sectors;
 | 
			
		||||
 | 
			
		||||
    if (!bdrv_is_read_only(bs)) {
 | 
			
		||||
        sectors = bdrv_getlength(bs) >> BDRV_SECTOR_BITS;
 | 
			
		||||
        if (sectors <= 0) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        bmds = qemu_mallocz(sizeof(BlkMigDevState));
 | 
			
		||||
        bmds->bs = bs;
 | 
			
		||||
        bmds->bulk_completed = 0;
 | 
			
		||||
        bmds->total_sectors = sectors;
 | 
			
		||||
        bmds->completed_sectors = 0;
 | 
			
		||||
        bmds->shared_base = block_mig_state.shared_base;
 | 
			
		||||
 | 
			
		||||
        block_mig_state.total_sector_sum += sectors;
 | 
			
		||||
 | 
			
		||||
        if (bmds->shared_base) {
 | 
			
		||||
            monitor_printf(mon, "Start migration for %s with shared base "
 | 
			
		||||
                                "image\n",
 | 
			
		||||
                           bs->device_name);
 | 
			
		||||
        } else {
 | 
			
		||||
            monitor_printf(mon, "Start full migration for %s\n",
 | 
			
		||||
                           bs->device_name);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        QSIMPLEQ_INSERT_TAIL(&block_mig_state.bmds_list, bmds, entry);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void init_blk_migration(Monitor *mon, QEMUFile *f)
 | 
			
		||||
{
 | 
			
		||||
    block_mig_state.submitted = 0;
 | 
			
		||||
    block_mig_state.read_done = 0;
 | 
			
		||||
    block_mig_state.transferred = 0;
 | 
			
		||||
    block_mig_state.total_sector_sum = 0;
 | 
			
		||||
    block_mig_state.prev_progress = -1;
 | 
			
		||||
    block_mig_state.bulk_completed = 0;
 | 
			
		||||
    block_mig_state.total_time = 0;
 | 
			
		||||
    block_mig_state.reads = 0;
 | 
			
		||||
 | 
			
		||||
    bdrv_iterate(init_blk_migration_it, mon);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int blk_mig_save_bulked_block(Monitor *mon, QEMUFile *f)
 | 
			
		||||
{
 | 
			
		||||
    int64_t completed_sector_sum = 0;
 | 
			
		||||
    BlkMigDevState *bmds;
 | 
			
		||||
    int progress;
 | 
			
		||||
    int ret = 0;
 | 
			
		||||
 | 
			
		||||
    QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) {
 | 
			
		||||
        if (bmds->bulk_completed == 0) {
 | 
			
		||||
            if (mig_save_device_bulk(mon, f, bmds) == 1) {
 | 
			
		||||
                /* completed bulk section for this device */
 | 
			
		||||
                bmds->bulk_completed = 1;
 | 
			
		||||
            }
 | 
			
		||||
            completed_sector_sum += bmds->completed_sectors;
 | 
			
		||||
            ret = 1;
 | 
			
		||||
            break;
 | 
			
		||||
        } else {
 | 
			
		||||
            completed_sector_sum += bmds->completed_sectors;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    progress = completed_sector_sum * 100 / block_mig_state.total_sector_sum;
 | 
			
		||||
    if (progress != block_mig_state.prev_progress) {
 | 
			
		||||
        block_mig_state.prev_progress = progress;
 | 
			
		||||
        qemu_put_be64(f, (progress << BDRV_SECTOR_BITS)
 | 
			
		||||
                         | BLK_MIG_FLAG_PROGRESS);
 | 
			
		||||
        monitor_printf(mon, "Completed %d %%\r", progress);
 | 
			
		||||
        monitor_flush(mon);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void blk_mig_reset_dirty_cursor(void)
 | 
			
		||||
{
 | 
			
		||||
    BlkMigDevState *bmds;
 | 
			
		||||
 | 
			
		||||
    QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) {
 | 
			
		||||
        bmds->cur_dirty = 0;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int mig_save_device_dirty(Monitor *mon, QEMUFile *f,
 | 
			
		||||
                                 BlkMigDevState *bmds, int is_async)
 | 
			
		||||
{
 | 
			
		||||
    BlkMigBlock *blk;
 | 
			
		||||
    int64_t total_sectors = bmds->total_sectors;
 | 
			
		||||
    int64_t sector;
 | 
			
		||||
    int nr_sectors;
 | 
			
		||||
 | 
			
		||||
    for (sector = bmds->cur_dirty; sector < bmds->total_sectors;) {
 | 
			
		||||
        if (bdrv_get_dirty(bmds->bs, sector)) {
 | 
			
		||||
 | 
			
		||||
            if (total_sectors - sector < BDRV_SECTORS_PER_DIRTY_CHUNK) {
 | 
			
		||||
                nr_sectors = total_sectors - sector;
 | 
			
		||||
            } else {
 | 
			
		||||
                nr_sectors = BDRV_SECTORS_PER_DIRTY_CHUNK;
 | 
			
		||||
            }
 | 
			
		||||
            blk = qemu_malloc(sizeof(BlkMigBlock));
 | 
			
		||||
            blk->buf = qemu_malloc(BLOCK_SIZE);
 | 
			
		||||
            blk->bmds = bmds;
 | 
			
		||||
            blk->sector = sector;
 | 
			
		||||
 | 
			
		||||
            if (is_async) {
 | 
			
		||||
                blk->iov.iov_base = blk->buf;
 | 
			
		||||
                blk->iov.iov_len = nr_sectors * BDRV_SECTOR_SIZE;
 | 
			
		||||
                qemu_iovec_init_external(&blk->qiov, &blk->iov, 1);
 | 
			
		||||
 | 
			
		||||
		blk->time = qemu_get_clock_ns(rt_clock);
 | 
			
		||||
 | 
			
		||||
                blk->aiocb = bdrv_aio_readv(bmds->bs, sector, &blk->qiov,
 | 
			
		||||
                                            nr_sectors, blk_mig_read_cb, blk);
 | 
			
		||||
                if (!blk->aiocb) {
 | 
			
		||||
                    goto error;
 | 
			
		||||
                }
 | 
			
		||||
                block_mig_state.submitted++;
 | 
			
		||||
            } else {
 | 
			
		||||
                if (bdrv_read(bmds->bs, sector, blk->buf,
 | 
			
		||||
                              nr_sectors) < 0) {
 | 
			
		||||
                    goto error;
 | 
			
		||||
                }
 | 
			
		||||
                blk_send(f, blk);
 | 
			
		||||
 | 
			
		||||
                qemu_free(blk->buf);
 | 
			
		||||
                qemu_free(blk);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            bdrv_reset_dirty(bmds->bs, sector, nr_sectors);
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        sector += BDRV_SECTORS_PER_DIRTY_CHUNK;
 | 
			
		||||
        bmds->cur_dirty = sector;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return (bmds->cur_dirty >= bmds->total_sectors);
 | 
			
		||||
 | 
			
		||||
error:
 | 
			
		||||
    monitor_printf(mon, "Error reading sector %" PRId64 "\n", sector);
 | 
			
		||||
    qemu_file_set_error(f);
 | 
			
		||||
    qemu_free(blk->buf);
 | 
			
		||||
    qemu_free(blk);
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int blk_mig_save_dirty_block(Monitor *mon, QEMUFile *f, int is_async)
 | 
			
		||||
{
 | 
			
		||||
    BlkMigDevState *bmds;
 | 
			
		||||
    int ret = 0;
 | 
			
		||||
 | 
			
		||||
    QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) {
 | 
			
		||||
        if (mig_save_device_dirty(mon, f, bmds, is_async) == 0) {
 | 
			
		||||
            ret = 1;
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void flush_blks(QEMUFile* f)
 | 
			
		||||
{
 | 
			
		||||
    BlkMigBlock *blk;
 | 
			
		||||
 | 
			
		||||
    DPRINTF("%s Enter submitted %d read_done %d transferred %d\n",
 | 
			
		||||
            __FUNCTION__, block_mig_state.submitted, block_mig_state.read_done,
 | 
			
		||||
            block_mig_state.transferred);
 | 
			
		||||
 | 
			
		||||
    while ((blk = QSIMPLEQ_FIRST(&block_mig_state.blk_list)) != NULL) {
 | 
			
		||||
        if (qemu_file_rate_limit(f)) {
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        if (blk->ret < 0) {
 | 
			
		||||
            qemu_file_set_error(f);
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        blk_send(f, blk);
 | 
			
		||||
 | 
			
		||||
        QSIMPLEQ_REMOVE_HEAD(&block_mig_state.blk_list, entry);
 | 
			
		||||
        qemu_free(blk->buf);
 | 
			
		||||
        qemu_free(blk);
 | 
			
		||||
 | 
			
		||||
        block_mig_state.read_done--;
 | 
			
		||||
        block_mig_state.transferred++;
 | 
			
		||||
        assert(block_mig_state.read_done >= 0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    DPRINTF("%s Exit submitted %d read_done %d transferred %d\n", __FUNCTION__,
 | 
			
		||||
            block_mig_state.submitted, block_mig_state.read_done,
 | 
			
		||||
            block_mig_state.transferred);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int64_t get_remaining_dirty(void)
 | 
			
		||||
{
 | 
			
		||||
    BlkMigDevState *bmds;
 | 
			
		||||
    int64_t dirty = 0;
 | 
			
		||||
 | 
			
		||||
    QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) {
 | 
			
		||||
        dirty += bdrv_get_dirty_count(bmds->bs);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return dirty * BLOCK_SIZE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int is_stage2_completed(void)
 | 
			
		||||
{
 | 
			
		||||
    int64_t remaining_dirty;
 | 
			
		||||
    long double bwidth;
 | 
			
		||||
 | 
			
		||||
    if (block_mig_state.bulk_completed == 1) {
 | 
			
		||||
 | 
			
		||||
        remaining_dirty = get_remaining_dirty();
 | 
			
		||||
	if (remaining_dirty == 0) {
 | 
			
		||||
	    return 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bwidth = compute_read_bwidth();
 | 
			
		||||
 | 
			
		||||
	if ((remaining_dirty / bwidth) <=
 | 
			
		||||
            migrate_max_downtime()) {
 | 
			
		||||
            /* finish stage2 because we think that we can finish remaing work
 | 
			
		||||
               below max_downtime */
 | 
			
		||||
 | 
			
		||||
            return 1;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void blk_mig_cleanup(Monitor *mon)
 | 
			
		||||
{
 | 
			
		||||
    BlkMigDevState *bmds;
 | 
			
		||||
    BlkMigBlock *blk;
 | 
			
		||||
 | 
			
		||||
    while ((bmds = QSIMPLEQ_FIRST(&block_mig_state.bmds_list)) != NULL) {
 | 
			
		||||
        QSIMPLEQ_REMOVE_HEAD(&block_mig_state.bmds_list, entry);
 | 
			
		||||
        qemu_free(bmds);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    while ((blk = QSIMPLEQ_FIRST(&block_mig_state.blk_list)) != NULL) {
 | 
			
		||||
        QSIMPLEQ_REMOVE_HEAD(&block_mig_state.blk_list, entry);
 | 
			
		||||
        qemu_free(blk->buf);
 | 
			
		||||
        qemu_free(blk);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    set_dirty_tracking(0);
 | 
			
		||||
 | 
			
		||||
    monitor_printf(mon, "\n");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int block_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    DPRINTF("Enter save live stage %d submitted %d transferred %d\n",
 | 
			
		||||
            stage, block_mig_state.submitted, block_mig_state.transferred);
 | 
			
		||||
 | 
			
		||||
    if (stage < 0) {
 | 
			
		||||
        blk_mig_cleanup(mon);
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (block_mig_state.blk_enable != 1) {
 | 
			
		||||
        /* no need to migrate storage */
 | 
			
		||||
        qemu_put_be64(f, BLK_MIG_FLAG_EOS);
 | 
			
		||||
        return 1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (stage == 1) {
 | 
			
		||||
        init_blk_migration(mon, f);
 | 
			
		||||
 | 
			
		||||
        /* start track dirty blocks */
 | 
			
		||||
        set_dirty_tracking(1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    flush_blks(f);
 | 
			
		||||
 | 
			
		||||
    if (qemu_file_has_error(f)) {
 | 
			
		||||
        blk_mig_cleanup(mon);
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    blk_mig_reset_dirty_cursor();
 | 
			
		||||
 | 
			
		||||
    if (stage == 2) {
 | 
			
		||||
        /* control the rate of transfer */
 | 
			
		||||
        while ((block_mig_state.submitted +
 | 
			
		||||
                block_mig_state.read_done) * BLOCK_SIZE <
 | 
			
		||||
               qemu_file_get_rate_limit(f)) {
 | 
			
		||||
            if (block_mig_state.bulk_completed == 0) {
 | 
			
		||||
                /* first finish the bulk phase */
 | 
			
		||||
                if (blk_mig_save_bulked_block(mon, f) == 0) {
 | 
			
		||||
                    /* finished saving bulk on all devices */
 | 
			
		||||
                    block_mig_state.bulk_completed = 1;
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                if (blk_mig_save_dirty_block(mon, f, 1) == 0) {
 | 
			
		||||
                    /* no more dirty blocks */
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        flush_blks(f);
 | 
			
		||||
 | 
			
		||||
        if (qemu_file_has_error(f)) {
 | 
			
		||||
            blk_mig_cleanup(mon);
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (stage == 3) {
 | 
			
		||||
        /* we know for sure that save bulk is completed and
 | 
			
		||||
           all async read completed */
 | 
			
		||||
        assert(block_mig_state.submitted == 0);
 | 
			
		||||
 | 
			
		||||
        while (blk_mig_save_dirty_block(mon, f, 0) != 0);
 | 
			
		||||
        blk_mig_cleanup(mon);
 | 
			
		||||
 | 
			
		||||
        /* report completion */
 | 
			
		||||
        qemu_put_be64(f, (100 << BDRV_SECTOR_BITS) | BLK_MIG_FLAG_PROGRESS);
 | 
			
		||||
 | 
			
		||||
        if (qemu_file_has_error(f)) {
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        monitor_printf(mon, "Block migration completed\n");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    qemu_put_be64(f, BLK_MIG_FLAG_EOS);
 | 
			
		||||
 | 
			
		||||
    return ((stage == 2) && is_stage2_completed());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int block_load(QEMUFile *f, void *opaque, int version_id)
 | 
			
		||||
{
 | 
			
		||||
    static int banner_printed;
 | 
			
		||||
    int len, flags;
 | 
			
		||||
    char device_name[256];
 | 
			
		||||
    int64_t addr;
 | 
			
		||||
    BlockDriverState *bs;
 | 
			
		||||
    uint8_t *buf;
 | 
			
		||||
 | 
			
		||||
    do {
 | 
			
		||||
        addr = qemu_get_be64(f);
 | 
			
		||||
 | 
			
		||||
        flags = addr & ~BDRV_SECTOR_MASK;
 | 
			
		||||
        addr >>= BDRV_SECTOR_BITS;
 | 
			
		||||
 | 
			
		||||
        if (flags & BLK_MIG_FLAG_DEVICE_BLOCK) {
 | 
			
		||||
            int ret;
 | 
			
		||||
            /* get device name */
 | 
			
		||||
            len = qemu_get_byte(f);
 | 
			
		||||
            qemu_get_buffer(f, (uint8_t *)device_name, len);
 | 
			
		||||
            device_name[len] = '\0';
 | 
			
		||||
 | 
			
		||||
            bs = bdrv_find(device_name);
 | 
			
		||||
            if (!bs) {
 | 
			
		||||
                fprintf(stderr, "Error unknown block device %s\n",
 | 
			
		||||
                        device_name);
 | 
			
		||||
                return -EINVAL;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            buf = qemu_malloc(BLOCK_SIZE);
 | 
			
		||||
 | 
			
		||||
            qemu_get_buffer(f, buf, BLOCK_SIZE);
 | 
			
		||||
            ret = bdrv_write(bs, addr, buf, BDRV_SECTORS_PER_DIRTY_CHUNK);
 | 
			
		||||
 | 
			
		||||
            qemu_free(buf);
 | 
			
		||||
            if (ret < 0) {
 | 
			
		||||
                return ret;
 | 
			
		||||
            }
 | 
			
		||||
        } else if (flags & BLK_MIG_FLAG_PROGRESS) {
 | 
			
		||||
            if (!banner_printed) {
 | 
			
		||||
                printf("Receiving block device images\n");
 | 
			
		||||
                banner_printed = 1;
 | 
			
		||||
            }
 | 
			
		||||
            printf("Completed %d %%%c", (int)addr,
 | 
			
		||||
                   (addr == 100) ? '\n' : '\r');
 | 
			
		||||
            fflush(stdout);
 | 
			
		||||
        } else if (!(flags & BLK_MIG_FLAG_EOS)) {
 | 
			
		||||
            fprintf(stderr, "Unknown flags\n");
 | 
			
		||||
            return -EINVAL;
 | 
			
		||||
        }
 | 
			
		||||
        if (qemu_file_has_error(f)) {
 | 
			
		||||
            return -EIO;
 | 
			
		||||
        }
 | 
			
		||||
    } while (!(flags & BLK_MIG_FLAG_EOS));
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void block_set_params(int blk_enable, int shared_base, void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    block_mig_state.blk_enable = blk_enable;
 | 
			
		||||
    block_mig_state.shared_base = shared_base;
 | 
			
		||||
 | 
			
		||||
    /* shared base means that blk_enable = 1 */
 | 
			
		||||
    block_mig_state.blk_enable |= shared_base;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void blk_mig_init(void)
 | 
			
		||||
{
 | 
			
		||||
    QSIMPLEQ_INIT(&block_mig_state.bmds_list);
 | 
			
		||||
    QSIMPLEQ_INIT(&block_mig_state.blk_list);
 | 
			
		||||
 | 
			
		||||
    register_savevm_live(NULL, "block", 0, 1, block_set_params,
 | 
			
		||||
                         block_save_live, NULL, block_load, &block_mig_state);
 | 
			
		||||
}
 | 
			
		||||
@@ -1,23 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * QEMU live block migration
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright IBM, Corp. 2009
 | 
			
		||||
 *
 | 
			
		||||
 * Authors:
 | 
			
		||||
 *  Liran Schour   <lirans@il.ibm.com>
 | 
			
		||||
 *
 | 
			
		||||
 * This work is licensed under the terms of the GNU GPL, version 2.  See
 | 
			
		||||
 * the COPYING file in the top-level directory.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef BLOCK_MIGRATION_H
 | 
			
		||||
#define BLOCK_MIGRATION_H
 | 
			
		||||
 | 
			
		||||
void blk_mig_init(void);
 | 
			
		||||
int blk_mig_active(void);
 | 
			
		||||
uint64_t blk_mig_bytes_transferred(void);
 | 
			
		||||
uint64_t blk_mig_bytes_remaining(void);
 | 
			
		||||
uint64_t blk_mig_bytes_total(void);
 | 
			
		||||
 | 
			
		||||
#endif /* BLOCK_MIGRATION_H */
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Block driver for the QCOW format
 | 
			
		||||
 * 
 | 
			
		||||
 * Copyright (c) 2004-2006 Fabrice Bellard
 | 
			
		||||
 * Copyright (c) 2004 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
 | 
			
		||||
@@ -21,9 +21,8 @@
 | 
			
		||||
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
			
		||||
 * THE SOFTWARE.
 | 
			
		||||
 */
 | 
			
		||||
#include "qemu-common.h"
 | 
			
		||||
#include "vl.h"
 | 
			
		||||
#include "block_int.h"
 | 
			
		||||
#include "module.h"
 | 
			
		||||
#include <zlib.h>
 | 
			
		||||
#include "aes.h"
 | 
			
		||||
 | 
			
		||||
@@ -54,7 +53,7 @@ typedef struct QCowHeader {
 | 
			
		||||
#define L2_CACHE_SIZE 16
 | 
			
		||||
 | 
			
		||||
typedef struct BDRVQcowState {
 | 
			
		||||
    BlockDriverState *hd;
 | 
			
		||||
    int fd;
 | 
			
		||||
    int cluster_bits;
 | 
			
		||||
    int cluster_size;
 | 
			
		||||
    int cluster_sectors;
 | 
			
		||||
@@ -76,7 +75,7 @@ typedef struct BDRVQcowState {
 | 
			
		||||
    AES_KEY aes_decrypt_key;
 | 
			
		||||
} BDRVQcowState;
 | 
			
		||||
 | 
			
		||||
static int decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset);
 | 
			
		||||
static int decompress_cluster(BDRVQcowState *s, uint64_t cluster_offset);
 | 
			
		||||
 | 
			
		||||
static int qcow_probe(const uint8_t *buf, int buf_size, const char *filename)
 | 
			
		||||
{
 | 
			
		||||
@@ -90,13 +89,20 @@ static int qcow_probe(const uint8_t *buf, int buf_size, const char *filename)
 | 
			
		||||
        return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int qcow_open(BlockDriverState *bs, int flags)
 | 
			
		||||
static int qcow_open(BlockDriverState *bs, const char *filename)
 | 
			
		||||
{
 | 
			
		||||
    BDRVQcowState *s = bs->opaque;
 | 
			
		||||
    int len, i, shift;
 | 
			
		||||
    int fd, len, i, shift;
 | 
			
		||||
    QCowHeader header;
 | 
			
		||||
    
 | 
			
		||||
    if (bdrv_pread(bs->file, 0, &header, sizeof(header)) != sizeof(header))
 | 
			
		||||
    fd = open(filename, O_RDWR | O_BINARY | O_LARGEFILE);
 | 
			
		||||
    if (fd < 0) {
 | 
			
		||||
        fd = open(filename, O_RDONLY | O_BINARY | O_LARGEFILE);
 | 
			
		||||
        if (fd < 0)
 | 
			
		||||
            return -1;
 | 
			
		||||
    }
 | 
			
		||||
    s->fd = fd;
 | 
			
		||||
    if (read(fd, &header, sizeof(header)) != sizeof(header))
 | 
			
		||||
        goto fail;
 | 
			
		||||
    be32_to_cpus(&header.magic);
 | 
			
		||||
    be32_to_cpus(&header.version);
 | 
			
		||||
@@ -132,7 +138,8 @@ static int qcow_open(BlockDriverState *bs, int flags)
 | 
			
		||||
    s->l1_table = qemu_malloc(s->l1_size * sizeof(uint64_t));
 | 
			
		||||
    if (!s->l1_table)
 | 
			
		||||
        goto fail;
 | 
			
		||||
    if (bdrv_pread(bs->file, s->l1_table_offset, s->l1_table, s->l1_size * sizeof(uint64_t)) !=
 | 
			
		||||
    lseek(fd, s->l1_table_offset, SEEK_SET);
 | 
			
		||||
    if (read(fd, s->l1_table, s->l1_size * sizeof(uint64_t)) != 
 | 
			
		||||
        s->l1_size * sizeof(uint64_t))
 | 
			
		||||
        goto fail;
 | 
			
		||||
    for(i = 0;i < s->l1_size; i++) {
 | 
			
		||||
@@ -155,7 +162,8 @@ static int qcow_open(BlockDriverState *bs, int flags)
 | 
			
		||||
        len = header.backing_file_size;
 | 
			
		||||
        if (len > 1023)
 | 
			
		||||
            len = 1023;
 | 
			
		||||
        if (bdrv_pread(bs->file, header.backing_file_offset, bs->backing_file, len) != len)
 | 
			
		||||
        lseek(fd, header.backing_file_offset, SEEK_SET);
 | 
			
		||||
        if (read(fd, bs->backing_file, len) != len)
 | 
			
		||||
            goto fail;
 | 
			
		||||
        bs->backing_file[len] = '\0';
 | 
			
		||||
    }
 | 
			
		||||
@@ -166,6 +174,7 @@ static int qcow_open(BlockDriverState *bs, int flags)
 | 
			
		||||
    qemu_free(s->l2_cache);
 | 
			
		||||
    qemu_free(s->cluster_cache);
 | 
			
		||||
    qemu_free(s->cluster_data);
 | 
			
		||||
    close(fd);
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -267,15 +276,14 @@ static uint64_t get_cluster_offset(BlockDriverState *bs,
 | 
			
		||||
        if (!allocate)
 | 
			
		||||
            return 0;
 | 
			
		||||
        /* allocate a new l2 entry */
 | 
			
		||||
        l2_offset = bdrv_getlength(bs->file);
 | 
			
		||||
        l2_offset = lseek(s->fd, 0, SEEK_END);
 | 
			
		||||
        /* round to cluster size */
 | 
			
		||||
        l2_offset = (l2_offset + s->cluster_size - 1) & ~(s->cluster_size - 1);
 | 
			
		||||
        /* update the L1 entry */
 | 
			
		||||
        s->l1_table[l1_index] = l2_offset;
 | 
			
		||||
        tmp = cpu_to_be64(l2_offset);
 | 
			
		||||
        if (bdrv_pwrite_sync(bs->file,
 | 
			
		||||
                s->l1_table_offset + l1_index * sizeof(tmp),
 | 
			
		||||
                &tmp, sizeof(tmp)) < 0)
 | 
			
		||||
        lseek(s->fd, s->l1_table_offset + l1_index * sizeof(tmp), SEEK_SET);
 | 
			
		||||
        if (write(s->fd, &tmp, sizeof(tmp)) != sizeof(tmp))
 | 
			
		||||
            return 0;
 | 
			
		||||
        new_l2_table = 1;
 | 
			
		||||
    }
 | 
			
		||||
@@ -301,13 +309,14 @@ static uint64_t get_cluster_offset(BlockDriverState *bs,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    l2_table = s->l2_cache + (min_index << s->l2_bits);
 | 
			
		||||
    lseek(s->fd, l2_offset, SEEK_SET);
 | 
			
		||||
    if (new_l2_table) {
 | 
			
		||||
        memset(l2_table, 0, s->l2_size * sizeof(uint64_t));
 | 
			
		||||
        if (bdrv_pwrite_sync(bs->file, l2_offset, l2_table,
 | 
			
		||||
                s->l2_size * sizeof(uint64_t)) < 0)
 | 
			
		||||
        if (write(s->fd, l2_table, s->l2_size * sizeof(uint64_t)) !=
 | 
			
		||||
            s->l2_size * sizeof(uint64_t))
 | 
			
		||||
            return 0;
 | 
			
		||||
    } else {
 | 
			
		||||
        if (bdrv_pread(bs->file, l2_offset, l2_table, s->l2_size * sizeof(uint64_t)) !=
 | 
			
		||||
        if (read(s->fd, l2_table, s->l2_size * sizeof(uint64_t)) != 
 | 
			
		||||
            s->l2_size * sizeof(uint64_t))
 | 
			
		||||
            return 0;
 | 
			
		||||
    }
 | 
			
		||||
@@ -326,42 +335,43 @@ static uint64_t get_cluster_offset(BlockDriverState *bs,
 | 
			
		||||
            /* if the cluster is already compressed, we must
 | 
			
		||||
               decompress it in the case it is not completely
 | 
			
		||||
               overwritten */
 | 
			
		||||
            if (decompress_cluster(bs, cluster_offset) < 0)
 | 
			
		||||
            if (decompress_cluster(s, cluster_offset) < 0)
 | 
			
		||||
                return 0;
 | 
			
		||||
            cluster_offset = bdrv_getlength(bs->file);
 | 
			
		||||
            cluster_offset = lseek(s->fd, 0, SEEK_END);
 | 
			
		||||
            cluster_offset = (cluster_offset + s->cluster_size - 1) & 
 | 
			
		||||
                ~(s->cluster_size - 1);
 | 
			
		||||
            /* write the cluster content */
 | 
			
		||||
            if (bdrv_pwrite(bs->file, cluster_offset, s->cluster_cache, s->cluster_size) !=
 | 
			
		||||
            lseek(s->fd, cluster_offset, SEEK_SET);
 | 
			
		||||
            if (write(s->fd, s->cluster_cache, s->cluster_size) != 
 | 
			
		||||
                s->cluster_size)
 | 
			
		||||
                return -1;
 | 
			
		||||
        } else {
 | 
			
		||||
            cluster_offset = bdrv_getlength(bs->file);
 | 
			
		||||
            cluster_offset = lseek(s->fd, 0, SEEK_END);
 | 
			
		||||
            if (allocate == 1) {
 | 
			
		||||
                /* round to cluster size */
 | 
			
		||||
                cluster_offset = (cluster_offset + s->cluster_size - 1) & 
 | 
			
		||||
                    ~(s->cluster_size - 1);
 | 
			
		||||
                bdrv_truncate(bs->file, cluster_offset + s->cluster_size);
 | 
			
		||||
                ftruncate(s->fd, cluster_offset + s->cluster_size);
 | 
			
		||||
                /* if encrypted, we must initialize the cluster
 | 
			
		||||
                   content which won't be written */
 | 
			
		||||
                if (s->crypt_method && 
 | 
			
		||||
                    (n_end - n_start) < s->cluster_sectors) {
 | 
			
		||||
                    uint64_t start_sect;
 | 
			
		||||
                    start_sect = (offset & ~(s->cluster_size - 1)) >> 9;
 | 
			
		||||
                    memset(s->cluster_data + 512, 0x00, 512);
 | 
			
		||||
                    memset(s->cluster_data + 512, 0xaa, 512);
 | 
			
		||||
                    for(i = 0; i < s->cluster_sectors; i++) {
 | 
			
		||||
                        if (i < n_start || i >= n_end) {
 | 
			
		||||
                            encrypt_sectors(s, start_sect + i, 
 | 
			
		||||
                                            s->cluster_data, 
 | 
			
		||||
                                            s->cluster_data + 512, 1, 1,
 | 
			
		||||
                                            &s->aes_encrypt_key);
 | 
			
		||||
                            if (bdrv_pwrite(bs->file, cluster_offset + i * 512,
 | 
			
		||||
                                            s->cluster_data, 512) != 512)
 | 
			
		||||
                            lseek(s->fd, cluster_offset + i * 512, SEEK_SET);
 | 
			
		||||
                            if (write(s->fd, s->cluster_data, 512) != 512)
 | 
			
		||||
                                return -1;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            } else if (allocate == 2) {
 | 
			
		||||
            } else {
 | 
			
		||||
                cluster_offset |= QCOW_OFLAG_COMPRESSED | 
 | 
			
		||||
                    (uint64_t)compressed_size << (63 - s->cluster_bits);
 | 
			
		||||
            }
 | 
			
		||||
@@ -369,8 +379,8 @@ static uint64_t get_cluster_offset(BlockDriverState *bs,
 | 
			
		||||
        /* update L2 table */
 | 
			
		||||
        tmp = cpu_to_be64(cluster_offset);
 | 
			
		||||
        l2_table[l2_index] = tmp;
 | 
			
		||||
        if (bdrv_pwrite_sync(bs->file, l2_offset + l2_index * sizeof(tmp),
 | 
			
		||||
                &tmp, sizeof(tmp)) < 0)
 | 
			
		||||
        lseek(s->fd, l2_offset + l2_index * sizeof(tmp), SEEK_SET);
 | 
			
		||||
        if (write(s->fd, &tmp, sizeof(tmp)) != sizeof(tmp))
 | 
			
		||||
            return 0;
 | 
			
		||||
    }
 | 
			
		||||
    return cluster_offset;
 | 
			
		||||
@@ -419,9 +429,8 @@ static int decompress_buffer(uint8_t *out_buf, int out_buf_size,
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
                              
 | 
			
		||||
static int decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset)
 | 
			
		||||
static int decompress_cluster(BDRVQcowState *s, uint64_t cluster_offset)
 | 
			
		||||
{
 | 
			
		||||
    BDRVQcowState *s = bs->opaque;
 | 
			
		||||
    int ret, csize;
 | 
			
		||||
    uint64_t coffset;
 | 
			
		||||
 | 
			
		||||
@@ -429,7 +438,8 @@ static int decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset)
 | 
			
		||||
    if (s->cluster_cache_offset != coffset) {
 | 
			
		||||
        csize = cluster_offset >> (63 - s->cluster_bits);
 | 
			
		||||
        csize &= (s->cluster_size - 1);
 | 
			
		||||
        ret = bdrv_pread(bs->file, coffset, s->cluster_data, csize);
 | 
			
		||||
        lseek(s->fd, coffset, SEEK_SET);
 | 
			
		||||
        ret = read(s->fd, s->cluster_data, csize);
 | 
			
		||||
        if (ret != csize) 
 | 
			
		||||
            return -1;
 | 
			
		||||
        if (decompress_buffer(s->cluster_cache, s->cluster_size,
 | 
			
		||||
@@ -441,8 +451,6 @@ static int decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset)
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#if 0
 | 
			
		||||
 | 
			
		||||
static int qcow_read(BlockDriverState *bs, int64_t sector_num, 
 | 
			
		||||
                     uint8_t *buf, int nb_sectors)
 | 
			
		||||
{
 | 
			
		||||
@@ -457,20 +465,14 @@ static int qcow_read(BlockDriverState *bs, int64_t sector_num,
 | 
			
		||||
        if (n > nb_sectors)
 | 
			
		||||
            n = nb_sectors;
 | 
			
		||||
        if (!cluster_offset) {
 | 
			
		||||
            if (bs->backing_hd) {
 | 
			
		||||
                /* read from the base image */
 | 
			
		||||
                ret = bdrv_read(bs->backing_hd, sector_num, buf, n);
 | 
			
		||||
                if (ret < 0)
 | 
			
		||||
                    return -1;
 | 
			
		||||
            } else {
 | 
			
		||||
            memset(buf, 0, 512 * n);
 | 
			
		||||
            }
 | 
			
		||||
        } else if (cluster_offset & QCOW_OFLAG_COMPRESSED) {
 | 
			
		||||
            if (decompress_cluster(bs, cluster_offset) < 0)
 | 
			
		||||
            if (decompress_cluster(s, cluster_offset) < 0)
 | 
			
		||||
                return -1;
 | 
			
		||||
            memcpy(buf, s->cluster_cache + index_in_cluster * 512, 512 * n);
 | 
			
		||||
        } else {
 | 
			
		||||
            ret = bdrv_pread(bs->file, cluster_offset + index_in_cluster * 512, buf, n * 512);
 | 
			
		||||
            lseek(s->fd, cluster_offset + index_in_cluster * 512, SEEK_SET);
 | 
			
		||||
            ret = read(s->fd, buf, n * 512);
 | 
			
		||||
            if (ret != n * 512) 
 | 
			
		||||
                return -1;
 | 
			
		||||
            if (s->crypt_method) {
 | 
			
		||||
@@ -484,250 +486,40 @@ static int qcow_read(BlockDriverState *bs, int64_t sector_num,
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
typedef struct QCowAIOCB {
 | 
			
		||||
    BlockDriverAIOCB common;
 | 
			
		||||
    int64_t sector_num;
 | 
			
		||||
    QEMUIOVector *qiov;
 | 
			
		||||
    uint8_t *buf;
 | 
			
		||||
    void *orig_buf;
 | 
			
		||||
    int nb_sectors;
 | 
			
		||||
    int n;
 | 
			
		||||
    uint64_t cluster_offset;
 | 
			
		||||
    uint8_t *cluster_data;
 | 
			
		||||
    struct iovec hd_iov;
 | 
			
		||||
    QEMUIOVector hd_qiov;
 | 
			
		||||
    BlockDriverAIOCB *hd_aiocb;
 | 
			
		||||
} QCowAIOCB;
 | 
			
		||||
 | 
			
		||||
static void qcow_aio_cancel(BlockDriverAIOCB *blockacb)
 | 
			
		||||
static int qcow_write(BlockDriverState *bs, int64_t sector_num, 
 | 
			
		||||
                     const uint8_t *buf, int nb_sectors)
 | 
			
		||||
{
 | 
			
		||||
    QCowAIOCB *acb = container_of(blockacb, QCowAIOCB, common);
 | 
			
		||||
    if (acb->hd_aiocb)
 | 
			
		||||
        bdrv_aio_cancel(acb->hd_aiocb);
 | 
			
		||||
    qemu_aio_release(acb);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static AIOPool qcow_aio_pool = {
 | 
			
		||||
    .aiocb_size         = sizeof(QCowAIOCB),
 | 
			
		||||
    .cancel             = qcow_aio_cancel,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static QCowAIOCB *qcow_aio_setup(BlockDriverState *bs,
 | 
			
		||||
        int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
 | 
			
		||||
        BlockDriverCompletionFunc *cb, void *opaque, int is_write)
 | 
			
		||||
{
 | 
			
		||||
    QCowAIOCB *acb;
 | 
			
		||||
 | 
			
		||||
    acb = qemu_aio_get(&qcow_aio_pool, bs, cb, opaque);
 | 
			
		||||
    if (!acb)
 | 
			
		||||
        return NULL;
 | 
			
		||||
    acb->hd_aiocb = NULL;
 | 
			
		||||
    acb->sector_num = sector_num;
 | 
			
		||||
    acb->qiov = qiov;
 | 
			
		||||
    if (qiov->niov > 1) {
 | 
			
		||||
        acb->buf = acb->orig_buf = qemu_blockalign(bs, qiov->size);
 | 
			
		||||
        if (is_write)
 | 
			
		||||
            qemu_iovec_to_buffer(qiov, acb->buf);
 | 
			
		||||
    } else {
 | 
			
		||||
        acb->buf = (uint8_t *)qiov->iov->iov_base;
 | 
			
		||||
    }
 | 
			
		||||
    acb->nb_sectors = nb_sectors;
 | 
			
		||||
    acb->n = 0;
 | 
			
		||||
    acb->cluster_offset = 0;
 | 
			
		||||
    return acb;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void qcow_aio_read_cb(void *opaque, int ret)
 | 
			
		||||
{
 | 
			
		||||
    QCowAIOCB *acb = opaque;
 | 
			
		||||
    BlockDriverState *bs = acb->common.bs;
 | 
			
		||||
    BDRVQcowState *s = bs->opaque;
 | 
			
		||||
    int index_in_cluster;
 | 
			
		||||
 | 
			
		||||
    acb->hd_aiocb = NULL;
 | 
			
		||||
    if (ret < 0)
 | 
			
		||||
        goto done;
 | 
			
		||||
 | 
			
		||||
 redo:
 | 
			
		||||
    /* post process the read buffer */
 | 
			
		||||
    if (!acb->cluster_offset) {
 | 
			
		||||
        /* nothing to do */
 | 
			
		||||
    } else if (acb->cluster_offset & QCOW_OFLAG_COMPRESSED) {
 | 
			
		||||
        /* nothing to do */
 | 
			
		||||
    } else {
 | 
			
		||||
        if (s->crypt_method) {
 | 
			
		||||
            encrypt_sectors(s, acb->sector_num, acb->buf, acb->buf,
 | 
			
		||||
                            acb->n, 0,
 | 
			
		||||
                            &s->aes_decrypt_key);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    acb->nb_sectors -= acb->n;
 | 
			
		||||
    acb->sector_num += acb->n;
 | 
			
		||||
    acb->buf += acb->n * 512;
 | 
			
		||||
 | 
			
		||||
    if (acb->nb_sectors == 0) {
 | 
			
		||||
        /* request completed */
 | 
			
		||||
        ret = 0;
 | 
			
		||||
        goto done;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* prepare next AIO request */
 | 
			
		||||
    acb->cluster_offset = get_cluster_offset(bs, acb->sector_num << 9,
 | 
			
		||||
                                             0, 0, 0, 0);
 | 
			
		||||
    index_in_cluster = acb->sector_num & (s->cluster_sectors - 1);
 | 
			
		||||
    acb->n = s->cluster_sectors - index_in_cluster;
 | 
			
		||||
    if (acb->n > acb->nb_sectors)
 | 
			
		||||
        acb->n = acb->nb_sectors;
 | 
			
		||||
 | 
			
		||||
    if (!acb->cluster_offset) {
 | 
			
		||||
        if (bs->backing_hd) {
 | 
			
		||||
            /* read from the base image */
 | 
			
		||||
            acb->hd_iov.iov_base = (void *)acb->buf;
 | 
			
		||||
            acb->hd_iov.iov_len = acb->n * 512;
 | 
			
		||||
            qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1);
 | 
			
		||||
            acb->hd_aiocb = bdrv_aio_readv(bs->backing_hd, acb->sector_num,
 | 
			
		||||
                &acb->hd_qiov, acb->n, qcow_aio_read_cb, acb);
 | 
			
		||||
            if (acb->hd_aiocb == NULL)
 | 
			
		||||
                goto done;
 | 
			
		||||
        } else {
 | 
			
		||||
            /* Note: in this case, no need to wait */
 | 
			
		||||
            memset(acb->buf, 0, 512 * acb->n);
 | 
			
		||||
            goto redo;
 | 
			
		||||
        }
 | 
			
		||||
    } else if (acb->cluster_offset & QCOW_OFLAG_COMPRESSED) {
 | 
			
		||||
        /* add AIO support for compressed blocks ? */
 | 
			
		||||
        if (decompress_cluster(bs, acb->cluster_offset) < 0)
 | 
			
		||||
            goto done;
 | 
			
		||||
        memcpy(acb->buf,
 | 
			
		||||
               s->cluster_cache + index_in_cluster * 512, 512 * acb->n);
 | 
			
		||||
        goto redo;
 | 
			
		||||
    } else {
 | 
			
		||||
        if ((acb->cluster_offset & 511) != 0) {
 | 
			
		||||
            ret = -EIO;
 | 
			
		||||
            goto done;
 | 
			
		||||
        }
 | 
			
		||||
        acb->hd_iov.iov_base = (void *)acb->buf;
 | 
			
		||||
        acb->hd_iov.iov_len = acb->n * 512;
 | 
			
		||||
        qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1);
 | 
			
		||||
        acb->hd_aiocb = bdrv_aio_readv(bs->file,
 | 
			
		||||
                            (acb->cluster_offset >> 9) + index_in_cluster,
 | 
			
		||||
                            &acb->hd_qiov, acb->n, qcow_aio_read_cb, acb);
 | 
			
		||||
        if (acb->hd_aiocb == NULL)
 | 
			
		||||
            goto done;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
done:
 | 
			
		||||
    if (acb->qiov->niov > 1) {
 | 
			
		||||
        qemu_iovec_from_buffer(acb->qiov, acb->orig_buf, acb->qiov->size);
 | 
			
		||||
        qemu_vfree(acb->orig_buf);
 | 
			
		||||
    }
 | 
			
		||||
    acb->common.cb(acb->common.opaque, ret);
 | 
			
		||||
    qemu_aio_release(acb);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static BlockDriverAIOCB *qcow_aio_readv(BlockDriverState *bs,
 | 
			
		||||
        int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
 | 
			
		||||
        BlockDriverCompletionFunc *cb, void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    QCowAIOCB *acb;
 | 
			
		||||
 | 
			
		||||
    acb = qcow_aio_setup(bs, sector_num, qiov, nb_sectors, cb, opaque, 0);
 | 
			
		||||
    if (!acb)
 | 
			
		||||
        return NULL;
 | 
			
		||||
 | 
			
		||||
    qcow_aio_read_cb(acb, 0);
 | 
			
		||||
    return &acb->common;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void qcow_aio_write_cb(void *opaque, int ret)
 | 
			
		||||
{
 | 
			
		||||
    QCowAIOCB *acb = opaque;
 | 
			
		||||
    BlockDriverState *bs = acb->common.bs;
 | 
			
		||||
    BDRVQcowState *s = bs->opaque;
 | 
			
		||||
    int index_in_cluster;
 | 
			
		||||
    int ret, index_in_cluster, n;
 | 
			
		||||
    uint64_t cluster_offset;
 | 
			
		||||
    const uint8_t *src_buf;
 | 
			
		||||
    
 | 
			
		||||
    acb->hd_aiocb = NULL;
 | 
			
		||||
 | 
			
		||||
    if (ret < 0)
 | 
			
		||||
        goto done;
 | 
			
		||||
 | 
			
		||||
    acb->nb_sectors -= acb->n;
 | 
			
		||||
    acb->sector_num += acb->n;
 | 
			
		||||
    acb->buf += acb->n * 512;
 | 
			
		||||
 | 
			
		||||
    if (acb->nb_sectors == 0) {
 | 
			
		||||
        /* request completed */
 | 
			
		||||
        ret = 0;
 | 
			
		||||
        goto done;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    index_in_cluster = acb->sector_num & (s->cluster_sectors - 1);
 | 
			
		||||
    acb->n = s->cluster_sectors - index_in_cluster;
 | 
			
		||||
    if (acb->n > acb->nb_sectors)
 | 
			
		||||
        acb->n = acb->nb_sectors;
 | 
			
		||||
    cluster_offset = get_cluster_offset(bs, acb->sector_num << 9, 1, 0,
 | 
			
		||||
    while (nb_sectors > 0) {
 | 
			
		||||
        index_in_cluster = sector_num & (s->cluster_sectors - 1);
 | 
			
		||||
        n = s->cluster_sectors - index_in_cluster;
 | 
			
		||||
        if (n > nb_sectors)
 | 
			
		||||
            n = nb_sectors;
 | 
			
		||||
        cluster_offset = get_cluster_offset(bs, sector_num << 9, 1, 0, 
 | 
			
		||||
                                            index_in_cluster, 
 | 
			
		||||
                                        index_in_cluster + acb->n);
 | 
			
		||||
    if (!cluster_offset || (cluster_offset & 511) != 0) {
 | 
			
		||||
        ret = -EIO;
 | 
			
		||||
        goto done;
 | 
			
		||||
    }
 | 
			
		||||
                                            index_in_cluster + n);
 | 
			
		||||
        if (!cluster_offset)
 | 
			
		||||
            return -1;
 | 
			
		||||
        lseek(s->fd, cluster_offset + index_in_cluster * 512, SEEK_SET);
 | 
			
		||||
        if (s->crypt_method) {
 | 
			
		||||
        if (!acb->cluster_data) {
 | 
			
		||||
            acb->cluster_data = qemu_mallocz(s->cluster_size);
 | 
			
		||||
            if (!acb->cluster_data) {
 | 
			
		||||
                ret = -ENOMEM;
 | 
			
		||||
                goto done;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        encrypt_sectors(s, acb->sector_num, acb->cluster_data, acb->buf,
 | 
			
		||||
                        acb->n, 1, &s->aes_encrypt_key);
 | 
			
		||||
        src_buf = acb->cluster_data;
 | 
			
		||||
            encrypt_sectors(s, sector_num, s->cluster_data, buf, n, 1,
 | 
			
		||||
                            &s->aes_encrypt_key);
 | 
			
		||||
            ret = write(s->fd, s->cluster_data, n * 512);
 | 
			
		||||
        } else {
 | 
			
		||||
        src_buf = acb->buf;
 | 
			
		||||
            ret = write(s->fd, buf, n * 512);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    acb->hd_iov.iov_base = (void *)src_buf;
 | 
			
		||||
    acb->hd_iov.iov_len = acb->n * 512;
 | 
			
		||||
    qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1);
 | 
			
		||||
    acb->hd_aiocb = bdrv_aio_writev(bs->file,
 | 
			
		||||
                                    (cluster_offset >> 9) + index_in_cluster,
 | 
			
		||||
                                    &acb->hd_qiov, acb->n,
 | 
			
		||||
                                    qcow_aio_write_cb, acb);
 | 
			
		||||
    if (acb->hd_aiocb == NULL)
 | 
			
		||||
        goto done;
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
done:
 | 
			
		||||
    if (acb->qiov->niov > 1)
 | 
			
		||||
        qemu_vfree(acb->orig_buf);
 | 
			
		||||
    acb->common.cb(acb->common.opaque, ret);
 | 
			
		||||
    qemu_aio_release(acb);
 | 
			
		||||
        if (ret != n * 512) 
 | 
			
		||||
            return -1;
 | 
			
		||||
        nb_sectors -= n;
 | 
			
		||||
        sector_num += n;
 | 
			
		||||
        buf += n * 512;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
static BlockDriverAIOCB *qcow_aio_writev(BlockDriverState *bs,
 | 
			
		||||
        int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
 | 
			
		||||
        BlockDriverCompletionFunc *cb, void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    BDRVQcowState *s = bs->opaque;
 | 
			
		||||
    QCowAIOCB *acb;
 | 
			
		||||
 | 
			
		||||
    s->cluster_cache_offset = -1; /* disable compressed cache */
 | 
			
		||||
 | 
			
		||||
    acb = qcow_aio_setup(bs, sector_num, qiov, nb_sectors, cb, opaque, 1);
 | 
			
		||||
    if (!acb)
 | 
			
		||||
        return NULL;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    qcow_aio_write_cb(acb, 0);
 | 
			
		||||
    return &acb->common;
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void qcow_close(BlockDriverState *bs)
 | 
			
		||||
@@ -737,33 +529,22 @@ static void qcow_close(BlockDriverState *bs)
 | 
			
		||||
    qemu_free(s->l2_cache);
 | 
			
		||||
    qemu_free(s->cluster_cache);
 | 
			
		||||
    qemu_free(s->cluster_data);
 | 
			
		||||
    close(s->fd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int qcow_create(const char *filename, QEMUOptionParameter *options)
 | 
			
		||||
static int qcow_create(const char *filename, int64_t total_size,
 | 
			
		||||
                      const char *backing_file, int flags)
 | 
			
		||||
{
 | 
			
		||||
    int fd, header_size, backing_filename_len, l1_size, i, shift;
 | 
			
		||||
    QCowHeader header;
 | 
			
		||||
    char backing_filename[1024];
 | 
			
		||||
    uint64_t tmp;
 | 
			
		||||
    int64_t total_size = 0;
 | 
			
		||||
    const char *backing_file = NULL;
 | 
			
		||||
    int flags = 0;
 | 
			
		||||
    int ret;
 | 
			
		||||
    struct stat st;
 | 
			
		||||
 | 
			
		||||
    /* Read out options */
 | 
			
		||||
    while (options && options->name) {
 | 
			
		||||
        if (!strcmp(options->name, BLOCK_OPT_SIZE)) {
 | 
			
		||||
            total_size = options->value.n / 512;
 | 
			
		||||
        } else if (!strcmp(options->name, BLOCK_OPT_BACKING_FILE)) {
 | 
			
		||||
            backing_file = options->value.s;
 | 
			
		||||
        } else if (!strcmp(options->name, BLOCK_OPT_ENCRYPT)) {
 | 
			
		||||
            flags |= options->value.n ? BLOCK_FLAG_ENCRYPT : 0;
 | 
			
		||||
        }
 | 
			
		||||
        options++;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
 | 
			
		||||
    fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE, 
 | 
			
		||||
              0644);
 | 
			
		||||
    if (fd < 0)
 | 
			
		||||
        return -errno;
 | 
			
		||||
        return -1;
 | 
			
		||||
    memset(&header, 0, sizeof(header));
 | 
			
		||||
    header.magic = cpu_to_be32(QCOW_MAGIC);
 | 
			
		||||
    header.version = cpu_to_be32(QCOW_VERSION);
 | 
			
		||||
@@ -772,14 +553,27 @@ static int qcow_create(const char *filename, QEMUOptionParameter *options)
 | 
			
		||||
    backing_filename_len = 0;
 | 
			
		||||
    if (backing_file) {
 | 
			
		||||
	if (strcmp(backing_file, "fat:")) {
 | 
			
		||||
	    const char *p;
 | 
			
		||||
	    /* XXX: this is a hack: we do not attempt to check for URL
 | 
			
		||||
	       like syntax */
 | 
			
		||||
	    p = strchr(backing_file, ':');
 | 
			
		||||
	    if (p && (p - backing_file) >= 2) {
 | 
			
		||||
		/* URL like but exclude "c:" like filenames */
 | 
			
		||||
		pstrcpy(backing_filename, sizeof(backing_filename),
 | 
			
		||||
			backing_file);
 | 
			
		||||
	    } else {
 | 
			
		||||
		realpath(backing_file, backing_filename);
 | 
			
		||||
		if (stat(backing_filename, &st) != 0) {
 | 
			
		||||
		    return -1;
 | 
			
		||||
		}
 | 
			
		||||
	    }
 | 
			
		||||
	    header.backing_file_offset = cpu_to_be64(header_size);
 | 
			
		||||
            backing_filename_len = strlen(backing_file);
 | 
			
		||||
	    backing_filename_len = strlen(backing_filename);
 | 
			
		||||
	    header.backing_file_size = cpu_to_be32(backing_filename_len);
 | 
			
		||||
	    header_size += backing_filename_len;
 | 
			
		||||
        } else {
 | 
			
		||||
            /* special backing file for vvfat */
 | 
			
		||||
	} else
 | 
			
		||||
	    backing_file = NULL;
 | 
			
		||||
        }
 | 
			
		||||
        header.mtime = cpu_to_be32(st.st_mtime);
 | 
			
		||||
        header.cluster_bits = 9; /* 512 byte cluster to avoid copying
 | 
			
		||||
                                    unmodifyed sectors */
 | 
			
		||||
        header.l2_bits = 12; /* 32 KB L2 tables */
 | 
			
		||||
@@ -792,56 +586,36 @@ static int qcow_create(const char *filename, QEMUOptionParameter *options)
 | 
			
		||||
    l1_size = ((total_size * 512) + (1LL << shift) - 1) >> shift;
 | 
			
		||||
 | 
			
		||||
    header.l1_table_offset = cpu_to_be64(header_size);
 | 
			
		||||
    if (flags & BLOCK_FLAG_ENCRYPT) {
 | 
			
		||||
    if (flags) {
 | 
			
		||||
        header.crypt_method = cpu_to_be32(QCOW_CRYPT_AES);
 | 
			
		||||
    } else {
 | 
			
		||||
        header.crypt_method = cpu_to_be32(QCOW_CRYPT_NONE);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /* write all the data */
 | 
			
		||||
    ret = qemu_write_full(fd, &header, sizeof(header));
 | 
			
		||||
    if (ret != sizeof(header)) {
 | 
			
		||||
        ret = -errno;
 | 
			
		||||
        goto exit;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    write(fd, &header, sizeof(header));
 | 
			
		||||
    if (backing_file) {
 | 
			
		||||
        ret = qemu_write_full(fd, backing_file, backing_filename_len);
 | 
			
		||||
        if (ret != backing_filename_len) {
 | 
			
		||||
            ret = -errno;
 | 
			
		||||
            goto exit;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        write(fd, backing_filename, backing_filename_len);
 | 
			
		||||
    }
 | 
			
		||||
    lseek(fd, header_size, SEEK_SET);
 | 
			
		||||
    tmp = 0;
 | 
			
		||||
    for(i = 0;i < l1_size; i++) {
 | 
			
		||||
        ret = qemu_write_full(fd, &tmp, sizeof(tmp));
 | 
			
		||||
        if (ret != sizeof(tmp)) {
 | 
			
		||||
            ret = -errno;
 | 
			
		||||
            goto exit;
 | 
			
		||||
        write(fd, &tmp, sizeof(tmp));
 | 
			
		||||
    }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ret = 0;
 | 
			
		||||
exit:
 | 
			
		||||
    close(fd);
 | 
			
		||||
    return ret;
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int qcow_make_empty(BlockDriverState *bs)
 | 
			
		||||
int qcow_make_empty(BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
    BDRVQcowState *s = bs->opaque;
 | 
			
		||||
    uint32_t l1_length = s->l1_size * sizeof(uint64_t);
 | 
			
		||||
    int ret;
 | 
			
		||||
 | 
			
		||||
    memset(s->l1_table, 0, l1_length);
 | 
			
		||||
    if (bdrv_pwrite_sync(bs->file, s->l1_table_offset, s->l1_table,
 | 
			
		||||
            l1_length) < 0)
 | 
			
		||||
    lseek(s->fd, s->l1_table_offset, SEEK_SET);
 | 
			
		||||
    if (write(s->fd, s->l1_table, l1_length) < 0)
 | 
			
		||||
	return -1;
 | 
			
		||||
    ret = bdrv_truncate(bs->file, s->l1_table_offset + l1_length);
 | 
			
		||||
    if (ret < 0)
 | 
			
		||||
        return ret;
 | 
			
		||||
    ftruncate(s->fd, s->l1_table_offset + l1_length);
 | 
			
		||||
 | 
			
		||||
    memset(s->l2_cache, 0, s->l2_size * L2_CACHE_SIZE * sizeof(uint64_t));
 | 
			
		||||
    memset(s->l2_cache_offsets, 0, L2_CACHE_SIZE * sizeof(uint64_t));
 | 
			
		||||
@@ -850,10 +624,18 @@ static int qcow_make_empty(BlockDriverState *bs)
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int qcow_get_cluster_size(BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
    BDRVQcowState *s = bs->opaque;
 | 
			
		||||
    if (bs->drv != &bdrv_qcow)
 | 
			
		||||
        return -1;
 | 
			
		||||
    return s->cluster_size;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* XXX: put compressed sectors first, then all the cluster aligned
 | 
			
		||||
   tables to avoid losing bytes in alignment */
 | 
			
		||||
static int qcow_write_compressed(BlockDriverState *bs, int64_t sector_num,
 | 
			
		||||
                                 const uint8_t *buf, int nb_sectors)
 | 
			
		||||
int qcow_compress_cluster(BlockDriverState *bs, int64_t sector_num, 
 | 
			
		||||
                          const uint8_t *buf)
 | 
			
		||||
{
 | 
			
		||||
    BDRVQcowState *s = bs->opaque;
 | 
			
		||||
    z_stream strm;
 | 
			
		||||
@@ -861,8 +643,8 @@ static int qcow_write_compressed(BlockDriverState *bs, int64_t sector_num,
 | 
			
		||||
    uint8_t *out_buf;
 | 
			
		||||
    uint64_t cluster_offset;
 | 
			
		||||
 | 
			
		||||
    if (nb_sectors != s->cluster_sectors)
 | 
			
		||||
        return -EINVAL;
 | 
			
		||||
    if (bs->drv != &bdrv_qcow)
 | 
			
		||||
        return -1;
 | 
			
		||||
 | 
			
		||||
    out_buf = qemu_malloc(s->cluster_size + (s->cluster_size / 1000) + 128);
 | 
			
		||||
    if (!out_buf)
 | 
			
		||||
@@ -895,12 +677,13 @@ static int qcow_write_compressed(BlockDriverState *bs, int64_t sector_num,
 | 
			
		||||
 | 
			
		||||
    if (ret != Z_STREAM_END || out_len >= s->cluster_size) {
 | 
			
		||||
        /* could not compress: write normal cluster */
 | 
			
		||||
        bdrv_write(bs, sector_num, buf, s->cluster_sectors);
 | 
			
		||||
        qcow_write(bs, sector_num, buf, s->cluster_sectors);
 | 
			
		||||
    } else {
 | 
			
		||||
        cluster_offset = get_cluster_offset(bs, sector_num << 9, 2, 
 | 
			
		||||
                                            out_len, 0, 0);
 | 
			
		||||
        cluster_offset &= s->cluster_offset_mask;
 | 
			
		||||
        if (bdrv_pwrite(bs->file, cluster_offset, out_buf, out_len) != out_len) {
 | 
			
		||||
        lseek(s->fd, cluster_offset, SEEK_SET);
 | 
			
		||||
        if (write(s->fd, out_buf, out_len) != out_len) {
 | 
			
		||||
            qemu_free(out_buf);
 | 
			
		||||
            return -1;
 | 
			
		||||
        }
 | 
			
		||||
@@ -910,67 +693,18 @@ static int qcow_write_compressed(BlockDriverState *bs, int64_t sector_num,
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void qcow_flush(BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
    bdrv_flush(bs->file);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static BlockDriverAIOCB *qcow_aio_flush(BlockDriverState *bs,
 | 
			
		||||
        BlockDriverCompletionFunc *cb, void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    return bdrv_aio_flush(bs->file, cb, opaque);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int qcow_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
 | 
			
		||||
{
 | 
			
		||||
    BDRVQcowState *s = bs->opaque;
 | 
			
		||||
    bdi->cluster_size = s->cluster_size;
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static QEMUOptionParameter qcow_create_options[] = {
 | 
			
		||||
    {
 | 
			
		||||
        .name = BLOCK_OPT_SIZE,
 | 
			
		||||
        .type = OPT_SIZE,
 | 
			
		||||
        .help = "Virtual disk size"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        .name = BLOCK_OPT_BACKING_FILE,
 | 
			
		||||
        .type = OPT_STRING,
 | 
			
		||||
        .help = "File name of a base image"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        .name = BLOCK_OPT_ENCRYPT,
 | 
			
		||||
        .type = OPT_FLAG,
 | 
			
		||||
        .help = "Encrypt the image"
 | 
			
		||||
    },
 | 
			
		||||
    { NULL }
 | 
			
		||||
BlockDriver bdrv_qcow = {
 | 
			
		||||
    "qcow",
 | 
			
		||||
    sizeof(BDRVQcowState),
 | 
			
		||||
    qcow_probe,
 | 
			
		||||
    qcow_open,
 | 
			
		||||
    qcow_read,
 | 
			
		||||
    qcow_write,
 | 
			
		||||
    qcow_close,
 | 
			
		||||
    qcow_create,
 | 
			
		||||
    qcow_is_allocated,
 | 
			
		||||
    qcow_set_key,
 | 
			
		||||
    qcow_make_empty
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static BlockDriver bdrv_qcow = {
 | 
			
		||||
    .format_name	= "qcow",
 | 
			
		||||
    .instance_size	= sizeof(BDRVQcowState),
 | 
			
		||||
    .bdrv_probe		= qcow_probe,
 | 
			
		||||
    .bdrv_open		= qcow_open,
 | 
			
		||||
    .bdrv_close		= qcow_close,
 | 
			
		||||
    .bdrv_create	= qcow_create,
 | 
			
		||||
    .bdrv_flush		= qcow_flush,
 | 
			
		||||
    .bdrv_is_allocated	= qcow_is_allocated,
 | 
			
		||||
    .bdrv_set_key	= qcow_set_key,
 | 
			
		||||
    .bdrv_make_empty	= qcow_make_empty,
 | 
			
		||||
    .bdrv_aio_readv	= qcow_aio_readv,
 | 
			
		||||
    .bdrv_aio_writev	= qcow_aio_writev,
 | 
			
		||||
    .bdrv_aio_flush	= qcow_aio_flush,
 | 
			
		||||
    .bdrv_write_compressed = qcow_write_compressed,
 | 
			
		||||
    .bdrv_get_info	= qcow_get_info,
 | 
			
		||||
 | 
			
		||||
    .create_options = qcow_create_options,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void bdrv_qcow_init(void)
 | 
			
		||||
{
 | 
			
		||||
    bdrv_register(&bdrv_qcow);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
block_init(bdrv_qcow_init);
 | 
			
		||||
							
								
								
									
										439
									
								
								block-vmdk.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										439
									
								
								block-vmdk.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,439 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Block driver for the VMDK format
 | 
			
		||||
 * 
 | 
			
		||||
 * Copyright (c) 2004 Fabrice Bellard
 | 
			
		||||
 * Copyright (c) 2005 Filip Navara
 | 
			
		||||
 * 
 | 
			
		||||
 * 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 "vl.h"
 | 
			
		||||
#include "block_int.h"
 | 
			
		||||
 | 
			
		||||
#define VMDK3_MAGIC (('C' << 24) | ('O' << 16) | ('W' << 8) | 'D')
 | 
			
		||||
#define VMDK4_MAGIC (('K' << 24) | ('D' << 16) | ('M' << 8) | 'V')
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    uint32_t version;
 | 
			
		||||
    uint32_t flags;
 | 
			
		||||
    uint32_t disk_sectors;
 | 
			
		||||
    uint32_t granularity;
 | 
			
		||||
    uint32_t l1dir_offset;
 | 
			
		||||
    uint32_t l1dir_size;
 | 
			
		||||
    uint32_t file_sectors;
 | 
			
		||||
    uint32_t cylinders;
 | 
			
		||||
    uint32_t heads;
 | 
			
		||||
    uint32_t sectors_per_track;
 | 
			
		||||
} VMDK3Header;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    uint32_t version;
 | 
			
		||||
    uint32_t flags;
 | 
			
		||||
    int64_t capacity;
 | 
			
		||||
    int64_t granularity;
 | 
			
		||||
    int64_t desc_offset;
 | 
			
		||||
    int64_t desc_size;
 | 
			
		||||
    int32_t num_gtes_per_gte;
 | 
			
		||||
    int64_t rgd_offset;
 | 
			
		||||
    int64_t gd_offset;
 | 
			
		||||
    int64_t grain_offset;
 | 
			
		||||
    char filler[1];
 | 
			
		||||
    char check_bytes[4];
 | 
			
		||||
} __attribute__((packed)) VMDK4Header;
 | 
			
		||||
 | 
			
		||||
#define L2_CACHE_SIZE 16
 | 
			
		||||
 | 
			
		||||
typedef struct BDRVVmdkState {
 | 
			
		||||
    int fd;
 | 
			
		||||
    int64_t l1_table_offset;
 | 
			
		||||
    int64_t l1_backup_table_offset;
 | 
			
		||||
    uint32_t *l1_table;
 | 
			
		||||
    uint32_t *l1_backup_table;
 | 
			
		||||
    unsigned int l1_size;
 | 
			
		||||
    uint32_t l1_entry_sectors;
 | 
			
		||||
 | 
			
		||||
    unsigned int l2_size;
 | 
			
		||||
    uint32_t *l2_cache;
 | 
			
		||||
    uint32_t l2_cache_offsets[L2_CACHE_SIZE];
 | 
			
		||||
    uint32_t l2_cache_counts[L2_CACHE_SIZE];
 | 
			
		||||
 | 
			
		||||
    unsigned int cluster_sectors;
 | 
			
		||||
} BDRVVmdkState;
 | 
			
		||||
 | 
			
		||||
static int vmdk_probe(const uint8_t *buf, int buf_size, const char *filename)
 | 
			
		||||
{
 | 
			
		||||
    uint32_t magic;
 | 
			
		||||
 | 
			
		||||
    if (buf_size < 4)
 | 
			
		||||
        return 0;
 | 
			
		||||
    magic = be32_to_cpu(*(uint32_t *)buf);
 | 
			
		||||
    if (magic == VMDK3_MAGIC ||
 | 
			
		||||
        magic == VMDK4_MAGIC)
 | 
			
		||||
        return 100;
 | 
			
		||||
    else
 | 
			
		||||
        return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int vmdk_open(BlockDriverState *bs, const char *filename)
 | 
			
		||||
{
 | 
			
		||||
    BDRVVmdkState *s = bs->opaque;
 | 
			
		||||
    int fd, i;
 | 
			
		||||
    uint32_t magic;
 | 
			
		||||
    int l1_size;
 | 
			
		||||
 | 
			
		||||
    fd = open(filename, O_RDWR | O_BINARY | O_LARGEFILE);
 | 
			
		||||
    if (fd < 0) {
 | 
			
		||||
        fd = open(filename, O_RDONLY | O_BINARY | O_LARGEFILE);
 | 
			
		||||
        if (fd < 0)
 | 
			
		||||
            return -1;
 | 
			
		||||
        bs->read_only = 1;
 | 
			
		||||
    }
 | 
			
		||||
    if (read(fd, &magic, sizeof(magic)) != sizeof(magic))
 | 
			
		||||
        goto fail;
 | 
			
		||||
    magic = be32_to_cpu(magic);
 | 
			
		||||
    if (magic == VMDK3_MAGIC) {
 | 
			
		||||
        VMDK3Header header;
 | 
			
		||||
        if (read(fd, &header, sizeof(header)) != 
 | 
			
		||||
            sizeof(header))
 | 
			
		||||
            goto fail;
 | 
			
		||||
        s->cluster_sectors = le32_to_cpu(header.granularity);
 | 
			
		||||
        s->l2_size = 1 << 9;
 | 
			
		||||
        s->l1_size = 1 << 6;
 | 
			
		||||
        bs->total_sectors = le32_to_cpu(header.disk_sectors);
 | 
			
		||||
        s->l1_table_offset = le32_to_cpu(header.l1dir_offset) << 9;
 | 
			
		||||
        s->l1_backup_table_offset = 0;
 | 
			
		||||
        s->l1_entry_sectors = s->l2_size * s->cluster_sectors;
 | 
			
		||||
    } else if (magic == VMDK4_MAGIC) {
 | 
			
		||||
        VMDK4Header header;
 | 
			
		||||
        
 | 
			
		||||
        if (read(fd, &header, sizeof(header)) != sizeof(header))
 | 
			
		||||
            goto fail;
 | 
			
		||||
        bs->total_sectors = le64_to_cpu(header.capacity);
 | 
			
		||||
        s->cluster_sectors = le64_to_cpu(header.granularity);
 | 
			
		||||
        s->l2_size = le32_to_cpu(header.num_gtes_per_gte);
 | 
			
		||||
        s->l1_entry_sectors = s->l2_size * s->cluster_sectors;
 | 
			
		||||
        if (s->l1_entry_sectors <= 0)
 | 
			
		||||
            goto fail;
 | 
			
		||||
        s->l1_size = (bs->total_sectors + s->l1_entry_sectors - 1) 
 | 
			
		||||
            / s->l1_entry_sectors;
 | 
			
		||||
        s->l1_table_offset = le64_to_cpu(header.rgd_offset) << 9;
 | 
			
		||||
        s->l1_backup_table_offset = le64_to_cpu(header.gd_offset) << 9;
 | 
			
		||||
    } else {
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
    /* read the L1 table */
 | 
			
		||||
    l1_size = s->l1_size * sizeof(uint32_t);
 | 
			
		||||
    s->l1_table = qemu_malloc(l1_size);
 | 
			
		||||
    if (!s->l1_table)
 | 
			
		||||
        goto fail;
 | 
			
		||||
    if (lseek(fd, s->l1_table_offset, SEEK_SET) == -1)
 | 
			
		||||
        goto fail;
 | 
			
		||||
    if (read(fd, s->l1_table, l1_size) != l1_size)
 | 
			
		||||
        goto fail;
 | 
			
		||||
    for(i = 0; i < s->l1_size; i++) {
 | 
			
		||||
        le32_to_cpus(&s->l1_table[i]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (s->l1_backup_table_offset) {
 | 
			
		||||
        s->l1_backup_table = qemu_malloc(l1_size);
 | 
			
		||||
        if (!s->l1_backup_table)
 | 
			
		||||
            goto fail;
 | 
			
		||||
        if (lseek(fd, s->l1_backup_table_offset, SEEK_SET) == -1)
 | 
			
		||||
            goto fail;
 | 
			
		||||
        if (read(fd, s->l1_backup_table, l1_size) != l1_size)
 | 
			
		||||
            goto fail;
 | 
			
		||||
        for(i = 0; i < s->l1_size; i++) {
 | 
			
		||||
            le32_to_cpus(&s->l1_backup_table[i]);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    s->l2_cache = qemu_malloc(s->l2_size * L2_CACHE_SIZE * sizeof(uint32_t));
 | 
			
		||||
    if (!s->l2_cache)
 | 
			
		||||
        goto fail;
 | 
			
		||||
    s->fd = fd;
 | 
			
		||||
    return 0;
 | 
			
		||||
 fail:
 | 
			
		||||
    qemu_free(s->l1_backup_table);
 | 
			
		||||
    qemu_free(s->l1_table);
 | 
			
		||||
    qemu_free(s->l2_cache);
 | 
			
		||||
    close(fd);
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static uint64_t get_cluster_offset(BlockDriverState *bs,
 | 
			
		||||
                                   uint64_t offset, int allocate)
 | 
			
		||||
{
 | 
			
		||||
    BDRVVmdkState *s = bs->opaque;
 | 
			
		||||
    unsigned int l1_index, l2_offset, l2_index;
 | 
			
		||||
    int min_index, i, j;
 | 
			
		||||
    uint32_t min_count, *l2_table, tmp;
 | 
			
		||||
    uint64_t cluster_offset;
 | 
			
		||||
    
 | 
			
		||||
    l1_index = (offset >> 9) / s->l1_entry_sectors;
 | 
			
		||||
    if (l1_index >= s->l1_size)
 | 
			
		||||
        return 0;
 | 
			
		||||
    l2_offset = s->l1_table[l1_index];
 | 
			
		||||
    if (!l2_offset)
 | 
			
		||||
        return 0;
 | 
			
		||||
    for(i = 0; i < L2_CACHE_SIZE; i++) {
 | 
			
		||||
        if (l2_offset == s->l2_cache_offsets[i]) {
 | 
			
		||||
            /* increment the hit count */
 | 
			
		||||
            if (++s->l2_cache_counts[i] == 0xffffffff) {
 | 
			
		||||
                for(j = 0; j < L2_CACHE_SIZE; j++) {
 | 
			
		||||
                    s->l2_cache_counts[j] >>= 1;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            l2_table = s->l2_cache + (i * s->l2_size);
 | 
			
		||||
            goto found;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    /* not found: load a new entry in the least used one */
 | 
			
		||||
    min_index = 0;
 | 
			
		||||
    min_count = 0xffffffff;
 | 
			
		||||
    for(i = 0; i < L2_CACHE_SIZE; i++) {
 | 
			
		||||
        if (s->l2_cache_counts[i] < min_count) {
 | 
			
		||||
            min_count = s->l2_cache_counts[i];
 | 
			
		||||
            min_index = i;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    l2_table = s->l2_cache + (min_index * s->l2_size);
 | 
			
		||||
    lseek(s->fd, (int64_t)l2_offset * 512, SEEK_SET);
 | 
			
		||||
    if (read(s->fd, l2_table, s->l2_size * sizeof(uint32_t)) != 
 | 
			
		||||
        s->l2_size * sizeof(uint32_t))
 | 
			
		||||
        return 0;
 | 
			
		||||
    s->l2_cache_offsets[min_index] = l2_offset;
 | 
			
		||||
    s->l2_cache_counts[min_index] = 1;
 | 
			
		||||
 found:
 | 
			
		||||
    l2_index = ((offset >> 9) / s->cluster_sectors) % s->l2_size;
 | 
			
		||||
    cluster_offset = le32_to_cpu(l2_table[l2_index]);
 | 
			
		||||
    if (!cluster_offset) {
 | 
			
		||||
        if (!allocate)
 | 
			
		||||
            return 0;
 | 
			
		||||
        cluster_offset = lseek(s->fd, 0, SEEK_END);
 | 
			
		||||
        ftruncate(s->fd, cluster_offset + (s->cluster_sectors << 9));
 | 
			
		||||
        cluster_offset >>= 9;
 | 
			
		||||
        /* update L2 table */
 | 
			
		||||
        tmp = cpu_to_le32(cluster_offset);
 | 
			
		||||
        l2_table[l2_index] = tmp;
 | 
			
		||||
        lseek(s->fd, ((int64_t)l2_offset * 512) + (l2_index * sizeof(tmp)), SEEK_SET);
 | 
			
		||||
        if (write(s->fd, &tmp, sizeof(tmp)) != sizeof(tmp))
 | 
			
		||||
            return 0;
 | 
			
		||||
        /* update backup L2 table */
 | 
			
		||||
        if (s->l1_backup_table_offset != 0) {
 | 
			
		||||
            l2_offset = s->l1_backup_table[l1_index];
 | 
			
		||||
            lseek(s->fd, ((int64_t)l2_offset * 512) + (l2_index * sizeof(tmp)), SEEK_SET);
 | 
			
		||||
            if (write(s->fd, &tmp, sizeof(tmp)) != sizeof(tmp))
 | 
			
		||||
                return 0;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    cluster_offset <<= 9;
 | 
			
		||||
    return cluster_offset;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int vmdk_is_allocated(BlockDriverState *bs, int64_t sector_num, 
 | 
			
		||||
                             int nb_sectors, int *pnum)
 | 
			
		||||
{
 | 
			
		||||
    BDRVVmdkState *s = bs->opaque;
 | 
			
		||||
    int index_in_cluster, n;
 | 
			
		||||
    uint64_t cluster_offset;
 | 
			
		||||
 | 
			
		||||
    cluster_offset = get_cluster_offset(bs, sector_num << 9, 0);
 | 
			
		||||
    index_in_cluster = sector_num % s->cluster_sectors;
 | 
			
		||||
    n = s->cluster_sectors - index_in_cluster;
 | 
			
		||||
    if (n > nb_sectors)
 | 
			
		||||
        n = nb_sectors;
 | 
			
		||||
    *pnum = n;
 | 
			
		||||
    return (cluster_offset != 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int vmdk_read(BlockDriverState *bs, int64_t sector_num, 
 | 
			
		||||
                    uint8_t *buf, int nb_sectors)
 | 
			
		||||
{
 | 
			
		||||
    BDRVVmdkState *s = bs->opaque;
 | 
			
		||||
    int ret, index_in_cluster, n;
 | 
			
		||||
    uint64_t cluster_offset;
 | 
			
		||||
    
 | 
			
		||||
    while (nb_sectors > 0) {
 | 
			
		||||
        cluster_offset = get_cluster_offset(bs, sector_num << 9, 0);
 | 
			
		||||
        index_in_cluster = sector_num % s->cluster_sectors;
 | 
			
		||||
        n = s->cluster_sectors - index_in_cluster;
 | 
			
		||||
        if (n > nb_sectors)
 | 
			
		||||
            n = nb_sectors;
 | 
			
		||||
        if (!cluster_offset) {
 | 
			
		||||
            memset(buf, 0, 512 * n);
 | 
			
		||||
        } else {
 | 
			
		||||
            lseek(s->fd, cluster_offset + index_in_cluster * 512, SEEK_SET);
 | 
			
		||||
            ret = read(s->fd, buf, n * 512);
 | 
			
		||||
            if (ret != n * 512) 
 | 
			
		||||
                return -1;
 | 
			
		||||
        }
 | 
			
		||||
        nb_sectors -= n;
 | 
			
		||||
        sector_num += n;
 | 
			
		||||
        buf += n * 512;
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int vmdk_write(BlockDriverState *bs, int64_t sector_num, 
 | 
			
		||||
                     const uint8_t *buf, int nb_sectors)
 | 
			
		||||
{
 | 
			
		||||
    BDRVVmdkState *s = bs->opaque;
 | 
			
		||||
    int ret, index_in_cluster, n;
 | 
			
		||||
    uint64_t cluster_offset;
 | 
			
		||||
 | 
			
		||||
    while (nb_sectors > 0) {
 | 
			
		||||
        index_in_cluster = sector_num & (s->cluster_sectors - 1);
 | 
			
		||||
        n = s->cluster_sectors - index_in_cluster;
 | 
			
		||||
        if (n > nb_sectors)
 | 
			
		||||
            n = nb_sectors;
 | 
			
		||||
        cluster_offset = get_cluster_offset(bs, sector_num << 9, 1);
 | 
			
		||||
        if (!cluster_offset)
 | 
			
		||||
            return -1;
 | 
			
		||||
        lseek(s->fd, cluster_offset + index_in_cluster * 512, SEEK_SET);
 | 
			
		||||
        ret = write(s->fd, buf, n * 512);
 | 
			
		||||
        if (ret != n * 512)
 | 
			
		||||
            return -1;
 | 
			
		||||
        nb_sectors -= n;
 | 
			
		||||
        sector_num += n;
 | 
			
		||||
        buf += n * 512;
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int vmdk_create(const char *filename, int64_t total_size,
 | 
			
		||||
                       const char *backing_file, int flags)
 | 
			
		||||
{
 | 
			
		||||
    int fd, i;
 | 
			
		||||
    VMDK4Header header;
 | 
			
		||||
    uint32_t tmp, magic, grains, gd_size, gt_size, gt_count;
 | 
			
		||||
    char *desc_template =
 | 
			
		||||
        "# Disk DescriptorFile\n"
 | 
			
		||||
        "version=1\n"
 | 
			
		||||
        "CID=%x\n"
 | 
			
		||||
        "parentCID=ffffffff\n"
 | 
			
		||||
        "createType=\"monolithicSparse\"\n"
 | 
			
		||||
        "\n"
 | 
			
		||||
        "# Extent description\n"
 | 
			
		||||
        "RW %lu SPARSE \"%s\"\n"
 | 
			
		||||
        "\n"
 | 
			
		||||
        "# The Disk Data Base \n"
 | 
			
		||||
        "#DDB\n"
 | 
			
		||||
        "\n"
 | 
			
		||||
        "ddb.virtualHWVersion = \"3\"\n"
 | 
			
		||||
        "ddb.geometry.cylinders = \"%lu\"\n"
 | 
			
		||||
        "ddb.geometry.heads = \"16\"\n"
 | 
			
		||||
        "ddb.geometry.sectors = \"63\"\n"
 | 
			
		||||
        "ddb.adapterType = \"ide\"\n";
 | 
			
		||||
    char desc[1024];
 | 
			
		||||
    const char *real_filename, *temp_str;
 | 
			
		||||
 | 
			
		||||
    /* XXX: add support for backing file */
 | 
			
		||||
 | 
			
		||||
    fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE,
 | 
			
		||||
              0644);
 | 
			
		||||
    if (fd < 0)
 | 
			
		||||
        return -1;
 | 
			
		||||
    magic = cpu_to_be32(VMDK4_MAGIC);
 | 
			
		||||
    memset(&header, 0, sizeof(header));
 | 
			
		||||
    header.version = cpu_to_le32(1);
 | 
			
		||||
    header.flags = cpu_to_le32(3); /* ?? */
 | 
			
		||||
    header.capacity = cpu_to_le64(total_size);
 | 
			
		||||
    header.granularity = cpu_to_le64(128);
 | 
			
		||||
    header.num_gtes_per_gte = cpu_to_le32(512);
 | 
			
		||||
 | 
			
		||||
    grains = (total_size + header.granularity - 1) / header.granularity;
 | 
			
		||||
    gt_size = ((header.num_gtes_per_gte * sizeof(uint32_t)) + 511) >> 9;
 | 
			
		||||
    gt_count = (grains + header.num_gtes_per_gte - 1) / header.num_gtes_per_gte;
 | 
			
		||||
    gd_size = (gt_count * sizeof(uint32_t) + 511) >> 9;
 | 
			
		||||
 | 
			
		||||
    header.desc_offset = 1;
 | 
			
		||||
    header.desc_size = 20;
 | 
			
		||||
    header.rgd_offset = header.desc_offset + header.desc_size;
 | 
			
		||||
    header.gd_offset = header.rgd_offset + gd_size + (gt_size * gt_count);
 | 
			
		||||
    header.grain_offset =
 | 
			
		||||
       ((header.gd_offset + gd_size + (gt_size * gt_count) +
 | 
			
		||||
         header.granularity - 1) / header.granularity) *
 | 
			
		||||
        header.granularity;
 | 
			
		||||
 | 
			
		||||
    header.desc_offset = cpu_to_le64(header.desc_offset);
 | 
			
		||||
    header.desc_size = cpu_to_le64(header.desc_size);
 | 
			
		||||
    header.rgd_offset = cpu_to_le64(header.rgd_offset);
 | 
			
		||||
    header.gd_offset = cpu_to_le64(header.gd_offset);
 | 
			
		||||
    header.grain_offset = cpu_to_le64(header.grain_offset);
 | 
			
		||||
 | 
			
		||||
    header.check_bytes[0] = 0xa;
 | 
			
		||||
    header.check_bytes[1] = 0x20;
 | 
			
		||||
    header.check_bytes[2] = 0xd;
 | 
			
		||||
    header.check_bytes[3] = 0xa;
 | 
			
		||||
    
 | 
			
		||||
    /* write all the data */    
 | 
			
		||||
    write(fd, &magic, sizeof(magic));
 | 
			
		||||
    write(fd, &header, sizeof(header));
 | 
			
		||||
 | 
			
		||||
    ftruncate(fd, header.grain_offset << 9);
 | 
			
		||||
 | 
			
		||||
    /* write grain directory */
 | 
			
		||||
    lseek(fd, le64_to_cpu(header.rgd_offset) << 9, SEEK_SET);
 | 
			
		||||
    for (i = 0, tmp = header.rgd_offset + gd_size;
 | 
			
		||||
         i < gt_count; i++, tmp += gt_size)
 | 
			
		||||
        write(fd, &tmp, sizeof(tmp));
 | 
			
		||||
   
 | 
			
		||||
    /* write backup grain directory */
 | 
			
		||||
    lseek(fd, le64_to_cpu(header.gd_offset) << 9, SEEK_SET);
 | 
			
		||||
    for (i = 0, tmp = header.gd_offset + gd_size;
 | 
			
		||||
         i < gt_count; i++, tmp += gt_size)
 | 
			
		||||
        write(fd, &tmp, sizeof(tmp));
 | 
			
		||||
 | 
			
		||||
    /* compose the descriptor */
 | 
			
		||||
    real_filename = filename;
 | 
			
		||||
    if ((temp_str = strrchr(real_filename, '\\')) != NULL)
 | 
			
		||||
        real_filename = temp_str + 1;
 | 
			
		||||
    if ((temp_str = strrchr(real_filename, '/')) != NULL)
 | 
			
		||||
        real_filename = temp_str + 1;
 | 
			
		||||
    if ((temp_str = strrchr(real_filename, ':')) != NULL)
 | 
			
		||||
        real_filename = temp_str + 1;
 | 
			
		||||
    sprintf(desc, desc_template, time(NULL), (unsigned long)total_size,
 | 
			
		||||
            real_filename, total_size / (63 * 16));
 | 
			
		||||
 | 
			
		||||
    /* write the descriptor */
 | 
			
		||||
    lseek(fd, le64_to_cpu(header.desc_offset) << 9, SEEK_SET);
 | 
			
		||||
    write(fd, desc, strlen(desc));
 | 
			
		||||
 | 
			
		||||
    close(fd);
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void vmdk_close(BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
    BDRVVmdkState *s = bs->opaque;
 | 
			
		||||
    qemu_free(s->l1_table);
 | 
			
		||||
    qemu_free(s->l2_cache);
 | 
			
		||||
    close(s->fd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
BlockDriver bdrv_vmdk = {
 | 
			
		||||
    "vmdk",
 | 
			
		||||
    sizeof(BDRVVmdkState),
 | 
			
		||||
    vmdk_probe,
 | 
			
		||||
    vmdk_open,
 | 
			
		||||
    vmdk_read,
 | 
			
		||||
    vmdk_write,
 | 
			
		||||
    vmdk_close,
 | 
			
		||||
    vmdk_create,
 | 
			
		||||
    vmdk_is_allocated,
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										242
									
								
								block-vpc.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										242
									
								
								block-vpc.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,242 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Block driver for Conectix/Microsoft Virtual PC images
 | 
			
		||||
 * 
 | 
			
		||||
 * Copyright (c) 2005 Alex Beregszaszi
 | 
			
		||||
 * 
 | 
			
		||||
 * 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 "vl.h"
 | 
			
		||||
#include "block_int.h"
 | 
			
		||||
 | 
			
		||||
/**************************************************************/
 | 
			
		||||
 | 
			
		||||
#define HEADER_SIZE 512
 | 
			
		||||
 | 
			
		||||
//#define CACHE
 | 
			
		||||
 | 
			
		||||
// always big-endian
 | 
			
		||||
struct vpc_subheader {
 | 
			
		||||
    char magic[8]; // "conectix" / "cxsparse"
 | 
			
		||||
    union {
 | 
			
		||||
	struct {
 | 
			
		||||
	    uint32_t unk1[2];
 | 
			
		||||
	    uint32_t unk2; // always zero?
 | 
			
		||||
	    uint32_t subheader_offset;
 | 
			
		||||
	    uint32_t unk3; // some size?
 | 
			
		||||
	    char creator[4]; // "vpc "
 | 
			
		||||
	    uint16_t major;
 | 
			
		||||
	    uint16_t minor;
 | 
			
		||||
	    char guest[4]; // "Wi2k"
 | 
			
		||||
	    uint32_t unk4[7];
 | 
			
		||||
	    uint8_t vnet_id[16]; // virtual network id, purpose unknown
 | 
			
		||||
	    // next 16 longs are used, but dunno the purpose
 | 
			
		||||
	    // next 6 longs unknown, following 7 long maybe a serial
 | 
			
		||||
	    char padding[HEADER_SIZE - 84];
 | 
			
		||||
	} main;
 | 
			
		||||
	struct {
 | 
			
		||||
	    uint32_t unk1[2]; // all bits set
 | 
			
		||||
	    uint32_t unk2; // always zero?
 | 
			
		||||
	    uint32_t pagetable_offset;
 | 
			
		||||
	    uint32_t unk3;
 | 
			
		||||
	    uint32_t pagetable_entries; // 32bit/entry
 | 
			
		||||
	    uint32_t pageentry_size; // 512*8*512
 | 
			
		||||
	    uint32_t nb_sectors;
 | 
			
		||||
	    char padding[HEADER_SIZE - 40];
 | 
			
		||||
	} sparse;
 | 
			
		||||
	char padding[HEADER_SIZE - 8];
 | 
			
		||||
    } type;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef struct BDRVVPCState {
 | 
			
		||||
    int fd;
 | 
			
		||||
    
 | 
			
		||||
    int pagetable_entries;
 | 
			
		||||
    uint32_t *pagetable;
 | 
			
		||||
 | 
			
		||||
    uint32_t pageentry_size;
 | 
			
		||||
#ifdef CACHE
 | 
			
		||||
    uint8_t *pageentry_u8;
 | 
			
		||||
    uint32_t *pageentry_u32;
 | 
			
		||||
    uint16_t *pageentry_u16;
 | 
			
		||||
    
 | 
			
		||||
    uint64_t last_bitmap;
 | 
			
		||||
#endif
 | 
			
		||||
} BDRVVPCState;
 | 
			
		||||
 | 
			
		||||
static int vpc_probe(const uint8_t *buf, int buf_size, const char *filename)
 | 
			
		||||
{
 | 
			
		||||
    if (buf_size >= 8 && !strncmp(buf, "conectix", 8))
 | 
			
		||||
	return 100;
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int vpc_open(BlockDriverState *bs, const char *filename)
 | 
			
		||||
{
 | 
			
		||||
    BDRVVPCState *s = bs->opaque;
 | 
			
		||||
    int fd, i;
 | 
			
		||||
    struct vpc_subheader header;
 | 
			
		||||
 | 
			
		||||
    fd = open(filename, O_RDWR | O_BINARY | O_LARGEFILE);
 | 
			
		||||
    if (fd < 0) {
 | 
			
		||||
        fd = open(filename, O_RDONLY | O_BINARY | O_LARGEFILE);
 | 
			
		||||
        if (fd < 0)
 | 
			
		||||
            return -1;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    bs->read_only = 1; // no write support yet
 | 
			
		||||
    
 | 
			
		||||
    s->fd = fd;
 | 
			
		||||
 | 
			
		||||
    if (read(fd, &header, HEADER_SIZE) != HEADER_SIZE)
 | 
			
		||||
        goto fail;
 | 
			
		||||
 | 
			
		||||
    if (strncmp(header.magic, "conectix", 8))
 | 
			
		||||
        goto fail;
 | 
			
		||||
    lseek(s->fd, be32_to_cpu(header.type.main.subheader_offset), SEEK_SET);
 | 
			
		||||
 | 
			
		||||
    if (read(fd, &header, HEADER_SIZE) != HEADER_SIZE)
 | 
			
		||||
        goto fail;
 | 
			
		||||
 | 
			
		||||
    if (strncmp(header.magic, "cxsparse", 8))
 | 
			
		||||
	goto fail;
 | 
			
		||||
 | 
			
		||||
    bs->total_sectors = ((uint64_t)be32_to_cpu(header.type.sparse.pagetable_entries) *
 | 
			
		||||
			be32_to_cpu(header.type.sparse.pageentry_size)) / 512;
 | 
			
		||||
 | 
			
		||||
    lseek(s->fd, be32_to_cpu(header.type.sparse.pagetable_offset), SEEK_SET);
 | 
			
		||||
 | 
			
		||||
    s->pagetable_entries = be32_to_cpu(header.type.sparse.pagetable_entries);
 | 
			
		||||
    s->pagetable = qemu_malloc(s->pagetable_entries * 4);
 | 
			
		||||
    if (!s->pagetable)
 | 
			
		||||
	goto fail;
 | 
			
		||||
    if (read(s->fd, s->pagetable, s->pagetable_entries * 4) !=
 | 
			
		||||
	s->pagetable_entries * 4)
 | 
			
		||||
	goto fail;
 | 
			
		||||
    for (i = 0; i < s->pagetable_entries; i++)
 | 
			
		||||
	be32_to_cpus(&s->pagetable[i]);
 | 
			
		||||
 | 
			
		||||
    s->pageentry_size = be32_to_cpu(header.type.sparse.pageentry_size);
 | 
			
		||||
#ifdef CACHE
 | 
			
		||||
    s->pageentry_u8 = qemu_malloc(512);
 | 
			
		||||
    if (!s->pageentry_u8)
 | 
			
		||||
	goto fail;
 | 
			
		||||
    s->pageentry_u32 = s->pageentry_u8;
 | 
			
		||||
    s->pageentry_u16 = s->pageentry_u8;
 | 
			
		||||
    s->last_pagetable = -1;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
 fail:
 | 
			
		||||
    close(fd);
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline int seek_to_sector(BlockDriverState *bs, int64_t sector_num)
 | 
			
		||||
{
 | 
			
		||||
    BDRVVPCState *s = bs->opaque;
 | 
			
		||||
    uint64_t offset = sector_num * 512;
 | 
			
		||||
    uint64_t bitmap_offset, block_offset;
 | 
			
		||||
    uint32_t pagetable_index, pageentry_index;
 | 
			
		||||
 | 
			
		||||
    pagetable_index = offset / s->pageentry_size;
 | 
			
		||||
    pageentry_index = (offset % s->pageentry_size) / 512;
 | 
			
		||||
    
 | 
			
		||||
    if (pagetable_index > s->pagetable_entries || s->pagetable[pagetable_index] == 0xffffffff)
 | 
			
		||||
	return -1; // not allocated
 | 
			
		||||
 | 
			
		||||
    bitmap_offset = 512 * s->pagetable[pagetable_index];
 | 
			
		||||
    block_offset = bitmap_offset + 512 + (512 * pageentry_index);
 | 
			
		||||
    
 | 
			
		||||
//    printf("sector: %llx, index: %x, offset: %x, bioff: %llx, bloff: %llx\n",
 | 
			
		||||
//	sector_num, pagetable_index, pageentry_index,
 | 
			
		||||
//	bitmap_offset, block_offset);
 | 
			
		||||
 | 
			
		||||
// disabled by reason
 | 
			
		||||
#if 0
 | 
			
		||||
#ifdef CACHE
 | 
			
		||||
    if (bitmap_offset != s->last_bitmap)
 | 
			
		||||
    {
 | 
			
		||||
	lseek(s->fd, bitmap_offset, SEEK_SET);
 | 
			
		||||
 | 
			
		||||
	s->last_bitmap = bitmap_offset;
 | 
			
		||||
	
 | 
			
		||||
	// Scary! Bitmap is stored as big endian 32bit entries,
 | 
			
		||||
	// while we used to look it up byte by byte
 | 
			
		||||
	read(s->fd, s->pageentry_u8, 512);
 | 
			
		||||
	for (i = 0; i < 128; i++)
 | 
			
		||||
	    be32_to_cpus(&s->pageentry_u32[i]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ((s->pageentry_u8[pageentry_index / 8] >> (pageentry_index % 8)) & 1)
 | 
			
		||||
	return -1;
 | 
			
		||||
#else
 | 
			
		||||
    lseek(s->fd, bitmap_offset + (pageentry_index / 8), SEEK_SET);
 | 
			
		||||
	
 | 
			
		||||
    read(s->fd, &bitmap_entry, 1);
 | 
			
		||||
 | 
			
		||||
    if ((bitmap_entry >> (pageentry_index % 8)) & 1)
 | 
			
		||||
	return -1; // not allocated
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
    lseek(s->fd, block_offset, SEEK_SET);
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int vpc_read(BlockDriverState *bs, int64_t sector_num, 
 | 
			
		||||
                    uint8_t *buf, int nb_sectors)
 | 
			
		||||
{
 | 
			
		||||
    BDRVVPCState *s = bs->opaque;
 | 
			
		||||
    int ret;
 | 
			
		||||
 | 
			
		||||
    while (nb_sectors > 0) {
 | 
			
		||||
	if (!seek_to_sector(bs, sector_num))
 | 
			
		||||
	{
 | 
			
		||||
	    ret = read(s->fd, buf, 512);
 | 
			
		||||
	    if (ret != 512)
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
            memset(buf, 0, 512);
 | 
			
		||||
        nb_sectors--;
 | 
			
		||||
        sector_num++;
 | 
			
		||||
        buf += 512;
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void vpc_close(BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
    BDRVVPCState *s = bs->opaque;
 | 
			
		||||
    qemu_free(s->pagetable);
 | 
			
		||||
#ifdef CACHE
 | 
			
		||||
    qemu_free(s->pageentry_u8);
 | 
			
		||||
#endif
 | 
			
		||||
    close(s->fd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
BlockDriver bdrv_vpc = {
 | 
			
		||||
    "vpc",
 | 
			
		||||
    sizeof(BDRVVPCState),
 | 
			
		||||
    vpc_probe,
 | 
			
		||||
    vpc_open,
 | 
			
		||||
    vpc_read,
 | 
			
		||||
    NULL,
 | 
			
		||||
    vpc_close,
 | 
			
		||||
};
 | 
			
		||||
@@ -24,9 +24,9 @@
 | 
			
		||||
 */
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
#include <dirent.h>
 | 
			
		||||
#include "qemu-common.h"
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
#include "vl.h"
 | 
			
		||||
#include "block_int.h"
 | 
			
		||||
#include "module.h"
 | 
			
		||||
 | 
			
		||||
#ifndef S_IWGRP
 | 
			
		||||
#define S_IWGRP 0
 | 
			
		||||
@@ -53,7 +53,7 @@
 | 
			
		||||
#define stderr STDERR
 | 
			
		||||
FILE* stderr = NULL;
 | 
			
		||||
 | 
			
		||||
static void checkpoint(void);
 | 
			
		||||
static void checkpoint();
 | 
			
		||||
 | 
			
		||||
#ifdef __MINGW32__
 | 
			
		||||
void nonono(const char* file, int line, const char* msg) {
 | 
			
		||||
@@ -61,7 +61,7 @@ void nonono(const char* file, int line, const char* msg) {
 | 
			
		||||
    exit(-5);
 | 
			
		||||
}
 | 
			
		||||
#undef assert
 | 
			
		||||
#define assert(a) do {if (!(a)) nonono(__FILE__, __LINE__, #a);}while(0)
 | 
			
		||||
#define assert(a) if (!(a)) nonono(__FILE__, __LINE__, #a)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#else
 | 
			
		||||
@@ -78,7 +78,7 @@ typedef struct array_t {
 | 
			
		||||
 | 
			
		||||
static inline void array_init(array_t* array,unsigned int item_size)
 | 
			
		||||
{
 | 
			
		||||
    array->pointer = NULL;
 | 
			
		||||
    array->pointer=0;
 | 
			
		||||
    array->size=0;
 | 
			
		||||
    array->next=0;
 | 
			
		||||
    array->item_size=item_size;
 | 
			
		||||
@@ -93,6 +93,7 @@ static inline void array_free(array_t* array)
 | 
			
		||||
 | 
			
		||||
/* does not automatically grow */
 | 
			
		||||
static inline void* array_get(array_t* array,unsigned int index) {
 | 
			
		||||
    assert(index >= 0);
 | 
			
		||||
    assert(index < array->next);
 | 
			
		||||
    return array->pointer + index * array->item_size;
 | 
			
		||||
}
 | 
			
		||||
@@ -101,7 +102,7 @@ static inline int array_ensure_allocated(array_t* array, int index)
 | 
			
		||||
{
 | 
			
		||||
    if((index + 1) * array->item_size > array->size) {
 | 
			
		||||
	int new_size = (index + 32) * array->item_size;
 | 
			
		||||
	array->pointer = qemu_realloc(array->pointer, new_size);
 | 
			
		||||
	array->pointer = realloc(array->pointer, new_size);
 | 
			
		||||
	if (!array->pointer)
 | 
			
		||||
	    return -1;
 | 
			
		||||
	array->size = new_size;
 | 
			
		||||
@@ -127,9 +128,9 @@ static inline void* array_get_next(array_t* array) {
 | 
			
		||||
static inline void* array_insert(array_t* array,unsigned int index,unsigned int count) {
 | 
			
		||||
    if((array->next+count)*array->item_size>array->size) {
 | 
			
		||||
	int increment=count*array->item_size;
 | 
			
		||||
	array->pointer=qemu_realloc(array->pointer,array->size+increment);
 | 
			
		||||
	array->pointer=realloc(array->pointer,array->size+increment);
 | 
			
		||||
	if(!array->pointer)
 | 
			
		||||
            return NULL;
 | 
			
		||||
	    return 0;
 | 
			
		||||
	array->size+=increment;
 | 
			
		||||
    }
 | 
			
		||||
    memmove(array->pointer+(index+count)*array->item_size,
 | 
			
		||||
@@ -159,7 +160,7 @@ static inline int array_roll(array_t* array,int index_to,int index_from,int coun
 | 
			
		||||
    is=array->item_size;
 | 
			
		||||
    from=array->pointer+index_from*is;
 | 
			
		||||
    to=array->pointer+index_to*is;
 | 
			
		||||
    buf=qemu_malloc(is*count);
 | 
			
		||||
    buf=malloc(is*count);
 | 
			
		||||
    memcpy(buf,from,is*count);
 | 
			
		||||
 | 
			
		||||
    if(index_to<index_from)
 | 
			
		||||
@@ -174,7 +175,7 @@ static inline int array_roll(array_t* array,int index_to,int index_from,int coun
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline int array_remove_slice(array_t* array,int index, int count)
 | 
			
		||||
inline int array_remove_slice(array_t* array,int index, int count)
 | 
			
		||||
{
 | 
			
		||||
    assert(index >=0);
 | 
			
		||||
    assert(count > 0);
 | 
			
		||||
@@ -185,15 +186,16 @@ static inline int array_remove_slice(array_t* array,int index, int count)
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int array_remove(array_t* array,int index)
 | 
			
		||||
int array_remove(array_t* array,int index)
 | 
			
		||||
{
 | 
			
		||||
    return array_remove_slice(array, index, 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* return the index for a given member */
 | 
			
		||||
static int array_index(array_t* array, void* pointer)
 | 
			
		||||
int array_index(array_t* array, void* pointer)
 | 
			
		||||
{
 | 
			
		||||
    size_t offset = (char*)pointer - array->pointer;
 | 
			
		||||
    assert(offset >= 0);
 | 
			
		||||
    assert((offset % array->item_size) == 0);
 | 
			
		||||
    assert(offset/array->item_size < array->next);
 | 
			
		||||
    return offset/array->item_size;
 | 
			
		||||
@@ -240,25 +242,21 @@ typedef struct bootsector_t {
 | 
			
		||||
    uint8_t magic[2];
 | 
			
		||||
} __attribute__((packed)) bootsector_t;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    uint8_t head;
 | 
			
		||||
    uint8_t sector;
 | 
			
		||||
    uint8_t cylinder;
 | 
			
		||||
} mbr_chs_t;
 | 
			
		||||
 | 
			
		||||
typedef struct partition_t {
 | 
			
		||||
    uint8_t attributes; /* 0x80 = bootable */
 | 
			
		||||
    mbr_chs_t start_CHS;
 | 
			
		||||
    uint8_t   fs_type; /* 0x1 = FAT12, 0x6 = FAT16, 0xe = FAT16_LBA, 0xb = FAT32, 0xc = FAT32_LBA */
 | 
			
		||||
    mbr_chs_t end_CHS;
 | 
			
		||||
    uint8_t start_head;
 | 
			
		||||
    uint8_t start_sector;
 | 
			
		||||
    uint8_t start_cylinder;
 | 
			
		||||
    uint8_t fs_type; /* 0x1 = FAT12, 0x6 = FAT16, 0xb = FAT32 */
 | 
			
		||||
    uint8_t end_head;
 | 
			
		||||
    uint8_t end_sector;
 | 
			
		||||
    uint8_t end_cylinder;
 | 
			
		||||
    uint32_t start_sector_long;
 | 
			
		||||
    uint32_t length_sector_long;
 | 
			
		||||
    uint32_t end_sector_long;
 | 
			
		||||
} __attribute__((packed)) partition_t;
 | 
			
		||||
 | 
			
		||||
typedef struct mbr_t {
 | 
			
		||||
    uint8_t ignored[0x1b8];
 | 
			
		||||
    uint32_t nt_id;
 | 
			
		||||
    uint8_t ignored2[2];
 | 
			
		||||
    uint8_t ignored[0x1be];
 | 
			
		||||
    partition_t partition[4];
 | 
			
		||||
    uint8_t magic[2];
 | 
			
		||||
} __attribute__((packed)) mbr_t;
 | 
			
		||||
@@ -352,26 +350,11 @@ typedef struct BDRVVVFATState {
 | 
			
		||||
    int downcase_short_names;
 | 
			
		||||
} BDRVVVFATState;
 | 
			
		||||
 | 
			
		||||
/* take the sector position spos and convert it to Cylinder/Head/Sector position
 | 
			
		||||
 * if the position is outside the specified geometry, fill maximum value for CHS
 | 
			
		||||
 * and return 1 to signal overflow.
 | 
			
		||||
 */
 | 
			
		||||
static int sector2CHS(BlockDriverState* bs, mbr_chs_t * chs, int spos){
 | 
			
		||||
    int head,sector;
 | 
			
		||||
    sector   = spos % (bs->secs);  spos/= bs->secs;
 | 
			
		||||
    head     = spos % (bs->heads); spos/= bs->heads;
 | 
			
		||||
    if(spos >= bs->cyls){
 | 
			
		||||
        /* Overflow,
 | 
			
		||||
        it happens if 32bit sector positions are used, while CHS is only 24bit.
 | 
			
		||||
        Windows/Dos is said to take 1023/255/63 as nonrepresentable CHS */
 | 
			
		||||
        chs->head     = 0xFF;
 | 
			
		||||
        chs->sector   = 0xFF;
 | 
			
		||||
        chs->cylinder = 0xFF;
 | 
			
		||||
        return 1;
 | 
			
		||||
    }
 | 
			
		||||
    chs->head     = (uint8_t)head;
 | 
			
		||||
    chs->sector   = (uint8_t)( (sector+1) | ((spos>>8)<<6) );
 | 
			
		||||
    chs->cylinder = (uint8_t)spos;
 | 
			
		||||
 | 
			
		||||
static int vvfat_probe(const uint8_t *buf, int buf_size, const char *filename)
 | 
			
		||||
{
 | 
			
		||||
    if (strstart(filename, "fat:", NULL))
 | 
			
		||||
	return 100;
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -380,29 +363,20 @@ static void init_mbr(BDRVVVFATState* s)
 | 
			
		||||
    /* TODO: if the files mbr.img and bootsect.img exist, use them */
 | 
			
		||||
    mbr_t* real_mbr=(mbr_t*)s->first_sectors;
 | 
			
		||||
    partition_t* partition=&(real_mbr->partition[0]);
 | 
			
		||||
    int lba;
 | 
			
		||||
 | 
			
		||||
    memset(s->first_sectors,0,512);
 | 
			
		||||
   
 | 
			
		||||
    /* Win NT Disk Signature */
 | 
			
		||||
    real_mbr->nt_id= cpu_to_le32(0xbe1afdfa);
 | 
			
		||||
 | 
			
		||||
    partition->attributes=0x80; /* bootable */
 | 
			
		||||
 | 
			
		||||
    /* LBA is used when partition is outside the CHS geometry */
 | 
			
		||||
    lba = sector2CHS(s->bs, &partition->start_CHS, s->first_sectors_number-1);
 | 
			
		||||
    lba|= sector2CHS(s->bs, &partition->end_CHS,   s->sector_count);
 | 
			
		||||
 | 
			
		||||
    /*LBA partitions are identified only by start/length_sector_long not by CHS*/
 | 
			
		||||
    partition->start_sector_long =cpu_to_le32(s->first_sectors_number-1);
 | 
			
		||||
    partition->length_sector_long=cpu_to_le32(s->sector_count - s->first_sectors_number+1);
 | 
			
		||||
 | 
			
		||||
    partition->start_head=1;
 | 
			
		||||
    partition->start_sector=1;
 | 
			
		||||
    partition->start_cylinder=0;
 | 
			
		||||
    /* FAT12/FAT16/FAT32 */
 | 
			
		||||
    /* DOS uses different types when partition is LBA,
 | 
			
		||||
       probably to prevent older versions from using CHS on them */
 | 
			
		||||
    partition->fs_type= s->fat_type==12 ? 0x1:
 | 
			
		||||
                        s->fat_type==16 ? (lba?0xe:0x06):
 | 
			
		||||
                         /*fat_tyoe==32*/ (lba?0xc:0x0b);
 | 
			
		||||
    partition->fs_type=(s->fat_type==12?0x1:s->fat_type==16?0x6:0xb);
 | 
			
		||||
    partition->end_head=s->bs->heads-1;
 | 
			
		||||
    partition->end_sector=0xff; /* end sector & upper 2 bits of cylinder */;
 | 
			
		||||
    partition->end_cylinder=0xff; /* lower 8 bits of end cylinder */;
 | 
			
		||||
    partition->start_sector_long=cpu_to_le32(s->bs->secs);
 | 
			
		||||
    partition->end_sector_long=cpu_to_le32(s->sector_count);
 | 
			
		||||
 | 
			
		||||
    real_mbr->magic[0]=0x55; real_mbr->magic[1]=0xaa;
 | 
			
		||||
}
 | 
			
		||||
@@ -410,19 +384,17 @@ static void init_mbr(BDRVVVFATState* s)
 | 
			
		||||
/* direntry functions */
 | 
			
		||||
 | 
			
		||||
/* dest is assumed to hold 258 bytes, and pads with 0xffff up to next multiple of 26 */
 | 
			
		||||
static inline int short2long_name(char* dest,const char* src)
 | 
			
		||||
static inline int short2long_name(unsigned char* dest,const char* src)
 | 
			
		||||
{
 | 
			
		||||
    int i;
 | 
			
		||||
    int len;
 | 
			
		||||
    for(i=0;i<129 && src[i];i++) {
 | 
			
		||||
        dest[2*i]=src[i];
 | 
			
		||||
	dest[2*i+1]=0;
 | 
			
		||||
    }
 | 
			
		||||
    len=2*i;
 | 
			
		||||
    dest[2*i]=dest[2*i+1]=0;
 | 
			
		||||
    for(i=2*i+2;(i%26);i++)
 | 
			
		||||
	dest[i]=0xff;
 | 
			
		||||
    return len;
 | 
			
		||||
    return i;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline direntry_t* create_long_filename(BDRVVVFATState* s,const char* filename)
 | 
			
		||||
@@ -439,7 +411,7 @@ static inline direntry_t* create_long_filename(BDRVVVFATState* s,const char* fil
 | 
			
		||||
	entry->begin=0;
 | 
			
		||||
	entry->name[0]=(number_of_entries-i)|(i==0?0x40:0);
 | 
			
		||||
    }
 | 
			
		||||
    for(i=0;i<26*number_of_entries;i++) {
 | 
			
		||||
    for(i=0;i<length;i++) {
 | 
			
		||||
	int offset=(i%26);
 | 
			
		||||
	if(offset<10) offset=1+offset;
 | 
			
		||||
	else if(offset<22) offset=14+offset-10;
 | 
			
		||||
@@ -452,7 +424,8 @@ static inline direntry_t* create_long_filename(BDRVVVFATState* s,const char* fil
 | 
			
		||||
 | 
			
		||||
static char is_free(const direntry_t* direntry)
 | 
			
		||||
{
 | 
			
		||||
    return direntry->name[0]==0xe5 || direntry->name[0]==0x00;
 | 
			
		||||
    /* return direntry->name[0]==0 ; */
 | 
			
		||||
    return direntry->attributes == 0 || direntry->name[0]==0xe5;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static char is_volume_label(const direntry_t* direntry)
 | 
			
		||||
@@ -509,12 +482,9 @@ static inline uint8_t fat_chksum(const direntry_t* entry)
 | 
			
		||||
    uint8_t chksum=0;
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
    for(i=0;i<11;i++) {
 | 
			
		||||
        unsigned char c;
 | 
			
		||||
 | 
			
		||||
        c = (i < 8) ? entry->name[i] : entry->extension[i-8];
 | 
			
		||||
        chksum=(((chksum&0xfe)>>1)|((chksum&0x01)?0x80:0)) + c;
 | 
			
		||||
    }
 | 
			
		||||
    for(i=0;i<11;i++)
 | 
			
		||||
	chksum=(((chksum&0xfe)>>1)|((chksum&0x01)?0x80:0))
 | 
			
		||||
	    +(unsigned char)entry->name[i];
 | 
			
		||||
    
 | 
			
		||||
    return chksum;
 | 
			
		||||
}
 | 
			
		||||
@@ -567,7 +537,7 @@ static inline uint32_t fat_get(BDRVVVFATState* s,unsigned int cluster)
 | 
			
		||||
	uint16_t* entry=array_get(&(s->fat),cluster);
 | 
			
		||||
	return le16_to_cpu(*entry);
 | 
			
		||||
    } else {
 | 
			
		||||
	const uint8_t* x=(uint8_t*)(s->fat.pointer)+cluster*3/2;
 | 
			
		||||
	const uint8_t* x=s->fat.pointer+cluster*3/2;
 | 
			
		||||
	return ((x[0]|(x[1]<<8))>>(cluster&1?4:0))&0x0fff;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -607,8 +577,8 @@ static inline direntry_t* create_short_and_long_name(BDRVVVFATState* s,
 | 
			
		||||
	unsigned int directory_start, const char* filename, int is_dot)
 | 
			
		||||
{
 | 
			
		||||
    int i,j,long_index=s->directory.next;
 | 
			
		||||
    direntry_t* entry = NULL;
 | 
			
		||||
    direntry_t* entry_long = NULL;
 | 
			
		||||
    direntry_t* entry=0;
 | 
			
		||||
    direntry_t* entry_long=0;
 | 
			
		||||
 | 
			
		||||
    if(is_dot) {
 | 
			
		||||
	entry=array_get_next(&(s->directory));
 | 
			
		||||
@@ -628,7 +598,7 @@ static inline direntry_t* create_short_and_long_name(BDRVVVFATState* s,
 | 
			
		||||
 | 
			
		||||
    entry=array_get_next(&(s->directory));
 | 
			
		||||
    memset(entry->name,0x20,11);
 | 
			
		||||
    memcpy(entry->name, filename, i);
 | 
			
		||||
    strncpy(entry->name,filename,i);
 | 
			
		||||
    
 | 
			
		||||
    if(j > 0)
 | 
			
		||||
	for (i = 0; i < 3 && filename[j+1+i]; i++)
 | 
			
		||||
@@ -699,7 +669,7 @@ static int read_directory(BDRVVVFATState* s, int mapping_index)
 | 
			
		||||
    int first_cluster = mapping->begin;
 | 
			
		||||
    int parent_index = mapping->info.dir.parent_mapping_index;
 | 
			
		||||
    mapping_t* parent_mapping = (mapping_t*)
 | 
			
		||||
        (parent_index >= 0 ? array_get(&(s->mapping), parent_index) : NULL);
 | 
			
		||||
	(parent_index >= 0 ? array_get(&(s->mapping), parent_index) : 0);
 | 
			
		||||
    int first_cluster_of_parent = parent_mapping ? parent_mapping->begin : -1;
 | 
			
		||||
 | 
			
		||||
    DIR* dir=opendir(dirname);
 | 
			
		||||
@@ -728,7 +698,8 @@ static int read_directory(BDRVVVFATState* s, int mapping_index)
 | 
			
		||||
	if(first_cluster == 0 && (is_dotdot || is_dot))
 | 
			
		||||
	    continue;
 | 
			
		||||
	
 | 
			
		||||
	buffer=(char*)qemu_malloc(length);
 | 
			
		||||
	buffer=(char*)malloc(length);
 | 
			
		||||
	assert(buffer);
 | 
			
		||||
	snprintf(buffer,length,"%s/%s",dirname,entry->d_name);
 | 
			
		||||
 | 
			
		||||
	if(stat(buffer,&st)<0) {
 | 
			
		||||
@@ -849,7 +820,8 @@ static int init_directories(BDRVVVFATState* s,
 | 
			
		||||
    memset(&(s->first_sectors[0]),0,0x40*0x200);
 | 
			
		||||
 | 
			
		||||
    s->cluster_size=s->sectors_per_cluster*0x200;
 | 
			
		||||
    s->cluster_buffer=qemu_malloc(s->cluster_size);
 | 
			
		||||
    s->cluster_buffer=malloc(s->cluster_size);
 | 
			
		||||
    assert(s->cluster_buffer);
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
     * The formula: sc = spf+1+spf*spc*(512*8/fat_type),
 | 
			
		||||
@@ -868,8 +840,7 @@ static int init_directories(BDRVVVFATState* s,
 | 
			
		||||
    {
 | 
			
		||||
	direntry_t* entry=array_get_next(&(s->directory));
 | 
			
		||||
	entry->attributes=0x28; /* archive | volume label */
 | 
			
		||||
	memcpy(entry->name,"QEMU VVF",8);
 | 
			
		||||
	memcpy(entry->extension,"AT ",3);
 | 
			
		||||
	snprintf(entry->name,11,"QEMU VVFAT");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Now build FAT, and write back information into directory */
 | 
			
		||||
@@ -883,7 +854,7 @@ static int init_directories(BDRVVVFATState* s,
 | 
			
		||||
    mapping->dir_index = 0;
 | 
			
		||||
    mapping->info.dir.parent_mapping_index = -1;
 | 
			
		||||
    mapping->first_mapping_index = -1;
 | 
			
		||||
    mapping->path = qemu_strdup(dirname);
 | 
			
		||||
    mapping->path = strdup(dirname);
 | 
			
		||||
    i = strlen(mapping->path);
 | 
			
		||||
    if (i > 0 && mapping->path[i - 1] == '/')
 | 
			
		||||
	mapping->path[i - 1] = '\0';
 | 
			
		||||
@@ -892,6 +863,7 @@ static int init_directories(BDRVVVFATState* s,
 | 
			
		||||
    s->path = mapping->path;
 | 
			
		||||
 | 
			
		||||
    for (i = 0, cluster = 0; i < s->mapping.next; i++) {
 | 
			
		||||
	int j;
 | 
			
		||||
	/* MS-DOS expects the FAT to be 0 for the root directory 
 | 
			
		||||
	 * (except for the media byte). */
 | 
			
		||||
	/* LATER TODO: still true for FAT32? */
 | 
			
		||||
@@ -924,24 +896,19 @@ static int init_directories(BDRVVVFATState* s,
 | 
			
		||||
 | 
			
		||||
	assert(mapping->begin < mapping->end);
 | 
			
		||||
 | 
			
		||||
	/* fix fat for entry */
 | 
			
		||||
	if (fix_fat) {
 | 
			
		||||
	    for(j = mapping->begin; j < mapping->end - 1; j++)
 | 
			
		||||
		fat_set(s, j, j+1);
 | 
			
		||||
	    fat_set(s, mapping->end - 1, s->max_fat_value);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* next free cluster */
 | 
			
		||||
	cluster = mapping->end;
 | 
			
		||||
 | 
			
		||||
	if(cluster > s->cluster_count) {
 | 
			
		||||
	    fprintf(stderr,"Directory does not fit in FAT%d (capacity %s)\n",
 | 
			
		||||
		    s->fat_type,
 | 
			
		||||
		    s->fat_type == 12 ? s->sector_count == 2880 ? "1.44 MB"
 | 
			
		||||
								: "2.88 MB"
 | 
			
		||||
				      : "504MB");
 | 
			
		||||
	    return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* fix fat for entry */
 | 
			
		||||
	if (fix_fat) {
 | 
			
		||||
	    int j;
 | 
			
		||||
	    for(j = mapping->begin; j < mapping->end - 1; j++)
 | 
			
		||||
		fat_set(s, j, j+1);
 | 
			
		||||
	    fat_set(s, mapping->end - 1, s->max_fat_value);
 | 
			
		||||
	    fprintf(stderr,"Directory does not fit in FAT%d\n",s->fat_type);
 | 
			
		||||
	    return -1;
 | 
			
		||||
	}
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -987,22 +954,18 @@ static int init_directories(BDRVVVFATState* s,
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef DEBUG
 | 
			
		||||
static BDRVVVFATState *vvv = NULL;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static int enable_write_target(BDRVVVFATState *s);
 | 
			
		||||
static int is_consistent(BDRVVVFATState *s);
 | 
			
		||||
 | 
			
		||||
static int vvfat_open(BlockDriverState *bs, const char* dirname, int flags)
 | 
			
		||||
static int vvfat_open(BlockDriverState *bs, const char* dirname)
 | 
			
		||||
{
 | 
			
		||||
    BDRVVVFATState *s = bs->opaque;
 | 
			
		||||
    int floppy = 0;
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
#ifdef DEBUG
 | 
			
		||||
    vvv = s;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
DLOG(if (stderr == NULL) {
 | 
			
		||||
    stderr = fopen("vvfat.log", "a");
 | 
			
		||||
@@ -1013,9 +976,10 @@ DLOG(if (stderr == NULL) {
 | 
			
		||||
 | 
			
		||||
    s->fat_type=16;
 | 
			
		||||
    /* LATER TODO: if FAT32, adjust */
 | 
			
		||||
    s->sector_count=0xec04f;
 | 
			
		||||
    s->sectors_per_cluster=0x10;
 | 
			
		||||
    /* 504MB disk*/
 | 
			
		||||
    bs->cyls=1024; bs->heads=16; bs->secs=63;
 | 
			
		||||
    /* LATER TODO: this could be wrong for FAT32 */
 | 
			
		||||
    bs->cyls=1023; bs->heads=15; bs->secs=63;
 | 
			
		||||
 | 
			
		||||
    s->current_cluster=0xffffffff;
 | 
			
		||||
 | 
			
		||||
@@ -1030,6 +994,12 @@ DLOG(if (stderr == NULL) {
 | 
			
		||||
    if (!strstart(dirname, "fat:", NULL))
 | 
			
		||||
	return -1;
 | 
			
		||||
 | 
			
		||||
    if (strstr(dirname, ":rw:")) {
 | 
			
		||||
	if (enable_write_target(s))
 | 
			
		||||
	    return -1;
 | 
			
		||||
	bs->read_only = 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (strstr(dirname, ":floppy:")) {
 | 
			
		||||
	floppy = 1;
 | 
			
		||||
	s->fat_type = 12;
 | 
			
		||||
@@ -1038,8 +1008,6 @@ DLOG(if (stderr == NULL) {
 | 
			
		||||
	bs->cyls = 80; bs->heads = 2; bs->secs = 36;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    s->sector_count=bs->cyls*bs->heads*bs->secs;
 | 
			
		||||
 | 
			
		||||
    if (strstr(dirname, ":32:")) {
 | 
			
		||||
	fprintf(stderr, "Big fat greek warning: FAT32 has not been tested. You are welcome to do so!\n");
 | 
			
		||||
	s->fat_type = 32;
 | 
			
		||||
@@ -1050,27 +1018,20 @@ DLOG(if (stderr == NULL) {
 | 
			
		||||
	s->sector_count=2880;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (strstr(dirname, ":rw:")) {
 | 
			
		||||
	if (enable_write_target(s))
 | 
			
		||||
	    return -1;
 | 
			
		||||
	bs->read_only = 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    i = strrchr(dirname, ':') - dirname;
 | 
			
		||||
    assert(i >= 3);
 | 
			
		||||
    if (dirname[i-2] == ':' && qemu_isalpha(dirname[i-1]))
 | 
			
		||||
    if (dirname[i-2] == ':' && isalpha(dirname[i-1]))
 | 
			
		||||
	/* workaround for DOS drive names */
 | 
			
		||||
	dirname += i-1;
 | 
			
		||||
    else
 | 
			
		||||
	dirname += i+1;
 | 
			
		||||
 | 
			
		||||
    bs->total_sectors=bs->cyls*bs->heads*bs->secs;
 | 
			
		||||
 | 
			
		||||
    if (s->sector_count > bs->total_sectors)
 | 
			
		||||
	s->sector_count = bs->total_sectors;
 | 
			
		||||
    if(init_directories(s, dirname))
 | 
			
		||||
	return -1;
 | 
			
		||||
 | 
			
		||||
    s->sector_count = s->faked_sectors + s->sectors_per_cluster*s->cluster_count;
 | 
			
		||||
 | 
			
		||||
    if(s->first_sectors_number==0x40)
 | 
			
		||||
	init_mbr(s);
 | 
			
		||||
 | 
			
		||||
@@ -1079,6 +1040,7 @@ DLOG(if (stderr == NULL) {
 | 
			
		||||
	bs->heads = bs->cyls = bs->secs = 0;
 | 
			
		||||
 | 
			
		||||
    //    assert(is_consistent(s));
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1099,8 +1061,8 @@ static inline void vvfat_close_current_file(BDRVVVFATState *s)
 | 
			
		||||
 */
 | 
			
		||||
static inline int find_mapping_for_cluster_aux(BDRVVVFATState* s,int cluster_num,int index1,int index2)
 | 
			
		||||
{
 | 
			
		||||
    int index3=index1+1;
 | 
			
		||||
    while(1) {
 | 
			
		||||
        int index3;
 | 
			
		||||
	mapping_t* mapping;
 | 
			
		||||
	index3=(index1+index2)/2;
 | 
			
		||||
	mapping=array_get(&(s->mapping),index3);
 | 
			
		||||
@@ -1129,10 +1091,10 @@ static inline mapping_t* find_mapping_for_cluster(BDRVVVFATState* s,int cluster_
 | 
			
		||||
    int index=find_mapping_for_cluster_aux(s,cluster_num,0,s->mapping.next);
 | 
			
		||||
    mapping_t* mapping;
 | 
			
		||||
    if(index>=s->mapping.next)
 | 
			
		||||
        return NULL;
 | 
			
		||||
	return 0;
 | 
			
		||||
    mapping=array_get(&(s->mapping),index);
 | 
			
		||||
    if(mapping->begin>cluster_num)
 | 
			
		||||
        return NULL;
 | 
			
		||||
	return 0;
 | 
			
		||||
    assert(mapping->begin<=cluster_num && mapping->end>cluster_num);
 | 
			
		||||
    return mapping;
 | 
			
		||||
}
 | 
			
		||||
@@ -1192,7 +1154,7 @@ static inline int read_cluster(BDRVVVFATState *s,int cluster_num)
 | 
			
		||||
		s->current_mapping = mapping;
 | 
			
		||||
read_cluster_directory:
 | 
			
		||||
		offset = s->cluster_size*(cluster_num-s->current_mapping->begin);
 | 
			
		||||
		s->cluster = (unsigned char*)s->directory.pointer+offset
 | 
			
		||||
		s->cluster = s->directory.pointer+offset
 | 
			
		||||
			+ 0x20*s->current_mapping->info.dir.first_dir_index;
 | 
			
		||||
		assert(((s->cluster-(unsigned char*)s->directory.pointer)%s->cluster_size)==0);
 | 
			
		||||
		assert((char*)s->cluster+s->cluster_size <= s->directory.pointer+s->directory.next*s->directory.item_size);
 | 
			
		||||
@@ -1244,14 +1206,14 @@ static void print_direntry(const direntry_t* direntry)
 | 
			
		||||
    int j = 0;
 | 
			
		||||
    char buffer[1024];
 | 
			
		||||
 | 
			
		||||
    fprintf(stderr, "direntry %p: ", direntry);
 | 
			
		||||
    fprintf(stderr, "direntry 0x%x: ", (int)direntry);
 | 
			
		||||
    if(!direntry)
 | 
			
		||||
	return;
 | 
			
		||||
    if(is_long_name(direntry)) {
 | 
			
		||||
	unsigned char* c=(unsigned char*)direntry;
 | 
			
		||||
	int i;
 | 
			
		||||
	for(i=1;i<11 && c[i] && c[i]!=0xff;i+=2)
 | 
			
		||||
#define ADD_CHAR(c) {buffer[j] = (c); if (buffer[j] < ' ') buffer[j] = 0xb0; j++;}
 | 
			
		||||
#define ADD_CHAR(c) {buffer[j] = (c); if (buffer[j] < ' ') buffer[j] = '°'; j++;}
 | 
			
		||||
	    ADD_CHAR(c[i]);
 | 
			
		||||
	for(i=14;i<26 && c[i] && c[i]!=0xff;i+=2)
 | 
			
		||||
	    ADD_CHAR(c[i]);
 | 
			
		||||
@@ -1273,11 +1235,7 @@ static void print_direntry(const direntry_t* direntry)
 | 
			
		||||
 | 
			
		||||
static void print_mapping(const mapping_t* mapping)
 | 
			
		||||
{
 | 
			
		||||
    fprintf(stderr, "mapping (%p): begin, end = %d, %d, dir_index = %d, "
 | 
			
		||||
        "first_mapping_index = %d, name = %s, mode = 0x%x, " ,
 | 
			
		||||
        mapping, mapping->begin, mapping->end, mapping->dir_index,
 | 
			
		||||
        mapping->first_mapping_index, mapping->path, mapping->mode);
 | 
			
		||||
 | 
			
		||||
    fprintf(stderr, "mapping (0x%x): begin, end = %d, %d, dir_index = %d, first_mapping_index = %d, name = %s, mode = 0x%x, " , (int)mapping, mapping->begin, mapping->end, mapping->dir_index, mapping->first_mapping_index, mapping->path, mapping->mode);
 | 
			
		||||
    if (mapping->mode & MODE_DIRECTORY)
 | 
			
		||||
	fprintf(stderr, "parent_mapping_index = %d, first_dir_index = %d\n", mapping->info.dir.parent_mapping_index, mapping->info.dir.first_dir_index);
 | 
			
		||||
    else
 | 
			
		||||
@@ -1418,12 +1376,7 @@ static void schedule_mkdir(BDRVVVFATState* s, uint32_t cluster, char* path)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    /*
 | 
			
		||||
     * Since the sequence number is at most 0x3f, and the filename
 | 
			
		||||
     * length is at most 13 times the sequence number, the maximal
 | 
			
		||||
     * filename length is 0x3f * 13 bytes.
 | 
			
		||||
     */
 | 
			
		||||
    unsigned char name[0x3f * 13 + 1];
 | 
			
		||||
    unsigned char name[1024];
 | 
			
		||||
    int checksum, len;
 | 
			
		||||
    int sequence_number;
 | 
			
		||||
} long_file_name;
 | 
			
		||||
@@ -1448,7 +1401,6 @@ static int parse_long_name(long_file_name* lfn,
 | 
			
		||||
	lfn->sequence_number = pointer[0] & 0x3f;
 | 
			
		||||
	lfn->checksum = pointer[13];
 | 
			
		||||
	lfn->name[0] = 0;
 | 
			
		||||
	lfn->name[lfn->sequence_number * 13] = 0;
 | 
			
		||||
    } else if ((pointer[0] & 0x3f) != --lfn->sequence_number)
 | 
			
		||||
	return -1;
 | 
			
		||||
    else if (pointer[13] != lfn->checksum)
 | 
			
		||||
@@ -1472,7 +1424,7 @@ static int parse_long_name(long_file_name* lfn,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (pointer[0] & 0x40)
 | 
			
		||||
	lfn->len = offset + strlen((char*)lfn->name + offset);
 | 
			
		||||
	lfn->len = offset + strlen(lfn->name + offset);
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
@@ -1491,7 +1443,7 @@ static int parse_short_name(BDRVVVFATState* s,
 | 
			
		||||
	if (direntry->name[i] <= ' ' || direntry->name[i] > 0x7f)
 | 
			
		||||
	    return -1;
 | 
			
		||||
	else if (s->downcase_short_names)
 | 
			
		||||
	    lfn->name[i] = qemu_tolower(direntry->name[i]);
 | 
			
		||||
	    lfn->name[i] = tolower(direntry->name[i]);
 | 
			
		||||
	else
 | 
			
		||||
	    lfn->name[i] = direntry->name[i];
 | 
			
		||||
    }
 | 
			
		||||
@@ -1504,14 +1456,14 @@ static int parse_short_name(BDRVVVFATState* s,
 | 
			
		||||
	    if (direntry->extension[j] <= ' ' || direntry->extension[j] > 0x7f)
 | 
			
		||||
		return -2;
 | 
			
		||||
	    else if (s->downcase_short_names)
 | 
			
		||||
		lfn->name[i + j] = qemu_tolower(direntry->extension[j]);
 | 
			
		||||
		lfn->name[i + j] = tolower(direntry->extension[j]);
 | 
			
		||||
	    else
 | 
			
		||||
		lfn->name[i + j] = direntry->extension[j];
 | 
			
		||||
	}
 | 
			
		||||
    } else
 | 
			
		||||
	lfn->name[i + j + 1] = '\0';
 | 
			
		||||
 | 
			
		||||
    lfn->len = strlen((char*)lfn->name);
 | 
			
		||||
    lfn->len = strlen(lfn->name);
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
@@ -1637,12 +1589,12 @@ static uint32_t get_cluster_count_for_direntry(BDRVVVFATState* s,
 | 
			
		||||
 | 
			
		||||
	    /* rename */
 | 
			
		||||
	    if (strcmp(basename, basename2))
 | 
			
		||||
		schedule_rename(s, cluster_num, qemu_strdup(path));
 | 
			
		||||
		schedule_rename(s, cluster_num, strdup(path));
 | 
			
		||||
	} else if (is_file(direntry))
 | 
			
		||||
	    /* new file */
 | 
			
		||||
	    schedule_new_file(s, qemu_strdup(path), cluster_num);
 | 
			
		||||
	    schedule_new_file(s, strdup(path), cluster_num);
 | 
			
		||||
	else {
 | 
			
		||||
            abort();
 | 
			
		||||
	    assert(0);
 | 
			
		||||
	    return 0;
 | 
			
		||||
	}
 | 
			
		||||
    }
 | 
			
		||||
@@ -1663,7 +1615,7 @@ static uint32_t get_cluster_count_for_direntry(BDRVVVFATState* s,
 | 
			
		||||
		    if (offset != mapping->info.file.offset + s->cluster_size
 | 
			
		||||
			    * (cluster_num - mapping->begin)) {
 | 
			
		||||
			/* offset of this cluster in file chain has changed */
 | 
			
		||||
                        abort();
 | 
			
		||||
			assert(0);
 | 
			
		||||
			copy_it = 1;
 | 
			
		||||
		    } else if (offset == 0) {
 | 
			
		||||
			const char* basename = get_basename(mapping->path);
 | 
			
		||||
@@ -1675,7 +1627,7 @@ static uint32_t get_cluster_count_for_direntry(BDRVVVFATState* s,
 | 
			
		||||
 | 
			
		||||
		    if (mapping->first_mapping_index != first_mapping_index
 | 
			
		||||
			    && mapping->info.file.offset > 0) {
 | 
			
		||||
                        abort();
 | 
			
		||||
			assert(0);
 | 
			
		||||
			copy_it = 1;
 | 
			
		||||
		    }
 | 
			
		||||
 | 
			
		||||
@@ -1734,7 +1686,7 @@ static int check_directory_consistency(BDRVVVFATState *s,
 | 
			
		||||
	int cluster_num, const char* path)
 | 
			
		||||
{
 | 
			
		||||
    int ret = 0;
 | 
			
		||||
    unsigned char* cluster = qemu_malloc(s->cluster_size);
 | 
			
		||||
    unsigned char* cluster = malloc(s->cluster_size);
 | 
			
		||||
    direntry_t* direntries = (direntry_t*)cluster;
 | 
			
		||||
    mapping_t* mapping = find_mapping_for_cluster(s, cluster_num);
 | 
			
		||||
 | 
			
		||||
@@ -1743,7 +1695,7 @@ static int check_directory_consistency(BDRVVVFATState *s,
 | 
			
		||||
    char path2[PATH_MAX];
 | 
			
		||||
 | 
			
		||||
    assert(path_len < PATH_MAX); /* len was tested before! */
 | 
			
		||||
    pstrcpy(path2, sizeof(path2), path);
 | 
			
		||||
    strcpy(path2, path);
 | 
			
		||||
    path2[path_len] = '/';
 | 
			
		||||
    path2[path_len + 1] = '\0';
 | 
			
		||||
 | 
			
		||||
@@ -1757,10 +1709,10 @@ static int check_directory_consistency(BDRVVVFATState *s,
 | 
			
		||||
	mapping->mode &= ~MODE_DELETED;
 | 
			
		||||
 | 
			
		||||
	if (strcmp(basename, basename2))
 | 
			
		||||
	    schedule_rename(s, cluster_num, qemu_strdup(path));
 | 
			
		||||
	    schedule_rename(s, cluster_num, strdup(path));
 | 
			
		||||
    } else
 | 
			
		||||
	/* new directory */
 | 
			
		||||
	schedule_mkdir(s, cluster_num, qemu_strdup(path));
 | 
			
		||||
	schedule_mkdir(s, cluster_num, strdup(path));
 | 
			
		||||
		
 | 
			
		||||
    lfn_init(&lfn);
 | 
			
		||||
    do {
 | 
			
		||||
@@ -1786,7 +1738,7 @@ DLOG(fprintf(stderr, "read cluster %d (sector %d)\n", (int)cluster_num, (int)clu
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < 0x10 * s->sectors_per_cluster; i++) {
 | 
			
		||||
	    int cluster_count = 0;
 | 
			
		||||
	    int cluster_count;
 | 
			
		||||
 | 
			
		||||
DLOG(fprintf(stderr, "check direntry %d: \n", i); print_direntry(direntries + i));
 | 
			
		||||
	    if (is_volume_label(direntries + i) || is_dot(direntries + i) ||
 | 
			
		||||
@@ -1807,8 +1759,8 @@ DLOG(fprintf(stderr, "check direntry %d: \n", i); print_direntry(direntries + i)
 | 
			
		||||
		    fprintf(stderr, "Error in short name (%d)\n", subret);
 | 
			
		||||
		    goto fail;
 | 
			
		||||
		}
 | 
			
		||||
		if (subret > 0 || !strcmp((char*)lfn.name, ".")
 | 
			
		||||
			|| !strcmp((char*)lfn.name, ".."))
 | 
			
		||||
		if (subret > 0 || !strcmp(lfn.name, ".")
 | 
			
		||||
			|| !strcmp(lfn.name, ".."))
 | 
			
		||||
		    continue;
 | 
			
		||||
	    }
 | 
			
		||||
	    lfn.checksum = 0x100; /* cannot use long name twice */
 | 
			
		||||
@@ -1817,8 +1769,7 @@ DLOG(fprintf(stderr, "check direntry %d: \n", i); print_direntry(direntries + i)
 | 
			
		||||
		fprintf(stderr, "Name too long: %s/%s\n", path, lfn.name);
 | 
			
		||||
		goto fail;
 | 
			
		||||
	    }
 | 
			
		||||
            pstrcpy(path2 + path_len + 1, sizeof(path2) - path_len - 1,
 | 
			
		||||
                    (char*)lfn.name);
 | 
			
		||||
	    strcpy(path2 + path_len + 1, lfn.name);
 | 
			
		||||
 | 
			
		||||
	    if (is_directory(direntries + i)) {
 | 
			
		||||
		if (begin_of_direntry(direntries + i) == 0) {
 | 
			
		||||
@@ -1841,7 +1792,7 @@ DLOG(fprintf(stderr, "check direntry %d: \n", i); print_direntry(direntries + i)
 | 
			
		||||
		    goto fail;
 | 
			
		||||
		}
 | 
			
		||||
	    } else
 | 
			
		||||
                abort(); /* cluster_count = 0; */
 | 
			
		||||
		assert(0); /* cluster_count = 0; */
 | 
			
		||||
 | 
			
		||||
	    ret += cluster_count;
 | 
			
		||||
	}
 | 
			
		||||
@@ -1875,7 +1826,7 @@ DLOG(checkpoint());
 | 
			
		||||
     */
 | 
			
		||||
    if (s->fat2 == NULL) {
 | 
			
		||||
	int size = 0x200 * s->sectors_per_fat;
 | 
			
		||||
	s->fat2 = qemu_malloc(size);
 | 
			
		||||
	s->fat2 = malloc(size);
 | 
			
		||||
	memcpy(s->fat2, s->fat.pointer, size);
 | 
			
		||||
    }
 | 
			
		||||
    check = vvfat_read(s->bs,
 | 
			
		||||
@@ -2217,7 +2168,7 @@ static int commit_one_file(BDRVVVFATState* s,
 | 
			
		||||
    uint32_t first_cluster = c;
 | 
			
		||||
    mapping_t* mapping = find_mapping_for_cluster(s, c);
 | 
			
		||||
    uint32_t size = filesize_of_direntry(direntry);
 | 
			
		||||
    char* cluster = qemu_malloc(s->cluster_size);
 | 
			
		||||
    char* cluster = malloc(s->cluster_size);
 | 
			
		||||
    uint32_t i;
 | 
			
		||||
    int fd = 0;
 | 
			
		||||
 | 
			
		||||
@@ -2227,7 +2178,7 @@ static int commit_one_file(BDRVVVFATState* s,
 | 
			
		||||
    for (i = s->cluster_size; i < offset; i += s->cluster_size)
 | 
			
		||||
	c = modified_fat_get(s, c);
 | 
			
		||||
 | 
			
		||||
    fd = open(mapping->path, O_RDWR | O_CREAT | O_BINARY, 0666);
 | 
			
		||||
    fd = open(mapping->path, O_RDWR | O_CREAT, 0666);
 | 
			
		||||
    if (fd < 0) {
 | 
			
		||||
	fprintf(stderr, "Could not open %s... (%s, %d)\n", mapping->path,
 | 
			
		||||
		strerror(errno), errno);
 | 
			
		||||
@@ -2247,9 +2198,10 @@ static int commit_one_file(BDRVVVFATState* s,
 | 
			
		||||
 | 
			
		||||
	assert((size - offset == 0 && fat_eof(s, c)) ||
 | 
			
		||||
		(size > offset && c >=2 && !fat_eof(s, c)));
 | 
			
		||||
	assert(size >= 0);
 | 
			
		||||
 | 
			
		||||
	ret = vvfat_read(s->bs, cluster2sector(s, c),
 | 
			
		||||
	    (uint8_t*)cluster, (rest_size + 0x1ff) / 0x200);
 | 
			
		||||
	    cluster, (rest_size + 0x1ff) / 0x200);
 | 
			
		||||
 | 
			
		||||
	if (ret < 0)
 | 
			
		||||
	    return ret;
 | 
			
		||||
@@ -2261,11 +2213,7 @@ static int commit_one_file(BDRVVVFATState* s,
 | 
			
		||||
	c = c1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (ftruncate(fd, size)) {
 | 
			
		||||
        perror("ftruncate()");
 | 
			
		||||
        close(fd);
 | 
			
		||||
        return -4;
 | 
			
		||||
    }
 | 
			
		||||
    ftruncate(fd, size);
 | 
			
		||||
    close(fd);
 | 
			
		||||
 | 
			
		||||
    return commit_mappings(s, first_cluster, dir_index);
 | 
			
		||||
@@ -2383,13 +2331,12 @@ static int handle_renames_and_mkdirs(BDRVVVFATState* s)
 | 
			
		||||
			    mapping_t* m = find_mapping_for_cluster(s,
 | 
			
		||||
				    begin_of_direntry(d));
 | 
			
		||||
			    int l = strlen(m->path);
 | 
			
		||||
			    char* new_path = qemu_malloc(l + diff + 1);
 | 
			
		||||
			    char* new_path = malloc(l + diff + 1);
 | 
			
		||||
 | 
			
		||||
			    assert(!strncmp(m->path, mapping->path, l2));
 | 
			
		||||
 | 
			
		||||
                            pstrcpy(new_path, l + diff + 1, mapping->path);
 | 
			
		||||
                            pstrcpy(new_path + l1, l + diff + 1 - l1,
 | 
			
		||||
                                    m->path + l2);
 | 
			
		||||
			    strcpy(new_path, mapping->path);
 | 
			
		||||
			    strcpy(new_path + l1, m->path + l2);
 | 
			
		||||
 | 
			
		||||
			    schedule_rename(s, m->begin, new_path);
 | 
			
		||||
			}
 | 
			
		||||
@@ -2462,17 +2409,14 @@ static int handle_commits(BDRVVVFATState* s)
 | 
			
		||||
	commit_t* commit = array_get(&(s->commits), i);
 | 
			
		||||
	switch(commit->action) {
 | 
			
		||||
	case ACTION_RENAME: case ACTION_MKDIR:
 | 
			
		||||
            abort();
 | 
			
		||||
	    assert(0);
 | 
			
		||||
	    fail = -2;
 | 
			
		||||
	    break;
 | 
			
		||||
	case ACTION_WRITEOUT: {
 | 
			
		||||
#ifndef NDEBUG
 | 
			
		||||
            /* these variables are only used by assert() below */
 | 
			
		||||
	    direntry_t* entry = array_get(&(s->directory),
 | 
			
		||||
		    commit->param.writeout.dir_index);
 | 
			
		||||
	    uint32_t begin = begin_of_direntry(entry);
 | 
			
		||||
	    mapping_t* mapping = find_mapping_for_cluster(s, begin);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	    assert(mapping);
 | 
			
		||||
	    assert(mapping->begin == begin);
 | 
			
		||||
@@ -2523,7 +2467,7 @@ static int handle_commits(BDRVVVFATState* s)
 | 
			
		||||
	    break;
 | 
			
		||||
	}
 | 
			
		||||
	default:
 | 
			
		||||
            abort();
 | 
			
		||||
	    assert(0);
 | 
			
		||||
	}
 | 
			
		||||
    }
 | 
			
		||||
    if (i > 0 && array_remove_slice(&(s->commits), 0, i))
 | 
			
		||||
@@ -2611,7 +2555,7 @@ static int do_commit(BDRVVVFATState* s)
 | 
			
		||||
    ret = handle_renames_and_mkdirs(s);
 | 
			
		||||
    if (ret) {
 | 
			
		||||
	fprintf(stderr, "Error handling renames (%d)\n", ret);
 | 
			
		||||
        abort();
 | 
			
		||||
	assert(0);
 | 
			
		||||
	return ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -2622,21 +2566,21 @@ static int do_commit(BDRVVVFATState* s)
 | 
			
		||||
    ret = commit_direntries(s, 0, -1);
 | 
			
		||||
    if (ret) {
 | 
			
		||||
	fprintf(stderr, "Fatal: error while committing (%d)\n", ret);
 | 
			
		||||
        abort();
 | 
			
		||||
	assert(0);
 | 
			
		||||
	return ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ret = handle_commits(s);
 | 
			
		||||
    if (ret) {
 | 
			
		||||
	fprintf(stderr, "Error handling commits (%d)\n", ret);
 | 
			
		||||
        abort();
 | 
			
		||||
	assert(0);
 | 
			
		||||
	return ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ret = handle_deletes(s);
 | 
			
		||||
    if (ret) {
 | 
			
		||||
	fprintf(stderr, "Error deleting\n");
 | 
			
		||||
        abort();
 | 
			
		||||
        assert(0);
 | 
			
		||||
	return ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -2665,11 +2609,6 @@ static int vvfat_write(BlockDriverState *bs, int64_t sector_num,
 | 
			
		||||
 | 
			
		||||
DLOG(checkpoint());
 | 
			
		||||
 | 
			
		||||
    /* Check if we're operating in read-only mode */
 | 
			
		||||
    if (s->qcow == NULL) {
 | 
			
		||||
        return -EACCES;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    vvfat_close_current_file(s);
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
@@ -2768,53 +2707,39 @@ static int vvfat_is_allocated(BlockDriverState *bs,
 | 
			
		||||
 | 
			
		||||
static int write_target_commit(BlockDriverState *bs, int64_t sector_num,
 | 
			
		||||
	const uint8_t* buffer, int nb_sectors) {
 | 
			
		||||
    BDRVVVFATState* s = *((BDRVVVFATState**) bs->opaque);
 | 
			
		||||
    BDRVVVFATState* s = bs->opaque;
 | 
			
		||||
    return try_commit(s);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void write_target_close(BlockDriverState *bs) {
 | 
			
		||||
    BDRVVVFATState* s = *((BDRVVVFATState**) bs->opaque);
 | 
			
		||||
    BDRVVVFATState* s = bs->opaque;
 | 
			
		||||
    bdrv_delete(s->qcow);
 | 
			
		||||
    free(s->qcow_filename);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static BlockDriver vvfat_write_target = {
 | 
			
		||||
    .format_name        = "vvfat_write_target",
 | 
			
		||||
    .bdrv_write         = write_target_commit,
 | 
			
		||||
    .bdrv_close         = write_target_close,
 | 
			
		||||
    "vvfat_write_target", 0, NULL, NULL, NULL,
 | 
			
		||||
    write_target_commit,
 | 
			
		||||
    write_target_close,
 | 
			
		||||
    NULL, NULL, NULL
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int enable_write_target(BDRVVVFATState *s)
 | 
			
		||||
{
 | 
			
		||||
    BlockDriver *bdrv_qcow;
 | 
			
		||||
    QEMUOptionParameter *options;
 | 
			
		||||
    int ret;
 | 
			
		||||
    int size = sector2cluster(s, s->sector_count);
 | 
			
		||||
    s->used_clusters = calloc(size, 1);
 | 
			
		||||
 | 
			
		||||
    array_init(&(s->commits), sizeof(commit_t));
 | 
			
		||||
 | 
			
		||||
    s->qcow_filename = qemu_malloc(1024);
 | 
			
		||||
    get_tmp_filename(s->qcow_filename, 1024);
 | 
			
		||||
 | 
			
		||||
    bdrv_qcow = bdrv_find_format("qcow");
 | 
			
		||||
    options = parse_option_parameters("", bdrv_qcow->create_options, NULL);
 | 
			
		||||
    set_option_parameter_int(options, BLOCK_OPT_SIZE, s->sector_count * 512);
 | 
			
		||||
    set_option_parameter(options, BLOCK_OPT_BACKING_FILE, "fat:");
 | 
			
		||||
 | 
			
		||||
    if (bdrv_create(bdrv_qcow, s->qcow_filename, options) < 0)
 | 
			
		||||
    s->qcow_filename = malloc(1024);
 | 
			
		||||
    strcpy(s->qcow_filename, "/tmp/vl.XXXXXX");
 | 
			
		||||
    get_tmp_filename(s->qcow_filename, strlen(s->qcow_filename) + 1);
 | 
			
		||||
    if (bdrv_create(&bdrv_qcow,
 | 
			
		||||
		s->qcow_filename, s->sector_count, "fat:", 0) < 0)
 | 
			
		||||
	return -1;
 | 
			
		||||
 | 
			
		||||
    s->qcow = bdrv_new("");
 | 
			
		||||
    if (s->qcow == NULL) {
 | 
			
		||||
    if (s->qcow == NULL || bdrv_open(s->qcow, s->qcow_filename, 0) < 0)
 | 
			
		||||
	return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ret = bdrv_open(s->qcow, s->qcow_filename,
 | 
			
		||||
            BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH, bdrv_qcow);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
	return ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#ifndef _WIN32
 | 
			
		||||
    unlink(s->qcow_filename);
 | 
			
		||||
@@ -2822,8 +2747,7 @@ static int enable_write_target(BDRVVVFATState *s)
 | 
			
		||||
 | 
			
		||||
    s->bs->backing_hd = calloc(sizeof(BlockDriverState), 1);
 | 
			
		||||
    s->bs->backing_hd->drv = &vvfat_write_target;
 | 
			
		||||
    s->bs->backing_hd->opaque = qemu_malloc(sizeof(void*));
 | 
			
		||||
    *(void**)s->bs->backing_hd->opaque = s;
 | 
			
		||||
    s->bs->backing_hd->opaque = s;
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
@@ -2840,26 +2764,20 @@ static void vvfat_close(BlockDriverState *bs)
 | 
			
		||||
        free(s->cluster_buffer);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static BlockDriver bdrv_vvfat = {
 | 
			
		||||
    .format_name	= "vvfat",
 | 
			
		||||
    .instance_size	= sizeof(BDRVVVFATState),
 | 
			
		||||
    .bdrv_file_open	= vvfat_open,
 | 
			
		||||
    .bdrv_read		= vvfat_read,
 | 
			
		||||
    .bdrv_write		= vvfat_write,
 | 
			
		||||
    .bdrv_close		= vvfat_close,
 | 
			
		||||
    .bdrv_is_allocated	= vvfat_is_allocated,
 | 
			
		||||
    .protocol_name	= "fat",
 | 
			
		||||
BlockDriver bdrv_vvfat = {
 | 
			
		||||
    "vvfat",
 | 
			
		||||
    sizeof(BDRVVVFATState),
 | 
			
		||||
    vvfat_probe,
 | 
			
		||||
    vvfat_open,
 | 
			
		||||
    vvfat_read,
 | 
			
		||||
    vvfat_write,
 | 
			
		||||
    vvfat_close,
 | 
			
		||||
    NULL,
 | 
			
		||||
    vvfat_is_allocated
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void bdrv_vvfat_init(void)
 | 
			
		||||
{
 | 
			
		||||
    bdrv_register(&bdrv_vvfat);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
block_init(bdrv_vvfat_init);
 | 
			
		||||
 | 
			
		||||
#ifdef DEBUG
 | 
			
		||||
static void checkpoint(void) {
 | 
			
		||||
static void checkpoint() {
 | 
			
		||||
    assert(((mapping_t*)array_get(&(vvv->mapping), 0))->end == 2);
 | 
			
		||||
    check1(vvv);
 | 
			
		||||
    check2(vvv);
 | 
			
		||||
@@ -2881,8 +2799,9 @@ static void checkpoint(void) {
 | 
			
		||||
    return;
 | 
			
		||||
    /* avoid compiler warnings: */
 | 
			
		||||
    hexdump(NULL, 100);
 | 
			
		||||
    remove_mapping(vvv, 0);
 | 
			
		||||
    remove_mapping(vvv, NULL);
 | 
			
		||||
    print_mapping(NULL);
 | 
			
		||||
    print_direntry(NULL);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										288
									
								
								block.h
									
									
									
									
									
								
							
							
						
						
									
										288
									
								
								block.h
									
									
									
									
									
								
							@@ -1,288 +0,0 @@
 | 
			
		||||
#ifndef BLOCK_H
 | 
			
		||||
#define BLOCK_H
 | 
			
		||||
 | 
			
		||||
#include "qemu-aio.h"
 | 
			
		||||
#include "qemu-common.h"
 | 
			
		||||
#include "qemu-option.h"
 | 
			
		||||
#include "qobject.h"
 | 
			
		||||
 | 
			
		||||
/* block.c */
 | 
			
		||||
typedef struct BlockDriver BlockDriver;
 | 
			
		||||
 | 
			
		||||
typedef struct BlockDriverInfo {
 | 
			
		||||
    /* in bytes, 0 if irrelevant */
 | 
			
		||||
    int cluster_size;
 | 
			
		||||
    /* offset at which the VM state can be saved (0 if not possible) */
 | 
			
		||||
    int64_t vm_state_offset;
 | 
			
		||||
} BlockDriverInfo;
 | 
			
		||||
 | 
			
		||||
typedef struct QEMUSnapshotInfo {
 | 
			
		||||
    char id_str[128]; /* unique snapshot id */
 | 
			
		||||
    /* the following fields are informative. They are not needed for
 | 
			
		||||
       the consistency of the snapshot */
 | 
			
		||||
    char name[256]; /* user choosen name */
 | 
			
		||||
    uint32_t vm_state_size; /* VM state info size */
 | 
			
		||||
    uint32_t date_sec; /* UTC date of the snapshot */
 | 
			
		||||
    uint32_t date_nsec;
 | 
			
		||||
    uint64_t vm_clock_nsec; /* VM clock relative to boot */
 | 
			
		||||
} QEMUSnapshotInfo;
 | 
			
		||||
 | 
			
		||||
#define BDRV_O_RDWR        0x0002
 | 
			
		||||
#define BDRV_O_SNAPSHOT    0x0008 /* open the file read only and save writes in a snapshot */
 | 
			
		||||
#define BDRV_O_NOCACHE     0x0020 /* do not use the host page cache */
 | 
			
		||||
#define BDRV_O_CACHE_WB    0x0040 /* use write-back caching */
 | 
			
		||||
#define BDRV_O_NATIVE_AIO  0x0080 /* use native AIO instead of the thread pool */
 | 
			
		||||
#define BDRV_O_NO_BACKING  0x0100 /* don't open the backing file */
 | 
			
		||||
#define BDRV_O_NO_FLUSH    0x0200 /* disable flushing on this disk */
 | 
			
		||||
 | 
			
		||||
#define BDRV_O_CACHE_MASK  (BDRV_O_NOCACHE | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH)
 | 
			
		||||
 | 
			
		||||
#define BDRV_SECTOR_BITS   9
 | 
			
		||||
#define BDRV_SECTOR_SIZE   (1ULL << BDRV_SECTOR_BITS)
 | 
			
		||||
#define BDRV_SECTOR_MASK   ~(BDRV_SECTOR_SIZE - 1)
 | 
			
		||||
 | 
			
		||||
typedef enum {
 | 
			
		||||
    BLOCK_ERR_REPORT, BLOCK_ERR_IGNORE, BLOCK_ERR_STOP_ENOSPC,
 | 
			
		||||
    BLOCK_ERR_STOP_ANY
 | 
			
		||||
} BlockErrorAction;
 | 
			
		||||
 | 
			
		||||
typedef enum {
 | 
			
		||||
    BDRV_ACTION_REPORT, BDRV_ACTION_IGNORE, BDRV_ACTION_STOP
 | 
			
		||||
} BlockMonEventAction;
 | 
			
		||||
 | 
			
		||||
void bdrv_mon_event(const BlockDriverState *bdrv,
 | 
			
		||||
                    BlockMonEventAction action, int is_read);
 | 
			
		||||
void bdrv_info_print(Monitor *mon, const QObject *data);
 | 
			
		||||
void bdrv_info(Monitor *mon, QObject **ret_data);
 | 
			
		||||
void bdrv_stats_print(Monitor *mon, const QObject *data);
 | 
			
		||||
void bdrv_info_stats(Monitor *mon, QObject **ret_data);
 | 
			
		||||
 | 
			
		||||
void bdrv_init(void);
 | 
			
		||||
void bdrv_init_with_whitelist(void);
 | 
			
		||||
BlockDriver *bdrv_find_protocol(const char *filename);
 | 
			
		||||
BlockDriver *bdrv_find_format(const char *format_name);
 | 
			
		||||
BlockDriver *bdrv_find_whitelisted_format(const char *format_name);
 | 
			
		||||
int bdrv_create(BlockDriver *drv, const char* filename,
 | 
			
		||||
    QEMUOptionParameter *options);
 | 
			
		||||
int bdrv_create_file(const char* filename, QEMUOptionParameter *options);
 | 
			
		||||
BlockDriverState *bdrv_new(const char *device_name);
 | 
			
		||||
void bdrv_delete(BlockDriverState *bs);
 | 
			
		||||
int bdrv_file_open(BlockDriverState **pbs, const char *filename, int flags);
 | 
			
		||||
int bdrv_open(BlockDriverState *bs, const char *filename, int flags,
 | 
			
		||||
              BlockDriver *drv);
 | 
			
		||||
void bdrv_close(BlockDriverState *bs);
 | 
			
		||||
int bdrv_attach(BlockDriverState *bs, DeviceState *qdev);
 | 
			
		||||
void bdrv_detach(BlockDriverState *bs, DeviceState *qdev);
 | 
			
		||||
DeviceState *bdrv_get_attached(BlockDriverState *bs);
 | 
			
		||||
int bdrv_read(BlockDriverState *bs, int64_t sector_num,
 | 
			
		||||
              uint8_t *buf, int nb_sectors);
 | 
			
		||||
int bdrv_write(BlockDriverState *bs, int64_t sector_num,
 | 
			
		||||
               const uint8_t *buf, int nb_sectors);
 | 
			
		||||
int bdrv_pread(BlockDriverState *bs, int64_t offset,
 | 
			
		||||
               void *buf, int count);
 | 
			
		||||
int bdrv_pwrite(BlockDriverState *bs, int64_t offset,
 | 
			
		||||
                const void *buf, int count);
 | 
			
		||||
int bdrv_pwrite_sync(BlockDriverState *bs, int64_t offset,
 | 
			
		||||
    const void *buf, int count);
 | 
			
		||||
int bdrv_write_sync(BlockDriverState *bs, int64_t sector_num,
 | 
			
		||||
    const uint8_t *buf, int nb_sectors);
 | 
			
		||||
int bdrv_truncate(BlockDriverState *bs, int64_t offset);
 | 
			
		||||
int64_t bdrv_getlength(BlockDriverState *bs);
 | 
			
		||||
void bdrv_get_geometry(BlockDriverState *bs, uint64_t *nb_sectors_ptr);
 | 
			
		||||
void bdrv_guess_geometry(BlockDriverState *bs, int *pcyls, int *pheads, int *psecs);
 | 
			
		||||
int bdrv_commit(BlockDriverState *bs);
 | 
			
		||||
void bdrv_commit_all(void);
 | 
			
		||||
int bdrv_change_backing_file(BlockDriverState *bs,
 | 
			
		||||
    const char *backing_file, const char *backing_fmt);
 | 
			
		||||
void bdrv_register(BlockDriver *bdrv);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
typedef struct BdrvCheckResult {
 | 
			
		||||
    int corruptions;
 | 
			
		||||
    int leaks;
 | 
			
		||||
    int check_errors;
 | 
			
		||||
} BdrvCheckResult;
 | 
			
		||||
 | 
			
		||||
int bdrv_check(BlockDriverState *bs, BdrvCheckResult *res);
 | 
			
		||||
 | 
			
		||||
/* async block I/O */
 | 
			
		||||
typedef struct BlockDriverAIOCB BlockDriverAIOCB;
 | 
			
		||||
typedef void BlockDriverCompletionFunc(void *opaque, int ret);
 | 
			
		||||
typedef void BlockDriverDirtyHandler(BlockDriverState *bs, int64_t sector,
 | 
			
		||||
				     int sector_num);
 | 
			
		||||
BlockDriverAIOCB *bdrv_aio_readv(BlockDriverState *bs, int64_t sector_num,
 | 
			
		||||
                                 QEMUIOVector *iov, int nb_sectors,
 | 
			
		||||
                                 BlockDriverCompletionFunc *cb, void *opaque);
 | 
			
		||||
BlockDriverAIOCB *bdrv_aio_writev(BlockDriverState *bs, int64_t sector_num,
 | 
			
		||||
                                  QEMUIOVector *iov, int nb_sectors,
 | 
			
		||||
                                  BlockDriverCompletionFunc *cb, void *opaque);
 | 
			
		||||
BlockDriverAIOCB *bdrv_aio_flush(BlockDriverState *bs,
 | 
			
		||||
				 BlockDriverCompletionFunc *cb, void *opaque);
 | 
			
		||||
void bdrv_aio_cancel(BlockDriverAIOCB *acb);
 | 
			
		||||
 | 
			
		||||
typedef struct BlockRequest {
 | 
			
		||||
    /* Fields to be filled by multiwrite caller */
 | 
			
		||||
    int64_t sector;
 | 
			
		||||
    int nb_sectors;
 | 
			
		||||
    QEMUIOVector *qiov;
 | 
			
		||||
    BlockDriverCompletionFunc *cb;
 | 
			
		||||
    void *opaque;
 | 
			
		||||
 | 
			
		||||
    /* Filled by multiwrite implementation */
 | 
			
		||||
    int error;
 | 
			
		||||
} BlockRequest;
 | 
			
		||||
 | 
			
		||||
int bdrv_aio_multiwrite(BlockDriverState *bs, BlockRequest *reqs,
 | 
			
		||||
    int num_reqs);
 | 
			
		||||
 | 
			
		||||
/* sg packet commands */
 | 
			
		||||
int bdrv_ioctl(BlockDriverState *bs, unsigned long int req, void *buf);
 | 
			
		||||
BlockDriverAIOCB *bdrv_aio_ioctl(BlockDriverState *bs,
 | 
			
		||||
        unsigned long int req, void *buf,
 | 
			
		||||
        BlockDriverCompletionFunc *cb, void *opaque);
 | 
			
		||||
 | 
			
		||||
/* Ensure contents are flushed to disk.  */
 | 
			
		||||
void bdrv_flush(BlockDriverState *bs);
 | 
			
		||||
void bdrv_flush_all(void);
 | 
			
		||||
void bdrv_close_all(void);
 | 
			
		||||
 | 
			
		||||
int bdrv_has_zero_init(BlockDriverState *bs);
 | 
			
		||||
int bdrv_is_allocated(BlockDriverState *bs, int64_t sector_num, int nb_sectors,
 | 
			
		||||
	int *pnum);
 | 
			
		||||
 | 
			
		||||
#define BDRV_TYPE_HD     0
 | 
			
		||||
#define BDRV_TYPE_CDROM  1
 | 
			
		||||
#define BDRV_TYPE_FLOPPY 2
 | 
			
		||||
#define BIOS_ATA_TRANSLATION_AUTO   0
 | 
			
		||||
#define BIOS_ATA_TRANSLATION_NONE   1
 | 
			
		||||
#define BIOS_ATA_TRANSLATION_LBA    2
 | 
			
		||||
#define BIOS_ATA_TRANSLATION_LARGE  3
 | 
			
		||||
#define BIOS_ATA_TRANSLATION_RECHS  4
 | 
			
		||||
 | 
			
		||||
void bdrv_set_geometry_hint(BlockDriverState *bs,
 | 
			
		||||
                            int cyls, int heads, int secs);
 | 
			
		||||
void bdrv_set_type_hint(BlockDriverState *bs, int type);
 | 
			
		||||
void bdrv_set_translation_hint(BlockDriverState *bs, int translation);
 | 
			
		||||
void bdrv_get_geometry_hint(BlockDriverState *bs,
 | 
			
		||||
                            int *pcyls, int *pheads, int *psecs);
 | 
			
		||||
int bdrv_get_type_hint(BlockDriverState *bs);
 | 
			
		||||
int bdrv_get_translation_hint(BlockDriverState *bs);
 | 
			
		||||
void bdrv_set_on_error(BlockDriverState *bs, BlockErrorAction on_read_error,
 | 
			
		||||
                       BlockErrorAction on_write_error);
 | 
			
		||||
BlockErrorAction bdrv_get_on_error(BlockDriverState *bs, int is_read);
 | 
			
		||||
void bdrv_set_removable(BlockDriverState *bs, int removable);
 | 
			
		||||
int bdrv_is_removable(BlockDriverState *bs);
 | 
			
		||||
int bdrv_is_read_only(BlockDriverState *bs);
 | 
			
		||||
int bdrv_is_sg(BlockDriverState *bs);
 | 
			
		||||
int bdrv_enable_write_cache(BlockDriverState *bs);
 | 
			
		||||
int bdrv_is_inserted(BlockDriverState *bs);
 | 
			
		||||
int bdrv_media_changed(BlockDriverState *bs);
 | 
			
		||||
int bdrv_is_locked(BlockDriverState *bs);
 | 
			
		||||
void bdrv_set_locked(BlockDriverState *bs, int locked);
 | 
			
		||||
int bdrv_eject(BlockDriverState *bs, int eject_flag);
 | 
			
		||||
void bdrv_set_change_cb(BlockDriverState *bs,
 | 
			
		||||
                        void (*change_cb)(void *opaque), void *opaque);
 | 
			
		||||
void bdrv_get_format(BlockDriverState *bs, char *buf, int buf_size);
 | 
			
		||||
BlockDriverState *bdrv_find(const char *name);
 | 
			
		||||
BlockDriverState *bdrv_next(BlockDriverState *bs);
 | 
			
		||||
void bdrv_iterate(void (*it)(void *opaque, BlockDriverState *bs),
 | 
			
		||||
                  void *opaque);
 | 
			
		||||
int bdrv_is_encrypted(BlockDriverState *bs);
 | 
			
		||||
int bdrv_key_required(BlockDriverState *bs);
 | 
			
		||||
int bdrv_set_key(BlockDriverState *bs, const char *key);
 | 
			
		||||
int bdrv_query_missing_keys(void);
 | 
			
		||||
void bdrv_iterate_format(void (*it)(void *opaque, const char *name),
 | 
			
		||||
                         void *opaque);
 | 
			
		||||
const char *bdrv_get_device_name(BlockDriverState *bs);
 | 
			
		||||
int bdrv_write_compressed(BlockDriverState *bs, int64_t sector_num,
 | 
			
		||||
                          const uint8_t *buf, int nb_sectors);
 | 
			
		||||
int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi);
 | 
			
		||||
 | 
			
		||||
const char *bdrv_get_encrypted_filename(BlockDriverState *bs);
 | 
			
		||||
void bdrv_get_backing_filename(BlockDriverState *bs,
 | 
			
		||||
                               char *filename, int filename_size);
 | 
			
		||||
int bdrv_can_snapshot(BlockDriverState *bs);
 | 
			
		||||
int bdrv_is_snapshot(BlockDriverState *bs);
 | 
			
		||||
BlockDriverState *bdrv_snapshots(void);
 | 
			
		||||
int bdrv_snapshot_create(BlockDriverState *bs,
 | 
			
		||||
                         QEMUSnapshotInfo *sn_info);
 | 
			
		||||
int bdrv_snapshot_goto(BlockDriverState *bs,
 | 
			
		||||
                       const char *snapshot_id);
 | 
			
		||||
int bdrv_snapshot_delete(BlockDriverState *bs, const char *snapshot_id);
 | 
			
		||||
int bdrv_snapshot_list(BlockDriverState *bs,
 | 
			
		||||
                       QEMUSnapshotInfo **psn_info);
 | 
			
		||||
char *bdrv_snapshot_dump(char *buf, int buf_size, QEMUSnapshotInfo *sn);
 | 
			
		||||
 | 
			
		||||
char *get_human_readable_size(char *buf, int buf_size, int64_t size);
 | 
			
		||||
int path_is_absolute(const char *path);
 | 
			
		||||
void path_combine(char *dest, int dest_size,
 | 
			
		||||
                  const char *base_path,
 | 
			
		||||
                  const char *filename);
 | 
			
		||||
 | 
			
		||||
int bdrv_save_vmstate(BlockDriverState *bs, const uint8_t *buf,
 | 
			
		||||
                      int64_t pos, int size);
 | 
			
		||||
 | 
			
		||||
int bdrv_load_vmstate(BlockDriverState *bs, uint8_t *buf,
 | 
			
		||||
                      int64_t pos, int size);
 | 
			
		||||
 | 
			
		||||
#define BDRV_SECTORS_PER_DIRTY_CHUNK 2048
 | 
			
		||||
 | 
			
		||||
void bdrv_set_dirty_tracking(BlockDriverState *bs, int enable);
 | 
			
		||||
int bdrv_get_dirty(BlockDriverState *bs, int64_t sector);
 | 
			
		||||
void bdrv_reset_dirty(BlockDriverState *bs, int64_t cur_sector,
 | 
			
		||||
                      int nr_sectors);
 | 
			
		||||
int64_t bdrv_get_dirty_count(BlockDriverState *bs);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
typedef enum {
 | 
			
		||||
    BLKDBG_L1_UPDATE,
 | 
			
		||||
 | 
			
		||||
    BLKDBG_L1_GROW_ALLOC_TABLE,
 | 
			
		||||
    BLKDBG_L1_GROW_WRITE_TABLE,
 | 
			
		||||
    BLKDBG_L1_GROW_ACTIVATE_TABLE,
 | 
			
		||||
 | 
			
		||||
    BLKDBG_L2_LOAD,
 | 
			
		||||
    BLKDBG_L2_UPDATE,
 | 
			
		||||
    BLKDBG_L2_UPDATE_COMPRESSED,
 | 
			
		||||
    BLKDBG_L2_ALLOC_COW_READ,
 | 
			
		||||
    BLKDBG_L2_ALLOC_WRITE,
 | 
			
		||||
 | 
			
		||||
    BLKDBG_READ,
 | 
			
		||||
    BLKDBG_READ_AIO,
 | 
			
		||||
    BLKDBG_READ_BACKING,
 | 
			
		||||
    BLKDBG_READ_BACKING_AIO,
 | 
			
		||||
    BLKDBG_READ_COMPRESSED,
 | 
			
		||||
 | 
			
		||||
    BLKDBG_WRITE_AIO,
 | 
			
		||||
    BLKDBG_WRITE_COMPRESSED,
 | 
			
		||||
 | 
			
		||||
    BLKDBG_VMSTATE_LOAD,
 | 
			
		||||
    BLKDBG_VMSTATE_SAVE,
 | 
			
		||||
 | 
			
		||||
    BLKDBG_COW_READ,
 | 
			
		||||
    BLKDBG_COW_WRITE,
 | 
			
		||||
 | 
			
		||||
    BLKDBG_REFTABLE_LOAD,
 | 
			
		||||
    BLKDBG_REFTABLE_GROW,
 | 
			
		||||
 | 
			
		||||
    BLKDBG_REFBLOCK_LOAD,
 | 
			
		||||
    BLKDBG_REFBLOCK_UPDATE,
 | 
			
		||||
    BLKDBG_REFBLOCK_UPDATE_PART,
 | 
			
		||||
    BLKDBG_REFBLOCK_ALLOC,
 | 
			
		||||
    BLKDBG_REFBLOCK_ALLOC_HOOKUP,
 | 
			
		||||
    BLKDBG_REFBLOCK_ALLOC_WRITE,
 | 
			
		||||
    BLKDBG_REFBLOCK_ALLOC_WRITE_BLOCKS,
 | 
			
		||||
    BLKDBG_REFBLOCK_ALLOC_WRITE_TABLE,
 | 
			
		||||
    BLKDBG_REFBLOCK_ALLOC_SWITCH_TABLE,
 | 
			
		||||
 | 
			
		||||
    BLKDBG_CLUSTER_ALLOC,
 | 
			
		||||
    BLKDBG_CLUSTER_ALLOC_BYTES,
 | 
			
		||||
    BLKDBG_CLUSTER_FREE,
 | 
			
		||||
 | 
			
		||||
    BLKDBG_EVENT_MAX,
 | 
			
		||||
} BlkDebugEvent;
 | 
			
		||||
 | 
			
		||||
#define BLKDBG_EVENT(bs, evt) bdrv_debug_event(bs, evt)
 | 
			
		||||
void bdrv_debug_event(BlockDriverState *bs, BlkDebugEvent event);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										473
									
								
								block/blkdebug.c
									
									
									
									
									
								
							
							
						
						
									
										473
									
								
								block/blkdebug.c
									
									
									
									
									
								
							@@ -1,473 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Block protocol for I/O error injection
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2010 Kevin Wolf <kwolf@redhat.com>
 | 
			
		||||
 *
 | 
			
		||||
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
 * of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
 * in the Software without restriction, including without limitation the rights
 | 
			
		||||
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
			
		||||
 * copies of the Software, and to permit persons to whom the Software is
 | 
			
		||||
 * furnished to do so, subject to the following conditions:
 | 
			
		||||
 *
 | 
			
		||||
 * The above copyright notice and this permission notice shall be included in
 | 
			
		||||
 * all copies or substantial portions of the Software.
 | 
			
		||||
 *
 | 
			
		||||
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 | 
			
		||||
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
			
		||||
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
			
		||||
 * THE SOFTWARE.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "qemu-common.h"
 | 
			
		||||
#include "block_int.h"
 | 
			
		||||
#include "module.h"
 | 
			
		||||
 | 
			
		||||
typedef struct BlkdebugVars {
 | 
			
		||||
    int state;
 | 
			
		||||
 | 
			
		||||
    /* If inject_errno != 0, an error is injected for requests */
 | 
			
		||||
    int inject_errno;
 | 
			
		||||
 | 
			
		||||
    /* Decides if all future requests fail (false) or only the next one and
 | 
			
		||||
     * after the next request inject_errno is reset to 0 (true) */
 | 
			
		||||
    bool inject_once;
 | 
			
		||||
 | 
			
		||||
    /* Decides if aio_readv/writev fails right away (true) or returns an error
 | 
			
		||||
     * return value only in the callback (false) */
 | 
			
		||||
    bool inject_immediately;
 | 
			
		||||
} BlkdebugVars;
 | 
			
		||||
 | 
			
		||||
typedef struct BDRVBlkdebugState {
 | 
			
		||||
    BlkdebugVars vars;
 | 
			
		||||
    QLIST_HEAD(list, BlkdebugRule) rules[BLKDBG_EVENT_MAX];
 | 
			
		||||
} BDRVBlkdebugState;
 | 
			
		||||
 | 
			
		||||
typedef struct BlkdebugAIOCB {
 | 
			
		||||
    BlockDriverAIOCB common;
 | 
			
		||||
    QEMUBH *bh;
 | 
			
		||||
    int ret;
 | 
			
		||||
} BlkdebugAIOCB;
 | 
			
		||||
 | 
			
		||||
static void blkdebug_aio_cancel(BlockDriverAIOCB *blockacb);
 | 
			
		||||
 | 
			
		||||
static AIOPool blkdebug_aio_pool = {
 | 
			
		||||
    .aiocb_size = sizeof(BlkdebugAIOCB),
 | 
			
		||||
    .cancel     = blkdebug_aio_cancel,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum {
 | 
			
		||||
    ACTION_INJECT_ERROR,
 | 
			
		||||
    ACTION_SET_STATE,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef struct BlkdebugRule {
 | 
			
		||||
    BlkDebugEvent event;
 | 
			
		||||
    int action;
 | 
			
		||||
    int state;
 | 
			
		||||
    union {
 | 
			
		||||
        struct {
 | 
			
		||||
            int error;
 | 
			
		||||
            int immediately;
 | 
			
		||||
            int once;
 | 
			
		||||
        } inject;
 | 
			
		||||
        struct {
 | 
			
		||||
            int new_state;
 | 
			
		||||
        } set_state;
 | 
			
		||||
    } options;
 | 
			
		||||
    QLIST_ENTRY(BlkdebugRule) next;
 | 
			
		||||
} BlkdebugRule;
 | 
			
		||||
 | 
			
		||||
static QemuOptsList inject_error_opts = {
 | 
			
		||||
    .name = "inject-error",
 | 
			
		||||
    .head = QTAILQ_HEAD_INITIALIZER(inject_error_opts.head),
 | 
			
		||||
    .desc = {
 | 
			
		||||
        {
 | 
			
		||||
            .name = "event",
 | 
			
		||||
            .type = QEMU_OPT_STRING,
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            .name = "state",
 | 
			
		||||
            .type = QEMU_OPT_NUMBER,
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            .name = "errno",
 | 
			
		||||
            .type = QEMU_OPT_NUMBER,
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            .name = "once",
 | 
			
		||||
            .type = QEMU_OPT_BOOL,
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            .name = "immediately",
 | 
			
		||||
            .type = QEMU_OPT_BOOL,
 | 
			
		||||
        },
 | 
			
		||||
        { /* end of list */ }
 | 
			
		||||
    },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static QemuOptsList set_state_opts = {
 | 
			
		||||
    .name = "set-state",
 | 
			
		||||
    .head = QTAILQ_HEAD_INITIALIZER(set_state_opts.head),
 | 
			
		||||
    .desc = {
 | 
			
		||||
        {
 | 
			
		||||
            .name = "event",
 | 
			
		||||
            .type = QEMU_OPT_STRING,
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            .name = "state",
 | 
			
		||||
            .type = QEMU_OPT_NUMBER,
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            .name = "new_state",
 | 
			
		||||
            .type = QEMU_OPT_NUMBER,
 | 
			
		||||
        },
 | 
			
		||||
        { /* end of list */ }
 | 
			
		||||
    },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static QemuOptsList *config_groups[] = {
 | 
			
		||||
    &inject_error_opts,
 | 
			
		||||
    &set_state_opts,
 | 
			
		||||
    NULL
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const char *event_names[BLKDBG_EVENT_MAX] = {
 | 
			
		||||
    [BLKDBG_L1_UPDATE]                      = "l1_update",
 | 
			
		||||
    [BLKDBG_L1_GROW_ALLOC_TABLE]            = "l1_grow.alloc_table",
 | 
			
		||||
    [BLKDBG_L1_GROW_WRITE_TABLE]            = "l1_grow.write_table",
 | 
			
		||||
    [BLKDBG_L1_GROW_ACTIVATE_TABLE]         = "l1_grow.activate_table",
 | 
			
		||||
 | 
			
		||||
    [BLKDBG_L2_LOAD]                        = "l2_load",
 | 
			
		||||
    [BLKDBG_L2_UPDATE]                      = "l2_update",
 | 
			
		||||
    [BLKDBG_L2_UPDATE_COMPRESSED]           = "l2_update_compressed",
 | 
			
		||||
    [BLKDBG_L2_ALLOC_COW_READ]              = "l2_alloc.cow_read",
 | 
			
		||||
    [BLKDBG_L2_ALLOC_WRITE]                 = "l2_alloc.write",
 | 
			
		||||
 | 
			
		||||
    [BLKDBG_READ]                           = "read",
 | 
			
		||||
    [BLKDBG_READ_AIO]                       = "read_aio",
 | 
			
		||||
    [BLKDBG_READ_BACKING]                   = "read_backing",
 | 
			
		||||
    [BLKDBG_READ_BACKING_AIO]               = "read_backing_aio",
 | 
			
		||||
    [BLKDBG_READ_COMPRESSED]                = "read_compressed",
 | 
			
		||||
 | 
			
		||||
    [BLKDBG_WRITE_AIO]                      = "write_aio",
 | 
			
		||||
    [BLKDBG_WRITE_COMPRESSED]               = "write_compressed",
 | 
			
		||||
 | 
			
		||||
    [BLKDBG_VMSTATE_LOAD]                   = "vmstate_load",
 | 
			
		||||
    [BLKDBG_VMSTATE_SAVE]                   = "vmstate_save",
 | 
			
		||||
 | 
			
		||||
    [BLKDBG_COW_READ]                       = "cow_read",
 | 
			
		||||
    [BLKDBG_COW_WRITE]                      = "cow_write",
 | 
			
		||||
 | 
			
		||||
    [BLKDBG_REFTABLE_LOAD]                  = "reftable_load",
 | 
			
		||||
    [BLKDBG_REFTABLE_GROW]                  = "reftable_grow",
 | 
			
		||||
 | 
			
		||||
    [BLKDBG_REFBLOCK_LOAD]                  = "refblock_load",
 | 
			
		||||
    [BLKDBG_REFBLOCK_UPDATE]                = "refblock_update",
 | 
			
		||||
    [BLKDBG_REFBLOCK_UPDATE_PART]           = "refblock_update_part",
 | 
			
		||||
    [BLKDBG_REFBLOCK_ALLOC]                 = "refblock_alloc",
 | 
			
		||||
    [BLKDBG_REFBLOCK_ALLOC_HOOKUP]          = "refblock_alloc.hookup",
 | 
			
		||||
    [BLKDBG_REFBLOCK_ALLOC_WRITE]           = "refblock_alloc.write",
 | 
			
		||||
    [BLKDBG_REFBLOCK_ALLOC_WRITE_BLOCKS]    = "refblock_alloc.write_blocks",
 | 
			
		||||
    [BLKDBG_REFBLOCK_ALLOC_WRITE_TABLE]     = "refblock_alloc.write_table",
 | 
			
		||||
    [BLKDBG_REFBLOCK_ALLOC_SWITCH_TABLE]    = "refblock_alloc.switch_table",
 | 
			
		||||
 | 
			
		||||
    [BLKDBG_CLUSTER_ALLOC]                  = "cluster_alloc",
 | 
			
		||||
    [BLKDBG_CLUSTER_ALLOC_BYTES]            = "cluster_alloc_bytes",
 | 
			
		||||
    [BLKDBG_CLUSTER_FREE]                   = "cluster_free",
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int get_event_by_name(const char *name, BlkDebugEvent *event)
 | 
			
		||||
{
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < BLKDBG_EVENT_MAX; i++) {
 | 
			
		||||
        if (!strcmp(event_names[i], name)) {
 | 
			
		||||
            *event = i;
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct add_rule_data {
 | 
			
		||||
    BDRVBlkdebugState *s;
 | 
			
		||||
    int action;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int add_rule(QemuOpts *opts, void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    struct add_rule_data *d = opaque;
 | 
			
		||||
    BDRVBlkdebugState *s = d->s;
 | 
			
		||||
    const char* event_name;
 | 
			
		||||
    BlkDebugEvent event;
 | 
			
		||||
    struct BlkdebugRule *rule;
 | 
			
		||||
 | 
			
		||||
    /* Find the right event for the rule */
 | 
			
		||||
    event_name = qemu_opt_get(opts, "event");
 | 
			
		||||
    if (!event_name || get_event_by_name(event_name, &event) < 0) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Set attributes common for all actions */
 | 
			
		||||
    rule = qemu_mallocz(sizeof(*rule));
 | 
			
		||||
    *rule = (struct BlkdebugRule) {
 | 
			
		||||
        .event  = event,
 | 
			
		||||
        .action = d->action,
 | 
			
		||||
        .state  = qemu_opt_get_number(opts, "state", 0),
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /* Parse action-specific options */
 | 
			
		||||
    switch (d->action) {
 | 
			
		||||
    case ACTION_INJECT_ERROR:
 | 
			
		||||
        rule->options.inject.error = qemu_opt_get_number(opts, "errno", EIO);
 | 
			
		||||
        rule->options.inject.once  = qemu_opt_get_bool(opts, "once", 0);
 | 
			
		||||
        rule->options.inject.immediately =
 | 
			
		||||
            qemu_opt_get_bool(opts, "immediately", 0);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case ACTION_SET_STATE:
 | 
			
		||||
        rule->options.set_state.new_state =
 | 
			
		||||
            qemu_opt_get_number(opts, "new_state", 0);
 | 
			
		||||
        break;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /* Add the rule */
 | 
			
		||||
    QLIST_INSERT_HEAD(&s->rules[event], rule, next);
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int read_config(BDRVBlkdebugState *s, const char *filename)
 | 
			
		||||
{
 | 
			
		||||
    FILE *f;
 | 
			
		||||
    int ret;
 | 
			
		||||
    struct add_rule_data d;
 | 
			
		||||
 | 
			
		||||
    f = fopen(filename, "r");
 | 
			
		||||
    if (f == NULL) {
 | 
			
		||||
        return -errno;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ret = qemu_config_parse(f, config_groups, filename);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    d.s = s;
 | 
			
		||||
    d.action = ACTION_INJECT_ERROR;
 | 
			
		||||
    qemu_opts_foreach(&inject_error_opts, add_rule, &d, 0);
 | 
			
		||||
 | 
			
		||||
    d.action = ACTION_SET_STATE;
 | 
			
		||||
    qemu_opts_foreach(&set_state_opts, add_rule, &d, 0);
 | 
			
		||||
 | 
			
		||||
    ret = 0;
 | 
			
		||||
fail:
 | 
			
		||||
    qemu_opts_reset(&inject_error_opts);
 | 
			
		||||
    qemu_opts_reset(&set_state_opts);
 | 
			
		||||
    fclose(f);
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Valid blkdebug filenames look like blkdebug:path/to/config:path/to/image */
 | 
			
		||||
static int blkdebug_open(BlockDriverState *bs, const char *filename, int flags)
 | 
			
		||||
{
 | 
			
		||||
    BDRVBlkdebugState *s = bs->opaque;
 | 
			
		||||
    int ret;
 | 
			
		||||
    char *config, *c;
 | 
			
		||||
 | 
			
		||||
    /* Parse the blkdebug: prefix */
 | 
			
		||||
    if (strncmp(filename, "blkdebug:", strlen("blkdebug:"))) {
 | 
			
		||||
        return -EINVAL;
 | 
			
		||||
    }
 | 
			
		||||
    filename += strlen("blkdebug:");
 | 
			
		||||
 | 
			
		||||
    /* Read rules from config file */
 | 
			
		||||
    c = strchr(filename, ':');
 | 
			
		||||
    if (c == NULL) {
 | 
			
		||||
        return -EINVAL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    config = strdup(filename);
 | 
			
		||||
    config[c - filename] = '\0';
 | 
			
		||||
    ret = read_config(s, config);
 | 
			
		||||
    free(config);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        return ret;
 | 
			
		||||
    }
 | 
			
		||||
    filename = c + 1;
 | 
			
		||||
 | 
			
		||||
    /* Set initial state */
 | 
			
		||||
    s->vars.state = 1;
 | 
			
		||||
 | 
			
		||||
    /* Open the backing file */
 | 
			
		||||
    ret = bdrv_file_open(&bs->file, filename, flags);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        return ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void error_callback_bh(void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    struct BlkdebugAIOCB *acb = opaque;
 | 
			
		||||
    qemu_bh_delete(acb->bh);
 | 
			
		||||
    acb->common.cb(acb->common.opaque, acb->ret);
 | 
			
		||||
    qemu_aio_release(acb);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void blkdebug_aio_cancel(BlockDriverAIOCB *blockacb)
 | 
			
		||||
{
 | 
			
		||||
    BlkdebugAIOCB *acb = container_of(blockacb, BlkdebugAIOCB, common);
 | 
			
		||||
    qemu_aio_release(acb);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static BlockDriverAIOCB *inject_error(BlockDriverState *bs,
 | 
			
		||||
    BlockDriverCompletionFunc *cb, void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    BDRVBlkdebugState *s = bs->opaque;
 | 
			
		||||
    int error = s->vars.inject_errno;
 | 
			
		||||
    struct BlkdebugAIOCB *acb;
 | 
			
		||||
    QEMUBH *bh;
 | 
			
		||||
 | 
			
		||||
    if (s->vars.inject_once) {
 | 
			
		||||
        s->vars.inject_errno = 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (s->vars.inject_immediately) {
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    acb = qemu_aio_get(&blkdebug_aio_pool, bs, cb, opaque);
 | 
			
		||||
    acb->ret = -error;
 | 
			
		||||
 | 
			
		||||
    bh = qemu_bh_new(error_callback_bh, acb);
 | 
			
		||||
    acb->bh = bh;
 | 
			
		||||
    qemu_bh_schedule(bh);
 | 
			
		||||
 | 
			
		||||
    return &acb->common;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static BlockDriverAIOCB *blkdebug_aio_readv(BlockDriverState *bs,
 | 
			
		||||
    int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
 | 
			
		||||
    BlockDriverCompletionFunc *cb, void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    BDRVBlkdebugState *s = bs->opaque;
 | 
			
		||||
 | 
			
		||||
    if (s->vars.inject_errno) {
 | 
			
		||||
        return inject_error(bs, cb, opaque);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    BlockDriverAIOCB *acb =
 | 
			
		||||
        bdrv_aio_readv(bs->file, sector_num, qiov, nb_sectors, cb, opaque);
 | 
			
		||||
    return acb;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static BlockDriverAIOCB *blkdebug_aio_writev(BlockDriverState *bs,
 | 
			
		||||
    int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
 | 
			
		||||
    BlockDriverCompletionFunc *cb, void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    BDRVBlkdebugState *s = bs->opaque;
 | 
			
		||||
 | 
			
		||||
    if (s->vars.inject_errno) {
 | 
			
		||||
        return inject_error(bs, cb, opaque);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    BlockDriverAIOCB *acb =
 | 
			
		||||
        bdrv_aio_writev(bs->file, sector_num, qiov, nb_sectors, cb, opaque);
 | 
			
		||||
    return acb;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void blkdebug_close(BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
    BDRVBlkdebugState *s = bs->opaque;
 | 
			
		||||
    BlkdebugRule *rule, *next;
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < BLKDBG_EVENT_MAX; i++) {
 | 
			
		||||
        QLIST_FOREACH_SAFE(rule, &s->rules[i], next, next) {
 | 
			
		||||
            QLIST_REMOVE(rule, next);
 | 
			
		||||
            qemu_free(rule);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void blkdebug_flush(BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
    bdrv_flush(bs->file);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static BlockDriverAIOCB *blkdebug_aio_flush(BlockDriverState *bs,
 | 
			
		||||
    BlockDriverCompletionFunc *cb, void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    return bdrv_aio_flush(bs->file, cb, opaque);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void process_rule(BlockDriverState *bs, struct BlkdebugRule *rule,
 | 
			
		||||
    BlkdebugVars *old_vars)
 | 
			
		||||
{
 | 
			
		||||
    BDRVBlkdebugState *s = bs->opaque;
 | 
			
		||||
    BlkdebugVars *vars = &s->vars;
 | 
			
		||||
 | 
			
		||||
    /* Only process rules for the current state */
 | 
			
		||||
    if (rule->state && rule->state != old_vars->state) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Take the action */
 | 
			
		||||
    switch (rule->action) {
 | 
			
		||||
    case ACTION_INJECT_ERROR:
 | 
			
		||||
        vars->inject_errno       = rule->options.inject.error;
 | 
			
		||||
        vars->inject_once        = rule->options.inject.once;
 | 
			
		||||
        vars->inject_immediately = rule->options.inject.immediately;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case ACTION_SET_STATE:
 | 
			
		||||
        vars->state              = rule->options.set_state.new_state;
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void blkdebug_debug_event(BlockDriverState *bs, BlkDebugEvent event)
 | 
			
		||||
{
 | 
			
		||||
    BDRVBlkdebugState *s = bs->opaque;
 | 
			
		||||
    struct BlkdebugRule *rule;
 | 
			
		||||
    BlkdebugVars old_vars = s->vars;
 | 
			
		||||
 | 
			
		||||
    if (event < 0 || event >= BLKDBG_EVENT_MAX) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    QLIST_FOREACH(rule, &s->rules[event], next) {
 | 
			
		||||
        process_rule(bs, rule, &old_vars);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static BlockDriver bdrv_blkdebug = {
 | 
			
		||||
    .format_name        = "blkdebug",
 | 
			
		||||
    .protocol_name      = "blkdebug",
 | 
			
		||||
 | 
			
		||||
    .instance_size      = sizeof(BDRVBlkdebugState),
 | 
			
		||||
 | 
			
		||||
    .bdrv_file_open     = blkdebug_open,
 | 
			
		||||
    .bdrv_close         = blkdebug_close,
 | 
			
		||||
    .bdrv_flush         = blkdebug_flush,
 | 
			
		||||
 | 
			
		||||
    .bdrv_aio_readv     = blkdebug_aio_readv,
 | 
			
		||||
    .bdrv_aio_writev    = blkdebug_aio_writev,
 | 
			
		||||
    .bdrv_aio_flush     = blkdebug_aio_flush,
 | 
			
		||||
 | 
			
		||||
    .bdrv_debug_event   = blkdebug_debug_event,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void bdrv_blkdebug_init(void)
 | 
			
		||||
{
 | 
			
		||||
    bdrv_register(&bdrv_blkdebug);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
block_init(bdrv_blkdebug_init);
 | 
			
		||||
							
								
								
									
										324
									
								
								block/cow.c
									
									
									
									
									
								
							
							
						
						
									
										324
									
								
								block/cow.c
									
									
									
									
									
								
							@@ -1,324 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Block driver for the COW format
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2004 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-common.h"
 | 
			
		||||
#include "block_int.h"
 | 
			
		||||
#include "module.h"
 | 
			
		||||
 | 
			
		||||
/**************************************************************/
 | 
			
		||||
/* COW block driver using file system holes */
 | 
			
		||||
 | 
			
		||||
/* user mode linux compatible COW file */
 | 
			
		||||
#define COW_MAGIC 0x4f4f4f4d  /* MOOO */
 | 
			
		||||
#define COW_VERSION 2
 | 
			
		||||
 | 
			
		||||
struct cow_header_v2 {
 | 
			
		||||
    uint32_t magic;
 | 
			
		||||
    uint32_t version;
 | 
			
		||||
    char backing_file[1024];
 | 
			
		||||
    int32_t mtime;
 | 
			
		||||
    uint64_t size;
 | 
			
		||||
    uint32_t sectorsize;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef struct BDRVCowState {
 | 
			
		||||
    int64_t cow_sectors_offset;
 | 
			
		||||
} BDRVCowState;
 | 
			
		||||
 | 
			
		||||
static int cow_probe(const uint8_t *buf, int buf_size, const char *filename)
 | 
			
		||||
{
 | 
			
		||||
    const struct cow_header_v2 *cow_header = (const void *)buf;
 | 
			
		||||
 | 
			
		||||
    if (buf_size >= sizeof(struct cow_header_v2) &&
 | 
			
		||||
        be32_to_cpu(cow_header->magic) == COW_MAGIC &&
 | 
			
		||||
        be32_to_cpu(cow_header->version) == COW_VERSION)
 | 
			
		||||
        return 100;
 | 
			
		||||
    else
 | 
			
		||||
        return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int cow_open(BlockDriverState *bs, int flags)
 | 
			
		||||
{
 | 
			
		||||
    BDRVCowState *s = bs->opaque;
 | 
			
		||||
    struct cow_header_v2 cow_header;
 | 
			
		||||
    int bitmap_size;
 | 
			
		||||
    int64_t size;
 | 
			
		||||
 | 
			
		||||
    /* see if it is a cow image */
 | 
			
		||||
    if (bdrv_pread(bs->file, 0, &cow_header, sizeof(cow_header)) !=
 | 
			
		||||
            sizeof(cow_header)) {
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (be32_to_cpu(cow_header.magic) != COW_MAGIC ||
 | 
			
		||||
        be32_to_cpu(cow_header.version) != COW_VERSION) {
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* cow image found */
 | 
			
		||||
    size = be64_to_cpu(cow_header.size);
 | 
			
		||||
    bs->total_sectors = size / 512;
 | 
			
		||||
 | 
			
		||||
    pstrcpy(bs->backing_file, sizeof(bs->backing_file),
 | 
			
		||||
            cow_header.backing_file);
 | 
			
		||||
 | 
			
		||||
    bitmap_size = ((bs->total_sectors + 7) >> 3) + sizeof(cow_header);
 | 
			
		||||
    s->cow_sectors_offset = (bitmap_size + 511) & ~511;
 | 
			
		||||
    return 0;
 | 
			
		||||
 fail:
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * XXX(hch): right now these functions are extremly ineffcient.
 | 
			
		||||
 * We should just read the whole bitmap we'll need in one go instead.
 | 
			
		||||
 */
 | 
			
		||||
static inline int cow_set_bit(BlockDriverState *bs, int64_t bitnum)
 | 
			
		||||
{
 | 
			
		||||
    uint64_t offset = sizeof(struct cow_header_v2) + bitnum / 8;
 | 
			
		||||
    uint8_t bitmap;
 | 
			
		||||
    int ret;
 | 
			
		||||
 | 
			
		||||
    ret = bdrv_pread(bs->file, offset, &bitmap, sizeof(bitmap));
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
       return ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bitmap |= (1 << (bitnum % 8));
 | 
			
		||||
 | 
			
		||||
    ret = bdrv_pwrite_sync(bs->file, offset, &bitmap, sizeof(bitmap));
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
       return ret;
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline int is_bit_set(BlockDriverState *bs, int64_t bitnum)
 | 
			
		||||
{
 | 
			
		||||
    uint64_t offset = sizeof(struct cow_header_v2) + bitnum / 8;
 | 
			
		||||
    uint8_t bitmap;
 | 
			
		||||
    int ret;
 | 
			
		||||
 | 
			
		||||
    ret = bdrv_pread(bs->file, offset, &bitmap, sizeof(bitmap));
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
       return ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return !!(bitmap & (1 << (bitnum % 8)));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Return true if first block has been changed (ie. current version is
 | 
			
		||||
 * in COW file).  Set the number of continuous blocks for which that
 | 
			
		||||
 * is true. */
 | 
			
		||||
static int cow_is_allocated(BlockDriverState *bs, int64_t sector_num,
 | 
			
		||||
        int nb_sectors, int *num_same)
 | 
			
		||||
{
 | 
			
		||||
    int changed;
 | 
			
		||||
 | 
			
		||||
    if (nb_sectors == 0) {
 | 
			
		||||
	*num_same = nb_sectors;
 | 
			
		||||
	return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    changed = is_bit_set(bs, sector_num);
 | 
			
		||||
    if (changed < 0) {
 | 
			
		||||
        return 0; /* XXX: how to return I/O errors? */
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (*num_same = 1; *num_same < nb_sectors; (*num_same)++) {
 | 
			
		||||
	if (is_bit_set(bs, sector_num + *num_same) != changed)
 | 
			
		||||
	    break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return changed;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int cow_update_bitmap(BlockDriverState *bs, int64_t sector_num,
 | 
			
		||||
        int nb_sectors)
 | 
			
		||||
{
 | 
			
		||||
    int error = 0;
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < nb_sectors; i++) {
 | 
			
		||||
        error = cow_set_bit(bs, sector_num + i);
 | 
			
		||||
        if (error) {
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return error;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int cow_read(BlockDriverState *bs, int64_t sector_num,
 | 
			
		||||
                    uint8_t *buf, int nb_sectors)
 | 
			
		||||
{
 | 
			
		||||
    BDRVCowState *s = bs->opaque;
 | 
			
		||||
    int ret, n;
 | 
			
		||||
 | 
			
		||||
    while (nb_sectors > 0) {
 | 
			
		||||
        if (cow_is_allocated(bs, sector_num, nb_sectors, &n)) {
 | 
			
		||||
            ret = bdrv_pread(bs->file,
 | 
			
		||||
                        s->cow_sectors_offset + sector_num * 512,
 | 
			
		||||
                        buf, n * 512);
 | 
			
		||||
            if (ret != n * 512)
 | 
			
		||||
                return -1;
 | 
			
		||||
        } else {
 | 
			
		||||
            if (bs->backing_hd) {
 | 
			
		||||
                /* read from the base image */
 | 
			
		||||
                ret = bdrv_read(bs->backing_hd, sector_num, buf, n);
 | 
			
		||||
                if (ret < 0)
 | 
			
		||||
                    return -1;
 | 
			
		||||
            } else {
 | 
			
		||||
            memset(buf, 0, n * 512);
 | 
			
		||||
        }
 | 
			
		||||
        }
 | 
			
		||||
        nb_sectors -= n;
 | 
			
		||||
        sector_num += n;
 | 
			
		||||
        buf += n * 512;
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int cow_write(BlockDriverState *bs, int64_t sector_num,
 | 
			
		||||
                     const uint8_t *buf, int nb_sectors)
 | 
			
		||||
{
 | 
			
		||||
    BDRVCowState *s = bs->opaque;
 | 
			
		||||
    int ret;
 | 
			
		||||
 | 
			
		||||
    ret = bdrv_pwrite(bs->file, s->cow_sectors_offset + sector_num * 512,
 | 
			
		||||
                      buf, nb_sectors * 512);
 | 
			
		||||
    if (ret != nb_sectors * 512)
 | 
			
		||||
        return -1;
 | 
			
		||||
 | 
			
		||||
    return cow_update_bitmap(bs, sector_num, nb_sectors);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void cow_close(BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int cow_create(const char *filename, QEMUOptionParameter *options)
 | 
			
		||||
{
 | 
			
		||||
    int fd, cow_fd;
 | 
			
		||||
    struct cow_header_v2 cow_header;
 | 
			
		||||
    struct stat st;
 | 
			
		||||
    int64_t image_sectors = 0;
 | 
			
		||||
    const char *image_filename = NULL;
 | 
			
		||||
    int ret;
 | 
			
		||||
 | 
			
		||||
    /* Read out options */
 | 
			
		||||
    while (options && options->name) {
 | 
			
		||||
        if (!strcmp(options->name, BLOCK_OPT_SIZE)) {
 | 
			
		||||
            image_sectors = options->value.n / 512;
 | 
			
		||||
        } else if (!strcmp(options->name, BLOCK_OPT_BACKING_FILE)) {
 | 
			
		||||
            image_filename = options->value.s;
 | 
			
		||||
        }
 | 
			
		||||
        options++;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    cow_fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
 | 
			
		||||
              0644);
 | 
			
		||||
    if (cow_fd < 0)
 | 
			
		||||
        return -errno;
 | 
			
		||||
    memset(&cow_header, 0, sizeof(cow_header));
 | 
			
		||||
    cow_header.magic = cpu_to_be32(COW_MAGIC);
 | 
			
		||||
    cow_header.version = cpu_to_be32(COW_VERSION);
 | 
			
		||||
    if (image_filename) {
 | 
			
		||||
        /* Note: if no file, we put a dummy mtime */
 | 
			
		||||
        cow_header.mtime = cpu_to_be32(0);
 | 
			
		||||
 | 
			
		||||
        fd = open(image_filename, O_RDONLY | O_BINARY);
 | 
			
		||||
        if (fd < 0) {
 | 
			
		||||
            close(cow_fd);
 | 
			
		||||
            goto mtime_fail;
 | 
			
		||||
        }
 | 
			
		||||
        if (fstat(fd, &st) != 0) {
 | 
			
		||||
            close(fd);
 | 
			
		||||
            goto mtime_fail;
 | 
			
		||||
        }
 | 
			
		||||
        close(fd);
 | 
			
		||||
        cow_header.mtime = cpu_to_be32(st.st_mtime);
 | 
			
		||||
    mtime_fail:
 | 
			
		||||
        pstrcpy(cow_header.backing_file, sizeof(cow_header.backing_file),
 | 
			
		||||
                image_filename);
 | 
			
		||||
    }
 | 
			
		||||
    cow_header.sectorsize = cpu_to_be32(512);
 | 
			
		||||
    cow_header.size = cpu_to_be64(image_sectors * 512);
 | 
			
		||||
    ret = qemu_write_full(cow_fd, &cow_header, sizeof(cow_header));
 | 
			
		||||
    if (ret != sizeof(cow_header)) {
 | 
			
		||||
        ret = -errno;
 | 
			
		||||
        goto exit;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* resize to include at least all the bitmap */
 | 
			
		||||
    ret = ftruncate(cow_fd, sizeof(cow_header) + ((image_sectors + 7) >> 3));
 | 
			
		||||
    if (ret) {
 | 
			
		||||
        ret = -errno;
 | 
			
		||||
        goto exit;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
exit:
 | 
			
		||||
    close(cow_fd);
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void cow_flush(BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
    bdrv_flush(bs->file);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static QEMUOptionParameter cow_create_options[] = {
 | 
			
		||||
    {
 | 
			
		||||
        .name = BLOCK_OPT_SIZE,
 | 
			
		||||
        .type = OPT_SIZE,
 | 
			
		||||
        .help = "Virtual disk size"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        .name = BLOCK_OPT_BACKING_FILE,
 | 
			
		||||
        .type = OPT_STRING,
 | 
			
		||||
        .help = "File name of a base image"
 | 
			
		||||
    },
 | 
			
		||||
    { NULL }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static BlockDriver bdrv_cow = {
 | 
			
		||||
    .format_name	= "cow",
 | 
			
		||||
    .instance_size	= sizeof(BDRVCowState),
 | 
			
		||||
    .bdrv_probe		= cow_probe,
 | 
			
		||||
    .bdrv_open		= cow_open,
 | 
			
		||||
    .bdrv_read		= cow_read,
 | 
			
		||||
    .bdrv_write		= cow_write,
 | 
			
		||||
    .bdrv_close		= cow_close,
 | 
			
		||||
    .bdrv_create	= cow_create,
 | 
			
		||||
    .bdrv_flush		= cow_flush,
 | 
			
		||||
    .bdrv_is_allocated	= cow_is_allocated,
 | 
			
		||||
 | 
			
		||||
    .create_options = cow_create_options,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void bdrv_cow_init(void)
 | 
			
		||||
{
 | 
			
		||||
    bdrv_register(&bdrv_cow);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
block_init(bdrv_cow_init);
 | 
			
		||||
							
								
								
									
										564
									
								
								block/curl.c
									
									
									
									
									
								
							
							
						
						
									
										564
									
								
								block/curl.c
									
									
									
									
									
								
							@@ -1,564 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * QEMU Block driver for CURL images
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2009 Alexander Graf <agraf@suse.de>
 | 
			
		||||
 *
 | 
			
		||||
 * 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-common.h"
 | 
			
		||||
#include "block_int.h"
 | 
			
		||||
#include <curl/curl.h>
 | 
			
		||||
 | 
			
		||||
// #define DEBUG
 | 
			
		||||
// #define DEBUG_VERBOSE
 | 
			
		||||
 | 
			
		||||
#ifdef DEBUG_CURL
 | 
			
		||||
#define DPRINTF(fmt, ...) do { printf(fmt, ## __VA_ARGS__); } while (0)
 | 
			
		||||
#else
 | 
			
		||||
#define DPRINTF(fmt, ...) do { } while (0)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define CURL_NUM_STATES 8
 | 
			
		||||
#define CURL_NUM_ACB    8
 | 
			
		||||
#define SECTOR_SIZE     512
 | 
			
		||||
#define READ_AHEAD_SIZE (256 * 1024)
 | 
			
		||||
 | 
			
		||||
#define FIND_RET_NONE   0
 | 
			
		||||
#define FIND_RET_OK     1
 | 
			
		||||
#define FIND_RET_WAIT   2
 | 
			
		||||
 | 
			
		||||
struct BDRVCURLState;
 | 
			
		||||
 | 
			
		||||
typedef struct CURLAIOCB {
 | 
			
		||||
    BlockDriverAIOCB common;
 | 
			
		||||
    QEMUIOVector *qiov;
 | 
			
		||||
    size_t start;
 | 
			
		||||
    size_t end;
 | 
			
		||||
} CURLAIOCB;
 | 
			
		||||
 | 
			
		||||
typedef struct CURLState
 | 
			
		||||
{
 | 
			
		||||
    struct BDRVCURLState *s;
 | 
			
		||||
    CURLAIOCB *acb[CURL_NUM_ACB];
 | 
			
		||||
    CURL *curl;
 | 
			
		||||
    char *orig_buf;
 | 
			
		||||
    size_t buf_start;
 | 
			
		||||
    size_t buf_off;
 | 
			
		||||
    size_t buf_len;
 | 
			
		||||
    char range[128];
 | 
			
		||||
    char errmsg[CURL_ERROR_SIZE];
 | 
			
		||||
    char in_use;
 | 
			
		||||
} CURLState;
 | 
			
		||||
 | 
			
		||||
typedef struct BDRVCURLState {
 | 
			
		||||
    CURLM *multi;
 | 
			
		||||
    size_t len;
 | 
			
		||||
    CURLState states[CURL_NUM_STATES];
 | 
			
		||||
    char *url;
 | 
			
		||||
    size_t readahead_size;
 | 
			
		||||
} BDRVCURLState;
 | 
			
		||||
 | 
			
		||||
static void curl_clean_state(CURLState *s);
 | 
			
		||||
static void curl_multi_do(void *arg);
 | 
			
		||||
 | 
			
		||||
static int curl_sock_cb(CURL *curl, curl_socket_t fd, int action,
 | 
			
		||||
                        void *s, void *sp)
 | 
			
		||||
{
 | 
			
		||||
    DPRINTF("CURL (AIO): Sock action %d on fd %d\n", action, fd);
 | 
			
		||||
    switch (action) {
 | 
			
		||||
        case CURL_POLL_IN:
 | 
			
		||||
            qemu_aio_set_fd_handler(fd, curl_multi_do, NULL, NULL, NULL, s);
 | 
			
		||||
            break;
 | 
			
		||||
        case CURL_POLL_OUT:
 | 
			
		||||
            qemu_aio_set_fd_handler(fd, NULL, curl_multi_do, NULL, NULL, s);
 | 
			
		||||
            break;
 | 
			
		||||
        case CURL_POLL_INOUT:
 | 
			
		||||
            qemu_aio_set_fd_handler(fd, curl_multi_do,
 | 
			
		||||
                                    curl_multi_do, NULL, NULL, s);
 | 
			
		||||
            break;
 | 
			
		||||
        case CURL_POLL_REMOVE:
 | 
			
		||||
            qemu_aio_set_fd_handler(fd, NULL, NULL, NULL, NULL, NULL);
 | 
			
		||||
            break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static size_t curl_size_cb(void *ptr, size_t size, size_t nmemb, void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    CURLState *s = ((CURLState*)opaque);
 | 
			
		||||
    size_t realsize = size * nmemb;
 | 
			
		||||
    size_t fsize;
 | 
			
		||||
 | 
			
		||||
    if(sscanf(ptr, "Content-Length: %zd", &fsize) == 1) {
 | 
			
		||||
        s->s->len = fsize;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return realsize;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static size_t curl_read_cb(void *ptr, size_t size, size_t nmemb, void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    CURLState *s = ((CURLState*)opaque);
 | 
			
		||||
    size_t realsize = size * nmemb;
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
    DPRINTF("CURL: Just reading %zd bytes\n", realsize);
 | 
			
		||||
 | 
			
		||||
    if (!s || !s->orig_buf)
 | 
			
		||||
        goto read_end;
 | 
			
		||||
 | 
			
		||||
    memcpy(s->orig_buf + s->buf_off, ptr, realsize);
 | 
			
		||||
    s->buf_off += realsize;
 | 
			
		||||
 | 
			
		||||
    for(i=0; i<CURL_NUM_ACB; i++) {
 | 
			
		||||
        CURLAIOCB *acb = s->acb[i];
 | 
			
		||||
 | 
			
		||||
        if (!acb)
 | 
			
		||||
            continue;
 | 
			
		||||
 | 
			
		||||
        if ((s->buf_off >= acb->end)) {
 | 
			
		||||
            qemu_iovec_from_buffer(acb->qiov, s->orig_buf + acb->start,
 | 
			
		||||
                                   acb->end - acb->start);
 | 
			
		||||
            acb->common.cb(acb->common.opaque, 0);
 | 
			
		||||
            qemu_aio_release(acb);
 | 
			
		||||
            s->acb[i] = NULL;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
read_end:
 | 
			
		||||
    return realsize;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int curl_find_buf(BDRVCURLState *s, size_t start, size_t len,
 | 
			
		||||
                         CURLAIOCB *acb)
 | 
			
		||||
{
 | 
			
		||||
    int i;
 | 
			
		||||
    size_t end = start + len;
 | 
			
		||||
 | 
			
		||||
    for (i=0; i<CURL_NUM_STATES; i++) {
 | 
			
		||||
        CURLState *state = &s->states[i];
 | 
			
		||||
        size_t buf_end = (state->buf_start + state->buf_off);
 | 
			
		||||
        size_t buf_fend = (state->buf_start + state->buf_len);
 | 
			
		||||
 | 
			
		||||
        if (!state->orig_buf)
 | 
			
		||||
            continue;
 | 
			
		||||
        if (!state->buf_off)
 | 
			
		||||
            continue;
 | 
			
		||||
 | 
			
		||||
        // Does the existing buffer cover our section?
 | 
			
		||||
        if ((start >= state->buf_start) &&
 | 
			
		||||
            (start <= buf_end) &&
 | 
			
		||||
            (end >= state->buf_start) &&
 | 
			
		||||
            (end <= buf_end))
 | 
			
		||||
        {
 | 
			
		||||
            char *buf = state->orig_buf + (start - state->buf_start);
 | 
			
		||||
 | 
			
		||||
            qemu_iovec_from_buffer(acb->qiov, buf, len);
 | 
			
		||||
            acb->common.cb(acb->common.opaque, 0);
 | 
			
		||||
 | 
			
		||||
            return FIND_RET_OK;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Wait for unfinished chunks
 | 
			
		||||
        if ((start >= state->buf_start) &&
 | 
			
		||||
            (start <= buf_fend) &&
 | 
			
		||||
            (end >= state->buf_start) &&
 | 
			
		||||
            (end <= buf_fend))
 | 
			
		||||
        {
 | 
			
		||||
            int j;
 | 
			
		||||
 | 
			
		||||
            acb->start = start - state->buf_start;
 | 
			
		||||
            acb->end = acb->start + len;
 | 
			
		||||
 | 
			
		||||
            for (j=0; j<CURL_NUM_ACB; j++) {
 | 
			
		||||
                if (!state->acb[j]) {
 | 
			
		||||
                    state->acb[j] = acb;
 | 
			
		||||
                    return FIND_RET_WAIT;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return FIND_RET_NONE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void curl_multi_do(void *arg)
 | 
			
		||||
{
 | 
			
		||||
    BDRVCURLState *s = (BDRVCURLState *)arg;
 | 
			
		||||
    int running;
 | 
			
		||||
    int r;
 | 
			
		||||
    int msgs_in_queue;
 | 
			
		||||
 | 
			
		||||
    if (!s->multi)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    do {
 | 
			
		||||
        r = curl_multi_socket_all(s->multi, &running);
 | 
			
		||||
    } while(r == CURLM_CALL_MULTI_PERFORM);
 | 
			
		||||
 | 
			
		||||
    /* Try to find done transfers, so we can free the easy
 | 
			
		||||
     * handle again. */
 | 
			
		||||
    do {
 | 
			
		||||
        CURLMsg *msg;
 | 
			
		||||
        msg = curl_multi_info_read(s->multi, &msgs_in_queue);
 | 
			
		||||
 | 
			
		||||
        if (!msg)
 | 
			
		||||
            break;
 | 
			
		||||
        if (msg->msg == CURLMSG_NONE)
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        switch (msg->msg) {
 | 
			
		||||
            case CURLMSG_DONE:
 | 
			
		||||
            {
 | 
			
		||||
                CURLState *state = NULL;
 | 
			
		||||
                curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, (char**)&state);
 | 
			
		||||
                curl_clean_state(state);
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            default:
 | 
			
		||||
                msgs_in_queue = 0;
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
    } while(msgs_in_queue);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static CURLState *curl_init_state(BDRVCURLState *s)
 | 
			
		||||
{
 | 
			
		||||
    CURLState *state = NULL;
 | 
			
		||||
    int i, j;
 | 
			
		||||
 | 
			
		||||
    do {
 | 
			
		||||
        for (i=0; i<CURL_NUM_STATES; i++) {
 | 
			
		||||
            for (j=0; j<CURL_NUM_ACB; j++)
 | 
			
		||||
                if (s->states[i].acb[j])
 | 
			
		||||
                    continue;
 | 
			
		||||
            if (s->states[i].in_use)
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            state = &s->states[i];
 | 
			
		||||
            state->in_use = 1;
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        if (!state) {
 | 
			
		||||
            usleep(100);
 | 
			
		||||
            curl_multi_do(s);
 | 
			
		||||
        }
 | 
			
		||||
    } while(!state);
 | 
			
		||||
 | 
			
		||||
    if (state->curl)
 | 
			
		||||
        goto has_curl;
 | 
			
		||||
 | 
			
		||||
    state->curl = curl_easy_init();
 | 
			
		||||
    if (!state->curl)
 | 
			
		||||
        return NULL;
 | 
			
		||||
    curl_easy_setopt(state->curl, CURLOPT_URL, s->url);
 | 
			
		||||
    curl_easy_setopt(state->curl, CURLOPT_TIMEOUT, 5);
 | 
			
		||||
    curl_easy_setopt(state->curl, CURLOPT_WRITEFUNCTION, (void *)curl_read_cb);
 | 
			
		||||
    curl_easy_setopt(state->curl, CURLOPT_WRITEDATA, (void *)state);
 | 
			
		||||
    curl_easy_setopt(state->curl, CURLOPT_PRIVATE, (void *)state);
 | 
			
		||||
    curl_easy_setopt(state->curl, CURLOPT_AUTOREFERER, 1);
 | 
			
		||||
    curl_easy_setopt(state->curl, CURLOPT_FOLLOWLOCATION, 1);
 | 
			
		||||
    curl_easy_setopt(state->curl, CURLOPT_NOSIGNAL, 1);
 | 
			
		||||
    curl_easy_setopt(state->curl, CURLOPT_ERRORBUFFER, state->errmsg);
 | 
			
		||||
    
 | 
			
		||||
#ifdef DEBUG_VERBOSE
 | 
			
		||||
    curl_easy_setopt(state->curl, CURLOPT_VERBOSE, 1);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
has_curl:
 | 
			
		||||
 | 
			
		||||
    state->s = s;
 | 
			
		||||
 | 
			
		||||
    return state;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void curl_clean_state(CURLState *s)
 | 
			
		||||
{
 | 
			
		||||
    if (s->s->multi)
 | 
			
		||||
        curl_multi_remove_handle(s->s->multi, s->curl);
 | 
			
		||||
    s->in_use = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int curl_open(BlockDriverState *bs, const char *filename, int flags)
 | 
			
		||||
{
 | 
			
		||||
    BDRVCURLState *s = bs->opaque;
 | 
			
		||||
    CURLState *state = NULL;
 | 
			
		||||
    double d;
 | 
			
		||||
 | 
			
		||||
    #define RA_OPTSTR ":readahead="
 | 
			
		||||
    char *file;
 | 
			
		||||
    char *ra;
 | 
			
		||||
    const char *ra_val;
 | 
			
		||||
    int parse_state = 0;
 | 
			
		||||
 | 
			
		||||
    static int inited = 0;
 | 
			
		||||
 | 
			
		||||
    file = qemu_strdup(filename);
 | 
			
		||||
    s->readahead_size = READ_AHEAD_SIZE;
 | 
			
		||||
 | 
			
		||||
    /* Parse a trailing ":readahead=#:" param, if present. */
 | 
			
		||||
    ra = file + strlen(file) - 1;
 | 
			
		||||
    while (ra >= file) {
 | 
			
		||||
        if (parse_state == 0) {
 | 
			
		||||
            if (*ra == ':')
 | 
			
		||||
                parse_state++;
 | 
			
		||||
            else
 | 
			
		||||
                break;
 | 
			
		||||
        } else if (parse_state == 1) {
 | 
			
		||||
            if (*ra > '9' || *ra < '0') {
 | 
			
		||||
                char *opt_start = ra - strlen(RA_OPTSTR) + 1;
 | 
			
		||||
                if (opt_start > file &&
 | 
			
		||||
                    strncmp(opt_start, RA_OPTSTR, strlen(RA_OPTSTR)) == 0) {
 | 
			
		||||
                    ra_val = ra + 1;
 | 
			
		||||
                    ra -= strlen(RA_OPTSTR) - 1;
 | 
			
		||||
                    *ra = '\0';
 | 
			
		||||
                    s->readahead_size = atoi(ra_val);
 | 
			
		||||
                    break;
 | 
			
		||||
                } else {
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        ra--;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ((s->readahead_size & 0x1ff) != 0) {
 | 
			
		||||
        fprintf(stderr, "HTTP_READAHEAD_SIZE %zd is not a multiple of 512\n",
 | 
			
		||||
                s->readahead_size);
 | 
			
		||||
        goto out_noclean;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!inited) {
 | 
			
		||||
        curl_global_init(CURL_GLOBAL_ALL);
 | 
			
		||||
        inited = 1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    DPRINTF("CURL: Opening %s\n", file);
 | 
			
		||||
    s->url = file;
 | 
			
		||||
    state = curl_init_state(s);
 | 
			
		||||
    if (!state)
 | 
			
		||||
        goto out_noclean;
 | 
			
		||||
 | 
			
		||||
    // Get file size
 | 
			
		||||
 | 
			
		||||
    curl_easy_setopt(state->curl, CURLOPT_NOBODY, 1);
 | 
			
		||||
    curl_easy_setopt(state->curl, CURLOPT_WRITEFUNCTION, (void *)curl_size_cb);
 | 
			
		||||
    if (curl_easy_perform(state->curl))
 | 
			
		||||
        goto out;
 | 
			
		||||
    curl_easy_getinfo(state->curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &d);
 | 
			
		||||
    curl_easy_setopt(state->curl, CURLOPT_WRITEFUNCTION, (void *)curl_read_cb);
 | 
			
		||||
    curl_easy_setopt(state->curl, CURLOPT_NOBODY, 0);
 | 
			
		||||
    if (d)
 | 
			
		||||
        s->len = (size_t)d;
 | 
			
		||||
    else if(!s->len)
 | 
			
		||||
        goto out;
 | 
			
		||||
    DPRINTF("CURL: Size = %zd\n", s->len);
 | 
			
		||||
 | 
			
		||||
    curl_clean_state(state);
 | 
			
		||||
    curl_easy_cleanup(state->curl);
 | 
			
		||||
    state->curl = NULL;
 | 
			
		||||
 | 
			
		||||
    // Now we know the file exists and its size, so let's
 | 
			
		||||
    // initialize the multi interface!
 | 
			
		||||
 | 
			
		||||
    s->multi = curl_multi_init();
 | 
			
		||||
    curl_multi_setopt( s->multi, CURLMOPT_SOCKETDATA, s); 
 | 
			
		||||
    curl_multi_setopt( s->multi, CURLMOPT_SOCKETFUNCTION, curl_sock_cb ); 
 | 
			
		||||
    curl_multi_do(s);
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
 | 
			
		||||
out:
 | 
			
		||||
    fprintf(stderr, "CURL: Error opening file: %s\n", state->errmsg);
 | 
			
		||||
    curl_easy_cleanup(state->curl);
 | 
			
		||||
    state->curl = NULL;
 | 
			
		||||
out_noclean:
 | 
			
		||||
    qemu_free(file);
 | 
			
		||||
    return -EINVAL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void curl_aio_cancel(BlockDriverAIOCB *blockacb)
 | 
			
		||||
{
 | 
			
		||||
    // Do we have to implement canceling? Seems to work without...
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static AIOPool curl_aio_pool = {
 | 
			
		||||
    .aiocb_size         = sizeof(CURLAIOCB),
 | 
			
		||||
    .cancel             = curl_aio_cancel,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static BlockDriverAIOCB *curl_aio_readv(BlockDriverState *bs,
 | 
			
		||||
        int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
 | 
			
		||||
        BlockDriverCompletionFunc *cb, void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    BDRVCURLState *s = bs->opaque;
 | 
			
		||||
    CURLAIOCB *acb;
 | 
			
		||||
    size_t start = sector_num * SECTOR_SIZE;
 | 
			
		||||
    size_t end;
 | 
			
		||||
    CURLState *state;
 | 
			
		||||
 | 
			
		||||
    acb = qemu_aio_get(&curl_aio_pool, bs, cb, opaque);
 | 
			
		||||
    if (!acb)
 | 
			
		||||
        return NULL;
 | 
			
		||||
 | 
			
		||||
    acb->qiov = qiov;
 | 
			
		||||
 | 
			
		||||
    // In case we have the requested data already (e.g. read-ahead),
 | 
			
		||||
    // we can just call the callback and be done.
 | 
			
		||||
 | 
			
		||||
    switch (curl_find_buf(s, start, nb_sectors * SECTOR_SIZE, acb)) {
 | 
			
		||||
        case FIND_RET_OK:
 | 
			
		||||
            qemu_aio_release(acb);
 | 
			
		||||
            // fall through
 | 
			
		||||
        case FIND_RET_WAIT:
 | 
			
		||||
            return &acb->common;
 | 
			
		||||
        default:
 | 
			
		||||
            break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // No cache found, so let's start a new request
 | 
			
		||||
 | 
			
		||||
    state = curl_init_state(s);
 | 
			
		||||
    if (!state)
 | 
			
		||||
        return NULL;
 | 
			
		||||
 | 
			
		||||
    acb->start = 0;
 | 
			
		||||
    acb->end = (nb_sectors * SECTOR_SIZE);
 | 
			
		||||
 | 
			
		||||
    state->buf_off = 0;
 | 
			
		||||
    if (state->orig_buf)
 | 
			
		||||
        qemu_free(state->orig_buf);
 | 
			
		||||
    state->buf_start = start;
 | 
			
		||||
    state->buf_len = acb->end + s->readahead_size;
 | 
			
		||||
    end = MIN(start + state->buf_len, s->len) - 1;
 | 
			
		||||
    state->orig_buf = qemu_malloc(state->buf_len);
 | 
			
		||||
    state->acb[0] = acb;
 | 
			
		||||
 | 
			
		||||
    snprintf(state->range, 127, "%zd-%zd", start, end);
 | 
			
		||||
    DPRINTF("CURL (AIO): Reading %d at %zd (%s)\n",
 | 
			
		||||
            (nb_sectors * SECTOR_SIZE), start, state->range);
 | 
			
		||||
    curl_easy_setopt(state->curl, CURLOPT_RANGE, state->range);
 | 
			
		||||
 | 
			
		||||
    curl_multi_add_handle(s->multi, state->curl);
 | 
			
		||||
    curl_multi_do(s);
 | 
			
		||||
 | 
			
		||||
    return &acb->common;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void curl_close(BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
    BDRVCURLState *s = bs->opaque;
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
    DPRINTF("CURL: Close\n");
 | 
			
		||||
    for (i=0; i<CURL_NUM_STATES; i++) {
 | 
			
		||||
        if (s->states[i].in_use)
 | 
			
		||||
            curl_clean_state(&s->states[i]);
 | 
			
		||||
        if (s->states[i].curl) {
 | 
			
		||||
            curl_easy_cleanup(s->states[i].curl);
 | 
			
		||||
            s->states[i].curl = NULL;
 | 
			
		||||
        }
 | 
			
		||||
        if (s->states[i].orig_buf) {
 | 
			
		||||
            qemu_free(s->states[i].orig_buf);
 | 
			
		||||
            s->states[i].orig_buf = NULL;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    if (s->multi)
 | 
			
		||||
        curl_multi_cleanup(s->multi);
 | 
			
		||||
    if (s->url)
 | 
			
		||||
        free(s->url);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int64_t curl_getlength(BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
    BDRVCURLState *s = bs->opaque;
 | 
			
		||||
    return s->len;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static BlockDriver bdrv_http = {
 | 
			
		||||
    .format_name     = "http",
 | 
			
		||||
    .protocol_name   = "http",
 | 
			
		||||
 | 
			
		||||
    .instance_size   = sizeof(BDRVCURLState),
 | 
			
		||||
    .bdrv_file_open  = curl_open,
 | 
			
		||||
    .bdrv_close      = curl_close,
 | 
			
		||||
    .bdrv_getlength  = curl_getlength,
 | 
			
		||||
 | 
			
		||||
    .bdrv_aio_readv  = curl_aio_readv,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static BlockDriver bdrv_https = {
 | 
			
		||||
    .format_name     = "https",
 | 
			
		||||
    .protocol_name   = "https",
 | 
			
		||||
 | 
			
		||||
    .instance_size   = sizeof(BDRVCURLState),
 | 
			
		||||
    .bdrv_file_open  = curl_open,
 | 
			
		||||
    .bdrv_close      = curl_close,
 | 
			
		||||
    .bdrv_getlength  = curl_getlength,
 | 
			
		||||
 | 
			
		||||
    .bdrv_aio_readv  = curl_aio_readv,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static BlockDriver bdrv_ftp = {
 | 
			
		||||
    .format_name     = "ftp",
 | 
			
		||||
    .protocol_name   = "ftp",
 | 
			
		||||
 | 
			
		||||
    .instance_size   = sizeof(BDRVCURLState),
 | 
			
		||||
    .bdrv_file_open  = curl_open,
 | 
			
		||||
    .bdrv_close      = curl_close,
 | 
			
		||||
    .bdrv_getlength  = curl_getlength,
 | 
			
		||||
 | 
			
		||||
    .bdrv_aio_readv  = curl_aio_readv,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static BlockDriver bdrv_ftps = {
 | 
			
		||||
    .format_name     = "ftps",
 | 
			
		||||
    .protocol_name   = "ftps",
 | 
			
		||||
 | 
			
		||||
    .instance_size   = sizeof(BDRVCURLState),
 | 
			
		||||
    .bdrv_file_open  = curl_open,
 | 
			
		||||
    .bdrv_close      = curl_close,
 | 
			
		||||
    .bdrv_getlength  = curl_getlength,
 | 
			
		||||
 | 
			
		||||
    .bdrv_aio_readv  = curl_aio_readv,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static BlockDriver bdrv_tftp = {
 | 
			
		||||
    .format_name     = "tftp",
 | 
			
		||||
    .protocol_name   = "tftp",
 | 
			
		||||
 | 
			
		||||
    .instance_size   = sizeof(BDRVCURLState),
 | 
			
		||||
    .bdrv_file_open  = curl_open,
 | 
			
		||||
    .bdrv_close      = curl_close,
 | 
			
		||||
    .bdrv_getlength  = curl_getlength,
 | 
			
		||||
 | 
			
		||||
    .bdrv_aio_readv  = curl_aio_readv,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void curl_block_init(void)
 | 
			
		||||
{
 | 
			
		||||
    bdrv_register(&bdrv_http);
 | 
			
		||||
    bdrv_register(&bdrv_https);
 | 
			
		||||
    bdrv_register(&bdrv_ftp);
 | 
			
		||||
    bdrv_register(&bdrv_ftps);
 | 
			
		||||
    bdrv_register(&bdrv_tftp);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
block_init(curl_block_init);
 | 
			
		||||
							
								
								
									
										193
									
								
								block/nbd.c
									
									
									
									
									
								
							
							
						
						
									
										193
									
								
								block/nbd.c
									
									
									
									
									
								
							@@ -1,193 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * QEMU Block driver for  NBD
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2008 Bull S.A.S.
 | 
			
		||||
 *     Author: Laurent Vivier <Laurent.Vivier@bull.net>
 | 
			
		||||
 *
 | 
			
		||||
 * Some parts:
 | 
			
		||||
 *    Copyright (C) 2007 Anthony Liguori <anthony@codemonkey.ws>
 | 
			
		||||
 *
 | 
			
		||||
 * 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-common.h"
 | 
			
		||||
#include "nbd.h"
 | 
			
		||||
#include "module.h"
 | 
			
		||||
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
 | 
			
		||||
typedef struct BDRVNBDState {
 | 
			
		||||
    int sock;
 | 
			
		||||
    off_t size;
 | 
			
		||||
    size_t blocksize;
 | 
			
		||||
} BDRVNBDState;
 | 
			
		||||
 | 
			
		||||
static int nbd_open(BlockDriverState *bs, const char* filename, int flags)
 | 
			
		||||
{
 | 
			
		||||
    BDRVNBDState *s = bs->opaque;
 | 
			
		||||
    const char *host;
 | 
			
		||||
    const char *unixpath;
 | 
			
		||||
    int sock;
 | 
			
		||||
    off_t size;
 | 
			
		||||
    size_t blocksize;
 | 
			
		||||
    int ret;
 | 
			
		||||
 | 
			
		||||
    if (!strstart(filename, "nbd:", &host))
 | 
			
		||||
        return -EINVAL;
 | 
			
		||||
 | 
			
		||||
    if (strstart(host, "unix:", &unixpath)) {
 | 
			
		||||
 | 
			
		||||
        if (unixpath[0] != '/')
 | 
			
		||||
            return -EINVAL;
 | 
			
		||||
 | 
			
		||||
        sock = unix_socket_outgoing(unixpath);
 | 
			
		||||
 | 
			
		||||
    } else {
 | 
			
		||||
        uint16_t port;
 | 
			
		||||
        char *p, *r;
 | 
			
		||||
        char hostname[128];
 | 
			
		||||
 | 
			
		||||
        pstrcpy(hostname, 128, host);
 | 
			
		||||
 | 
			
		||||
        p = strchr(hostname, ':');
 | 
			
		||||
        if (p == NULL)
 | 
			
		||||
            return -EINVAL;
 | 
			
		||||
 | 
			
		||||
        *p = '\0';
 | 
			
		||||
        p++;
 | 
			
		||||
 | 
			
		||||
        port = strtol(p, &r, 0);
 | 
			
		||||
        if (r == p)
 | 
			
		||||
            return -EINVAL;
 | 
			
		||||
        sock = tcp_socket_outgoing(hostname, port);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (sock == -1)
 | 
			
		||||
        return -errno;
 | 
			
		||||
 | 
			
		||||
    ret = nbd_receive_negotiate(sock, &size, &blocksize);
 | 
			
		||||
    if (ret == -1)
 | 
			
		||||
        return -errno;
 | 
			
		||||
 | 
			
		||||
    s->sock = sock;
 | 
			
		||||
    s->size = size;
 | 
			
		||||
    s->blocksize = blocksize;
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int nbd_read(BlockDriverState *bs, int64_t sector_num,
 | 
			
		||||
                    uint8_t *buf, int nb_sectors)
 | 
			
		||||
{
 | 
			
		||||
    BDRVNBDState *s = bs->opaque;
 | 
			
		||||
    struct nbd_request request;
 | 
			
		||||
    struct nbd_reply reply;
 | 
			
		||||
 | 
			
		||||
    request.type = NBD_CMD_READ;
 | 
			
		||||
    request.handle = (uint64_t)(intptr_t)bs;
 | 
			
		||||
    request.from = sector_num * 512;;
 | 
			
		||||
    request.len = nb_sectors * 512;
 | 
			
		||||
 | 
			
		||||
    if (nbd_send_request(s->sock, &request) == -1)
 | 
			
		||||
        return -errno;
 | 
			
		||||
 | 
			
		||||
    if (nbd_receive_reply(s->sock, &reply) == -1)
 | 
			
		||||
        return -errno;
 | 
			
		||||
 | 
			
		||||
    if (reply.error !=0)
 | 
			
		||||
        return -reply.error;
 | 
			
		||||
 | 
			
		||||
    if (reply.handle != request.handle)
 | 
			
		||||
        return -EIO;
 | 
			
		||||
 | 
			
		||||
    if (nbd_wr_sync(s->sock, buf, request.len, 1) != request.len)
 | 
			
		||||
        return -EIO;
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int nbd_write(BlockDriverState *bs, int64_t sector_num,
 | 
			
		||||
                     const uint8_t *buf, int nb_sectors)
 | 
			
		||||
{
 | 
			
		||||
    BDRVNBDState *s = bs->opaque;
 | 
			
		||||
    struct nbd_request request;
 | 
			
		||||
    struct nbd_reply reply;
 | 
			
		||||
 | 
			
		||||
    request.type = NBD_CMD_WRITE;
 | 
			
		||||
    request.handle = (uint64_t)(intptr_t)bs;
 | 
			
		||||
    request.from = sector_num * 512;;
 | 
			
		||||
    request.len = nb_sectors * 512;
 | 
			
		||||
 | 
			
		||||
    if (nbd_send_request(s->sock, &request) == -1)
 | 
			
		||||
        return -errno;
 | 
			
		||||
 | 
			
		||||
    if (nbd_wr_sync(s->sock, (uint8_t*)buf, request.len, 0) != request.len)
 | 
			
		||||
        return -EIO;
 | 
			
		||||
 | 
			
		||||
    if (nbd_receive_reply(s->sock, &reply) == -1)
 | 
			
		||||
        return -errno;
 | 
			
		||||
 | 
			
		||||
    if (reply.error !=0)
 | 
			
		||||
        return -reply.error;
 | 
			
		||||
 | 
			
		||||
    if (reply.handle != request.handle)
 | 
			
		||||
        return -EIO;
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void nbd_close(BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
    BDRVNBDState *s = bs->opaque;
 | 
			
		||||
    struct nbd_request request;
 | 
			
		||||
 | 
			
		||||
    request.type = NBD_CMD_DISC;
 | 
			
		||||
    request.handle = (uint64_t)(intptr_t)bs;
 | 
			
		||||
    request.from = 0;
 | 
			
		||||
    request.len = 0;
 | 
			
		||||
    nbd_send_request(s->sock, &request);
 | 
			
		||||
 | 
			
		||||
    close(s->sock);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int64_t nbd_getlength(BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
    BDRVNBDState *s = bs->opaque;
 | 
			
		||||
 | 
			
		||||
    return s->size;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static BlockDriver bdrv_nbd = {
 | 
			
		||||
    .format_name	= "nbd",
 | 
			
		||||
    .instance_size	= sizeof(BDRVNBDState),
 | 
			
		||||
    .bdrv_file_open	= nbd_open,
 | 
			
		||||
    .bdrv_read		= nbd_read,
 | 
			
		||||
    .bdrv_write		= nbd_write,
 | 
			
		||||
    .bdrv_close		= nbd_close,
 | 
			
		||||
    .bdrv_getlength	= nbd_getlength,
 | 
			
		||||
    .protocol_name	= "nbd",
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void bdrv_nbd_init(void)
 | 
			
		||||
{
 | 
			
		||||
    bdrv_register(&bdrv_nbd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
block_init(bdrv_nbd_init);
 | 
			
		||||
@@ -1,157 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Block driver for Parallels disk image format
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2007 Alex Beregszaszi
 | 
			
		||||
 *
 | 
			
		||||
 * This code is based on comparing different disk images created by Parallels.
 | 
			
		||||
 *
 | 
			
		||||
 * 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-common.h"
 | 
			
		||||
#include "block_int.h"
 | 
			
		||||
#include "module.h"
 | 
			
		||||
 | 
			
		||||
/**************************************************************/
 | 
			
		||||
 | 
			
		||||
#define HEADER_MAGIC "WithoutFreeSpace"
 | 
			
		||||
#define HEADER_VERSION 2
 | 
			
		||||
#define HEADER_SIZE 64
 | 
			
		||||
 | 
			
		||||
// always little-endian
 | 
			
		||||
struct parallels_header {
 | 
			
		||||
    char magic[16]; // "WithoutFreeSpace"
 | 
			
		||||
    uint32_t version;
 | 
			
		||||
    uint32_t heads;
 | 
			
		||||
    uint32_t cylinders;
 | 
			
		||||
    uint32_t tracks;
 | 
			
		||||
    uint32_t catalog_entries;
 | 
			
		||||
    uint32_t nb_sectors;
 | 
			
		||||
    char padding[24];
 | 
			
		||||
} __attribute__((packed));
 | 
			
		||||
 | 
			
		||||
typedef struct BDRVParallelsState {
 | 
			
		||||
 | 
			
		||||
    uint32_t *catalog_bitmap;
 | 
			
		||||
    int catalog_size;
 | 
			
		||||
 | 
			
		||||
    int tracks;
 | 
			
		||||
} BDRVParallelsState;
 | 
			
		||||
 | 
			
		||||
static int parallels_probe(const uint8_t *buf, int buf_size, const char *filename)
 | 
			
		||||
{
 | 
			
		||||
    const struct parallels_header *ph = (const void *)buf;
 | 
			
		||||
 | 
			
		||||
    if (buf_size < HEADER_SIZE)
 | 
			
		||||
	return 0;
 | 
			
		||||
 | 
			
		||||
    if (!memcmp(ph->magic, HEADER_MAGIC, 16) &&
 | 
			
		||||
	(le32_to_cpu(ph->version) == HEADER_VERSION))
 | 
			
		||||
	return 100;
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int parallels_open(BlockDriverState *bs, int flags)
 | 
			
		||||
{
 | 
			
		||||
    BDRVParallelsState *s = bs->opaque;
 | 
			
		||||
    int i;
 | 
			
		||||
    struct parallels_header ph;
 | 
			
		||||
 | 
			
		||||
    bs->read_only = 1; // no write support yet
 | 
			
		||||
 | 
			
		||||
    if (bdrv_pread(bs->file, 0, &ph, sizeof(ph)) != sizeof(ph))
 | 
			
		||||
        goto fail;
 | 
			
		||||
 | 
			
		||||
    if (memcmp(ph.magic, HEADER_MAGIC, 16) ||
 | 
			
		||||
	(le32_to_cpu(ph.version) != HEADER_VERSION)) {
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bs->total_sectors = le32_to_cpu(ph.nb_sectors);
 | 
			
		||||
 | 
			
		||||
    s->tracks = le32_to_cpu(ph.tracks);
 | 
			
		||||
 | 
			
		||||
    s->catalog_size = le32_to_cpu(ph.catalog_entries);
 | 
			
		||||
    s->catalog_bitmap = qemu_malloc(s->catalog_size * 4);
 | 
			
		||||
    if (bdrv_pread(bs->file, 64, s->catalog_bitmap, s->catalog_size * 4) !=
 | 
			
		||||
	s->catalog_size * 4)
 | 
			
		||||
	goto fail;
 | 
			
		||||
    for (i = 0; i < s->catalog_size; i++)
 | 
			
		||||
	le32_to_cpus(&s->catalog_bitmap[i]);
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
fail:
 | 
			
		||||
    if (s->catalog_bitmap)
 | 
			
		||||
	qemu_free(s->catalog_bitmap);
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int64_t seek_to_sector(BlockDriverState *bs, int64_t sector_num)
 | 
			
		||||
{
 | 
			
		||||
    BDRVParallelsState *s = bs->opaque;
 | 
			
		||||
    uint32_t index, offset;
 | 
			
		||||
 | 
			
		||||
    index = sector_num / s->tracks;
 | 
			
		||||
    offset = sector_num % s->tracks;
 | 
			
		||||
 | 
			
		||||
    /* not allocated */
 | 
			
		||||
    if ((index > s->catalog_size) || (s->catalog_bitmap[index] == 0))
 | 
			
		||||
	return -1;
 | 
			
		||||
    return (uint64_t)(s->catalog_bitmap[index] + offset) * 512;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int parallels_read(BlockDriverState *bs, int64_t sector_num,
 | 
			
		||||
                    uint8_t *buf, int nb_sectors)
 | 
			
		||||
{
 | 
			
		||||
    while (nb_sectors > 0) {
 | 
			
		||||
        int64_t position = seek_to_sector(bs, sector_num);
 | 
			
		||||
        if (position >= 0) {
 | 
			
		||||
            if (bdrv_pread(bs->file, position, buf, 512) != 512)
 | 
			
		||||
                return -1;
 | 
			
		||||
        } else {
 | 
			
		||||
            memset(buf, 0, 512);
 | 
			
		||||
        }
 | 
			
		||||
        nb_sectors--;
 | 
			
		||||
        sector_num++;
 | 
			
		||||
        buf += 512;
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void parallels_close(BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
    BDRVParallelsState *s = bs->opaque;
 | 
			
		||||
    qemu_free(s->catalog_bitmap);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static BlockDriver bdrv_parallels = {
 | 
			
		||||
    .format_name	= "parallels",
 | 
			
		||||
    .instance_size	= sizeof(BDRVParallelsState),
 | 
			
		||||
    .bdrv_probe		= parallels_probe,
 | 
			
		||||
    .bdrv_open		= parallels_open,
 | 
			
		||||
    .bdrv_read		= parallels_read,
 | 
			
		||||
    .bdrv_close		= parallels_close,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void bdrv_parallels_init(void)
 | 
			
		||||
{
 | 
			
		||||
    bdrv_register(&bdrv_parallels);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
block_init(bdrv_parallels_init);
 | 
			
		||||
@@ -1,931 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Block driver for the QCOW version 2 format
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2004-2006 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 <zlib.h>
 | 
			
		||||
 | 
			
		||||
#include "qemu-common.h"
 | 
			
		||||
#include "block_int.h"
 | 
			
		||||
#include "block/qcow2.h"
 | 
			
		||||
 | 
			
		||||
int qcow2_grow_l1_table(BlockDriverState *bs, int min_size)
 | 
			
		||||
{
 | 
			
		||||
    BDRVQcowState *s = bs->opaque;
 | 
			
		||||
    int new_l1_size, new_l1_size2, ret, i;
 | 
			
		||||
    uint64_t *new_l1_table;
 | 
			
		||||
    int64_t new_l1_table_offset;
 | 
			
		||||
    uint8_t data[12];
 | 
			
		||||
 | 
			
		||||
    new_l1_size = s->l1_size;
 | 
			
		||||
    if (min_size <= new_l1_size)
 | 
			
		||||
        return 0;
 | 
			
		||||
    if (new_l1_size == 0) {
 | 
			
		||||
        new_l1_size = 1;
 | 
			
		||||
    }
 | 
			
		||||
    while (min_size > new_l1_size) {
 | 
			
		||||
        new_l1_size = (new_l1_size * 3 + 1) / 2;
 | 
			
		||||
    }
 | 
			
		||||
#ifdef DEBUG_ALLOC2
 | 
			
		||||
    printf("grow l1_table from %d to %d\n", s->l1_size, new_l1_size);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    new_l1_size2 = sizeof(uint64_t) * new_l1_size;
 | 
			
		||||
    new_l1_table = qemu_mallocz(align_offset(new_l1_size2, 512));
 | 
			
		||||
    memcpy(new_l1_table, s->l1_table, s->l1_size * sizeof(uint64_t));
 | 
			
		||||
 | 
			
		||||
    /* write new table (align to cluster) */
 | 
			
		||||
    BLKDBG_EVENT(bs->file, BLKDBG_L1_GROW_ALLOC_TABLE);
 | 
			
		||||
    new_l1_table_offset = qcow2_alloc_clusters(bs, new_l1_size2);
 | 
			
		||||
    if (new_l1_table_offset < 0) {
 | 
			
		||||
        qemu_free(new_l1_table);
 | 
			
		||||
        return new_l1_table_offset;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    BLKDBG_EVENT(bs->file, BLKDBG_L1_GROW_WRITE_TABLE);
 | 
			
		||||
    for(i = 0; i < s->l1_size; i++)
 | 
			
		||||
        new_l1_table[i] = cpu_to_be64(new_l1_table[i]);
 | 
			
		||||
    ret = bdrv_pwrite_sync(bs->file, new_l1_table_offset, new_l1_table, new_l1_size2);
 | 
			
		||||
    if (ret < 0)
 | 
			
		||||
        goto fail;
 | 
			
		||||
    for(i = 0; i < s->l1_size; i++)
 | 
			
		||||
        new_l1_table[i] = be64_to_cpu(new_l1_table[i]);
 | 
			
		||||
 | 
			
		||||
    /* set new table */
 | 
			
		||||
    BLKDBG_EVENT(bs->file, BLKDBG_L1_GROW_ACTIVATE_TABLE);
 | 
			
		||||
    cpu_to_be32w((uint32_t*)data, new_l1_size);
 | 
			
		||||
    cpu_to_be64w((uint64_t*)(data + 4), new_l1_table_offset);
 | 
			
		||||
    ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, l1_size), data,sizeof(data));
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
    qemu_free(s->l1_table);
 | 
			
		||||
    qcow2_free_clusters(bs, s->l1_table_offset, s->l1_size * sizeof(uint64_t));
 | 
			
		||||
    s->l1_table_offset = new_l1_table_offset;
 | 
			
		||||
    s->l1_table = new_l1_table;
 | 
			
		||||
    s->l1_size = new_l1_size;
 | 
			
		||||
    return 0;
 | 
			
		||||
 fail:
 | 
			
		||||
    qemu_free(new_l1_table);
 | 
			
		||||
    qcow2_free_clusters(bs, new_l1_table_offset, new_l1_size2);
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void qcow2_l2_cache_reset(BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
    BDRVQcowState *s = bs->opaque;
 | 
			
		||||
 | 
			
		||||
    memset(s->l2_cache, 0, s->l2_size * L2_CACHE_SIZE * sizeof(uint64_t));
 | 
			
		||||
    memset(s->l2_cache_offsets, 0, L2_CACHE_SIZE * sizeof(uint64_t));
 | 
			
		||||
    memset(s->l2_cache_counts, 0, L2_CACHE_SIZE * sizeof(uint32_t));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline int l2_cache_new_entry(BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
    BDRVQcowState *s = bs->opaque;
 | 
			
		||||
    uint32_t min_count;
 | 
			
		||||
    int min_index, i;
 | 
			
		||||
 | 
			
		||||
    /* find a new entry in the least used one */
 | 
			
		||||
    min_index = 0;
 | 
			
		||||
    min_count = 0xffffffff;
 | 
			
		||||
    for(i = 0; i < L2_CACHE_SIZE; i++) {
 | 
			
		||||
        if (s->l2_cache_counts[i] < min_count) {
 | 
			
		||||
            min_count = s->l2_cache_counts[i];
 | 
			
		||||
            min_index = i;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return min_index;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * seek_l2_table
 | 
			
		||||
 *
 | 
			
		||||
 * seek l2_offset in the l2_cache table
 | 
			
		||||
 * if not found, return NULL,
 | 
			
		||||
 * if found,
 | 
			
		||||
 *   increments the l2 cache hit count of the entry,
 | 
			
		||||
 *   if counter overflow, divide by two all counters
 | 
			
		||||
 *   return the pointer to the l2 cache entry
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
static uint64_t *seek_l2_table(BDRVQcowState *s, uint64_t l2_offset)
 | 
			
		||||
{
 | 
			
		||||
    int i, j;
 | 
			
		||||
 | 
			
		||||
    for(i = 0; i < L2_CACHE_SIZE; i++) {
 | 
			
		||||
        if (l2_offset == s->l2_cache_offsets[i]) {
 | 
			
		||||
            /* increment the hit count */
 | 
			
		||||
            if (++s->l2_cache_counts[i] == 0xffffffff) {
 | 
			
		||||
                for(j = 0; j < L2_CACHE_SIZE; j++) {
 | 
			
		||||
                    s->l2_cache_counts[j] >>= 1;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            return s->l2_cache + (i << s->l2_bits);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * l2_load
 | 
			
		||||
 *
 | 
			
		||||
 * Loads a L2 table into memory. If the table is in the cache, the cache
 | 
			
		||||
 * is used; otherwise the L2 table is loaded from the image file.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns a pointer to the L2 table on success, or NULL if the read from
 | 
			
		||||
 * the image file failed.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
static int l2_load(BlockDriverState *bs, uint64_t l2_offset,
 | 
			
		||||
    uint64_t **l2_table)
 | 
			
		||||
{
 | 
			
		||||
    BDRVQcowState *s = bs->opaque;
 | 
			
		||||
    int min_index;
 | 
			
		||||
    int ret;
 | 
			
		||||
 | 
			
		||||
    /* seek if the table for the given offset is in the cache */
 | 
			
		||||
 | 
			
		||||
    *l2_table = seek_l2_table(s, l2_offset);
 | 
			
		||||
    if (*l2_table != NULL) {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* not found: load a new entry in the least used one */
 | 
			
		||||
 | 
			
		||||
    min_index = l2_cache_new_entry(bs);
 | 
			
		||||
    *l2_table = s->l2_cache + (min_index << s->l2_bits);
 | 
			
		||||
 | 
			
		||||
    BLKDBG_EVENT(bs->file, BLKDBG_L2_LOAD);
 | 
			
		||||
    ret = bdrv_pread(bs->file, l2_offset, *l2_table,
 | 
			
		||||
        s->l2_size * sizeof(uint64_t));
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        return ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    s->l2_cache_offsets[min_index] = l2_offset;
 | 
			
		||||
    s->l2_cache_counts[min_index] = 1;
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Writes one sector of the L1 table to the disk (can't update single entries
 | 
			
		||||
 * and we really don't want bdrv_pread to perform a read-modify-write)
 | 
			
		||||
 */
 | 
			
		||||
#define L1_ENTRIES_PER_SECTOR (512 / 8)
 | 
			
		||||
static int write_l1_entry(BlockDriverState *bs, int l1_index)
 | 
			
		||||
{
 | 
			
		||||
    BDRVQcowState *s = bs->opaque;
 | 
			
		||||
    uint64_t buf[L1_ENTRIES_PER_SECTOR];
 | 
			
		||||
    int l1_start_index;
 | 
			
		||||
    int i, ret;
 | 
			
		||||
 | 
			
		||||
    l1_start_index = l1_index & ~(L1_ENTRIES_PER_SECTOR - 1);
 | 
			
		||||
    for (i = 0; i < L1_ENTRIES_PER_SECTOR; i++) {
 | 
			
		||||
        buf[i] = cpu_to_be64(s->l1_table[l1_start_index + i]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    BLKDBG_EVENT(bs->file, BLKDBG_L1_UPDATE);
 | 
			
		||||
    ret = bdrv_pwrite_sync(bs->file, s->l1_table_offset + 8 * l1_start_index,
 | 
			
		||||
        buf, sizeof(buf));
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        return ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * l2_allocate
 | 
			
		||||
 *
 | 
			
		||||
 * Allocate a new l2 entry in the file. If l1_index points to an already
 | 
			
		||||
 * used entry in the L2 table (i.e. we are doing a copy on write for the L2
 | 
			
		||||
 * table) copy the contents of the old L2 table into the newly allocated one.
 | 
			
		||||
 * Otherwise the new table is initialized with zeros.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table)
 | 
			
		||||
{
 | 
			
		||||
    BDRVQcowState *s = bs->opaque;
 | 
			
		||||
    int min_index;
 | 
			
		||||
    uint64_t old_l2_offset;
 | 
			
		||||
    uint64_t *l2_table;
 | 
			
		||||
    int64_t l2_offset;
 | 
			
		||||
    int ret;
 | 
			
		||||
 | 
			
		||||
    old_l2_offset = s->l1_table[l1_index];
 | 
			
		||||
 | 
			
		||||
    /* allocate a new l2 entry */
 | 
			
		||||
 | 
			
		||||
    l2_offset = qcow2_alloc_clusters(bs, s->l2_size * sizeof(uint64_t));
 | 
			
		||||
    if (l2_offset < 0) {
 | 
			
		||||
        return l2_offset;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* allocate a new entry in the l2 cache */
 | 
			
		||||
 | 
			
		||||
    min_index = l2_cache_new_entry(bs);
 | 
			
		||||
    l2_table = s->l2_cache + (min_index << s->l2_bits);
 | 
			
		||||
 | 
			
		||||
    if (old_l2_offset == 0) {
 | 
			
		||||
        /* if there was no old l2 table, clear the new table */
 | 
			
		||||
        memset(l2_table, 0, s->l2_size * sizeof(uint64_t));
 | 
			
		||||
    } else {
 | 
			
		||||
        /* if there was an old l2 table, read it from the disk */
 | 
			
		||||
        BLKDBG_EVENT(bs->file, BLKDBG_L2_ALLOC_COW_READ);
 | 
			
		||||
        ret = bdrv_pread(bs->file, old_l2_offset, l2_table,
 | 
			
		||||
            s->l2_size * sizeof(uint64_t));
 | 
			
		||||
        if (ret < 0) {
 | 
			
		||||
            goto fail;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    /* write the l2 table to the file */
 | 
			
		||||
    BLKDBG_EVENT(bs->file, BLKDBG_L2_ALLOC_WRITE);
 | 
			
		||||
    ret = bdrv_pwrite_sync(bs->file, l2_offset, l2_table,
 | 
			
		||||
        s->l2_size * sizeof(uint64_t));
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* update the L1 entry */
 | 
			
		||||
    s->l1_table[l1_index] = l2_offset | QCOW_OFLAG_COPIED;
 | 
			
		||||
    ret = write_l1_entry(bs, l1_index);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* update the l2 cache entry */
 | 
			
		||||
 | 
			
		||||
    s->l2_cache_offsets[min_index] = l2_offset;
 | 
			
		||||
    s->l2_cache_counts[min_index] = 1;
 | 
			
		||||
 | 
			
		||||
    *table = l2_table;
 | 
			
		||||
    return 0;
 | 
			
		||||
 | 
			
		||||
fail:
 | 
			
		||||
    s->l1_table[l1_index] = old_l2_offset;
 | 
			
		||||
    qcow2_l2_cache_reset(bs);
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int count_contiguous_clusters(uint64_t nb_clusters, int cluster_size,
 | 
			
		||||
        uint64_t *l2_table, uint64_t start, uint64_t mask)
 | 
			
		||||
{
 | 
			
		||||
    int i;
 | 
			
		||||
    uint64_t offset = be64_to_cpu(l2_table[0]) & ~mask;
 | 
			
		||||
 | 
			
		||||
    if (!offset)
 | 
			
		||||
        return 0;
 | 
			
		||||
 | 
			
		||||
    for (i = start; i < start + nb_clusters; i++)
 | 
			
		||||
        if (offset + (uint64_t) i * cluster_size != (be64_to_cpu(l2_table[i]) & ~mask))
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
	return (i - start);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int count_contiguous_free_clusters(uint64_t nb_clusters, uint64_t *l2_table)
 | 
			
		||||
{
 | 
			
		||||
    int i = 0;
 | 
			
		||||
 | 
			
		||||
    while(nb_clusters-- && l2_table[i] == 0)
 | 
			
		||||
        i++;
 | 
			
		||||
 | 
			
		||||
    return i;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* The crypt function is compatible with the linux cryptoloop
 | 
			
		||||
   algorithm for < 4 GB images. NOTE: out_buf == in_buf is
 | 
			
		||||
   supported */
 | 
			
		||||
void qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
 | 
			
		||||
                           uint8_t *out_buf, const uint8_t *in_buf,
 | 
			
		||||
                           int nb_sectors, int enc,
 | 
			
		||||
                           const AES_KEY *key)
 | 
			
		||||
{
 | 
			
		||||
    union {
 | 
			
		||||
        uint64_t ll[2];
 | 
			
		||||
        uint8_t b[16];
 | 
			
		||||
    } ivec;
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
    for(i = 0; i < nb_sectors; i++) {
 | 
			
		||||
        ivec.ll[0] = cpu_to_le64(sector_num);
 | 
			
		||||
        ivec.ll[1] = 0;
 | 
			
		||||
        AES_cbc_encrypt(in_buf, out_buf, 512, key,
 | 
			
		||||
                        ivec.b, enc);
 | 
			
		||||
        sector_num++;
 | 
			
		||||
        in_buf += 512;
 | 
			
		||||
        out_buf += 512;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static int qcow_read(BlockDriverState *bs, int64_t sector_num,
 | 
			
		||||
                     uint8_t *buf, int nb_sectors)
 | 
			
		||||
{
 | 
			
		||||
    BDRVQcowState *s = bs->opaque;
 | 
			
		||||
    int ret, index_in_cluster, n, n1;
 | 
			
		||||
    uint64_t cluster_offset;
 | 
			
		||||
 | 
			
		||||
    while (nb_sectors > 0) {
 | 
			
		||||
        n = nb_sectors;
 | 
			
		||||
 | 
			
		||||
        ret = qcow2_get_cluster_offset(bs, sector_num << 9, &n,
 | 
			
		||||
            &cluster_offset);
 | 
			
		||||
        if (ret < 0) {
 | 
			
		||||
            return ret;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        index_in_cluster = sector_num & (s->cluster_sectors - 1);
 | 
			
		||||
        if (!cluster_offset) {
 | 
			
		||||
            if (bs->backing_hd) {
 | 
			
		||||
                /* read from the base image */
 | 
			
		||||
                n1 = qcow2_backing_read1(bs->backing_hd, sector_num, buf, n);
 | 
			
		||||
                if (n1 > 0) {
 | 
			
		||||
                    BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING);
 | 
			
		||||
                    ret = bdrv_read(bs->backing_hd, sector_num, buf, n1);
 | 
			
		||||
                    if (ret < 0)
 | 
			
		||||
                        return -1;
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                memset(buf, 0, 512 * n);
 | 
			
		||||
            }
 | 
			
		||||
        } else if (cluster_offset & QCOW_OFLAG_COMPRESSED) {
 | 
			
		||||
            if (qcow2_decompress_cluster(bs, cluster_offset) < 0)
 | 
			
		||||
                return -1;
 | 
			
		||||
            memcpy(buf, s->cluster_cache + index_in_cluster * 512, 512 * n);
 | 
			
		||||
        } else {
 | 
			
		||||
            BLKDBG_EVENT(bs->file, BLKDBG_READ);
 | 
			
		||||
            ret = bdrv_pread(bs->file, cluster_offset + index_in_cluster * 512, buf, n * 512);
 | 
			
		||||
            if (ret != n * 512)
 | 
			
		||||
                return -1;
 | 
			
		||||
            if (s->crypt_method) {
 | 
			
		||||
                qcow2_encrypt_sectors(s, sector_num, buf, buf, n, 0,
 | 
			
		||||
                                &s->aes_decrypt_key);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        nb_sectors -= n;
 | 
			
		||||
        sector_num += n;
 | 
			
		||||
        buf += n * 512;
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int copy_sectors(BlockDriverState *bs, uint64_t start_sect,
 | 
			
		||||
                        uint64_t cluster_offset, int n_start, int n_end)
 | 
			
		||||
{
 | 
			
		||||
    BDRVQcowState *s = bs->opaque;
 | 
			
		||||
    int n, ret;
 | 
			
		||||
 | 
			
		||||
    n = n_end - n_start;
 | 
			
		||||
    if (n <= 0)
 | 
			
		||||
        return 0;
 | 
			
		||||
    BLKDBG_EVENT(bs->file, BLKDBG_COW_READ);
 | 
			
		||||
    ret = qcow_read(bs, start_sect + n_start, s->cluster_data, n);
 | 
			
		||||
    if (ret < 0)
 | 
			
		||||
        return ret;
 | 
			
		||||
    if (s->crypt_method) {
 | 
			
		||||
        qcow2_encrypt_sectors(s, start_sect + n_start,
 | 
			
		||||
                        s->cluster_data,
 | 
			
		||||
                        s->cluster_data, n, 1,
 | 
			
		||||
                        &s->aes_encrypt_key);
 | 
			
		||||
    }
 | 
			
		||||
    BLKDBG_EVENT(bs->file, BLKDBG_COW_WRITE);
 | 
			
		||||
    ret = bdrv_write_sync(bs->file, (cluster_offset >> 9) + n_start,
 | 
			
		||||
        s->cluster_data, n);
 | 
			
		||||
    if (ret < 0)
 | 
			
		||||
        return ret;
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * get_cluster_offset
 | 
			
		||||
 *
 | 
			
		||||
 * For a given offset of the disk image, find the cluster offset in
 | 
			
		||||
 * qcow2 file. The offset is stored in *cluster_offset.
 | 
			
		||||
 *
 | 
			
		||||
 * on entry, *num is the number of contiguous clusters we'd like to
 | 
			
		||||
 * access following offset.
 | 
			
		||||
 *
 | 
			
		||||
 * on exit, *num is the number of contiguous clusters we can read.
 | 
			
		||||
 *
 | 
			
		||||
 * Return 0, if the offset is found
 | 
			
		||||
 * Return -errno, otherwise.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
 | 
			
		||||
    int *num, uint64_t *cluster_offset)
 | 
			
		||||
{
 | 
			
		||||
    BDRVQcowState *s = bs->opaque;
 | 
			
		||||
    unsigned int l1_index, l2_index;
 | 
			
		||||
    uint64_t l2_offset, *l2_table;
 | 
			
		||||
    int l1_bits, c;
 | 
			
		||||
    unsigned int index_in_cluster, nb_clusters;
 | 
			
		||||
    uint64_t nb_available, nb_needed;
 | 
			
		||||
    int ret;
 | 
			
		||||
 | 
			
		||||
    index_in_cluster = (offset >> 9) & (s->cluster_sectors - 1);
 | 
			
		||||
    nb_needed = *num + index_in_cluster;
 | 
			
		||||
 | 
			
		||||
    l1_bits = s->l2_bits + s->cluster_bits;
 | 
			
		||||
 | 
			
		||||
    /* compute how many bytes there are between the offset and
 | 
			
		||||
     * the end of the l1 entry
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    nb_available = (1ULL << l1_bits) - (offset & ((1ULL << l1_bits) - 1));
 | 
			
		||||
 | 
			
		||||
    /* compute the number of available sectors */
 | 
			
		||||
 | 
			
		||||
    nb_available = (nb_available >> 9) + index_in_cluster;
 | 
			
		||||
 | 
			
		||||
    if (nb_needed > nb_available) {
 | 
			
		||||
        nb_needed = nb_available;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    *cluster_offset = 0;
 | 
			
		||||
 | 
			
		||||
    /* seek the the l2 offset in the l1 table */
 | 
			
		||||
 | 
			
		||||
    l1_index = offset >> l1_bits;
 | 
			
		||||
    if (l1_index >= s->l1_size)
 | 
			
		||||
        goto out;
 | 
			
		||||
 | 
			
		||||
    l2_offset = s->l1_table[l1_index];
 | 
			
		||||
 | 
			
		||||
    /* seek the l2 table of the given l2 offset */
 | 
			
		||||
 | 
			
		||||
    if (!l2_offset)
 | 
			
		||||
        goto out;
 | 
			
		||||
 | 
			
		||||
    /* load the l2 table in memory */
 | 
			
		||||
 | 
			
		||||
    l2_offset &= ~QCOW_OFLAG_COPIED;
 | 
			
		||||
    ret = l2_load(bs, l2_offset, &l2_table);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        return ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* find the cluster offset for the given disk offset */
 | 
			
		||||
 | 
			
		||||
    l2_index = (offset >> s->cluster_bits) & (s->l2_size - 1);
 | 
			
		||||
    *cluster_offset = be64_to_cpu(l2_table[l2_index]);
 | 
			
		||||
    nb_clusters = size_to_clusters(s, nb_needed << 9);
 | 
			
		||||
 | 
			
		||||
    if (!*cluster_offset) {
 | 
			
		||||
        /* how many empty clusters ? */
 | 
			
		||||
        c = count_contiguous_free_clusters(nb_clusters, &l2_table[l2_index]);
 | 
			
		||||
    } else {
 | 
			
		||||
        /* how many allocated clusters ? */
 | 
			
		||||
        c = count_contiguous_clusters(nb_clusters, s->cluster_size,
 | 
			
		||||
                &l2_table[l2_index], 0, QCOW_OFLAG_COPIED);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
   nb_available = (c * s->cluster_sectors);
 | 
			
		||||
out:
 | 
			
		||||
    if (nb_available > nb_needed)
 | 
			
		||||
        nb_available = nb_needed;
 | 
			
		||||
 | 
			
		||||
    *num = nb_available - index_in_cluster;
 | 
			
		||||
 | 
			
		||||
    *cluster_offset &=~QCOW_OFLAG_COPIED;
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * get_cluster_table
 | 
			
		||||
 *
 | 
			
		||||
 * for a given disk offset, load (and allocate if needed)
 | 
			
		||||
 * the l2 table.
 | 
			
		||||
 *
 | 
			
		||||
 * the l2 table offset in the qcow2 file and the cluster index
 | 
			
		||||
 * in the l2 table are given to the caller.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns 0 on success, -errno in failure case
 | 
			
		||||
 */
 | 
			
		||||
static int get_cluster_table(BlockDriverState *bs, uint64_t offset,
 | 
			
		||||
                             uint64_t **new_l2_table,
 | 
			
		||||
                             uint64_t *new_l2_offset,
 | 
			
		||||
                             int *new_l2_index)
 | 
			
		||||
{
 | 
			
		||||
    BDRVQcowState *s = bs->opaque;
 | 
			
		||||
    unsigned int l1_index, l2_index;
 | 
			
		||||
    uint64_t l2_offset;
 | 
			
		||||
    uint64_t *l2_table = NULL;
 | 
			
		||||
    int ret;
 | 
			
		||||
 | 
			
		||||
    /* seek the the l2 offset in the l1 table */
 | 
			
		||||
 | 
			
		||||
    l1_index = offset >> (s->l2_bits + s->cluster_bits);
 | 
			
		||||
    if (l1_index >= s->l1_size) {
 | 
			
		||||
        ret = qcow2_grow_l1_table(bs, l1_index + 1);
 | 
			
		||||
        if (ret < 0) {
 | 
			
		||||
            return ret;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    l2_offset = s->l1_table[l1_index];
 | 
			
		||||
 | 
			
		||||
    /* seek the l2 table of the given l2 offset */
 | 
			
		||||
 | 
			
		||||
    if (l2_offset & QCOW_OFLAG_COPIED) {
 | 
			
		||||
        /* load the l2 table in memory */
 | 
			
		||||
        l2_offset &= ~QCOW_OFLAG_COPIED;
 | 
			
		||||
        ret = l2_load(bs, l2_offset, &l2_table);
 | 
			
		||||
        if (ret < 0) {
 | 
			
		||||
            return ret;
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        if (l2_offset)
 | 
			
		||||
            qcow2_free_clusters(bs, l2_offset, s->l2_size * sizeof(uint64_t));
 | 
			
		||||
        ret = l2_allocate(bs, l1_index, &l2_table);
 | 
			
		||||
        if (ret < 0) {
 | 
			
		||||
            return ret;
 | 
			
		||||
        }
 | 
			
		||||
        l2_offset = s->l1_table[l1_index] & ~QCOW_OFLAG_COPIED;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* find the cluster offset for the given disk offset */
 | 
			
		||||
 | 
			
		||||
    l2_index = (offset >> s->cluster_bits) & (s->l2_size - 1);
 | 
			
		||||
 | 
			
		||||
    *new_l2_table = l2_table;
 | 
			
		||||
    *new_l2_offset = l2_offset;
 | 
			
		||||
    *new_l2_index = l2_index;
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * alloc_compressed_cluster_offset
 | 
			
		||||
 *
 | 
			
		||||
 * For a given offset of the disk image, return cluster offset in
 | 
			
		||||
 * qcow2 file.
 | 
			
		||||
 *
 | 
			
		||||
 * If the offset is not found, allocate a new compressed cluster.
 | 
			
		||||
 *
 | 
			
		||||
 * Return the cluster offset if successful,
 | 
			
		||||
 * Return 0, otherwise.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs,
 | 
			
		||||
                                               uint64_t offset,
 | 
			
		||||
                                               int compressed_size)
 | 
			
		||||
{
 | 
			
		||||
    BDRVQcowState *s = bs->opaque;
 | 
			
		||||
    int l2_index, ret;
 | 
			
		||||
    uint64_t l2_offset, *l2_table;
 | 
			
		||||
    int64_t cluster_offset;
 | 
			
		||||
    int nb_csectors;
 | 
			
		||||
 | 
			
		||||
    ret = get_cluster_table(bs, offset, &l2_table, &l2_offset, &l2_index);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    cluster_offset = be64_to_cpu(l2_table[l2_index]);
 | 
			
		||||
    if (cluster_offset & QCOW_OFLAG_COPIED)
 | 
			
		||||
        return cluster_offset & ~QCOW_OFLAG_COPIED;
 | 
			
		||||
 | 
			
		||||
    if (cluster_offset)
 | 
			
		||||
        qcow2_free_any_clusters(bs, cluster_offset, 1);
 | 
			
		||||
 | 
			
		||||
    cluster_offset = qcow2_alloc_bytes(bs, compressed_size);
 | 
			
		||||
    if (cluster_offset < 0) {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    nb_csectors = ((cluster_offset + compressed_size - 1) >> 9) -
 | 
			
		||||
                  (cluster_offset >> 9);
 | 
			
		||||
 | 
			
		||||
    cluster_offset |= QCOW_OFLAG_COMPRESSED |
 | 
			
		||||
                      ((uint64_t)nb_csectors << s->csize_shift);
 | 
			
		||||
 | 
			
		||||
    /* update L2 table */
 | 
			
		||||
 | 
			
		||||
    /* compressed clusters never have the copied flag */
 | 
			
		||||
 | 
			
		||||
    BLKDBG_EVENT(bs->file, BLKDBG_L2_UPDATE_COMPRESSED);
 | 
			
		||||
    l2_table[l2_index] = cpu_to_be64(cluster_offset);
 | 
			
		||||
    if (bdrv_pwrite_sync(bs->file,
 | 
			
		||||
                    l2_offset + l2_index * sizeof(uint64_t),
 | 
			
		||||
                    l2_table + l2_index,
 | 
			
		||||
                    sizeof(uint64_t)) < 0)
 | 
			
		||||
        return 0;
 | 
			
		||||
 | 
			
		||||
    return cluster_offset;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Write L2 table updates to disk, writing whole sectors to avoid a
 | 
			
		||||
 * read-modify-write in bdrv_pwrite
 | 
			
		||||
 */
 | 
			
		||||
#define L2_ENTRIES_PER_SECTOR (512 / 8)
 | 
			
		||||
static int write_l2_entries(BlockDriverState *bs, uint64_t *l2_table,
 | 
			
		||||
    uint64_t l2_offset, int l2_index, int num)
 | 
			
		||||
{
 | 
			
		||||
    int l2_start_index = l2_index & ~(L1_ENTRIES_PER_SECTOR - 1);
 | 
			
		||||
    int start_offset = (8 * l2_index) & ~511;
 | 
			
		||||
    int end_offset = (8 * (l2_index + num) + 511) & ~511;
 | 
			
		||||
    size_t len = end_offset - start_offset;
 | 
			
		||||
    int ret;
 | 
			
		||||
 | 
			
		||||
    BLKDBG_EVENT(bs->file, BLKDBG_L2_UPDATE);
 | 
			
		||||
    ret = bdrv_pwrite(bs->file, l2_offset + start_offset,
 | 
			
		||||
        &l2_table[l2_start_index], len);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        return ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m)
 | 
			
		||||
{
 | 
			
		||||
    BDRVQcowState *s = bs->opaque;
 | 
			
		||||
    int i, j = 0, l2_index, ret;
 | 
			
		||||
    uint64_t *old_cluster, start_sect, l2_offset, *l2_table;
 | 
			
		||||
    uint64_t cluster_offset = m->cluster_offset;
 | 
			
		||||
 | 
			
		||||
    if (m->nb_clusters == 0)
 | 
			
		||||
        return 0;
 | 
			
		||||
 | 
			
		||||
    old_cluster = qemu_malloc(m->nb_clusters * sizeof(uint64_t));
 | 
			
		||||
 | 
			
		||||
    /* copy content of unmodified sectors */
 | 
			
		||||
    start_sect = (m->offset & ~(s->cluster_size - 1)) >> 9;
 | 
			
		||||
    if (m->n_start) {
 | 
			
		||||
        ret = copy_sectors(bs, start_sect, cluster_offset, 0, m->n_start);
 | 
			
		||||
        if (ret < 0)
 | 
			
		||||
            goto err;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (m->nb_available & (s->cluster_sectors - 1)) {
 | 
			
		||||
        uint64_t end = m->nb_available & ~(uint64_t)(s->cluster_sectors - 1);
 | 
			
		||||
        ret = copy_sectors(bs, start_sect + end, cluster_offset + (end << 9),
 | 
			
		||||
                m->nb_available - end, s->cluster_sectors);
 | 
			
		||||
        if (ret < 0)
 | 
			
		||||
            goto err;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* update L2 table */
 | 
			
		||||
    ret = get_cluster_table(bs, m->offset, &l2_table, &l2_offset, &l2_index);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        goto err;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < m->nb_clusters; i++) {
 | 
			
		||||
        /* if two concurrent writes happen to the same unallocated cluster
 | 
			
		||||
	 * each write allocates separate cluster and writes data concurrently.
 | 
			
		||||
	 * The first one to complete updates l2 table with pointer to its
 | 
			
		||||
	 * cluster the second one has to do RMW (which is done above by
 | 
			
		||||
	 * copy_sectors()), update l2 table with its cluster pointer and free
 | 
			
		||||
	 * old cluster. This is what this loop does */
 | 
			
		||||
        if(l2_table[l2_index + i] != 0)
 | 
			
		||||
            old_cluster[j++] = l2_table[l2_index + i];
 | 
			
		||||
 | 
			
		||||
        l2_table[l2_index + i] = cpu_to_be64((cluster_offset +
 | 
			
		||||
                    (i << s->cluster_bits)) | QCOW_OFLAG_COPIED);
 | 
			
		||||
     }
 | 
			
		||||
 | 
			
		||||
    ret = write_l2_entries(bs, l2_table, l2_offset, l2_index, m->nb_clusters);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        qcow2_l2_cache_reset(bs);
 | 
			
		||||
        goto err;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
     * If this was a COW, we need to decrease the refcount of the old cluster.
 | 
			
		||||
     * Also flush bs->file to get the right order for L2 and refcount update.
 | 
			
		||||
     */
 | 
			
		||||
    if (j != 0) {
 | 
			
		||||
        bdrv_flush(bs->file);
 | 
			
		||||
        for (i = 0; i < j; i++) {
 | 
			
		||||
            qcow2_free_any_clusters(bs,
 | 
			
		||||
                be64_to_cpu(old_cluster[i]) & ~QCOW_OFLAG_COPIED, 1);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ret = 0;
 | 
			
		||||
err:
 | 
			
		||||
    qemu_free(old_cluster);
 | 
			
		||||
    return ret;
 | 
			
		||||
 }
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * alloc_cluster_offset
 | 
			
		||||
 *
 | 
			
		||||
 * For a given offset of the disk image, return cluster offset in qcow2 file.
 | 
			
		||||
 * If the offset is not found, allocate a new cluster.
 | 
			
		||||
 *
 | 
			
		||||
 * If the cluster was already allocated, m->nb_clusters is set to 0,
 | 
			
		||||
 * m->depends_on is set to NULL and the other fields in m are meaningless.
 | 
			
		||||
 *
 | 
			
		||||
 * If the cluster is newly allocated, m->nb_clusters is set to the number of
 | 
			
		||||
 * contiguous clusters that have been allocated. This may be 0 if the request
 | 
			
		||||
 * conflict with another write request in flight; in this case, m->depends_on
 | 
			
		||||
 * is set and the remaining fields of m are meaningless.
 | 
			
		||||
 *
 | 
			
		||||
 * If m->nb_clusters is non-zero, the other fields of m are valid and contain
 | 
			
		||||
 * information about the first allocated cluster.
 | 
			
		||||
 *
 | 
			
		||||
 * Return 0 on success and -errno in error cases
 | 
			
		||||
 */
 | 
			
		||||
int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset,
 | 
			
		||||
    int n_start, int n_end, int *num, QCowL2Meta *m)
 | 
			
		||||
{
 | 
			
		||||
    BDRVQcowState *s = bs->opaque;
 | 
			
		||||
    int l2_index, ret;
 | 
			
		||||
    uint64_t l2_offset, *l2_table;
 | 
			
		||||
    int64_t cluster_offset;
 | 
			
		||||
    unsigned int nb_clusters, i = 0;
 | 
			
		||||
    QCowL2Meta *old_alloc;
 | 
			
		||||
 | 
			
		||||
    ret = get_cluster_table(bs, offset, &l2_table, &l2_offset, &l2_index);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        return ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    nb_clusters = size_to_clusters(s, n_end << 9);
 | 
			
		||||
 | 
			
		||||
    nb_clusters = MIN(nb_clusters, s->l2_size - l2_index);
 | 
			
		||||
 | 
			
		||||
    cluster_offset = be64_to_cpu(l2_table[l2_index]);
 | 
			
		||||
 | 
			
		||||
    /* We keep all QCOW_OFLAG_COPIED clusters */
 | 
			
		||||
 | 
			
		||||
    if (cluster_offset & QCOW_OFLAG_COPIED) {
 | 
			
		||||
        nb_clusters = count_contiguous_clusters(nb_clusters, s->cluster_size,
 | 
			
		||||
                &l2_table[l2_index], 0, 0);
 | 
			
		||||
 | 
			
		||||
        cluster_offset &= ~QCOW_OFLAG_COPIED;
 | 
			
		||||
        m->nb_clusters = 0;
 | 
			
		||||
        m->depends_on = NULL;
 | 
			
		||||
 | 
			
		||||
        goto out;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* for the moment, multiple compressed clusters are not managed */
 | 
			
		||||
 | 
			
		||||
    if (cluster_offset & QCOW_OFLAG_COMPRESSED)
 | 
			
		||||
        nb_clusters = 1;
 | 
			
		||||
 | 
			
		||||
    /* how many available clusters ? */
 | 
			
		||||
 | 
			
		||||
    while (i < nb_clusters) {
 | 
			
		||||
        i += count_contiguous_clusters(nb_clusters - i, s->cluster_size,
 | 
			
		||||
                &l2_table[l2_index], i, 0);
 | 
			
		||||
        if ((i >= nb_clusters) || be64_to_cpu(l2_table[l2_index + i])) {
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        i += count_contiguous_free_clusters(nb_clusters - i,
 | 
			
		||||
                &l2_table[l2_index + i]);
 | 
			
		||||
        if (i >= nb_clusters) {
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        cluster_offset = be64_to_cpu(l2_table[l2_index + i]);
 | 
			
		||||
 | 
			
		||||
        if ((cluster_offset & QCOW_OFLAG_COPIED) ||
 | 
			
		||||
                (cluster_offset & QCOW_OFLAG_COMPRESSED))
 | 
			
		||||
            break;
 | 
			
		||||
    }
 | 
			
		||||
    assert(i <= nb_clusters);
 | 
			
		||||
    nb_clusters = i;
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
     * Check if there already is an AIO write request in flight which allocates
 | 
			
		||||
     * the same cluster. In this case we need to wait until the previous
 | 
			
		||||
     * request has completed and updated the L2 table accordingly.
 | 
			
		||||
     */
 | 
			
		||||
    QLIST_FOREACH(old_alloc, &s->cluster_allocs, next_in_flight) {
 | 
			
		||||
 | 
			
		||||
        uint64_t end_offset = offset + nb_clusters * s->cluster_size;
 | 
			
		||||
        uint64_t old_offset = old_alloc->offset;
 | 
			
		||||
        uint64_t old_end_offset = old_alloc->offset +
 | 
			
		||||
            old_alloc->nb_clusters * s->cluster_size;
 | 
			
		||||
 | 
			
		||||
        if (end_offset < old_offset || offset > old_end_offset) {
 | 
			
		||||
            /* No intersection */
 | 
			
		||||
        } else {
 | 
			
		||||
            if (offset < old_offset) {
 | 
			
		||||
                /* Stop at the start of a running allocation */
 | 
			
		||||
                nb_clusters = (old_offset - offset) >> s->cluster_bits;
 | 
			
		||||
            } else {
 | 
			
		||||
                nb_clusters = 0;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (nb_clusters == 0) {
 | 
			
		||||
                /* Set dependency and wait for a callback */
 | 
			
		||||
                m->depends_on = old_alloc;
 | 
			
		||||
                m->nb_clusters = 0;
 | 
			
		||||
                *num = 0;
 | 
			
		||||
                return 0;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!nb_clusters) {
 | 
			
		||||
        abort();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    QLIST_INSERT_HEAD(&s->cluster_allocs, m, next_in_flight);
 | 
			
		||||
 | 
			
		||||
    /* allocate a new cluster */
 | 
			
		||||
 | 
			
		||||
    cluster_offset = qcow2_alloc_clusters(bs, nb_clusters * s->cluster_size);
 | 
			
		||||
    if (cluster_offset < 0) {
 | 
			
		||||
        QLIST_REMOVE(m, next_in_flight);
 | 
			
		||||
        return cluster_offset;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* save info needed for meta data update */
 | 
			
		||||
    m->offset = offset;
 | 
			
		||||
    m->n_start = n_start;
 | 
			
		||||
    m->nb_clusters = nb_clusters;
 | 
			
		||||
 | 
			
		||||
out:
 | 
			
		||||
    m->nb_available = MIN(nb_clusters << (s->cluster_bits - 9), n_end);
 | 
			
		||||
    m->cluster_offset = cluster_offset;
 | 
			
		||||
 | 
			
		||||
    *num = m->nb_available - n_start;
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int decompress_buffer(uint8_t *out_buf, int out_buf_size,
 | 
			
		||||
                             const uint8_t *buf, int buf_size)
 | 
			
		||||
{
 | 
			
		||||
    z_stream strm1, *strm = &strm1;
 | 
			
		||||
    int ret, out_len;
 | 
			
		||||
 | 
			
		||||
    memset(strm, 0, sizeof(*strm));
 | 
			
		||||
 | 
			
		||||
    strm->next_in = (uint8_t *)buf;
 | 
			
		||||
    strm->avail_in = buf_size;
 | 
			
		||||
    strm->next_out = out_buf;
 | 
			
		||||
    strm->avail_out = out_buf_size;
 | 
			
		||||
 | 
			
		||||
    ret = inflateInit2(strm, -12);
 | 
			
		||||
    if (ret != Z_OK)
 | 
			
		||||
        return -1;
 | 
			
		||||
    ret = inflate(strm, Z_FINISH);
 | 
			
		||||
    out_len = strm->next_out - out_buf;
 | 
			
		||||
    if ((ret != Z_STREAM_END && ret != Z_BUF_ERROR) ||
 | 
			
		||||
        out_len != out_buf_size) {
 | 
			
		||||
        inflateEnd(strm);
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
    inflateEnd(strm);
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset)
 | 
			
		||||
{
 | 
			
		||||
    BDRVQcowState *s = bs->opaque;
 | 
			
		||||
    int ret, csize, nb_csectors, sector_offset;
 | 
			
		||||
    uint64_t coffset;
 | 
			
		||||
 | 
			
		||||
    coffset = cluster_offset & s->cluster_offset_mask;
 | 
			
		||||
    if (s->cluster_cache_offset != coffset) {
 | 
			
		||||
        nb_csectors = ((cluster_offset >> s->csize_shift) & s->csize_mask) + 1;
 | 
			
		||||
        sector_offset = coffset & 511;
 | 
			
		||||
        csize = nb_csectors * 512 - sector_offset;
 | 
			
		||||
        BLKDBG_EVENT(bs->file, BLKDBG_READ_COMPRESSED);
 | 
			
		||||
        ret = bdrv_read(bs->file, coffset >> 9, s->cluster_data, nb_csectors);
 | 
			
		||||
        if (ret < 0) {
 | 
			
		||||
            return -1;
 | 
			
		||||
        }
 | 
			
		||||
        if (decompress_buffer(s->cluster_cache, s->cluster_size,
 | 
			
		||||
                              s->cluster_data + sector_offset, csize) < 0) {
 | 
			
		||||
            return -1;
 | 
			
		||||
        }
 | 
			
		||||
        s->cluster_cache_offset = coffset;
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1,418 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Block driver for the QCOW version 2 format
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2004-2006 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-common.h"
 | 
			
		||||
#include "block_int.h"
 | 
			
		||||
#include "block/qcow2.h"
 | 
			
		||||
 | 
			
		||||
typedef struct __attribute__((packed)) QCowSnapshotHeader {
 | 
			
		||||
    /* header is 8 byte aligned */
 | 
			
		||||
    uint64_t l1_table_offset;
 | 
			
		||||
 | 
			
		||||
    uint32_t l1_size;
 | 
			
		||||
    uint16_t id_str_size;
 | 
			
		||||
    uint16_t name_size;
 | 
			
		||||
 | 
			
		||||
    uint32_t date_sec;
 | 
			
		||||
    uint32_t date_nsec;
 | 
			
		||||
 | 
			
		||||
    uint64_t vm_clock_nsec;
 | 
			
		||||
 | 
			
		||||
    uint32_t vm_state_size;
 | 
			
		||||
    uint32_t extra_data_size; /* for extension */
 | 
			
		||||
    /* extra data follows */
 | 
			
		||||
    /* id_str follows */
 | 
			
		||||
    /* name follows  */
 | 
			
		||||
} QCowSnapshotHeader;
 | 
			
		||||
 | 
			
		||||
void qcow2_free_snapshots(BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
    BDRVQcowState *s = bs->opaque;
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
    for(i = 0; i < s->nb_snapshots; i++) {
 | 
			
		||||
        qemu_free(s->snapshots[i].name);
 | 
			
		||||
        qemu_free(s->snapshots[i].id_str);
 | 
			
		||||
    }
 | 
			
		||||
    qemu_free(s->snapshots);
 | 
			
		||||
    s->snapshots = NULL;
 | 
			
		||||
    s->nb_snapshots = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int qcow2_read_snapshots(BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
    BDRVQcowState *s = bs->opaque;
 | 
			
		||||
    QCowSnapshotHeader h;
 | 
			
		||||
    QCowSnapshot *sn;
 | 
			
		||||
    int i, id_str_size, name_size;
 | 
			
		||||
    int64_t offset;
 | 
			
		||||
    uint32_t extra_data_size;
 | 
			
		||||
 | 
			
		||||
    if (!s->nb_snapshots) {
 | 
			
		||||
        s->snapshots = NULL;
 | 
			
		||||
        s->snapshots_size = 0;
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    offset = s->snapshots_offset;
 | 
			
		||||
    s->snapshots = qemu_mallocz(s->nb_snapshots * sizeof(QCowSnapshot));
 | 
			
		||||
    for(i = 0; i < s->nb_snapshots; i++) {
 | 
			
		||||
        offset = align_offset(offset, 8);
 | 
			
		||||
        if (bdrv_pread(bs->file, offset, &h, sizeof(h)) != sizeof(h))
 | 
			
		||||
            goto fail;
 | 
			
		||||
        offset += sizeof(h);
 | 
			
		||||
        sn = s->snapshots + i;
 | 
			
		||||
        sn->l1_table_offset = be64_to_cpu(h.l1_table_offset);
 | 
			
		||||
        sn->l1_size = be32_to_cpu(h.l1_size);
 | 
			
		||||
        sn->vm_state_size = be32_to_cpu(h.vm_state_size);
 | 
			
		||||
        sn->date_sec = be32_to_cpu(h.date_sec);
 | 
			
		||||
        sn->date_nsec = be32_to_cpu(h.date_nsec);
 | 
			
		||||
        sn->vm_clock_nsec = be64_to_cpu(h.vm_clock_nsec);
 | 
			
		||||
        extra_data_size = be32_to_cpu(h.extra_data_size);
 | 
			
		||||
 | 
			
		||||
        id_str_size = be16_to_cpu(h.id_str_size);
 | 
			
		||||
        name_size = be16_to_cpu(h.name_size);
 | 
			
		||||
 | 
			
		||||
        offset += extra_data_size;
 | 
			
		||||
 | 
			
		||||
        sn->id_str = qemu_malloc(id_str_size + 1);
 | 
			
		||||
        if (bdrv_pread(bs->file, offset, sn->id_str, id_str_size) != id_str_size)
 | 
			
		||||
            goto fail;
 | 
			
		||||
        offset += id_str_size;
 | 
			
		||||
        sn->id_str[id_str_size] = '\0';
 | 
			
		||||
 | 
			
		||||
        sn->name = qemu_malloc(name_size + 1);
 | 
			
		||||
        if (bdrv_pread(bs->file, offset, sn->name, name_size) != name_size)
 | 
			
		||||
            goto fail;
 | 
			
		||||
        offset += name_size;
 | 
			
		||||
        sn->name[name_size] = '\0';
 | 
			
		||||
    }
 | 
			
		||||
    s->snapshots_size = offset - s->snapshots_offset;
 | 
			
		||||
    return 0;
 | 
			
		||||
 fail:
 | 
			
		||||
    qcow2_free_snapshots(bs);
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* add at the end of the file a new list of snapshots */
 | 
			
		||||
static int qcow_write_snapshots(BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
    BDRVQcowState *s = bs->opaque;
 | 
			
		||||
    QCowSnapshot *sn;
 | 
			
		||||
    QCowSnapshotHeader h;
 | 
			
		||||
    int i, name_size, id_str_size, snapshots_size;
 | 
			
		||||
    uint64_t data64;
 | 
			
		||||
    uint32_t data32;
 | 
			
		||||
    int64_t offset, snapshots_offset;
 | 
			
		||||
 | 
			
		||||
    /* compute the size of the snapshots */
 | 
			
		||||
    offset = 0;
 | 
			
		||||
    for(i = 0; i < s->nb_snapshots; i++) {
 | 
			
		||||
        sn = s->snapshots + i;
 | 
			
		||||
        offset = align_offset(offset, 8);
 | 
			
		||||
        offset += sizeof(h);
 | 
			
		||||
        offset += strlen(sn->id_str);
 | 
			
		||||
        offset += strlen(sn->name);
 | 
			
		||||
    }
 | 
			
		||||
    snapshots_size = offset;
 | 
			
		||||
 | 
			
		||||
    snapshots_offset = qcow2_alloc_clusters(bs, snapshots_size);
 | 
			
		||||
    offset = snapshots_offset;
 | 
			
		||||
    if (offset < 0) {
 | 
			
		||||
        return offset;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for(i = 0; i < s->nb_snapshots; i++) {
 | 
			
		||||
        sn = s->snapshots + i;
 | 
			
		||||
        memset(&h, 0, sizeof(h));
 | 
			
		||||
        h.l1_table_offset = cpu_to_be64(sn->l1_table_offset);
 | 
			
		||||
        h.l1_size = cpu_to_be32(sn->l1_size);
 | 
			
		||||
        h.vm_state_size = cpu_to_be32(sn->vm_state_size);
 | 
			
		||||
        h.date_sec = cpu_to_be32(sn->date_sec);
 | 
			
		||||
        h.date_nsec = cpu_to_be32(sn->date_nsec);
 | 
			
		||||
        h.vm_clock_nsec = cpu_to_be64(sn->vm_clock_nsec);
 | 
			
		||||
 | 
			
		||||
        id_str_size = strlen(sn->id_str);
 | 
			
		||||
        name_size = strlen(sn->name);
 | 
			
		||||
        h.id_str_size = cpu_to_be16(id_str_size);
 | 
			
		||||
        h.name_size = cpu_to_be16(name_size);
 | 
			
		||||
        offset = align_offset(offset, 8);
 | 
			
		||||
        if (bdrv_pwrite_sync(bs->file, offset, &h, sizeof(h)) < 0)
 | 
			
		||||
            goto fail;
 | 
			
		||||
        offset += sizeof(h);
 | 
			
		||||
        if (bdrv_pwrite_sync(bs->file, offset, sn->id_str, id_str_size) < 0)
 | 
			
		||||
            goto fail;
 | 
			
		||||
        offset += id_str_size;
 | 
			
		||||
        if (bdrv_pwrite_sync(bs->file, offset, sn->name, name_size) < 0)
 | 
			
		||||
            goto fail;
 | 
			
		||||
        offset += name_size;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* update the various header fields */
 | 
			
		||||
    data64 = cpu_to_be64(snapshots_offset);
 | 
			
		||||
    if (bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, snapshots_offset),
 | 
			
		||||
                    &data64, sizeof(data64)) < 0)
 | 
			
		||||
        goto fail;
 | 
			
		||||
    data32 = cpu_to_be32(s->nb_snapshots);
 | 
			
		||||
    if (bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, nb_snapshots),
 | 
			
		||||
                    &data32, sizeof(data32)) < 0)
 | 
			
		||||
        goto fail;
 | 
			
		||||
 | 
			
		||||
    /* free the old snapshot table */
 | 
			
		||||
    qcow2_free_clusters(bs, s->snapshots_offset, s->snapshots_size);
 | 
			
		||||
    s->snapshots_offset = snapshots_offset;
 | 
			
		||||
    s->snapshots_size = snapshots_size;
 | 
			
		||||
    return 0;
 | 
			
		||||
 fail:
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void find_new_snapshot_id(BlockDriverState *bs,
 | 
			
		||||
                                 char *id_str, int id_str_size)
 | 
			
		||||
{
 | 
			
		||||
    BDRVQcowState *s = bs->opaque;
 | 
			
		||||
    QCowSnapshot *sn;
 | 
			
		||||
    int i, id, id_max = 0;
 | 
			
		||||
 | 
			
		||||
    for(i = 0; i < s->nb_snapshots; i++) {
 | 
			
		||||
        sn = s->snapshots + i;
 | 
			
		||||
        id = strtoul(sn->id_str, NULL, 10);
 | 
			
		||||
        if (id > id_max)
 | 
			
		||||
            id_max = id;
 | 
			
		||||
    }
 | 
			
		||||
    snprintf(id_str, id_str_size, "%d", id_max + 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int find_snapshot_by_id(BlockDriverState *bs, const char *id_str)
 | 
			
		||||
{
 | 
			
		||||
    BDRVQcowState *s = bs->opaque;
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
    for(i = 0; i < s->nb_snapshots; i++) {
 | 
			
		||||
        if (!strcmp(s->snapshots[i].id_str, id_str))
 | 
			
		||||
            return i;
 | 
			
		||||
    }
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int find_snapshot_by_id_or_name(BlockDriverState *bs, const char *name)
 | 
			
		||||
{
 | 
			
		||||
    BDRVQcowState *s = bs->opaque;
 | 
			
		||||
    int i, ret;
 | 
			
		||||
 | 
			
		||||
    ret = find_snapshot_by_id(bs, name);
 | 
			
		||||
    if (ret >= 0)
 | 
			
		||||
        return ret;
 | 
			
		||||
    for(i = 0; i < s->nb_snapshots; i++) {
 | 
			
		||||
        if (!strcmp(s->snapshots[i].name, name))
 | 
			
		||||
            return i;
 | 
			
		||||
    }
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* if no id is provided, a new one is constructed */
 | 
			
		||||
int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
 | 
			
		||||
{
 | 
			
		||||
    BDRVQcowState *s = bs->opaque;
 | 
			
		||||
    QCowSnapshot *snapshots1, sn1, *sn = &sn1;
 | 
			
		||||
    int i, ret;
 | 
			
		||||
    uint64_t *l1_table = NULL;
 | 
			
		||||
    int64_t l1_table_offset;
 | 
			
		||||
 | 
			
		||||
    memset(sn, 0, sizeof(*sn));
 | 
			
		||||
 | 
			
		||||
    if (sn_info->id_str[0] == '\0') {
 | 
			
		||||
        /* compute a new id */
 | 
			
		||||
        find_new_snapshot_id(bs, sn_info->id_str, sizeof(sn_info->id_str));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* check that the ID is unique */
 | 
			
		||||
    if (find_snapshot_by_id(bs, sn_info->id_str) >= 0)
 | 
			
		||||
        return -ENOENT;
 | 
			
		||||
 | 
			
		||||
    sn->id_str = qemu_strdup(sn_info->id_str);
 | 
			
		||||
    if (!sn->id_str)
 | 
			
		||||
        goto fail;
 | 
			
		||||
    sn->name = qemu_strdup(sn_info->name);
 | 
			
		||||
    if (!sn->name)
 | 
			
		||||
        goto fail;
 | 
			
		||||
    sn->vm_state_size = sn_info->vm_state_size;
 | 
			
		||||
    sn->date_sec = sn_info->date_sec;
 | 
			
		||||
    sn->date_nsec = sn_info->date_nsec;
 | 
			
		||||
    sn->vm_clock_nsec = sn_info->vm_clock_nsec;
 | 
			
		||||
 | 
			
		||||
    ret = qcow2_update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, 1);
 | 
			
		||||
    if (ret < 0)
 | 
			
		||||
        goto fail;
 | 
			
		||||
 | 
			
		||||
    /* create the L1 table of the snapshot */
 | 
			
		||||
    l1_table_offset = qcow2_alloc_clusters(bs, s->l1_size * sizeof(uint64_t));
 | 
			
		||||
    if (l1_table_offset < 0) {
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    sn->l1_table_offset = l1_table_offset;
 | 
			
		||||
    sn->l1_size = s->l1_size;
 | 
			
		||||
 | 
			
		||||
    if (s->l1_size != 0) {
 | 
			
		||||
        l1_table = qemu_malloc(s->l1_size * sizeof(uint64_t));
 | 
			
		||||
    } else {
 | 
			
		||||
        l1_table = NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for(i = 0; i < s->l1_size; i++) {
 | 
			
		||||
        l1_table[i] = cpu_to_be64(s->l1_table[i]);
 | 
			
		||||
    }
 | 
			
		||||
    if (bdrv_pwrite_sync(bs->file, sn->l1_table_offset,
 | 
			
		||||
                    l1_table, s->l1_size * sizeof(uint64_t)) < 0)
 | 
			
		||||
        goto fail;
 | 
			
		||||
    qemu_free(l1_table);
 | 
			
		||||
    l1_table = NULL;
 | 
			
		||||
 | 
			
		||||
    snapshots1 = qemu_malloc((s->nb_snapshots + 1) * sizeof(QCowSnapshot));
 | 
			
		||||
    if (s->snapshots) {
 | 
			
		||||
        memcpy(snapshots1, s->snapshots, s->nb_snapshots * sizeof(QCowSnapshot));
 | 
			
		||||
        qemu_free(s->snapshots);
 | 
			
		||||
    }
 | 
			
		||||
    s->snapshots = snapshots1;
 | 
			
		||||
    s->snapshots[s->nb_snapshots++] = *sn;
 | 
			
		||||
 | 
			
		||||
    if (qcow_write_snapshots(bs) < 0)
 | 
			
		||||
        goto fail;
 | 
			
		||||
#ifdef DEBUG_ALLOC
 | 
			
		||||
    qcow2_check_refcounts(bs);
 | 
			
		||||
#endif
 | 
			
		||||
    return 0;
 | 
			
		||||
 fail:
 | 
			
		||||
    qemu_free(sn->name);
 | 
			
		||||
    qemu_free(l1_table);
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* copy the snapshot 'snapshot_name' into the current disk image */
 | 
			
		||||
int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id)
 | 
			
		||||
{
 | 
			
		||||
    BDRVQcowState *s = bs->opaque;
 | 
			
		||||
    QCowSnapshot *sn;
 | 
			
		||||
    int i, snapshot_index, l1_size2;
 | 
			
		||||
 | 
			
		||||
    snapshot_index = find_snapshot_by_id_or_name(bs, snapshot_id);
 | 
			
		||||
    if (snapshot_index < 0)
 | 
			
		||||
        return -ENOENT;
 | 
			
		||||
    sn = &s->snapshots[snapshot_index];
 | 
			
		||||
 | 
			
		||||
    if (qcow2_update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, -1) < 0)
 | 
			
		||||
        goto fail;
 | 
			
		||||
 | 
			
		||||
    if (qcow2_grow_l1_table(bs, sn->l1_size) < 0)
 | 
			
		||||
        goto fail;
 | 
			
		||||
 | 
			
		||||
    s->l1_size = sn->l1_size;
 | 
			
		||||
    l1_size2 = s->l1_size * sizeof(uint64_t);
 | 
			
		||||
    /* copy the snapshot l1 table to the current l1 table */
 | 
			
		||||
    if (bdrv_pread(bs->file, sn->l1_table_offset,
 | 
			
		||||
                   s->l1_table, l1_size2) != l1_size2)
 | 
			
		||||
        goto fail;
 | 
			
		||||
    if (bdrv_pwrite_sync(bs->file, s->l1_table_offset,
 | 
			
		||||
                    s->l1_table, l1_size2) < 0)
 | 
			
		||||
        goto fail;
 | 
			
		||||
    for(i = 0;i < s->l1_size; i++) {
 | 
			
		||||
        be64_to_cpus(&s->l1_table[i]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (qcow2_update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, 1) < 0)
 | 
			
		||||
        goto fail;
 | 
			
		||||
 | 
			
		||||
#ifdef DEBUG_ALLOC
 | 
			
		||||
    qcow2_check_refcounts(bs);
 | 
			
		||||
#endif
 | 
			
		||||
    return 0;
 | 
			
		||||
 fail:
 | 
			
		||||
    return -EIO;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int qcow2_snapshot_delete(BlockDriverState *bs, const char *snapshot_id)
 | 
			
		||||
{
 | 
			
		||||
    BDRVQcowState *s = bs->opaque;
 | 
			
		||||
    QCowSnapshot *sn;
 | 
			
		||||
    int snapshot_index, ret;
 | 
			
		||||
 | 
			
		||||
    snapshot_index = find_snapshot_by_id_or_name(bs, snapshot_id);
 | 
			
		||||
    if (snapshot_index < 0)
 | 
			
		||||
        return -ENOENT;
 | 
			
		||||
    sn = &s->snapshots[snapshot_index];
 | 
			
		||||
 | 
			
		||||
    ret = qcow2_update_snapshot_refcount(bs, sn->l1_table_offset, sn->l1_size, -1);
 | 
			
		||||
    if (ret < 0)
 | 
			
		||||
        return ret;
 | 
			
		||||
    /* must update the copied flag on the current cluster offsets */
 | 
			
		||||
    ret = qcow2_update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, 0);
 | 
			
		||||
    if (ret < 0)
 | 
			
		||||
        return ret;
 | 
			
		||||
    qcow2_free_clusters(bs, sn->l1_table_offset, sn->l1_size * sizeof(uint64_t));
 | 
			
		||||
 | 
			
		||||
    qemu_free(sn->id_str);
 | 
			
		||||
    qemu_free(sn->name);
 | 
			
		||||
    memmove(sn, sn + 1, (s->nb_snapshots - snapshot_index - 1) * sizeof(*sn));
 | 
			
		||||
    s->nb_snapshots--;
 | 
			
		||||
    ret = qcow_write_snapshots(bs);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        /* XXX: restore snapshot if error ? */
 | 
			
		||||
        return ret;
 | 
			
		||||
    }
 | 
			
		||||
#ifdef DEBUG_ALLOC
 | 
			
		||||
    qcow2_check_refcounts(bs);
 | 
			
		||||
#endif
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int qcow2_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab)
 | 
			
		||||
{
 | 
			
		||||
    BDRVQcowState *s = bs->opaque;
 | 
			
		||||
    QEMUSnapshotInfo *sn_tab, *sn_info;
 | 
			
		||||
    QCowSnapshot *sn;
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
    if (!s->nb_snapshots) {
 | 
			
		||||
        *psn_tab = NULL;
 | 
			
		||||
        return s->nb_snapshots;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    sn_tab = qemu_mallocz(s->nb_snapshots * sizeof(QEMUSnapshotInfo));
 | 
			
		||||
    for(i = 0; i < s->nb_snapshots; i++) {
 | 
			
		||||
        sn_info = sn_tab + i;
 | 
			
		||||
        sn = s->snapshots + i;
 | 
			
		||||
        pstrcpy(sn_info->id_str, sizeof(sn_info->id_str),
 | 
			
		||||
                sn->id_str);
 | 
			
		||||
        pstrcpy(sn_info->name, sizeof(sn_info->name),
 | 
			
		||||
                sn->name);
 | 
			
		||||
        sn_info->vm_state_size = sn->vm_state_size;
 | 
			
		||||
        sn_info->date_sec = sn->date_sec;
 | 
			
		||||
        sn_info->date_nsec = sn->date_nsec;
 | 
			
		||||
        sn_info->vm_clock_nsec = sn->vm_clock_nsec;
 | 
			
		||||
    }
 | 
			
		||||
    *psn_tab = sn_tab;
 | 
			
		||||
    return s->nb_snapshots;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										1371
									
								
								block/qcow2.c
									
									
									
									
									
								
							
							
						
						
									
										1371
									
								
								block/qcow2.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										218
									
								
								block/qcow2.h
									
									
									
									
									
								
							
							
						
						
									
										218
									
								
								block/qcow2.h
									
									
									
									
									
								
							@@ -1,218 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Block driver for the QCOW version 2 format
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2004-2006 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.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef BLOCK_QCOW2_H
 | 
			
		||||
#define BLOCK_QCOW2_H
 | 
			
		||||
 | 
			
		||||
#include "aes.h"
 | 
			
		||||
 | 
			
		||||
//#define DEBUG_ALLOC
 | 
			
		||||
//#define DEBUG_ALLOC2
 | 
			
		||||
//#define DEBUG_EXT
 | 
			
		||||
 | 
			
		||||
#define QCOW_MAGIC (('Q' << 24) | ('F' << 16) | ('I' << 8) | 0xfb)
 | 
			
		||||
#define QCOW_VERSION 2
 | 
			
		||||
 | 
			
		||||
#define QCOW_CRYPT_NONE 0
 | 
			
		||||
#define QCOW_CRYPT_AES  1
 | 
			
		||||
 | 
			
		||||
#define QCOW_MAX_CRYPT_CLUSTERS 32
 | 
			
		||||
 | 
			
		||||
/* indicate that the refcount of the referenced cluster is exactly one. */
 | 
			
		||||
#define QCOW_OFLAG_COPIED     (1LL << 63)
 | 
			
		||||
/* indicate that the cluster is compressed (they never have the copied flag) */
 | 
			
		||||
#define QCOW_OFLAG_COMPRESSED (1LL << 62)
 | 
			
		||||
 | 
			
		||||
#define REFCOUNT_SHIFT 1 /* refcount size is 2 bytes */
 | 
			
		||||
 | 
			
		||||
#define MIN_CLUSTER_BITS 9
 | 
			
		||||
#define MAX_CLUSTER_BITS 21
 | 
			
		||||
 | 
			
		||||
#define L2_CACHE_SIZE 16
 | 
			
		||||
 | 
			
		||||
typedef struct QCowHeader {
 | 
			
		||||
    uint32_t magic;
 | 
			
		||||
    uint32_t version;
 | 
			
		||||
    uint64_t backing_file_offset;
 | 
			
		||||
    uint32_t backing_file_size;
 | 
			
		||||
    uint32_t cluster_bits;
 | 
			
		||||
    uint64_t size; /* in bytes */
 | 
			
		||||
    uint32_t crypt_method;
 | 
			
		||||
    uint32_t l1_size; /* XXX: save number of clusters instead ? */
 | 
			
		||||
    uint64_t l1_table_offset;
 | 
			
		||||
    uint64_t refcount_table_offset;
 | 
			
		||||
    uint32_t refcount_table_clusters;
 | 
			
		||||
    uint32_t nb_snapshots;
 | 
			
		||||
    uint64_t snapshots_offset;
 | 
			
		||||
} QCowHeader;
 | 
			
		||||
 | 
			
		||||
typedef struct QCowSnapshot {
 | 
			
		||||
    uint64_t l1_table_offset;
 | 
			
		||||
    uint32_t l1_size;
 | 
			
		||||
    char *id_str;
 | 
			
		||||
    char *name;
 | 
			
		||||
    uint32_t vm_state_size;
 | 
			
		||||
    uint32_t date_sec;
 | 
			
		||||
    uint32_t date_nsec;
 | 
			
		||||
    uint64_t vm_clock_nsec;
 | 
			
		||||
} QCowSnapshot;
 | 
			
		||||
 | 
			
		||||
typedef struct BDRVQcowState {
 | 
			
		||||
    BlockDriverState *hd;
 | 
			
		||||
    int cluster_bits;
 | 
			
		||||
    int cluster_size;
 | 
			
		||||
    int cluster_sectors;
 | 
			
		||||
    int l2_bits;
 | 
			
		||||
    int l2_size;
 | 
			
		||||
    int l1_size;
 | 
			
		||||
    int l1_vm_state_index;
 | 
			
		||||
    int csize_shift;
 | 
			
		||||
    int csize_mask;
 | 
			
		||||
    uint64_t cluster_offset_mask;
 | 
			
		||||
    uint64_t l1_table_offset;
 | 
			
		||||
    uint64_t *l1_table;
 | 
			
		||||
    uint64_t *l2_cache;
 | 
			
		||||
    uint64_t l2_cache_offsets[L2_CACHE_SIZE];
 | 
			
		||||
    uint32_t l2_cache_counts[L2_CACHE_SIZE];
 | 
			
		||||
    uint8_t *cluster_cache;
 | 
			
		||||
    uint8_t *cluster_data;
 | 
			
		||||
    uint64_t cluster_cache_offset;
 | 
			
		||||
    QLIST_HEAD(QCowClusterAlloc, QCowL2Meta) cluster_allocs;
 | 
			
		||||
 | 
			
		||||
    uint64_t *refcount_table;
 | 
			
		||||
    uint64_t refcount_table_offset;
 | 
			
		||||
    uint32_t refcount_table_size;
 | 
			
		||||
    uint64_t refcount_block_cache_offset;
 | 
			
		||||
    uint16_t *refcount_block_cache;
 | 
			
		||||
    int64_t free_cluster_index;
 | 
			
		||||
    int64_t free_byte_offset;
 | 
			
		||||
 | 
			
		||||
    uint32_t crypt_method; /* current crypt method, 0 if no key yet */
 | 
			
		||||
    uint32_t crypt_method_header;
 | 
			
		||||
    AES_KEY aes_encrypt_key;
 | 
			
		||||
    AES_KEY aes_decrypt_key;
 | 
			
		||||
    uint64_t snapshots_offset;
 | 
			
		||||
    int snapshots_size;
 | 
			
		||||
    int nb_snapshots;
 | 
			
		||||
    QCowSnapshot *snapshots;
 | 
			
		||||
} BDRVQcowState;
 | 
			
		||||
 | 
			
		||||
/* XXX: use std qcow open function ? */
 | 
			
		||||
typedef struct QCowCreateState {
 | 
			
		||||
    int cluster_size;
 | 
			
		||||
    int cluster_bits;
 | 
			
		||||
    uint16_t *refcount_block;
 | 
			
		||||
    uint64_t *refcount_table;
 | 
			
		||||
    int64_t l1_table_offset;
 | 
			
		||||
    int64_t refcount_table_offset;
 | 
			
		||||
    int64_t refcount_block_offset;
 | 
			
		||||
} QCowCreateState;
 | 
			
		||||
 | 
			
		||||
struct QCowAIOCB;
 | 
			
		||||
 | 
			
		||||
/* XXX This could be private for qcow2-cluster.c */
 | 
			
		||||
typedef struct QCowL2Meta
 | 
			
		||||
{
 | 
			
		||||
    uint64_t offset;
 | 
			
		||||
    uint64_t cluster_offset;
 | 
			
		||||
    int n_start;
 | 
			
		||||
    int nb_available;
 | 
			
		||||
    int nb_clusters;
 | 
			
		||||
    struct QCowL2Meta *depends_on;
 | 
			
		||||
    QLIST_HEAD(QCowAioDependencies, QCowAIOCB) dependent_requests;
 | 
			
		||||
 | 
			
		||||
    QLIST_ENTRY(QCowL2Meta) next_in_flight;
 | 
			
		||||
} QCowL2Meta;
 | 
			
		||||
 | 
			
		||||
static inline int size_to_clusters(BDRVQcowState *s, int64_t size)
 | 
			
		||||
{
 | 
			
		||||
    return (size + (s->cluster_size - 1)) >> s->cluster_bits;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline int size_to_l1(BDRVQcowState *s, int64_t size)
 | 
			
		||||
{
 | 
			
		||||
    int shift = s->cluster_bits + s->l2_bits;
 | 
			
		||||
    return (size + (1ULL << shift) - 1) >> shift;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline int64_t align_offset(int64_t offset, int n)
 | 
			
		||||
{
 | 
			
		||||
    offset = (offset + n - 1) & ~(n - 1);
 | 
			
		||||
    return offset;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// FIXME Need qcow2_ prefix to global functions
 | 
			
		||||
 | 
			
		||||
/* qcow2.c functions */
 | 
			
		||||
int qcow2_backing_read1(BlockDriverState *bs,
 | 
			
		||||
                  int64_t sector_num, uint8_t *buf, int nb_sectors);
 | 
			
		||||
 | 
			
		||||
/* qcow2-refcount.c functions */
 | 
			
		||||
int qcow2_refcount_init(BlockDriverState *bs);
 | 
			
		||||
void qcow2_refcount_close(BlockDriverState *bs);
 | 
			
		||||
 | 
			
		||||
int64_t qcow2_alloc_clusters(BlockDriverState *bs, int64_t size);
 | 
			
		||||
int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size);
 | 
			
		||||
void qcow2_free_clusters(BlockDriverState *bs,
 | 
			
		||||
    int64_t offset, int64_t size);
 | 
			
		||||
void qcow2_free_any_clusters(BlockDriverState *bs,
 | 
			
		||||
    uint64_t cluster_offset, int nb_clusters);
 | 
			
		||||
 | 
			
		||||
void qcow2_create_refcount_update(QCowCreateState *s, int64_t offset,
 | 
			
		||||
    int64_t size);
 | 
			
		||||
int qcow2_update_snapshot_refcount(BlockDriverState *bs,
 | 
			
		||||
    int64_t l1_table_offset, int l1_size, int addend);
 | 
			
		||||
 | 
			
		||||
int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res);
 | 
			
		||||
 | 
			
		||||
/* qcow2-cluster.c functions */
 | 
			
		||||
int qcow2_grow_l1_table(BlockDriverState *bs, int min_size);
 | 
			
		||||
void qcow2_l2_cache_reset(BlockDriverState *bs);
 | 
			
		||||
int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset);
 | 
			
		||||
void qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
 | 
			
		||||
                     uint8_t *out_buf, const uint8_t *in_buf,
 | 
			
		||||
                     int nb_sectors, int enc,
 | 
			
		||||
                     const AES_KEY *key);
 | 
			
		||||
 | 
			
		||||
int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
 | 
			
		||||
    int *num, uint64_t *cluster_offset);
 | 
			
		||||
int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset,
 | 
			
		||||
    int n_start, int n_end, int *num, QCowL2Meta *m);
 | 
			
		||||
uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs,
 | 
			
		||||
                                         uint64_t offset,
 | 
			
		||||
                                         int compressed_size);
 | 
			
		||||
 | 
			
		||||
int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m);
 | 
			
		||||
 | 
			
		||||
/* qcow2-snapshot.c functions */
 | 
			
		||||
int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info);
 | 
			
		||||
int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id);
 | 
			
		||||
int qcow2_snapshot_delete(BlockDriverState *bs, const char *snapshot_id);
 | 
			
		||||
int qcow2_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab);
 | 
			
		||||
 | 
			
		||||
void qcow2_free_snapshots(BlockDriverState *bs);
 | 
			
		||||
int qcow2_read_snapshots(BlockDriverState *bs);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
@@ -1,43 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * QEMU Posix block I/O backend AIO support
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright IBM, Corp. 2008
 | 
			
		||||
 *
 | 
			
		||||
 * Authors:
 | 
			
		||||
 *  Anthony Liguori   <aliguori@us.ibm.com>
 | 
			
		||||
 *
 | 
			
		||||
 * This work is licensed under the terms of the GNU GPL, version 2.  See
 | 
			
		||||
 * the COPYING file in the top-level directory.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
#ifndef QEMU_RAW_POSIX_AIO_H
 | 
			
		||||
#define QEMU_RAW_POSIX_AIO_H
 | 
			
		||||
 | 
			
		||||
/* AIO request types */
 | 
			
		||||
#define QEMU_AIO_READ         0x0001
 | 
			
		||||
#define QEMU_AIO_WRITE        0x0002
 | 
			
		||||
#define QEMU_AIO_IOCTL        0x0004
 | 
			
		||||
#define QEMU_AIO_FLUSH        0x0008
 | 
			
		||||
#define QEMU_AIO_TYPE_MASK \
 | 
			
		||||
	(QEMU_AIO_READ|QEMU_AIO_WRITE|QEMU_AIO_IOCTL|QEMU_AIO_FLUSH)
 | 
			
		||||
 | 
			
		||||
/* AIO flags */
 | 
			
		||||
#define QEMU_AIO_MISALIGNED   0x1000
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* posix-aio-compat.c - thread pool based implementation */
 | 
			
		||||
int paio_init(void);
 | 
			
		||||
BlockDriverAIOCB *paio_submit(BlockDriverState *bs, int fd,
 | 
			
		||||
        int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
 | 
			
		||||
        BlockDriverCompletionFunc *cb, void *opaque, int type);
 | 
			
		||||
BlockDriverAIOCB *paio_ioctl(BlockDriverState *bs, int fd,
 | 
			
		||||
        unsigned long int req, void *buf,
 | 
			
		||||
        BlockDriverCompletionFunc *cb, void *opaque);
 | 
			
		||||
 | 
			
		||||
/* linux-aio.c - Linux native implementation */
 | 
			
		||||
void *laio_init(void);
 | 
			
		||||
BlockDriverAIOCB *laio_submit(BlockDriverState *bs, void *aio_ctx, int fd,
 | 
			
		||||
        int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
 | 
			
		||||
        BlockDriverCompletionFunc *cb, void *opaque, int type);
 | 
			
		||||
 | 
			
		||||
#endif /* QEMU_RAW_POSIX_AIO_H */
 | 
			
		||||
							
								
								
									
										1380
									
								
								block/raw-posix.c
									
									
									
									
									
								
							
							
						
						
									
										1380
									
								
								block/raw-posix.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1,423 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Block driver for RAW files (win32)
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2006 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-common.h"
 | 
			
		||||
#include "qemu-timer.h"
 | 
			
		||||
#include "block_int.h"
 | 
			
		||||
#include "module.h"
 | 
			
		||||
#include <windows.h>
 | 
			
		||||
#include <winioctl.h>
 | 
			
		||||
 | 
			
		||||
#define FTYPE_FILE 0
 | 
			
		||||
#define FTYPE_CD     1
 | 
			
		||||
#define FTYPE_HARDDISK 2
 | 
			
		||||
 | 
			
		||||
typedef struct BDRVRawState {
 | 
			
		||||
    HANDLE hfile;
 | 
			
		||||
    int type;
 | 
			
		||||
    char drive_path[16]; /* format: "d:\" */
 | 
			
		||||
} BDRVRawState;
 | 
			
		||||
 | 
			
		||||
int qemu_ftruncate64(int fd, int64_t length)
 | 
			
		||||
{
 | 
			
		||||
    LARGE_INTEGER li;
 | 
			
		||||
    LONG high;
 | 
			
		||||
    HANDLE h;
 | 
			
		||||
    BOOL res;
 | 
			
		||||
 | 
			
		||||
    if ((GetVersion() & 0x80000000UL) && (length >> 32) != 0)
 | 
			
		||||
	return -1;
 | 
			
		||||
 | 
			
		||||
    h = (HANDLE)_get_osfhandle(fd);
 | 
			
		||||
 | 
			
		||||
    /* get current position, ftruncate do not change position */
 | 
			
		||||
    li.HighPart = 0;
 | 
			
		||||
    li.LowPart = SetFilePointer (h, 0, &li.HighPart, FILE_CURRENT);
 | 
			
		||||
    if (li.LowPart == 0xffffffffUL && GetLastError() != NO_ERROR)
 | 
			
		||||
	return -1;
 | 
			
		||||
 | 
			
		||||
    high = length >> 32;
 | 
			
		||||
    if (!SetFilePointer(h, (DWORD) length, &high, FILE_BEGIN))
 | 
			
		||||
	return -1;
 | 
			
		||||
    res = SetEndOfFile(h);
 | 
			
		||||
 | 
			
		||||
    /* back to old position */
 | 
			
		||||
    SetFilePointer(h, li.LowPart, &li.HighPart, FILE_BEGIN);
 | 
			
		||||
    return res ? 0 : -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int set_sparse(int fd)
 | 
			
		||||
{
 | 
			
		||||
    DWORD returned;
 | 
			
		||||
    return (int) DeviceIoControl((HANDLE)_get_osfhandle(fd), FSCTL_SET_SPARSE,
 | 
			
		||||
				 NULL, 0, NULL, 0, &returned, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int raw_open(BlockDriverState *bs, const char *filename, int flags)
 | 
			
		||||
{
 | 
			
		||||
    BDRVRawState *s = bs->opaque;
 | 
			
		||||
    int access_flags;
 | 
			
		||||
    DWORD overlapped;
 | 
			
		||||
 | 
			
		||||
    s->type = FTYPE_FILE;
 | 
			
		||||
 | 
			
		||||
    if (flags & BDRV_O_RDWR) {
 | 
			
		||||
        access_flags = GENERIC_READ | GENERIC_WRITE;
 | 
			
		||||
    } else {
 | 
			
		||||
        access_flags = GENERIC_READ;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    overlapped = FILE_ATTRIBUTE_NORMAL;
 | 
			
		||||
    if ((flags & BDRV_O_NOCACHE))
 | 
			
		||||
        overlapped |= FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH;
 | 
			
		||||
    else if (!(flags & BDRV_O_CACHE_WB))
 | 
			
		||||
        overlapped |= FILE_FLAG_WRITE_THROUGH;
 | 
			
		||||
    s->hfile = CreateFile(filename, access_flags,
 | 
			
		||||
                          FILE_SHARE_READ, NULL,
 | 
			
		||||
                          OPEN_EXISTING, overlapped, NULL);
 | 
			
		||||
    if (s->hfile == INVALID_HANDLE_VALUE) {
 | 
			
		||||
        int err = GetLastError();
 | 
			
		||||
 | 
			
		||||
        if (err == ERROR_ACCESS_DENIED)
 | 
			
		||||
            return -EACCES;
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int raw_read(BlockDriverState *bs, int64_t sector_num,
 | 
			
		||||
                    uint8_t *buf, int nb_sectors)
 | 
			
		||||
{
 | 
			
		||||
    BDRVRawState *s = bs->opaque;
 | 
			
		||||
    OVERLAPPED ov;
 | 
			
		||||
    DWORD ret_count;
 | 
			
		||||
    int ret;
 | 
			
		||||
    int64_t offset = sector_num * 512;
 | 
			
		||||
    int count = nb_sectors * 512;
 | 
			
		||||
 | 
			
		||||
    memset(&ov, 0, sizeof(ov));
 | 
			
		||||
    ov.Offset = offset;
 | 
			
		||||
    ov.OffsetHigh = offset >> 32;
 | 
			
		||||
    ret = ReadFile(s->hfile, buf, count, &ret_count, &ov);
 | 
			
		||||
    if (!ret)
 | 
			
		||||
        return ret_count;
 | 
			
		||||
    if (ret_count == count)
 | 
			
		||||
        ret_count = 0;
 | 
			
		||||
    return ret_count;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int raw_write(BlockDriverState *bs, int64_t sector_num,
 | 
			
		||||
                     const uint8_t *buf, int nb_sectors)
 | 
			
		||||
{
 | 
			
		||||
    BDRVRawState *s = bs->opaque;
 | 
			
		||||
    OVERLAPPED ov;
 | 
			
		||||
    DWORD ret_count;
 | 
			
		||||
    int ret;
 | 
			
		||||
    int64_t offset = sector_num * 512;
 | 
			
		||||
    int count = nb_sectors * 512;
 | 
			
		||||
 | 
			
		||||
    memset(&ov, 0, sizeof(ov));
 | 
			
		||||
    ov.Offset = offset;
 | 
			
		||||
    ov.OffsetHigh = offset >> 32;
 | 
			
		||||
    ret = WriteFile(s->hfile, buf, count, &ret_count, &ov);
 | 
			
		||||
    if (!ret)
 | 
			
		||||
        return ret_count;
 | 
			
		||||
    if (ret_count == count)
 | 
			
		||||
        ret_count = 0;
 | 
			
		||||
    return ret_count;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void raw_flush(BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
    BDRVRawState *s = bs->opaque;
 | 
			
		||||
    FlushFileBuffers(s->hfile);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void raw_close(BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
    BDRVRawState *s = bs->opaque;
 | 
			
		||||
    CloseHandle(s->hfile);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int raw_truncate(BlockDriverState *bs, int64_t offset)
 | 
			
		||||
{
 | 
			
		||||
    BDRVRawState *s = bs->opaque;
 | 
			
		||||
    LONG low, high;
 | 
			
		||||
 | 
			
		||||
    low = offset;
 | 
			
		||||
    high = offset >> 32;
 | 
			
		||||
    if (!SetFilePointer(s->hfile, low, &high, FILE_BEGIN))
 | 
			
		||||
	return -EIO;
 | 
			
		||||
    if (!SetEndOfFile(s->hfile))
 | 
			
		||||
        return -EIO;
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int64_t raw_getlength(BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
    BDRVRawState *s = bs->opaque;
 | 
			
		||||
    LARGE_INTEGER l;
 | 
			
		||||
    ULARGE_INTEGER available, total, total_free;
 | 
			
		||||
    DISK_GEOMETRY_EX dg;
 | 
			
		||||
    DWORD count;
 | 
			
		||||
    BOOL status;
 | 
			
		||||
 | 
			
		||||
    switch(s->type) {
 | 
			
		||||
    case FTYPE_FILE:
 | 
			
		||||
        l.LowPart = GetFileSize(s->hfile, (PDWORD)&l.HighPart);
 | 
			
		||||
        if (l.LowPart == 0xffffffffUL && GetLastError() != NO_ERROR)
 | 
			
		||||
            return -EIO;
 | 
			
		||||
        break;
 | 
			
		||||
    case FTYPE_CD:
 | 
			
		||||
        if (!GetDiskFreeSpaceEx(s->drive_path, &available, &total, &total_free))
 | 
			
		||||
            return -EIO;
 | 
			
		||||
        l.QuadPart = total.QuadPart;
 | 
			
		||||
        break;
 | 
			
		||||
    case FTYPE_HARDDISK:
 | 
			
		||||
        status = DeviceIoControl(s->hfile, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX,
 | 
			
		||||
                                 NULL, 0, &dg, sizeof(dg), &count, NULL);
 | 
			
		||||
        if (status != 0) {
 | 
			
		||||
            l = dg.DiskSize;
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
    default:
 | 
			
		||||
        return -EIO;
 | 
			
		||||
    }
 | 
			
		||||
    return l.QuadPart;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int raw_create(const char *filename, QEMUOptionParameter *options)
 | 
			
		||||
{
 | 
			
		||||
    int fd;
 | 
			
		||||
    int64_t total_size = 0;
 | 
			
		||||
 | 
			
		||||
    /* Read out options */
 | 
			
		||||
    while (options && options->name) {
 | 
			
		||||
        if (!strcmp(options->name, BLOCK_OPT_SIZE)) {
 | 
			
		||||
            total_size = options->value.n / 512;
 | 
			
		||||
        }
 | 
			
		||||
        options++;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
 | 
			
		||||
              0644);
 | 
			
		||||
    if (fd < 0)
 | 
			
		||||
        return -EIO;
 | 
			
		||||
    set_sparse(fd);
 | 
			
		||||
    ftruncate(fd, total_size * 512);
 | 
			
		||||
    close(fd);
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static QEMUOptionParameter raw_create_options[] = {
 | 
			
		||||
    {
 | 
			
		||||
        .name = BLOCK_OPT_SIZE,
 | 
			
		||||
        .type = OPT_SIZE,
 | 
			
		||||
        .help = "Virtual disk size"
 | 
			
		||||
    },
 | 
			
		||||
    { NULL }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static BlockDriver bdrv_file = {
 | 
			
		||||
    .format_name	= "file",
 | 
			
		||||
    .protocol_name	= "file",
 | 
			
		||||
    .instance_size	= sizeof(BDRVRawState),
 | 
			
		||||
    .bdrv_file_open	= raw_open,
 | 
			
		||||
    .bdrv_close		= raw_close,
 | 
			
		||||
    .bdrv_create	= raw_create,
 | 
			
		||||
    .bdrv_flush		= raw_flush,
 | 
			
		||||
    .bdrv_read		= raw_read,
 | 
			
		||||
    .bdrv_write		= raw_write,
 | 
			
		||||
    .bdrv_truncate	= raw_truncate,
 | 
			
		||||
    .bdrv_getlength	= raw_getlength,
 | 
			
		||||
 | 
			
		||||
    .create_options = raw_create_options,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/***********************************************/
 | 
			
		||||
/* host device */
 | 
			
		||||
 | 
			
		||||
static int find_cdrom(char *cdrom_name, int cdrom_name_size)
 | 
			
		||||
{
 | 
			
		||||
    char drives[256], *pdrv = drives;
 | 
			
		||||
    UINT type;
 | 
			
		||||
 | 
			
		||||
    memset(drives, 0, sizeof(drives));
 | 
			
		||||
    GetLogicalDriveStrings(sizeof(drives), drives);
 | 
			
		||||
    while(pdrv[0] != '\0') {
 | 
			
		||||
        type = GetDriveType(pdrv);
 | 
			
		||||
        switch(type) {
 | 
			
		||||
        case DRIVE_CDROM:
 | 
			
		||||
            snprintf(cdrom_name, cdrom_name_size, "\\\\.\\%c:", pdrv[0]);
 | 
			
		||||
            return 0;
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        pdrv += lstrlen(pdrv) + 1;
 | 
			
		||||
    }
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int find_device_type(BlockDriverState *bs, const char *filename)
 | 
			
		||||
{
 | 
			
		||||
    BDRVRawState *s = bs->opaque;
 | 
			
		||||
    UINT type;
 | 
			
		||||
    const char *p;
 | 
			
		||||
 | 
			
		||||
    if (strstart(filename, "\\\\.\\", &p) ||
 | 
			
		||||
        strstart(filename, "//./", &p)) {
 | 
			
		||||
        if (stristart(p, "PhysicalDrive", NULL))
 | 
			
		||||
            return FTYPE_HARDDISK;
 | 
			
		||||
        snprintf(s->drive_path, sizeof(s->drive_path), "%c:\\", p[0]);
 | 
			
		||||
        type = GetDriveType(s->drive_path);
 | 
			
		||||
        switch (type) {
 | 
			
		||||
        case DRIVE_REMOVABLE:
 | 
			
		||||
        case DRIVE_FIXED:
 | 
			
		||||
            return FTYPE_HARDDISK;
 | 
			
		||||
        case DRIVE_CDROM:
 | 
			
		||||
            return FTYPE_CD;
 | 
			
		||||
        default:
 | 
			
		||||
            return FTYPE_FILE;
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        return FTYPE_FILE;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int hdev_probe_device(const char *filename)
 | 
			
		||||
{
 | 
			
		||||
    if (strstart(filename, "/dev/cdrom", NULL))
 | 
			
		||||
        return 100;
 | 
			
		||||
    if (is_windows_drive(filename))
 | 
			
		||||
        return 100;
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int hdev_open(BlockDriverState *bs, const char *filename, int flags)
 | 
			
		||||
{
 | 
			
		||||
    BDRVRawState *s = bs->opaque;
 | 
			
		||||
    int access_flags, create_flags;
 | 
			
		||||
    DWORD overlapped;
 | 
			
		||||
    char device_name[64];
 | 
			
		||||
 | 
			
		||||
    if (strstart(filename, "/dev/cdrom", NULL)) {
 | 
			
		||||
        if (find_cdrom(device_name, sizeof(device_name)) < 0)
 | 
			
		||||
            return -ENOENT;
 | 
			
		||||
        filename = device_name;
 | 
			
		||||
    } else {
 | 
			
		||||
        /* transform drive letters into device name */
 | 
			
		||||
        if (((filename[0] >= 'a' && filename[0] <= 'z') ||
 | 
			
		||||
             (filename[0] >= 'A' && filename[0] <= 'Z')) &&
 | 
			
		||||
            filename[1] == ':' && filename[2] == '\0') {
 | 
			
		||||
            snprintf(device_name, sizeof(device_name), "\\\\.\\%c:", filename[0]);
 | 
			
		||||
            filename = device_name;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    s->type = find_device_type(bs, filename);
 | 
			
		||||
 | 
			
		||||
    if (flags & BDRV_O_RDWR) {
 | 
			
		||||
        access_flags = GENERIC_READ | GENERIC_WRITE;
 | 
			
		||||
    } else {
 | 
			
		||||
        access_flags = GENERIC_READ;
 | 
			
		||||
    }
 | 
			
		||||
    create_flags = OPEN_EXISTING;
 | 
			
		||||
 | 
			
		||||
    overlapped = FILE_ATTRIBUTE_NORMAL;
 | 
			
		||||
    if ((flags & BDRV_O_NOCACHE))
 | 
			
		||||
        overlapped |= FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH;
 | 
			
		||||
    else if (!(flags & BDRV_O_CACHE_WB))
 | 
			
		||||
        overlapped |= FILE_FLAG_WRITE_THROUGH;
 | 
			
		||||
    s->hfile = CreateFile(filename, access_flags,
 | 
			
		||||
                          FILE_SHARE_READ, NULL,
 | 
			
		||||
                          create_flags, overlapped, NULL);
 | 
			
		||||
    if (s->hfile == INVALID_HANDLE_VALUE) {
 | 
			
		||||
        int err = GetLastError();
 | 
			
		||||
 | 
			
		||||
        if (err == ERROR_ACCESS_DENIED)
 | 
			
		||||
            return -EACCES;
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#if 0
 | 
			
		||||
/***********************************************/
 | 
			
		||||
/* removable device additional commands */
 | 
			
		||||
 | 
			
		||||
static int raw_is_inserted(BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
    return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int raw_media_changed(BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
    return -ENOTSUP;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int raw_eject(BlockDriverState *bs, int eject_flag)
 | 
			
		||||
{
 | 
			
		||||
    DWORD ret_count;
 | 
			
		||||
 | 
			
		||||
    if (s->type == FTYPE_FILE)
 | 
			
		||||
        return -ENOTSUP;
 | 
			
		||||
    if (eject_flag) {
 | 
			
		||||
        DeviceIoControl(s->hfile, IOCTL_STORAGE_EJECT_MEDIA,
 | 
			
		||||
                        NULL, 0, NULL, 0, &lpBytesReturned, NULL);
 | 
			
		||||
    } else {
 | 
			
		||||
        DeviceIoControl(s->hfile, IOCTL_STORAGE_LOAD_MEDIA,
 | 
			
		||||
                        NULL, 0, NULL, 0, &lpBytesReturned, NULL);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int raw_set_locked(BlockDriverState *bs, int locked)
 | 
			
		||||
{
 | 
			
		||||
    return -ENOTSUP;
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static int hdev_has_zero_init(BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static BlockDriver bdrv_host_device = {
 | 
			
		||||
    .format_name	= "host_device",
 | 
			
		||||
    .protocol_name	= "host_device",
 | 
			
		||||
    .instance_size	= sizeof(BDRVRawState),
 | 
			
		||||
    .bdrv_probe_device	= hdev_probe_device,
 | 
			
		||||
    .bdrv_file_open	= hdev_open,
 | 
			
		||||
    .bdrv_close		= raw_close,
 | 
			
		||||
    .bdrv_flush		= raw_flush,
 | 
			
		||||
    .bdrv_has_zero_init = hdev_has_zero_init,
 | 
			
		||||
 | 
			
		||||
    .bdrv_read		= raw_read,
 | 
			
		||||
    .bdrv_write	        = raw_write,
 | 
			
		||||
    .bdrv_getlength	= raw_getlength,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void bdrv_file_init(void)
 | 
			
		||||
{
 | 
			
		||||
    bdrv_register(&bdrv_file);
 | 
			
		||||
    bdrv_register(&bdrv_host_device);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
block_init(bdrv_file_init);
 | 
			
		||||
							
								
								
									
										150
									
								
								block/raw.c
									
									
									
									
									
								
							
							
						
						
									
										150
									
								
								block/raw.c
									
									
									
									
									
								
							@@ -1,150 +0,0 @@
 | 
			
		||||
 | 
			
		||||
#include "qemu-common.h"
 | 
			
		||||
#include "block_int.h"
 | 
			
		||||
#include "module.h"
 | 
			
		||||
 | 
			
		||||
static int raw_open(BlockDriverState *bs, int flags)
 | 
			
		||||
{
 | 
			
		||||
    bs->sg = bs->file->sg;
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int raw_read(BlockDriverState *bs, int64_t sector_num,
 | 
			
		||||
                    uint8_t *buf, int nb_sectors)
 | 
			
		||||
{
 | 
			
		||||
    return bdrv_read(bs->file, sector_num, buf, nb_sectors);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int raw_write(BlockDriverState *bs, int64_t sector_num,
 | 
			
		||||
                     const uint8_t *buf, int nb_sectors)
 | 
			
		||||
{
 | 
			
		||||
    return bdrv_write(bs->file, sector_num, buf, nb_sectors);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static BlockDriverAIOCB *raw_aio_readv(BlockDriverState *bs,
 | 
			
		||||
    int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
 | 
			
		||||
    BlockDriverCompletionFunc *cb, void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    return bdrv_aio_readv(bs->file, sector_num, qiov, nb_sectors, cb, opaque);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static BlockDriverAIOCB *raw_aio_writev(BlockDriverState *bs,
 | 
			
		||||
    int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
 | 
			
		||||
    BlockDriverCompletionFunc *cb, void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    return bdrv_aio_writev(bs->file, sector_num, qiov, nb_sectors, cb, opaque);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void raw_close(BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void raw_flush(BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
    bdrv_flush(bs->file);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static BlockDriverAIOCB *raw_aio_flush(BlockDriverState *bs,
 | 
			
		||||
    BlockDriverCompletionFunc *cb, void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    return bdrv_aio_flush(bs->file, cb, opaque);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int64_t raw_getlength(BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
    return bdrv_getlength(bs->file);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int raw_truncate(BlockDriverState *bs, int64_t offset)
 | 
			
		||||
{
 | 
			
		||||
    return bdrv_truncate(bs->file, offset);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int raw_probe(const uint8_t *buf, int buf_size, const char *filename)
 | 
			
		||||
{
 | 
			
		||||
   return 1; /* everything can be opened as raw image */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int raw_is_inserted(BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
    return bdrv_is_inserted(bs->file);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int raw_eject(BlockDriverState *bs, int eject_flag)
 | 
			
		||||
{
 | 
			
		||||
    return bdrv_eject(bs->file, eject_flag);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int raw_set_locked(BlockDriverState *bs, int locked)
 | 
			
		||||
{
 | 
			
		||||
    bdrv_set_locked(bs->file, locked);
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int raw_ioctl(BlockDriverState *bs, unsigned long int req, void *buf)
 | 
			
		||||
{
 | 
			
		||||
   return bdrv_ioctl(bs->file, req, buf);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static BlockDriverAIOCB *raw_aio_ioctl(BlockDriverState *bs,
 | 
			
		||||
        unsigned long int req, void *buf,
 | 
			
		||||
        BlockDriverCompletionFunc *cb, void *opaque)
 | 
			
		||||
{
 | 
			
		||||
   return bdrv_aio_ioctl(bs->file, req, buf, cb, opaque);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int raw_create(const char *filename, QEMUOptionParameter *options)
 | 
			
		||||
{
 | 
			
		||||
    return bdrv_create_file(filename, options);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static QEMUOptionParameter raw_create_options[] = {
 | 
			
		||||
    {
 | 
			
		||||
        .name = BLOCK_OPT_SIZE,
 | 
			
		||||
        .type = OPT_SIZE,
 | 
			
		||||
        .help = "Virtual disk size"
 | 
			
		||||
    },
 | 
			
		||||
    { NULL }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int raw_has_zero_init(BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
    return bdrv_has_zero_init(bs->file);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static BlockDriver bdrv_raw = {
 | 
			
		||||
    .format_name        = "raw",
 | 
			
		||||
 | 
			
		||||
    /* It's really 0, but we need to make qemu_malloc() happy */
 | 
			
		||||
    .instance_size      = 1,
 | 
			
		||||
 | 
			
		||||
    .bdrv_open          = raw_open,
 | 
			
		||||
    .bdrv_close         = raw_close,
 | 
			
		||||
    .bdrv_read          = raw_read,
 | 
			
		||||
    .bdrv_write         = raw_write,
 | 
			
		||||
    .bdrv_flush         = raw_flush,
 | 
			
		||||
    .bdrv_probe         = raw_probe,
 | 
			
		||||
    .bdrv_getlength     = raw_getlength,
 | 
			
		||||
    .bdrv_truncate      = raw_truncate,
 | 
			
		||||
 | 
			
		||||
    .bdrv_aio_readv     = raw_aio_readv,
 | 
			
		||||
    .bdrv_aio_writev    = raw_aio_writev,
 | 
			
		||||
    .bdrv_aio_flush     = raw_aio_flush,
 | 
			
		||||
 | 
			
		||||
    .bdrv_is_inserted   = raw_is_inserted,
 | 
			
		||||
    .bdrv_eject         = raw_eject,
 | 
			
		||||
    .bdrv_set_locked    = raw_set_locked,
 | 
			
		||||
    .bdrv_ioctl         = raw_ioctl,
 | 
			
		||||
    .bdrv_aio_ioctl     = raw_aio_ioctl,
 | 
			
		||||
 | 
			
		||||
    .bdrv_create        = raw_create,
 | 
			
		||||
    .create_options     = raw_create_options,
 | 
			
		||||
    .bdrv_has_zero_init = raw_has_zero_init,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void bdrv_raw_init(void)
 | 
			
		||||
{
 | 
			
		||||
    bdrv_register(&bdrv_raw);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
block_init(bdrv_raw_init);
 | 
			
		||||
							
								
								
									
										2036
									
								
								block/sheepdog.c
									
									
									
									
									
								
							
							
						
						
									
										2036
									
								
								block/sheepdog.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										962
									
								
								block/vdi.c
									
									
									
									
									
								
							
							
						
						
									
										962
									
								
								block/vdi.c
									
									
									
									
									
								
							@@ -1,962 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Block driver for the Virtual Disk Image (VDI) format
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2009 Stefan Weil
 | 
			
		||||
 *
 | 
			
		||||
 * 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) version 3 or any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 * Reference:
 | 
			
		||||
 * http://forums.virtualbox.org/viewtopic.php?t=8046
 | 
			
		||||
 *
 | 
			
		||||
 * This driver supports create / read / write operations on VDI images.
 | 
			
		||||
 *
 | 
			
		||||
 * Todo (see also TODO in code):
 | 
			
		||||
 *
 | 
			
		||||
 * Some features like snapshots are still missing.
 | 
			
		||||
 *
 | 
			
		||||
 * Deallocation of zero-filled blocks and shrinking images are missing, too
 | 
			
		||||
 * (might be added to common block layer).
 | 
			
		||||
 *
 | 
			
		||||
 * Allocation of blocks could be optimized (less writes to block map and
 | 
			
		||||
 * header).
 | 
			
		||||
 *
 | 
			
		||||
 * Read and write of adjacents blocks could be done in one operation
 | 
			
		||||
 * (current code uses one operation per block (1 MiB).
 | 
			
		||||
 *
 | 
			
		||||
 * The code is not thread safe (missing locks for changes in header and
 | 
			
		||||
 * block table, no problem with current QEMU).
 | 
			
		||||
 *
 | 
			
		||||
 * Hints:
 | 
			
		||||
 *
 | 
			
		||||
 * Blocks (VDI documentation) correspond to clusters (QEMU).
 | 
			
		||||
 * QEMU's backing files could be implemented using VDI snapshot files (TODO).
 | 
			
		||||
 * VDI snapshot files may also contain the complete machine state.
 | 
			
		||||
 * Maybe this machine state can be converted to QEMU PC machine snapshot data.
 | 
			
		||||
 *
 | 
			
		||||
 * The driver keeps a block cache (little endian entries) in memory.
 | 
			
		||||
 * For the standard block size (1 MiB), a 1 TiB disk will use 4 MiB RAM,
 | 
			
		||||
 * so this seems to be reasonable.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "qemu-common.h"
 | 
			
		||||
#include "block_int.h"
 | 
			
		||||
#include "module.h"
 | 
			
		||||
 | 
			
		||||
#if defined(CONFIG_UUID)
 | 
			
		||||
#include <uuid/uuid.h>
 | 
			
		||||
#else
 | 
			
		||||
/* TODO: move uuid emulation to some central place in QEMU. */
 | 
			
		||||
#include "sysemu.h"     /* UUID_FMT */
 | 
			
		||||
typedef unsigned char uuid_t[16];
 | 
			
		||||
void uuid_generate(uuid_t out);
 | 
			
		||||
int uuid_is_null(const uuid_t uu);
 | 
			
		||||
void uuid_unparse(const uuid_t uu, char *out);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Code configuration options. */
 | 
			
		||||
 | 
			
		||||
/* Enable debug messages. */
 | 
			
		||||
//~ #define CONFIG_VDI_DEBUG
 | 
			
		||||
 | 
			
		||||
/* Support write operations on VDI images. */
 | 
			
		||||
#define CONFIG_VDI_WRITE
 | 
			
		||||
 | 
			
		||||
/* Support non-standard block (cluster) size. This is untested.
 | 
			
		||||
 * Maybe it will be needed for very large images.
 | 
			
		||||
 */
 | 
			
		||||
//~ #define CONFIG_VDI_BLOCK_SIZE
 | 
			
		||||
 | 
			
		||||
/* Support static (fixed, pre-allocated) images. */
 | 
			
		||||
#define CONFIG_VDI_STATIC_IMAGE
 | 
			
		||||
 | 
			
		||||
/* Command line option for static images. */
 | 
			
		||||
#define BLOCK_OPT_STATIC "static"
 | 
			
		||||
 | 
			
		||||
#define KiB     1024
 | 
			
		||||
#define MiB     (KiB * KiB)
 | 
			
		||||
 | 
			
		||||
#define SECTOR_SIZE 512
 | 
			
		||||
 | 
			
		||||
#if defined(CONFIG_VDI_DEBUG)
 | 
			
		||||
#define logout(fmt, ...) \
 | 
			
		||||
                fprintf(stderr, "vdi\t%-24s" fmt, __func__, ##__VA_ARGS__)
 | 
			
		||||
#else
 | 
			
		||||
#define logout(fmt, ...) ((void)0)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Image signature. */
 | 
			
		||||
#define VDI_SIGNATURE 0xbeda107f
 | 
			
		||||
 | 
			
		||||
/* Image version. */
 | 
			
		||||
#define VDI_VERSION_1_1 0x00010001
 | 
			
		||||
 | 
			
		||||
/* Image type. */
 | 
			
		||||
#define VDI_TYPE_DYNAMIC 1
 | 
			
		||||
#define VDI_TYPE_STATIC  2
 | 
			
		||||
 | 
			
		||||
/* Innotek / SUN images use these strings in header.text:
 | 
			
		||||
 * "<<< innotek VirtualBox Disk Image >>>\n"
 | 
			
		||||
 * "<<< Sun xVM VirtualBox Disk Image >>>\n"
 | 
			
		||||
 * "<<< Sun VirtualBox Disk Image >>>\n"
 | 
			
		||||
 * The value does not matter, so QEMU created images use a different text.
 | 
			
		||||
 */
 | 
			
		||||
#define VDI_TEXT "<<< QEMU VM Virtual Disk Image >>>\n"
 | 
			
		||||
 | 
			
		||||
/* Unallocated blocks use this index (no need to convert endianess). */
 | 
			
		||||
#define VDI_UNALLOCATED UINT32_MAX
 | 
			
		||||
 | 
			
		||||
#if !defined(CONFIG_UUID)
 | 
			
		||||
void uuid_generate(uuid_t out)
 | 
			
		||||
{
 | 
			
		||||
    memset(out, 0, sizeof(out));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int uuid_is_null(const uuid_t uu)
 | 
			
		||||
{
 | 
			
		||||
    uuid_t null_uuid = { 0 };
 | 
			
		||||
    return memcmp(uu, null_uuid, sizeof(uu)) == 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void uuid_unparse(const uuid_t uu, char *out)
 | 
			
		||||
{
 | 
			
		||||
    snprintf(out, 37, UUID_FMT,
 | 
			
		||||
            uu[0], uu[1], uu[2], uu[3], uu[4], uu[5], uu[6], uu[7],
 | 
			
		||||
            uu[8], uu[9], uu[10], uu[11], uu[12], uu[13], uu[14], uu[15]);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    BlockDriverAIOCB common;
 | 
			
		||||
    int64_t sector_num;
 | 
			
		||||
    QEMUIOVector *qiov;
 | 
			
		||||
    uint8_t *buf;
 | 
			
		||||
    /* Total number of sectors. */
 | 
			
		||||
    int nb_sectors;
 | 
			
		||||
    /* Number of sectors for current AIO. */
 | 
			
		||||
    int n_sectors;
 | 
			
		||||
    /* New allocated block map entry. */
 | 
			
		||||
    uint32_t bmap_first;
 | 
			
		||||
    uint32_t bmap_last;
 | 
			
		||||
    /* Buffer for new allocated block. */
 | 
			
		||||
    void *block_buffer;
 | 
			
		||||
    void *orig_buf;
 | 
			
		||||
    int header_modified;
 | 
			
		||||
    BlockDriverAIOCB *hd_aiocb;
 | 
			
		||||
    struct iovec hd_iov;
 | 
			
		||||
    QEMUIOVector hd_qiov;
 | 
			
		||||
    QEMUBH *bh;
 | 
			
		||||
} VdiAIOCB;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    char text[0x40];
 | 
			
		||||
    uint32_t signature;
 | 
			
		||||
    uint32_t version;
 | 
			
		||||
    uint32_t header_size;
 | 
			
		||||
    uint32_t image_type;
 | 
			
		||||
    uint32_t image_flags;
 | 
			
		||||
    char description[256];
 | 
			
		||||
    uint32_t offset_bmap;
 | 
			
		||||
    uint32_t offset_data;
 | 
			
		||||
    uint32_t cylinders;         /* disk geometry, unused here */
 | 
			
		||||
    uint32_t heads;             /* disk geometry, unused here */
 | 
			
		||||
    uint32_t sectors;           /* disk geometry, unused here */
 | 
			
		||||
    uint32_t sector_size;
 | 
			
		||||
    uint32_t unused1;
 | 
			
		||||
    uint64_t disk_size;
 | 
			
		||||
    uint32_t block_size;
 | 
			
		||||
    uint32_t block_extra;       /* unused here */
 | 
			
		||||
    uint32_t blocks_in_image;
 | 
			
		||||
    uint32_t blocks_allocated;
 | 
			
		||||
    uuid_t uuid_image;
 | 
			
		||||
    uuid_t uuid_last_snap;
 | 
			
		||||
    uuid_t uuid_link;
 | 
			
		||||
    uuid_t uuid_parent;
 | 
			
		||||
    uint64_t unused2[7];
 | 
			
		||||
} VdiHeader;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    BlockDriverState *hd;
 | 
			
		||||
    /* The block map entries are little endian (even in memory). */
 | 
			
		||||
    uint32_t *bmap;
 | 
			
		||||
    /* Size of block (bytes). */
 | 
			
		||||
    uint32_t block_size;
 | 
			
		||||
    /* Size of block (sectors). */
 | 
			
		||||
    uint32_t block_sectors;
 | 
			
		||||
    /* First sector of block map. */
 | 
			
		||||
    uint32_t bmap_sector;
 | 
			
		||||
    /* VDI header (converted to host endianess). */
 | 
			
		||||
    VdiHeader header;
 | 
			
		||||
} BDRVVdiState;
 | 
			
		||||
 | 
			
		||||
/* Change UUID from little endian (IPRT = VirtualBox format) to big endian
 | 
			
		||||
 * format (network byte order, standard, see RFC 4122) and vice versa.
 | 
			
		||||
 */
 | 
			
		||||
static void uuid_convert(uuid_t uuid)
 | 
			
		||||
{
 | 
			
		||||
    bswap32s((uint32_t *)&uuid[0]);
 | 
			
		||||
    bswap16s((uint16_t *)&uuid[4]);
 | 
			
		||||
    bswap16s((uint16_t *)&uuid[6]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void vdi_header_to_cpu(VdiHeader *header)
 | 
			
		||||
{
 | 
			
		||||
    le32_to_cpus(&header->signature);
 | 
			
		||||
    le32_to_cpus(&header->version);
 | 
			
		||||
    le32_to_cpus(&header->header_size);
 | 
			
		||||
    le32_to_cpus(&header->image_type);
 | 
			
		||||
    le32_to_cpus(&header->image_flags);
 | 
			
		||||
    le32_to_cpus(&header->offset_bmap);
 | 
			
		||||
    le32_to_cpus(&header->offset_data);
 | 
			
		||||
    le32_to_cpus(&header->cylinders);
 | 
			
		||||
    le32_to_cpus(&header->heads);
 | 
			
		||||
    le32_to_cpus(&header->sectors);
 | 
			
		||||
    le32_to_cpus(&header->sector_size);
 | 
			
		||||
    le64_to_cpus(&header->disk_size);
 | 
			
		||||
    le32_to_cpus(&header->block_size);
 | 
			
		||||
    le32_to_cpus(&header->block_extra);
 | 
			
		||||
    le32_to_cpus(&header->blocks_in_image);
 | 
			
		||||
    le32_to_cpus(&header->blocks_allocated);
 | 
			
		||||
    uuid_convert(header->uuid_image);
 | 
			
		||||
    uuid_convert(header->uuid_last_snap);
 | 
			
		||||
    uuid_convert(header->uuid_link);
 | 
			
		||||
    uuid_convert(header->uuid_parent);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void vdi_header_to_le(VdiHeader *header)
 | 
			
		||||
{
 | 
			
		||||
    cpu_to_le32s(&header->signature);
 | 
			
		||||
    cpu_to_le32s(&header->version);
 | 
			
		||||
    cpu_to_le32s(&header->header_size);
 | 
			
		||||
    cpu_to_le32s(&header->image_type);
 | 
			
		||||
    cpu_to_le32s(&header->image_flags);
 | 
			
		||||
    cpu_to_le32s(&header->offset_bmap);
 | 
			
		||||
    cpu_to_le32s(&header->offset_data);
 | 
			
		||||
    cpu_to_le32s(&header->cylinders);
 | 
			
		||||
    cpu_to_le32s(&header->heads);
 | 
			
		||||
    cpu_to_le32s(&header->sectors);
 | 
			
		||||
    cpu_to_le32s(&header->sector_size);
 | 
			
		||||
    cpu_to_le64s(&header->disk_size);
 | 
			
		||||
    cpu_to_le32s(&header->block_size);
 | 
			
		||||
    cpu_to_le32s(&header->block_extra);
 | 
			
		||||
    cpu_to_le32s(&header->blocks_in_image);
 | 
			
		||||
    cpu_to_le32s(&header->blocks_allocated);
 | 
			
		||||
    cpu_to_le32s(&header->blocks_allocated);
 | 
			
		||||
    uuid_convert(header->uuid_image);
 | 
			
		||||
    uuid_convert(header->uuid_last_snap);
 | 
			
		||||
    uuid_convert(header->uuid_link);
 | 
			
		||||
    uuid_convert(header->uuid_parent);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#if defined(CONFIG_VDI_DEBUG)
 | 
			
		||||
static void vdi_header_print(VdiHeader *header)
 | 
			
		||||
{
 | 
			
		||||
    char uuid[37];
 | 
			
		||||
    logout("text        %s", header->text);
 | 
			
		||||
    logout("signature   0x%04x\n", header->signature);
 | 
			
		||||
    logout("header size 0x%04x\n", header->header_size);
 | 
			
		||||
    logout("image type  0x%04x\n", header->image_type);
 | 
			
		||||
    logout("image flags 0x%04x\n", header->image_flags);
 | 
			
		||||
    logout("description %s\n", header->description);
 | 
			
		||||
    logout("offset bmap 0x%04x\n", header->offset_bmap);
 | 
			
		||||
    logout("offset data 0x%04x\n", header->offset_data);
 | 
			
		||||
    logout("cylinders   0x%04x\n", header->cylinders);
 | 
			
		||||
    logout("heads       0x%04x\n", header->heads);
 | 
			
		||||
    logout("sectors     0x%04x\n", header->sectors);
 | 
			
		||||
    logout("sector size 0x%04x\n", header->sector_size);
 | 
			
		||||
    logout("image size  0x%" PRIx64 " B (%" PRIu64 " MiB)\n",
 | 
			
		||||
           header->disk_size, header->disk_size / MiB);
 | 
			
		||||
    logout("block size  0x%04x\n", header->block_size);
 | 
			
		||||
    logout("block extra 0x%04x\n", header->block_extra);
 | 
			
		||||
    logout("blocks tot. 0x%04x\n", header->blocks_in_image);
 | 
			
		||||
    logout("blocks all. 0x%04x\n", header->blocks_allocated);
 | 
			
		||||
    uuid_unparse(header->uuid_image, uuid);
 | 
			
		||||
    logout("uuid image  %s\n", uuid);
 | 
			
		||||
    uuid_unparse(header->uuid_last_snap, uuid);
 | 
			
		||||
    logout("uuid snap   %s\n", uuid);
 | 
			
		||||
    uuid_unparse(header->uuid_link, uuid);
 | 
			
		||||
    logout("uuid link   %s\n", uuid);
 | 
			
		||||
    uuid_unparse(header->uuid_parent, uuid);
 | 
			
		||||
    logout("uuid parent %s\n", uuid);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static int vdi_check(BlockDriverState *bs, BdrvCheckResult *res)
 | 
			
		||||
{
 | 
			
		||||
    /* TODO: additional checks possible. */
 | 
			
		||||
    BDRVVdiState *s = (BDRVVdiState *)bs->opaque;
 | 
			
		||||
    uint32_t blocks_allocated = 0;
 | 
			
		||||
    uint32_t block;
 | 
			
		||||
    uint32_t *bmap;
 | 
			
		||||
    logout("\n");
 | 
			
		||||
 | 
			
		||||
    bmap = qemu_malloc(s->header.blocks_in_image * sizeof(uint32_t));
 | 
			
		||||
    memset(bmap, 0xff, s->header.blocks_in_image * sizeof(uint32_t));
 | 
			
		||||
 | 
			
		||||
    /* Check block map and value of blocks_allocated. */
 | 
			
		||||
    for (block = 0; block < s->header.blocks_in_image; block++) {
 | 
			
		||||
        uint32_t bmap_entry = le32_to_cpu(s->bmap[block]);
 | 
			
		||||
        if (bmap_entry != VDI_UNALLOCATED) {
 | 
			
		||||
            if (bmap_entry < s->header.blocks_in_image) {
 | 
			
		||||
                blocks_allocated++;
 | 
			
		||||
                if (bmap[bmap_entry] == VDI_UNALLOCATED) {
 | 
			
		||||
                    bmap[bmap_entry] = bmap_entry;
 | 
			
		||||
                } else {
 | 
			
		||||
                    fprintf(stderr, "ERROR: block index %" PRIu32
 | 
			
		||||
                            " also used by %" PRIu32 "\n", bmap[bmap_entry], bmap_entry);
 | 
			
		||||
                    res->corruptions++;
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                fprintf(stderr, "ERROR: block index %" PRIu32
 | 
			
		||||
                        " too large, is %" PRIu32 "\n", block, bmap_entry);
 | 
			
		||||
                res->corruptions++;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    if (blocks_allocated != s->header.blocks_allocated) {
 | 
			
		||||
        fprintf(stderr, "ERROR: allocated blocks mismatch, is %" PRIu32
 | 
			
		||||
               ", should be %" PRIu32 "\n",
 | 
			
		||||
               blocks_allocated, s->header.blocks_allocated);
 | 
			
		||||
        res->corruptions++;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    qemu_free(bmap);
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int vdi_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
 | 
			
		||||
{
 | 
			
		||||
    /* TODO: vdi_get_info would be needed for machine snapshots.
 | 
			
		||||
       vm_state_offset is still missing. */
 | 
			
		||||
    BDRVVdiState *s = (BDRVVdiState *)bs->opaque;
 | 
			
		||||
    logout("\n");
 | 
			
		||||
    bdi->cluster_size = s->block_size;
 | 
			
		||||
    bdi->vm_state_offset = 0;
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int vdi_make_empty(BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
    /* TODO: missing code. */
 | 
			
		||||
    logout("\n");
 | 
			
		||||
    /* The return value for missing code must be 0, see block.c. */
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int vdi_probe(const uint8_t *buf, int buf_size, const char *filename)
 | 
			
		||||
{
 | 
			
		||||
    const VdiHeader *header = (const VdiHeader *)buf;
 | 
			
		||||
    int result = 0;
 | 
			
		||||
 | 
			
		||||
    logout("\n");
 | 
			
		||||
 | 
			
		||||
    if (buf_size < sizeof(*header)) {
 | 
			
		||||
        /* Header too small, no VDI. */
 | 
			
		||||
    } else if (le32_to_cpu(header->signature) == VDI_SIGNATURE) {
 | 
			
		||||
        result = 100;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (result == 0) {
 | 
			
		||||
        logout("no vdi image\n");
 | 
			
		||||
    } else {
 | 
			
		||||
        logout("%s", header->text);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int vdi_open(BlockDriverState *bs, int flags)
 | 
			
		||||
{
 | 
			
		||||
    BDRVVdiState *s = bs->opaque;
 | 
			
		||||
    VdiHeader header;
 | 
			
		||||
    size_t bmap_size;
 | 
			
		||||
 | 
			
		||||
    logout("\n");
 | 
			
		||||
 | 
			
		||||
    if (bdrv_read(bs->file, 0, (uint8_t *)&header, 1) < 0) {
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    vdi_header_to_cpu(&header);
 | 
			
		||||
#if defined(CONFIG_VDI_DEBUG)
 | 
			
		||||
    vdi_header_print(&header);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    if (header.disk_size % SECTOR_SIZE != 0) {
 | 
			
		||||
        /* 'VBoxManage convertfromraw' can create images with odd disk sizes.
 | 
			
		||||
           We accept them but round the disk size to the next multiple of
 | 
			
		||||
           SECTOR_SIZE. */
 | 
			
		||||
        logout("odd disk size %" PRIu64 " B, round up\n", header.disk_size);
 | 
			
		||||
        header.disk_size += SECTOR_SIZE - 1;
 | 
			
		||||
        header.disk_size &= ~(SECTOR_SIZE - 1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (header.version != VDI_VERSION_1_1) {
 | 
			
		||||
        logout("unsupported version %u.%u\n",
 | 
			
		||||
               header.version >> 16, header.version & 0xffff);
 | 
			
		||||
        goto fail;
 | 
			
		||||
    } else if (header.offset_bmap % SECTOR_SIZE != 0) {
 | 
			
		||||
        /* We only support block maps which start on a sector boundary. */
 | 
			
		||||
        logout("unsupported block map offset 0x%x B\n", header.offset_bmap);
 | 
			
		||||
        goto fail;
 | 
			
		||||
    } else if (header.offset_data % SECTOR_SIZE != 0) {
 | 
			
		||||
        /* We only support data blocks which start on a sector boundary. */
 | 
			
		||||
        logout("unsupported data offset 0x%x B\n", header.offset_data);
 | 
			
		||||
        goto fail;
 | 
			
		||||
    } else if (header.sector_size != SECTOR_SIZE) {
 | 
			
		||||
        logout("unsupported sector size %u B\n", header.sector_size);
 | 
			
		||||
        goto fail;
 | 
			
		||||
    } else if (header.block_size != 1 * MiB) {
 | 
			
		||||
        logout("unsupported block size %u B\n", header.block_size);
 | 
			
		||||
        goto fail;
 | 
			
		||||
    } else if (header.disk_size >
 | 
			
		||||
               (uint64_t)header.blocks_in_image * header.block_size) {
 | 
			
		||||
        logout("unsupported disk size %" PRIu64 " B\n", header.disk_size);
 | 
			
		||||
        goto fail;
 | 
			
		||||
    } else if (!uuid_is_null(header.uuid_link)) {
 | 
			
		||||
        logout("link uuid != 0, unsupported\n");
 | 
			
		||||
        goto fail;
 | 
			
		||||
    } else if (!uuid_is_null(header.uuid_parent)) {
 | 
			
		||||
        logout("parent uuid != 0, unsupported\n");
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bs->total_sectors = header.disk_size / SECTOR_SIZE;
 | 
			
		||||
 | 
			
		||||
    s->block_size = header.block_size;
 | 
			
		||||
    s->block_sectors = header.block_size / SECTOR_SIZE;
 | 
			
		||||
    s->bmap_sector = header.offset_bmap / SECTOR_SIZE;
 | 
			
		||||
    s->header = header;
 | 
			
		||||
 | 
			
		||||
    bmap_size = header.blocks_in_image * sizeof(uint32_t);
 | 
			
		||||
    bmap_size = (bmap_size + SECTOR_SIZE - 1) / SECTOR_SIZE;
 | 
			
		||||
    if (bmap_size > 0) {
 | 
			
		||||
        s->bmap = qemu_malloc(bmap_size * SECTOR_SIZE);
 | 
			
		||||
    }
 | 
			
		||||
    if (bdrv_read(bs->file, s->bmap_sector, (uint8_t *)s->bmap, bmap_size) < 0) {
 | 
			
		||||
        goto fail_free_bmap;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
 | 
			
		||||
 fail_free_bmap:
 | 
			
		||||
    qemu_free(s->bmap);
 | 
			
		||||
 | 
			
		||||
 fail:
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int vdi_is_allocated(BlockDriverState *bs, int64_t sector_num,
 | 
			
		||||
                             int nb_sectors, int *pnum)
 | 
			
		||||
{
 | 
			
		||||
    /* TODO: Check for too large sector_num (in bdrv_is_allocated or here). */
 | 
			
		||||
    BDRVVdiState *s = (BDRVVdiState *)bs->opaque;
 | 
			
		||||
    size_t bmap_index = sector_num / s->block_sectors;
 | 
			
		||||
    size_t sector_in_block = sector_num % s->block_sectors;
 | 
			
		||||
    int n_sectors = s->block_sectors - sector_in_block;
 | 
			
		||||
    uint32_t bmap_entry = le32_to_cpu(s->bmap[bmap_index]);
 | 
			
		||||
    logout("%p, %" PRId64 ", %d, %p\n", bs, sector_num, nb_sectors, pnum);
 | 
			
		||||
    if (n_sectors > nb_sectors) {
 | 
			
		||||
        n_sectors = nb_sectors;
 | 
			
		||||
    }
 | 
			
		||||
    *pnum = n_sectors;
 | 
			
		||||
    return bmap_entry != VDI_UNALLOCATED;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void vdi_aio_cancel(BlockDriverAIOCB *blockacb)
 | 
			
		||||
{
 | 
			
		||||
    /* TODO: This code is untested. How can I get it executed? */
 | 
			
		||||
    VdiAIOCB *acb = container_of(blockacb, VdiAIOCB, common);
 | 
			
		||||
    logout("\n");
 | 
			
		||||
    if (acb->hd_aiocb) {
 | 
			
		||||
        bdrv_aio_cancel(acb->hd_aiocb);
 | 
			
		||||
    }
 | 
			
		||||
    qemu_aio_release(acb);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static AIOPool vdi_aio_pool = {
 | 
			
		||||
    .aiocb_size = sizeof(VdiAIOCB),
 | 
			
		||||
    .cancel = vdi_aio_cancel,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static VdiAIOCB *vdi_aio_setup(BlockDriverState *bs, int64_t sector_num,
 | 
			
		||||
        QEMUIOVector *qiov, int nb_sectors,
 | 
			
		||||
        BlockDriverCompletionFunc *cb, void *opaque, int is_write)
 | 
			
		||||
{
 | 
			
		||||
    VdiAIOCB *acb;
 | 
			
		||||
 | 
			
		||||
    logout("%p, %" PRId64 ", %p, %d, %p, %p, %d\n",
 | 
			
		||||
           bs, sector_num, qiov, nb_sectors, cb, opaque, is_write);
 | 
			
		||||
 | 
			
		||||
    acb = qemu_aio_get(&vdi_aio_pool, bs, cb, opaque);
 | 
			
		||||
    if (acb) {
 | 
			
		||||
        acb->hd_aiocb = NULL;
 | 
			
		||||
        acb->sector_num = sector_num;
 | 
			
		||||
        acb->qiov = qiov;
 | 
			
		||||
        if (qiov->niov > 1) {
 | 
			
		||||
            acb->buf = qemu_blockalign(bs, qiov->size);
 | 
			
		||||
            acb->orig_buf = acb->buf;
 | 
			
		||||
            if (is_write) {
 | 
			
		||||
                qemu_iovec_to_buffer(qiov, acb->buf);
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            acb->buf = (uint8_t *)qiov->iov->iov_base;
 | 
			
		||||
        }
 | 
			
		||||
        acb->nb_sectors = nb_sectors;
 | 
			
		||||
        acb->n_sectors = 0;
 | 
			
		||||
        acb->bmap_first = VDI_UNALLOCATED;
 | 
			
		||||
        acb->bmap_last = VDI_UNALLOCATED;
 | 
			
		||||
        acb->block_buffer = NULL;
 | 
			
		||||
        acb->header_modified = 0;
 | 
			
		||||
    }
 | 
			
		||||
    return acb;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int vdi_schedule_bh(QEMUBHFunc *cb, VdiAIOCB *acb)
 | 
			
		||||
{
 | 
			
		||||
    logout("\n");
 | 
			
		||||
 | 
			
		||||
    if (acb->bh) {
 | 
			
		||||
        return -EIO;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    acb->bh = qemu_bh_new(cb, acb);
 | 
			
		||||
    if (!acb->bh) {
 | 
			
		||||
        return -EIO;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    qemu_bh_schedule(acb->bh);
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void vdi_aio_read_cb(void *opaque, int ret);
 | 
			
		||||
 | 
			
		||||
static void vdi_aio_read_bh(void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    VdiAIOCB *acb = opaque;
 | 
			
		||||
    logout("\n");
 | 
			
		||||
    qemu_bh_delete(acb->bh);
 | 
			
		||||
    acb->bh = NULL;
 | 
			
		||||
    vdi_aio_read_cb(opaque, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void vdi_aio_read_cb(void *opaque, int ret)
 | 
			
		||||
{
 | 
			
		||||
    VdiAIOCB *acb = opaque;
 | 
			
		||||
    BlockDriverState *bs = acb->common.bs;
 | 
			
		||||
    BDRVVdiState *s = bs->opaque;
 | 
			
		||||
    uint32_t bmap_entry;
 | 
			
		||||
    uint32_t block_index;
 | 
			
		||||
    uint32_t sector_in_block;
 | 
			
		||||
    uint32_t n_sectors;
 | 
			
		||||
 | 
			
		||||
    logout("%u sectors read\n", acb->n_sectors);
 | 
			
		||||
 | 
			
		||||
    acb->hd_aiocb = NULL;
 | 
			
		||||
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        goto done;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    acb->nb_sectors -= acb->n_sectors;
 | 
			
		||||
 | 
			
		||||
    if (acb->nb_sectors == 0) {
 | 
			
		||||
        /* request completed */
 | 
			
		||||
        ret = 0;
 | 
			
		||||
        goto done;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    acb->sector_num += acb->n_sectors;
 | 
			
		||||
    acb->buf += acb->n_sectors * SECTOR_SIZE;
 | 
			
		||||
 | 
			
		||||
    block_index = acb->sector_num / s->block_sectors;
 | 
			
		||||
    sector_in_block = acb->sector_num % s->block_sectors;
 | 
			
		||||
    n_sectors = s->block_sectors - sector_in_block;
 | 
			
		||||
    if (n_sectors > acb->nb_sectors) {
 | 
			
		||||
        n_sectors = acb->nb_sectors;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    logout("will read %u sectors starting at sector %" PRIu64 "\n",
 | 
			
		||||
           n_sectors, acb->sector_num);
 | 
			
		||||
 | 
			
		||||
    /* prepare next AIO request */
 | 
			
		||||
    acb->n_sectors = n_sectors;
 | 
			
		||||
    bmap_entry = le32_to_cpu(s->bmap[block_index]);
 | 
			
		||||
    if (bmap_entry == VDI_UNALLOCATED) {
 | 
			
		||||
        /* Block not allocated, return zeros, no need to wait. */
 | 
			
		||||
        memset(acb->buf, 0, n_sectors * SECTOR_SIZE);
 | 
			
		||||
        ret = vdi_schedule_bh(vdi_aio_read_bh, acb);
 | 
			
		||||
        if (ret < 0) {
 | 
			
		||||
            goto done;
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        uint64_t offset = s->header.offset_data / SECTOR_SIZE +
 | 
			
		||||
                          (uint64_t)bmap_entry * s->block_sectors +
 | 
			
		||||
                          sector_in_block;
 | 
			
		||||
        acb->hd_iov.iov_base = (void *)acb->buf;
 | 
			
		||||
        acb->hd_iov.iov_len = n_sectors * SECTOR_SIZE;
 | 
			
		||||
        qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1);
 | 
			
		||||
        acb->hd_aiocb = bdrv_aio_readv(bs->file, offset, &acb->hd_qiov,
 | 
			
		||||
                                       n_sectors, vdi_aio_read_cb, acb);
 | 
			
		||||
        if (acb->hd_aiocb == NULL) {
 | 
			
		||||
            goto done;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return;
 | 
			
		||||
done:
 | 
			
		||||
    if (acb->qiov->niov > 1) {
 | 
			
		||||
        qemu_iovec_from_buffer(acb->qiov, acb->orig_buf, acb->qiov->size);
 | 
			
		||||
        qemu_vfree(acb->orig_buf);
 | 
			
		||||
    }
 | 
			
		||||
    acb->common.cb(acb->common.opaque, ret);
 | 
			
		||||
    qemu_aio_release(acb);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static BlockDriverAIOCB *vdi_aio_readv(BlockDriverState *bs,
 | 
			
		||||
        int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
 | 
			
		||||
        BlockDriverCompletionFunc *cb, void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    VdiAIOCB *acb;
 | 
			
		||||
    logout("\n");
 | 
			
		||||
    acb = vdi_aio_setup(bs, sector_num, qiov, nb_sectors, cb, opaque, 0);
 | 
			
		||||
    if (!acb) {
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
    vdi_aio_read_cb(acb, 0);
 | 
			
		||||
    return &acb->common;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void vdi_aio_write_cb(void *opaque, int ret)
 | 
			
		||||
{
 | 
			
		||||
    VdiAIOCB *acb = opaque;
 | 
			
		||||
    BlockDriverState *bs = acb->common.bs;
 | 
			
		||||
    BDRVVdiState *s = bs->opaque;
 | 
			
		||||
    uint32_t bmap_entry;
 | 
			
		||||
    uint32_t block_index;
 | 
			
		||||
    uint32_t sector_in_block;
 | 
			
		||||
    uint32_t n_sectors;
 | 
			
		||||
 | 
			
		||||
    acb->hd_aiocb = NULL;
 | 
			
		||||
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        goto done;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    acb->nb_sectors -= acb->n_sectors;
 | 
			
		||||
    acb->sector_num += acb->n_sectors;
 | 
			
		||||
    acb->buf += acb->n_sectors * SECTOR_SIZE;
 | 
			
		||||
 | 
			
		||||
    if (acb->nb_sectors == 0) {
 | 
			
		||||
        logout("finished data write\n");
 | 
			
		||||
        acb->n_sectors = 0;
 | 
			
		||||
        if (acb->header_modified) {
 | 
			
		||||
            VdiHeader *header = acb->block_buffer;
 | 
			
		||||
            logout("now writing modified header\n");
 | 
			
		||||
            assert(acb->bmap_first != VDI_UNALLOCATED);
 | 
			
		||||
            *header = s->header;
 | 
			
		||||
            vdi_header_to_le(header);
 | 
			
		||||
            acb->header_modified = 0;
 | 
			
		||||
            acb->hd_iov.iov_base = acb->block_buffer;
 | 
			
		||||
            acb->hd_iov.iov_len = SECTOR_SIZE;
 | 
			
		||||
            qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1);
 | 
			
		||||
            acb->hd_aiocb = bdrv_aio_writev(bs->file, 0, &acb->hd_qiov, 1,
 | 
			
		||||
                                            vdi_aio_write_cb, acb);
 | 
			
		||||
            if (acb->hd_aiocb == NULL) {
 | 
			
		||||
                goto done;
 | 
			
		||||
            }
 | 
			
		||||
            return;
 | 
			
		||||
        } else if (acb->bmap_first != VDI_UNALLOCATED) {
 | 
			
		||||
            /* One or more new blocks were allocated. */
 | 
			
		||||
            uint64_t offset;
 | 
			
		||||
            uint32_t bmap_first;
 | 
			
		||||
            uint32_t bmap_last;
 | 
			
		||||
            qemu_free(acb->block_buffer);
 | 
			
		||||
            acb->block_buffer = NULL;
 | 
			
		||||
            bmap_first = acb->bmap_first;
 | 
			
		||||
            bmap_last = acb->bmap_last;
 | 
			
		||||
            logout("now writing modified block map entry %u...%u\n",
 | 
			
		||||
                   bmap_first, bmap_last);
 | 
			
		||||
            /* Write modified sectors from block map. */
 | 
			
		||||
            bmap_first /= (SECTOR_SIZE / sizeof(uint32_t));
 | 
			
		||||
            bmap_last /= (SECTOR_SIZE / sizeof(uint32_t));
 | 
			
		||||
            n_sectors = bmap_last - bmap_first + 1;
 | 
			
		||||
            offset = s->bmap_sector + bmap_first;
 | 
			
		||||
            acb->bmap_first = VDI_UNALLOCATED;
 | 
			
		||||
            acb->hd_iov.iov_base = (void *)((uint8_t *)&s->bmap[0] +
 | 
			
		||||
                                            bmap_first * SECTOR_SIZE);
 | 
			
		||||
            acb->hd_iov.iov_len = n_sectors * SECTOR_SIZE;
 | 
			
		||||
            qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1);
 | 
			
		||||
            logout("will write %u block map sectors starting from entry %u\n",
 | 
			
		||||
                   n_sectors, bmap_first);
 | 
			
		||||
            acb->hd_aiocb = bdrv_aio_writev(bs->file, offset, &acb->hd_qiov,
 | 
			
		||||
                                            n_sectors, vdi_aio_write_cb, acb);
 | 
			
		||||
            if (acb->hd_aiocb == NULL) {
 | 
			
		||||
                goto done;
 | 
			
		||||
            }
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        ret = 0;
 | 
			
		||||
        goto done;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    logout("%u sectors written\n", acb->n_sectors);
 | 
			
		||||
 | 
			
		||||
    block_index = acb->sector_num / s->block_sectors;
 | 
			
		||||
    sector_in_block = acb->sector_num % s->block_sectors;
 | 
			
		||||
    n_sectors = s->block_sectors - sector_in_block;
 | 
			
		||||
    if (n_sectors > acb->nb_sectors) {
 | 
			
		||||
        n_sectors = acb->nb_sectors;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    logout("will write %u sectors starting at sector %" PRIu64 "\n",
 | 
			
		||||
           n_sectors, acb->sector_num);
 | 
			
		||||
 | 
			
		||||
    /* prepare next AIO request */
 | 
			
		||||
    acb->n_sectors = n_sectors;
 | 
			
		||||
    bmap_entry = le32_to_cpu(s->bmap[block_index]);
 | 
			
		||||
    if (bmap_entry == VDI_UNALLOCATED) {
 | 
			
		||||
        /* Allocate new block and write to it. */
 | 
			
		||||
        uint64_t offset;
 | 
			
		||||
        uint8_t *block;
 | 
			
		||||
        bmap_entry = s->header.blocks_allocated;
 | 
			
		||||
        s->bmap[block_index] = cpu_to_le32(bmap_entry);
 | 
			
		||||
        s->header.blocks_allocated++;
 | 
			
		||||
        offset = s->header.offset_data / SECTOR_SIZE +
 | 
			
		||||
                 (uint64_t)bmap_entry * s->block_sectors;
 | 
			
		||||
        block = acb->block_buffer;
 | 
			
		||||
        if (block == NULL) {
 | 
			
		||||
            block = qemu_mallocz(s->block_size);
 | 
			
		||||
            acb->block_buffer = block;
 | 
			
		||||
            acb->bmap_first = block_index;
 | 
			
		||||
            assert(!acb->header_modified);
 | 
			
		||||
            acb->header_modified = 1;
 | 
			
		||||
        }
 | 
			
		||||
        acb->bmap_last = block_index;
 | 
			
		||||
        memcpy(block + sector_in_block * SECTOR_SIZE,
 | 
			
		||||
               acb->buf, n_sectors * SECTOR_SIZE);
 | 
			
		||||
        acb->hd_iov.iov_base = (void *)block;
 | 
			
		||||
        acb->hd_iov.iov_len = s->block_size;
 | 
			
		||||
        qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1);
 | 
			
		||||
        acb->hd_aiocb = bdrv_aio_writev(bs->file, offset,
 | 
			
		||||
                                        &acb->hd_qiov, s->block_sectors,
 | 
			
		||||
                                        vdi_aio_write_cb, acb);
 | 
			
		||||
        if (acb->hd_aiocb == NULL) {
 | 
			
		||||
            goto done;
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        uint64_t offset = s->header.offset_data / SECTOR_SIZE +
 | 
			
		||||
                          (uint64_t)bmap_entry * s->block_sectors +
 | 
			
		||||
                          sector_in_block;
 | 
			
		||||
        acb->hd_iov.iov_base = (void *)acb->buf;
 | 
			
		||||
        acb->hd_iov.iov_len = n_sectors * SECTOR_SIZE;
 | 
			
		||||
        qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1);
 | 
			
		||||
        acb->hd_aiocb = bdrv_aio_writev(bs->file, offset, &acb->hd_qiov,
 | 
			
		||||
                                        n_sectors, vdi_aio_write_cb, acb);
 | 
			
		||||
        if (acb->hd_aiocb == NULL) {
 | 
			
		||||
            goto done;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
done:
 | 
			
		||||
    if (acb->qiov->niov > 1) {
 | 
			
		||||
        qemu_vfree(acb->orig_buf);
 | 
			
		||||
    }
 | 
			
		||||
    acb->common.cb(acb->common.opaque, ret);
 | 
			
		||||
    qemu_aio_release(acb);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static BlockDriverAIOCB *vdi_aio_writev(BlockDriverState *bs,
 | 
			
		||||
        int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
 | 
			
		||||
        BlockDriverCompletionFunc *cb, void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    VdiAIOCB *acb;
 | 
			
		||||
    logout("\n");
 | 
			
		||||
    acb = vdi_aio_setup(bs, sector_num, qiov, nb_sectors, cb, opaque, 1);
 | 
			
		||||
    if (!acb) {
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
    vdi_aio_write_cb(acb, 0);
 | 
			
		||||
    return &acb->common;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int vdi_create(const char *filename, QEMUOptionParameter *options)
 | 
			
		||||
{
 | 
			
		||||
    int fd;
 | 
			
		||||
    int result = 0;
 | 
			
		||||
    uint64_t bytes = 0;
 | 
			
		||||
    uint32_t blocks;
 | 
			
		||||
    size_t block_size = 1 * MiB;
 | 
			
		||||
    uint32_t image_type = VDI_TYPE_DYNAMIC;
 | 
			
		||||
    VdiHeader header;
 | 
			
		||||
    size_t i;
 | 
			
		||||
    size_t bmap_size;
 | 
			
		||||
    uint32_t *bmap;
 | 
			
		||||
 | 
			
		||||
    logout("\n");
 | 
			
		||||
 | 
			
		||||
    /* Read out options. */
 | 
			
		||||
    while (options && options->name) {
 | 
			
		||||
        if (!strcmp(options->name, BLOCK_OPT_SIZE)) {
 | 
			
		||||
            bytes = options->value.n;
 | 
			
		||||
#if defined(CONFIG_VDI_BLOCK_SIZE)
 | 
			
		||||
        } else if (!strcmp(options->name, BLOCK_OPT_CLUSTER_SIZE)) {
 | 
			
		||||
            if (options->value.n) {
 | 
			
		||||
                /* TODO: Additional checks (SECTOR_SIZE * 2^n, ...). */
 | 
			
		||||
                block_size = options->value.n;
 | 
			
		||||
            }
 | 
			
		||||
#endif
 | 
			
		||||
#if defined(CONFIG_VDI_STATIC_IMAGE)
 | 
			
		||||
        } else if (!strcmp(options->name, BLOCK_OPT_STATIC)) {
 | 
			
		||||
            if (options->value.n) {
 | 
			
		||||
                image_type = VDI_TYPE_STATIC;
 | 
			
		||||
            }
 | 
			
		||||
#endif
 | 
			
		||||
        }
 | 
			
		||||
        options++;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE,
 | 
			
		||||
              0644);
 | 
			
		||||
    if (fd < 0) {
 | 
			
		||||
        return -errno;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* We need enough blocks to store the given disk size,
 | 
			
		||||
       so always round up. */
 | 
			
		||||
    blocks = (bytes + block_size - 1) / block_size;
 | 
			
		||||
 | 
			
		||||
    bmap_size = blocks * sizeof(uint32_t);
 | 
			
		||||
    bmap_size = ((bmap_size + SECTOR_SIZE - 1) & ~(SECTOR_SIZE -1));
 | 
			
		||||
 | 
			
		||||
    memset(&header, 0, sizeof(header));
 | 
			
		||||
    pstrcpy(header.text, sizeof(header.text), VDI_TEXT);
 | 
			
		||||
    header.signature = VDI_SIGNATURE;
 | 
			
		||||
    header.version = VDI_VERSION_1_1;
 | 
			
		||||
    header.header_size = 0x180;
 | 
			
		||||
    header.image_type = image_type;
 | 
			
		||||
    header.offset_bmap = 0x200;
 | 
			
		||||
    header.offset_data = 0x200 + bmap_size;
 | 
			
		||||
    header.sector_size = SECTOR_SIZE;
 | 
			
		||||
    header.disk_size = bytes;
 | 
			
		||||
    header.block_size = block_size;
 | 
			
		||||
    header.blocks_in_image = blocks;
 | 
			
		||||
    if (image_type == VDI_TYPE_STATIC) {
 | 
			
		||||
        header.blocks_allocated = blocks;
 | 
			
		||||
    }
 | 
			
		||||
    uuid_generate(header.uuid_image);
 | 
			
		||||
    uuid_generate(header.uuid_last_snap);
 | 
			
		||||
    /* There is no need to set header.uuid_link or header.uuid_parent here. */
 | 
			
		||||
#if defined(CONFIG_VDI_DEBUG)
 | 
			
		||||
    vdi_header_print(&header);
 | 
			
		||||
#endif
 | 
			
		||||
    vdi_header_to_le(&header);
 | 
			
		||||
    if (write(fd, &header, sizeof(header)) < 0) {
 | 
			
		||||
        result = -errno;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bmap = NULL;
 | 
			
		||||
    if (bmap_size > 0) {
 | 
			
		||||
        bmap = (uint32_t *)qemu_mallocz(bmap_size);
 | 
			
		||||
    }
 | 
			
		||||
    for (i = 0; i < blocks; i++) {
 | 
			
		||||
        if (image_type == VDI_TYPE_STATIC) {
 | 
			
		||||
            bmap[i] = i;
 | 
			
		||||
        } else {
 | 
			
		||||
            bmap[i] = VDI_UNALLOCATED;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    if (write(fd, bmap, bmap_size) < 0) {
 | 
			
		||||
        result = -errno;
 | 
			
		||||
    }
 | 
			
		||||
    qemu_free(bmap);
 | 
			
		||||
    if (image_type == VDI_TYPE_STATIC) {
 | 
			
		||||
        if (ftruncate(fd, sizeof(header) + bmap_size + blocks * block_size)) {
 | 
			
		||||
            result = -errno;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (close(fd) < 0) {
 | 
			
		||||
        result = -errno;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void vdi_close(BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void vdi_flush(BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
    logout("\n");
 | 
			
		||||
    bdrv_flush(bs->file);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static QEMUOptionParameter vdi_create_options[] = {
 | 
			
		||||
    {
 | 
			
		||||
        .name = BLOCK_OPT_SIZE,
 | 
			
		||||
        .type = OPT_SIZE,
 | 
			
		||||
        .help = "Virtual disk size"
 | 
			
		||||
    },
 | 
			
		||||
#if defined(CONFIG_VDI_BLOCK_SIZE)
 | 
			
		||||
    {
 | 
			
		||||
        .name = BLOCK_OPT_CLUSTER_SIZE,
 | 
			
		||||
        .type = OPT_SIZE,
 | 
			
		||||
        .help = "VDI cluster (block) size"
 | 
			
		||||
    },
 | 
			
		||||
#endif
 | 
			
		||||
#if defined(CONFIG_VDI_STATIC_IMAGE)
 | 
			
		||||
    {
 | 
			
		||||
        .name = BLOCK_OPT_STATIC,
 | 
			
		||||
        .type = OPT_FLAG,
 | 
			
		||||
        .help = "VDI static (pre-allocated) image"
 | 
			
		||||
    },
 | 
			
		||||
#endif
 | 
			
		||||
    /* TODO: An additional option to set UUID values might be useful. */
 | 
			
		||||
    { NULL }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static BlockDriver bdrv_vdi = {
 | 
			
		||||
    .format_name = "vdi",
 | 
			
		||||
    .instance_size = sizeof(BDRVVdiState),
 | 
			
		||||
    .bdrv_probe = vdi_probe,
 | 
			
		||||
    .bdrv_open = vdi_open,
 | 
			
		||||
    .bdrv_close = vdi_close,
 | 
			
		||||
    .bdrv_create = vdi_create,
 | 
			
		||||
    .bdrv_flush = vdi_flush,
 | 
			
		||||
    .bdrv_is_allocated = vdi_is_allocated,
 | 
			
		||||
    .bdrv_make_empty = vdi_make_empty,
 | 
			
		||||
 | 
			
		||||
    .bdrv_aio_readv = vdi_aio_readv,
 | 
			
		||||
#if defined(CONFIG_VDI_WRITE)
 | 
			
		||||
    .bdrv_aio_writev = vdi_aio_writev,
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    .bdrv_get_info = vdi_get_info,
 | 
			
		||||
 | 
			
		||||
    .create_options = vdi_create_options,
 | 
			
		||||
    .bdrv_check = vdi_check,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void bdrv_vdi_init(void)
 | 
			
		||||
{
 | 
			
		||||
    logout("\n");
 | 
			
		||||
    bdrv_register(&bdrv_vdi);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
block_init(bdrv_vdi_init);
 | 
			
		||||
							
								
								
									
										871
									
								
								block/vmdk.c
									
									
									
									
									
								
							
							
						
						
									
										871
									
								
								block/vmdk.c
									
									
									
									
									
								
							@@ -1,871 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Block driver for the VMDK format
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2004 Fabrice Bellard
 | 
			
		||||
 * Copyright (c) 2005 Filip Navara
 | 
			
		||||
 *
 | 
			
		||||
 * 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-common.h"
 | 
			
		||||
#include "block_int.h"
 | 
			
		||||
#include "module.h"
 | 
			
		||||
 | 
			
		||||
#define VMDK3_MAGIC (('C' << 24) | ('O' << 16) | ('W' << 8) | 'D')
 | 
			
		||||
#define VMDK4_MAGIC (('K' << 24) | ('D' << 16) | ('M' << 8) | 'V')
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    uint32_t version;
 | 
			
		||||
    uint32_t flags;
 | 
			
		||||
    uint32_t disk_sectors;
 | 
			
		||||
    uint32_t granularity;
 | 
			
		||||
    uint32_t l1dir_offset;
 | 
			
		||||
    uint32_t l1dir_size;
 | 
			
		||||
    uint32_t file_sectors;
 | 
			
		||||
    uint32_t cylinders;
 | 
			
		||||
    uint32_t heads;
 | 
			
		||||
    uint32_t sectors_per_track;
 | 
			
		||||
} VMDK3Header;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    uint32_t version;
 | 
			
		||||
    uint32_t flags;
 | 
			
		||||
    int64_t capacity;
 | 
			
		||||
    int64_t granularity;
 | 
			
		||||
    int64_t desc_offset;
 | 
			
		||||
    int64_t desc_size;
 | 
			
		||||
    int32_t num_gtes_per_gte;
 | 
			
		||||
    int64_t rgd_offset;
 | 
			
		||||
    int64_t gd_offset;
 | 
			
		||||
    int64_t grain_offset;
 | 
			
		||||
    char filler[1];
 | 
			
		||||
    char check_bytes[4];
 | 
			
		||||
} __attribute__((packed)) VMDK4Header;
 | 
			
		||||
 | 
			
		||||
#define L2_CACHE_SIZE 16
 | 
			
		||||
 | 
			
		||||
typedef struct BDRVVmdkState {
 | 
			
		||||
    BlockDriverState *hd;
 | 
			
		||||
    int64_t l1_table_offset;
 | 
			
		||||
    int64_t l1_backup_table_offset;
 | 
			
		||||
    uint32_t *l1_table;
 | 
			
		||||
    uint32_t *l1_backup_table;
 | 
			
		||||
    unsigned int l1_size;
 | 
			
		||||
    uint32_t l1_entry_sectors;
 | 
			
		||||
 | 
			
		||||
    unsigned int l2_size;
 | 
			
		||||
    uint32_t *l2_cache;
 | 
			
		||||
    uint32_t l2_cache_offsets[L2_CACHE_SIZE];
 | 
			
		||||
    uint32_t l2_cache_counts[L2_CACHE_SIZE];
 | 
			
		||||
 | 
			
		||||
    unsigned int cluster_sectors;
 | 
			
		||||
    uint32_t parent_cid;
 | 
			
		||||
} BDRVVmdkState;
 | 
			
		||||
 | 
			
		||||
typedef struct VmdkMetaData {
 | 
			
		||||
    uint32_t offset;
 | 
			
		||||
    unsigned int l1_index;
 | 
			
		||||
    unsigned int l2_index;
 | 
			
		||||
    unsigned int l2_offset;
 | 
			
		||||
    int valid;
 | 
			
		||||
} VmdkMetaData;
 | 
			
		||||
 | 
			
		||||
static int vmdk_probe(const uint8_t *buf, int buf_size, const char *filename)
 | 
			
		||||
{
 | 
			
		||||
    uint32_t magic;
 | 
			
		||||
 | 
			
		||||
    if (buf_size < 4)
 | 
			
		||||
        return 0;
 | 
			
		||||
    magic = be32_to_cpu(*(uint32_t *)buf);
 | 
			
		||||
    if (magic == VMDK3_MAGIC ||
 | 
			
		||||
        magic == VMDK4_MAGIC)
 | 
			
		||||
        return 100;
 | 
			
		||||
    else
 | 
			
		||||
        return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define CHECK_CID 1
 | 
			
		||||
 | 
			
		||||
#define SECTOR_SIZE 512
 | 
			
		||||
#define DESC_SIZE 20*SECTOR_SIZE	// 20 sectors of 512 bytes each
 | 
			
		||||
#define HEADER_SIZE 512   			// first sector of 512 bytes
 | 
			
		||||
 | 
			
		||||
static uint32_t vmdk_read_cid(BlockDriverState *bs, int parent)
 | 
			
		||||
{
 | 
			
		||||
    char desc[DESC_SIZE];
 | 
			
		||||
    uint32_t cid;
 | 
			
		||||
    const char *p_name, *cid_str;
 | 
			
		||||
    size_t cid_str_size;
 | 
			
		||||
 | 
			
		||||
    /* the descriptor offset = 0x200 */
 | 
			
		||||
    if (bdrv_pread(bs->file, 0x200, desc, DESC_SIZE) != DESC_SIZE)
 | 
			
		||||
        return 0;
 | 
			
		||||
 | 
			
		||||
    if (parent) {
 | 
			
		||||
        cid_str = "parentCID";
 | 
			
		||||
        cid_str_size = sizeof("parentCID");
 | 
			
		||||
    } else {
 | 
			
		||||
        cid_str = "CID";
 | 
			
		||||
        cid_str_size = sizeof("CID");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ((p_name = strstr(desc,cid_str)) != NULL) {
 | 
			
		||||
        p_name += cid_str_size;
 | 
			
		||||
        sscanf(p_name,"%x",&cid);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return cid;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int vmdk_write_cid(BlockDriverState *bs, uint32_t cid)
 | 
			
		||||
{
 | 
			
		||||
    char desc[DESC_SIZE], tmp_desc[DESC_SIZE];
 | 
			
		||||
    char *p_name, *tmp_str;
 | 
			
		||||
 | 
			
		||||
    /* the descriptor offset = 0x200 */
 | 
			
		||||
    if (bdrv_pread(bs->file, 0x200, desc, DESC_SIZE) != DESC_SIZE)
 | 
			
		||||
        return -1;
 | 
			
		||||
 | 
			
		||||
    tmp_str = strstr(desc,"parentCID");
 | 
			
		||||
    pstrcpy(tmp_desc, sizeof(tmp_desc), tmp_str);
 | 
			
		||||
    if ((p_name = strstr(desc,"CID")) != NULL) {
 | 
			
		||||
        p_name += sizeof("CID");
 | 
			
		||||
        snprintf(p_name, sizeof(desc) - (p_name - desc), "%x\n", cid);
 | 
			
		||||
        pstrcat(desc, sizeof(desc), tmp_desc);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (bdrv_pwrite_sync(bs->file, 0x200, desc, DESC_SIZE) < 0)
 | 
			
		||||
        return -1;
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int vmdk_is_cid_valid(BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
#ifdef CHECK_CID
 | 
			
		||||
    BDRVVmdkState *s = bs->opaque;
 | 
			
		||||
    BlockDriverState *p_bs = bs->backing_hd;
 | 
			
		||||
    uint32_t cur_pcid;
 | 
			
		||||
 | 
			
		||||
    if (p_bs) {
 | 
			
		||||
        cur_pcid = vmdk_read_cid(p_bs,0);
 | 
			
		||||
        if (s->parent_cid != cur_pcid)
 | 
			
		||||
            // CID not valid
 | 
			
		||||
            return 0;
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
    // CID valid
 | 
			
		||||
    return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int vmdk_snapshot_create(const char *filename, const char *backing_file)
 | 
			
		||||
{
 | 
			
		||||
    int snp_fd, p_fd;
 | 
			
		||||
    int ret;
 | 
			
		||||
    uint32_t p_cid;
 | 
			
		||||
    char *p_name, *gd_buf, *rgd_buf;
 | 
			
		||||
    const char *real_filename, *temp_str;
 | 
			
		||||
    VMDK4Header header;
 | 
			
		||||
    uint32_t gde_entries, gd_size;
 | 
			
		||||
    int64_t gd_offset, rgd_offset, capacity, gt_size;
 | 
			
		||||
    char p_desc[DESC_SIZE], s_desc[DESC_SIZE], hdr[HEADER_SIZE];
 | 
			
		||||
    static const char desc_template[] =
 | 
			
		||||
    "# Disk DescriptorFile\n"
 | 
			
		||||
    "version=1\n"
 | 
			
		||||
    "CID=%x\n"
 | 
			
		||||
    "parentCID=%x\n"
 | 
			
		||||
    "createType=\"monolithicSparse\"\n"
 | 
			
		||||
    "parentFileNameHint=\"%s\"\n"
 | 
			
		||||
    "\n"
 | 
			
		||||
    "# Extent description\n"
 | 
			
		||||
    "RW %u SPARSE \"%s\"\n"
 | 
			
		||||
    "\n"
 | 
			
		||||
    "# The Disk Data Base \n"
 | 
			
		||||
    "#DDB\n"
 | 
			
		||||
    "\n";
 | 
			
		||||
 | 
			
		||||
    snp_fd = open(filename, O_RDWR | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE, 0644);
 | 
			
		||||
    if (snp_fd < 0)
 | 
			
		||||
        return -errno;
 | 
			
		||||
    p_fd = open(backing_file, O_RDONLY | O_BINARY | O_LARGEFILE);
 | 
			
		||||
    if (p_fd < 0) {
 | 
			
		||||
        close(snp_fd);
 | 
			
		||||
        return -errno;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* read the header */
 | 
			
		||||
    if (lseek(p_fd, 0x0, SEEK_SET) == -1) {
 | 
			
		||||
        ret = -errno;
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
    if (read(p_fd, hdr, HEADER_SIZE) != HEADER_SIZE) {
 | 
			
		||||
        ret = -errno;
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* write the header */
 | 
			
		||||
    if (lseek(snp_fd, 0x0, SEEK_SET) == -1) {
 | 
			
		||||
        ret = -errno;
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
    if (write(snp_fd, hdr, HEADER_SIZE) == -1) {
 | 
			
		||||
        ret = -errno;
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    memset(&header, 0, sizeof(header));
 | 
			
		||||
    memcpy(&header,&hdr[4], sizeof(header)); // skip the VMDK4_MAGIC
 | 
			
		||||
 | 
			
		||||
    if (ftruncate(snp_fd, header.grain_offset << 9)) {
 | 
			
		||||
        ret = -errno;
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
    /* the descriptor offset = 0x200 */
 | 
			
		||||
    if (lseek(p_fd, 0x200, SEEK_SET) == -1) {
 | 
			
		||||
        ret = -errno;
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
    if (read(p_fd, p_desc, DESC_SIZE) != DESC_SIZE) {
 | 
			
		||||
        ret = -errno;
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ((p_name = strstr(p_desc,"CID")) != NULL) {
 | 
			
		||||
        p_name += sizeof("CID");
 | 
			
		||||
        sscanf(p_name,"%x",&p_cid);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    real_filename = filename;
 | 
			
		||||
    if ((temp_str = strrchr(real_filename, '\\')) != NULL)
 | 
			
		||||
        real_filename = temp_str + 1;
 | 
			
		||||
    if ((temp_str = strrchr(real_filename, '/')) != NULL)
 | 
			
		||||
        real_filename = temp_str + 1;
 | 
			
		||||
    if ((temp_str = strrchr(real_filename, ':')) != NULL)
 | 
			
		||||
        real_filename = temp_str + 1;
 | 
			
		||||
 | 
			
		||||
    snprintf(s_desc, sizeof(s_desc), desc_template, p_cid, p_cid, backing_file,
 | 
			
		||||
             (uint32_t)header.capacity, real_filename);
 | 
			
		||||
 | 
			
		||||
    /* write the descriptor */
 | 
			
		||||
    if (lseek(snp_fd, 0x200, SEEK_SET) == -1) {
 | 
			
		||||
        ret = -errno;
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
    if (write(snp_fd, s_desc, strlen(s_desc)) == -1) {
 | 
			
		||||
        ret = -errno;
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    gd_offset = header.gd_offset * SECTOR_SIZE;     // offset of GD table
 | 
			
		||||
    rgd_offset = header.rgd_offset * SECTOR_SIZE;   // offset of RGD table
 | 
			
		||||
    capacity = header.capacity * SECTOR_SIZE;       // Extent size
 | 
			
		||||
    /*
 | 
			
		||||
     * Each GDE span 32M disk, means:
 | 
			
		||||
     * 512 GTE per GT, each GTE points to grain
 | 
			
		||||
     */
 | 
			
		||||
    gt_size = (int64_t)header.num_gtes_per_gte * header.granularity * SECTOR_SIZE;
 | 
			
		||||
    if (!gt_size) {
 | 
			
		||||
        ret = -EINVAL;
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
    gde_entries = (uint32_t)(capacity / gt_size);  // number of gde/rgde
 | 
			
		||||
    gd_size = gde_entries * sizeof(uint32_t);
 | 
			
		||||
 | 
			
		||||
    /* write RGD */
 | 
			
		||||
    rgd_buf = qemu_malloc(gd_size);
 | 
			
		||||
    if (lseek(p_fd, rgd_offset, SEEK_SET) == -1) {
 | 
			
		||||
        ret = -errno;
 | 
			
		||||
        goto fail_rgd;
 | 
			
		||||
    }
 | 
			
		||||
    if (read(p_fd, rgd_buf, gd_size) != gd_size) {
 | 
			
		||||
        ret = -errno;
 | 
			
		||||
        goto fail_rgd;
 | 
			
		||||
    }
 | 
			
		||||
    if (lseek(snp_fd, rgd_offset, SEEK_SET) == -1) {
 | 
			
		||||
        ret = -errno;
 | 
			
		||||
        goto fail_rgd;
 | 
			
		||||
    }
 | 
			
		||||
    if (write(snp_fd, rgd_buf, gd_size) == -1) {
 | 
			
		||||
        ret = -errno;
 | 
			
		||||
        goto fail_rgd;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* write GD */
 | 
			
		||||
    gd_buf = qemu_malloc(gd_size);
 | 
			
		||||
    if (lseek(p_fd, gd_offset, SEEK_SET) == -1) {
 | 
			
		||||
        ret = -errno;
 | 
			
		||||
        goto fail_gd;
 | 
			
		||||
    }
 | 
			
		||||
    if (read(p_fd, gd_buf, gd_size) != gd_size) {
 | 
			
		||||
        ret = -errno;
 | 
			
		||||
        goto fail_gd;
 | 
			
		||||
    }
 | 
			
		||||
    if (lseek(snp_fd, gd_offset, SEEK_SET) == -1) {
 | 
			
		||||
        ret = -errno;
 | 
			
		||||
        goto fail_gd;
 | 
			
		||||
    }
 | 
			
		||||
    if (write(snp_fd, gd_buf, gd_size) == -1) {
 | 
			
		||||
        ret = -errno;
 | 
			
		||||
        goto fail_gd;
 | 
			
		||||
    }
 | 
			
		||||
    ret = 0;
 | 
			
		||||
 | 
			
		||||
fail_gd:
 | 
			
		||||
    qemu_free(gd_buf);
 | 
			
		||||
fail_rgd:
 | 
			
		||||
    qemu_free(rgd_buf);
 | 
			
		||||
fail:
 | 
			
		||||
    close(p_fd);
 | 
			
		||||
    close(snp_fd);
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int vmdk_parent_open(BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
    char *p_name;
 | 
			
		||||
    char desc[DESC_SIZE];
 | 
			
		||||
 | 
			
		||||
    /* the descriptor offset = 0x200 */
 | 
			
		||||
    if (bdrv_pread(bs->file, 0x200, desc, DESC_SIZE) != DESC_SIZE)
 | 
			
		||||
        return -1;
 | 
			
		||||
 | 
			
		||||
    if ((p_name = strstr(desc,"parentFileNameHint")) != NULL) {
 | 
			
		||||
        char *end_name;
 | 
			
		||||
 | 
			
		||||
        p_name += sizeof("parentFileNameHint") + 1;
 | 
			
		||||
        if ((end_name = strchr(p_name,'\"')) == NULL)
 | 
			
		||||
            return -1;
 | 
			
		||||
        if ((end_name - p_name) > sizeof (bs->backing_file) - 1)
 | 
			
		||||
            return -1;
 | 
			
		||||
 | 
			
		||||
        pstrcpy(bs->backing_file, end_name - p_name + 1, p_name);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int vmdk_open(BlockDriverState *bs, int flags)
 | 
			
		||||
{
 | 
			
		||||
    BDRVVmdkState *s = bs->opaque;
 | 
			
		||||
    uint32_t magic;
 | 
			
		||||
    int l1_size, i;
 | 
			
		||||
 | 
			
		||||
    if (bdrv_pread(bs->file, 0, &magic, sizeof(magic)) != sizeof(magic))
 | 
			
		||||
        goto fail;
 | 
			
		||||
 | 
			
		||||
    magic = be32_to_cpu(magic);
 | 
			
		||||
    if (magic == VMDK3_MAGIC) {
 | 
			
		||||
        VMDK3Header header;
 | 
			
		||||
 | 
			
		||||
        if (bdrv_pread(bs->file, sizeof(magic), &header, sizeof(header)) != sizeof(header))
 | 
			
		||||
            goto fail;
 | 
			
		||||
        s->cluster_sectors = le32_to_cpu(header.granularity);
 | 
			
		||||
        s->l2_size = 1 << 9;
 | 
			
		||||
        s->l1_size = 1 << 6;
 | 
			
		||||
        bs->total_sectors = le32_to_cpu(header.disk_sectors);
 | 
			
		||||
        s->l1_table_offset = le32_to_cpu(header.l1dir_offset) << 9;
 | 
			
		||||
        s->l1_backup_table_offset = 0;
 | 
			
		||||
        s->l1_entry_sectors = s->l2_size * s->cluster_sectors;
 | 
			
		||||
    } else if (magic == VMDK4_MAGIC) {
 | 
			
		||||
        VMDK4Header header;
 | 
			
		||||
 | 
			
		||||
        if (bdrv_pread(bs->file, sizeof(magic), &header, sizeof(header)) != sizeof(header))
 | 
			
		||||
            goto fail;
 | 
			
		||||
        bs->total_sectors = le64_to_cpu(header.capacity);
 | 
			
		||||
        s->cluster_sectors = le64_to_cpu(header.granularity);
 | 
			
		||||
        s->l2_size = le32_to_cpu(header.num_gtes_per_gte);
 | 
			
		||||
        s->l1_entry_sectors = s->l2_size * s->cluster_sectors;
 | 
			
		||||
        if (s->l1_entry_sectors <= 0)
 | 
			
		||||
            goto fail;
 | 
			
		||||
        s->l1_size = (bs->total_sectors + s->l1_entry_sectors - 1)
 | 
			
		||||
            / s->l1_entry_sectors;
 | 
			
		||||
        s->l1_table_offset = le64_to_cpu(header.rgd_offset) << 9;
 | 
			
		||||
        s->l1_backup_table_offset = le64_to_cpu(header.gd_offset) << 9;
 | 
			
		||||
 | 
			
		||||
        // try to open parent images, if exist
 | 
			
		||||
        if (vmdk_parent_open(bs) != 0)
 | 
			
		||||
            goto fail;
 | 
			
		||||
        // write the CID once after the image creation
 | 
			
		||||
        s->parent_cid = vmdk_read_cid(bs,1);
 | 
			
		||||
    } else {
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* read the L1 table */
 | 
			
		||||
    l1_size = s->l1_size * sizeof(uint32_t);
 | 
			
		||||
    s->l1_table = qemu_malloc(l1_size);
 | 
			
		||||
    if (bdrv_pread(bs->file, s->l1_table_offset, s->l1_table, l1_size) != l1_size)
 | 
			
		||||
        goto fail;
 | 
			
		||||
    for(i = 0; i < s->l1_size; i++) {
 | 
			
		||||
        le32_to_cpus(&s->l1_table[i]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (s->l1_backup_table_offset) {
 | 
			
		||||
        s->l1_backup_table = qemu_malloc(l1_size);
 | 
			
		||||
        if (bdrv_pread(bs->file, s->l1_backup_table_offset, s->l1_backup_table, l1_size) != l1_size)
 | 
			
		||||
            goto fail;
 | 
			
		||||
        for(i = 0; i < s->l1_size; i++) {
 | 
			
		||||
            le32_to_cpus(&s->l1_backup_table[i]);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    s->l2_cache = qemu_malloc(s->l2_size * L2_CACHE_SIZE * sizeof(uint32_t));
 | 
			
		||||
    return 0;
 | 
			
		||||
 fail:
 | 
			
		||||
    qemu_free(s->l1_backup_table);
 | 
			
		||||
    qemu_free(s->l1_table);
 | 
			
		||||
    qemu_free(s->l2_cache);
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static uint64_t get_cluster_offset(BlockDriverState *bs, VmdkMetaData *m_data,
 | 
			
		||||
                                   uint64_t offset, int allocate);
 | 
			
		||||
 | 
			
		||||
static int get_whole_cluster(BlockDriverState *bs, uint64_t cluster_offset,
 | 
			
		||||
                             uint64_t offset, int allocate)
 | 
			
		||||
{
 | 
			
		||||
    BDRVVmdkState *s = bs->opaque;
 | 
			
		||||
    uint8_t  whole_grain[s->cluster_sectors*512];        // 128 sectors * 512 bytes each = grain size 64KB
 | 
			
		||||
 | 
			
		||||
    // we will be here if it's first write on non-exist grain(cluster).
 | 
			
		||||
    // try to read from parent image, if exist
 | 
			
		||||
    if (bs->backing_hd) {
 | 
			
		||||
        int ret;
 | 
			
		||||
 | 
			
		||||
        if (!vmdk_is_cid_valid(bs))
 | 
			
		||||
            return -1;
 | 
			
		||||
 | 
			
		||||
        ret = bdrv_read(bs->backing_hd, offset >> 9, whole_grain,
 | 
			
		||||
            s->cluster_sectors);
 | 
			
		||||
        if (ret < 0) {
 | 
			
		||||
            return -1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        //Write grain only into the active image
 | 
			
		||||
        ret = bdrv_write(bs->file, cluster_offset, whole_grain,
 | 
			
		||||
            s->cluster_sectors);
 | 
			
		||||
        if (ret < 0) {
 | 
			
		||||
            return -1;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int vmdk_L2update(BlockDriverState *bs, VmdkMetaData *m_data)
 | 
			
		||||
{
 | 
			
		||||
    BDRVVmdkState *s = bs->opaque;
 | 
			
		||||
 | 
			
		||||
    /* update L2 table */
 | 
			
		||||
    if (bdrv_pwrite_sync(bs->file, ((int64_t)m_data->l2_offset * 512) + (m_data->l2_index * sizeof(m_data->offset)),
 | 
			
		||||
                    &(m_data->offset), sizeof(m_data->offset)) < 0)
 | 
			
		||||
        return -1;
 | 
			
		||||
    /* update backup L2 table */
 | 
			
		||||
    if (s->l1_backup_table_offset != 0) {
 | 
			
		||||
        m_data->l2_offset = s->l1_backup_table[m_data->l1_index];
 | 
			
		||||
        if (bdrv_pwrite_sync(bs->file, ((int64_t)m_data->l2_offset * 512) + (m_data->l2_index * sizeof(m_data->offset)),
 | 
			
		||||
                        &(m_data->offset), sizeof(m_data->offset)) < 0)
 | 
			
		||||
            return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static uint64_t get_cluster_offset(BlockDriverState *bs, VmdkMetaData *m_data,
 | 
			
		||||
                                   uint64_t offset, int allocate)
 | 
			
		||||
{
 | 
			
		||||
    BDRVVmdkState *s = bs->opaque;
 | 
			
		||||
    unsigned int l1_index, l2_offset, l2_index;
 | 
			
		||||
    int min_index, i, j;
 | 
			
		||||
    uint32_t min_count, *l2_table, tmp = 0;
 | 
			
		||||
    uint64_t cluster_offset;
 | 
			
		||||
 | 
			
		||||
    if (m_data)
 | 
			
		||||
        m_data->valid = 0;
 | 
			
		||||
 | 
			
		||||
    l1_index = (offset >> 9) / s->l1_entry_sectors;
 | 
			
		||||
    if (l1_index >= s->l1_size)
 | 
			
		||||
        return 0;
 | 
			
		||||
    l2_offset = s->l1_table[l1_index];
 | 
			
		||||
    if (!l2_offset)
 | 
			
		||||
        return 0;
 | 
			
		||||
    for(i = 0; i < L2_CACHE_SIZE; i++) {
 | 
			
		||||
        if (l2_offset == s->l2_cache_offsets[i]) {
 | 
			
		||||
            /* increment the hit count */
 | 
			
		||||
            if (++s->l2_cache_counts[i] == 0xffffffff) {
 | 
			
		||||
                for(j = 0; j < L2_CACHE_SIZE; j++) {
 | 
			
		||||
                    s->l2_cache_counts[j] >>= 1;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            l2_table = s->l2_cache + (i * s->l2_size);
 | 
			
		||||
            goto found;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    /* not found: load a new entry in the least used one */
 | 
			
		||||
    min_index = 0;
 | 
			
		||||
    min_count = 0xffffffff;
 | 
			
		||||
    for(i = 0; i < L2_CACHE_SIZE; i++) {
 | 
			
		||||
        if (s->l2_cache_counts[i] < min_count) {
 | 
			
		||||
            min_count = s->l2_cache_counts[i];
 | 
			
		||||
            min_index = i;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    l2_table = s->l2_cache + (min_index * s->l2_size);
 | 
			
		||||
    if (bdrv_pread(bs->file, (int64_t)l2_offset * 512, l2_table, s->l2_size * sizeof(uint32_t)) !=
 | 
			
		||||
                                                                        s->l2_size * sizeof(uint32_t))
 | 
			
		||||
        return 0;
 | 
			
		||||
 | 
			
		||||
    s->l2_cache_offsets[min_index] = l2_offset;
 | 
			
		||||
    s->l2_cache_counts[min_index] = 1;
 | 
			
		||||
 found:
 | 
			
		||||
    l2_index = ((offset >> 9) / s->cluster_sectors) % s->l2_size;
 | 
			
		||||
    cluster_offset = le32_to_cpu(l2_table[l2_index]);
 | 
			
		||||
 | 
			
		||||
    if (!cluster_offset) {
 | 
			
		||||
        if (!allocate)
 | 
			
		||||
            return 0;
 | 
			
		||||
 | 
			
		||||
        // Avoid the L2 tables update for the images that have snapshots.
 | 
			
		||||
        cluster_offset = bdrv_getlength(bs->file);
 | 
			
		||||
        bdrv_truncate(bs->file, cluster_offset + (s->cluster_sectors << 9));
 | 
			
		||||
 | 
			
		||||
        cluster_offset >>= 9;
 | 
			
		||||
        tmp = cpu_to_le32(cluster_offset);
 | 
			
		||||
        l2_table[l2_index] = tmp;
 | 
			
		||||
 | 
			
		||||
        /* First of all we write grain itself, to avoid race condition
 | 
			
		||||
         * that may to corrupt the image.
 | 
			
		||||
         * This problem may occur because of insufficient space on host disk
 | 
			
		||||
         * or inappropriate VM shutdown.
 | 
			
		||||
         */
 | 
			
		||||
        if (get_whole_cluster(bs, cluster_offset, offset, allocate) == -1)
 | 
			
		||||
            return 0;
 | 
			
		||||
 | 
			
		||||
        if (m_data) {
 | 
			
		||||
            m_data->offset = tmp;
 | 
			
		||||
            m_data->l1_index = l1_index;
 | 
			
		||||
            m_data->l2_index = l2_index;
 | 
			
		||||
            m_data->l2_offset = l2_offset;
 | 
			
		||||
            m_data->valid = 1;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    cluster_offset <<= 9;
 | 
			
		||||
    return cluster_offset;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int vmdk_is_allocated(BlockDriverState *bs, int64_t sector_num,
 | 
			
		||||
                             int nb_sectors, int *pnum)
 | 
			
		||||
{
 | 
			
		||||
    BDRVVmdkState *s = bs->opaque;
 | 
			
		||||
    int index_in_cluster, n;
 | 
			
		||||
    uint64_t cluster_offset;
 | 
			
		||||
 | 
			
		||||
    cluster_offset = get_cluster_offset(bs, NULL, sector_num << 9, 0);
 | 
			
		||||
    index_in_cluster = sector_num % s->cluster_sectors;
 | 
			
		||||
    n = s->cluster_sectors - index_in_cluster;
 | 
			
		||||
    if (n > nb_sectors)
 | 
			
		||||
        n = nb_sectors;
 | 
			
		||||
    *pnum = n;
 | 
			
		||||
    return (cluster_offset != 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int vmdk_read(BlockDriverState *bs, int64_t sector_num,
 | 
			
		||||
                    uint8_t *buf, int nb_sectors)
 | 
			
		||||
{
 | 
			
		||||
    BDRVVmdkState *s = bs->opaque;
 | 
			
		||||
    int index_in_cluster, n, ret;
 | 
			
		||||
    uint64_t cluster_offset;
 | 
			
		||||
 | 
			
		||||
    while (nb_sectors > 0) {
 | 
			
		||||
        cluster_offset = get_cluster_offset(bs, NULL, sector_num << 9, 0);
 | 
			
		||||
        index_in_cluster = sector_num % s->cluster_sectors;
 | 
			
		||||
        n = s->cluster_sectors - index_in_cluster;
 | 
			
		||||
        if (n > nb_sectors)
 | 
			
		||||
            n = nb_sectors;
 | 
			
		||||
        if (!cluster_offset) {
 | 
			
		||||
            // try to read from parent image, if exist
 | 
			
		||||
            if (bs->backing_hd) {
 | 
			
		||||
                if (!vmdk_is_cid_valid(bs))
 | 
			
		||||
                    return -1;
 | 
			
		||||
                ret = bdrv_read(bs->backing_hd, sector_num, buf, n);
 | 
			
		||||
                if (ret < 0)
 | 
			
		||||
                    return -1;
 | 
			
		||||
            } else {
 | 
			
		||||
                memset(buf, 0, 512 * n);
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            if(bdrv_pread(bs->file, cluster_offset + index_in_cluster * 512, buf, n * 512) != n * 512)
 | 
			
		||||
                return -1;
 | 
			
		||||
        }
 | 
			
		||||
        nb_sectors -= n;
 | 
			
		||||
        sector_num += n;
 | 
			
		||||
        buf += n * 512;
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int vmdk_write(BlockDriverState *bs, int64_t sector_num,
 | 
			
		||||
                     const uint8_t *buf, int nb_sectors)
 | 
			
		||||
{
 | 
			
		||||
    BDRVVmdkState *s = bs->opaque;
 | 
			
		||||
    VmdkMetaData m_data;
 | 
			
		||||
    int index_in_cluster, n;
 | 
			
		||||
    uint64_t cluster_offset;
 | 
			
		||||
    static int cid_update = 0;
 | 
			
		||||
 | 
			
		||||
    if (sector_num > bs->total_sectors) {
 | 
			
		||||
        fprintf(stderr,
 | 
			
		||||
                "(VMDK) Wrong offset: sector_num=0x%" PRIx64
 | 
			
		||||
                " total_sectors=0x%" PRIx64 "\n",
 | 
			
		||||
                sector_num, bs->total_sectors);
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    while (nb_sectors > 0) {
 | 
			
		||||
        index_in_cluster = sector_num & (s->cluster_sectors - 1);
 | 
			
		||||
        n = s->cluster_sectors - index_in_cluster;
 | 
			
		||||
        if (n > nb_sectors)
 | 
			
		||||
            n = nb_sectors;
 | 
			
		||||
        cluster_offset = get_cluster_offset(bs, &m_data, sector_num << 9, 1);
 | 
			
		||||
        if (!cluster_offset)
 | 
			
		||||
            return -1;
 | 
			
		||||
 | 
			
		||||
        if (bdrv_pwrite(bs->file, cluster_offset + index_in_cluster * 512, buf, n * 512) != n * 512)
 | 
			
		||||
            return -1;
 | 
			
		||||
        if (m_data.valid) {
 | 
			
		||||
            /* update L2 tables */
 | 
			
		||||
            if (vmdk_L2update(bs, &m_data) == -1)
 | 
			
		||||
                return -1;
 | 
			
		||||
        }
 | 
			
		||||
        nb_sectors -= n;
 | 
			
		||||
        sector_num += n;
 | 
			
		||||
        buf += n * 512;
 | 
			
		||||
 | 
			
		||||
        // update CID on the first write every time the virtual disk is opened
 | 
			
		||||
        if (!cid_update) {
 | 
			
		||||
            vmdk_write_cid(bs, time(NULL));
 | 
			
		||||
            cid_update++;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int vmdk_create(const char *filename, QEMUOptionParameter *options)
 | 
			
		||||
{
 | 
			
		||||
    int fd, i;
 | 
			
		||||
    VMDK4Header header;
 | 
			
		||||
    uint32_t tmp, magic, grains, gd_size, gt_size, gt_count;
 | 
			
		||||
    static const char desc_template[] =
 | 
			
		||||
        "# Disk DescriptorFile\n"
 | 
			
		||||
        "version=1\n"
 | 
			
		||||
        "CID=%x\n"
 | 
			
		||||
        "parentCID=ffffffff\n"
 | 
			
		||||
        "createType=\"monolithicSparse\"\n"
 | 
			
		||||
        "\n"
 | 
			
		||||
        "# Extent description\n"
 | 
			
		||||
        "RW %" PRId64 " SPARSE \"%s\"\n"
 | 
			
		||||
        "\n"
 | 
			
		||||
        "# The Disk Data Base \n"
 | 
			
		||||
        "#DDB\n"
 | 
			
		||||
        "\n"
 | 
			
		||||
        "ddb.virtualHWVersion = \"%d\"\n"
 | 
			
		||||
        "ddb.geometry.cylinders = \"%" PRId64 "\"\n"
 | 
			
		||||
        "ddb.geometry.heads = \"16\"\n"
 | 
			
		||||
        "ddb.geometry.sectors = \"63\"\n"
 | 
			
		||||
        "ddb.adapterType = \"ide\"\n";
 | 
			
		||||
    char desc[1024];
 | 
			
		||||
    const char *real_filename, *temp_str;
 | 
			
		||||
    int64_t total_size = 0;
 | 
			
		||||
    const char *backing_file = NULL;
 | 
			
		||||
    int flags = 0;
 | 
			
		||||
    int ret;
 | 
			
		||||
 | 
			
		||||
    // Read out options
 | 
			
		||||
    while (options && options->name) {
 | 
			
		||||
        if (!strcmp(options->name, BLOCK_OPT_SIZE)) {
 | 
			
		||||
            total_size = options->value.n / 512;
 | 
			
		||||
        } else if (!strcmp(options->name, BLOCK_OPT_BACKING_FILE)) {
 | 
			
		||||
            backing_file = options->value.s;
 | 
			
		||||
        } else if (!strcmp(options->name, BLOCK_OPT_COMPAT6)) {
 | 
			
		||||
            flags |= options->value.n ? BLOCK_FLAG_COMPAT6: 0;
 | 
			
		||||
        }
 | 
			
		||||
        options++;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* XXX: add support for backing file */
 | 
			
		||||
    if (backing_file) {
 | 
			
		||||
        return vmdk_snapshot_create(filename, backing_file);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE,
 | 
			
		||||
              0644);
 | 
			
		||||
    if (fd < 0)
 | 
			
		||||
        return -errno;
 | 
			
		||||
    magic = cpu_to_be32(VMDK4_MAGIC);
 | 
			
		||||
    memset(&header, 0, sizeof(header));
 | 
			
		||||
    header.version = cpu_to_le32(1);
 | 
			
		||||
    header.flags = cpu_to_le32(3); /* ?? */
 | 
			
		||||
    header.capacity = cpu_to_le64(total_size);
 | 
			
		||||
    header.granularity = cpu_to_le64(128);
 | 
			
		||||
    header.num_gtes_per_gte = cpu_to_le32(512);
 | 
			
		||||
 | 
			
		||||
    grains = (total_size + header.granularity - 1) / header.granularity;
 | 
			
		||||
    gt_size = ((header.num_gtes_per_gte * sizeof(uint32_t)) + 511) >> 9;
 | 
			
		||||
    gt_count = (grains + header.num_gtes_per_gte - 1) / header.num_gtes_per_gte;
 | 
			
		||||
    gd_size = (gt_count * sizeof(uint32_t) + 511) >> 9;
 | 
			
		||||
 | 
			
		||||
    header.desc_offset = 1;
 | 
			
		||||
    header.desc_size = 20;
 | 
			
		||||
    header.rgd_offset = header.desc_offset + header.desc_size;
 | 
			
		||||
    header.gd_offset = header.rgd_offset + gd_size + (gt_size * gt_count);
 | 
			
		||||
    header.grain_offset =
 | 
			
		||||
       ((header.gd_offset + gd_size + (gt_size * gt_count) +
 | 
			
		||||
         header.granularity - 1) / header.granularity) *
 | 
			
		||||
        header.granularity;
 | 
			
		||||
 | 
			
		||||
    header.desc_offset = cpu_to_le64(header.desc_offset);
 | 
			
		||||
    header.desc_size = cpu_to_le64(header.desc_size);
 | 
			
		||||
    header.rgd_offset = cpu_to_le64(header.rgd_offset);
 | 
			
		||||
    header.gd_offset = cpu_to_le64(header.gd_offset);
 | 
			
		||||
    header.grain_offset = cpu_to_le64(header.grain_offset);
 | 
			
		||||
 | 
			
		||||
    header.check_bytes[0] = 0xa;
 | 
			
		||||
    header.check_bytes[1] = 0x20;
 | 
			
		||||
    header.check_bytes[2] = 0xd;
 | 
			
		||||
    header.check_bytes[3] = 0xa;
 | 
			
		||||
 | 
			
		||||
    /* write all the data */
 | 
			
		||||
    ret = qemu_write_full(fd, &magic, sizeof(magic));
 | 
			
		||||
    if (ret != sizeof(magic)) {
 | 
			
		||||
        ret = -errno;
 | 
			
		||||
        goto exit;
 | 
			
		||||
    }
 | 
			
		||||
    ret = qemu_write_full(fd, &header, sizeof(header));
 | 
			
		||||
    if (ret != sizeof(header)) {
 | 
			
		||||
        ret = -errno;
 | 
			
		||||
        goto exit;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ret = ftruncate(fd, header.grain_offset << 9);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        ret = -errno;
 | 
			
		||||
        goto exit;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* write grain directory */
 | 
			
		||||
    lseek(fd, le64_to_cpu(header.rgd_offset) << 9, SEEK_SET);
 | 
			
		||||
    for (i = 0, tmp = header.rgd_offset + gd_size;
 | 
			
		||||
         i < gt_count; i++, tmp += gt_size) {
 | 
			
		||||
        ret = qemu_write_full(fd, &tmp, sizeof(tmp));
 | 
			
		||||
        if (ret != sizeof(tmp)) {
 | 
			
		||||
            ret = -errno;
 | 
			
		||||
            goto exit;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* write backup grain directory */
 | 
			
		||||
    lseek(fd, le64_to_cpu(header.gd_offset) << 9, SEEK_SET);
 | 
			
		||||
    for (i = 0, tmp = header.gd_offset + gd_size;
 | 
			
		||||
         i < gt_count; i++, tmp += gt_size) {
 | 
			
		||||
        ret = qemu_write_full(fd, &tmp, sizeof(tmp));
 | 
			
		||||
        if (ret != sizeof(tmp)) {
 | 
			
		||||
            ret = -errno;
 | 
			
		||||
            goto exit;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* compose the descriptor */
 | 
			
		||||
    real_filename = filename;
 | 
			
		||||
    if ((temp_str = strrchr(real_filename, '\\')) != NULL)
 | 
			
		||||
        real_filename = temp_str + 1;
 | 
			
		||||
    if ((temp_str = strrchr(real_filename, '/')) != NULL)
 | 
			
		||||
        real_filename = temp_str + 1;
 | 
			
		||||
    if ((temp_str = strrchr(real_filename, ':')) != NULL)
 | 
			
		||||
        real_filename = temp_str + 1;
 | 
			
		||||
    snprintf(desc, sizeof(desc), desc_template, (unsigned int)time(NULL),
 | 
			
		||||
             total_size, real_filename,
 | 
			
		||||
             (flags & BLOCK_FLAG_COMPAT6 ? 6 : 4),
 | 
			
		||||
             total_size / (int64_t)(63 * 16));
 | 
			
		||||
 | 
			
		||||
    /* write the descriptor */
 | 
			
		||||
    lseek(fd, le64_to_cpu(header.desc_offset) << 9, SEEK_SET);
 | 
			
		||||
    ret = qemu_write_full(fd, desc, strlen(desc));
 | 
			
		||||
    if (ret != strlen(desc)) {
 | 
			
		||||
        ret = -errno;
 | 
			
		||||
        goto exit;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ret = 0;
 | 
			
		||||
exit:
 | 
			
		||||
    close(fd);
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void vmdk_close(BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
    BDRVVmdkState *s = bs->opaque;
 | 
			
		||||
 | 
			
		||||
    qemu_free(s->l1_table);
 | 
			
		||||
    qemu_free(s->l2_cache);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void vmdk_flush(BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
    bdrv_flush(bs->file);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static QEMUOptionParameter vmdk_create_options[] = {
 | 
			
		||||
    {
 | 
			
		||||
        .name = BLOCK_OPT_SIZE,
 | 
			
		||||
        .type = OPT_SIZE,
 | 
			
		||||
        .help = "Virtual disk size"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        .name = BLOCK_OPT_BACKING_FILE,
 | 
			
		||||
        .type = OPT_STRING,
 | 
			
		||||
        .help = "File name of a base image"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        .name = BLOCK_OPT_COMPAT6,
 | 
			
		||||
        .type = OPT_FLAG,
 | 
			
		||||
        .help = "VMDK version 6 image"
 | 
			
		||||
    },
 | 
			
		||||
    { NULL }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static BlockDriver bdrv_vmdk = {
 | 
			
		||||
    .format_name	= "vmdk",
 | 
			
		||||
    .instance_size	= sizeof(BDRVVmdkState),
 | 
			
		||||
    .bdrv_probe		= vmdk_probe,
 | 
			
		||||
    .bdrv_open      = vmdk_open,
 | 
			
		||||
    .bdrv_read		= vmdk_read,
 | 
			
		||||
    .bdrv_write		= vmdk_write,
 | 
			
		||||
    .bdrv_close		= vmdk_close,
 | 
			
		||||
    .bdrv_create	= vmdk_create,
 | 
			
		||||
    .bdrv_flush		= vmdk_flush,
 | 
			
		||||
    .bdrv_is_allocated	= vmdk_is_allocated,
 | 
			
		||||
 | 
			
		||||
    .create_options = vmdk_create_options,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void bdrv_vmdk_init(void)
 | 
			
		||||
{
 | 
			
		||||
    bdrv_register(&bdrv_vmdk);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
block_init(bdrv_vmdk_init);
 | 
			
		||||
							
								
								
									
										638
									
								
								block/vpc.c
									
									
									
									
									
								
							
							
						
						
									
										638
									
								
								block/vpc.c
									
									
									
									
									
								
							@@ -1,638 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Block driver for Connectix / Microsoft Virtual PC images
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2005 Alex Beregszaszi
 | 
			
		||||
 * Copyright (c) 2009 Kevin Wolf <kwolf@suse.de>
 | 
			
		||||
 *
 | 
			
		||||
 * 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-common.h"
 | 
			
		||||
#include "block_int.h"
 | 
			
		||||
#include "module.h"
 | 
			
		||||
 | 
			
		||||
/**************************************************************/
 | 
			
		||||
 | 
			
		||||
#define HEADER_SIZE 512
 | 
			
		||||
 | 
			
		||||
//#define CACHE
 | 
			
		||||
 | 
			
		||||
enum vhd_type {
 | 
			
		||||
    VHD_FIXED           = 2,
 | 
			
		||||
    VHD_DYNAMIC         = 3,
 | 
			
		||||
    VHD_DIFFERENCING    = 4,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Seconds since Jan 1, 2000 0:00:00 (UTC)
 | 
			
		||||
#define VHD_TIMESTAMP_BASE 946684800
 | 
			
		||||
 | 
			
		||||
// always big-endian
 | 
			
		||||
struct vhd_footer {
 | 
			
		||||
    char        creator[8]; // "conectix"
 | 
			
		||||
    uint32_t    features;
 | 
			
		||||
    uint32_t    version;
 | 
			
		||||
 | 
			
		||||
    // Offset of next header structure, 0xFFFFFFFF if none
 | 
			
		||||
    uint64_t    data_offset;
 | 
			
		||||
 | 
			
		||||
    // Seconds since Jan 1, 2000 0:00:00 (UTC)
 | 
			
		||||
    uint32_t    timestamp;
 | 
			
		||||
 | 
			
		||||
    char        creator_app[4]; // "vpc "
 | 
			
		||||
    uint16_t    major;
 | 
			
		||||
    uint16_t    minor;
 | 
			
		||||
    char        creator_os[4]; // "Wi2k"
 | 
			
		||||
 | 
			
		||||
    uint64_t    orig_size;
 | 
			
		||||
    uint64_t    size;
 | 
			
		||||
 | 
			
		||||
    uint16_t    cyls;
 | 
			
		||||
    uint8_t     heads;
 | 
			
		||||
    uint8_t     secs_per_cyl;
 | 
			
		||||
 | 
			
		||||
    uint32_t    type;
 | 
			
		||||
 | 
			
		||||
    // Checksum of the Hard Disk Footer ("one's complement of the sum of all
 | 
			
		||||
    // the bytes in the footer without the checksum field")
 | 
			
		||||
    uint32_t    checksum;
 | 
			
		||||
 | 
			
		||||
    // UUID used to identify a parent hard disk (backing file)
 | 
			
		||||
    uint8_t     uuid[16];
 | 
			
		||||
 | 
			
		||||
    uint8_t     in_saved_state;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct vhd_dyndisk_header {
 | 
			
		||||
    char        magic[8]; // "cxsparse"
 | 
			
		||||
 | 
			
		||||
    // Offset of next header structure, 0xFFFFFFFF if none
 | 
			
		||||
    uint64_t    data_offset;
 | 
			
		||||
 | 
			
		||||
    // Offset of the Block Allocation Table (BAT)
 | 
			
		||||
    uint64_t    table_offset;
 | 
			
		||||
 | 
			
		||||
    uint32_t    version;
 | 
			
		||||
    uint32_t    max_table_entries; // 32bit/entry
 | 
			
		||||
 | 
			
		||||
    // 2 MB by default, must be a power of two
 | 
			
		||||
    uint32_t    block_size;
 | 
			
		||||
 | 
			
		||||
    uint32_t    checksum;
 | 
			
		||||
    uint8_t     parent_uuid[16];
 | 
			
		||||
    uint32_t    parent_timestamp;
 | 
			
		||||
    uint32_t    reserved;
 | 
			
		||||
 | 
			
		||||
    // Backing file name (in UTF-16)
 | 
			
		||||
    uint8_t     parent_name[512];
 | 
			
		||||
 | 
			
		||||
    struct {
 | 
			
		||||
        uint32_t    platform;
 | 
			
		||||
        uint32_t    data_space;
 | 
			
		||||
        uint32_t    data_length;
 | 
			
		||||
        uint32_t    reserved;
 | 
			
		||||
        uint64_t    data_offset;
 | 
			
		||||
    } parent_locator[8];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef struct BDRVVPCState {
 | 
			
		||||
    BlockDriverState *hd;
 | 
			
		||||
 | 
			
		||||
    uint8_t footer_buf[HEADER_SIZE];
 | 
			
		||||
    uint64_t free_data_block_offset;
 | 
			
		||||
    int max_table_entries;
 | 
			
		||||
    uint32_t *pagetable;
 | 
			
		||||
    uint64_t bat_offset;
 | 
			
		||||
    uint64_t last_bitmap_offset;
 | 
			
		||||
 | 
			
		||||
    uint32_t block_size;
 | 
			
		||||
    uint32_t bitmap_size;
 | 
			
		||||
 | 
			
		||||
#ifdef CACHE
 | 
			
		||||
    uint8_t *pageentry_u8;
 | 
			
		||||
    uint32_t *pageentry_u32;
 | 
			
		||||
    uint16_t *pageentry_u16;
 | 
			
		||||
 | 
			
		||||
    uint64_t last_bitmap;
 | 
			
		||||
#endif
 | 
			
		||||
} BDRVVPCState;
 | 
			
		||||
 | 
			
		||||
static uint32_t vpc_checksum(uint8_t* buf, size_t size)
 | 
			
		||||
{
 | 
			
		||||
    uint32_t res = 0;
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < size; i++)
 | 
			
		||||
        res += buf[i];
 | 
			
		||||
 | 
			
		||||
    return ~res;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static int vpc_probe(const uint8_t *buf, int buf_size, const char *filename)
 | 
			
		||||
{
 | 
			
		||||
    if (buf_size >= 8 && !strncmp((char *)buf, "conectix", 8))
 | 
			
		||||
	return 100;
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int vpc_open(BlockDriverState *bs, int flags)
 | 
			
		||||
{
 | 
			
		||||
    BDRVVPCState *s = bs->opaque;
 | 
			
		||||
    int i;
 | 
			
		||||
    struct vhd_footer* footer;
 | 
			
		||||
    struct vhd_dyndisk_header* dyndisk_header;
 | 
			
		||||
    uint8_t buf[HEADER_SIZE];
 | 
			
		||||
    uint32_t checksum;
 | 
			
		||||
 | 
			
		||||
    if (bdrv_pread(bs->file, 0, s->footer_buf, HEADER_SIZE) != HEADER_SIZE)
 | 
			
		||||
        goto fail;
 | 
			
		||||
 | 
			
		||||
    footer = (struct vhd_footer*) s->footer_buf;
 | 
			
		||||
    if (strncmp(footer->creator, "conectix", 8))
 | 
			
		||||
        goto fail;
 | 
			
		||||
 | 
			
		||||
    checksum = be32_to_cpu(footer->checksum);
 | 
			
		||||
    footer->checksum = 0;
 | 
			
		||||
    if (vpc_checksum(s->footer_buf, HEADER_SIZE) != checksum)
 | 
			
		||||
        fprintf(stderr, "block-vpc: The header checksum of '%s' is "
 | 
			
		||||
            "incorrect.\n", bs->filename);
 | 
			
		||||
 | 
			
		||||
    // The visible size of a image in Virtual PC depends on the geometry
 | 
			
		||||
    // rather than on the size stored in the footer (the size in the footer
 | 
			
		||||
    // is too large usually)
 | 
			
		||||
    bs->total_sectors = (int64_t)
 | 
			
		||||
        be16_to_cpu(footer->cyls) * footer->heads * footer->secs_per_cyl;
 | 
			
		||||
 | 
			
		||||
    if (bdrv_pread(bs->file, be64_to_cpu(footer->data_offset), buf, HEADER_SIZE)
 | 
			
		||||
            != HEADER_SIZE)
 | 
			
		||||
        goto fail;
 | 
			
		||||
 | 
			
		||||
    dyndisk_header = (struct vhd_dyndisk_header*) buf;
 | 
			
		||||
 | 
			
		||||
    if (strncmp(dyndisk_header->magic, "cxsparse", 8))
 | 
			
		||||
        goto fail;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    s->block_size = be32_to_cpu(dyndisk_header->block_size);
 | 
			
		||||
    s->bitmap_size = ((s->block_size / (8 * 512)) + 511) & ~511;
 | 
			
		||||
 | 
			
		||||
    s->max_table_entries = be32_to_cpu(dyndisk_header->max_table_entries);
 | 
			
		||||
    s->pagetable = qemu_malloc(s->max_table_entries * 4);
 | 
			
		||||
 | 
			
		||||
    s->bat_offset = be64_to_cpu(dyndisk_header->table_offset);
 | 
			
		||||
    if (bdrv_pread(bs->file, s->bat_offset, s->pagetable,
 | 
			
		||||
            s->max_table_entries * 4) != s->max_table_entries * 4)
 | 
			
		||||
	    goto fail;
 | 
			
		||||
 | 
			
		||||
    s->free_data_block_offset =
 | 
			
		||||
        (s->bat_offset + (s->max_table_entries * 4) + 511) & ~511;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < s->max_table_entries; i++) {
 | 
			
		||||
        be32_to_cpus(&s->pagetable[i]);
 | 
			
		||||
        if (s->pagetable[i] != 0xFFFFFFFF) {
 | 
			
		||||
            int64_t next = (512 * (int64_t) s->pagetable[i]) +
 | 
			
		||||
                s->bitmap_size + s->block_size;
 | 
			
		||||
 | 
			
		||||
            if (next> s->free_data_block_offset)
 | 
			
		||||
                s->free_data_block_offset = next;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    s->last_bitmap_offset = (int64_t) -1;
 | 
			
		||||
 | 
			
		||||
#ifdef CACHE
 | 
			
		||||
    s->pageentry_u8 = qemu_malloc(512);
 | 
			
		||||
    s->pageentry_u32 = s->pageentry_u8;
 | 
			
		||||
    s->pageentry_u16 = s->pageentry_u8;
 | 
			
		||||
    s->last_pagetable = -1;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
 fail:
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Returns the absolute byte offset of the given sector in the image file.
 | 
			
		||||
 * If the sector is not allocated, -1 is returned instead.
 | 
			
		||||
 *
 | 
			
		||||
 * The parameter write must be 1 if the offset will be used for a write
 | 
			
		||||
 * operation (the block bitmaps is updated then), 0 otherwise.
 | 
			
		||||
 */
 | 
			
		||||
static inline int64_t get_sector_offset(BlockDriverState *bs,
 | 
			
		||||
    int64_t sector_num, int write)
 | 
			
		||||
{
 | 
			
		||||
    BDRVVPCState *s = bs->opaque;
 | 
			
		||||
    uint64_t offset = sector_num * 512;
 | 
			
		||||
    uint64_t bitmap_offset, block_offset;
 | 
			
		||||
    uint32_t pagetable_index, pageentry_index;
 | 
			
		||||
 | 
			
		||||
    pagetable_index = offset / s->block_size;
 | 
			
		||||
    pageentry_index = (offset % s->block_size) / 512;
 | 
			
		||||
 | 
			
		||||
    if (pagetable_index >= s->max_table_entries || s->pagetable[pagetable_index] == 0xffffffff)
 | 
			
		||||
        return -1; // not allocated
 | 
			
		||||
 | 
			
		||||
    bitmap_offset = 512 * (uint64_t) s->pagetable[pagetable_index];
 | 
			
		||||
    block_offset = bitmap_offset + s->bitmap_size + (512 * pageentry_index);
 | 
			
		||||
 | 
			
		||||
    // We must ensure that we don't write to any sectors which are marked as
 | 
			
		||||
    // unused in the bitmap. We get away with setting all bits in the block
 | 
			
		||||
    // bitmap each time we write to a new block. This might cause Virtual PC to
 | 
			
		||||
    // miss sparse read optimization, but it's not a problem in terms of
 | 
			
		||||
    // correctness.
 | 
			
		||||
    if (write && (s->last_bitmap_offset != bitmap_offset)) {
 | 
			
		||||
        uint8_t bitmap[s->bitmap_size];
 | 
			
		||||
 | 
			
		||||
        s->last_bitmap_offset = bitmap_offset;
 | 
			
		||||
        memset(bitmap, 0xff, s->bitmap_size);
 | 
			
		||||
        bdrv_pwrite_sync(bs->file, bitmap_offset, bitmap, s->bitmap_size);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
//    printf("sector: %" PRIx64 ", index: %x, offset: %x, bioff: %" PRIx64 ", bloff: %" PRIx64 "\n",
 | 
			
		||||
//	sector_num, pagetable_index, pageentry_index,
 | 
			
		||||
//	bitmap_offset, block_offset);
 | 
			
		||||
 | 
			
		||||
// disabled by reason
 | 
			
		||||
#if 0
 | 
			
		||||
#ifdef CACHE
 | 
			
		||||
    if (bitmap_offset != s->last_bitmap)
 | 
			
		||||
    {
 | 
			
		||||
	lseek(s->fd, bitmap_offset, SEEK_SET);
 | 
			
		||||
 | 
			
		||||
	s->last_bitmap = bitmap_offset;
 | 
			
		||||
 | 
			
		||||
	// Scary! Bitmap is stored as big endian 32bit entries,
 | 
			
		||||
	// while we used to look it up byte by byte
 | 
			
		||||
	read(s->fd, s->pageentry_u8, 512);
 | 
			
		||||
	for (i = 0; i < 128; i++)
 | 
			
		||||
	    be32_to_cpus(&s->pageentry_u32[i]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ((s->pageentry_u8[pageentry_index / 8] >> (pageentry_index % 8)) & 1)
 | 
			
		||||
	return -1;
 | 
			
		||||
#else
 | 
			
		||||
    lseek(s->fd, bitmap_offset + (pageentry_index / 8), SEEK_SET);
 | 
			
		||||
 | 
			
		||||
    read(s->fd, &bitmap_entry, 1);
 | 
			
		||||
 | 
			
		||||
    if ((bitmap_entry >> (pageentry_index % 8)) & 1)
 | 
			
		||||
	return -1; // not allocated
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    return block_offset;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Writes the footer to the end of the image file. This is needed when the
 | 
			
		||||
 * file grows as it overwrites the old footer
 | 
			
		||||
 *
 | 
			
		||||
 * Returns 0 on success and < 0 on error
 | 
			
		||||
 */
 | 
			
		||||
static int rewrite_footer(BlockDriverState* bs)
 | 
			
		||||
{
 | 
			
		||||
    int ret;
 | 
			
		||||
    BDRVVPCState *s = bs->opaque;
 | 
			
		||||
    int64_t offset = s->free_data_block_offset;
 | 
			
		||||
 | 
			
		||||
    ret = bdrv_pwrite_sync(bs->file, offset, s->footer_buf, HEADER_SIZE);
 | 
			
		||||
    if (ret < 0)
 | 
			
		||||
        return ret;
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Allocates a new block. This involves writing a new footer and updating
 | 
			
		||||
 * the Block Allocation Table to use the space at the old end of the image
 | 
			
		||||
 * file (overwriting the old footer)
 | 
			
		||||
 *
 | 
			
		||||
 * Returns the sectors' offset in the image file on success and < 0 on error
 | 
			
		||||
 */
 | 
			
		||||
static int64_t alloc_block(BlockDriverState* bs, int64_t sector_num)
 | 
			
		||||
{
 | 
			
		||||
    BDRVVPCState *s = bs->opaque;
 | 
			
		||||
    int64_t bat_offset;
 | 
			
		||||
    uint32_t index, bat_value;
 | 
			
		||||
    int ret;
 | 
			
		||||
    uint8_t bitmap[s->bitmap_size];
 | 
			
		||||
 | 
			
		||||
    // Check if sector_num is valid
 | 
			
		||||
    if ((sector_num < 0) || (sector_num > bs->total_sectors))
 | 
			
		||||
        return -1;
 | 
			
		||||
 | 
			
		||||
    // Write entry into in-memory BAT
 | 
			
		||||
    index = (sector_num * 512) / s->block_size;
 | 
			
		||||
    if (s->pagetable[index] != 0xFFFFFFFF)
 | 
			
		||||
        return -1;
 | 
			
		||||
 | 
			
		||||
    s->pagetable[index] = s->free_data_block_offset / 512;
 | 
			
		||||
 | 
			
		||||
    // Initialize the block's bitmap
 | 
			
		||||
    memset(bitmap, 0xff, s->bitmap_size);
 | 
			
		||||
    bdrv_pwrite_sync(bs->file, s->free_data_block_offset, bitmap,
 | 
			
		||||
        s->bitmap_size);
 | 
			
		||||
 | 
			
		||||
    // Write new footer (the old one will be overwritten)
 | 
			
		||||
    s->free_data_block_offset += s->block_size + s->bitmap_size;
 | 
			
		||||
    ret = rewrite_footer(bs);
 | 
			
		||||
    if (ret < 0)
 | 
			
		||||
        goto fail;
 | 
			
		||||
 | 
			
		||||
    // Write BAT entry to disk
 | 
			
		||||
    bat_offset = s->bat_offset + (4 * index);
 | 
			
		||||
    bat_value = be32_to_cpu(s->pagetable[index]);
 | 
			
		||||
    ret = bdrv_pwrite_sync(bs->file, bat_offset, &bat_value, 4);
 | 
			
		||||
    if (ret < 0)
 | 
			
		||||
        goto fail;
 | 
			
		||||
 | 
			
		||||
    return get_sector_offset(bs, sector_num, 0);
 | 
			
		||||
 | 
			
		||||
fail:
 | 
			
		||||
    s->free_data_block_offset -= (s->block_size + s->bitmap_size);
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int vpc_read(BlockDriverState *bs, int64_t sector_num,
 | 
			
		||||
                    uint8_t *buf, int nb_sectors)
 | 
			
		||||
{
 | 
			
		||||
    BDRVVPCState *s = bs->opaque;
 | 
			
		||||
    int ret;
 | 
			
		||||
    int64_t offset;
 | 
			
		||||
    int64_t sectors, sectors_per_block;
 | 
			
		||||
 | 
			
		||||
    while (nb_sectors > 0) {
 | 
			
		||||
        offset = get_sector_offset(bs, sector_num, 0);
 | 
			
		||||
 | 
			
		||||
        sectors_per_block = s->block_size >> BDRV_SECTOR_BITS;
 | 
			
		||||
        sectors = sectors_per_block - (sector_num % sectors_per_block);
 | 
			
		||||
        if (sectors > nb_sectors) {
 | 
			
		||||
            sectors = nb_sectors;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (offset == -1) {
 | 
			
		||||
            memset(buf, 0, sectors * BDRV_SECTOR_SIZE);
 | 
			
		||||
        } else {
 | 
			
		||||
            ret = bdrv_pread(bs->file, offset, buf,
 | 
			
		||||
                sectors * BDRV_SECTOR_SIZE);
 | 
			
		||||
            if (ret != sectors * BDRV_SECTOR_SIZE) {
 | 
			
		||||
                return -1;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        nb_sectors -= sectors;
 | 
			
		||||
        sector_num += sectors;
 | 
			
		||||
        buf += sectors * BDRV_SECTOR_SIZE;
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int vpc_write(BlockDriverState *bs, int64_t sector_num,
 | 
			
		||||
    const uint8_t *buf, int nb_sectors)
 | 
			
		||||
{
 | 
			
		||||
    BDRVVPCState *s = bs->opaque;
 | 
			
		||||
    int64_t offset;
 | 
			
		||||
    int64_t sectors, sectors_per_block;
 | 
			
		||||
    int ret;
 | 
			
		||||
 | 
			
		||||
    while (nb_sectors > 0) {
 | 
			
		||||
        offset = get_sector_offset(bs, sector_num, 1);
 | 
			
		||||
 | 
			
		||||
        sectors_per_block = s->block_size >> BDRV_SECTOR_BITS;
 | 
			
		||||
        sectors = sectors_per_block - (sector_num % sectors_per_block);
 | 
			
		||||
        if (sectors > nb_sectors) {
 | 
			
		||||
            sectors = nb_sectors;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (offset == -1) {
 | 
			
		||||
            offset = alloc_block(bs, sector_num);
 | 
			
		||||
            if (offset < 0)
 | 
			
		||||
                return -1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ret = bdrv_pwrite(bs->file, offset, buf, sectors * BDRV_SECTOR_SIZE);
 | 
			
		||||
        if (ret != sectors * BDRV_SECTOR_SIZE) {
 | 
			
		||||
            return -1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        nb_sectors -= sectors;
 | 
			
		||||
        sector_num += sectors;
 | 
			
		||||
        buf += sectors * BDRV_SECTOR_SIZE;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Calculates the number of cylinders, heads and sectors per cylinder
 | 
			
		||||
 * based on a given number of sectors. This is the algorithm described
 | 
			
		||||
 * in the VHD specification.
 | 
			
		||||
 *
 | 
			
		||||
 * Note that the geometry doesn't always exactly match total_sectors but
 | 
			
		||||
 * may round it down.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns 0 on success, -EFBIG if the size is larger than 127 GB
 | 
			
		||||
 */
 | 
			
		||||
static int calculate_geometry(int64_t total_sectors, uint16_t* cyls,
 | 
			
		||||
    uint8_t* heads, uint8_t* secs_per_cyl)
 | 
			
		||||
{
 | 
			
		||||
    uint32_t cyls_times_heads;
 | 
			
		||||
 | 
			
		||||
    if (total_sectors > 65535 * 16 * 255)
 | 
			
		||||
        return -EFBIG;
 | 
			
		||||
 | 
			
		||||
    if (total_sectors > 65535 * 16 * 63) {
 | 
			
		||||
        *secs_per_cyl = 255;
 | 
			
		||||
        *heads = 16;
 | 
			
		||||
        cyls_times_heads = total_sectors / *secs_per_cyl;
 | 
			
		||||
    } else {
 | 
			
		||||
        *secs_per_cyl = 17;
 | 
			
		||||
        cyls_times_heads = total_sectors / *secs_per_cyl;
 | 
			
		||||
        *heads = (cyls_times_heads + 1023) / 1024;
 | 
			
		||||
 | 
			
		||||
        if (*heads < 4)
 | 
			
		||||
            *heads = 4;
 | 
			
		||||
 | 
			
		||||
        if (cyls_times_heads >= (*heads * 1024) || *heads > 16) {
 | 
			
		||||
            *secs_per_cyl = 31;
 | 
			
		||||
            *heads = 16;
 | 
			
		||||
            cyls_times_heads = total_sectors / *secs_per_cyl;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (cyls_times_heads >= (*heads * 1024)) {
 | 
			
		||||
            *secs_per_cyl = 63;
 | 
			
		||||
            *heads = 16;
 | 
			
		||||
            cyls_times_heads = total_sectors / *secs_per_cyl;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    *cyls = cyls_times_heads / *heads;
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int vpc_create(const char *filename, QEMUOptionParameter *options)
 | 
			
		||||
{
 | 
			
		||||
    uint8_t buf[1024];
 | 
			
		||||
    struct vhd_footer* footer = (struct vhd_footer*) buf;
 | 
			
		||||
    struct vhd_dyndisk_header* dyndisk_header =
 | 
			
		||||
        (struct vhd_dyndisk_header*) buf;
 | 
			
		||||
    int fd, i;
 | 
			
		||||
    uint16_t cyls = 0;
 | 
			
		||||
    uint8_t heads = 0;
 | 
			
		||||
    uint8_t secs_per_cyl = 0;
 | 
			
		||||
    size_t block_size, num_bat_entries;
 | 
			
		||||
    int64_t total_sectors = 0;
 | 
			
		||||
 | 
			
		||||
    // Read out options
 | 
			
		||||
    while (options && options->name) {
 | 
			
		||||
        if (!strcmp(options->name, "size")) {
 | 
			
		||||
            total_sectors = options->value.n / 512;
 | 
			
		||||
        }
 | 
			
		||||
        options++;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Create the file
 | 
			
		||||
    fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
 | 
			
		||||
    if (fd < 0)
 | 
			
		||||
        return -EIO;
 | 
			
		||||
 | 
			
		||||
    /* Calculate matching total_size and geometry. Increase the number of
 | 
			
		||||
       sectors requested until we get enough (or fail). */
 | 
			
		||||
    for (i = 0; total_sectors > (int64_t)cyls * heads * secs_per_cyl; i++) {
 | 
			
		||||
        if (calculate_geometry(total_sectors + i,
 | 
			
		||||
                               &cyls, &heads, &secs_per_cyl)) {
 | 
			
		||||
            return -EFBIG;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    total_sectors = (int64_t) cyls * heads * secs_per_cyl;
 | 
			
		||||
 | 
			
		||||
    // Prepare the Hard Disk Footer
 | 
			
		||||
    memset(buf, 0, 1024);
 | 
			
		||||
 | 
			
		||||
    memcpy(footer->creator, "conectix", 8);
 | 
			
		||||
    // TODO Check if "qemu" creator_app is ok for VPC
 | 
			
		||||
    memcpy(footer->creator_app, "qemu", 4);
 | 
			
		||||
    memcpy(footer->creator_os, "Wi2k", 4);
 | 
			
		||||
 | 
			
		||||
    footer->features = be32_to_cpu(0x02);
 | 
			
		||||
    footer->version = be32_to_cpu(0x00010000);
 | 
			
		||||
    footer->data_offset = be64_to_cpu(HEADER_SIZE);
 | 
			
		||||
    footer->timestamp = be32_to_cpu(time(NULL) - VHD_TIMESTAMP_BASE);
 | 
			
		||||
 | 
			
		||||
    // Version of Virtual PC 2007
 | 
			
		||||
    footer->major = be16_to_cpu(0x0005);
 | 
			
		||||
    footer->minor =be16_to_cpu(0x0003);
 | 
			
		||||
 | 
			
		||||
    footer->orig_size = be64_to_cpu(total_sectors * 512);
 | 
			
		||||
    footer->size = be64_to_cpu(total_sectors * 512);
 | 
			
		||||
 | 
			
		||||
    footer->cyls = be16_to_cpu(cyls);
 | 
			
		||||
    footer->heads = heads;
 | 
			
		||||
    footer->secs_per_cyl = secs_per_cyl;
 | 
			
		||||
 | 
			
		||||
    footer->type = be32_to_cpu(VHD_DYNAMIC);
 | 
			
		||||
 | 
			
		||||
    // TODO uuid is missing
 | 
			
		||||
 | 
			
		||||
    footer->checksum = be32_to_cpu(vpc_checksum(buf, HEADER_SIZE));
 | 
			
		||||
 | 
			
		||||
    // Write the footer (twice: at the beginning and at the end)
 | 
			
		||||
    block_size = 0x200000;
 | 
			
		||||
    num_bat_entries = (total_sectors + block_size / 512) / (block_size / 512);
 | 
			
		||||
 | 
			
		||||
    if (write(fd, buf, HEADER_SIZE) != HEADER_SIZE)
 | 
			
		||||
        return -EIO;
 | 
			
		||||
 | 
			
		||||
    if (lseek(fd, 1536 + ((num_bat_entries * 4 + 511) & ~511), SEEK_SET) < 0)
 | 
			
		||||
        return -EIO;
 | 
			
		||||
    if (write(fd, buf, HEADER_SIZE) != HEADER_SIZE)
 | 
			
		||||
        return -EIO;
 | 
			
		||||
 | 
			
		||||
    // Write the initial BAT
 | 
			
		||||
    if (lseek(fd, 3 * 512, SEEK_SET) < 0)
 | 
			
		||||
        return -EIO;
 | 
			
		||||
 | 
			
		||||
    memset(buf, 0xFF, 512);
 | 
			
		||||
    for (i = 0; i < (num_bat_entries * 4 + 511) / 512; i++)
 | 
			
		||||
        if (write(fd, buf, 512) != 512)
 | 
			
		||||
            return -EIO;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    // Prepare the Dynamic Disk Header
 | 
			
		||||
    memset(buf, 0, 1024);
 | 
			
		||||
 | 
			
		||||
    memcpy(dyndisk_header->magic, "cxsparse", 8);
 | 
			
		||||
 | 
			
		||||
    dyndisk_header->data_offset = be64_to_cpu(0xFFFFFFFF);
 | 
			
		||||
    dyndisk_header->table_offset = be64_to_cpu(3 * 512);
 | 
			
		||||
    dyndisk_header->version = be32_to_cpu(0x00010000);
 | 
			
		||||
    dyndisk_header->block_size = be32_to_cpu(block_size);
 | 
			
		||||
    dyndisk_header->max_table_entries = be32_to_cpu(num_bat_entries);
 | 
			
		||||
 | 
			
		||||
    dyndisk_header->checksum = be32_to_cpu(vpc_checksum(buf, 1024));
 | 
			
		||||
 | 
			
		||||
    // Write the header
 | 
			
		||||
    if (lseek(fd, 512, SEEK_SET) < 0)
 | 
			
		||||
        return -EIO;
 | 
			
		||||
    if (write(fd, buf, 1024) != 1024)
 | 
			
		||||
        return -EIO;
 | 
			
		||||
 | 
			
		||||
    close(fd);
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void vpc_close(BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
    BDRVVPCState *s = bs->opaque;
 | 
			
		||||
    qemu_free(s->pagetable);
 | 
			
		||||
#ifdef CACHE
 | 
			
		||||
    qemu_free(s->pageentry_u8);
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static QEMUOptionParameter vpc_create_options[] = {
 | 
			
		||||
    {
 | 
			
		||||
        .name = BLOCK_OPT_SIZE,
 | 
			
		||||
        .type = OPT_SIZE,
 | 
			
		||||
        .help = "Virtual disk size"
 | 
			
		||||
    },
 | 
			
		||||
    { NULL }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static BlockDriver bdrv_vpc = {
 | 
			
		||||
    .format_name	= "vpc",
 | 
			
		||||
    .instance_size	= sizeof(BDRVVPCState),
 | 
			
		||||
    .bdrv_probe		= vpc_probe,
 | 
			
		||||
    .bdrv_open		= vpc_open,
 | 
			
		||||
    .bdrv_read		= vpc_read,
 | 
			
		||||
    .bdrv_write		= vpc_write,
 | 
			
		||||
    .bdrv_close		= vpc_close,
 | 
			
		||||
    .bdrv_create	= vpc_create,
 | 
			
		||||
 | 
			
		||||
    .create_options = vpc_create_options,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void bdrv_vpc_init(void)
 | 
			
		||||
{
 | 
			
		||||
    bdrv_register(&bdrv_vpc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
block_init(bdrv_vpc_init);
 | 
			
		||||
							
								
								
									
										192
									
								
								block_int.h
									
									
									
									
									
								
							
							
						
						
									
										192
									
								
								block_int.h
									
									
									
									
									
								
							@@ -24,229 +24,57 @@
 | 
			
		||||
#ifndef BLOCK_INT_H
 | 
			
		||||
#define BLOCK_INT_H
 | 
			
		||||
 | 
			
		||||
#include "block.h"
 | 
			
		||||
#include "qemu-option.h"
 | 
			
		||||
#include "qemu-queue.h"
 | 
			
		||||
 | 
			
		||||
#define BLOCK_FLAG_ENCRYPT	1
 | 
			
		||||
#define BLOCK_FLAG_COMPRESS	2
 | 
			
		||||
#define BLOCK_FLAG_COMPAT6	4
 | 
			
		||||
 | 
			
		||||
#define BLOCK_OPT_SIZE          "size"
 | 
			
		||||
#define BLOCK_OPT_ENCRYPT       "encryption"
 | 
			
		||||
#define BLOCK_OPT_COMPAT6       "compat6"
 | 
			
		||||
#define BLOCK_OPT_BACKING_FILE  "backing_file"
 | 
			
		||||
#define BLOCK_OPT_BACKING_FMT   "backing_fmt"
 | 
			
		||||
#define BLOCK_OPT_CLUSTER_SIZE  "cluster_size"
 | 
			
		||||
#define BLOCK_OPT_PREALLOC      "preallocation"
 | 
			
		||||
 | 
			
		||||
typedef struct AIOPool {
 | 
			
		||||
    void (*cancel)(BlockDriverAIOCB *acb);
 | 
			
		||||
    int aiocb_size;
 | 
			
		||||
    BlockDriverAIOCB *free_aiocb;
 | 
			
		||||
} AIOPool;
 | 
			
		||||
 | 
			
		||||
struct BlockDriver {
 | 
			
		||||
    const char *format_name;
 | 
			
		||||
    int instance_size;
 | 
			
		||||
    int (*bdrv_probe)(const uint8_t *buf, int buf_size, const char *filename);
 | 
			
		||||
    int (*bdrv_probe_device)(const char *filename);
 | 
			
		||||
    int (*bdrv_open)(BlockDriverState *bs, int flags);
 | 
			
		||||
    int (*bdrv_file_open)(BlockDriverState *bs, const char *filename, int flags);
 | 
			
		||||
    int (*bdrv_open)(BlockDriverState *bs, const char *filename);
 | 
			
		||||
    int (*bdrv_read)(BlockDriverState *bs, int64_t sector_num, 
 | 
			
		||||
                     uint8_t *buf, int nb_sectors);
 | 
			
		||||
    int (*bdrv_write)(BlockDriverState *bs, int64_t sector_num, 
 | 
			
		||||
                      const uint8_t *buf, int nb_sectors);
 | 
			
		||||
    void (*bdrv_close)(BlockDriverState *bs);
 | 
			
		||||
    int (*bdrv_create)(const char *filename, QEMUOptionParameter *options);
 | 
			
		||||
    void (*bdrv_flush)(BlockDriverState *bs);
 | 
			
		||||
    int (*bdrv_create)(const char *filename, int64_t total_sectors, 
 | 
			
		||||
                       const char *backing_file, int flags);
 | 
			
		||||
    int (*bdrv_is_allocated)(BlockDriverState *bs, int64_t sector_num,
 | 
			
		||||
                             int nb_sectors, int *pnum);
 | 
			
		||||
    int (*bdrv_set_key)(BlockDriverState *bs, const char *key);
 | 
			
		||||
    int (*bdrv_make_empty)(BlockDriverState *bs);
 | 
			
		||||
    /* aio */
 | 
			
		||||
    BlockDriverAIOCB *(*bdrv_aio_readv)(BlockDriverState *bs,
 | 
			
		||||
        int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
 | 
			
		||||
        BlockDriverCompletionFunc *cb, void *opaque);
 | 
			
		||||
    BlockDriverAIOCB *(*bdrv_aio_writev)(BlockDriverState *bs,
 | 
			
		||||
        int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
 | 
			
		||||
        BlockDriverCompletionFunc *cb, void *opaque);
 | 
			
		||||
    BlockDriverAIOCB *(*bdrv_aio_flush)(BlockDriverState *bs,
 | 
			
		||||
        BlockDriverCompletionFunc *cb, void *opaque);
 | 
			
		||||
 | 
			
		||||
    int (*bdrv_aio_multiwrite)(BlockDriverState *bs, BlockRequest *reqs,
 | 
			
		||||
        int num_reqs);
 | 
			
		||||
    int (*bdrv_merge_requests)(BlockDriverState *bs, BlockRequest* a,
 | 
			
		||||
        BlockRequest *b);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    const char *protocol_name;
 | 
			
		||||
    int (*bdrv_truncate)(BlockDriverState *bs, int64_t offset);
 | 
			
		||||
    int64_t (*bdrv_getlength)(BlockDriverState *bs);
 | 
			
		||||
    int (*bdrv_write_compressed)(BlockDriverState *bs, int64_t sector_num,
 | 
			
		||||
                                 const uint8_t *buf, int nb_sectors);
 | 
			
		||||
 | 
			
		||||
    int (*bdrv_snapshot_create)(BlockDriverState *bs,
 | 
			
		||||
                                QEMUSnapshotInfo *sn_info);
 | 
			
		||||
    int (*bdrv_snapshot_goto)(BlockDriverState *bs,
 | 
			
		||||
                              const char *snapshot_id);
 | 
			
		||||
    int (*bdrv_snapshot_delete)(BlockDriverState *bs, const char *snapshot_id);
 | 
			
		||||
    int (*bdrv_snapshot_list)(BlockDriverState *bs,
 | 
			
		||||
                              QEMUSnapshotInfo **psn_info);
 | 
			
		||||
    int (*bdrv_get_info)(BlockDriverState *bs, BlockDriverInfo *bdi);
 | 
			
		||||
 | 
			
		||||
    int (*bdrv_save_vmstate)(BlockDriverState *bs, const uint8_t *buf,
 | 
			
		||||
                             int64_t pos, int size);
 | 
			
		||||
    int (*bdrv_load_vmstate)(BlockDriverState *bs, uint8_t *buf,
 | 
			
		||||
                             int64_t pos, int size);
 | 
			
		||||
 | 
			
		||||
    int (*bdrv_change_backing_file)(BlockDriverState *bs,
 | 
			
		||||
        const char *backing_file, const char *backing_fmt);
 | 
			
		||||
 | 
			
		||||
    /* removable device specific */
 | 
			
		||||
    int (*bdrv_is_inserted)(BlockDriverState *bs);
 | 
			
		||||
    int (*bdrv_media_changed)(BlockDriverState *bs);
 | 
			
		||||
    int (*bdrv_eject)(BlockDriverState *bs, int eject_flag);
 | 
			
		||||
    int (*bdrv_set_locked)(BlockDriverState *bs, int locked);
 | 
			
		||||
 | 
			
		||||
    /* to control generic scsi devices */
 | 
			
		||||
    int (*bdrv_ioctl)(BlockDriverState *bs, unsigned long int req, void *buf);
 | 
			
		||||
    BlockDriverAIOCB *(*bdrv_aio_ioctl)(BlockDriverState *bs,
 | 
			
		||||
        unsigned long int req, void *buf,
 | 
			
		||||
        BlockDriverCompletionFunc *cb, void *opaque);
 | 
			
		||||
 | 
			
		||||
    /* List of options for creating images, terminated by name == NULL */
 | 
			
		||||
    QEMUOptionParameter *create_options;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
     * Returns 0 for completed check, -errno for internal errors.
 | 
			
		||||
     * The check results are stored in result.
 | 
			
		||||
     */
 | 
			
		||||
    int (*bdrv_check)(BlockDriverState* bs, BdrvCheckResult *result);
 | 
			
		||||
 | 
			
		||||
    void (*bdrv_debug_event)(BlockDriverState *bs, BlkDebugEvent event);
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
     * Returns 1 if newly created images are guaranteed to contain only
 | 
			
		||||
     * zeros, 0 otherwise.
 | 
			
		||||
     */
 | 
			
		||||
    int (*bdrv_has_zero_init)(BlockDriverState *bs);
 | 
			
		||||
 | 
			
		||||
    QLIST_ENTRY(BlockDriver) list;
 | 
			
		||||
    struct BlockDriver *next;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct BlockDriverState {
 | 
			
		||||
    int64_t total_sectors; /* if we are reading a disk image, give its
 | 
			
		||||
                              size in sectors */
 | 
			
		||||
    int64_t total_sectors;
 | 
			
		||||
    int read_only; /* if true, the media is read only */
 | 
			
		||||
    int keep_read_only; /* if true, the media was requested to stay read only */
 | 
			
		||||
    int open_flags; /* flags used to open the file, re-used for re-open */
 | 
			
		||||
    int inserted; /* if true, the media is present */
 | 
			
		||||
    int removable; /* if true, the media can be removed */
 | 
			
		||||
    int locked;    /* if true, the media cannot temporarily be ejected */
 | 
			
		||||
    int tray_open; /* if true, the virtual tray is open */
 | 
			
		||||
    int encrypted; /* if true, the media is encrypted */
 | 
			
		||||
    int valid_key; /* if true, a valid encryption key has been set */
 | 
			
		||||
    int sg;        /* if true, the device is a /dev/sg* */
 | 
			
		||||
    /* event callback when inserting/removing */
 | 
			
		||||
    void (*change_cb)(void *opaque);
 | 
			
		||||
    void *change_opaque;
 | 
			
		||||
 | 
			
		||||
    BlockDriver *drv; /* NULL means no media */
 | 
			
		||||
    BlockDriver *drv;
 | 
			
		||||
    void *opaque;
 | 
			
		||||
 | 
			
		||||
    DeviceState *peer;
 | 
			
		||||
    int boot_sector_enabled;
 | 
			
		||||
    uint8_t boot_sector_data[512];
 | 
			
		||||
 | 
			
		||||
    char filename[1024];
 | 
			
		||||
    char backing_file[1024]; /* if non zero, the image is a diff of
 | 
			
		||||
                                this file image */
 | 
			
		||||
    char backing_format[16]; /* if non-zero and backing_file exists */
 | 
			
		||||
    int is_temporary;
 | 
			
		||||
    int media_changed;
 | 
			
		||||
    
 | 
			
		||||
    BlockDriverState *backing_hd;
 | 
			
		||||
    BlockDriverState *file;
 | 
			
		||||
 | 
			
		||||
    /* async read/write emulation */
 | 
			
		||||
 | 
			
		||||
    void *sync_aiocb;
 | 
			
		||||
 | 
			
		||||
    /* I/O stats (display with "info blockstats"). */
 | 
			
		||||
    uint64_t rd_bytes;
 | 
			
		||||
    uint64_t wr_bytes;
 | 
			
		||||
    uint64_t rd_ops;
 | 
			
		||||
    uint64_t wr_ops;
 | 
			
		||||
    uint64_t wr_highest_sector;
 | 
			
		||||
 | 
			
		||||
    /* Whether the disk can expand beyond total_sectors */
 | 
			
		||||
    int growable;
 | 
			
		||||
 | 
			
		||||
    /* the memory alignment required for the buffers handled by this driver */
 | 
			
		||||
    int buffer_alignment;
 | 
			
		||||
 | 
			
		||||
    /* do we need to tell the quest if we have a volatile write cache? */
 | 
			
		||||
    int enable_write_cache;
 | 
			
		||||
    
 | 
			
		||||
    /* NOTE: the following infos are only hints for real hardware
 | 
			
		||||
       drivers. They are not used by the block driver */
 | 
			
		||||
    int cyls, heads, secs, translation;
 | 
			
		||||
    int type;
 | 
			
		||||
    BlockErrorAction on_read_error, on_write_error;
 | 
			
		||||
    char device_name[32];
 | 
			
		||||
    unsigned long *dirty_bitmap;
 | 
			
		||||
    int64_t dirty_count;
 | 
			
		||||
    QTAILQ_ENTRY(BlockDriverState) list;
 | 
			
		||||
    void *private;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct BlockDriverAIOCB {
 | 
			
		||||
    AIOPool *pool;
 | 
			
		||||
    BlockDriverState *bs;
 | 
			
		||||
    BlockDriverCompletionFunc *cb;
 | 
			
		||||
    void *opaque;
 | 
			
		||||
    BlockDriverAIOCB *next;
 | 
			
		||||
    BlockDriverState *next;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void get_tmp_filename(char *filename, int size);
 | 
			
		||||
 | 
			
		||||
void *qemu_aio_get(AIOPool *pool, BlockDriverState *bs,
 | 
			
		||||
                   BlockDriverCompletionFunc *cb, void *opaque);
 | 
			
		||||
void qemu_aio_release(void *p);
 | 
			
		||||
 | 
			
		||||
void *qemu_blockalign(BlockDriverState *bs, size_t size);
 | 
			
		||||
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
int is_windows_drive(const char *filename);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
typedef struct BlockConf {
 | 
			
		||||
    BlockDriverState *bs;
 | 
			
		||||
    uint16_t physical_block_size;
 | 
			
		||||
    uint16_t logical_block_size;
 | 
			
		||||
    uint16_t min_io_size;
 | 
			
		||||
    uint32_t opt_io_size;
 | 
			
		||||
} BlockConf;
 | 
			
		||||
 | 
			
		||||
static inline unsigned int get_physical_block_exp(BlockConf *conf)
 | 
			
		||||
{
 | 
			
		||||
    unsigned int exp = 0, size;
 | 
			
		||||
 | 
			
		||||
    for (size = conf->physical_block_size;
 | 
			
		||||
        size > conf->logical_block_size;
 | 
			
		||||
        size >>= 1) {
 | 
			
		||||
        exp++;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return exp;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define DEFINE_BLOCK_PROPERTIES(_state, _conf)                          \
 | 
			
		||||
    DEFINE_PROP_DRIVE("drive", _state, _conf.bs),                       \
 | 
			
		||||
    DEFINE_PROP_UINT16("logical_block_size", _state,                    \
 | 
			
		||||
                       _conf.logical_block_size, 512),                  \
 | 
			
		||||
    DEFINE_PROP_UINT16("physical_block_size", _state,                   \
 | 
			
		||||
                       _conf.physical_block_size, 512),                 \
 | 
			
		||||
    DEFINE_PROP_UINT16("min_io_size", _state, _conf.min_io_size, 0),  \
 | 
			
		||||
    DEFINE_PROP_UINT32("opt_io_size", _state, _conf.opt_io_size, 0)
 | 
			
		||||
 | 
			
		||||
#endif /* BLOCK_INT_H */
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										599
									
								
								blockdev.c
									
									
									
									
									
								
							
							
						
						
									
										599
									
								
								blockdev.c
									
									
									
									
									
								
							@@ -1,599 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * QEMU host block devices
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2003-2008 Fabrice Bellard
 | 
			
		||||
 *
 | 
			
		||||
 * 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 "block.h"
 | 
			
		||||
#include "blockdev.h"
 | 
			
		||||
#include "monitor.h"
 | 
			
		||||
#include "qerror.h"
 | 
			
		||||
#include "qemu-option.h"
 | 
			
		||||
#include "qemu-config.h"
 | 
			
		||||
#include "sysemu.h"
 | 
			
		||||
 | 
			
		||||
static QTAILQ_HEAD(drivelist, DriveInfo) drives = QTAILQ_HEAD_INITIALIZER(drives);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * We automatically delete the drive when a device using it gets
 | 
			
		||||
 * unplugged.  Questionable feature, but we can't just drop it.
 | 
			
		||||
 * Device models call blockdev_mark_auto_del() to schedule the
 | 
			
		||||
 * automatic deletion, and generic qdev code calls blockdev_auto_del()
 | 
			
		||||
 * when deletion is actually safe.
 | 
			
		||||
 */
 | 
			
		||||
void blockdev_mark_auto_del(BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
    DriveInfo *dinfo = drive_get_by_blockdev(bs);
 | 
			
		||||
 | 
			
		||||
    dinfo->auto_del = 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void blockdev_auto_del(BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
    DriveInfo *dinfo = drive_get_by_blockdev(bs);
 | 
			
		||||
 | 
			
		||||
    if (dinfo->auto_del) {
 | 
			
		||||
        drive_uninit(dinfo);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QemuOpts *drive_add(const char *file, const char *fmt, ...)
 | 
			
		||||
{
 | 
			
		||||
    va_list ap;
 | 
			
		||||
    char optstr[1024];
 | 
			
		||||
    QemuOpts *opts;
 | 
			
		||||
 | 
			
		||||
    va_start(ap, fmt);
 | 
			
		||||
    vsnprintf(optstr, sizeof(optstr), fmt, ap);
 | 
			
		||||
    va_end(ap);
 | 
			
		||||
 | 
			
		||||
    opts = qemu_opts_parse(&qemu_drive_opts, optstr, 0);
 | 
			
		||||
    if (!opts) {
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
    if (file)
 | 
			
		||||
        qemu_opt_set(opts, "file", file);
 | 
			
		||||
    return opts;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DriveInfo *drive_get(BlockInterfaceType type, int bus, int unit)
 | 
			
		||||
{
 | 
			
		||||
    DriveInfo *dinfo;
 | 
			
		||||
 | 
			
		||||
    /* seek interface, bus and unit */
 | 
			
		||||
 | 
			
		||||
    QTAILQ_FOREACH(dinfo, &drives, next) {
 | 
			
		||||
        if (dinfo->type == type &&
 | 
			
		||||
	    dinfo->bus == bus &&
 | 
			
		||||
	    dinfo->unit == unit)
 | 
			
		||||
            return dinfo;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int drive_get_max_bus(BlockInterfaceType type)
 | 
			
		||||
{
 | 
			
		||||
    int max_bus;
 | 
			
		||||
    DriveInfo *dinfo;
 | 
			
		||||
 | 
			
		||||
    max_bus = -1;
 | 
			
		||||
    QTAILQ_FOREACH(dinfo, &drives, next) {
 | 
			
		||||
        if(dinfo->type == type &&
 | 
			
		||||
           dinfo->bus > max_bus)
 | 
			
		||||
            max_bus = dinfo->bus;
 | 
			
		||||
    }
 | 
			
		||||
    return max_bus;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DriveInfo *drive_get_by_blockdev(BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
    DriveInfo *dinfo;
 | 
			
		||||
 | 
			
		||||
    QTAILQ_FOREACH(dinfo, &drives, next) {
 | 
			
		||||
        if (dinfo->bdrv == bs) {
 | 
			
		||||
            return dinfo;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void bdrv_format_print(void *opaque, const char *name)
 | 
			
		||||
{
 | 
			
		||||
    fprintf(stderr, " %s", name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void drive_uninit(DriveInfo *dinfo)
 | 
			
		||||
{
 | 
			
		||||
    qemu_opts_del(dinfo->opts);
 | 
			
		||||
    bdrv_delete(dinfo->bdrv);
 | 
			
		||||
    QTAILQ_REMOVE(&drives, dinfo, next);
 | 
			
		||||
    qemu_free(dinfo);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int parse_block_error_action(const char *buf, int is_read)
 | 
			
		||||
{
 | 
			
		||||
    if (!strcmp(buf, "ignore")) {
 | 
			
		||||
        return BLOCK_ERR_IGNORE;
 | 
			
		||||
    } else if (!is_read && !strcmp(buf, "enospc")) {
 | 
			
		||||
        return BLOCK_ERR_STOP_ENOSPC;
 | 
			
		||||
    } else if (!strcmp(buf, "stop")) {
 | 
			
		||||
        return BLOCK_ERR_STOP_ANY;
 | 
			
		||||
    } else if (!strcmp(buf, "report")) {
 | 
			
		||||
        return BLOCK_ERR_REPORT;
 | 
			
		||||
    } else {
 | 
			
		||||
        fprintf(stderr, "qemu: '%s' invalid %s error action\n",
 | 
			
		||||
            buf, is_read ? "read" : "write");
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi, int *fatal_error)
 | 
			
		||||
{
 | 
			
		||||
    const char *buf;
 | 
			
		||||
    const char *file = NULL;
 | 
			
		||||
    char devname[128];
 | 
			
		||||
    const char *serial;
 | 
			
		||||
    const char *mediastr = "";
 | 
			
		||||
    BlockInterfaceType type;
 | 
			
		||||
    enum { MEDIA_DISK, MEDIA_CDROM } media;
 | 
			
		||||
    int bus_id, unit_id;
 | 
			
		||||
    int cyls, heads, secs, translation;
 | 
			
		||||
    BlockDriver *drv = NULL;
 | 
			
		||||
    int max_devs;
 | 
			
		||||
    int index;
 | 
			
		||||
    int ro = 0;
 | 
			
		||||
    int bdrv_flags = 0;
 | 
			
		||||
    int on_read_error, on_write_error;
 | 
			
		||||
    const char *devaddr;
 | 
			
		||||
    DriveInfo *dinfo;
 | 
			
		||||
    int snapshot = 0;
 | 
			
		||||
    int ret;
 | 
			
		||||
 | 
			
		||||
    *fatal_error = 1;
 | 
			
		||||
 | 
			
		||||
    translation = BIOS_ATA_TRANSLATION_AUTO;
 | 
			
		||||
 | 
			
		||||
    if (default_to_scsi) {
 | 
			
		||||
        type = IF_SCSI;
 | 
			
		||||
        max_devs = MAX_SCSI_DEVS;
 | 
			
		||||
        pstrcpy(devname, sizeof(devname), "scsi");
 | 
			
		||||
    } else {
 | 
			
		||||
        type = IF_IDE;
 | 
			
		||||
        max_devs = MAX_IDE_DEVS;
 | 
			
		||||
        pstrcpy(devname, sizeof(devname), "ide");
 | 
			
		||||
    }
 | 
			
		||||
    media = MEDIA_DISK;
 | 
			
		||||
 | 
			
		||||
    /* extract parameters */
 | 
			
		||||
    bus_id  = qemu_opt_get_number(opts, "bus", 0);
 | 
			
		||||
    unit_id = qemu_opt_get_number(opts, "unit", -1);
 | 
			
		||||
    index   = qemu_opt_get_number(opts, "index", -1);
 | 
			
		||||
 | 
			
		||||
    cyls  = qemu_opt_get_number(opts, "cyls", 0);
 | 
			
		||||
    heads = qemu_opt_get_number(opts, "heads", 0);
 | 
			
		||||
    secs  = qemu_opt_get_number(opts, "secs", 0);
 | 
			
		||||
 | 
			
		||||
    snapshot = qemu_opt_get_bool(opts, "snapshot", 0);
 | 
			
		||||
    ro = qemu_opt_get_bool(opts, "readonly", 0);
 | 
			
		||||
 | 
			
		||||
    file = qemu_opt_get(opts, "file");
 | 
			
		||||
    serial = qemu_opt_get(opts, "serial");
 | 
			
		||||
 | 
			
		||||
    if ((buf = qemu_opt_get(opts, "if")) != NULL) {
 | 
			
		||||
        pstrcpy(devname, sizeof(devname), buf);
 | 
			
		||||
        if (!strcmp(buf, "ide")) {
 | 
			
		||||
	    type = IF_IDE;
 | 
			
		||||
            max_devs = MAX_IDE_DEVS;
 | 
			
		||||
        } else if (!strcmp(buf, "scsi")) {
 | 
			
		||||
	    type = IF_SCSI;
 | 
			
		||||
            max_devs = MAX_SCSI_DEVS;
 | 
			
		||||
        } else if (!strcmp(buf, "floppy")) {
 | 
			
		||||
	    type = IF_FLOPPY;
 | 
			
		||||
            max_devs = 0;
 | 
			
		||||
        } else if (!strcmp(buf, "pflash")) {
 | 
			
		||||
	    type = IF_PFLASH;
 | 
			
		||||
            max_devs = 0;
 | 
			
		||||
	} else if (!strcmp(buf, "mtd")) {
 | 
			
		||||
	    type = IF_MTD;
 | 
			
		||||
            max_devs = 0;
 | 
			
		||||
	} else if (!strcmp(buf, "sd")) {
 | 
			
		||||
	    type = IF_SD;
 | 
			
		||||
            max_devs = 0;
 | 
			
		||||
        } else if (!strcmp(buf, "virtio")) {
 | 
			
		||||
            type = IF_VIRTIO;
 | 
			
		||||
            max_devs = 0;
 | 
			
		||||
	} else if (!strcmp(buf, "xen")) {
 | 
			
		||||
	    type = IF_XEN;
 | 
			
		||||
            max_devs = 0;
 | 
			
		||||
	} else if (!strcmp(buf, "none")) {
 | 
			
		||||
	    type = IF_NONE;
 | 
			
		||||
            max_devs = 0;
 | 
			
		||||
	} else {
 | 
			
		||||
            fprintf(stderr, "qemu: unsupported bus type '%s'\n", buf);
 | 
			
		||||
            return NULL;
 | 
			
		||||
	}
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (cyls || heads || secs) {
 | 
			
		||||
        if (cyls < 1 || (type == IF_IDE && cyls > 16383)) {
 | 
			
		||||
            fprintf(stderr, "qemu: '%s' invalid physical cyls number\n", buf);
 | 
			
		||||
	    return NULL;
 | 
			
		||||
	}
 | 
			
		||||
        if (heads < 1 || (type == IF_IDE && heads > 16)) {
 | 
			
		||||
            fprintf(stderr, "qemu: '%s' invalid physical heads number\n", buf);
 | 
			
		||||
	    return NULL;
 | 
			
		||||
	}
 | 
			
		||||
        if (secs < 1 || (type == IF_IDE && secs > 63)) {
 | 
			
		||||
            fprintf(stderr, "qemu: '%s' invalid physical secs number\n", buf);
 | 
			
		||||
	    return NULL;
 | 
			
		||||
	}
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ((buf = qemu_opt_get(opts, "trans")) != NULL) {
 | 
			
		||||
        if (!cyls) {
 | 
			
		||||
            fprintf(stderr,
 | 
			
		||||
                    "qemu: '%s' trans must be used with cyls,heads and secs\n",
 | 
			
		||||
                    buf);
 | 
			
		||||
            return NULL;
 | 
			
		||||
        }
 | 
			
		||||
        if (!strcmp(buf, "none"))
 | 
			
		||||
            translation = BIOS_ATA_TRANSLATION_NONE;
 | 
			
		||||
        else if (!strcmp(buf, "lba"))
 | 
			
		||||
            translation = BIOS_ATA_TRANSLATION_LBA;
 | 
			
		||||
        else if (!strcmp(buf, "auto"))
 | 
			
		||||
            translation = BIOS_ATA_TRANSLATION_AUTO;
 | 
			
		||||
	else {
 | 
			
		||||
            fprintf(stderr, "qemu: '%s' invalid translation type\n", buf);
 | 
			
		||||
	    return NULL;
 | 
			
		||||
	}
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ((buf = qemu_opt_get(opts, "media")) != NULL) {
 | 
			
		||||
        if (!strcmp(buf, "disk")) {
 | 
			
		||||
	    media = MEDIA_DISK;
 | 
			
		||||
	} else if (!strcmp(buf, "cdrom")) {
 | 
			
		||||
            if (cyls || secs || heads) {
 | 
			
		||||
                fprintf(stderr,
 | 
			
		||||
                        "qemu: '%s' invalid physical CHS format\n", buf);
 | 
			
		||||
	        return NULL;
 | 
			
		||||
            }
 | 
			
		||||
	    media = MEDIA_CDROM;
 | 
			
		||||
	} else {
 | 
			
		||||
	    fprintf(stderr, "qemu: '%s' invalid media\n", buf);
 | 
			
		||||
	    return NULL;
 | 
			
		||||
	}
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ((buf = qemu_opt_get(opts, "cache")) != NULL) {
 | 
			
		||||
        if (!strcmp(buf, "off") || !strcmp(buf, "none")) {
 | 
			
		||||
            bdrv_flags |= BDRV_O_NOCACHE;
 | 
			
		||||
        } else if (!strcmp(buf, "writeback")) {
 | 
			
		||||
            bdrv_flags |= BDRV_O_CACHE_WB;
 | 
			
		||||
        } else if (!strcmp(buf, "unsafe")) {
 | 
			
		||||
            bdrv_flags |= BDRV_O_CACHE_WB;
 | 
			
		||||
            bdrv_flags |= BDRV_O_NO_FLUSH;
 | 
			
		||||
        } else if (!strcmp(buf, "writethrough")) {
 | 
			
		||||
            /* this is the default */
 | 
			
		||||
        } else {
 | 
			
		||||
           fprintf(stderr, "qemu: invalid cache option\n");
 | 
			
		||||
           return NULL;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_LINUX_AIO
 | 
			
		||||
    if ((buf = qemu_opt_get(opts, "aio")) != NULL) {
 | 
			
		||||
        if (!strcmp(buf, "native")) {
 | 
			
		||||
            bdrv_flags |= BDRV_O_NATIVE_AIO;
 | 
			
		||||
        } else if (!strcmp(buf, "threads")) {
 | 
			
		||||
            /* this is the default */
 | 
			
		||||
        } else {
 | 
			
		||||
           fprintf(stderr, "qemu: invalid aio option\n");
 | 
			
		||||
           return NULL;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    if ((buf = qemu_opt_get(opts, "format")) != NULL) {
 | 
			
		||||
       if (strcmp(buf, "?") == 0) {
 | 
			
		||||
            fprintf(stderr, "qemu: Supported formats:");
 | 
			
		||||
            bdrv_iterate_format(bdrv_format_print, NULL);
 | 
			
		||||
            fprintf(stderr, "\n");
 | 
			
		||||
	    return NULL;
 | 
			
		||||
        }
 | 
			
		||||
        drv = bdrv_find_whitelisted_format(buf);
 | 
			
		||||
        if (!drv) {
 | 
			
		||||
            fprintf(stderr, "qemu: '%s' invalid format\n", buf);
 | 
			
		||||
            return NULL;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    on_write_error = BLOCK_ERR_STOP_ENOSPC;
 | 
			
		||||
    if ((buf = qemu_opt_get(opts, "werror")) != NULL) {
 | 
			
		||||
        if (type != IF_IDE && type != IF_SCSI && type != IF_VIRTIO && type != IF_NONE) {
 | 
			
		||||
            fprintf(stderr, "werror is no supported by this format\n");
 | 
			
		||||
            return NULL;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        on_write_error = parse_block_error_action(buf, 0);
 | 
			
		||||
        if (on_write_error < 0) {
 | 
			
		||||
            return NULL;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    on_read_error = BLOCK_ERR_REPORT;
 | 
			
		||||
    if ((buf = qemu_opt_get(opts, "rerror")) != NULL) {
 | 
			
		||||
        if (type != IF_IDE && type != IF_VIRTIO && type != IF_NONE) {
 | 
			
		||||
            fprintf(stderr, "rerror is no supported by this format\n");
 | 
			
		||||
            return NULL;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        on_read_error = parse_block_error_action(buf, 1);
 | 
			
		||||
        if (on_read_error < 0) {
 | 
			
		||||
            return NULL;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ((devaddr = qemu_opt_get(opts, "addr")) != NULL) {
 | 
			
		||||
        if (type != IF_VIRTIO) {
 | 
			
		||||
            fprintf(stderr, "addr is not supported\n");
 | 
			
		||||
            return NULL;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* compute bus and unit according index */
 | 
			
		||||
 | 
			
		||||
    if (index != -1) {
 | 
			
		||||
        if (bus_id != 0 || unit_id != -1) {
 | 
			
		||||
            fprintf(stderr,
 | 
			
		||||
                    "qemu: index cannot be used with bus and unit\n");
 | 
			
		||||
            return NULL;
 | 
			
		||||
        }
 | 
			
		||||
        if (max_devs == 0)
 | 
			
		||||
        {
 | 
			
		||||
            unit_id = index;
 | 
			
		||||
            bus_id = 0;
 | 
			
		||||
        } else {
 | 
			
		||||
            unit_id = index % max_devs;
 | 
			
		||||
            bus_id = index / max_devs;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* if user doesn't specify a unit_id,
 | 
			
		||||
     * try to find the first free
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    if (unit_id == -1) {
 | 
			
		||||
       unit_id = 0;
 | 
			
		||||
       while (drive_get(type, bus_id, unit_id) != NULL) {
 | 
			
		||||
           unit_id++;
 | 
			
		||||
           if (max_devs && unit_id >= max_devs) {
 | 
			
		||||
               unit_id -= max_devs;
 | 
			
		||||
               bus_id++;
 | 
			
		||||
           }
 | 
			
		||||
       }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* check unit id */
 | 
			
		||||
 | 
			
		||||
    if (max_devs && unit_id >= max_devs) {
 | 
			
		||||
        fprintf(stderr, "qemu: unit %d too big (max is %d)\n",
 | 
			
		||||
                unit_id, max_devs - 1);
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
     * ignore multiple definitions
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    if (drive_get(type, bus_id, unit_id) != NULL) {
 | 
			
		||||
        *fatal_error = 0;
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* init */
 | 
			
		||||
 | 
			
		||||
    dinfo = qemu_mallocz(sizeof(*dinfo));
 | 
			
		||||
    if ((buf = qemu_opts_id(opts)) != NULL) {
 | 
			
		||||
        dinfo->id = qemu_strdup(buf);
 | 
			
		||||
    } else {
 | 
			
		||||
        /* no id supplied -> create one */
 | 
			
		||||
        dinfo->id = qemu_mallocz(32);
 | 
			
		||||
        if (type == IF_IDE || type == IF_SCSI)
 | 
			
		||||
            mediastr = (media == MEDIA_CDROM) ? "-cd" : "-hd";
 | 
			
		||||
        if (max_devs)
 | 
			
		||||
            snprintf(dinfo->id, 32, "%s%i%s%i",
 | 
			
		||||
                     devname, bus_id, mediastr, unit_id);
 | 
			
		||||
        else
 | 
			
		||||
            snprintf(dinfo->id, 32, "%s%s%i",
 | 
			
		||||
                     devname, mediastr, unit_id);
 | 
			
		||||
    }
 | 
			
		||||
    dinfo->bdrv = bdrv_new(dinfo->id);
 | 
			
		||||
    dinfo->devaddr = devaddr;
 | 
			
		||||
    dinfo->type = type;
 | 
			
		||||
    dinfo->bus = bus_id;
 | 
			
		||||
    dinfo->unit = unit_id;
 | 
			
		||||
    dinfo->opts = opts;
 | 
			
		||||
    if (serial)
 | 
			
		||||
        strncpy(dinfo->serial, serial, sizeof(dinfo->serial) - 1);
 | 
			
		||||
    QTAILQ_INSERT_TAIL(&drives, dinfo, next);
 | 
			
		||||
 | 
			
		||||
    bdrv_set_on_error(dinfo->bdrv, on_read_error, on_write_error);
 | 
			
		||||
 | 
			
		||||
    switch(type) {
 | 
			
		||||
    case IF_IDE:
 | 
			
		||||
    case IF_SCSI:
 | 
			
		||||
    case IF_XEN:
 | 
			
		||||
    case IF_NONE:
 | 
			
		||||
        switch(media) {
 | 
			
		||||
	case MEDIA_DISK:
 | 
			
		||||
            if (cyls != 0) {
 | 
			
		||||
                bdrv_set_geometry_hint(dinfo->bdrv, cyls, heads, secs);
 | 
			
		||||
                bdrv_set_translation_hint(dinfo->bdrv, translation);
 | 
			
		||||
            }
 | 
			
		||||
	    break;
 | 
			
		||||
	case MEDIA_CDROM:
 | 
			
		||||
            bdrv_set_type_hint(dinfo->bdrv, BDRV_TYPE_CDROM);
 | 
			
		||||
	    break;
 | 
			
		||||
	}
 | 
			
		||||
        break;
 | 
			
		||||
    case IF_SD:
 | 
			
		||||
        /* FIXME: This isn't really a floppy, but it's a reasonable
 | 
			
		||||
           approximation.  */
 | 
			
		||||
    case IF_FLOPPY:
 | 
			
		||||
        bdrv_set_type_hint(dinfo->bdrv, BDRV_TYPE_FLOPPY);
 | 
			
		||||
        break;
 | 
			
		||||
    case IF_PFLASH:
 | 
			
		||||
    case IF_MTD:
 | 
			
		||||
        break;
 | 
			
		||||
    case IF_VIRTIO:
 | 
			
		||||
        /* add virtio block device */
 | 
			
		||||
        opts = qemu_opts_create(&qemu_device_opts, NULL, 0);
 | 
			
		||||
        qemu_opt_set(opts, "driver", "virtio-blk-pci");
 | 
			
		||||
        qemu_opt_set(opts, "drive", dinfo->id);
 | 
			
		||||
        if (devaddr)
 | 
			
		||||
            qemu_opt_set(opts, "addr", devaddr);
 | 
			
		||||
        break;
 | 
			
		||||
    case IF_COUNT:
 | 
			
		||||
        abort();
 | 
			
		||||
    }
 | 
			
		||||
    if (!file || !*file) {
 | 
			
		||||
        *fatal_error = 0;
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
    if (snapshot) {
 | 
			
		||||
        /* always use cache=unsafe with snapshot */
 | 
			
		||||
        bdrv_flags &= ~BDRV_O_CACHE_MASK;
 | 
			
		||||
        bdrv_flags |= (BDRV_O_SNAPSHOT|BDRV_O_CACHE_WB|BDRV_O_NO_FLUSH);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (media == MEDIA_CDROM) {
 | 
			
		||||
        /* CDROM is fine for any interface, don't check.  */
 | 
			
		||||
        ro = 1;
 | 
			
		||||
    } else if (ro == 1) {
 | 
			
		||||
        if (type != IF_SCSI && type != IF_VIRTIO && type != IF_FLOPPY && type != IF_NONE) {
 | 
			
		||||
            fprintf(stderr, "qemu: readonly flag not supported for drive with this interface\n");
 | 
			
		||||
            return NULL;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bdrv_flags |= ro ? 0 : BDRV_O_RDWR;
 | 
			
		||||
 | 
			
		||||
    ret = bdrv_open(dinfo->bdrv, file, bdrv_flags, drv);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        fprintf(stderr, "qemu: could not open disk image %s: %s\n",
 | 
			
		||||
                        file, strerror(-ret));
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (bdrv_key_required(dinfo->bdrv))
 | 
			
		||||
        autostart = 0;
 | 
			
		||||
    *fatal_error = 0;
 | 
			
		||||
    return dinfo;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void do_commit(Monitor *mon, const QDict *qdict)
 | 
			
		||||
{
 | 
			
		||||
    const char *device = qdict_get_str(qdict, "device");
 | 
			
		||||
    BlockDriverState *bs;
 | 
			
		||||
 | 
			
		||||
    if (!strcmp(device, "all")) {
 | 
			
		||||
        bdrv_commit_all();
 | 
			
		||||
    } else {
 | 
			
		||||
        bs = bdrv_find(device);
 | 
			
		||||
        if (!bs) {
 | 
			
		||||
            qerror_report(QERR_DEVICE_NOT_FOUND, device);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        bdrv_commit(bs);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int eject_device(Monitor *mon, BlockDriverState *bs, int force)
 | 
			
		||||
{
 | 
			
		||||
    if (!force) {
 | 
			
		||||
        if (!bdrv_is_removable(bs)) {
 | 
			
		||||
            qerror_report(QERR_DEVICE_NOT_REMOVABLE,
 | 
			
		||||
                           bdrv_get_device_name(bs));
 | 
			
		||||
            return -1;
 | 
			
		||||
        }
 | 
			
		||||
        if (bdrv_is_locked(bs)) {
 | 
			
		||||
            qerror_report(QERR_DEVICE_LOCKED, bdrv_get_device_name(bs));
 | 
			
		||||
            return -1;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    bdrv_close(bs);
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int do_eject(Monitor *mon, const QDict *qdict, QObject **ret_data)
 | 
			
		||||
{
 | 
			
		||||
    BlockDriverState *bs;
 | 
			
		||||
    int force = qdict_get_try_bool(qdict, "force", 0);
 | 
			
		||||
    const char *filename = qdict_get_str(qdict, "device");
 | 
			
		||||
 | 
			
		||||
    bs = bdrv_find(filename);
 | 
			
		||||
    if (!bs) {
 | 
			
		||||
        qerror_report(QERR_DEVICE_NOT_FOUND, filename);
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
    return eject_device(mon, bs, force);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int do_block_set_passwd(Monitor *mon, const QDict *qdict,
 | 
			
		||||
                        QObject **ret_data)
 | 
			
		||||
{
 | 
			
		||||
    BlockDriverState *bs;
 | 
			
		||||
    int err;
 | 
			
		||||
 | 
			
		||||
    bs = bdrv_find(qdict_get_str(qdict, "device"));
 | 
			
		||||
    if (!bs) {
 | 
			
		||||
        qerror_report(QERR_DEVICE_NOT_FOUND, qdict_get_str(qdict, "device"));
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    err = bdrv_set_key(bs, qdict_get_str(qdict, "password"));
 | 
			
		||||
    if (err == -EINVAL) {
 | 
			
		||||
        qerror_report(QERR_DEVICE_NOT_ENCRYPTED, bdrv_get_device_name(bs));
 | 
			
		||||
        return -1;
 | 
			
		||||
    } else if (err < 0) {
 | 
			
		||||
        qerror_report(QERR_INVALID_PASSWORD);
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int do_change_block(Monitor *mon, const char *device,
 | 
			
		||||
                    const char *filename, const char *fmt)
 | 
			
		||||
{
 | 
			
		||||
    BlockDriverState *bs;
 | 
			
		||||
    BlockDriver *drv = NULL;
 | 
			
		||||
    int bdrv_flags;
 | 
			
		||||
 | 
			
		||||
    bs = bdrv_find(device);
 | 
			
		||||
    if (!bs) {
 | 
			
		||||
        qerror_report(QERR_DEVICE_NOT_FOUND, device);
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
    if (fmt) {
 | 
			
		||||
        drv = bdrv_find_whitelisted_format(fmt);
 | 
			
		||||
        if (!drv) {
 | 
			
		||||
            qerror_report(QERR_INVALID_BLOCK_FORMAT, fmt);
 | 
			
		||||
            return -1;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    if (eject_device(mon, bs, 0) < 0) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
    bdrv_flags = bdrv_is_read_only(bs) ? 0 : BDRV_O_RDWR;
 | 
			
		||||
    bdrv_flags |= bdrv_is_snapshot(bs) ? BDRV_O_SNAPSHOT : 0;
 | 
			
		||||
    if (bdrv_open(bs, filename, bdrv_flags, drv) < 0) {
 | 
			
		||||
        qerror_report(QERR_OPEN_FILE_FAILED, filename);
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
    return monitor_read_bdrv_key_start(mon, bs, NULL, NULL);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										62
									
								
								blockdev.h
									
									
									
									
									
								
							
							
						
						
									
										62
									
								
								blockdev.h
									
									
									
									
									
								
							@@ -1,62 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * QEMU host block devices
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2003-2008 Fabrice Bellard
 | 
			
		||||
 *
 | 
			
		||||
 * This work is licensed under the terms of the GNU GPL, version 2 or
 | 
			
		||||
 * later.  See the COPYING file in the top-level directory.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef BLOCKDEV_H
 | 
			
		||||
#define BLOCKDEV_H
 | 
			
		||||
 | 
			
		||||
#include "block.h"
 | 
			
		||||
#include "qemu-queue.h"
 | 
			
		||||
 | 
			
		||||
void blockdev_mark_auto_del(BlockDriverState *bs);
 | 
			
		||||
void blockdev_auto_del(BlockDriverState *bs);
 | 
			
		||||
 | 
			
		||||
typedef enum {
 | 
			
		||||
    IF_NONE,
 | 
			
		||||
    IF_IDE, IF_SCSI, IF_FLOPPY, IF_PFLASH, IF_MTD, IF_SD, IF_VIRTIO, IF_XEN,
 | 
			
		||||
    IF_COUNT
 | 
			
		||||
} BlockInterfaceType;
 | 
			
		||||
 | 
			
		||||
#define BLOCK_SERIAL_STRLEN 20
 | 
			
		||||
 | 
			
		||||
typedef struct DriveInfo {
 | 
			
		||||
    BlockDriverState *bdrv;
 | 
			
		||||
    char *id;
 | 
			
		||||
    const char *devaddr;
 | 
			
		||||
    BlockInterfaceType type;
 | 
			
		||||
    int bus;
 | 
			
		||||
    int unit;
 | 
			
		||||
    int auto_del;               /* see blockdev_mark_auto_del() */
 | 
			
		||||
    QemuOpts *opts;
 | 
			
		||||
    char serial[BLOCK_SERIAL_STRLEN + 1];
 | 
			
		||||
    QTAILQ_ENTRY(DriveInfo) next;
 | 
			
		||||
} DriveInfo;
 | 
			
		||||
 | 
			
		||||
#define MAX_IDE_DEVS	2
 | 
			
		||||
#define MAX_SCSI_DEVS	7
 | 
			
		||||
 | 
			
		||||
extern DriveInfo *drive_get(BlockInterfaceType type, int bus, int unit);
 | 
			
		||||
extern int drive_get_max_bus(BlockInterfaceType type);
 | 
			
		||||
extern void drive_uninit(DriveInfo *dinfo);
 | 
			
		||||
extern DriveInfo *drive_get_by_blockdev(BlockDriverState *bs);
 | 
			
		||||
 | 
			
		||||
extern QemuOpts *drive_add(const char *file, const char *fmt, ...);
 | 
			
		||||
extern DriveInfo *drive_init(QemuOpts *arg, int default_to_scsi,
 | 
			
		||||
                             int *fatal_error);
 | 
			
		||||
 | 
			
		||||
/* device-hotplug */
 | 
			
		||||
 | 
			
		||||
DriveInfo *add_init_drive(const char *opts);
 | 
			
		||||
 | 
			
		||||
void do_commit(Monitor *mon, const QDict *qdict);
 | 
			
		||||
int do_eject(Monitor *mon, const QDict *qdict, QObject **ret_data);
 | 
			
		||||
int do_block_set_passwd(Monitor *mon, const QDict *qdict, QObject **ret_data);
 | 
			
		||||
int do_change_block(Monitor *mon, const char *device,
 | 
			
		||||
                    const char *filename, const char *fmt);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
@@ -1,121 +0,0 @@
 | 
			
		||||
/*-
 | 
			
		||||
 * Copyright (c) 1982, 1986, 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.
 | 
			
		||||
 *
 | 
			
		||||
 *      @(#)mman.h      8.2 (Berkeley) 1/9/95
 | 
			
		||||
 * $FreeBSD: src/sys/sys/mman.h,v 1.42 2008/03/28 04:29:27 ps Exp $
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#define TARGET_FREEBSD_MAP_RESERVED0080 0x0080  /* previously misimplemented MAP_INHERIT */
 | 
			
		||||
#define TARGET_FREEBSD_MAP_RESERVED0100 0x0100  /* previously unimplemented MAP_NOEXTEND */
 | 
			
		||||
#define TARGET_FREEBSD_MAP_STACK        0x0400  /* region grows down, like a stack */
 | 
			
		||||
#define TARGET_FREEBSD_MAP_NOSYNC       0x0800  /* page to but do not sync underlying file */
 | 
			
		||||
 | 
			
		||||
#define TARGET_FREEBSD_MAP_FLAGMASK     0x1ff7
 | 
			
		||||
 | 
			
		||||
/*      $NetBSD: mman.h,v 1.42 2008/11/18 22:13:49 ad Exp $     */
 | 
			
		||||
 | 
			
		||||
/*-
 | 
			
		||||
 * Copyright (c) 1982, 1986, 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.
 | 
			
		||||
 * 3. 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.
 | 
			
		||||
 *
 | 
			
		||||
 *      @(#)mman.h      8.2 (Berkeley) 1/9/95
 | 
			
		||||
 */
 | 
			
		||||
#define TARGET_NETBSD_MAP_INHERIT       0x0080  /* region is retained after exec */
 | 
			
		||||
#define TARGET_NETBSD_MAP_TRYFIXED      0x0400 /* attempt hint address, even within break */
 | 
			
		||||
#define TARGET_NETBSD_MAP_WIRED         0x0800  /* mlock() mapping when it is established */
 | 
			
		||||
 | 
			
		||||
#define TARGET_NETBSD_MAP_STACK         0x2000  /* allocated from memory, swap space (stack) */
 | 
			
		||||
 | 
			
		||||
#define TARGET_NETBSD_MAP_FLAGMASK      0x3ff7
 | 
			
		||||
 | 
			
		||||
/*      $OpenBSD: mman.h,v 1.18 2003/07/21 22:52:19 tedu Exp $  */
 | 
			
		||||
/*      $NetBSD: mman.h,v 1.11 1995/03/26 20:24:23 jtc Exp $    */
 | 
			
		||||
 | 
			
		||||
/*-
 | 
			
		||||
 * Copyright (c) 1982, 1986, 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.
 | 
			
		||||
 * 3. 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.
 | 
			
		||||
 *
 | 
			
		||||
 *      @(#)mman.h      8.1 (Berkeley) 6/2/93
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#define TARGET_OPENBSD_MAP_INHERIT      0x0080  /* region is retained after exec */
 | 
			
		||||
#define TARGET_OPENBSD_MAP_NOEXTEND     0x0100  /* for MAP_FILE, don't change file size */
 | 
			
		||||
#define TARGET_OPENBSD_MAP_TRYFIXED     0x0400  /* attempt hint address, even within heap */
 | 
			
		||||
 | 
			
		||||
#define TARGET_OPENBSD_MAP_FLAGMASK     0x17f7
 | 
			
		||||
 | 
			
		||||
// XXX
 | 
			
		||||
#define TARGET_BSD_MAP_FLAGMASK         0x3ff7
 | 
			
		||||
@@ -1,204 +0,0 @@
 | 
			
		||||
/* Code for loading BSD executables.  Mostly linux kernel code.  */
 | 
			
		||||
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
 | 
			
		||||
#include "qemu.h"
 | 
			
		||||
 | 
			
		||||
#define TARGET_NGROUPS 32
 | 
			
		||||
 | 
			
		||||
/* ??? This should really be somewhere else.  */
 | 
			
		||||
abi_long memcpy_to_target(abi_ulong dest, const void *src,
 | 
			
		||||
                          unsigned long len)
 | 
			
		||||
{
 | 
			
		||||
    void *host_ptr;
 | 
			
		||||
 | 
			
		||||
    host_ptr = lock_user(VERIFY_WRITE, dest, len, 0);
 | 
			
		||||
    if (!host_ptr)
 | 
			
		||||
        return -TARGET_EFAULT;
 | 
			
		||||
    memcpy(host_ptr, src, len);
 | 
			
		||||
    unlock_user(host_ptr, dest, 1);
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int in_group_p(gid_t g)
 | 
			
		||||
{
 | 
			
		||||
    /* return TRUE if we're in the specified group, FALSE otherwise */
 | 
			
		||||
    int         ngroup;
 | 
			
		||||
    int         i;
 | 
			
		||||
    gid_t       grouplist[TARGET_NGROUPS];
 | 
			
		||||
 | 
			
		||||
    ngroup = getgroups(TARGET_NGROUPS, grouplist);
 | 
			
		||||
    for(i = 0; i < ngroup; i++) {
 | 
			
		||||
        if(grouplist[i] == g) {
 | 
			
		||||
            return 1;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int count(char ** vec)
 | 
			
		||||
{
 | 
			
		||||
    int         i;
 | 
			
		||||
 | 
			
		||||
    for(i = 0; *vec; i++) {
 | 
			
		||||
        vec++;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return(i);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int prepare_binprm(struct linux_binprm *bprm)
 | 
			
		||||
{
 | 
			
		||||
    struct stat         st;
 | 
			
		||||
    int mode;
 | 
			
		||||
    int retval, id_change;
 | 
			
		||||
 | 
			
		||||
    if(fstat(bprm->fd, &st) < 0) {
 | 
			
		||||
        return(-errno);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    mode = st.st_mode;
 | 
			
		||||
    if(!S_ISREG(mode)) {        /* Must be regular file */
 | 
			
		||||
        return(-EACCES);
 | 
			
		||||
    }
 | 
			
		||||
    if(!(mode & 0111)) {        /* Must have at least one execute bit set */
 | 
			
		||||
        return(-EACCES);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bprm->e_uid = geteuid();
 | 
			
		||||
    bprm->e_gid = getegid();
 | 
			
		||||
    id_change = 0;
 | 
			
		||||
 | 
			
		||||
    /* Set-uid? */
 | 
			
		||||
    if(mode & S_ISUID) {
 | 
			
		||||
        bprm->e_uid = st.st_uid;
 | 
			
		||||
        if(bprm->e_uid != geteuid()) {
 | 
			
		||||
            id_change = 1;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Set-gid? */
 | 
			
		||||
    /*
 | 
			
		||||
     * If setgid is set but no group execute bit then this
 | 
			
		||||
     * is a candidate for mandatory locking, not a setgid
 | 
			
		||||
     * executable.
 | 
			
		||||
     */
 | 
			
		||||
    if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) {
 | 
			
		||||
        bprm->e_gid = st.st_gid;
 | 
			
		||||
        if (!in_group_p(bprm->e_gid)) {
 | 
			
		||||
                id_change = 1;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    memset(bprm->buf, 0, sizeof(bprm->buf));
 | 
			
		||||
    retval = lseek(bprm->fd, 0L, SEEK_SET);
 | 
			
		||||
    if(retval >= 0) {
 | 
			
		||||
        retval = read(bprm->fd, bprm->buf, 128);
 | 
			
		||||
    }
 | 
			
		||||
    if(retval < 0) {
 | 
			
		||||
        perror("prepare_binprm");
 | 
			
		||||
        exit(-1);
 | 
			
		||||
        /* return(-errno); */
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
        return(retval);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Construct the envp and argv tables on the target stack.  */
 | 
			
		||||
abi_ulong loader_build_argptr(int envc, int argc, abi_ulong sp,
 | 
			
		||||
                              abi_ulong stringp, int push_ptr)
 | 
			
		||||
{
 | 
			
		||||
    int n = sizeof(abi_ulong);
 | 
			
		||||
    abi_ulong envp;
 | 
			
		||||
    abi_ulong argv;
 | 
			
		||||
 | 
			
		||||
    sp -= (envc + 1) * n;
 | 
			
		||||
    envp = sp;
 | 
			
		||||
    sp -= (argc + 1) * n;
 | 
			
		||||
    argv = sp;
 | 
			
		||||
    if (push_ptr) {
 | 
			
		||||
        /* FIXME - handle put_user() failures */
 | 
			
		||||
        sp -= n;
 | 
			
		||||
        put_user_ual(envp, sp);
 | 
			
		||||
        sp -= n;
 | 
			
		||||
        put_user_ual(argv, sp);
 | 
			
		||||
    }
 | 
			
		||||
    sp -= n;
 | 
			
		||||
    /* FIXME - handle put_user() failures */
 | 
			
		||||
    put_user_ual(argc, sp);
 | 
			
		||||
 | 
			
		||||
    while (argc-- > 0) {
 | 
			
		||||
        /* FIXME - handle put_user() failures */
 | 
			
		||||
        put_user_ual(stringp, argv);
 | 
			
		||||
        argv += n;
 | 
			
		||||
        stringp += target_strlen(stringp) + 1;
 | 
			
		||||
    }
 | 
			
		||||
    /* FIXME - handle put_user() failures */
 | 
			
		||||
    put_user_ual(0, argv);
 | 
			
		||||
    while (envc-- > 0) {
 | 
			
		||||
        /* FIXME - handle put_user() failures */
 | 
			
		||||
        put_user_ual(stringp, envp);
 | 
			
		||||
        envp += n;
 | 
			
		||||
        stringp += target_strlen(stringp) + 1;
 | 
			
		||||
    }
 | 
			
		||||
    /* FIXME - handle put_user() failures */
 | 
			
		||||
    put_user_ual(0, envp);
 | 
			
		||||
 | 
			
		||||
    return sp;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int loader_exec(const char * filename, char ** argv, char ** envp,
 | 
			
		||||
             struct target_pt_regs * regs, struct image_info *infop)
 | 
			
		||||
{
 | 
			
		||||
    struct linux_binprm bprm;
 | 
			
		||||
    int retval;
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
    bprm.p = TARGET_PAGE_SIZE*MAX_ARG_PAGES-sizeof(unsigned int);
 | 
			
		||||
    for (i=0 ; i<MAX_ARG_PAGES ; i++)       /* clear page-table */
 | 
			
		||||
            bprm.page[i] = NULL;
 | 
			
		||||
    retval = open(filename, O_RDONLY);
 | 
			
		||||
    if (retval < 0)
 | 
			
		||||
        return retval;
 | 
			
		||||
    bprm.fd = retval;
 | 
			
		||||
    bprm.filename = (char *)filename;
 | 
			
		||||
    bprm.argc = count(argv);
 | 
			
		||||
    bprm.argv = argv;
 | 
			
		||||
    bprm.envc = count(envp);
 | 
			
		||||
    bprm.envp = envp;
 | 
			
		||||
 | 
			
		||||
    retval = prepare_binprm(&bprm);
 | 
			
		||||
 | 
			
		||||
    infop->host_argv = argv;
 | 
			
		||||
 | 
			
		||||
    if(retval>=0) {
 | 
			
		||||
        if (bprm.buf[0] == 0x7f
 | 
			
		||||
                && bprm.buf[1] == 'E'
 | 
			
		||||
                && bprm.buf[2] == 'L'
 | 
			
		||||
                && bprm.buf[3] == 'F') {
 | 
			
		||||
            retval = load_elf_binary(&bprm,regs,infop);
 | 
			
		||||
        } else {
 | 
			
		||||
            fprintf(stderr, "Unknown binary format\n");
 | 
			
		||||
            return -1;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if(retval>=0) {
 | 
			
		||||
        /* success.  Initialize important registers */
 | 
			
		||||
        do_init_thread(regs, infop);
 | 
			
		||||
        return retval;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Something went wrong, return the inode and free the argument pages*/
 | 
			
		||||
    for (i=0 ; i<MAX_ARG_PAGES ; i++) {
 | 
			
		||||
        free(bprm.page[i]);
 | 
			
		||||
    }
 | 
			
		||||
    return(retval);
 | 
			
		||||
}
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user