Compare commits
1 Commits
v0.12.2
...
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
|
||||
51
.gitignore
vendored
51
.gitignore
vendored
@@ -1,51 +0,0 @@
|
||||
config-devices.*
|
||||
config-all-devices.*
|
||||
config-host.*
|
||||
config-target.*
|
||||
i386
|
||||
*-softmmu
|
||||
*-darwin-user
|
||||
*-linux-user
|
||||
*-bsd-user
|
||||
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.texi
|
||||
qemu-img-cmds.texi
|
||||
qemu-img-cmds.h
|
||||
qemu-io
|
||||
qemu-monitor.texi
|
||||
.gdbinit
|
||||
*.a
|
||||
*.aux
|
||||
*.cp
|
||||
*.dvi
|
||||
*.exe
|
||||
*.fn
|
||||
*.ky
|
||||
*.log
|
||||
*.pg
|
||||
*.toc
|
||||
*.tp
|
||||
*.vr
|
||||
*.d
|
||||
*.o
|
||||
.pc
|
||||
patches
|
||||
pc-bios/bios-pq/status
|
||||
pc-bios/vgabios-pq/status
|
||||
pc-bios/optionrom/multiboot.bin
|
||||
pc-bios/optionrom/multiboot.raw
|
||||
.stgit-*
|
||||
6
.gitmodules
vendored
6
.gitmodules
vendored
@@ -1,6 +0,0 @@
|
||||
[submodule "roms/vgabios"]
|
||||
path = roms/vgabios
|
||||
url = ../vgabios.git
|
||||
[submodule "roms/seabios"]
|
||||
path = roms/seabios
|
||||
url = ../seabios.git
|
||||
78
CODING_STYLE
78
CODING_STYLE
@@ -1,78 +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.
|
||||
|
||||
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.
|
||||
|
||||
|
||||
254
Changelog
254
Changelog
@@ -1,239 +1,3 @@
|
||||
version 0.12.2:
|
||||
- Qemu's internal TFTP server breaks lock-step-iness of TFTP (Milan Plzik)
|
||||
- osdep.c: Fix accept4 fallback (Kevin Wolf)
|
||||
- pc: add rombar to compat properties for pc-0.10 and pc-0.11 (Gerd Hoffmann)
|
||||
- pci: allow loading roms via fw_cfg. (Gerd Hoffmann)
|
||||
- roms: rework rom loading via fw (Gerd Hoffmann)
|
||||
- fw_cfg: rom loader tweaks. (Gerd Hoffmann)
|
||||
- roms: minor fixes and cleanups. (Gerd Hoffmann)
|
||||
- pc: add machine type for 0.12 (Gerd Hoffmann)
|
||||
- loader: more ignores for rom intended to be loaded by the bios (Aurelien Jarno)
|
||||
- vnc_refresh: return if vd->timer is NULL (Stefano Stabellini)
|
||||
- QMP: Don't free async event's 'data' (Luiz Capitulino)
|
||||
- Handle TFTP ERROR from client (Thomas Horsten)
|
||||
- dmg: fix ->open failure (Christoph Hellwig)
|
||||
- virtio-pci: thinko fix (Michael S. Tsirkin)
|
||||
- pc-bios: Update README (SeaBIOS) (Stefan Weil)
|
||||
- vmware_vga: Check cursor dimensions passed from guest to avoid buffer overflow (Roland Dreier)
|
||||
- remove pending exception on vcpu reset. (Gleb Natapov)
|
||||
- Fix CPU topology initialization (Jiri Denemark)
|
||||
- MCE: Fix bug of IA32_MCG_STATUS after system reset (Huang Ying)
|
||||
- linuxboot: fix gdt address calculation (Avi Kivity)
|
||||
- QMP: Drop wrong assert() (Luiz Capitulino)
|
||||
- vnc: Fix artifacts in hextile decoding (Anthony Liguori)
|
||||
- target-i386: Fix "call im" on x86_64 when executing 32-bit code (Aurelien Jarno)
|
||||
- Add missing newline at the end of options list (Michael Tokarev)
|
||||
- Don't load options roms intended to be loaded by the bios in qemu (Avi Kivity)
|
||||
- USB: Improve usbdevice error messages (Scott Tsai)
|
||||
- cpu-all.h: fix cpu_get_real_ticks() #ifdef (Aurelien Jarno)
|
||||
- alpha: fix compile (Blue Swirl)
|
||||
- user_only: compile everything with -fpie (Kirill A. Shutemov)
|
||||
- fdc/sparc32: don't hang on detection under OBP (Artyom Tarasenko)
|
||||
- scsi-disk: Inquiry with allocation length of CDB < 36 (v4) (Artyom Tarasenko)
|
||||
- e1000: fix init values for command register (Michael S. Tsirkin)
|
||||
|
||||
version 0.12.1:
|
||||
- loader: fix rom loading at address 0 (fixes target-arm) (Aurelien Jarno)
|
||||
- loader: fix rom_copy (fixes multiboot) (Kevin Wolf)
|
||||
|
||||
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)
|
||||
@@ -241,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.)
|
||||
@@ -271,7 +35,7 @@ version 0.8.0:
|
||||
(Johannes Schindelin)
|
||||
|
||||
version 0.7.2:
|
||||
|
||||
|
||||
- x86_64 fixes (Win2000 and Linux 2.6 boot in 32 bit)
|
||||
- merge self modifying code handling in dirty ram page mecanism.
|
||||
- MIPS fixes (Ralf Baechle)
|
||||
@@ -320,7 +84,7 @@ version 0.6.1:
|
||||
- Mac OS X port (Pierre d'Herbemont)
|
||||
- Virtual console support
|
||||
- Better monitor line edition
|
||||
- New block device layer
|
||||
- New block device layer
|
||||
- New 'qcow' growable disk image support with AES encryption and
|
||||
transparent decompression
|
||||
- VMware 3 and 4 read-only disk image support (untested)
|
||||
@@ -386,7 +150,7 @@ version 0.5.5:
|
||||
- FDC fixes for Win98
|
||||
|
||||
version 0.5.4:
|
||||
|
||||
|
||||
- qemu-fast fixes
|
||||
- BIOS area protection fix (aka EMM386.EXE fix) (Mike Nordell)
|
||||
- keyboard/mouse fix (Mike Nordell)
|
||||
@@ -413,7 +177,7 @@ version 0.5.3:
|
||||
- added accurate CR0.MP/ME/TS emulation
|
||||
- fixed DMA memory write access (Win95 boot floppy fix)
|
||||
- graphical x86 linux loader
|
||||
- command line monitor
|
||||
- command line monitor
|
||||
- generic removable device support
|
||||
- support of CD-ROM change
|
||||
- multiple network interface support
|
||||
@@ -451,7 +215,7 @@ version 0.5.2:
|
||||
- eflags optimisation fix for string operations
|
||||
|
||||
version 0.5.1:
|
||||
|
||||
|
||||
- float access fixes when using soft mmu
|
||||
- PC emulation support on PowerPC
|
||||
- A20 support
|
||||
@@ -466,7 +230,7 @@ version 0.5.1:
|
||||
- Major SPARC target fixes (dynamically linked programs begin to work)
|
||||
|
||||
version 0.5.0:
|
||||
|
||||
|
||||
- full hardware level VGA emulation
|
||||
- graphical display with SDL
|
||||
- added PS/2 mouse and keyboard emulation
|
||||
@@ -504,7 +268,7 @@ version 0.4.2:
|
||||
- SMP kernels can at least be booted
|
||||
|
||||
version 0.4.1:
|
||||
|
||||
|
||||
- more accurate timer support in vl.
|
||||
- more reliable NE2000 probe in vl.
|
||||
- added 2.5.66 kernel in vl-test.
|
||||
@@ -590,7 +354,7 @@ version 0.1.3:
|
||||
- added bound, cmpxchg8b, cpuid instructions
|
||||
- added 16 bit addressing support/override for string operations
|
||||
- poll() fix
|
||||
|
||||
|
||||
version 0.1.2:
|
||||
|
||||
- compile fixes
|
||||
|
||||
22
LICENSE
22
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) QEMU is a trademark of Fabrice Bellard.
|
||||
|
||||
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.
|
||||
|
||||
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 Thiemo Seufer
|
||||
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 Thiemo Seufer
|
||||
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 ?
|
||||
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 ?
|
||||
447
Makefile
447
Makefile
@@ -1,273 +1,45 @@
|
||||
# Makefile for QEMU.
|
||||
|
||||
# This needs to be defined before rules.mak
|
||||
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: 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 \
|
||||
recurse-all speed tar tarbin test build-all
|
||||
|
||||
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
|
||||
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 \
|
||||
echo "WARNING: $@ out of date." ;\
|
||||
echo "Run \"make defconfig\" to regenerate." ; \
|
||||
rm $@.tmp ; \
|
||||
else \
|
||||
mv $@.tmp $@ ; \
|
||||
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,)
|
||||
|
||||
$(filter %-softmmu,$(SUBDIR_RULES)): libqemu_common.a
|
||||
|
||||
$(filter %-user,$(SUBDIR_RULES)): libuser.a
|
||||
|
||||
libuser.a: $(GENERATED_HEADERS)
|
||||
$(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C libuser V="$(V)" TARGET_DIR="libuser/" all,)
|
||||
|
||||
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)
|
||||
|
||||
#######################################################################
|
||||
# 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
|
||||
block-obj-$(CONFIG_POSIX) += posix-aio-compat.o
|
||||
block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o
|
||||
|
||||
block-nested-y += 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
|
||||
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))
|
||||
|
||||
######################################################################
|
||||
# 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.
|
||||
|
||||
obj-y = $(block-obj-y)
|
||||
obj-y += $(net-obj-y)
|
||||
obj-y += $(qobject-obj-y)
|
||||
obj-y += readline.o console.o
|
||||
|
||||
obj-y += tcg-runtime.o host-utils.o
|
||||
obj-y += irq.o ioport.o
|
||||
obj-$(CONFIG_PTIMER) += ptimer.o
|
||||
obj-$(CONFIG_MAX7310) += max7310.o
|
||||
obj-$(CONFIG_WM8750) += wm8750.o
|
||||
obj-$(CONFIG_TWL92230) += twl92230.o
|
||||
obj-$(CONFIG_TSC2005) += tsc2005.o
|
||||
obj-$(CONFIG_LM832X) += lm832x.o
|
||||
obj-$(CONFIG_TMP105) += tmp105.o
|
||||
obj-$(CONFIG_STELLARIS_INPUT) += stellaris_input.o
|
||||
obj-$(CONFIG_SSD0303) += ssd0303.o
|
||||
obj-$(CONFIG_SSD0323) += ssd0323.o
|
||||
obj-$(CONFIG_ADS7846) += ads7846.o
|
||||
obj-$(CONFIG_MAX111X) += max111x.o
|
||||
obj-$(CONFIG_DS1338) += ds1338.o
|
||||
obj-y += i2c.o smbus.o smbus_eeprom.o
|
||||
obj-y += eeprom93xx.o
|
||||
obj-y += scsi-disk.o cdrom.o
|
||||
obj-y += scsi-generic.o scsi-bus.o
|
||||
obj-y += usb.o usb-hub.o usb-$(HOST_USB).o usb-hid.o usb-msd.o usb-wacom.o
|
||||
obj-y += usb-serial.o usb-net.o usb-bus.o
|
||||
obj-$(CONFIG_SSI) += ssi.o
|
||||
obj-$(CONFIG_SSI_SD) += ssi-sd.o
|
||||
obj-$(CONFIG_SD) += sd.o
|
||||
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
|
||||
obj-y += bt-hci-csr.o
|
||||
obj-y += buffered_file.o migration.o migration-tcp.o qemu-sockets.o
|
||||
obj-y += qemu-char.o aio.o savevm.o
|
||||
obj-y += msmouse.o ps2.o
|
||||
obj-y += qdev.o qdev-properties.o
|
||||
obj-y += qemu-config.o block-migration.o
|
||||
|
||||
obj-$(CONFIG_BRLAPI) += baum.o
|
||||
obj-$(CONFIG_POSIX) += migration-exec.o migration-unix.o migration-fd.o
|
||||
|
||||
audio/audio.o audio/fmodaudio.o: QEMU_CFLAGS += $(FMOD_CFLAGS)
|
||||
|
||||
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
|
||||
obj-y += $(addprefix audio/, $(audio-obj-y))
|
||||
|
||||
obj-y += keymaps.o
|
||||
obj-$(CONFIG_SDL) += sdl.o sdl_zoom.o x_keymap.o
|
||||
obj-$(CONFIG_CURSES) += curses.o
|
||||
obj-y += vnc.o acl.o d3des.o
|
||||
obj-$(CONFIG_VNC_TLS) += vnc-tls.o vnc-auth-vencrypt.o
|
||||
obj-$(CONFIG_VNC_SASL) += vnc-auth-sasl.o
|
||||
obj-$(CONFIG_COCOA) += cocoa.o
|
||||
obj-$(CONFIG_IOTHREAD) += qemu-thread.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
|
||||
obj-$(CONFIG_SLIRP) += $(addprefix slirp/, $(slirp-obj-y))
|
||||
|
||||
# xen backend driver support
|
||||
obj-$(CONFIG_XEN) += xen_backend.o xen_devconfig.o
|
||||
obj-$(CONFIG_XEN) += xen_console.o xenfb.o xen_disk.o xen_nic.o
|
||||
|
||||
QEMU_CFLAGS+=$(CURL_CFLAGS)
|
||||
|
||||
cocoa.o: cocoa.m
|
||||
|
||||
keymaps.o: keymaps.c keymaps.h
|
||||
|
||||
sdl_zoom.o: sdl_zoom.c sdl_zoom.h sdl_zoom_template.h
|
||||
|
||||
sdl.o: sdl.c keymaps.h sdl_keysym.h sdl_zoom.h
|
||||
|
||||
sdl.o audio/sdlaudio.o sdl_zoom.o baum.o: QEMU_CFLAGS += $(SDL_CFLAGS)
|
||||
|
||||
acl.o: acl.h acl.c
|
||||
|
||||
vnc.h: vnc-tls.h vnc-auth-vencrypt.h vnc-auth-sasl.h keymaps.h
|
||||
|
||||
vnc.o: vnc.c vnc.h vnc_keysym.h vnchextile.h d3des.c d3des.h acl.h
|
||||
|
||||
vnc.o: QEMU_CFLAGS += $(VNC_TLS_CFLAGS)
|
||||
|
||||
vnc-tls.o: vnc-tls.c vnc.h
|
||||
|
||||
vnc-auth-vencrypt.o: vnc-auth-vencrypt.c vnc.h
|
||||
|
||||
vnc-auth-sasl.o: vnc-auth-sasl.c vnc.h
|
||||
|
||||
curses.o: curses.c keymaps.h curses_keys.h
|
||||
|
||||
bt-host.o: QEMU_CFLAGS += $(BLUEZ_CFLAGS)
|
||||
|
||||
libqemu_common.a: $(obj-y)
|
||||
|
||||
######################################################################
|
||||
|
||||
qemu-img.o: qemu-img-cmds.h
|
||||
|
||||
qemu-img$(EXESUF): qemu-img.o qemu-tool.o $(block-obj-y) $(qobject-obj-y)
|
||||
|
||||
qemu-nbd$(EXESUF): qemu-nbd.o qemu-tool.o $(block-obj-y) $(qobject-obj-y)
|
||||
|
||||
qemu-io$(EXESUF): qemu-io.o qemu-tool.o cmd.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: 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 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
|
||||
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; 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 config-all-devices.mak
|
||||
rm -f roms/seabios/config.mak roms/vgabios/config.mak
|
||||
rm -f qemu-{doc,tech}.{info,aux,cp,dvi,fn,info,ky,log,pg,toc,tp,vr}
|
||||
for d in $(TARGET_DIRS) libhw32 libhw64 libuser; do \
|
||||
rm -f config-host.mak config-host.h $(DOCS)
|
||||
for d in $(TARGET_DIRS); do \
|
||||
rm -rf $$d || exit 1 ; \
|
||||
done
|
||||
|
||||
@@ -275,171 +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 \
|
||||
pxe-e1000.bin pxe-i82559er.bin \
|
||||
pxe-ne2k_pci.bin pxe-pcnet.bin \
|
||||
pxe-rtl8139.bin pxe-virtio.bin \
|
||||
bamboo.dtb petalogix-s3adsp1800.dtb \
|
||||
multiboot.bin linuxboot.bin
|
||||
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: all $(if $(BUILD_DOCS),install-doc)
|
||||
$(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)"; \
|
||||
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
|
||||
TAGS:
|
||||
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
|
||||
%.html: %.texi
|
||||
$(call quiet-command,texi2html -I=. -monolithic -number $<," GEN $@")
|
||||
texi2html -monolithic -number $<
|
||||
|
||||
%.info: %.texi
|
||||
$(call quiet-command,makeinfo -I . $< -o $@," GEN $@")
|
||||
makeinfo $< -o $@
|
||||
|
||||
%.dvi: %.texi
|
||||
$(call quiet-command,texi2dvi -I . $<," GEN $@")
|
||||
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 > $@
|
||||
|
||||
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 $@")
|
||||
|
||||
info: qemu-doc.info qemu-tech.info
|
||||
|
||||
dvi: qemu-doc.dvi qemu-tech.dvi
|
||||
|
||||
html: qemu-doc.html qemu-tech.html
|
||||
|
||||
qemu-doc.dvi qemu-doc.html qemu-doc.info: 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)
|
||||
|
||||
# generate a binary distribution
|
||||
tarbin:
|
||||
cd / && tar zcvf ~/qemu-$(VERSION)-$(ARCH).tar.gz \
|
||||
( 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-arm \
|
||||
$(bindir)/qemu-system-cris \
|
||||
$(bindir)/qemu-system-m68k \
|
||||
$(bindir)/qemu-system-microblaze \
|
||||
$(bindir)/qemu-system-mips \
|
||||
$(bindir)/qemu-system-mipsel \
|
||||
$(bindir)/qemu-system-mips64 \
|
||||
$(bindir)/qemu-system-mips64el \
|
||||
$(bindir)/qemu-system-ppc \
|
||||
$(bindir)/qemu-system-ppcemb \
|
||||
$(bindir)/qemu-system-ppc64 \
|
||||
$(bindir)/qemu-system-sh4 \
|
||||
$(bindir)/qemu-system-sh4eb \
|
||||
$(bindir)/qemu-system-sparc \
|
||||
$(bindir)/qemu-system-arm \
|
||||
$(bindir)/qemu-i386 \
|
||||
$(bindir)/qemu-x86_64 \
|
||||
$(bindir)/qemu-alpha \
|
||||
$(bindir)/qemu-arm \
|
||||
$(bindir)/qemu-armeb \
|
||||
$(bindir)/qemu-cris \
|
||||
$(bindir)/qemu-m68k \
|
||||
$(bindir)/qemu-microblaze \
|
||||
$(bindir)/qemu-mips \
|
||||
$(bindir)/qemu-mipsel \
|
||||
$(bindir)/qemu-ppc \
|
||||
$(bindir)/qemu-ppc64 \
|
||||
$(bindir)/qemu-ppc64abi32 \
|
||||
$(bindir)/qemu-sh4 \
|
||||
$(bindir)/qemu-sh4eb \
|
||||
$(bindir)/qemu-sparc \
|
||||
$(bindir)/qemu-sparc64 \
|
||||
$(bindir)/qemu-sparc32plus \
|
||||
$(bindir)/qemu-img \
|
||||
$(bindir)/qemu-nbd \
|
||||
$(bindir)/qemu-arm \
|
||||
$(bindir)/qemu-armeb \
|
||||
$(bindir)/qemu-sparc \
|
||||
$(bindir)/qemu-ppc \
|
||||
$(bindir)/qemu-mips \
|
||||
$(bindir)/qemu-mipsel \
|
||||
$(bindir)/qemu-img \
|
||||
$(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)
|
||||
ifneq ($(wildcard .depend),)
|
||||
include .depend
|
||||
endif
|
||||
|
||||
53
Makefile.hw
53
Makefile.hw
@@ -1,53 +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
|
||||
|
||||
VPATH=$(SRC_PATH):$(SRC_PATH)/hw
|
||||
|
||||
QEMU_CFLAGS+=-I.. -I$(SRC_PATH)/fpu
|
||||
|
||||
obj-y =
|
||||
obj-y += loader.o
|
||||
obj-y += virtio.o
|
||||
obj-y += fw_cfg.o
|
||||
obj-y += watchdog.o
|
||||
obj-$(CONFIG_ECC) += ecc.o
|
||||
obj-$(CONFIG_NAND) += nand.o
|
||||
|
||||
obj-$(CONFIG_M48T59) += m48t59.o
|
||||
obj-$(CONFIG_ESCC) += escc.o
|
||||
|
||||
# PCI watchdog devices
|
||||
obj-y += wdt_i6300esb.o
|
||||
|
||||
obj-y += msix.o
|
||||
|
||||
# PCI network cards
|
||||
obj-y += ne2000.o
|
||||
|
||||
obj-$(CONFIG_SMC91C111) += smc91c111.o
|
||||
obj-$(CONFIG_LAN9118) += lan9118.o
|
||||
|
||||
# SCSI layer
|
||||
obj-y += lsi53c895a.o
|
||||
obj-$(CONFIG_ESP) += esp.o
|
||||
|
||||
obj-y += dma-helpers.o sysbus.o isa-bus.o
|
||||
obj-$(CONFIG_QDEV_ADDR) += qdev-addr.o
|
||||
|
||||
all: $(HWLIB)
|
||||
# Dummy command so that make thinks it has done something
|
||||
@true
|
||||
|
||||
$(HWLIB): $(obj-y)
|
||||
|
||||
clean:
|
||||
rm -f *.o *.d *.a *~
|
||||
|
||||
# Include automatically generated dependency files
|
||||
-include $(wildcard *.d */*.d)
|
||||
744
Makefile.target
744
Makefile.target
@@ -1,336 +1,506 @@
|
||||
# -*- Mode: makefile -*-
|
||||
|
||||
# This needs to be defined before rules.mak
|
||||
GENERATED_HEADERS = config-target.h
|
||||
|
||||
include ../config-host.mak
|
||||
include config-devices.mak
|
||||
include config-target.mak
|
||||
include $(SRC_PATH)/rules.mak
|
||||
include 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)
|
||||
VPATH=$(SRC_PATH):$(TARGET_PATH):$(SRC_PATH)/hw
|
||||
QEMU_CFLAGS+= -I.. -I$(TARGET_PATH) -DNEED_CPU_H
|
||||
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: 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
|
||||
$(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
|
||||
|
||||
#########################################################
|
||||
# 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
|
||||
libobj-$(CONFIG_NEED_MMU) += mmu.o
|
||||
libobj-$(TARGET_ARM) += neon_helper.o iwmmxt_helper.o
|
||||
libobj-$(TARGET_ALPHA) += alpha_palcode.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
|
||||
|
||||
# NOTE: the disassembler code is only needed for debugging
|
||||
libobj-y += disas.o
|
||||
libobj-$(CONFIG_ALPHA_DIS) += alpha-dis.o
|
||||
libobj-$(CONFIG_ARM_DIS) += arm-dis.o
|
||||
libobj-$(CONFIG_CRIS_DIS) += cris-dis.o
|
||||
libobj-$(CONFIG_HPPA_DIS) += hppa-dis.o
|
||||
libobj-$(CONFIG_I386_DIS) += i386-dis.o
|
||||
libobj-$(CONFIG_M68K_DIS) += m68k-dis.o
|
||||
libobj-$(CONFIG_MICROBLAZE_DIS) += microblaze-dis.o
|
||||
libobj-$(CONFIG_MIPS_DIS) += mips-dis.o
|
||||
libobj-$(CONFIG_PPC_DIS) += ppc-dis.o
|
||||
libobj-$(CONFIG_S390_DIS) += s390-dis.o
|
||||
libobj-$(CONFIG_SH4_DIS) += sh4-dis.o
|
||||
libobj-$(CONFIG_SPARC_DIS) += sparc-dis.o
|
||||
|
||||
# libqemu
|
||||
|
||||
libqemu.a: $(libobj-y)
|
||||
|
||||
translate.o: translate.c cpu.h
|
||||
|
||||
translate-all.o: translate-all.c cpu.h
|
||||
|
||||
tcg/tcg.o: 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)
|
||||
|
||||
# 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)
|
||||
|
||||
#########################################################
|
||||
# Linux user emulator target
|
||||
|
||||
ifdef CONFIG_LINUX_USER
|
||||
|
||||
VPATH+=:$(SRC_PATH)/linux-user:$(SRC_PATH)/linux-user/$(TARGET_ABI_DIR)
|
||||
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
|
||||
|
||||
obj-$(TARGET_HAS_BFLT) += flatload.o
|
||||
obj-$(TARGET_HAS_ELFLOAD32) += elfload32.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
|
||||
|
||||
ARLIBS=../libuser/libuser.a libqemu.a
|
||||
|
||||
endif #CONFIG_LINUX_USER
|
||||
|
||||
#########################################################
|
||||
# Darwin user emulator target
|
||||
|
||||
ifdef CONFIG_DARWIN_USER
|
||||
|
||||
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
|
||||
|
||||
ARLIBS=../libuser/libuser.a libqemu.a
|
||||
|
||||
endif #CONFIG_DARWIN_USER
|
||||
|
||||
#########################################################
|
||||
# BSD user emulator target
|
||||
|
||||
ifdef CONFIG_BSD_USER
|
||||
|
||||
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
|
||||
|
||||
ARLIBS=../libuser/libuser.a libqemu.a
|
||||
|
||||
endif #CONFIG_BSD_USER
|
||||
|
||||
#########################################################
|
||||
# System emulator target
|
||||
ifdef CONFIG_SOFTMMU
|
||||
|
||||
obj-y = vl.o async.o monitor.o pci.o pci_host.o pcie_host.o machine.o gdbstub.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-console.o virtio-pci.o
|
||||
obj-$(CONFIG_KVM) += kvm.o kvm-all.o
|
||||
obj-$(CONFIG_ISA_MMIO) += isa_mmio.o
|
||||
LIBS+=-lz
|
||||
|
||||
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
|
||||
|
||||
QEMU_CFLAGS += $(VNC_TLS_CFLAGS)
|
||||
QEMU_CFLAGS += $(VNC_SASL_CFLAGS)
|
||||
|
||||
# xen backend driver support
|
||||
obj-$(CONFIG_XEN) += xen_machine_pv.o xen_domainbuild.o
|
||||
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
|
||||
obj-$(CONFIG_USB_OHCI) += usb-ohci.o
|
||||
VL_OBJS+= usb.o usb-hub.o usb-uhci.o usb-linux.o usb-hid.o
|
||||
|
||||
# PCI network cards
|
||||
obj-y += eepro100.o
|
||||
obj-y += pcnet.o
|
||||
obj-y += rtl8139.o
|
||||
obj-y += e1000.o
|
||||
VL_OBJS+= ne2000.o rtl8139.o
|
||||
|
||||
ifeq ($(TARGET_BASE_ARCH), i386)
|
||||
# Hardware support
|
||||
obj-i386-y = ide/core.o ide/qdev.o ide/isa.o ide/pci.o ide/piix.o
|
||||
obj-i386-y += pckbd.o $(sound-obj-y) dma.o
|
||||
obj-i386-y += vga.o vga-pci.o vga-isa.o
|
||||
obj-i386-y += fdc.o mc146818rtc.o serial.o i8259.o i8254.o pcspk.o pc.o
|
||||
obj-i386-y += cirrus_vga.o apic.o ioapic.o parallel.o acpi.o piix_pci.o
|
||||
obj-i386-y += usb-uhci.o vmmouse.o vmport.o vmware_vga.o hpet.o
|
||||
obj-i386-y += device-hotplug.o pci-hotplug.o smbios.o wdt_ib700.o
|
||||
obj-i386-y += ne2000-isa.o
|
||||
|
||||
# shared objects
|
||||
obj-ppc-y = ppc.o ide/core.o ide/qdev.o ide/isa.o ide/pci.o ide/macio.o
|
||||
obj-ppc-y += ide/cmd646.o
|
||||
obj-ppc-y += vga.o vga-pci.o $(sound-obj-y) dma.o openpic.o
|
||||
# PREP target
|
||||
obj-ppc-y += pckbd.o serial.o i8259.o i8254.o fdc.o mc146818rtc.o
|
||||
obj-ppc-y += prep_pci.o ppc_prep.o ne2000-isa.o
|
||||
# Mac shared devices
|
||||
obj-ppc-y += macio.o cuda.o adb.o mac_nvram.o mac_dbdma.o
|
||||
# OldWorld PowerMac
|
||||
obj-ppc-y += heathrow_pic.o grackle_pci.o ppc_oldworld.o
|
||||
# NewWorld PowerMac
|
||||
obj-ppc-y += unin_pci.o ppc_newworld.o
|
||||
# PowerPC 4xx boards
|
||||
obj-ppc-y += pflash_cfi02.o 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_pci.o 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_timer.o mips_int.o dma.o vga.o serial.o i8254.o i8259.o rc4030.o
|
||||
obj-mips-y += vga-pci.o vga-isa.o vga-isa-mm.o
|
||||
obj-mips-y += g364fb.o jazz_led.o dp8393x.o
|
||||
obj-mips-y += ide/core.o ide/qdev.o ide/isa.o ide/pci.o ide/piix.o
|
||||
obj-mips-y += gt64xxx.o pckbd.o fdc.o mc146818rtc.o usb-uhci.o acpi.o ds1225y.o
|
||||
obj-mips-y += piix4.o parallel.o cirrus_vga.o pcspk.o $(sound-obj-y)
|
||||
obj-mips-y += mipsnet.o ne2000-isa.o
|
||||
obj-mips-y += pflash_cfi01.o
|
||||
obj-mips-y += vmware_vga.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-y += pflash_cfi02.o
|
||||
|
||||
obj-microblaze-$(CONFIG_FDT) += device_tree.o
|
||||
|
||||
# Boards
|
||||
obj-cris-y = cris_pic_cpu.o etraxfs.o 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
|
||||
|
||||
obj-cris-y += pflash_cfi02.o
|
||||
|
||||
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)
|
||||
obj-sparc-y = sun4u.o pckbd.o apb_pci.o
|
||||
obj-sparc-y += ide/core.o ide/qdev.o ide/pci.o ide/cmd646.o
|
||||
obj-sparc-y += vga.o vga-pci.o
|
||||
obj-sparc-y += fdc.o mc146818rtc.o serial.o
|
||||
obj-sparc-y += cirrus_vga.o parallel.o
|
||||
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
|
||||
obj-sparc-y = sun4m.o lance.o tcx.o iommu.o slavio_intctl.o
|
||||
obj-sparc-y += slavio_timer.o slavio_misc.o fdc.o sparc32_dma.o
|
||||
obj-sparc-y += cs4231.o eccmemctl.o sbi.o sun4c_intctl.o
|
||||
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
|
||||
|
||||
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 += pflash_cfi01.o gumstix.o
|
||||
obj-arm-y += zaurus.o ide/core.o ide/microdrive.o serial.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
|
||||
obj-arm-y += omap2.o omap_dss.o soc_dma.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 pflash_cfi02.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
|
||||
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
|
||||
|
||||
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 serial.o
|
||||
obj-sh4-y += ide/core.o ide/mmio.o
|
||||
ifeq ($(ARCH),ia64)
|
||||
VL_LDFLAGS+=-Wl,-G0 -Wl,-T,$(SRC_PATH)/ia64.ld
|
||||
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
|
||||
ifdef CONFIG_WIN32
|
||||
SDL_LIBS := $(filter-out -mwindows, $(SDL_LIBS)) -mconsole
|
||||
endif
|
||||
|
||||
obj-s390x-y = s390-virtio-bus.o s390-virtio.o
|
||||
$(QEMU_SYSTEM): $(VL_OBJS) libqemu.a
|
||||
$(CC) $(VL_LDFLAGS) -o $@ $^ $(LIBS) $(SDL_LIBS) $(COCOA_LIBS) $(VL_LIBS)
|
||||
|
||||
main.o vl.o: QEMU_CFLAGS+=$(GPROF_CFLAGS)
|
||||
cocoa.o: cocoa.m
|
||||
$(CC) $(CFLAGS) $(DEFINES) -c -o $@ $<
|
||||
|
||||
vl.o: QEMU_CFLAGS+=$(SDL_CFLAGS)
|
||||
sdl.o: sdl.c keymaps.c sdl_keysym.h
|
||||
$(CC) $(CFLAGS) $(DEFINES) $(SDL_CFLAGS) -c -o $@ $<
|
||||
|
||||
vl.o: qemu-options.h
|
||||
vnc.o: vnc.c keymaps.c sdl_keysym.h vnchextile.h
|
||||
$(CC) $(CFLAGS) $(DEFINES) -c -o $@ $<
|
||||
|
||||
monitor.o: qemu-monitor.h
|
||||
sdlaudio.o: sdlaudio.c
|
||||
$(CC) $(CFLAGS) $(DEFINES) $(SDL_CFLAGS) -c -o $@ $<
|
||||
|
||||
ARLIBS=../libqemu_common.a libqemu.a $(HWLIB)
|
||||
depend: $(SRCS)
|
||||
$(CC) -MM $(CFLAGS) $(DEFINES) $^ 1>.depend
|
||||
|
||||
endif # CONFIG_SOFTMMU
|
||||
vldepend: $(VL_OBJS:.o=.c)
|
||||
$(CC) -MM $(CFLAGS) $(DEFINES) $^ 1>.depend
|
||||
|
||||
obj-$(CONFIG_GDBSTUB_XML) += gdbstub-xml.o
|
||||
# libqemu
|
||||
|
||||
$(QEMU_PROG): $(obj-y) $(obj-$(TARGET_BASE_ARCH)-y) $(ARLIBS)
|
||||
$(call LINK,$(obj-y) $(obj-$(TARGET_BASE_ARCH)-y))
|
||||
libqemu.a: $(LIBOBJS)
|
||||
rm -f $@
|
||||
$(AR) rcs $@ $(LIBOBJS)
|
||||
|
||||
translate.o: translate.c gen-op.h opc.h cpu.h
|
||||
|
||||
gdbstub-xml.c: $(TARGET_XML_FILES) feature_to_c.sh
|
||||
$(call quiet-command,rm -f $@ && $(SHELL) $(SRC_PATH)/feature_to_c.sh $@ $(TARGET_XML_FILES)," GEN $(TARGET_DIR)$@")
|
||||
translate-all.o: translate-all.c opc.h cpu.h
|
||||
|
||||
qemu-options.h: $(SRC_PATH)/qemu-options.hx
|
||||
$(call quiet-command,sh $(SRC_PATH)/hxtool -h < $< > $@," GEN $(TARGET_DIR)$@")
|
||||
translate-op.o: translate-all.c op.h opc.h cpu.h
|
||||
|
||||
qemu-monitor.h: $(SRC_PATH)/qemu-monitor.hx
|
||||
$(call quiet-command,sh $(SRC_PATH)/hxtool -h < $< > $@," GEN $(TARGET_DIR)$@")
|
||||
op.h: op.o $(DYNGEN)
|
||||
$(DYNGEN) -o $@ $<
|
||||
|
||||
opc.h: op.o $(DYNGEN)
|
||||
$(DYNGEN) -c -o $@ $<
|
||||
|
||||
gen-op.h: op.o $(DYNGEN)
|
||||
$(DYNGEN) -g -o $@ $<
|
||||
|
||||
op.o: op.c
|
||||
$(CC) $(OP_CFLAGS) $(DEFINES) -c -o $@ $<
|
||||
|
||||
helper.o: helper.c
|
||||
$(CC) $(HELPER_CFLAGS) $(DEFINES) -c -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
|
||||
|
||||
ifeq ($(TARGET_ARCH), arm)
|
||||
op.o: op.c op_template.h
|
||||
pl110.o: pl110_template.h
|
||||
endif
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
ifeq ($(TARGET_ARCH), mips)
|
||||
op.o: op.c op_template.c op_mem.c
|
||||
op_helper.o: op_helper_mem.c
|
||||
endif
|
||||
|
||||
loader.o: loader.c elf_ops.h
|
||||
|
||||
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
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CFLAGS) $(DEFINES) -c -o $@ $<
|
||||
|
||||
%.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-options.h 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
|
||||
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,32 +0,0 @@
|
||||
# Makefile for qemu target independent user files.
|
||||
|
||||
include ../config-host.mak
|
||||
include $(SRC_PATH)/rules.mak
|
||||
-include config.mak
|
||||
|
||||
.PHONY: all
|
||||
|
||||
# Do not take %.o from $(SRC_PATH), only %.c and %.h
|
||||
# All %.o for user targets should be built with -fpie, when
|
||||
# configured with --enable-user-pie, so we don't want to
|
||||
# take %.o from $(SRC_PATH), since they built without -fpie
|
||||
vpath %.c %.h $(SRC_PATH)
|
||||
|
||||
QEMU_CFLAGS+=-I..
|
||||
|
||||
obj-y =
|
||||
obj-y += envlist.o path.o
|
||||
obj-y += tcg-runtime.o host-utils.o
|
||||
obj-y += cutils.o cache-utils.o
|
||||
|
||||
all: libuser.a
|
||||
# Dummy command so that make thinks it has done something
|
||||
@true
|
||||
|
||||
libuser.a: $(obj-y)
|
||||
|
||||
clean:
|
||||
rm -f *.o *.d *.a *~
|
||||
|
||||
# Include automatically generated dependency files
|
||||
-include $(wildcard *.d */*.d)
|
||||
63
QMP/README
63
QMP/README
@@ -1,63 +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 has the following features:
|
||||
|
||||
- Lightweight, text-based, easy to parse data format
|
||||
- Asynchronous events support
|
||||
- Stability
|
||||
|
||||
For more information, please, refer to the following files:
|
||||
|
||||
o qmp-spec.txt QEMU Monitor Protocol current specification
|
||||
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
|
||||
|
||||
[1] http://www.json.org
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
To enable QMP, QEMU has to be started in "control mode". There are
|
||||
two ways of doing this, the simplest one is using the the '-qmp'
|
||||
command-line option.
|
||||
|
||||
For example:
|
||||
|
||||
$ qemu [...] -qmp tcp:localhost:4444,server
|
||||
|
||||
Will start QEMU in control mode, waiting for a client TCP connection
|
||||
on localhost port 4444.
|
||||
|
||||
It is also possible to use the '-mon' command-line option to have
|
||||
more complex combinations. Please, refer to the QEMU's manpage for
|
||||
more information.
|
||||
|
||||
Simple Testing
|
||||
--------------
|
||||
|
||||
To manually test QMP one can connect with telnet and issue commands:
|
||||
|
||||
$ telnet localhost 4444
|
||||
Trying 127.0.0.1...
|
||||
Connected to localhost.
|
||||
Escape character is '^]'.
|
||||
{"QMP": {"capabilities": []}}
|
||||
{ "execute": "query-version" }
|
||||
{"return": {"qemu": "0.11.50", "package": ""}}
|
||||
|
||||
Contact
|
||||
-------
|
||||
|
||||
http://www.linux-kvm.org/page/MonitorProtocol
|
||||
Luiz Fernando N. Capitulino <lcapitulino@redhat.com>
|
||||
@@ -1,26 +0,0 @@
|
||||
QEMU Monitor Protocol: Events
|
||||
=============================
|
||||
|
||||
1 SHUTDOWN
|
||||
-----------
|
||||
|
||||
Description: Issued when the Virtual Machine is powered down.
|
||||
Data: None.
|
||||
|
||||
2 RESET
|
||||
-------
|
||||
|
||||
Description: Issued when the Virtual Machine is reseted.
|
||||
Data: None.
|
||||
|
||||
3 STOP
|
||||
------
|
||||
|
||||
Description: Issued when the Virtual Machine is stopped.
|
||||
Data: None.
|
||||
|
||||
4 DEBUG
|
||||
-------
|
||||
|
||||
Description: Issued when the Virtual Machine enters debug mode.
|
||||
Data: None.
|
||||
@@ -1,72 +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()
|
||||
|
||||
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()
|
||||
203
QMP/qmp-spec.txt
203
QMP/qmp-spec.txt
@@ -1,203 +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
|
||||
waiting for commands.
|
||||
|
||||
The format is:
|
||||
|
||||
{ "QMP": { "capabilities": json-array } }
|
||||
|
||||
Where,
|
||||
|
||||
- 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": {"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. Compatibility Considerations
|
||||
--------------------------------
|
||||
|
||||
In order to achieve maximum compatibility between versions, 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
|
||||
|
||||
Additionally, Clients should always:
|
||||
|
||||
- Check the capabilities json-array at connection time
|
||||
- Check the availability of commands with 'query-commands' before issuing them
|
||||
|
||||
5. Recommendations to Client implementors
|
||||
-----------------------------------------
|
||||
|
||||
5.1 The Server should be always started in pause mode, thus the Client is
|
||||
able to perform any setup procedure without the risk of race conditions
|
||||
and related problems
|
||||
72
QMP/qmp.py
72
QMP/qmp.py
@@ -1,72 +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:
|
||||
return json.loads(self.sock.recv(1024))
|
||||
except ValueError:
|
||||
return
|
||||
|
||||
def __init__(self, filename):
|
||||
self.filename = filename
|
||||
self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
||||
32
QMP/vm-info
32
QMP/vm-info
@@ -1,32 +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()
|
||||
|
||||
for cmd in [ 'version', 'hpet', 'kvm', 'status', 'uuid', 'balloon' ]:
|
||||
print cmd + ': ' + str(qemu.send('query-' + cmd))
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
2
README
2
README
@@ -1,3 +1,3 @@
|
||||
Read the documentation in qemu-doc.html.
|
||||
|
||||
Fabrice Bellard.
|
||||
Fabrice Bellard.
|
||||
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:
|
||||
*/
|
||||
17
aes.c
17
aes.c
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
*
|
||||
*
|
||||
* aes.c - integrated in QEMU by Fabrice Bellard from the OpenSSL project.
|
||||
*/
|
||||
/*
|
||||
@@ -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]))
|
||||
@@ -1264,7 +1267,7 @@ void AES_decrypt(const unsigned char *in, unsigned char *out,
|
||||
|
||||
void AES_cbc_encrypt(const unsigned char *in, unsigned char *out,
|
||||
const unsigned long length, const AES_KEY *key,
|
||||
unsigned char *ivec, const int enc)
|
||||
unsigned char *ivec, const int enc)
|
||||
{
|
||||
|
||||
unsigned long n;
|
||||
@@ -1291,7 +1294,7 @@ void AES_cbc_encrypt(const unsigned char *in, unsigned char *out,
|
||||
AES_encrypt(tmp, tmp, key);
|
||||
memcpy(out, tmp, AES_BLOCK_SIZE);
|
||||
memcpy(ivec, tmp, AES_BLOCK_SIZE);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
while (len >= AES_BLOCK_SIZE) {
|
||||
memcpy(tmp, in, AES_BLOCK_SIZE);
|
||||
@@ -1309,6 +1312,6 @@ void AES_cbc_encrypt(const unsigned char *in, unsigned char *out,
|
||||
for(n=0; n < len; ++n)
|
||||
out[n] = tmp[n] ^ ivec[n];
|
||||
memcpy(ivec, tmp, AES_BLOCK_SIZE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
228
aio.c
228
aio.c
@@ -1,228 +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) {
|
||||
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);
|
||||
}
|
||||
125
alpha-dis.c
125
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 */
|
||||
@@ -373,7 +374,7 @@ const struct alpha_operand alpha_operands[] =
|
||||
|
||||
/* The signed "23-bit" aligned displacement of Branch format insns */
|
||||
#define BDISP (MDISP + 1)
|
||||
{ 21, 0, BFD_RELOC_23_PCREL_S2,
|
||||
{ 21, 0, BFD_RELOC_23_PCREL_S2,
|
||||
AXP_OPERAND_RELATIVE, insert_bdisp, extract_bdisp },
|
||||
|
||||
/* The 26-bit PALcode function */
|
||||
@@ -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: */
|
||||
|
||||
468
arm-semi.c
468
arm-semi.c
@@ -1,468 +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:
|
||||
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
1140
audio/audio.c
1140
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,42 +473,43 @@ 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;
|
||||
sw->vol = nominal_volume;
|
||||
sw->callback.fn = callback_fn;
|
||||
sw->callback.opaque = callback_opaque;
|
||||
if (sw) {
|
||||
sw->vol = nominal_volume;
|
||||
sw->callback.fn = callback_fn;
|
||||
sw->callback.opaque = callback_opaque;
|
||||
|
||||
#ifdef DAC
|
||||
if (live) {
|
||||
int mixed =
|
||||
(live << old_sw->info.shift)
|
||||
* old_sw->info.bytes_per_second
|
||||
/ sw->info.bytes_per_second;
|
||||
if (live) {
|
||||
int mixed =
|
||||
(live << old_sw->info.shift)
|
||||
* old_sw->info.bytes_per_second
|
||||
/ sw->info.bytes_per_second;
|
||||
|
||||
#ifdef DEBUG_PLIVE
|
||||
dolog ("Silence will be mixed %d\n", mixed);
|
||||
dolog ("Silence will be mixed %d\n", mixed);
|
||||
#endif
|
||||
sw->total_hw_samples_mixed += mixed;
|
||||
}
|
||||
sw->total_hw_samples_mixed += mixed;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_AUDIO
|
||||
dolog ("%s\n", name);
|
||||
audio_pcm_print_info ("hw", &sw->hw->info);
|
||||
audio_pcm_print_info ("sw", &sw->info);
|
||||
dolog ("%s\n", name);
|
||||
audio_pcm_print_info ("hw", &sw->hw->info);
|
||||
audio_pcm_print_info ("sw", &sw->info);
|
||||
#endif
|
||||
}
|
||||
|
||||
return sw;
|
||||
|
||||
@@ -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 16 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);
|
||||
samples = bytes >> hw->info.shift;
|
||||
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;
|
||||
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;
|
||||
int live = audio_pcm_hw_get_live_in (hw);
|
||||
int dead = hw->samples - live;
|
||||
int samples = 0;
|
||||
int samples;
|
||||
|
||||
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 ());
|
||||
bytes = audio_MIN (bytes, INT_MAX);
|
||||
samples = bytes >> hw->info.shift;
|
||||
samples = audio_MIN (samples, dead);
|
||||
|
||||
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,19 +21,11 @@
|
||||
* 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"
|
||||
@@ -42,11 +34,10 @@ 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 {
|
||||
@@ -55,6 +46,7 @@ typedef struct OSSVoiceIn {
|
||||
int fd;
|
||||
int nfrags;
|
||||
int fragsize;
|
||||
int old_optr;
|
||||
} OSSVoiceIn;
|
||||
|
||||
static struct {
|
||||
@@ -63,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 {
|
||||
@@ -116,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);
|
||||
@@ -185,7 +142,7 @@ static int oss_to_audfmt (int ossfmt, audfmt_e *fmt, int *endianness)
|
||||
{
|
||||
switch (ossfmt) {
|
||||
case AFMT_S8:
|
||||
*endianness = 0;
|
||||
*endianness =0;
|
||||
*fmt = AUD_FMT_S8;
|
||||
break;
|
||||
|
||||
@@ -240,17 +197,13 @@ static int oss_open (int in, struct oss_params *req,
|
||||
struct oss_params *obt, int *pfd)
|
||||
{
|
||||
int fd;
|
||||
int version;
|
||||
int oflags = conf.exclusive ? O_EXCL : 0;
|
||||
int mmmmssss;
|
||||
audio_buf_info abinfo;
|
||||
int fmt, freq, nchannels;
|
||||
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;
|
||||
@@ -276,38 +229,16 @@ 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;
|
||||
}
|
||||
|
||||
if (ioctl (fd, OSS_GETVERSION, &version)) {
|
||||
oss_logerr2 (errno, typ, "Failed to get OSS version\n");
|
||||
version = 0;
|
||||
}
|
||||
|
||||
if (conf.debug) {
|
||||
dolog ("OSS version = %#x\n", version);
|
||||
}
|
||||
|
||||
#ifdef SNDCTL_DSP_POLICY
|
||||
if (conf.policy >= 0 && 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;
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
int mmmmssss = (req->nfrags << 16) | ctz32 (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;
|
||||
}
|
||||
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)) {
|
||||
@@ -315,12 +246,6 @@ static int oss_open (int in, struct oss_params *req,
|
||||
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;
|
||||
@@ -349,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) {
|
||||
@@ -408,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 {
|
||||
@@ -419,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",
|
||||
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);
|
||||
}
|
||||
if (abinfo.bytes < 0 || abinfo.bytes > bufsize) {
|
||||
ldebug ("warning: Invalid available size, size=%d bufsize=%d\n",
|
||||
abinfo.bytes, bufsize);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -442,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;
|
||||
}
|
||||
|
||||
@@ -472,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;
|
||||
@@ -480,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;
|
||||
|
||||
@@ -503,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;
|
||||
|
||||
@@ -519,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,
|
||||
@@ -529,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) {
|
||||
@@ -585,48 +527,25 @@ static int oss_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||
int trig;
|
||||
OSSVoiceOut *oss = (OSSVoiceOut *) hw;
|
||||
|
||||
if (!oss->mmapped) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
audio_pcm_info_clear_buf (&hw->info, oss->pcm_buf, hw->samples);
|
||||
trig = PCM_ENABLE_OUTPUT;
|
||||
if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
|
||||
oss_logerr (
|
||||
errno,
|
||||
"SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n"
|
||||
);
|
||||
return -1;
|
||||
}
|
||||
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) {
|
||||
oss_logerr (
|
||||
errno,
|
||||
"SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n"
|
||||
);
|
||||
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) {
|
||||
@@ -638,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;
|
||||
@@ -646,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;
|
||||
|
||||
@@ -668,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;
|
||||
|
||||
@@ -716,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) {
|
||||
@@ -732,6 +654,7 @@ static int oss_run_in (HWVoiceIn *hw)
|
||||
bufs[0].len = dead << hwshift;
|
||||
}
|
||||
|
||||
|
||||
for (i = 0; i < 2; ++i) {
|
||||
ssize_t nread;
|
||||
|
||||
@@ -781,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;
|
||||
}
|
||||
|
||||
@@ -821,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 SNDCTL_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;
|
||||
|
||||
183
audio/sdlaudio.c
183
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"
|
||||
@@ -41,17 +31,17 @@
|
||||
typedef struct SDLVoiceOut {
|
||||
HWVoiceOut hw;
|
||||
int live;
|
||||
int rpos;
|
||||
int decr;
|
||||
int pending;
|
||||
} SDLVoiceOut;
|
||||
|
||||
static struct {
|
||||
int nb_samples;
|
||||
} conf = {
|
||||
.nb_samples = 1024
|
||||
1024
|
||||
};
|
||||
|
||||
static struct SDLAudioState {
|
||||
struct SDLAudioState {
|
||||
int exit;
|
||||
SDL_mutex *mutex;
|
||||
SDL_sem *sem;
|
||||
@@ -187,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;
|
||||
}
|
||||
|
||||
@@ -225,10 +204,6 @@ static void sdl_callback (void *opaque, Uint8 *buf, int len)
|
||||
HWVoiceOut *hw = &sdl->hw;
|
||||
int samples = len >> hw->info.shift;
|
||||
|
||||
if (sdl_lock (s, "sdl_callback")) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (s->exit) {
|
||||
return;
|
||||
}
|
||||
@@ -236,34 +211,50 @@ static void sdl_callback (void *opaque, Uint8 *buf, int len)
|
||||
while (samples) {
|
||||
int to_mix, decr;
|
||||
|
||||
while (!sdl->pending) {
|
||||
if (sdl_unlock (s, "sdl_callback")) {
|
||||
return;
|
||||
}
|
||||
|
||||
sdl_wait (s, "sdl_callback");
|
||||
if (s->exit) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (sdl_lock (s, "sdl_callback")) {
|
||||
return;
|
||||
}
|
||||
sdl->pending += sdl->live;
|
||||
sdl->live = 0;
|
||||
/* dolog ("in callback samples=%d\n", samples); */
|
||||
sdl_wait (s, "sdl_callback");
|
||||
if (s->exit) {
|
||||
return;
|
||||
}
|
||||
|
||||
to_mix = audio_MIN (samples, sdl->pending);
|
||||
decr = audio_pcm_hw_clip_out (hw, buf, to_mix, 0);
|
||||
buf += decr << hw->info.shift;
|
||||
samples -= decr;
|
||||
sdl->decr += decr;
|
||||
sdl->pending -= decr;
|
||||
}
|
||||
if (sdl_lock (s, "sdl_callback")) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (sdl_unlock (s, "sdl_callback")) {
|
||||
return;
|
||||
if (audio_bug (AUDIO_FUNC, sdl->live < 0 || sdl->live > hw->samples)) {
|
||||
dolog ("sdl->live=%d hw->samples=%d\n",
|
||||
sdl->live, hw->samples);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!sdl->live) {
|
||||
goto again;
|
||||
}
|
||||
|
||||
/* dolog ("in callback live=%d\n", live); */
|
||||
to_mix = audio_MIN (samples, sdl->live);
|
||||
decr = to_mix;
|
||||
while (to_mix) {
|
||||
int chunk = audio_MIN (to_mix, hw->samples - hw->rpos);
|
||||
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;
|
||||
}
|
||||
samples -= decr;
|
||||
sdl->live -= decr;
|
||||
sdl->decr += decr;
|
||||
|
||||
again:
|
||||
if (sdl_unlock (s, "sdl_callback")) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* dolog ("done len=%d\n", len); */
|
||||
}
|
||||
|
||||
static int sdl_write_out (SWVoiceOut *sw, void *buf, int len)
|
||||
@@ -271,25 +262,36 @@ 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;
|
||||
}
|
||||
|
||||
sdl->live = live;
|
||||
decr = sdl->decr;
|
||||
sdl->decr = 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,
|
||||
live,
|
||||
sdl->live);
|
||||
}
|
||||
|
||||
decr = audio_MIN (sdl->decr, live);
|
||||
sdl->decr -= decr;
|
||||
|
||||
sdl->live = live - decr;
|
||||
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;
|
||||
}
|
||||
@@ -301,7 +303,7 @@ 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;
|
||||
@@ -310,7 +312,7 @@ static int sdl_init_out (HWVoiceOut *hw, struct audsettings *as)
|
||||
int endianess;
|
||||
int err;
|
||||
audfmt_e effective_fmt;
|
||||
struct audsettings obt_as;
|
||||
audsettings_t obt_as;
|
||||
|
||||
shift <<= as->nchannels == 2;
|
||||
|
||||
@@ -334,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;
|
||||
@@ -398,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)
|
||||
};
|
||||
27
balloon.h
27
balloon.h
@@ -1,27 +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 "cpu-defs.h"
|
||||
|
||||
typedef ram_addr_t (QEMUBalloonEvent)(void *opaque, ram_addr_t target);
|
||||
|
||||
void qemu_add_balloon_handler(QEMUBalloonEvent *func, void *opaque);
|
||||
|
||||
void qemu_balloon(ram_addr_t target);
|
||||
|
||||
ram_addr_t qemu_balloon_status(void);
|
||||
|
||||
#endif
|
||||
@@ -1,9 +1,9 @@
|
||||
/*
|
||||
* Block driver for the various disk image formats used by Bochs
|
||||
* Currently only for "growing" type in read-only mode
|
||||
*
|
||||
*
|
||||
* 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
|
||||
@@ -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,13 +37,13 @@
|
||||
// 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"
|
||||
uint32_t version;
|
||||
uint32_t header; // size of header
|
||||
|
||||
|
||||
union {
|
||||
struct {
|
||||
uint32_t catalog; // num of entries
|
||||
@@ -58,35 +56,14 @@ 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;
|
||||
|
||||
|
||||
int data_offset;
|
||||
|
||||
|
||||
int bitmap_blocks;
|
||||
int extent_blocks;
|
||||
int extent_size;
|
||||
@@ -95,36 +72,34 @@ typedef struct BDRVBochsState {
|
||||
static int bochs_probe(const uint8_t *buf, int buf_size, const char *filename)
|
||||
{
|
||||
const struct bochs_header *bochs = (const void *)buf;
|
||||
|
||||
|
||||
if (buf_size < HEADER_SIZE)
|
||||
return 0;
|
||||
|
||||
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, const char *filename, int flags)
|
||||
static int bochs_open(BlockDriverState *bs, const char *filename)
|
||||
{
|
||||
BDRVBochsState *s = bs->opaque;
|
||||
int fd, i;
|
||||
struct bochs_header bochs;
|
||||
struct bochs_header_v1 header_v1;
|
||||
|
||||
fd = open(filename, O_RDWR | O_BINARY);
|
||||
fd = open(filename, O_RDWR | O_BINARY | O_LARGEFILE);
|
||||
if (fd < 0) {
|
||||
fd = open(filename, O_RDONLY | O_BINARY);
|
||||
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, &bochs, sizeof(bochs)) != sizeof(bochs)) {
|
||||
@@ -134,22 +109,18 @@ static int bochs_open(BlockDriverState *bs, const char *filename, int flags)
|
||||
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;
|
||||
}
|
||||
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 (!s->catalog_bitmap)
|
||||
goto fail;
|
||||
if (read(s->fd, s->catalog_bitmap, s->catalog_size * 4) !=
|
||||
s->catalog_size * 4)
|
||||
goto fail;
|
||||
@@ -160,7 +131,7 @@ static int bochs_open(BlockDriverState *bs, const char *filename, int flags)
|
||||
|
||||
s->bitmap_blocks = 1 + (le32_to_cpu(bochs.extra.redolog.bitmap) - 1) / 512;
|
||||
s->extent_blocks = 1 + (le32_to_cpu(bochs.extra.redolog.extent) - 1) / 512;
|
||||
|
||||
|
||||
s->extent_size = le32_to_cpu(bochs.extra.redolog.extent);
|
||||
|
||||
return 0;
|
||||
@@ -179,7 +150,7 @@ static inline int seek_to_sector(BlockDriverState *bs, int64_t sector_num)
|
||||
// seek to sector
|
||||
extent_index = offset / s->extent_size;
|
||||
extent_offset = (offset % s->extent_size) / 512;
|
||||
|
||||
|
||||
if (s->catalog_bitmap[extent_index] == 0xffffffff)
|
||||
{
|
||||
// fprintf(stderr, "page not allocated [%x - %x:%x]\n",
|
||||
@@ -190,17 +161,17 @@ static inline int seek_to_sector(BlockDriverState *bs, int64_t sector_num)
|
||||
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));
|
||||
|
||||
|
||||
// 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",
|
||||
@@ -209,11 +180,11 @@ static inline int seek_to_sector(BlockDriverState *bs, int64_t sector_num)
|
||||
}
|
||||
|
||||
lseek(s->fd, block_offset, SEEK_SET);
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bochs_read(BlockDriverState *bs, int64_t sector_num,
|
||||
static int bochs_read(BlockDriverState *bs, int64_t sector_num,
|
||||
uint8_t *buf, int nb_sectors)
|
||||
{
|
||||
BDRVBochsState *s = bs->opaque;
|
||||
@@ -242,18 +213,12 @@ static void bochs_close(BlockDriverState *bs)
|
||||
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);
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* QEMU Block driver for CLOOP images
|
||||
*
|
||||
*
|
||||
* Copyright (c) 2004 Johannes E. Schindelin
|
||||
*
|
||||
*
|
||||
* 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
|
||||
@@ -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>
|
||||
|
||||
typedef struct BDRVCloopState {
|
||||
@@ -51,14 +50,14 @@ static int cloop_probe(const uint8_t *buf, int buf_size, const char *filename)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cloop_open(BlockDriverState *bs, const char *filename, 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);
|
||||
s->fd = open(filename, O_RDONLY | O_BINARY | O_LARGEFILE);
|
||||
if (s->fd < 0)
|
||||
return -errno;
|
||||
return -1;
|
||||
bs->read_only = 1;
|
||||
|
||||
/* read header */
|
||||
@@ -76,7 +75,8 @@ cloop_close:
|
||||
|
||||
/* read offsets */
|
||||
offsets_size=s->n_blocks*sizeof(uint64_t);
|
||||
s->offsets=(uint64_t*)qemu_malloc(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++) {
|
||||
@@ -89,12 +89,14 @@ cloop_close:
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
||||
|
||||
s->sectors_per_block = s->block_size/512;
|
||||
bs->total_sectors = s->n_blocks*s->sectors_per_block;
|
||||
return 0;
|
||||
@@ -105,12 +107,12 @@ static inline int cloop_read_block(BDRVCloopState *s,int block_num)
|
||||
if(s->current_block != block_num) {
|
||||
int ret;
|
||||
uint32_t bytes = s->offsets[block_num+1]-s->offsets[block_num];
|
||||
|
||||
|
||||
lseek(s->fd, s->offsets[block_num], SEEK_SET);
|
||||
ret = read(s->fd, s->compressed_block, bytes);
|
||||
if (ret != bytes)
|
||||
if (ret != bytes)
|
||||
return -1;
|
||||
|
||||
|
||||
s->zstream.next_in = s->compressed_block;
|
||||
s->zstream.avail_in = bytes;
|
||||
s->zstream.next_out = s->uncompressed_block;
|
||||
@@ -121,13 +123,13 @@ static inline int cloop_read_block(BDRVCloopState *s,int block_num)
|
||||
ret = inflate(&s->zstream, Z_FINISH);
|
||||
if(ret != Z_STREAM_END || s->zstream.total_out != s->block_size)
|
||||
return -1;
|
||||
|
||||
|
||||
s->current_block = block_num;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cloop_read(BlockDriverState *bs, int64_t sector_num,
|
||||
static int cloop_read(BlockDriverState *bs, int64_t sector_num,
|
||||
uint8_t *buf, int nb_sectors)
|
||||
{
|
||||
BDRVCloopState *s = bs->opaque;
|
||||
@@ -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);
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* 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
|
||||
@@ -22,9 +22,8 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#ifndef _WIN32
|
||||
#include "qemu-common.h"
|
||||
#include "vl.h"
|
||||
#include "block_int.h"
|
||||
#include "module.h"
|
||||
#include <sys/mman.h>
|
||||
|
||||
/**************************************************************/
|
||||
@@ -57,13 +56,13 @@ static int cow_probe(const uint8_t *buf, int buf_size, const char *filename)
|
||||
|
||||
if (buf_size >= sizeof(struct cow_header_v2) &&
|
||||
be32_to_cpu(cow_header->magic) == COW_MAGIC &&
|
||||
be32_to_cpu(cow_header->version) == COW_VERSION)
|
||||
be32_to_cpu(cow_header->version) == COW_VERSION)
|
||||
return 100;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cow_open(BlockDriverState *bs, const char *filename, int flags)
|
||||
static int cow_open(BlockDriverState *bs, const char *filename)
|
||||
{
|
||||
BDRVCowState *s = bs->opaque;
|
||||
int fd;
|
||||
@@ -86,20 +85,36 @@ static int cow_open(BlockDriverState *bs, const char *filename, int flags)
|
||||
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),
|
||||
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 = (void *)mmap(get_mmap_addr(s->cow_bitmap_size),
|
||||
s->cow_bitmap_size,
|
||||
PROT_READ | PROT_WRITE,
|
||||
MAP_SHARED, s->fd, 0);
|
||||
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);
|
||||
@@ -144,35 +159,28 @@ static inline int is_changed(uint8_t *bitmap,
|
||||
return changed;
|
||||
}
|
||||
|
||||
static int cow_is_allocated(BlockDriverState *bs, int64_t sector_num,
|
||||
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,
|
||||
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)
|
||||
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;
|
||||
@@ -180,15 +188,15 @@ static int cow_read(BlockDriverState *bs, int64_t sector_num,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cow_write(BlockDriverState *bs, int64_t sector_num,
|
||||
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)
|
||||
if (ret != nb_sectors * 512)
|
||||
return -1;
|
||||
for (i = 0; i < nb_sectors; i++)
|
||||
cow_set_bit(s->cow_bitmap, sector_num + i);
|
||||
@@ -198,29 +206,21 @@ static int cow_write(BlockDriverState *bs, int64_t sector_num,
|
||||
static void cow_close(BlockDriverState *bs)
|
||||
{
|
||||
BDRVCowState *s = bs->opaque;
|
||||
munmap((void *)s->cow_bitmap_addr, s->cow_bitmap_size);
|
||||
munmap(s->cow_bitmap_addr, s->cow_bitmap_size);
|
||||
close(s->fd);
|
||||
}
|
||||
|
||||
static int cow_create(const char *filename, QEMUOptionParameter *options)
|
||||
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;
|
||||
int64_t image_sectors = 0;
|
||||
const char *image_filename = NULL;
|
||||
|
||||
/* 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++;
|
||||
}
|
||||
if (flags)
|
||||
return -ENOTSUP;
|
||||
|
||||
cow_fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
|
||||
cow_fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE,
|
||||
0644);
|
||||
if (cow_fd < 0)
|
||||
return -1;
|
||||
@@ -228,23 +228,18 @@ static int cow_create(const char *filename, QEMUOptionParameter *options)
|
||||
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;
|
||||
return -1;
|
||||
}
|
||||
if (fstat(fd, &st) != 0) {
|
||||
close(fd);
|
||||
goto mtime_fail;
|
||||
return -1;
|
||||
}
|
||||
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);
|
||||
realpath(image_filename, cow_header.backing_file);
|
||||
}
|
||||
cow_header.sectorsize = cpu_to_be32(512);
|
||||
cow_header.size = cpu_to_be64(image_sectors * 512);
|
||||
@@ -255,45 +250,15 @@ static int cow_create(const char *filename, QEMUOptionParameter *options)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cow_flush(BlockDriverState *bs)
|
||||
{
|
||||
BDRVCowState *s = bs->opaque;
|
||||
qemu_fdatasync(s->fd);
|
||||
}
|
||||
|
||||
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 }
|
||||
BlockDriver bdrv_cow = {
|
||||
"cow",
|
||||
sizeof(BDRVCowState),
|
||||
cow_probe,
|
||||
cow_open,
|
||||
cow_read,
|
||||
cow_write,
|
||||
cow_close,
|
||||
cow_create,
|
||||
cow_is_allocated,
|
||||
};
|
||||
|
||||
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);
|
||||
#endif
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* QEMU Block driver for DMG images
|
||||
*
|
||||
*
|
||||
* Copyright (c) 2004 Johannes E. Schindelin
|
||||
*
|
||||
*
|
||||
* 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
|
||||
@@ -21,15 +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,
|
||||
@@ -74,37 +73,40 @@ static off_t read_uint32(int fd)
|
||||
return be32_to_cpu(buffer);
|
||||
}
|
||||
|
||||
static int dmg_open(BlockDriverState *bs, const char *filename, 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;
|
||||
|
||||
s->fd = open(filename, O_RDONLY | O_BINARY);
|
||||
s->fd = open(filename, O_RDONLY | O_BINARY | O_LARGEFILE);
|
||||
if (s->fd < 0)
|
||||
return -errno;
|
||||
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 */
|
||||
if(lseek(s->fd,-0x1d8,SEEK_END)<0) {
|
||||
goto fail;
|
||||
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 fail;
|
||||
goto dmg_close;
|
||||
if(lseek(s->fd,info_begin,SEEK_SET)<0)
|
||||
goto fail;
|
||||
goto dmg_close;
|
||||
if(read_uint32(s->fd)!=0x100)
|
||||
goto fail;
|
||||
goto dmg_close;
|
||||
if((count = read_uint32(s->fd))==0)
|
||||
goto fail;
|
||||
goto dmg_close;
|
||||
info_end = info_begin+count;
|
||||
if(lseek(s->fd,0xf8,SEEK_CUR)<0)
|
||||
goto fail;
|
||||
goto dmg_close;
|
||||
|
||||
/* read offsets */
|
||||
last_in_offset = last_out_offset = 0;
|
||||
@@ -113,21 +115,21 @@ static int dmg_open(BlockDriverState *bs, const char *filename, int flags)
|
||||
|
||||
count = read_uint32(s->fd);
|
||||
if(count==0)
|
||||
goto fail;
|
||||
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;
|
||||
if(lseek(s->fd,200,SEEK_CUR)<0)
|
||||
goto fail;
|
||||
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(s->fd);
|
||||
@@ -139,7 +141,7 @@ static int dmg_open(BlockDriverState *bs, const char *filename, int flags)
|
||||
chunk_count--;
|
||||
i--;
|
||||
if(lseek(s->fd,36,SEEK_CUR)<0)
|
||||
goto fail;
|
||||
goto dmg_close;
|
||||
continue;
|
||||
}
|
||||
read_uint32(s->fd);
|
||||
@@ -157,17 +159,16 @@ static int dmg_open(BlockDriverState *bs, const char *filename, 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:
|
||||
close(s->fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int is_sector_in_chunk(BDRVDMGState* s,
|
||||
@@ -226,7 +227,7 @@ static inline int dmg_read_chunk(BDRVDMGState *s,int sector_num)
|
||||
|
||||
if (ret != s->lengths[chunk])
|
||||
return -1;
|
||||
|
||||
|
||||
s->zstream.next_in = s->compressed_chunk;
|
||||
s->zstream.avail_in = s->lengths[chunk];
|
||||
s->zstream.next_out = s->uncompressed_chunk;
|
||||
@@ -252,7 +253,7 @@ static inline int dmg_read_chunk(BDRVDMGState *s,int sector_num)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dmg_read(BlockDriverState *bs, int64_t sector_num,
|
||||
static int dmg_read(BlockDriverState *bs, int64_t sector_num,
|
||||
uint8_t *buf, int nb_sectors)
|
||||
{
|
||||
BDRVDMGState *s = bs->opaque;
|
||||
@@ -284,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,541 +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 "monitor.h"
|
||||
#include "block-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 MAX_BLOCKS_READ 10000
|
||||
#define BLOCKS_READ_CHANGE 100
|
||||
#define INITIAL_BLOCKS_READ 100
|
||||
|
||||
//#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 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;
|
||||
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;
|
||||
} 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 void blk_mig_read_cb(void *opaque, int ret)
|
||||
{
|
||||
BlkMigBlock *blk = opaque;
|
||||
|
||||
blk->ret = ret;
|
||||
|
||||
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, int is_async)
|
||||
{
|
||||
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;
|
||||
|
||||
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->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++;
|
||||
} else {
|
||||
if (bdrv_read(bs, cur_sector, blk->buf, nr_sectors) < 0) {
|
||||
goto error;
|
||||
}
|
||||
blk_send(f, blk);
|
||||
|
||||
qemu_free(blk->buf);
|
||||
qemu_free(blk);
|
||||
}
|
||||
|
||||
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(Monitor *mon, QEMUFile *f)
|
||||
{
|
||||
BlkMigDevState *bmds;
|
||||
BlockDriverState *bs;
|
||||
int64_t sectors;
|
||||
|
||||
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;
|
||||
|
||||
for (bs = bdrv_first; bs != NULL; bs = bs->next) {
|
||||
if (bs->type == BDRV_TYPE_HD) {
|
||||
sectors = bdrv_getlength(bs) >> BDRV_SECTOR_BITS;
|
||||
if (sectors == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
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 int blk_mig_save_bulked_block(Monitor *mon, QEMUFile *f, int is_async)
|
||||
{
|
||||
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, is_async) == 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;
|
||||
}
|
||||
|
||||
#define MAX_NUM_BLOCKS 4
|
||||
|
||||
static void blk_mig_save_dirty_blocks(Monitor *mon, QEMUFile *f)
|
||||
{
|
||||
BlkMigDevState *bmds;
|
||||
BlkMigBlock blk;
|
||||
int64_t sector;
|
||||
|
||||
blk.buf = qemu_malloc(BLOCK_SIZE);
|
||||
|
||||
QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) {
|
||||
for (sector = 0; sector < bmds->cur_sector;) {
|
||||
if (bdrv_get_dirty(bmds->bs, sector)) {
|
||||
if (bdrv_read(bmds->bs, sector, blk.buf,
|
||||
BDRV_SECTORS_PER_DIRTY_CHUNK) < 0) {
|
||||
monitor_printf(mon, "Error reading sector %" PRId64 "\n",
|
||||
sector);
|
||||
qemu_file_set_error(f);
|
||||
qemu_free(blk.buf);
|
||||
return;
|
||||
}
|
||||
blk.bmds = bmds;
|
||||
blk.sector = sector;
|
||||
blk_send(f, &blk);
|
||||
|
||||
bdrv_reset_dirty(bmds->bs, sector,
|
||||
BDRV_SECTORS_PER_DIRTY_CHUNK);
|
||||
}
|
||||
sector += BDRV_SECTORS_PER_DIRTY_CHUNK;
|
||||
}
|
||||
}
|
||||
|
||||
qemu_free(blk.buf);
|
||||
}
|
||||
|
||||
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 int is_stage2_completed(void)
|
||||
{
|
||||
BlkMigDevState *bmds;
|
||||
|
||||
if (block_mig_state.submitted > 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) {
|
||||
if (bmds->bulk_completed == 0) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* control the rate of transfer */
|
||||
while ((block_mig_state.submitted +
|
||||
block_mig_state.read_done) * BLOCK_SIZE <
|
||||
qemu_file_get_rate_limit(f)) {
|
||||
if (blk_mig_save_bulked_block(mon, f, 1) == 0) {
|
||||
/* no more bulk blocks for now */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
flush_blks(f);
|
||||
|
||||
if (qemu_file_has_error(f)) {
|
||||
blk_mig_cleanup(mon);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (stage == 3) {
|
||||
while (blk_mig_save_bulked_block(mon, f, 0) != 0) {
|
||||
/* empty */
|
||||
}
|
||||
|
||||
blk_mig_save_dirty_blocks(mon, f);
|
||||
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) {
|
||||
/* 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);
|
||||
bdrv_write(bs, addr, buf, BDRV_SECTORS_PER_DIRTY_CHUNK);
|
||||
|
||||
qemu_free(buf);
|
||||
} 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("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,8 +1,8 @@
|
||||
/*
|
||||
* 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
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
@@ -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;
|
||||
@@ -81,25 +80,29 @@ static int decompress_cluster(BDRVQcowState *s, uint64_t cluster_offset);
|
||||
static int qcow_probe(const uint8_t *buf, int buf_size, const char *filename)
|
||||
{
|
||||
const QCowHeader *cow_header = (const void *)buf;
|
||||
|
||||
|
||||
if (buf_size >= sizeof(QCowHeader) &&
|
||||
be32_to_cpu(cow_header->magic) == QCOW_MAGIC &&
|
||||
be32_to_cpu(cow_header->version) == QCOW_VERSION)
|
||||
be32_to_cpu(cow_header->version) == QCOW_VERSION)
|
||||
return 100;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcow_open(BlockDriverState *bs, const char *filename, int flags)
|
||||
static int qcow_open(BlockDriverState *bs, const char *filename)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int len, i, shift, ret;
|
||||
int fd, len, i, shift;
|
||||
QCowHeader header;
|
||||
|
||||
ret = bdrv_file_open(&s->hd, filename, flags);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (bdrv_pread(s->hd, 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);
|
||||
@@ -109,7 +112,7 @@ static int qcow_open(BlockDriverState *bs, const char *filename, int flags)
|
||||
be64_to_cpus(&header.size);
|
||||
be32_to_cpus(&header.crypt_method);
|
||||
be64_to_cpus(&header.l1_table_offset);
|
||||
|
||||
|
||||
if (header.magic != QCOW_MAGIC || header.version != QCOW_VERSION)
|
||||
goto fail;
|
||||
if (header.size <= 1 || header.cluster_bits < 9)
|
||||
@@ -135,7 +138,8 @@ static int qcow_open(BlockDriverState *bs, const char *filename, int flags)
|
||||
s->l1_table = qemu_malloc(s->l1_size * sizeof(uint64_t));
|
||||
if (!s->l1_table)
|
||||
goto fail;
|
||||
if (bdrv_pread(s->hd, 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++) {
|
||||
@@ -152,13 +156,14 @@ static int qcow_open(BlockDriverState *bs, const char *filename, int flags)
|
||||
if (!s->cluster_data)
|
||||
goto fail;
|
||||
s->cluster_cache_offset = -1;
|
||||
|
||||
|
||||
/* read the backing file name */
|
||||
if (header.backing_file_offset != 0) {
|
||||
len = header.backing_file_size;
|
||||
if (len > 1023)
|
||||
len = 1023;
|
||||
if (bdrv_pread(s->hd, 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';
|
||||
}
|
||||
@@ -169,7 +174,7 @@ static int qcow_open(BlockDriverState *bs, const char *filename, int flags)
|
||||
qemu_free(s->l2_cache);
|
||||
qemu_free(s->cluster_cache);
|
||||
qemu_free(s->cluster_data);
|
||||
bdrv_delete(s->hd);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -178,7 +183,7 @@ static int qcow_set_key(BlockDriverState *bs, const char *key)
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
uint8_t keybuf[16];
|
||||
int len, i;
|
||||
|
||||
|
||||
memset(keybuf, 0, 16);
|
||||
len = strlen(key);
|
||||
if (len > 16)
|
||||
@@ -232,7 +237,7 @@ static void encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
|
||||
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,
|
||||
AES_cbc_encrypt(in_buf, out_buf, 512, key,
|
||||
ivec.b, enc);
|
||||
sector_num++;
|
||||
in_buf += 512;
|
||||
@@ -249,7 +254,7 @@ static void encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
|
||||
*
|
||||
* 2 to allocate a compressed cluster of size
|
||||
* 'compressed_size'. 'compressed_size' must be > 0 and <
|
||||
* cluster_size
|
||||
* cluster_size
|
||||
*
|
||||
* return 0 if not allocated.
|
||||
*/
|
||||
@@ -263,7 +268,7 @@ static uint64_t get_cluster_offset(BlockDriverState *bs,
|
||||
uint64_t l2_offset, *l2_table, cluster_offset, tmp;
|
||||
uint32_t min_count;
|
||||
int new_l2_table;
|
||||
|
||||
|
||||
l1_index = offset >> (s->l2_bits + s->cluster_bits);
|
||||
l2_offset = s->l1_table[l1_index];
|
||||
new_l2_table = 0;
|
||||
@@ -271,14 +276,14 @@ static uint64_t get_cluster_offset(BlockDriverState *bs,
|
||||
if (!allocate)
|
||||
return 0;
|
||||
/* allocate a new l2 entry */
|
||||
l2_offset = bdrv_getlength(s->hd);
|
||||
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(s->hd, s->l1_table_offset + l1_index * sizeof(tmp),
|
||||
&tmp, sizeof(tmp)) != sizeof(tmp))
|
||||
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;
|
||||
}
|
||||
@@ -304,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(s->hd, l2_offset, l2_table, s->l2_size * sizeof(uint64_t)) !=
|
||||
if (write(s->fd, l2_table, s->l2_size * sizeof(uint64_t)) !=
|
||||
s->l2_size * sizeof(uint64_t))
|
||||
return 0;
|
||||
} else {
|
||||
if (bdrv_pread(s->hd, 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;
|
||||
}
|
||||
@@ -319,7 +325,7 @@ static uint64_t get_cluster_offset(BlockDriverState *bs,
|
||||
found:
|
||||
l2_index = (offset >> s->cluster_bits) & (s->l2_size - 1);
|
||||
cluster_offset = be64_to_cpu(l2_table[l2_index]);
|
||||
if (!cluster_offset ||
|
||||
if (!cluster_offset ||
|
||||
((cluster_offset & QCOW_OFLAG_COMPRESSED) && allocate == 1)) {
|
||||
if (!allocate)
|
||||
return 0;
|
||||
@@ -331,55 +337,56 @@ static uint64_t get_cluster_offset(BlockDriverState *bs,
|
||||
overwritten */
|
||||
if (decompress_cluster(s, cluster_offset) < 0)
|
||||
return 0;
|
||||
cluster_offset = bdrv_getlength(s->hd);
|
||||
cluster_offset = (cluster_offset + s->cluster_size - 1) &
|
||||
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(s->hd, 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(s->hd);
|
||||
cluster_offset = lseek(s->fd, 0, SEEK_END);
|
||||
if (allocate == 1) {
|
||||
/* round to cluster size */
|
||||
cluster_offset = (cluster_offset + s->cluster_size - 1) &
|
||||
cluster_offset = (cluster_offset + s->cluster_size - 1) &
|
||||
~(s->cluster_size - 1);
|
||||
bdrv_truncate(s->hd, 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 &&
|
||||
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,
|
||||
encrypt_sectors(s, start_sect + i,
|
||||
s->cluster_data,
|
||||
s->cluster_data + 512, 1, 1,
|
||||
&s->aes_encrypt_key);
|
||||
if (bdrv_pwrite(s->hd, 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) {
|
||||
cluster_offset |= QCOW_OFLAG_COMPRESSED |
|
||||
} else {
|
||||
cluster_offset |= QCOW_OFLAG_COMPRESSED |
|
||||
(uint64_t)compressed_size << (63 - s->cluster_bits);
|
||||
}
|
||||
}
|
||||
/* update L2 table */
|
||||
tmp = cpu_to_be64(cluster_offset);
|
||||
l2_table[l2_index] = tmp;
|
||||
if (bdrv_pwrite(s->hd,
|
||||
l2_offset + l2_index * sizeof(tmp), &tmp, sizeof(tmp)) != sizeof(tmp))
|
||||
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;
|
||||
}
|
||||
|
||||
static int qcow_is_allocated(BlockDriverState *bs, int64_t sector_num,
|
||||
static int qcow_is_allocated(BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors, int *pnum)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
@@ -421,7 +428,7 @@ static int decompress_buffer(uint8_t *out_buf, int out_buf_size,
|
||||
inflateEnd(strm);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int decompress_cluster(BDRVQcowState *s, uint64_t cluster_offset)
|
||||
{
|
||||
int ret, csize;
|
||||
@@ -431,8 +438,9 @@ static int decompress_cluster(BDRVQcowState *s, 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(s->hd, coffset, s->cluster_data, csize);
|
||||
if (ret != 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,
|
||||
s->cluster_data, csize) < 0) {
|
||||
@@ -443,15 +451,13 @@ static int decompress_cluster(BDRVQcowState *s, uint64_t cluster_offset)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
||||
static int qcow_read(BlockDriverState *bs, int64_t sector_num,
|
||||
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;
|
||||
uint64_t cluster_offset;
|
||||
|
||||
|
||||
while (nb_sectors > 0) {
|
||||
cluster_offset = get_cluster_offset(bs, sector_num << 9, 0, 0, 0, 0);
|
||||
index_in_cluster = sector_num & (s->cluster_sectors - 1);
|
||||
@@ -459,24 +465,18 @@ 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);
|
||||
}
|
||||
memset(buf, 0, 512 * n);
|
||||
} else if (cluster_offset & QCOW_OFLAG_COMPRESSED) {
|
||||
if (decompress_cluster(s, cluster_offset) < 0)
|
||||
return -1;
|
||||
memcpy(buf, s->cluster_cache + index_in_cluster * 512, 512 * n);
|
||||
} else {
|
||||
ret = bdrv_pread(s->hd, cluster_offset + index_in_cluster * 512, buf, n * 512);
|
||||
if (ret != 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) {
|
||||
encrypt_sectors(s, sector_num, buf, buf, n, 0,
|
||||
encrypt_sectors(s, sector_num, buf, buf, n, 0,
|
||||
&s->aes_decrypt_key);
|
||||
}
|
||||
}
|
||||
@@ -486,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 = (QCowAIOCB *)blockacb;
|
||||
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 {
|
||||
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, 0,
|
||||
index_in_cluster,
|
||||
index_in_cluster + n);
|
||||
if (!cluster_offset)
|
||||
return -1;
|
||||
lseek(s->fd, cluster_offset + index_in_cluster * 512, SEEK_SET);
|
||||
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;
|
||||
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 {
|
||||
/* Note: in this case, no need to wait */
|
||||
memset(acb->buf, 0, 512 * acb->n);
|
||||
goto redo;
|
||||
ret = write(s->fd, buf, n * 512);
|
||||
}
|
||||
} else if (acb->cluster_offset & QCOW_OFLAG_COMPRESSED) {
|
||||
/* add AIO support for compressed blocks ? */
|
||||
if (decompress_cluster(s, 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(s->hd,
|
||||
(acb->cluster_offset >> 9) + index_in_cluster,
|
||||
&acb->hd_qiov, acb->n, qcow_aio_read_cb, acb);
|
||||
if (acb->hd_aiocb == NULL)
|
||||
goto done;
|
||||
if (ret != n * 512)
|
||||
return -1;
|
||||
nb_sectors -= n;
|
||||
sector_num += n;
|
||||
buf += n * 512;
|
||||
}
|
||||
|
||||
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;
|
||||
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,
|
||||
index_in_cluster,
|
||||
index_in_cluster + acb->n);
|
||||
if (!cluster_offset || (cluster_offset & 511) != 0) {
|
||||
ret = -EIO;
|
||||
goto done;
|
||||
}
|
||||
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;
|
||||
} else {
|
||||
src_buf = acb->buf;
|
||||
}
|
||||
|
||||
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(s->hd,
|
||||
(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);
|
||||
}
|
||||
|
||||
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)
|
||||
@@ -739,31 +529,20 @@ static void qcow_close(BlockDriverState *bs)
|
||||
qemu_free(s->l2_cache);
|
||||
qemu_free(s->cluster_cache);
|
||||
qemu_free(s->cluster_data);
|
||||
bdrv_delete(s->hd);
|
||||
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;
|
||||
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 -1;
|
||||
memset(&header, 0, sizeof(header));
|
||||
@@ -773,15 +552,28 @@ static int qcow_create(const char *filename, QEMUOptionParameter *options)
|
||||
header_size = sizeof(header);
|
||||
backing_filename_len = 0;
|
||||
if (backing_file) {
|
||||
if (strcmp(backing_file, "fat:")) {
|
||||
header.backing_file_offset = cpu_to_be64(header_size);
|
||||
backing_filename_len = strlen(backing_file);
|
||||
header.backing_file_size = cpu_to_be32(backing_filename_len);
|
||||
header_size += backing_filename_len;
|
||||
} else {
|
||||
/* special backing file for vvfat */
|
||||
backing_file = NULL;
|
||||
}
|
||||
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_filename);
|
||||
header.backing_file_size = cpu_to_be32(backing_filename_len);
|
||||
header_size += backing_filename_len;
|
||||
} 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 */
|
||||
@@ -794,16 +586,16 @@ 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 */
|
||||
write(fd, &header, sizeof(header));
|
||||
if (backing_file) {
|
||||
write(fd, backing_file, backing_filename_len);
|
||||
write(fd, backing_filename, backing_filename_len);
|
||||
}
|
||||
lseek(fd, header_size, SEEK_SET);
|
||||
tmp = 0;
|
||||
@@ -814,18 +606,16 @@ static int qcow_create(const char *filename, QEMUOptionParameter *options)
|
||||
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(s->hd, 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(s->hd, 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));
|
||||
@@ -834,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;
|
||||
@@ -845,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)
|
||||
@@ -855,7 +653,7 @@ static int qcow_write_compressed(BlockDriverState *bs, int64_t sector_num,
|
||||
/* best compression, small window, no zlib header */
|
||||
memset(&strm, 0, sizeof(strm));
|
||||
ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION,
|
||||
Z_DEFLATED, -12,
|
||||
Z_DEFLATED, -12,
|
||||
9, Z_DEFAULT_STRATEGY);
|
||||
if (ret != 0) {
|
||||
qemu_free(out_buf);
|
||||
@@ -879,76 +677,34 @@ 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,
|
||||
cluster_offset = get_cluster_offset(bs, sector_num << 9, 2,
|
||||
out_len, 0, 0);
|
||||
cluster_offset &= s->cluster_offset_mask;
|
||||
if (bdrv_pwrite(s->hd, 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
qemu_free(out_buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void qcow_flush(BlockDriverState *bs)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
bdrv_flush(s->hd);
|
||||
}
|
||||
|
||||
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_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,
|
||||
};
|
||||
@@ -1,9 +1,9 @@
|
||||
/* vim:set shiftwidth=4 ts=8: */
|
||||
/*
|
||||
* QEMU Block driver for virtual VFAT (shadows a local directory)
|
||||
*
|
||||
*
|
||||
* Copyright (c) 2004,2005 Johannes E. Schindelin
|
||||
*
|
||||
*
|
||||
* 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
|
||||
@@ -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
|
||||
@@ -38,7 +38,7 @@
|
||||
/* TODO: add ":bootsector=blabla.img:" */
|
||||
/* LATER TODO: add automatic boot sector generation from
|
||||
BOOTEASY.ASM and Ranish Partition Manager
|
||||
Note that DOS assumes the system files to be the first files in the
|
||||
Note that DOS assumes the system files to be the first files in the
|
||||
file system (test if the boot sector still relies on that fact)! */
|
||||
/* MAYBE TODO: write block-visofs.c */
|
||||
/* TODO: call try_commit() only after a timeout */
|
||||
@@ -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,
|
||||
@@ -152,21 +153,21 @@ static inline int array_roll(array_t* array,int index_to,int index_from,int coun
|
||||
index_to<0 || index_to>=array->next ||
|
||||
index_from<0 || index_from>=array->next)
|
||||
return -1;
|
||||
|
||||
|
||||
if(index_to==index_from)
|
||||
return 0;
|
||||
|
||||
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)
|
||||
memmove(to+is*count,to,from-to);
|
||||
else
|
||||
memmove(from,from+is*count,to-from);
|
||||
|
||||
|
||||
memcpy(to,buf,is*count);
|
||||
|
||||
free(buf);
|
||||
@@ -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;
|
||||
@@ -321,10 +319,10 @@ typedef struct BDRVVVFATState {
|
||||
BlockDriverState* bs; /* pointer to parent */
|
||||
unsigned int first_sectors_number; /* 1 for a single partition, 0x40 for a disk with partition table */
|
||||
unsigned char first_sectors[0x40*0x200];
|
||||
|
||||
|
||||
int fat_type; /* 16 or 32 */
|
||||
array_t fat,directory,mapping;
|
||||
|
||||
|
||||
unsigned int cluster_size;
|
||||
unsigned int sectors_per_cluster;
|
||||
unsigned int sectors_per_fat;
|
||||
@@ -334,7 +332,7 @@ typedef struct BDRVVVFATState {
|
||||
uint32_t sector_count; /* total number of sectors of the partition */
|
||||
uint32_t cluster_count; /* total number of clusters of this partition */
|
||||
uint32_t max_fat_value;
|
||||
|
||||
|
||||
int current_fd;
|
||||
mapping_t* current_mapping;
|
||||
unsigned char* cluster; /* points to current cluster */
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -379,30 +362,21 @@ 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;
|
||||
partition_t* partition=&(real_mbr->partition[0]);
|
||||
|
||||
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,13 +482,10 @@ 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;
|
||||
}
|
||||
|
||||
@@ -526,7 +496,7 @@ static uint16_t fat_datetime(time_t time,int return_time) {
|
||||
t=localtime(&time); /* this is not thread safe */
|
||||
#else
|
||||
struct tm t1;
|
||||
t = &t1;
|
||||
t=&t1;
|
||||
localtime_r(&time,t);
|
||||
#endif
|
||||
if(return_time)
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -591,7 +561,7 @@ static inline void init_fat(BDRVVVFATState* s)
|
||||
s->sectors_per_fat * 0x200 / s->fat.item_size - 1);
|
||||
}
|
||||
memset(s->fat.pointer,0,s->fat.size);
|
||||
|
||||
|
||||
switch(s->fat_type) {
|
||||
case 12: s->max_fat_value=0xfff; break;
|
||||
case 16: s->max_fat_value=0xffff; break;
|
||||
@@ -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));
|
||||
@@ -616,10 +586,10 @@ static inline direntry_t* create_short_and_long_name(BDRVVVFATState* s,
|
||||
memcpy(entry->name,filename,strlen(filename));
|
||||
return entry;
|
||||
}
|
||||
|
||||
|
||||
entry_long=create_long_filename(s,filename);
|
||||
|
||||
i = strlen(filename);
|
||||
|
||||
i = strlen(filename);
|
||||
for(j = i - 1; j>0 && filename[j]!='.';j--);
|
||||
if (j > 0)
|
||||
i = (j > 8 ? 8 : j);
|
||||
@@ -628,8 +598,8 @@ 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++)
|
||||
entry->extension[i] = filename[j+1+i];
|
||||
@@ -655,7 +625,7 @@ static inline direntry_t* create_short_and_long_name(BDRVVVFATState* s,
|
||||
if(entry1==entry) /* no dupe found */
|
||||
break;
|
||||
|
||||
/* use all 8 characters of name */
|
||||
/* use all 8 characters of name */
|
||||
if(entry->name[7]==' ') {
|
||||
int j;
|
||||
for(j=6;j>0 && entry->name[j]==' ';j--)
|
||||
@@ -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);
|
||||
@@ -712,11 +682,11 @@ static int read_directory(BDRVVVFATState* s, int mapping_index)
|
||||
mapping->end = mapping->begin;
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
i = mapping->info.dir.first_dir_index =
|
||||
first_cluster == 0 ? 0 : s->directory.next;
|
||||
|
||||
/* actually read the directory, and allocate the mappings */
|
||||
/* actually read the directory, and allocate the mappings */
|
||||
while((entry=readdir(dir))) {
|
||||
unsigned int length=strlen(dirname)+2+strlen(entry->d_name);
|
||||
char* buffer;
|
||||
@@ -727,8 +697,9 @@ 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) {
|
||||
@@ -801,7 +772,7 @@ static int read_directory(BDRVVVFATState* s, int mapping_index)
|
||||
memset(array_get(&(s->directory), cur), 0,
|
||||
(ROOT_ENTRIES - cur) * sizeof(direntry_t));
|
||||
}
|
||||
|
||||
|
||||
/* reget the mapping, since s->mapping was possibly realloc()ed */
|
||||
mapping = (mapping_t*)array_get(&(s->mapping), mapping_index);
|
||||
first_cluster += (s->directory.next - mapping->info.dir.first_dir_index)
|
||||
@@ -810,7 +781,7 @@ static int read_directory(BDRVVVFATState* s, int mapping_index)
|
||||
|
||||
direntry = (direntry_t*)array_get(&(s->directory), mapping->dir_index);
|
||||
set_begin_of_direntry(direntry, mapping->begin);
|
||||
|
||||
|
||||
return 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),
|
||||
@@ -860,7 +832,7 @@ static int init_directories(BDRVVVFATState* s,
|
||||
*/
|
||||
i = 1+s->sectors_per_cluster*0x200*8/s->fat_type;
|
||||
s->sectors_per_fat=(s->sector_count+i)/i; /* round up */
|
||||
|
||||
|
||||
array_init(&(s->mapping),sizeof(mapping_t));
|
||||
array_init(&(s->directory),sizeof(direntry_t));
|
||||
|
||||
@@ -868,7 +840,7 @@ static int init_directories(BDRVVVFATState* s,
|
||||
{
|
||||
direntry_t* entry=array_get_next(&(s->directory));
|
||||
entry->attributes=0x28; /* archive | volume label */
|
||||
snprintf((char*)entry->name,11,"QEMU VVFAT");
|
||||
snprintf(entry->name,11,"QEMU VVFAT");
|
||||
}
|
||||
|
||||
/* Now build FAT, and write back information into directory */
|
||||
@@ -891,7 +863,8 @@ static int init_directories(BDRVVVFATState* s,
|
||||
s->path = mapping->path;
|
||||
|
||||
for (i = 0, cluster = 0; i < s->mapping.next; i++) {
|
||||
/* MS-DOS expects the FAT to be 0 for the root directory
|
||||
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? */
|
||||
int fix_fat = (i != 0);
|
||||
@@ -923,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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -986,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");
|
||||
@@ -1012,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;
|
||||
|
||||
@@ -1025,10 +990,16 @@ DLOG(if (stderr == NULL) {
|
||||
s->qcow_filename = NULL;
|
||||
s->fat2 = NULL;
|
||||
s->downcase_short_names = 1;
|
||||
|
||||
|
||||
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;
|
||||
@@ -1037,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;
|
||||
@@ -1049,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);
|
||||
|
||||
@@ -1078,6 +1040,7 @@ DLOG(if (stderr == NULL) {
|
||||
bs->heads = bs->cyls = bs->secs = 0;
|
||||
|
||||
// assert(is_consistent(s));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1117,7 +1080,7 @@ static inline int find_mapping_for_cluster_aux(BDRVVVFATState* s,int cluster_num
|
||||
assert(index1<=index2);
|
||||
DLOG(mapping=array_get(&(s->mapping),index1);
|
||||
assert(mapping->begin<=cluster_num);
|
||||
assert(index2 >= s->mapping.next ||
|
||||
assert(index2 >= s->mapping.next ||
|
||||
((mapping = array_get(&(s->mapping),index2)) &&
|
||||
mapping->end>cluster_num)));
|
||||
}
|
||||
@@ -1128,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;
|
||||
}
|
||||
@@ -1191,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);
|
||||
@@ -1250,7 +1213,7 @@ static void print_direntry(const direntry_t* 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]);
|
||||
@@ -1280,7 +1243,7 @@ static void print_mapping(const mapping_t* mapping)
|
||||
}
|
||||
#endif
|
||||
|
||||
static int vvfat_read(BlockDriverState *bs, int64_t sector_num,
|
||||
static int vvfat_read(BlockDriverState *bs, int64_t sector_num,
|
||||
uint8_t *buf, int nb_sectors)
|
||||
{
|
||||
BDRVVVFATState *s = bs->opaque;
|
||||
@@ -1413,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;
|
||||
@@ -1443,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)
|
||||
@@ -1467,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;
|
||||
}
|
||||
@@ -1486,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];
|
||||
}
|
||||
@@ -1499,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;
|
||||
}
|
||||
@@ -1721,7 +1678,7 @@ static uint32_t get_cluster_count_for_direntry(BDRVVVFATState* s,
|
||||
}
|
||||
|
||||
/*
|
||||
* This function looks at the modified data (qcow).
|
||||
* This function looks at the modified data (qcow).
|
||||
* It returns 0 upon inconsistency or error, and the number of clusters
|
||||
* used by the directory, its subdirectories and their files.
|
||||
*/
|
||||
@@ -1729,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);
|
||||
|
||||
@@ -1738,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';
|
||||
|
||||
@@ -1756,7 +1713,7 @@ static int check_directory_consistency(BDRVVVFATState *s,
|
||||
} else
|
||||
/* new directory */
|
||||
schedule_mkdir(s, cluster_num, strdup(path));
|
||||
|
||||
|
||||
lfn_init(&lfn);
|
||||
do {
|
||||
int i;
|
||||
@@ -1781,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) ||
|
||||
@@ -1802,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 */
|
||||
@@ -1812,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) {
|
||||
@@ -1870,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,
|
||||
@@ -2097,7 +2053,7 @@ static int commit_mappings(BDRVVVFATState* s,
|
||||
}
|
||||
|
||||
next_mapping->dir_index = mapping->dir_index;
|
||||
next_mapping->first_mapping_index =
|
||||
next_mapping->first_mapping_index =
|
||||
mapping->first_mapping_index < 0 ?
|
||||
array_index(&(s->mapping), mapping) :
|
||||
mapping->first_mapping_index;
|
||||
@@ -2117,7 +2073,7 @@ static int commit_mappings(BDRVVVFATState* s,
|
||||
|
||||
mapping = next_mapping;
|
||||
}
|
||||
|
||||
|
||||
cluster = c1;
|
||||
}
|
||||
|
||||
@@ -2212,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;
|
||||
|
||||
@@ -2222,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);
|
||||
@@ -2242,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;
|
||||
@@ -2374,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);
|
||||
}
|
||||
@@ -2603,7 +2559,7 @@ static int do_commit(BDRVVVFATState* s)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* copy FAT (with bdrv_read) */
|
||||
/* copy FAT (with bdrv_read) */
|
||||
memcpy(s->fat.pointer, s->fat2, 0x200 * s->sectors_per_fat);
|
||||
|
||||
/* recurse direntries from root (using bs->bdrv_read) */
|
||||
@@ -2645,10 +2601,10 @@ DLOG(checkpoint());
|
||||
return do_commit(s);
|
||||
}
|
||||
|
||||
static int vvfat_write(BlockDriverState *bs, int64_t sector_num,
|
||||
static int vvfat_write(BlockDriverState *bs, int64_t sector_num,
|
||||
const uint8_t *buf, int nb_sectors)
|
||||
{
|
||||
BDRVVVFATState *s = bs->opaque;
|
||||
BDRVVVFATState *s = bs->opaque;
|
||||
int i, ret;
|
||||
|
||||
DLOG(checkpoint());
|
||||
@@ -2687,7 +2643,7 @@ DLOG(checkpoint());
|
||||
begin = sector_num;
|
||||
if (end > sector_num + nb_sectors)
|
||||
end = sector_num + nb_sectors;
|
||||
dir_index = mapping->dir_index +
|
||||
dir_index = mapping->dir_index +
|
||||
0x10 * (begin - mapping->begin * s->sectors_per_cluster);
|
||||
direntries = (direntry_t*)(buf + 0x200 * (begin - sector_num));
|
||||
|
||||
@@ -2746,7 +2702,7 @@ static int vvfat_is_allocated(BlockDriverState *bs,
|
||||
*n = nb_sectors;
|
||||
else if (*n < 0)
|
||||
return 0;
|
||||
return 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int write_target_commit(BlockDriverState *bs, int64_t sector_num,
|
||||
@@ -2762,29 +2718,24 @@ static void write_target_close(BlockDriverState *bs) {
|
||||
}
|
||||
|
||||
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 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 || bdrv_open(s->qcow, s->qcow_filename, 0) < 0)
|
||||
@@ -2813,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_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);
|
||||
@@ -2859,3 +2804,4 @@ static void checkpoint(void) {
|
||||
print_direntry(NULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
204
block.h
204
block.h
@@ -1,204 +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_RDONLY 0x0000
|
||||
#define BDRV_O_RDWR 0x0002
|
||||
#define BDRV_O_ACCESS 0x0003
|
||||
#define BDRV_O_CREAT 0x0004 /* create an empty file */
|
||||
#define BDRV_O_SNAPSHOT 0x0008 /* open the file read only and save writes in a snapshot */
|
||||
#define BDRV_O_FILE 0x0010 /* open as a raw file (do not try to
|
||||
use a disk image format on top of
|
||||
it (default for
|
||||
bdrv_file_open()) */
|
||||
#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_CACHE_MASK (BDRV_O_NOCACHE | BDRV_O_CACHE_WB)
|
||||
|
||||
#define BDRV_SECTOR_BITS 9
|
||||
#define BDRV_SECTOR_SIZE (1 << BDRV_SECTOR_BITS)
|
||||
#define BDRV_SECTOR_MASK ~(BDRV_SECTOR_SIZE - 1);
|
||||
|
||||
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_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_create2(BlockDriver *drv,
|
||||
const char *filename, int64_t size_in_sectors,
|
||||
const char *backing_file, const char *backing_format,
|
||||
int flags);
|
||||
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);
|
||||
int bdrv_open2(BlockDriverState *bs, const char *filename, int flags,
|
||||
BlockDriver *drv);
|
||||
void bdrv_close(BlockDriverState *bs);
|
||||
int bdrv_check(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_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_register(BlockDriver *bdrv);
|
||||
|
||||
/* 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);
|
||||
|
||||
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);
|
||||
int bdrv_is_removable(BlockDriverState *bs);
|
||||
int bdrv_is_read_only(BlockDriverState *bs);
|
||||
int bdrv_set_read_only(BlockDriverState *bs, int read_only);
|
||||
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);
|
||||
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_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);
|
||||
#endif
|
||||
562
block/curl.c
562
block/curl.c
@@ -1,562 +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;
|
||||
long long fsize;
|
||||
|
||||
if(sscanf(ptr, "Content-Length: %lld", &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 %lld bytes\n", (unsigned long long)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 = 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 = %lld\n", (long long)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, "%lld-%lld", (long long)start, (long long)end);
|
||||
dprintf("CURL (AIO): Reading %d at %lld (%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_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_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_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_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_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);
|
||||
196
block/nbd.c
196
block/nbd.c
@@ -1,196 +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 ((flags & BDRV_O_CREAT))
|
||||
return -EINVAL;
|
||||
|
||||
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_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,182 +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 {
|
||||
int fd;
|
||||
|
||||
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, const char *filename, int flags)
|
||||
{
|
||||
BDRVParallelsState *s = bs->opaque;
|
||||
int fd, i;
|
||||
struct parallels_header ph;
|
||||
|
||||
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, &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);
|
||||
|
||||
if (lseek(s->fd, 64, SEEK_SET) != 64)
|
||||
goto fail;
|
||||
|
||||
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 (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]);
|
||||
|
||||
return 0;
|
||||
fail:
|
||||
if (s->catalog_bitmap)
|
||||
qemu_free(s->catalog_bitmap);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int seek_to_sector(BlockDriverState *bs, int64_t sector_num)
|
||||
{
|
||||
BDRVParallelsState *s = bs->opaque;
|
||||
uint32_t index, offset;
|
||||
uint64_t position;
|
||||
|
||||
index = sector_num / s->tracks;
|
||||
offset = sector_num % s->tracks;
|
||||
|
||||
// not allocated
|
||||
if ((index > s->catalog_size) || (s->catalog_bitmap[index] == 0))
|
||||
return -1;
|
||||
|
||||
position = (uint64_t)(s->catalog_bitmap[index] + offset) * 512;
|
||||
|
||||
// fprintf(stderr, "sector: %llx index=%x offset=%x pointer=%x position=%x\n",
|
||||
// sector_num, index, offset, s->catalog_bitmap[index], position);
|
||||
|
||||
if (lseek(s->fd, position, SEEK_SET) != position)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parallels_read(BlockDriverState *bs, int64_t sector_num,
|
||||
uint8_t *buf, int nb_sectors)
|
||||
{
|
||||
BDRVParallelsState *s = bs->opaque;
|
||||
|
||||
while (nb_sectors > 0) {
|
||||
if (!seek_to_sector(bs, sector_num)) {
|
||||
if (read(s->fd, 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);
|
||||
close(s->fd);
|
||||
}
|
||||
|
||||
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,844 +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;
|
||||
uint64_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) */
|
||||
new_l1_table_offset = qcow2_alloc_clusters(bs, new_l1_size2);
|
||||
|
||||
for(i = 0; i < s->l1_size; i++)
|
||||
new_l1_table[i] = cpu_to_be64(new_l1_table[i]);
|
||||
ret = bdrv_pwrite(s->hd, new_l1_table_offset, new_l1_table, new_l1_size2);
|
||||
if (ret != new_l1_size2)
|
||||
goto fail;
|
||||
for(i = 0; i < s->l1_size; i++)
|
||||
new_l1_table[i] = be64_to_cpu(new_l1_table[i]);
|
||||
|
||||
/* set new table */
|
||||
cpu_to_be32w((uint32_t*)data, new_l1_size);
|
||||
cpu_to_be64w((uint64_t*)(data + 4), new_l1_table_offset);
|
||||
if (bdrv_pwrite(s->hd, offsetof(QCowHeader, l1_size), data,
|
||||
sizeof(data)) != sizeof(data))
|
||||
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(s->l1_table);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
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 uint64_t *l2_load(BlockDriverState *bs, uint64_t l2_offset)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int min_index;
|
||||
uint64_t *l2_table;
|
||||
|
||||
/* 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 l2_table;
|
||||
|
||||
/* 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);
|
||||
if (bdrv_pread(s->hd, l2_offset, l2_table, s->l2_size * sizeof(uint64_t)) !=
|
||||
s->l2_size * sizeof(uint64_t))
|
||||
return NULL;
|
||||
s->l2_cache_offsets[min_index] = l2_offset;
|
||||
s->l2_cache_counts[min_index] = 1;
|
||||
|
||||
return l2_table;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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(BDRVQcowState *s, int l1_index)
|
||||
{
|
||||
uint64_t buf[L1_ENTRIES_PER_SECTOR];
|
||||
int l1_start_index;
|
||||
int i;
|
||||
|
||||
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]);
|
||||
}
|
||||
|
||||
if (bdrv_pwrite(s->hd, s->l1_table_offset + 8 * l1_start_index,
|
||||
buf, sizeof(buf)) != sizeof(buf))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
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 uint64_t *l2_allocate(BlockDriverState *bs, int l1_index)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int min_index;
|
||||
uint64_t old_l2_offset;
|
||||
uint64_t *l2_table, l2_offset;
|
||||
|
||||
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));
|
||||
|
||||
/* update the L1 entry */
|
||||
|
||||
s->l1_table[l1_index] = l2_offset | QCOW_OFLAG_COPIED;
|
||||
if (write_l1_entry(s, l1_index) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* 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 */
|
||||
if (bdrv_pread(s->hd, old_l2_offset,
|
||||
l2_table, s->l2_size * sizeof(uint64_t)) !=
|
||||
s->l2_size * sizeof(uint64_t))
|
||||
return NULL;
|
||||
}
|
||||
/* write the l2 table to the file */
|
||||
if (bdrv_pwrite(s->hd, l2_offset,
|
||||
l2_table, s->l2_size * sizeof(uint64_t)) !=
|
||||
s->l2_size * sizeof(uint64_t))
|
||||
return NULL;
|
||||
|
||||
/* update the l2 cache entry */
|
||||
|
||||
s->l2_cache_offsets[min_index] = l2_offset;
|
||||
s->l2_cache_counts[min_index] = 1;
|
||||
|
||||
return l2_table;
|
||||
}
|
||||
|
||||
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;
|
||||
cluster_offset = qcow2_get_cluster_offset(bs, sector_num << 9, &n);
|
||||
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) {
|
||||
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(s, cluster_offset) < 0)
|
||||
return -1;
|
||||
memcpy(buf, s->cluster_cache + index_in_cluster * 512, 512 * n);
|
||||
} else {
|
||||
ret = bdrv_pread(s->hd, 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;
|
||||
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);
|
||||
}
|
||||
ret = bdrv_write(s->hd, (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, return cluster offset in
|
||||
* qcow2 file.
|
||||
*
|
||||
* 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 1, if the offset is found
|
||||
* Return 0, otherwise.
|
||||
*
|
||||
*/
|
||||
|
||||
uint64_t qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
|
||||
int *num)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
unsigned int l1_index, l2_index;
|
||||
uint64_t l2_offset, *l2_table, cluster_offset;
|
||||
int l1_bits, c;
|
||||
unsigned int index_in_cluster, nb_clusters;
|
||||
uint64_t nb_available, nb_needed;
|
||||
|
||||
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;
|
||||
l2_table = l2_load(bs, l2_offset);
|
||||
if (l2_table == NULL)
|
||||
return 0;
|
||||
|
||||
/* 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;
|
||||
|
||||
return cluster_offset & ~QCOW_OFLAG_COPIED;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
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, *l2_table;
|
||||
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 0;
|
||||
}
|
||||
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;
|
||||
l2_table = l2_load(bs, l2_offset);
|
||||
if (l2_table == NULL)
|
||||
return 0;
|
||||
} else {
|
||||
if (l2_offset)
|
||||
qcow2_free_clusters(bs, l2_offset, s->l2_size * sizeof(uint64_t));
|
||||
l2_table = l2_allocate(bs, l1_index);
|
||||
if (l2_table == NULL)
|
||||
return 0;
|
||||
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 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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, 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);
|
||||
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 */
|
||||
|
||||
l2_table[l2_index] = cpu_to_be64(cluster_offset);
|
||||
if (bdrv_pwrite(s->hd,
|
||||
l2_offset + l2_index * sizeof(uint64_t),
|
||||
l2_table + l2_index,
|
||||
sizeof(uint64_t)) != sizeof(uint64_t))
|
||||
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(BDRVQcowState *s, 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;
|
||||
|
||||
if (bdrv_pwrite(s->hd, l2_offset + start_offset, &l2_table[l2_start_index],
|
||||
len) != len)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, uint64_t cluster_offset,
|
||||
QCowL2Meta *m)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int i, j = 0, l2_index, ret;
|
||||
uint64_t *old_cluster, start_sect, l2_offset, *l2_table;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
ret = -EIO;
|
||||
/* update L2 table */
|
||||
if (!get_cluster_table(bs, m->offset, &l2_table, &l2_offset, &l2_index))
|
||||
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);
|
||||
}
|
||||
|
||||
if (write_l2_entries(s, l2_table, l2_offset, l2_index, m->nb_clusters) < 0) {
|
||||
ret = -1;
|
||||
goto err;
|
||||
}
|
||||
|
||||
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.
|
||||
*
|
||||
* Return the cluster offset if successful,
|
||||
* Return 0, otherwise.
|
||||
*
|
||||
*/
|
||||
|
||||
uint64_t 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, 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 0;
|
||||
|
||||
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;
|
||||
|
||||
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(be64_to_cpu(l2_table[l2_index + i]))
|
||||
break;
|
||||
|
||||
i += count_contiguous_free_clusters(nb_clusters - i,
|
||||
&l2_table[l2_index + i]);
|
||||
|
||||
cluster_offset = be64_to_cpu(l2_table[l2_index + i]);
|
||||
|
||||
if ((cluster_offset & QCOW_OFLAG_COPIED) ||
|
||||
(cluster_offset & QCOW_OFLAG_COMPRESSED))
|
||||
break;
|
||||
}
|
||||
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);
|
||||
|
||||
/* 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);
|
||||
|
||||
*num = m->nb_available - n_start;
|
||||
|
||||
return cluster_offset;
|
||||
}
|
||||
|
||||
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(BDRVQcowState *s, uint64_t cluster_offset)
|
||||
{
|
||||
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;
|
||||
ret = bdrv_read(s->hd, 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;
|
||||
}
|
||||
@@ -1,902 +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"
|
||||
|
||||
static int64_t alloc_clusters_noref(BlockDriverState *bs, int64_t size);
|
||||
static int update_refcount(BlockDriverState *bs,
|
||||
int64_t offset, int64_t length,
|
||||
int addend);
|
||||
|
||||
|
||||
static int cache_refcount_updates = 0;
|
||||
|
||||
static int write_refcount_block(BDRVQcowState *s)
|
||||
{
|
||||
size_t size = s->cluster_size;
|
||||
|
||||
if (s->refcount_block_cache_offset == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (bdrv_pwrite(s->hd, s->refcount_block_cache_offset,
|
||||
s->refcount_block_cache, size) != size)
|
||||
{
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*********************************************************/
|
||||
/* refcount handling */
|
||||
|
||||
int qcow2_refcount_init(BlockDriverState *bs)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int ret, refcount_table_size2, i;
|
||||
|
||||
s->refcount_block_cache = qemu_malloc(s->cluster_size);
|
||||
refcount_table_size2 = s->refcount_table_size * sizeof(uint64_t);
|
||||
s->refcount_table = qemu_malloc(refcount_table_size2);
|
||||
if (s->refcount_table_size > 0) {
|
||||
ret = bdrv_pread(s->hd, s->refcount_table_offset,
|
||||
s->refcount_table, refcount_table_size2);
|
||||
if (ret != refcount_table_size2)
|
||||
goto fail;
|
||||
for(i = 0; i < s->refcount_table_size; i++)
|
||||
be64_to_cpus(&s->refcount_table[i]);
|
||||
}
|
||||
return 0;
|
||||
fail:
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
void qcow2_refcount_close(BlockDriverState *bs)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
qemu_free(s->refcount_block_cache);
|
||||
qemu_free(s->refcount_table);
|
||||
}
|
||||
|
||||
|
||||
static int load_refcount_block(BlockDriverState *bs,
|
||||
int64_t refcount_block_offset)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int ret;
|
||||
|
||||
if (cache_refcount_updates) {
|
||||
write_refcount_block(s);
|
||||
}
|
||||
|
||||
ret = bdrv_pread(s->hd, refcount_block_offset, s->refcount_block_cache,
|
||||
s->cluster_size);
|
||||
if (ret != s->cluster_size)
|
||||
return -EIO;
|
||||
s->refcount_block_cache_offset = refcount_block_offset;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_refcount(BlockDriverState *bs, int64_t cluster_index)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int refcount_table_index, block_index;
|
||||
int64_t refcount_block_offset;
|
||||
|
||||
refcount_table_index = cluster_index >> (s->cluster_bits - REFCOUNT_SHIFT);
|
||||
if (refcount_table_index >= s->refcount_table_size)
|
||||
return 0;
|
||||
refcount_block_offset = s->refcount_table[refcount_table_index];
|
||||
if (!refcount_block_offset)
|
||||
return 0;
|
||||
if (refcount_block_offset != s->refcount_block_cache_offset) {
|
||||
/* better than nothing: return allocated if read error */
|
||||
if (load_refcount_block(bs, refcount_block_offset) < 0)
|
||||
return 1;
|
||||
}
|
||||
block_index = cluster_index &
|
||||
((1 << (s->cluster_bits - REFCOUNT_SHIFT)) - 1);
|
||||
return be16_to_cpu(s->refcount_block_cache[block_index]);
|
||||
}
|
||||
|
||||
static int grow_refcount_table(BlockDriverState *bs, int min_size)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int new_table_size, new_table_size2, refcount_table_clusters, i, ret;
|
||||
uint64_t *new_table;
|
||||
int64_t table_offset;
|
||||
uint8_t data[12];
|
||||
int old_table_size;
|
||||
int64_t old_table_offset;
|
||||
|
||||
if (min_size <= s->refcount_table_size)
|
||||
return 0;
|
||||
/* compute new table size */
|
||||
refcount_table_clusters = s->refcount_table_size >> (s->cluster_bits - 3);
|
||||
for(;;) {
|
||||
if (refcount_table_clusters == 0) {
|
||||
refcount_table_clusters = 1;
|
||||
} else {
|
||||
refcount_table_clusters = (refcount_table_clusters * 3 + 1) / 2;
|
||||
}
|
||||
new_table_size = refcount_table_clusters << (s->cluster_bits - 3);
|
||||
if (min_size <= new_table_size)
|
||||
break;
|
||||
}
|
||||
#ifdef DEBUG_ALLOC2
|
||||
printf("grow_refcount_table from %d to %d\n",
|
||||
s->refcount_table_size,
|
||||
new_table_size);
|
||||
#endif
|
||||
new_table_size2 = new_table_size * sizeof(uint64_t);
|
||||
new_table = qemu_mallocz(new_table_size2);
|
||||
memcpy(new_table, s->refcount_table,
|
||||
s->refcount_table_size * sizeof(uint64_t));
|
||||
for(i = 0; i < s->refcount_table_size; i++)
|
||||
cpu_to_be64s(&new_table[i]);
|
||||
/* Note: we cannot update the refcount now to avoid recursion */
|
||||
table_offset = alloc_clusters_noref(bs, new_table_size2);
|
||||
ret = bdrv_pwrite(s->hd, table_offset, new_table, new_table_size2);
|
||||
if (ret != new_table_size2)
|
||||
goto fail;
|
||||
for(i = 0; i < s->refcount_table_size; i++)
|
||||
be64_to_cpus(&new_table[i]);
|
||||
|
||||
cpu_to_be64w((uint64_t*)data, table_offset);
|
||||
cpu_to_be32w((uint32_t*)(data + 8), refcount_table_clusters);
|
||||
if (bdrv_pwrite(s->hd, offsetof(QCowHeader, refcount_table_offset),
|
||||
data, sizeof(data)) != sizeof(data))
|
||||
goto fail;
|
||||
qemu_free(s->refcount_table);
|
||||
old_table_offset = s->refcount_table_offset;
|
||||
old_table_size = s->refcount_table_size;
|
||||
s->refcount_table = new_table;
|
||||
s->refcount_table_size = new_table_size;
|
||||
s->refcount_table_offset = table_offset;
|
||||
|
||||
update_refcount(bs, table_offset, new_table_size2, 1);
|
||||
qcow2_free_clusters(bs, old_table_offset, old_table_size * sizeof(uint64_t));
|
||||
return 0;
|
||||
fail:
|
||||
qemu_free(new_table);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
|
||||
static int64_t alloc_refcount_block(BlockDriverState *bs, int64_t cluster_index)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int64_t offset, refcount_block_offset;
|
||||
unsigned int refcount_table_index;
|
||||
int ret;
|
||||
uint64_t data64;
|
||||
int cache = cache_refcount_updates;
|
||||
|
||||
/* Find L1 index and grow refcount table if needed */
|
||||
refcount_table_index = cluster_index >> (s->cluster_bits - REFCOUNT_SHIFT);
|
||||
if (refcount_table_index >= s->refcount_table_size) {
|
||||
ret = grow_refcount_table(bs, refcount_table_index + 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Load or allocate the refcount block */
|
||||
refcount_block_offset = s->refcount_table[refcount_table_index];
|
||||
if (!refcount_block_offset) {
|
||||
if (cache_refcount_updates) {
|
||||
write_refcount_block(s);
|
||||
cache_refcount_updates = 0;
|
||||
}
|
||||
/* create a new refcount block */
|
||||
/* Note: we cannot update the refcount now to avoid recursion */
|
||||
offset = alloc_clusters_noref(bs, s->cluster_size);
|
||||
memset(s->refcount_block_cache, 0, s->cluster_size);
|
||||
ret = bdrv_pwrite(s->hd, offset, s->refcount_block_cache, s->cluster_size);
|
||||
if (ret != s->cluster_size)
|
||||
return -EINVAL;
|
||||
s->refcount_table[refcount_table_index] = offset;
|
||||
data64 = cpu_to_be64(offset);
|
||||
ret = bdrv_pwrite(s->hd, s->refcount_table_offset +
|
||||
refcount_table_index * sizeof(uint64_t),
|
||||
&data64, sizeof(data64));
|
||||
if (ret != sizeof(data64))
|
||||
return -EINVAL;
|
||||
|
||||
refcount_block_offset = offset;
|
||||
s->refcount_block_cache_offset = offset;
|
||||
update_refcount(bs, offset, s->cluster_size, 1);
|
||||
cache_refcount_updates = cache;
|
||||
} else {
|
||||
if (refcount_block_offset != s->refcount_block_cache_offset) {
|
||||
if (load_refcount_block(bs, refcount_block_offset) < 0)
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
return refcount_block_offset;
|
||||
}
|
||||
|
||||
#define REFCOUNTS_PER_SECTOR (512 >> REFCOUNT_SHIFT)
|
||||
static int write_refcount_block_entries(BDRVQcowState *s,
|
||||
int64_t refcount_block_offset, int first_index, int last_index)
|
||||
{
|
||||
size_t size;
|
||||
|
||||
if (cache_refcount_updates) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
first_index &= ~(REFCOUNTS_PER_SECTOR - 1);
|
||||
last_index = (last_index + REFCOUNTS_PER_SECTOR)
|
||||
& ~(REFCOUNTS_PER_SECTOR - 1);
|
||||
|
||||
size = (last_index - first_index) << REFCOUNT_SHIFT;
|
||||
if (bdrv_pwrite(s->hd,
|
||||
refcount_block_offset + (first_index << REFCOUNT_SHIFT),
|
||||
&s->refcount_block_cache[first_index], size) != size)
|
||||
{
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* XXX: cache several refcount block clusters ? */
|
||||
static int update_refcount(BlockDriverState *bs,
|
||||
int64_t offset, int64_t length,
|
||||
int addend)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int64_t start, last, cluster_offset;
|
||||
int64_t refcount_block_offset = 0;
|
||||
int64_t table_index = -1, old_table_index;
|
||||
int first_index = -1, last_index = -1;
|
||||
|
||||
#ifdef DEBUG_ALLOC2
|
||||
printf("update_refcount: offset=%" PRId64 " size=%" PRId64 " addend=%d\n",
|
||||
offset, length, addend);
|
||||
#endif
|
||||
if (length <= 0)
|
||||
return -EINVAL;
|
||||
start = offset & ~(s->cluster_size - 1);
|
||||
last = (offset + length - 1) & ~(s->cluster_size - 1);
|
||||
for(cluster_offset = start; cluster_offset <= last;
|
||||
cluster_offset += s->cluster_size)
|
||||
{
|
||||
int block_index, refcount;
|
||||
int64_t cluster_index = cluster_offset >> s->cluster_bits;
|
||||
|
||||
/* Only write refcount block to disk when we are done with it */
|
||||
old_table_index = table_index;
|
||||
table_index = cluster_index >> (s->cluster_bits - REFCOUNT_SHIFT);
|
||||
if ((old_table_index >= 0) && (table_index != old_table_index)) {
|
||||
|
||||
if (write_refcount_block_entries(s, refcount_block_offset,
|
||||
first_index, last_index) < 0)
|
||||
{
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
first_index = -1;
|
||||
last_index = -1;
|
||||
}
|
||||
|
||||
/* Load the refcount block and allocate it if needed */
|
||||
refcount_block_offset = alloc_refcount_block(bs, cluster_index);
|
||||
if (refcount_block_offset < 0) {
|
||||
return refcount_block_offset;
|
||||
}
|
||||
|
||||
/* we can update the count and save it */
|
||||
block_index = cluster_index &
|
||||
((1 << (s->cluster_bits - REFCOUNT_SHIFT)) - 1);
|
||||
if (first_index == -1 || block_index < first_index) {
|
||||
first_index = block_index;
|
||||
}
|
||||
if (block_index > last_index) {
|
||||
last_index = block_index;
|
||||
}
|
||||
|
||||
refcount = be16_to_cpu(s->refcount_block_cache[block_index]);
|
||||
refcount += addend;
|
||||
if (refcount < 0 || refcount > 0xffff)
|
||||
return -EINVAL;
|
||||
if (refcount == 0 && cluster_index < s->free_cluster_index) {
|
||||
s->free_cluster_index = cluster_index;
|
||||
}
|
||||
s->refcount_block_cache[block_index] = cpu_to_be16(refcount);
|
||||
}
|
||||
|
||||
/* Write last changed block to disk */
|
||||
if (refcount_block_offset != 0) {
|
||||
if (write_refcount_block_entries(s, refcount_block_offset,
|
||||
first_index, last_index) < 0)
|
||||
{
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* addend must be 1 or -1 */
|
||||
static int update_cluster_refcount(BlockDriverState *bs,
|
||||
int64_t cluster_index,
|
||||
int addend)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int ret;
|
||||
|
||||
ret = update_refcount(bs, cluster_index << s->cluster_bits, 1, addend);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return get_refcount(bs, cluster_index);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*********************************************************/
|
||||
/* cluster allocation functions */
|
||||
|
||||
|
||||
|
||||
/* return < 0 if error */
|
||||
static int64_t alloc_clusters_noref(BlockDriverState *bs, int64_t size)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int i, nb_clusters;
|
||||
|
||||
nb_clusters = size_to_clusters(s, size);
|
||||
retry:
|
||||
for(i = 0; i < nb_clusters; i++) {
|
||||
int64_t i = s->free_cluster_index++;
|
||||
if (get_refcount(bs, i) != 0)
|
||||
goto retry;
|
||||
}
|
||||
#ifdef DEBUG_ALLOC2
|
||||
printf("alloc_clusters: size=%" PRId64 " -> %" PRId64 "\n",
|
||||
size,
|
||||
(s->free_cluster_index - nb_clusters) << s->cluster_bits);
|
||||
#endif
|
||||
return (s->free_cluster_index - nb_clusters) << s->cluster_bits;
|
||||
}
|
||||
|
||||
int64_t qcow2_alloc_clusters(BlockDriverState *bs, int64_t size)
|
||||
{
|
||||
int64_t offset;
|
||||
|
||||
offset = alloc_clusters_noref(bs, size);
|
||||
update_refcount(bs, offset, size, 1);
|
||||
return offset;
|
||||
}
|
||||
|
||||
/* only used to allocate compressed sectors. We try to allocate
|
||||
contiguous sectors. size must be <= cluster_size */
|
||||
int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int64_t offset, cluster_offset;
|
||||
int free_in_cluster;
|
||||
|
||||
assert(size > 0 && size <= s->cluster_size);
|
||||
if (s->free_byte_offset == 0) {
|
||||
s->free_byte_offset = qcow2_alloc_clusters(bs, s->cluster_size);
|
||||
}
|
||||
redo:
|
||||
free_in_cluster = s->cluster_size -
|
||||
(s->free_byte_offset & (s->cluster_size - 1));
|
||||
if (size <= free_in_cluster) {
|
||||
/* enough space in current cluster */
|
||||
offset = s->free_byte_offset;
|
||||
s->free_byte_offset += size;
|
||||
free_in_cluster -= size;
|
||||
if (free_in_cluster == 0)
|
||||
s->free_byte_offset = 0;
|
||||
if ((offset & (s->cluster_size - 1)) != 0)
|
||||
update_cluster_refcount(bs, offset >> s->cluster_bits, 1);
|
||||
} else {
|
||||
offset = qcow2_alloc_clusters(bs, s->cluster_size);
|
||||
cluster_offset = s->free_byte_offset & ~(s->cluster_size - 1);
|
||||
if ((cluster_offset + s->cluster_size) == offset) {
|
||||
/* we are lucky: contiguous data */
|
||||
offset = s->free_byte_offset;
|
||||
update_cluster_refcount(bs, offset >> s->cluster_bits, 1);
|
||||
s->free_byte_offset += size;
|
||||
} else {
|
||||
s->free_byte_offset = offset;
|
||||
goto redo;
|
||||
}
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
void qcow2_free_clusters(BlockDriverState *bs,
|
||||
int64_t offset, int64_t size)
|
||||
{
|
||||
update_refcount(bs, offset, size, -1);
|
||||
}
|
||||
|
||||
/*
|
||||
* free_any_clusters
|
||||
*
|
||||
* free clusters according to its type: compressed or not
|
||||
*
|
||||
*/
|
||||
|
||||
void qcow2_free_any_clusters(BlockDriverState *bs,
|
||||
uint64_t cluster_offset, int nb_clusters)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
|
||||
/* free the cluster */
|
||||
|
||||
if (cluster_offset & QCOW_OFLAG_COMPRESSED) {
|
||||
int nb_csectors;
|
||||
nb_csectors = ((cluster_offset >> s->csize_shift) &
|
||||
s->csize_mask) + 1;
|
||||
qcow2_free_clusters(bs,
|
||||
(cluster_offset & s->cluster_offset_mask) & ~511,
|
||||
nb_csectors * 512);
|
||||
return;
|
||||
}
|
||||
|
||||
qcow2_free_clusters(bs, cluster_offset, nb_clusters << s->cluster_bits);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*********************************************************/
|
||||
/* snapshots and image creation */
|
||||
|
||||
|
||||
|
||||
void qcow2_create_refcount_update(QCowCreateState *s, int64_t offset,
|
||||
int64_t size)
|
||||
{
|
||||
int refcount;
|
||||
int64_t start, last, cluster_offset;
|
||||
uint16_t *p;
|
||||
|
||||
start = offset & ~(s->cluster_size - 1);
|
||||
last = (offset + size - 1) & ~(s->cluster_size - 1);
|
||||
for(cluster_offset = start; cluster_offset <= last;
|
||||
cluster_offset += s->cluster_size) {
|
||||
p = &s->refcount_block[cluster_offset >> s->cluster_bits];
|
||||
refcount = be16_to_cpu(*p);
|
||||
refcount++;
|
||||
*p = cpu_to_be16(refcount);
|
||||
}
|
||||
}
|
||||
|
||||
/* update the refcounts of snapshots and the copied flag */
|
||||
int qcow2_update_snapshot_refcount(BlockDriverState *bs,
|
||||
int64_t l1_table_offset, int l1_size, int addend)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
uint64_t *l1_table, *l2_table, l2_offset, offset, l1_size2, l1_allocated;
|
||||
int64_t old_offset, old_l2_offset;
|
||||
int l2_size, i, j, l1_modified, l2_modified, nb_csectors, refcount;
|
||||
|
||||
qcow2_l2_cache_reset(bs);
|
||||
cache_refcount_updates = 1;
|
||||
|
||||
l2_table = NULL;
|
||||
l1_table = NULL;
|
||||
l1_size2 = l1_size * sizeof(uint64_t);
|
||||
l1_allocated = 0;
|
||||
if (l1_table_offset != s->l1_table_offset) {
|
||||
if (l1_size2 != 0) {
|
||||
l1_table = qemu_mallocz(align_offset(l1_size2, 512));
|
||||
} else {
|
||||
l1_table = NULL;
|
||||
}
|
||||
l1_allocated = 1;
|
||||
if (bdrv_pread(s->hd, l1_table_offset,
|
||||
l1_table, l1_size2) != l1_size2)
|
||||
goto fail;
|
||||
for(i = 0;i < l1_size; i++)
|
||||
be64_to_cpus(&l1_table[i]);
|
||||
} else {
|
||||
assert(l1_size == s->l1_size);
|
||||
l1_table = s->l1_table;
|
||||
l1_allocated = 0;
|
||||
}
|
||||
|
||||
l2_size = s->l2_size * sizeof(uint64_t);
|
||||
l2_table = qemu_malloc(l2_size);
|
||||
l1_modified = 0;
|
||||
for(i = 0; i < l1_size; i++) {
|
||||
l2_offset = l1_table[i];
|
||||
if (l2_offset) {
|
||||
old_l2_offset = l2_offset;
|
||||
l2_offset &= ~QCOW_OFLAG_COPIED;
|
||||
l2_modified = 0;
|
||||
if (bdrv_pread(s->hd, l2_offset, l2_table, l2_size) != l2_size)
|
||||
goto fail;
|
||||
for(j = 0; j < s->l2_size; j++) {
|
||||
offset = be64_to_cpu(l2_table[j]);
|
||||
if (offset != 0) {
|
||||
old_offset = offset;
|
||||
offset &= ~QCOW_OFLAG_COPIED;
|
||||
if (offset & QCOW_OFLAG_COMPRESSED) {
|
||||
nb_csectors = ((offset >> s->csize_shift) &
|
||||
s->csize_mask) + 1;
|
||||
if (addend != 0)
|
||||
update_refcount(bs, (offset & s->cluster_offset_mask) & ~511,
|
||||
nb_csectors * 512, addend);
|
||||
/* compressed clusters are never modified */
|
||||
refcount = 2;
|
||||
} else {
|
||||
if (addend != 0) {
|
||||
refcount = update_cluster_refcount(bs, offset >> s->cluster_bits, addend);
|
||||
} else {
|
||||
refcount = get_refcount(bs, offset >> s->cluster_bits);
|
||||
}
|
||||
}
|
||||
|
||||
if (refcount == 1) {
|
||||
offset |= QCOW_OFLAG_COPIED;
|
||||
}
|
||||
if (offset != old_offset) {
|
||||
l2_table[j] = cpu_to_be64(offset);
|
||||
l2_modified = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (l2_modified) {
|
||||
if (bdrv_pwrite(s->hd,
|
||||
l2_offset, l2_table, l2_size) != l2_size)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (addend != 0) {
|
||||
refcount = update_cluster_refcount(bs, l2_offset >> s->cluster_bits, addend);
|
||||
} else {
|
||||
refcount = get_refcount(bs, l2_offset >> s->cluster_bits);
|
||||
}
|
||||
if (refcount == 1) {
|
||||
l2_offset |= QCOW_OFLAG_COPIED;
|
||||
}
|
||||
if (l2_offset != old_l2_offset) {
|
||||
l1_table[i] = l2_offset;
|
||||
l1_modified = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (l1_modified) {
|
||||
for(i = 0; i < l1_size; i++)
|
||||
cpu_to_be64s(&l1_table[i]);
|
||||
if (bdrv_pwrite(s->hd, l1_table_offset, l1_table,
|
||||
l1_size2) != l1_size2)
|
||||
goto fail;
|
||||
for(i = 0; i < l1_size; i++)
|
||||
be64_to_cpus(&l1_table[i]);
|
||||
}
|
||||
if (l1_allocated)
|
||||
qemu_free(l1_table);
|
||||
qemu_free(l2_table);
|
||||
cache_refcount_updates = 0;
|
||||
write_refcount_block(s);
|
||||
return 0;
|
||||
fail:
|
||||
if (l1_allocated)
|
||||
qemu_free(l1_table);
|
||||
qemu_free(l2_table);
|
||||
cache_refcount_updates = 0;
|
||||
write_refcount_block(s);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*********************************************************/
|
||||
/* refcount checking functions */
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Increases the refcount for a range of clusters in a given refcount table.
|
||||
* This is used to construct a temporary refcount table out of L1 and L2 tables
|
||||
* which can be compared the the refcount table saved in the image.
|
||||
*
|
||||
* Returns the number of errors in the image that were found
|
||||
*/
|
||||
static int inc_refcounts(BlockDriverState *bs,
|
||||
uint16_t *refcount_table,
|
||||
int refcount_table_size,
|
||||
int64_t offset, int64_t size)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int64_t start, last, cluster_offset;
|
||||
int k;
|
||||
int errors = 0;
|
||||
|
||||
if (size <= 0)
|
||||
return 0;
|
||||
|
||||
start = offset & ~(s->cluster_size - 1);
|
||||
last = (offset + size - 1) & ~(s->cluster_size - 1);
|
||||
for(cluster_offset = start; cluster_offset <= last;
|
||||
cluster_offset += s->cluster_size) {
|
||||
k = cluster_offset >> s->cluster_bits;
|
||||
if (k < 0 || k >= refcount_table_size) {
|
||||
fprintf(stderr, "ERROR: invalid cluster offset=0x%" PRIx64 "\n",
|
||||
cluster_offset);
|
||||
errors++;
|
||||
} else {
|
||||
if (++refcount_table[k] == 0) {
|
||||
fprintf(stderr, "ERROR: overflow cluster offset=0x%" PRIx64
|
||||
"\n", cluster_offset);
|
||||
errors++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
/*
|
||||
* Increases the refcount in the given refcount table for the all clusters
|
||||
* referenced in the L2 table. While doing so, performs some checks on L2
|
||||
* entries.
|
||||
*
|
||||
* Returns the number of errors found by the checks or -errno if an internal
|
||||
* error occurred.
|
||||
*/
|
||||
static int check_refcounts_l2(BlockDriverState *bs,
|
||||
uint16_t *refcount_table, int refcount_table_size, int64_t l2_offset,
|
||||
int check_copied)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
uint64_t *l2_table, offset;
|
||||
int i, l2_size, nb_csectors, refcount;
|
||||
int errors = 0;
|
||||
|
||||
/* Read L2 table from disk */
|
||||
l2_size = s->l2_size * sizeof(uint64_t);
|
||||
l2_table = qemu_malloc(l2_size);
|
||||
|
||||
if (bdrv_pread(s->hd, l2_offset, l2_table, l2_size) != l2_size)
|
||||
goto fail;
|
||||
|
||||
/* Do the actual checks */
|
||||
for(i = 0; i < s->l2_size; i++) {
|
||||
offset = be64_to_cpu(l2_table[i]);
|
||||
if (offset != 0) {
|
||||
if (offset & QCOW_OFLAG_COMPRESSED) {
|
||||
/* Compressed clusters don't have QCOW_OFLAG_COPIED */
|
||||
if (offset & QCOW_OFLAG_COPIED) {
|
||||
fprintf(stderr, "ERROR: cluster %" PRId64 ": "
|
||||
"copied flag must never be set for compressed "
|
||||
"clusters\n", offset >> s->cluster_bits);
|
||||
offset &= ~QCOW_OFLAG_COPIED;
|
||||
errors++;
|
||||
}
|
||||
|
||||
/* Mark cluster as used */
|
||||
nb_csectors = ((offset >> s->csize_shift) &
|
||||
s->csize_mask) + 1;
|
||||
offset &= s->cluster_offset_mask;
|
||||
errors += inc_refcounts(bs, refcount_table,
|
||||
refcount_table_size,
|
||||
offset & ~511, nb_csectors * 512);
|
||||
} else {
|
||||
/* QCOW_OFLAG_COPIED must be set iff refcount == 1 */
|
||||
if (check_copied) {
|
||||
uint64_t entry = offset;
|
||||
offset &= ~QCOW_OFLAG_COPIED;
|
||||
refcount = get_refcount(bs, offset >> s->cluster_bits);
|
||||
if ((refcount == 1) != ((entry & QCOW_OFLAG_COPIED) != 0)) {
|
||||
fprintf(stderr, "ERROR OFLAG_COPIED: offset=%"
|
||||
PRIx64 " refcount=%d\n", entry, refcount);
|
||||
errors++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Mark cluster as used */
|
||||
offset &= ~QCOW_OFLAG_COPIED;
|
||||
errors += inc_refcounts(bs, refcount_table,
|
||||
refcount_table_size,
|
||||
offset, s->cluster_size);
|
||||
|
||||
/* Correct offsets are cluster aligned */
|
||||
if (offset & (s->cluster_size - 1)) {
|
||||
fprintf(stderr, "ERROR offset=%" PRIx64 ": Cluster is not "
|
||||
"properly aligned; L2 entry corrupted.\n", offset);
|
||||
errors++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
qemu_free(l2_table);
|
||||
return errors;
|
||||
|
||||
fail:
|
||||
fprintf(stderr, "ERROR: I/O error in check_refcounts_l1\n");
|
||||
qemu_free(l2_table);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/*
|
||||
* Increases the refcount for the L1 table, its L2 tables and all referenced
|
||||
* clusters in the given refcount table. While doing so, performs some checks
|
||||
* on L1 and L2 entries.
|
||||
*
|
||||
* Returns the number of errors found by the checks or -errno if an internal
|
||||
* error occurred.
|
||||
*/
|
||||
static int check_refcounts_l1(BlockDriverState *bs,
|
||||
uint16_t *refcount_table,
|
||||
int refcount_table_size,
|
||||
int64_t l1_table_offset, int l1_size,
|
||||
int check_copied)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
uint64_t *l1_table, l2_offset, l1_size2;
|
||||
int i, refcount, ret;
|
||||
int errors = 0;
|
||||
|
||||
l1_size2 = l1_size * sizeof(uint64_t);
|
||||
|
||||
/* Mark L1 table as used */
|
||||
errors += inc_refcounts(bs, refcount_table, refcount_table_size,
|
||||
l1_table_offset, l1_size2);
|
||||
|
||||
/* Read L1 table entries from disk */
|
||||
if (l1_size2 == 0) {
|
||||
l1_table = NULL;
|
||||
} else {
|
||||
l1_table = qemu_malloc(l1_size2);
|
||||
if (bdrv_pread(s->hd, l1_table_offset,
|
||||
l1_table, l1_size2) != l1_size2)
|
||||
goto fail;
|
||||
for(i = 0;i < l1_size; i++)
|
||||
be64_to_cpus(&l1_table[i]);
|
||||
}
|
||||
|
||||
/* Do the actual checks */
|
||||
for(i = 0; i < l1_size; i++) {
|
||||
l2_offset = l1_table[i];
|
||||
if (l2_offset) {
|
||||
/* QCOW_OFLAG_COPIED must be set iff refcount == 1 */
|
||||
if (check_copied) {
|
||||
refcount = get_refcount(bs, (l2_offset & ~QCOW_OFLAG_COPIED)
|
||||
>> s->cluster_bits);
|
||||
if ((refcount == 1) != ((l2_offset & QCOW_OFLAG_COPIED) != 0)) {
|
||||
fprintf(stderr, "ERROR OFLAG_COPIED: l2_offset=%" PRIx64
|
||||
" refcount=%d\n", l2_offset, refcount);
|
||||
errors++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Mark L2 table as used */
|
||||
l2_offset &= ~QCOW_OFLAG_COPIED;
|
||||
errors += inc_refcounts(bs, refcount_table,
|
||||
refcount_table_size,
|
||||
l2_offset,
|
||||
s->cluster_size);
|
||||
|
||||
/* L2 tables are cluster aligned */
|
||||
if (l2_offset & (s->cluster_size - 1)) {
|
||||
fprintf(stderr, "ERROR l2_offset=%" PRIx64 ": Table is not "
|
||||
"cluster aligned; L1 entry corrupted\n", l2_offset);
|
||||
errors++;
|
||||
}
|
||||
|
||||
/* Process and check L2 entries */
|
||||
ret = check_refcounts_l2(bs, refcount_table, refcount_table_size,
|
||||
l2_offset, check_copied);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
errors += ret;
|
||||
}
|
||||
}
|
||||
qemu_free(l1_table);
|
||||
return errors;
|
||||
|
||||
fail:
|
||||
fprintf(stderr, "ERROR: I/O error in check_refcounts_l1\n");
|
||||
qemu_free(l1_table);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks an image for refcount consistency.
|
||||
*
|
||||
* Returns 0 if no errors are found, the number of errors in case the image is
|
||||
* detected as corrupted, and -errno when an internal error occured.
|
||||
*/
|
||||
int qcow2_check_refcounts(BlockDriverState *bs)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int64_t size;
|
||||
int nb_clusters, refcount1, refcount2, i;
|
||||
QCowSnapshot *sn;
|
||||
uint16_t *refcount_table;
|
||||
int ret, errors = 0;
|
||||
|
||||
size = bdrv_getlength(s->hd);
|
||||
nb_clusters = size_to_clusters(s, size);
|
||||
refcount_table = qemu_mallocz(nb_clusters * sizeof(uint16_t));
|
||||
|
||||
/* header */
|
||||
errors += inc_refcounts(bs, refcount_table, nb_clusters,
|
||||
0, s->cluster_size);
|
||||
|
||||
/* current L1 table */
|
||||
ret = check_refcounts_l1(bs, refcount_table, nb_clusters,
|
||||
s->l1_table_offset, s->l1_size, 1);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
errors += ret;
|
||||
|
||||
/* snapshots */
|
||||
for(i = 0; i < s->nb_snapshots; i++) {
|
||||
sn = s->snapshots + i;
|
||||
check_refcounts_l1(bs, refcount_table, nb_clusters,
|
||||
sn->l1_table_offset, sn->l1_size, 0);
|
||||
}
|
||||
errors += inc_refcounts(bs, refcount_table, nb_clusters,
|
||||
s->snapshots_offset, s->snapshots_size);
|
||||
|
||||
/* refcount data */
|
||||
errors += inc_refcounts(bs, refcount_table, nb_clusters,
|
||||
s->refcount_table_offset,
|
||||
s->refcount_table_size * sizeof(uint64_t));
|
||||
for(i = 0; i < s->refcount_table_size; i++) {
|
||||
int64_t offset;
|
||||
offset = s->refcount_table[i];
|
||||
if (offset != 0) {
|
||||
errors += inc_refcounts(bs, refcount_table, nb_clusters,
|
||||
offset, s->cluster_size);
|
||||
}
|
||||
}
|
||||
|
||||
/* compare ref counts */
|
||||
for(i = 0; i < nb_clusters; i++) {
|
||||
refcount1 = get_refcount(bs, i);
|
||||
refcount2 = refcount_table[i];
|
||||
if (refcount1 != refcount2) {
|
||||
fprintf(stderr, "ERROR cluster %d refcount=%d reference=%d\n",
|
||||
i, refcount1, refcount2);
|
||||
errors++;
|
||||
}
|
||||
}
|
||||
|
||||
qemu_free(refcount_table);
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
@@ -1,410 +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(s->hd, 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(s->hd, 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(s->hd, 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;
|
||||
|
||||
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(s->hd, offset, &h, sizeof(h)) != sizeof(h))
|
||||
goto fail;
|
||||
offset += sizeof(h);
|
||||
if (bdrv_pwrite(s->hd, offset, sn->id_str, id_str_size) != id_str_size)
|
||||
goto fail;
|
||||
offset += id_str_size;
|
||||
if (bdrv_pwrite(s->hd, offset, sn->name, name_size) != name_size)
|
||||
goto fail;
|
||||
offset += name_size;
|
||||
}
|
||||
|
||||
/* update the various header fields */
|
||||
data64 = cpu_to_be64(snapshots_offset);
|
||||
if (bdrv_pwrite(s->hd, offsetof(QCowHeader, snapshots_offset),
|
||||
&data64, sizeof(data64)) != sizeof(data64))
|
||||
goto fail;
|
||||
data32 = cpu_to_be32(s->nb_snapshots);
|
||||
if (bdrv_pwrite(s->hd, offsetof(QCowHeader, nb_snapshots),
|
||||
&data32, sizeof(data32)) != sizeof(data32))
|
||||
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;
|
||||
|
||||
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 */
|
||||
sn->l1_table_offset = qcow2_alloc_clusters(bs, s->l1_size * sizeof(uint64_t));
|
||||
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(s->hd, sn->l1_table_offset,
|
||||
l1_table, s->l1_size * sizeof(uint64_t)) !=
|
||||
(s->l1_size * sizeof(uint64_t)))
|
||||
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(s->hd, sn->l1_table_offset,
|
||||
s->l1_table, l1_size2) != l1_size2)
|
||||
goto fail;
|
||||
if (bdrv_pwrite(s->hd, s->l1_table_offset,
|
||||
s->l1_table, l1_size2) != l1_size2)
|
||||
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;
|
||||
}
|
||||
|
||||
1149
block/qcow2.c
1149
block/qcow2.c
File diff suppressed because it is too large
Load Diff
214
block/qcow2.h
214
block/qcow2.h
@@ -1,214 +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;
|
||||
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 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);
|
||||
|
||||
/* 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(BDRVQcowState *s, 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);
|
||||
|
||||
uint64_t qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
|
||||
int *num);
|
||||
uint64_t 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, uint64_t cluster_offset,
|
||||
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 */
|
||||
1354
block/raw-posix.c
1354
block/raw-posix.c
File diff suppressed because it is too large
Load Diff
@@ -1,419 +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, create_flags;
|
||||
DWORD overlapped;
|
||||
|
||||
s->type = FTYPE_FILE;
|
||||
|
||||
if ((flags & BDRV_O_ACCESS) == O_RDWR) {
|
||||
access_flags = GENERIC_READ | GENERIC_WRITE;
|
||||
} else {
|
||||
access_flags = GENERIC_READ;
|
||||
}
|
||||
if (flags & BDRV_O_CREAT) {
|
||||
create_flags = CREATE_ALWAYS;
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
|
||||
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_raw = {
|
||||
.format_name = "raw",
|
||||
.instance_size = sizeof(BDRVRawState),
|
||||
.bdrv_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_ACCESS) == 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 BlockDriver bdrv_host_device = {
|
||||
.format_name = "host_device",
|
||||
.instance_size = sizeof(BDRVRawState),
|
||||
.bdrv_probe_device = hdev_probe_device,
|
||||
.bdrv_open = hdev_open,
|
||||
.bdrv_close = raw_close,
|
||||
.bdrv_flush = raw_flush,
|
||||
|
||||
.bdrv_read = raw_read,
|
||||
.bdrv_write = raw_write,
|
||||
.bdrv_getlength = raw_getlength,
|
||||
};
|
||||
|
||||
static void bdrv_raw_init(void)
|
||||
{
|
||||
bdrv_register(&bdrv_raw);
|
||||
bdrv_register(&bdrv_host_device);
|
||||
}
|
||||
|
||||
block_init(bdrv_raw_init);
|
||||
956
block/vdi.c
956
block/vdi.c
@@ -1,956 +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)
|
||||
{
|
||||
/* TODO: additional checks possible. */
|
||||
BDRVVdiState *s = (BDRVVdiState *)bs->opaque;
|
||||
int n_errors = 0;
|
||||
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);
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "ERROR: block index %" PRIu32
|
||||
" too large, is %" PRIu32 "\n", block, bmap_entry);
|
||||
n_errors++;
|
||||
}
|
||||
}
|
||||
}
|
||||
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);
|
||||
n_errors++;
|
||||
}
|
||||
|
||||
qemu_free(bmap);
|
||||
|
||||
return n_errors;
|
||||
}
|
||||
|
||||
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, const char *filename, int flags)
|
||||
{
|
||||
BDRVVdiState *s = bs->opaque;
|
||||
VdiHeader header;
|
||||
size_t bmap_size;
|
||||
int ret;
|
||||
|
||||
logout("\n");
|
||||
|
||||
ret = bdrv_file_open(&s->hd, filename, flags);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (bdrv_read(s->hd, 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.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("unexpected block number %u B\n", header.blocks_in_image);
|
||||
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;
|
||||
s->bmap = qemu_malloc(bmap_size * SECTOR_SIZE);
|
||||
if (bdrv_read(s->hd, s->bmap_sector, (uint8_t *)s->bmap, bmap_size) < 0) {
|
||||
goto fail_free_bmap;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail_free_bmap:
|
||||
qemu_free(s->bmap);
|
||||
|
||||
fail:
|
||||
bdrv_delete(s->hd);
|
||||
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 = (VdiAIOCB *)blockacb;
|
||||
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(s->hd, 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(s->hd, 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(s->hd, 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(s->hd, 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(s->hd, 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;
|
||||
}
|
||||
|
||||
blocks = bytes / 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 = (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)
|
||||
{
|
||||
BDRVVdiState *s = bs->opaque;
|
||||
logout("\n");
|
||||
bdrv_delete(s->hd);
|
||||
}
|
||||
|
||||
static void vdi_flush(BlockDriverState *bs)
|
||||
{
|
||||
BDRVVdiState *s = bs->opaque;
|
||||
logout("\n");
|
||||
bdrv_flush(s->hd);
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
870
block/vmdk.c
870
block/vmdk.c
@@ -1,870 +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;
|
||||
int is_parent;
|
||||
} BDRVVmdkState;
|
||||
|
||||
typedef struct VmdkMetaData {
|
||||
uint32_t offset;
|
||||
unsigned int l1_index;
|
||||
unsigned int l2_index;
|
||||
unsigned int l2_offset;
|
||||
int valid;
|
||||
} VmdkMetaData;
|
||||
|
||||
typedef struct ActiveBDRVState{
|
||||
BlockDriverState *hd; // active image handler
|
||||
uint64_t cluster_offset; // current write offset
|
||||
}ActiveBDRVState;
|
||||
|
||||
static ActiveBDRVState activeBDRV;
|
||||
|
||||
|
||||
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)
|
||||
{
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
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(s->hd, 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)
|
||||
{
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
char desc[DESC_SIZE], tmp_desc[DESC_SIZE];
|
||||
char *p_name, *tmp_str;
|
||||
|
||||
/* the descriptor offset = 0x200 */
|
||||
if (bdrv_pread(s->hd, 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(s->hd, 0x200, desc, DESC_SIZE) != DESC_SIZE)
|
||||
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;
|
||||
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 -1;
|
||||
p_fd = open(backing_file, O_RDONLY | O_BINARY | O_LARGEFILE);
|
||||
if (p_fd < 0) {
|
||||
close(snp_fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* read the header */
|
||||
if (lseek(p_fd, 0x0, SEEK_SET) == -1)
|
||||
goto fail;
|
||||
if (read(p_fd, hdr, HEADER_SIZE) != HEADER_SIZE)
|
||||
goto fail;
|
||||
|
||||
/* write the header */
|
||||
if (lseek(snp_fd, 0x0, SEEK_SET) == -1)
|
||||
goto fail;
|
||||
if (write(snp_fd, hdr, HEADER_SIZE) == -1)
|
||||
goto fail;
|
||||
|
||||
memset(&header, 0, sizeof(header));
|
||||
memcpy(&header,&hdr[4], sizeof(header)); // skip the VMDK4_MAGIC
|
||||
|
||||
ftruncate(snp_fd, header.grain_offset << 9);
|
||||
/* the descriptor offset = 0x200 */
|
||||
if (lseek(p_fd, 0x200, SEEK_SET) == -1)
|
||||
goto fail;
|
||||
if (read(p_fd, p_desc, DESC_SIZE) != DESC_SIZE)
|
||||
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)
|
||||
goto fail;
|
||||
if (write(snp_fd, s_desc, strlen(s_desc)) == -1)
|
||||
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)
|
||||
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)
|
||||
goto fail_rgd;
|
||||
if (read(p_fd, rgd_buf, gd_size) != gd_size)
|
||||
goto fail_rgd;
|
||||
if (lseek(snp_fd, rgd_offset, SEEK_SET) == -1)
|
||||
goto fail_rgd;
|
||||
if (write(snp_fd, rgd_buf, gd_size) == -1)
|
||||
goto fail_rgd;
|
||||
qemu_free(rgd_buf);
|
||||
|
||||
/* write GD */
|
||||
gd_buf = qemu_malloc(gd_size);
|
||||
if (lseek(p_fd, gd_offset, SEEK_SET) == -1)
|
||||
goto fail_gd;
|
||||
if (read(p_fd, gd_buf, gd_size) != gd_size)
|
||||
goto fail_gd;
|
||||
if (lseek(snp_fd, gd_offset, SEEK_SET) == -1)
|
||||
goto fail_gd;
|
||||
if (write(snp_fd, gd_buf, gd_size) == -1)
|
||||
goto fail_gd;
|
||||
qemu_free(gd_buf);
|
||||
|
||||
close(p_fd);
|
||||
close(snp_fd);
|
||||
return 0;
|
||||
|
||||
fail_gd:
|
||||
qemu_free(gd_buf);
|
||||
fail_rgd:
|
||||
qemu_free(rgd_buf);
|
||||
fail:
|
||||
close(p_fd);
|
||||
close(snp_fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void vmdk_parent_close(BlockDriverState *bs)
|
||||
{
|
||||
if (bs->backing_hd)
|
||||
bdrv_close(bs->backing_hd);
|
||||
}
|
||||
|
||||
static int parent_open = 0;
|
||||
static int vmdk_parent_open(BlockDriverState *bs, const char * filename)
|
||||
{
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
char *p_name;
|
||||
char desc[DESC_SIZE];
|
||||
char parent_img_name[1024];
|
||||
|
||||
/* the descriptor offset = 0x200 */
|
||||
if (bdrv_pread(s->hd, 0x200, desc, DESC_SIZE) != DESC_SIZE)
|
||||
return -1;
|
||||
|
||||
if ((p_name = strstr(desc,"parentFileNameHint")) != NULL) {
|
||||
char *end_name;
|
||||
struct stat file_buf;
|
||||
|
||||
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);
|
||||
if (stat(bs->backing_file, &file_buf) != 0) {
|
||||
path_combine(parent_img_name, sizeof(parent_img_name),
|
||||
filename, bs->backing_file);
|
||||
} else {
|
||||
pstrcpy(parent_img_name, sizeof(parent_img_name),
|
||||
bs->backing_file);
|
||||
}
|
||||
|
||||
bs->backing_hd = bdrv_new("");
|
||||
if (!bs->backing_hd) {
|
||||
failure:
|
||||
bdrv_close(s->hd);
|
||||
return -1;
|
||||
}
|
||||
parent_open = 1;
|
||||
if (bdrv_open(bs->backing_hd, parent_img_name, BDRV_O_RDONLY) < 0)
|
||||
goto failure;
|
||||
parent_open = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vmdk_open(BlockDriverState *bs, const char *filename, int flags)
|
||||
{
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
uint32_t magic;
|
||||
int l1_size, i, ret;
|
||||
|
||||
if (parent_open)
|
||||
// Parent must be opened as RO.
|
||||
flags = BDRV_O_RDONLY;
|
||||
|
||||
ret = bdrv_file_open(&s->hd, filename, flags);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (bdrv_pread(s->hd, 0, &magic, sizeof(magic)) != sizeof(magic))
|
||||
goto fail;
|
||||
|
||||
magic = be32_to_cpu(magic);
|
||||
if (magic == VMDK3_MAGIC) {
|
||||
VMDK3Header header;
|
||||
|
||||
if (bdrv_pread(s->hd, 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(s->hd, 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;
|
||||
|
||||
if (parent_open)
|
||||
s->is_parent = 1;
|
||||
else
|
||||
s->is_parent = 0;
|
||||
|
||||
// try to open parent images, if exist
|
||||
if (vmdk_parent_open(bs, filename) != 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(s->hd, 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(s->hd, 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);
|
||||
bdrv_delete(s->hd);
|
||||
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)
|
||||
{
|
||||
uint64_t parent_cluster_offset;
|
||||
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) {
|
||||
BDRVVmdkState *ps = bs->backing_hd->opaque;
|
||||
|
||||
if (!vmdk_is_cid_valid(bs))
|
||||
return -1;
|
||||
|
||||
parent_cluster_offset = get_cluster_offset(bs->backing_hd, NULL,
|
||||
offset, allocate);
|
||||
|
||||
if (parent_cluster_offset) {
|
||||
BDRVVmdkState *act_s = activeBDRV.hd->opaque;
|
||||
|
||||
if (bdrv_pread(ps->hd, parent_cluster_offset, whole_grain, ps->cluster_sectors*512) != ps->cluster_sectors*512)
|
||||
return -1;
|
||||
|
||||
//Write grain only into the active image
|
||||
if (bdrv_pwrite(act_s->hd, activeBDRV.cluster_offset << 9, whole_grain, sizeof(whole_grain)) != sizeof(whole_grain))
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vmdk_L2update(BlockDriverState *bs, VmdkMetaData *m_data)
|
||||
{
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
|
||||
/* update L2 table */
|
||||
if (bdrv_pwrite(s->hd, ((int64_t)m_data->l2_offset * 512) + (m_data->l2_index * sizeof(m_data->offset)),
|
||||
&(m_data->offset), sizeof(m_data->offset)) != sizeof(m_data->offset))
|
||||
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(s->hd, ((int64_t)m_data->l2_offset * 512) + (m_data->l2_index * sizeof(m_data->offset)),
|
||||
&(m_data->offset), sizeof(m_data->offset)) != sizeof(m_data->offset))
|
||||
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(s->hd, (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.
|
||||
if (!s->is_parent) {
|
||||
cluster_offset = bdrv_getlength(s->hd);
|
||||
bdrv_truncate(s->hd, cluster_offset + (s->cluster_sectors << 9));
|
||||
|
||||
cluster_offset >>= 9;
|
||||
tmp = cpu_to_le32(cluster_offset);
|
||||
l2_table[l2_index] = tmp;
|
||||
// Save the active image state
|
||||
activeBDRV.cluster_offset = cluster_offset;
|
||||
activeBDRV.hd = bs;
|
||||
}
|
||||
/* 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(s->hd, 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(s->hd, 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;
|
||||
|
||||
// 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 -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;
|
||||
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);
|
||||
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);
|
||||
// try to close parent image, if exist
|
||||
vmdk_parent_close(s->hd);
|
||||
bdrv_delete(s->hd);
|
||||
}
|
||||
|
||||
static void vmdk_flush(BlockDriverState *bs)
|
||||
{
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
bdrv_flush(s->hd);
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
623
block/vpc.c
623
block/vpc.c
@@ -1,623 +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, const char *filename, int flags)
|
||||
{
|
||||
BDRVVPCState *s = bs->opaque;
|
||||
int ret, i;
|
||||
struct vhd_footer* footer;
|
||||
struct vhd_dyndisk_header* dyndisk_header;
|
||||
uint8_t buf[HEADER_SIZE];
|
||||
uint32_t checksum;
|
||||
|
||||
ret = bdrv_file_open(&s->hd, filename, flags);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (bdrv_pread(s->hd, 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", 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(s->hd, 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(s->hd, 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:
|
||||
bdrv_delete(s->hd);
|
||||
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(s->hd, 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(s->hd, 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(s->hd, 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(s->hd, 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;
|
||||
|
||||
while (nb_sectors > 0) {
|
||||
offset = get_sector_offset(bs, sector_num, 0);
|
||||
|
||||
if (offset == -1) {
|
||||
memset(buf, 0, 512);
|
||||
} else {
|
||||
ret = bdrv_pread(s->hd, offset, buf, 512);
|
||||
if (ret != 512)
|
||||
return -1;
|
||||
}
|
||||
|
||||
nb_sectors--;
|
||||
sector_num++;
|
||||
buf += 512;
|
||||
}
|
||||
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;
|
||||
int ret;
|
||||
|
||||
while (nb_sectors > 0) {
|
||||
offset = get_sector_offset(bs, sector_num, 1);
|
||||
|
||||
if (offset == -1) {
|
||||
offset = alloc_block(bs, sector_num);
|
||||
if (offset < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = bdrv_pwrite(s->hd, offset, buf, 512);
|
||||
if (ret != 512)
|
||||
return -1;
|
||||
|
||||
nb_sectors--;
|
||||
sector_num++;
|
||||
buf += 512;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// Note: Rounding up deviates from the Virtual PC behaviour
|
||||
// However, we need this to avoid truncating images in qemu-img convert
|
||||
*cyls = (cyls_times_heads + *heads - 1) / *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;
|
||||
uint8_t heads;
|
||||
uint8_t secs_per_cyl;
|
||||
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
|
||||
if (calculate_geometry(total_sectors, &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
|
||||
bdrv_delete(s->hd);
|
||||
}
|
||||
|
||||
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);
|
||||
151
block_int.h
151
block_int.h
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* QEMU System Emulator block driver
|
||||
*
|
||||
*
|
||||
* Copyright (c) 2003 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
|
||||
@@ -24,178 +24,57 @@
|
||||
#ifndef BLOCK_INT_H
|
||||
#define BLOCK_INT_H
|
||||
|
||||
#include "block.h"
|
||||
#include "qemu-option.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, const char *filename, int flags);
|
||||
int (*bdrv_read)(BlockDriverState *bs, int64_t sector_num,
|
||||
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,
|
||||
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);
|
||||
|
||||
/* 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 number of errors in image, -errno for internal errors */
|
||||
int (*bdrv_check)(BlockDriverState* bs);
|
||||
|
||||
/* Set if newly created images are not guaranteed to contain only zeros */
|
||||
int no_zero_init;
|
||||
|
||||
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 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 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;
|
||||
|
||||
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;
|
||||
/* 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;
|
||||
|
||||
/* 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;
|
||||
char device_name[32];
|
||||
unsigned long *dirty_bitmap;
|
||||
BlockDriverState *next;
|
||||
void *private;
|
||||
};
|
||||
|
||||
struct BlockDriverAIOCB {
|
||||
AIOPool *pool;
|
||||
BlockDriverState *bs;
|
||||
BlockDriverCompletionFunc *cb;
|
||||
void *opaque;
|
||||
BlockDriverAIOCB *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);
|
||||
|
||||
extern BlockDriverState *bdrv_first;
|
||||
|
||||
#ifdef _WIN32
|
||||
int is_windows_drive(const char *filename);
|
||||
#endif
|
||||
|
||||
#endif /* BLOCK_INT_H */
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
1552
bsd-user/elfload.c
1552
bsd-user/elfload.c
File diff suppressed because it is too large
Load Diff
@@ -1,149 +0,0 @@
|
||||
/* $OpenBSD: errno.h,v 1.20 2007/09/03 14:37:52 millert Exp $ */
|
||||
/* $NetBSD: errno.h,v 1.10 1996/01/20 01:33:53 jtc Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1982, 1986, 1989, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
* (c) UNIX System Laboratories, Inc.
|
||||
* All or some portions of this file are derived from material licensed
|
||||
* to the University of California by American Telephone and Telegraph
|
||||
* Co. or Unix System Laboratories, Inc. and are reproduced herein with
|
||||
* the permission of UNIX System Laboratories, Inc.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @(#)errno.h 8.5 (Berkeley) 1/21/94
|
||||
*/
|
||||
|
||||
#define TARGET_EPERM 1 /* Operation not permitted */
|
||||
#define TARGET_ENOENT 2 /* No such file or directory */
|
||||
#define TARGET_ESRCH 3 /* No such process */
|
||||
#define TARGET_EINTR 4 /* Interrupted system call */
|
||||
#define TARGET_EIO 5 /* Input/output error */
|
||||
#define TARGET_ENXIO 6 /* Device not configured */
|
||||
#define TARGET_E2BIG 7 /* Argument list too long */
|
||||
#define TARGET_ENOEXEC 8 /* Exec format error */
|
||||
#define TARGET_EBADF 9 /* Bad file descriptor */
|
||||
#define TARGET_ECHILD 10 /* No child processes */
|
||||
#define TARGET_EDEADLK 11 /* Resource deadlock avoided */
|
||||
/* 11 was EAGAIN */
|
||||
#define TARGET_ENOMEM 12 /* Cannot allocate memory */
|
||||
#define TARGET_EACCES 13 /* Permission denied */
|
||||
#define TARGET_EFAULT 14 /* Bad address */
|
||||
#define TARGET_ENOTBLK 15 /* Block device required */
|
||||
#define TARGET_EBUSY 16 /* Device busy */
|
||||
#define TARGET_EEXIST 17 /* File exists */
|
||||
#define TARGET_EXDEV 18 /* Cross-device link */
|
||||
#define TARGET_ENODEV 19 /* Operation not supported by device */
|
||||
#define TARGET_ENOTDIR 20 /* Not a directory */
|
||||
#define TARGET_EISDIR 21 /* Is a directory */
|
||||
#define TARGET_EINVAL 22 /* Invalid argument */
|
||||
#define TARGET_ENFILE 23 /* Too many open files in system */
|
||||
#define TARGET_EMFILE 24 /* Too many open files */
|
||||
#define TARGET_ENOTTY 25 /* Inappropriate ioctl for device */
|
||||
#define TARGET_ETXTBSY 26 /* Text file busy */
|
||||
#define TARGET_EFBIG 27 /* File too large */
|
||||
#define TARGET_ENOSPC 28 /* No space left on device */
|
||||
#define TARGET_ESPIPE 29 /* Illegal seek */
|
||||
#define TARGET_EROFS 30 /* Read-only file system */
|
||||
#define TARGET_EMLINK 31 /* Too many links */
|
||||
#define TARGET_EPIPE 32 /* Broken pipe */
|
||||
|
||||
/* math software */
|
||||
#define TARGET_EDOM 33 /* Numerical argument out of domain */
|
||||
#define TARGET_ERANGE 34 /* Result too large */
|
||||
|
||||
/* non-blocking and interrupt i/o */
|
||||
#define TARGET_EAGAIN 35 /* Resource temporarily unavailable */
|
||||
#define TARGET_EWOULDBLOCK EAGAIN /* Operation would block */
|
||||
#define TARGET_EINPROGRESS 36 /* Operation now in progress */
|
||||
#define TARGET_EALREADY 37 /* Operation already in progress */
|
||||
|
||||
/* ipc/network software -- argument errors */
|
||||
#define TARGET_ENOTSOCK 38 /* Socket operation on non-socket */
|
||||
#define TARGET_EDESTADDRREQ 39 /* Destination address required */
|
||||
#define TARGET_EMSGSIZE 40 /* Message too long */
|
||||
#define TARGET_EPROTOTYPE 41 /* Protocol wrong type for socket */
|
||||
#define TARGET_ENOPROTOOPT 42 /* Protocol not available */
|
||||
#define TARGET_EPROTONOSUPPORT 43 /* Protocol not supported */
|
||||
#define TARGET_ESOCKTNOSUPPORT 44 /* Socket type not supported */
|
||||
#define TARGET_EOPNOTSUPP 45 /* Operation not supported */
|
||||
#define TARGET_EPFNOSUPPORT 46 /* Protocol family not supported */
|
||||
#define TARGET_EAFNOSUPPORT 47 /* Address family not supported by protocol family */
|
||||
#define TARGET_EADDRINUSE 48 /* Address already in use */
|
||||
#define TARGET_EADDRNOTAVAIL 49 /* Can't assign requested address */
|
||||
|
||||
/* ipc/network software -- operational errors */
|
||||
#define TARGET_ENETDOWN 50 /* Network is down */
|
||||
#define TARGET_ENETUNREACH 51 /* Network is unreachable */
|
||||
#define TARGET_ENETRESET 52 /* Network dropped connection on reset */
|
||||
#define TARGET_ECONNABORTED 53 /* Software caused connection abort */
|
||||
#define TARGET_ECONNRESET 54 /* Connection reset by peer */
|
||||
#define TARGET_ENOBUFS 55 /* No buffer space available */
|
||||
#define TARGET_EISCONN 56 /* Socket is already connected */
|
||||
#define TARGET_ENOTCONN 57 /* Socket is not connected */
|
||||
#define TARGET_ESHUTDOWN 58 /* Can't send after socket shutdown */
|
||||
#define TARGET_ETOOMANYREFS 59 /* Too many references: can't splice */
|
||||
#define TARGET_ETIMEDOUT 60 /* Operation timed out */
|
||||
#define TARGET_ECONNREFUSED 61 /* Connection refused */
|
||||
|
||||
#define TARGET_ELOOP 62 /* Too many levels of symbolic links */
|
||||
#define TARGET_ENAMETOOLONG 63 /* File name too long */
|
||||
|
||||
/* should be rearranged */
|
||||
#define TARGET_EHOSTDOWN 64 /* Host is down */
|
||||
#define TARGET_EHOSTUNREACH 65 /* No route to host */
|
||||
#define TARGET_ENOTEMPTY 66 /* Directory not empty */
|
||||
|
||||
/* quotas & mush */
|
||||
#define TARGET_EPROCLIM 67 /* Too many processes */
|
||||
#define TARGET_EUSERS 68 /* Too many users */
|
||||
#define TARGET_EDQUOT 69 /* Disk quota exceeded */
|
||||
|
||||
/* Network File System */
|
||||
#define TARGET_ESTALE 70 /* Stale NFS file handle */
|
||||
#define TARGET_EREMOTE 71 /* Too many levels of remote in path */
|
||||
#define TARGET_EBADRPC 72 /* RPC struct is bad */
|
||||
#define TARGET_ERPCMISMATCH 73 /* RPC version wrong */
|
||||
#define TARGET_EPROGUNAVAIL 74 /* RPC prog. not avail */
|
||||
#define TARGET_EPROGMISMATCH 75 /* Program version wrong */
|
||||
#define TARGET_EPROCUNAVAIL 76 /* Bad procedure for program */
|
||||
|
||||
#define TARGET_ENOLCK 77 /* No locks available */
|
||||
#define TARGET_ENOSYS 78 /* Function not implemented */
|
||||
|
||||
#define TARGET_EFTYPE 79 /* Inappropriate file type or format */
|
||||
#define TARGET_EAUTH 80 /* Authentication error */
|
||||
#define TARGET_ENEEDAUTH 81 /* Need authenticator */
|
||||
#define TARGET_EIPSEC 82 /* IPsec processing failure */
|
||||
#define TARGET_ENOATTR 83 /* Attribute not found */
|
||||
#define TARGET_EILSEQ 84 /* Illegal byte sequence */
|
||||
#define TARGET_ENOMEDIUM 85 /* No medium found */
|
||||
#define TARGET_EMEDIUMTYPE 86 /* Wrong Medium Type */
|
||||
#define TARGET_EOVERFLOW 87 /* Conversion overflow */
|
||||
#define TARGET_ECANCELED 88 /* Operation canceled */
|
||||
#define TARGET_EIDRM 89 /* Identifier removed */
|
||||
#define TARGET_ENOMSG 90 /* No message of desired type */
|
||||
#define TARGET_ELAST 90 /* Must be equal largest errno */
|
||||
@@ -1,171 +0,0 @@
|
||||
{ TARGET_FREEBSD_NR___getcwd, "__getcwd", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR___semctl, "__semctl", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR___syscall, "__syscall", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR___sysctl, "__sysctl", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_accept, "accept", "%s(%d,%#x,%#x)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_access, "access", "%s(\"%s\",%#o)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_acct, "acct", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_adjtime, "adjtime", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_bind, "bind", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_break, "break", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_chdir, "chdir", "%s(\"%s\")", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_chflags, "chflags", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_chmod, "chmod", "%s(\"%s\",%#o)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_chown, "chown", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_chroot, "chroot", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_clock_getres, "clock_getres", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_clock_gettime, "clock_gettime", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_clock_settime, "clock_settime", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_close, "close", "%s(%d)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_connect, "connect", "%s(%d,%#x,%d)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_dup, "dup", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_dup2, "dup2", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_execve, "execve", NULL, print_execve, NULL },
|
||||
{ TARGET_FREEBSD_NR_exit, "exit", "%s(%d)\n", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_fchdir, "fchdir", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_fchflags, "fchflags", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_fchmod, "fchmod", "%s(%d,%#o)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_fchown, "fchown", "%s(\"%s\",%d,%d)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_fcntl, "fcntl", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_fhopen, "fhopen", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_fhstat, "fhstat", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_fhstatfs, "fhstatfs", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_flock, "flock", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_fork, "fork", "%s()", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_fpathconf, "fpathconf", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_fstat, "fstat", "%s(%d,%p)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_fstatfs, "fstatfs", "%s(%d,%p)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_fsync, "fsync", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_ftruncate, "ftruncate", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_futimes, "futimes", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_getdirentries, "getdirentries", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_freebsd6_mmap, "freebsd6_mmap", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_getegid, "getegid", "%s()", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_geteuid, "geteuid", "%s()", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_getfh, "getfh", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_getfsstat, "getfsstat", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_getgid, "getgid", "%s()", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_getgroups, "getgroups", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_getitimer, "getitimer", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_getlogin, "getlogin", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_getpeername, "getpeername", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_getpgid, "getpgid", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_getpgrp, "getpgrp", "%s()", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_getpid, "getpid", "%s()", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_getppid, "getppid", "%s()", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_getpriority, "getpriority", "%s(%#x,%#x)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_getresgid, "getresgid", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_getresuid, "getresuid", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_getrlimit, "getrlimit", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_getrusage, "getrusage", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_getsid, "getsid", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_getsockname, "getsockname", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_getsockopt, "getsockopt", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_gettimeofday, "gettimeofday", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_getuid, "getuid", "%s()", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_ioctl, "ioctl", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_issetugid, "issetugid", "%s()", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_kevent, "kevent", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_kill, "kill", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_kqueue, "kqueue", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_ktrace, "ktrace", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_lchown, "lchown", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_link, "link", "%s(\"%s\",\"%s\")", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_listen, "listen", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_lseek, "lseek", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_lstat, "lstat", "%s(\"%s\",%p)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_madvise, "madvise", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_mincore, "mincore", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_minherit, "minherit", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_mkdir, "mkdir", "%s(\"%s\",%#o)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_mkfifo, "mkfifo", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_mknod, "mknod", "%s(\"%s\",%#o,%#x)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_mlock, "mlock", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_mlockall, "mlockall", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_mmap, "mmap", NULL, NULL, print_syscall_ret_addr },
|
||||
{ TARGET_FREEBSD_NR_mount, "mount", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_mprotect, "mprotect", "%s(%#x,%#x,%d)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_msgctl, "msgctl", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_msgget, "msgget", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_msgrcv, "msgrcv", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_msgsnd, "msgsnd", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_msync, "msync", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_munlock, "munlock", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_munlockall, "munlockall", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_munmap, "munmap", "%s(%p,%d)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_nanosleep, "nanosleep", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_nfssvc, "nfssvc", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_open, "open", "%s(\"%s\",%#x,%#o)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_pathconf, "pathconf", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_pipe, "pipe", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_poll, "poll", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_pread, "pread", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_preadv, "preadv", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_profil, "profil", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_ptrace, "ptrace", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_pwrite, "pwrite", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_pwritev, "pwritev", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_quotactl, "quotactl", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_read, "read", "%s(%d,%#x,%d)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_readlink, "readlink", "%s(\"%s\",%p,%d)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_readv, "readv", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_reboot, "reboot", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_recvfrom, "recvfrom", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_recvmsg, "recvmsg", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_rename, "rename", "%s(\"%s\",\"%s\")", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_revoke, "revoke", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_rfork, "rfork", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_rmdir, "rmdir", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_sbrk, "sbrk", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_sched_yield, "sched_yield", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_select, "select", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_semget, "semget", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_semop, "semop", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_sendmsg, "sendmsg", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_sendto, "sendto", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_setegid, "setegid", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_seteuid, "seteuid", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_setgid, "setgid", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_setgroups, "setgroups", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_setitimer, "setitimer", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_setlogin, "setlogin", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_setpgid, "setpgid", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_setpriority, "setpriority", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_setregid, "setregid", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_setresgid, "setresgid", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_setresuid, "setresuid", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_setreuid, "setreuid", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_setrlimit, "setrlimit", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_setsid, "setsid", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_setsockopt, "setsockopt", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_settimeofday, "settimeofday", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_setuid, "setuid", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_shmat, "shmat", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_shmctl, "shmctl", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_shmdt, "shmdt", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_shmget, "shmget", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_shutdown, "shutdown", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_sigaction, "sigaction", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_sigaltstack, "sigaltstack", "%s(%p,%p)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_sigpending, "sigpending", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_sigprocmask, "sigprocmask", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_sigreturn, "sigreturn", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_sigsuspend, "sigsuspend", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_socket, "socket", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_socketpair, "socketpair", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_sstk, "sstk", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_stat, "stat", "%s(\"%s\",%p)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_statfs, "statfs", "%s(\"%s\",%p)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_symlink, "symlink", "%s(\"%s\",\"%s\")", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_sync, "sync", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_sysarch, "sysarch", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_syscall, "syscall", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_truncate, "truncate", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_umask, "umask", "%s(%#o)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_unlink, "unlink", "%s(\"%s\")", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_unmount, "unmount", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_utimes, "utimes", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_vfork, "vfork", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_wait4, "wait4", NULL, NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_write, "write", "%s(%d,%#x,%d)", NULL, NULL },
|
||||
{ TARGET_FREEBSD_NR_writev, "writev", "%s(%d,%p,%#x)", NULL, NULL },
|
||||
@@ -1,373 +0,0 @@
|
||||
/*
|
||||
* System call numbers.
|
||||
*
|
||||
* $FreeBSD: src/sys/sys/syscall.h,v 1.224 2008/08/24 21:23:08 rwatson Exp $
|
||||
* created from FreeBSD: head/sys/kern/syscalls.master 182123 2008-08-24 21:20:35Z rwatson
|
||||
*/
|
||||
|
||||
#define TARGET_FREEBSD_NR_syscall 0
|
||||
#define TARGET_FREEBSD_NR_exit 1
|
||||
#define TARGET_FREEBSD_NR_fork 2
|
||||
#define TARGET_FREEBSD_NR_read 3
|
||||
#define TARGET_FREEBSD_NR_write 4
|
||||
#define TARGET_FREEBSD_NR_open 5
|
||||
#define TARGET_FREEBSD_NR_close 6
|
||||
#define TARGET_FREEBSD_NR_wait4 7
|
||||
#define TARGET_FREEBSD_NR_link 9
|
||||
#define TARGET_FREEBSD_NR_unlink 10
|
||||
#define TARGET_FREEBSD_NR_chdir 12
|
||||
#define TARGET_FREEBSD_NR_fchdir 13
|
||||
#define TARGET_FREEBSD_NR_mknod 14
|
||||
#define TARGET_FREEBSD_NR_chmod 15
|
||||
#define TARGET_FREEBSD_NR_chown 16
|
||||
#define TARGET_FREEBSD_NR_break 17
|
||||
#define TARGET_FREEBSD_NR_freebsd4_getfsstat 18
|
||||
#define TARGET_FREEBSD_NR_getpid 20
|
||||
#define TARGET_FREEBSD_NR_mount 21
|
||||
#define TARGET_FREEBSD_NR_unmount 22
|
||||
#define TARGET_FREEBSD_NR_setuid 23
|
||||
#define TARGET_FREEBSD_NR_getuid 24
|
||||
#define TARGET_FREEBSD_NR_geteuid 25
|
||||
#define TARGET_FREEBSD_NR_ptrace 26
|
||||
#define TARGET_FREEBSD_NR_recvmsg 27
|
||||
#define TARGET_FREEBSD_NR_sendmsg 28
|
||||
#define TARGET_FREEBSD_NR_recvfrom 29
|
||||
#define TARGET_FREEBSD_NR_accept 30
|
||||
#define TARGET_FREEBSD_NR_getpeername 31
|
||||
#define TARGET_FREEBSD_NR_getsockname 32
|
||||
#define TARGET_FREEBSD_NR_access 33
|
||||
#define TARGET_FREEBSD_NR_chflags 34
|
||||
#define TARGET_FREEBSD_NR_fchflags 35
|
||||
#define TARGET_FREEBSD_NR_sync 36
|
||||
#define TARGET_FREEBSD_NR_kill 37
|
||||
#define TARGET_FREEBSD_NR_getppid 39
|
||||
#define TARGET_FREEBSD_NR_dup 41
|
||||
#define TARGET_FREEBSD_NR_pipe 42
|
||||
#define TARGET_FREEBSD_NR_getegid 43
|
||||
#define TARGET_FREEBSD_NR_profil 44
|
||||
#define TARGET_FREEBSD_NR_ktrace 45
|
||||
#define TARGET_FREEBSD_NR_getgid 47
|
||||
#define TARGET_FREEBSD_NR_getlogin 49
|
||||
#define TARGET_FREEBSD_NR_setlogin 50
|
||||
#define TARGET_FREEBSD_NR_acct 51
|
||||
#define TARGET_FREEBSD_NR_sigaltstack 53
|
||||
#define TARGET_FREEBSD_NR_ioctl 54
|
||||
#define TARGET_FREEBSD_NR_reboot 55
|
||||
#define TARGET_FREEBSD_NR_revoke 56
|
||||
#define TARGET_FREEBSD_NR_symlink 57
|
||||
#define TARGET_FREEBSD_NR_readlink 58
|
||||
#define TARGET_FREEBSD_NR_execve 59
|
||||
#define TARGET_FREEBSD_NR_umask 60
|
||||
#define TARGET_FREEBSD_NR_chroot 61
|
||||
#define TARGET_FREEBSD_NR_msync 65
|
||||
#define TARGET_FREEBSD_NR_vfork 66
|
||||
#define TARGET_FREEBSD_NR_sbrk 69
|
||||
#define TARGET_FREEBSD_NR_sstk 70
|
||||
#define TARGET_FREEBSD_NR_vadvise 72
|
||||
#define TARGET_FREEBSD_NR_munmap 73
|
||||
#define TARGET_FREEBSD_NR_mprotect 74
|
||||
#define TARGET_FREEBSD_NR_madvise 75
|
||||
#define TARGET_FREEBSD_NR_mincore 78
|
||||
#define TARGET_FREEBSD_NR_getgroups 79
|
||||
#define TARGET_FREEBSD_NR_setgroups 80
|
||||
#define TARGET_FREEBSD_NR_getpgrp 81
|
||||
#define TARGET_FREEBSD_NR_setpgid 82
|
||||
#define TARGET_FREEBSD_NR_setitimer 83
|
||||
#define TARGET_FREEBSD_NR_swapon 85
|
||||
#define TARGET_FREEBSD_NR_getitimer 86
|
||||
#define TARGET_FREEBSD_NR_getdtablesize 89
|
||||
#define TARGET_FREEBSD_NR_dup2 90
|
||||
#define TARGET_FREEBSD_NR_fcntl 92
|
||||
#define TARGET_FREEBSD_NR_select 93
|
||||
#define TARGET_FREEBSD_NR_fsync 95
|
||||
#define TARGET_FREEBSD_NR_setpriority 96
|
||||
#define TARGET_FREEBSD_NR_socket 97
|
||||
#define TARGET_FREEBSD_NR_connect 98
|
||||
#define TARGET_FREEBSD_NR_getpriority 100
|
||||
#define TARGET_FREEBSD_NR_bind 104
|
||||
#define TARGET_FREEBSD_NR_setsockopt 105
|
||||
#define TARGET_FREEBSD_NR_listen 106
|
||||
#define TARGET_FREEBSD_NR_gettimeofday 116
|
||||
#define TARGET_FREEBSD_NR_getrusage 117
|
||||
#define TARGET_FREEBSD_NR_getsockopt 118
|
||||
#define TARGET_FREEBSD_NR_readv 120
|
||||
#define TARGET_FREEBSD_NR_writev 121
|
||||
#define TARGET_FREEBSD_NR_settimeofday 122
|
||||
#define TARGET_FREEBSD_NR_fchown 123
|
||||
#define TARGET_FREEBSD_NR_fchmod 124
|
||||
#define TARGET_FREEBSD_NR_setreuid 126
|
||||
#define TARGET_FREEBSD_NR_setregid 127
|
||||
#define TARGET_FREEBSD_NR_rename 128
|
||||
#define TARGET_FREEBSD_NR_flock 131
|
||||
#define TARGET_FREEBSD_NR_mkfifo 132
|
||||
#define TARGET_FREEBSD_NR_sendto 133
|
||||
#define TARGET_FREEBSD_NR_shutdown 134
|
||||
#define TARGET_FREEBSD_NR_socketpair 135
|
||||
#define TARGET_FREEBSD_NR_mkdir 136
|
||||
#define TARGET_FREEBSD_NR_rmdir 137
|
||||
#define TARGET_FREEBSD_NR_utimes 138
|
||||
#define TARGET_FREEBSD_NR_adjtime 140
|
||||
#define TARGET_FREEBSD_NR_setsid 147
|
||||
#define TARGET_FREEBSD_NR_quotactl 148
|
||||
#define TARGET_FREEBSD_NR_nlm_syscall 154
|
||||
#define TARGET_FREEBSD_NR_nfssvc 155
|
||||
#define TARGET_FREEBSD_NR_freebsd4_statfs 157
|
||||
#define TARGET_FREEBSD_NR_freebsd4_fstatfs 158
|
||||
#define TARGET_FREEBSD_NR_lgetfh 160
|
||||
#define TARGET_FREEBSD_NR_getfh 161
|
||||
#define TARGET_FREEBSD_NR_getdomainname 162
|
||||
#define TARGET_FREEBSD_NR_setdomainname 163
|
||||
#define TARGET_FREEBSD_NR_uname 164
|
||||
#define TARGET_FREEBSD_NR_sysarch 165
|
||||
#define TARGET_FREEBSD_NR_rtprio 166
|
||||
#define TARGET_FREEBSD_NR_semsys 169
|
||||
#define TARGET_FREEBSD_NR_msgsys 170
|
||||
#define TARGET_FREEBSD_NR_shmsys 171
|
||||
#define TARGET_FREEBSD_NR_freebsd6_pread 173
|
||||
#define TARGET_FREEBSD_NR_freebsd6_pwrite 174
|
||||
#define TARGET_FREEBSD_NR_setfib 175
|
||||
#define TARGET_FREEBSD_NR_ntp_adjtime 176
|
||||
#define TARGET_FREEBSD_NR_setgid 181
|
||||
#define TARGET_FREEBSD_NR_setegid 182
|
||||
#define TARGET_FREEBSD_NR_seteuid 183
|
||||
#define TARGET_FREEBSD_NR_stat 188
|
||||
#define TARGET_FREEBSD_NR_fstat 189
|
||||
#define TARGET_FREEBSD_NR_lstat 190
|
||||
#define TARGET_FREEBSD_NR_pathconf 191
|
||||
#define TARGET_FREEBSD_NR_fpathconf 192
|
||||
#define TARGET_FREEBSD_NR_getrlimit 194
|
||||
#define TARGET_FREEBSD_NR_setrlimit 195
|
||||
#define TARGET_FREEBSD_NR_getdirentries 196
|
||||
#define TARGET_FREEBSD_NR_freebsd6_mmap 197
|
||||
#define TARGET_FREEBSD_NR___syscall 198
|
||||
#define TARGET_FREEBSD_NR_freebsd6_lseek 199
|
||||
#define TARGET_FREEBSD_NR_freebsd6_truncate 200
|
||||
#define TARGET_FREEBSD_NR_freebsd6_ftruncate 201
|
||||
#define TARGET_FREEBSD_NR___sysctl 202
|
||||
#define TARGET_FREEBSD_NR_mlock 203
|
||||
#define TARGET_FREEBSD_NR_munlock 204
|
||||
#define TARGET_FREEBSD_NR_undelete 205
|
||||
#define TARGET_FREEBSD_NR_futimes 206
|
||||
#define TARGET_FREEBSD_NR_getpgid 207
|
||||
#define TARGET_FREEBSD_NR_poll 209
|
||||
#define TARGET_FREEBSD_NR___semctl 220
|
||||
#define TARGET_FREEBSD_NR_semget 221
|
||||
#define TARGET_FREEBSD_NR_semop 222
|
||||
#define TARGET_FREEBSD_NR_msgctl 224
|
||||
#define TARGET_FREEBSD_NR_msgget 225
|
||||
#define TARGET_FREEBSD_NR_msgsnd 226
|
||||
#define TARGET_FREEBSD_NR_msgrcv 227
|
||||
#define TARGET_FREEBSD_NR_shmat 228
|
||||
#define TARGET_FREEBSD_NR_shmctl 229
|
||||
#define TARGET_FREEBSD_NR_shmdt 230
|
||||
#define TARGET_FREEBSD_NR_shmget 231
|
||||
#define TARGET_FREEBSD_NR_clock_gettime 232
|
||||
#define TARGET_FREEBSD_NR_clock_settime 233
|
||||
#define TARGET_FREEBSD_NR_clock_getres 234
|
||||
#define TARGET_FREEBSD_NR_ktimer_create 235
|
||||
#define TARGET_FREEBSD_NR_ktimer_delete 236
|
||||
#define TARGET_FREEBSD_NR_ktimer_settime 237
|
||||
#define TARGET_FREEBSD_NR_ktimer_gettime 238
|
||||
#define TARGET_FREEBSD_NR_ktimer_getoverrun 239
|
||||
#define TARGET_FREEBSD_NR_nanosleep 240
|
||||
#define TARGET_FREEBSD_NR_ntp_gettime 248
|
||||
#define TARGET_FREEBSD_NR_minherit 250
|
||||
#define TARGET_FREEBSD_NR_rfork 251
|
||||
#define TARGET_FREEBSD_NR_openbsd_poll 252
|
||||
#define TARGET_FREEBSD_NR_issetugid 253
|
||||
#define TARGET_FREEBSD_NR_lchown 254
|
||||
#define TARGET_FREEBSD_NR_aio_read 255
|
||||
#define TARGET_FREEBSD_NR_aio_write 256
|
||||
#define TARGET_FREEBSD_NR_lio_listio 257
|
||||
#define TARGET_FREEBSD_NR_getdents 272
|
||||
#define TARGET_FREEBSD_NR_lchmod 274
|
||||
#define TARGET_FREEBSD_NR_netbsd_lchown 275
|
||||
#define TARGET_FREEBSD_NR_lutimes 276
|
||||
#define TARGET_FREEBSD_NR_netbsd_msync 277
|
||||
#define TARGET_FREEBSD_NR_nstat 278
|
||||
#define TARGET_FREEBSD_NR_nfstat 279
|
||||
#define TARGET_FREEBSD_NR_nlstat 280
|
||||
#define TARGET_FREEBSD_NR_preadv 289
|
||||
#define TARGET_FREEBSD_NR_pwritev 290
|
||||
#define TARGET_FREEBSD_NR_freebsd4_fhstatfs 297
|
||||
#define TARGET_FREEBSD_NR_fhopen 298
|
||||
#define TARGET_FREEBSD_NR_fhstat 299
|
||||
#define TARGET_FREEBSD_NR_modnext 300
|
||||
#define TARGET_FREEBSD_NR_modstat 301
|
||||
#define TARGET_FREEBSD_NR_modfnext 302
|
||||
#define TARGET_FREEBSD_NR_modfind 303
|
||||
#define TARGET_FREEBSD_NR_kldload 304
|
||||
#define TARGET_FREEBSD_NR_kldunload 305
|
||||
#define TARGET_FREEBSD_NR_kldfind 306
|
||||
#define TARGET_FREEBSD_NR_kldnext 307
|
||||
#define TARGET_FREEBSD_NR_kldstat 308
|
||||
#define TARGET_FREEBSD_NR_kldfirstmod 309
|
||||
#define TARGET_FREEBSD_NR_getsid 310
|
||||
#define TARGET_FREEBSD_NR_setresuid 311
|
||||
#define TARGET_FREEBSD_NR_setresgid 312
|
||||
#define TARGET_FREEBSD_NR_aio_return 314
|
||||
#define TARGET_FREEBSD_NR_aio_suspend 315
|
||||
#define TARGET_FREEBSD_NR_aio_cancel 316
|
||||
#define TARGET_FREEBSD_NR_aio_error 317
|
||||
#define TARGET_FREEBSD_NR_oaio_read 318
|
||||
#define TARGET_FREEBSD_NR_oaio_write 319
|
||||
#define TARGET_FREEBSD_NR_olio_listio 320
|
||||
#define TARGET_FREEBSD_NR_yield 321
|
||||
#define TARGET_FREEBSD_NR_mlockall 324
|
||||
#define TARGET_FREEBSD_NR_munlockall 325
|
||||
#define TARGET_FREEBSD_NR___getcwd 326
|
||||
#define TARGET_FREEBSD_NR_sched_setparam 327
|
||||
#define TARGET_FREEBSD_NR_sched_getparam 328
|
||||
#define TARGET_FREEBSD_NR_sched_setscheduler 329
|
||||
#define TARGET_FREEBSD_NR_sched_getscheduler 330
|
||||
#define TARGET_FREEBSD_NR_sched_yield 331
|
||||
#define TARGET_FREEBSD_NR_sched_get_priority_max 332
|
||||
#define TARGET_FREEBSD_NR_sched_get_priority_min 333
|
||||
#define TARGET_FREEBSD_NR_sched_rr_get_interval 334
|
||||
#define TARGET_FREEBSD_NR_utrace 335
|
||||
#define TARGET_FREEBSD_NR_freebsd4_sendfile 336
|
||||
#define TARGET_FREEBSD_NR_kldsym 337
|
||||
#define TARGET_FREEBSD_NR_jail 338
|
||||
#define TARGET_FREEBSD_NR_sigprocmask 340
|
||||
#define TARGET_FREEBSD_NR_sigsuspend 341
|
||||
#define TARGET_FREEBSD_NR_freebsd4_sigaction 342
|
||||
#define TARGET_FREEBSD_NR_sigpending 343
|
||||
#define TARGET_FREEBSD_NR_freebsd4_sigreturn 344
|
||||
#define TARGET_FREEBSD_NR_sigtimedwait 345
|
||||
#define TARGET_FREEBSD_NR_sigwaitinfo 346
|
||||
#define TARGET_FREEBSD_NR___acl_get_file 347
|
||||
#define TARGET_FREEBSD_NR___acl_set_file 348
|
||||
#define TARGET_FREEBSD_NR___acl_get_fd 349
|
||||
#define TARGET_FREEBSD_NR___acl_set_fd 350
|
||||
#define TARGET_FREEBSD_NR___acl_delete_file 351
|
||||
#define TARGET_FREEBSD_NR___acl_delete_fd 352
|
||||
#define TARGET_FREEBSD_NR___acl_aclcheck_file 353
|
||||
#define TARGET_FREEBSD_NR___acl_aclcheck_fd 354
|
||||
#define TARGET_FREEBSD_NR_extattrctl 355
|
||||
#define TARGET_FREEBSD_NR_extattr_set_file 356
|
||||
#define TARGET_FREEBSD_NR_extattr_get_file 357
|
||||
#define TARGET_FREEBSD_NR_extattr_delete_file 358
|
||||
#define TARGET_FREEBSD_NR_aio_waitcomplete 359
|
||||
#define TARGET_FREEBSD_NR_getresuid 360
|
||||
#define TARGET_FREEBSD_NR_getresgid 361
|
||||
#define TARGET_FREEBSD_NR_kqueue 362
|
||||
#define TARGET_FREEBSD_NR_kevent 363
|
||||
#define TARGET_FREEBSD_NR_extattr_set_fd 371
|
||||
#define TARGET_FREEBSD_NR_extattr_get_fd 372
|
||||
#define TARGET_FREEBSD_NR_extattr_delete_fd 373
|
||||
#define TARGET_FREEBSD_NR___setugid 374
|
||||
#define TARGET_FREEBSD_NR_nfsclnt 375
|
||||
#define TARGET_FREEBSD_NR_eaccess 376
|
||||
#define TARGET_FREEBSD_NR_nmount 378
|
||||
#define TARGET_FREEBSD_NR___mac_get_proc 384
|
||||
#define TARGET_FREEBSD_NR___mac_set_proc 385
|
||||
#define TARGET_FREEBSD_NR___mac_get_fd 386
|
||||
#define TARGET_FREEBSD_NR___mac_get_file 387
|
||||
#define TARGET_FREEBSD_NR___mac_set_fd 388
|
||||
#define TARGET_FREEBSD_NR___mac_set_file 389
|
||||
#define TARGET_FREEBSD_NR_kenv 390
|
||||
#define TARGET_FREEBSD_NR_lchflags 391
|
||||
#define TARGET_FREEBSD_NR_uuidgen 392
|
||||
#define TARGET_FREEBSD_NR_sendfile 393
|
||||
#define TARGET_FREEBSD_NR_mac_syscall 394
|
||||
#define TARGET_FREEBSD_NR_getfsstat 395
|
||||
#define TARGET_FREEBSD_NR_statfs 396
|
||||
#define TARGET_FREEBSD_NR_fstatfs 397
|
||||
#define TARGET_FREEBSD_NR_fhstatfs 398
|
||||
#define TARGET_FREEBSD_NR_ksem_close 400
|
||||
#define TARGET_FREEBSD_NR_ksem_post 401
|
||||
#define TARGET_FREEBSD_NR_ksem_wait 402
|
||||
#define TARGET_FREEBSD_NR_ksem_trywait 403
|
||||
#define TARGET_FREEBSD_NR_ksem_init 404
|
||||
#define TARGET_FREEBSD_NR_ksem_open 405
|
||||
#define TARGET_FREEBSD_NR_ksem_unlink 406
|
||||
#define TARGET_FREEBSD_NR_ksem_getvalue 407
|
||||
#define TARGET_FREEBSD_NR_ksem_destroy 408
|
||||
#define TARGET_FREEBSD_NR___mac_get_pid 409
|
||||
#define TARGET_FREEBSD_NR___mac_get_link 410
|
||||
#define TARGET_FREEBSD_NR___mac_set_link 411
|
||||
#define TARGET_FREEBSD_NR_extattr_set_link 412
|
||||
#define TARGET_FREEBSD_NR_extattr_get_link 413
|
||||
#define TARGET_FREEBSD_NR_extattr_delete_link 414
|
||||
#define TARGET_FREEBSD_NR___mac_execve 415
|
||||
#define TARGET_FREEBSD_NR_sigaction 416
|
||||
#define TARGET_FREEBSD_NR_sigreturn 417
|
||||
#define TARGET_FREEBSD_NR_getcontext 421
|
||||
#define TARGET_FREEBSD_NR_setcontext 422
|
||||
#define TARGET_FREEBSD_NR_swapcontext 423
|
||||
#define TARGET_FREEBSD_NR_swapoff 424
|
||||
#define TARGET_FREEBSD_NR___acl_get_link 425
|
||||
#define TARGET_FREEBSD_NR___acl_set_link 426
|
||||
#define TARGET_FREEBSD_NR___acl_delete_link 427
|
||||
#define TARGET_FREEBSD_NR___acl_aclcheck_link 428
|
||||
#define TARGET_FREEBSD_NR_sigwait 429
|
||||
#define TARGET_FREEBSD_NR_thr_create 430
|
||||
#define TARGET_FREEBSD_NR_thr_exit 431
|
||||
#define TARGET_FREEBSD_NR_thr_self 432
|
||||
#define TARGET_FREEBSD_NR_thr_kill 433
|
||||
#define TARGET_FREEBSD_NR__umtx_lock 434
|
||||
#define TARGET_FREEBSD_NR__umtx_unlock 435
|
||||
#define TARGET_FREEBSD_NR_jail_attach 436
|
||||
#define TARGET_FREEBSD_NR_extattr_list_fd 437
|
||||
#define TARGET_FREEBSD_NR_extattr_list_file 438
|
||||
#define TARGET_FREEBSD_NR_extattr_list_link 439
|
||||
#define TARGET_FREEBSD_NR_ksem_timedwait 441
|
||||
#define TARGET_FREEBSD_NR_thr_suspend 442
|
||||
#define TARGET_FREEBSD_NR_thr_wake 443
|
||||
#define TARGET_FREEBSD_NR_kldunloadf 444
|
||||
#define TARGET_FREEBSD_NR_audit 445
|
||||
#define TARGET_FREEBSD_NR_auditon 446
|
||||
#define TARGET_FREEBSD_NR_getauid 447
|
||||
#define TARGET_FREEBSD_NR_setauid 448
|
||||
#define TARGET_FREEBSD_NR_getaudit 449
|
||||
#define TARGET_FREEBSD_NR_setaudit 450
|
||||
#define TARGET_FREEBSD_NR_getaudit_addr 451
|
||||
#define TARGET_FREEBSD_NR_setaudit_addr 452
|
||||
#define TARGET_FREEBSD_NR_auditctl 453
|
||||
#define TARGET_FREEBSD_NR__umtx_op 454
|
||||
#define TARGET_FREEBSD_NR_thr_new 455
|
||||
#define TARGET_FREEBSD_NR_sigqueue 456
|
||||
#define TARGET_FREEBSD_NR_kmq_open 457
|
||||
#define TARGET_FREEBSD_NR_kmq_setattr 458
|
||||
#define TARGET_FREEBSD_NR_kmq_timedreceive 459
|
||||
#define TARGET_FREEBSD_NR_kmq_timedsend 460
|
||||
#define TARGET_FREEBSD_NR_kmq_notify 461
|
||||
#define TARGET_FREEBSD_NR_kmq_unlink 462
|
||||
#define TARGET_FREEBSD_NR_abort2 463
|
||||
#define TARGET_FREEBSD_NR_thr_set_name 464
|
||||
#define TARGET_FREEBSD_NR_aio_fsync 465
|
||||
#define TARGET_FREEBSD_NR_rtprio_thread 466
|
||||
#define TARGET_FREEBSD_NR_sctp_peeloff 471
|
||||
#define TARGET_FREEBSD_NR_sctp_generic_sendmsg 472
|
||||
#define TARGET_FREEBSD_NR_sctp_generic_sendmsg_iov 473
|
||||
#define TARGET_FREEBSD_NR_sctp_generic_recvmsg 474
|
||||
#define TARGET_FREEBSD_NR_pread 475
|
||||
#define TARGET_FREEBSD_NR_pwrite 476
|
||||
#define TARGET_FREEBSD_NR_mmap 477
|
||||
#define TARGET_FREEBSD_NR_lseek 478
|
||||
#define TARGET_FREEBSD_NR_truncate 479
|
||||
#define TARGET_FREEBSD_NR_ftruncate 480
|
||||
#define TARGET_FREEBSD_NR_thr_kill2 481
|
||||
#define TARGET_FREEBSD_NR_shm_open 482
|
||||
#define TARGET_FREEBSD_NR_shm_unlink 483
|
||||
#define TARGET_FREEBSD_NR_cpuset 484
|
||||
#define TARGET_FREEBSD_NR_cpuset_setid 485
|
||||
#define TARGET_FREEBSD_NR_cpuset_getid 486
|
||||
#define TARGET_FREEBSD_NR_cpuset_getaffinity 487
|
||||
#define TARGET_FREEBSD_NR_cpuset_setaffinity 488
|
||||
#define TARGET_FREEBSD_NR_faccessat 489
|
||||
#define TARGET_FREEBSD_NR_fchmodat 490
|
||||
#define TARGET_FREEBSD_NR_fchownat 491
|
||||
#define TARGET_FREEBSD_NR_fexecve 492
|
||||
#define TARGET_FREEBSD_NR_fstatat 493
|
||||
#define TARGET_FREEBSD_NR_futimesat 494
|
||||
#define TARGET_FREEBSD_NR_linkat 495
|
||||
#define TARGET_FREEBSD_NR_mkdirat 496
|
||||
#define TARGET_FREEBSD_NR_mkfifoat 497
|
||||
#define TARGET_FREEBSD_NR_mknodat 498
|
||||
#define TARGET_FREEBSD_NR_openat 499
|
||||
#define TARGET_FREEBSD_NR_readlinkat 500
|
||||
#define TARGET_FREEBSD_NR_renameat 501
|
||||
#define TARGET_FREEBSD_NR_symlinkat 502
|
||||
#define TARGET_FREEBSD_NR_unlinkat 503
|
||||
#define TARGET_FREEBSD_NR_posix_openpt 504
|
||||
@@ -1,161 +0,0 @@
|
||||
/* default linux values for the selectors */
|
||||
#define __USER_CS (0x23)
|
||||
#define __USER_DS (0x2B)
|
||||
|
||||
struct target_pt_regs {
|
||||
long ebx;
|
||||
long ecx;
|
||||
long edx;
|
||||
long esi;
|
||||
long edi;
|
||||
long ebp;
|
||||
long eax;
|
||||
int xds;
|
||||
int xes;
|
||||
long orig_eax;
|
||||
long eip;
|
||||
int xcs;
|
||||
long eflags;
|
||||
long esp;
|
||||
int xss;
|
||||
};
|
||||
|
||||
/* ioctls */
|
||||
|
||||
#define TARGET_LDT_ENTRIES 8192
|
||||
#define TARGET_LDT_ENTRY_SIZE 8
|
||||
|
||||
#define TARGET_GDT_ENTRIES 9
|
||||
#define TARGET_GDT_ENTRY_TLS_ENTRIES 3
|
||||
#define TARGET_GDT_ENTRY_TLS_MIN 6
|
||||
#define TARGET_GDT_ENTRY_TLS_MAX (TARGET_GDT_ENTRY_TLS_MIN + TARGET_GDT_ENTRY_TLS_ENTRIES - 1)
|
||||
|
||||
struct target_modify_ldt_ldt_s {
|
||||
unsigned int entry_number;
|
||||
abi_ulong base_addr;
|
||||
unsigned int limit;
|
||||
unsigned int flags;
|
||||
};
|
||||
|
||||
/* vm86 defines */
|
||||
|
||||
#define TARGET_BIOSSEG 0x0f000
|
||||
|
||||
#define TARGET_CPU_086 0
|
||||
#define TARGET_CPU_186 1
|
||||
#define TARGET_CPU_286 2
|
||||
#define TARGET_CPU_386 3
|
||||
#define TARGET_CPU_486 4
|
||||
#define TARGET_CPU_586 5
|
||||
|
||||
#define TARGET_VM86_SIGNAL 0 /* return due to signal */
|
||||
#define TARGET_VM86_UNKNOWN 1 /* unhandled GP fault - IO-instruction or similar */
|
||||
#define TARGET_VM86_INTx 2 /* int3/int x instruction (ARG = x) */
|
||||
#define TARGET_VM86_STI 3 /* sti/popf/iret instruction enabled virtual interrupts */
|
||||
|
||||
/*
|
||||
* Additional return values when invoking new vm86()
|
||||
*/
|
||||
#define TARGET_VM86_PICRETURN 4 /* return due to pending PIC request */
|
||||
#define TARGET_VM86_TRAP 6 /* return due to DOS-debugger request */
|
||||
|
||||
/*
|
||||
* function codes when invoking new vm86()
|
||||
*/
|
||||
#define TARGET_VM86_PLUS_INSTALL_CHECK 0
|
||||
#define TARGET_VM86_ENTER 1
|
||||
#define TARGET_VM86_ENTER_NO_BYPASS 2
|
||||
#define TARGET_VM86_REQUEST_IRQ 3
|
||||
#define TARGET_VM86_FREE_IRQ 4
|
||||
#define TARGET_VM86_GET_IRQ_BITS 5
|
||||
#define TARGET_VM86_GET_AND_RESET_IRQ 6
|
||||
|
||||
/*
|
||||
* This is the stack-layout seen by the user space program when we have
|
||||
* done a translation of "SAVE_ALL" from vm86 mode. The real kernel layout
|
||||
* is 'kernel_vm86_regs' (see below).
|
||||
*/
|
||||
|
||||
struct target_vm86_regs {
|
||||
/*
|
||||
* normal regs, with special meaning for the segment descriptors..
|
||||
*/
|
||||
abi_long ebx;
|
||||
abi_long ecx;
|
||||
abi_long edx;
|
||||
abi_long esi;
|
||||
abi_long edi;
|
||||
abi_long ebp;
|
||||
abi_long eax;
|
||||
abi_long __null_ds;
|
||||
abi_long __null_es;
|
||||
abi_long __null_fs;
|
||||
abi_long __null_gs;
|
||||
abi_long orig_eax;
|
||||
abi_long eip;
|
||||
unsigned short cs, __csh;
|
||||
abi_long eflags;
|
||||
abi_long esp;
|
||||
unsigned short ss, __ssh;
|
||||
/*
|
||||
* these are specific to v86 mode:
|
||||
*/
|
||||
unsigned short es, __esh;
|
||||
unsigned short ds, __dsh;
|
||||
unsigned short fs, __fsh;
|
||||
unsigned short gs, __gsh;
|
||||
};
|
||||
|
||||
struct target_revectored_struct {
|
||||
abi_ulong __map[8]; /* 256 bits */
|
||||
};
|
||||
|
||||
struct target_vm86_struct {
|
||||
struct target_vm86_regs regs;
|
||||
abi_ulong flags;
|
||||
abi_ulong screen_bitmap;
|
||||
abi_ulong cpu_type;
|
||||
struct target_revectored_struct int_revectored;
|
||||
struct target_revectored_struct int21_revectored;
|
||||
};
|
||||
|
||||
/*
|
||||
* flags masks
|
||||
*/
|
||||
#define TARGET_VM86_SCREEN_BITMAP 0x0001
|
||||
|
||||
struct target_vm86plus_info_struct {
|
||||
abi_ulong flags;
|
||||
#define TARGET_force_return_for_pic (1 << 0)
|
||||
#define TARGET_vm86dbg_active (1 << 1) /* for debugger */
|
||||
#define TARGET_vm86dbg_TFpendig (1 << 2) /* for debugger */
|
||||
#define TARGET_is_vm86pus (1 << 31) /* for vm86 internal use */
|
||||
unsigned char vm86dbg_intxxtab[32]; /* for debugger */
|
||||
};
|
||||
|
||||
struct target_vm86plus_struct {
|
||||
struct target_vm86_regs regs;
|
||||
abi_ulong flags;
|
||||
abi_ulong screen_bitmap;
|
||||
abi_ulong cpu_type;
|
||||
struct target_revectored_struct int_revectored;
|
||||
struct target_revectored_struct int21_revectored;
|
||||
struct target_vm86plus_info_struct vm86plus;
|
||||
};
|
||||
|
||||
/* FreeBSD sysarch(2) */
|
||||
#define TARGET_FREEBSD_I386_GET_LDT 0
|
||||
#define TARGET_FREEBSD_I386_SET_LDT 1
|
||||
/* I386_IOPL */
|
||||
#define TARGET_FREEBSD_I386_GET_IOPERM 3
|
||||
#define TARGET_FREEBSD_I386_SET_IOPERM 4
|
||||
/* xxxxx */
|
||||
#define TARGET_FREEBSD_I386_VM86 6
|
||||
#define TARGET_FREEBSD_I386_GET_FSBASE 7
|
||||
#define TARGET_FREEBSD_I386_SET_FSBASE 8
|
||||
#define TARGET_FREEBSD_I386_GET_GSBASE 9
|
||||
#define TARGET_FREEBSD_I386_SET_GSBASE 10
|
||||
|
||||
|
||||
#define UNAME_MACHINE "i386"
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
#ifndef TARGET_SIGNAL_H
|
||||
#define TARGET_SIGNAL_H
|
||||
|
||||
#include "cpu.h"
|
||||
|
||||
/* this struct defines a stack used during syscall handling */
|
||||
|
||||
typedef struct target_sigaltstack {
|
||||
abi_ulong ss_sp;
|
||||
abi_long ss_flags;
|
||||
abi_ulong ss_size;
|
||||
} target_stack_t;
|
||||
|
||||
|
||||
static inline abi_ulong get_sp_from_cpustate(CPUX86State *state)
|
||||
{
|
||||
return state->regs[R_ESP];
|
||||
}
|
||||
|
||||
#endif /* TARGET_SIGNAL_H */
|
||||
1114
bsd-user/main.c
1114
bsd-user/main.c
File diff suppressed because it is too large
Load Diff
560
bsd-user/mmap.c
560
bsd-user/mmap.c
@@ -1,560 +0,0 @@
|
||||
/*
|
||||
* mmap support for qemu
|
||||
*
|
||||
* Copyright (c) 2003 - 2008 Fabrice Bellard
|
||||
*
|
||||
* 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 <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include "qemu.h"
|
||||
#include "qemu-common.h"
|
||||
#include "bsd-mman.h"
|
||||
|
||||
//#define DEBUG_MMAP
|
||||
|
||||
#if defined(CONFIG_USE_NPTL)
|
||||
pthread_mutex_t mmap_mutex;
|
||||
static int __thread mmap_lock_count;
|
||||
|
||||
void mmap_lock(void)
|
||||
{
|
||||
if (mmap_lock_count++ == 0) {
|
||||
pthread_mutex_lock(&mmap_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
void mmap_unlock(void)
|
||||
{
|
||||
if (--mmap_lock_count == 0) {
|
||||
pthread_mutex_unlock(&mmap_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
/* Grab lock to make sure things are in a consistent state after fork(). */
|
||||
void mmap_fork_start(void)
|
||||
{
|
||||
if (mmap_lock_count)
|
||||
abort();
|
||||
pthread_mutex_lock(&mmap_mutex);
|
||||
}
|
||||
|
||||
void mmap_fork_end(int child)
|
||||
{
|
||||
if (child)
|
||||
pthread_mutex_init(&mmap_mutex, NULL);
|
||||
else
|
||||
pthread_mutex_unlock(&mmap_mutex);
|
||||
}
|
||||
#else
|
||||
/* We aren't threadsafe to start with, so no need to worry about locking. */
|
||||
void mmap_lock(void)
|
||||
{
|
||||
}
|
||||
|
||||
void mmap_unlock(void)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
void *qemu_vmalloc(size_t size)
|
||||
{
|
||||
void *p;
|
||||
unsigned long addr;
|
||||
mmap_lock();
|
||||
/* Use map and mark the pages as used. */
|
||||
p = mmap(NULL, size, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANON, -1, 0);
|
||||
|
||||
addr = (unsigned long)p;
|
||||
if (addr == (target_ulong) addr) {
|
||||
/* Allocated region overlaps guest address space.
|
||||
This may recurse. */
|
||||
page_set_flags(addr & TARGET_PAGE_MASK, TARGET_PAGE_ALIGN(addr + size),
|
||||
PAGE_RESERVED);
|
||||
}
|
||||
|
||||
mmap_unlock();
|
||||
return p;
|
||||
}
|
||||
|
||||
void *qemu_malloc(size_t size)
|
||||
{
|
||||
char * p;
|
||||
size += 16;
|
||||
p = qemu_vmalloc(size);
|
||||
*(size_t *)p = size;
|
||||
return p + 16;
|
||||
}
|
||||
|
||||
/* We use map, which is always zero initialized. */
|
||||
void * qemu_mallocz(size_t size)
|
||||
{
|
||||
return qemu_malloc(size);
|
||||
}
|
||||
|
||||
void qemu_free(void *ptr)
|
||||
{
|
||||
/* FIXME: We should unmark the reserved pages here. However this gets
|
||||
complicated when one target page spans multiple host pages, so we
|
||||
don't bother. */
|
||||
size_t *p;
|
||||
p = (size_t *)((char *)ptr - 16);
|
||||
munmap(p, *p);
|
||||
}
|
||||
|
||||
void *qemu_realloc(void *ptr, size_t size)
|
||||
{
|
||||
size_t old_size, copy;
|
||||
void *new_ptr;
|
||||
|
||||
if (!ptr)
|
||||
return qemu_malloc(size);
|
||||
old_size = *(size_t *)((char *)ptr - 16);
|
||||
copy = old_size < size ? old_size : size;
|
||||
new_ptr = qemu_malloc(size);
|
||||
memcpy(new_ptr, ptr, copy);
|
||||
qemu_free(ptr);
|
||||
return new_ptr;
|
||||
}
|
||||
|
||||
/* NOTE: all the constants are the HOST ones, but addresses are target. */
|
||||
int target_mprotect(abi_ulong start, abi_ulong len, int prot)
|
||||
{
|
||||
abi_ulong end, host_start, host_end, addr;
|
||||
int prot1, ret;
|
||||
|
||||
#ifdef DEBUG_MMAP
|
||||
printf("mprotect: start=0x" TARGET_FMT_lx
|
||||
" len=0x" TARGET_FMT_lx " prot=%c%c%c\n", start, len,
|
||||
prot & PROT_READ ? 'r' : '-',
|
||||
prot & PROT_WRITE ? 'w' : '-',
|
||||
prot & PROT_EXEC ? 'x' : '-');
|
||||
#endif
|
||||
|
||||
if ((start & ~TARGET_PAGE_MASK) != 0)
|
||||
return -EINVAL;
|
||||
len = TARGET_PAGE_ALIGN(len);
|
||||
end = start + len;
|
||||
if (end < start)
|
||||
return -EINVAL;
|
||||
prot &= PROT_READ | PROT_WRITE | PROT_EXEC;
|
||||
if (len == 0)
|
||||
return 0;
|
||||
|
||||
mmap_lock();
|
||||
host_start = start & qemu_host_page_mask;
|
||||
host_end = HOST_PAGE_ALIGN(end);
|
||||
if (start > host_start) {
|
||||
/* handle host page containing start */
|
||||
prot1 = prot;
|
||||
for(addr = host_start; addr < start; addr += TARGET_PAGE_SIZE) {
|
||||
prot1 |= page_get_flags(addr);
|
||||
}
|
||||
if (host_end == host_start + qemu_host_page_size) {
|
||||
for(addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) {
|
||||
prot1 |= page_get_flags(addr);
|
||||
}
|
||||
end = host_end;
|
||||
}
|
||||
ret = mprotect(g2h(host_start), qemu_host_page_size, prot1 & PAGE_BITS);
|
||||
if (ret != 0)
|
||||
goto error;
|
||||
host_start += qemu_host_page_size;
|
||||
}
|
||||
if (end < host_end) {
|
||||
prot1 = prot;
|
||||
for(addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) {
|
||||
prot1 |= page_get_flags(addr);
|
||||
}
|
||||
ret = mprotect(g2h(host_end - qemu_host_page_size), qemu_host_page_size,
|
||||
prot1 & PAGE_BITS);
|
||||
if (ret != 0)
|
||||
goto error;
|
||||
host_end -= qemu_host_page_size;
|
||||
}
|
||||
|
||||
/* handle the pages in the middle */
|
||||
if (host_start < host_end) {
|
||||
ret = mprotect(g2h(host_start), host_end - host_start, prot);
|
||||
if (ret != 0)
|
||||
goto error;
|
||||
}
|
||||
page_set_flags(start, start + len, prot | PAGE_VALID);
|
||||
mmap_unlock();
|
||||
return 0;
|
||||
error:
|
||||
mmap_unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* map an incomplete host page */
|
||||
static int mmap_frag(abi_ulong real_start,
|
||||
abi_ulong start, abi_ulong end,
|
||||
int prot, int flags, int fd, abi_ulong offset)
|
||||
{
|
||||
abi_ulong real_end, addr;
|
||||
void *host_start;
|
||||
int prot1, prot_new;
|
||||
|
||||
real_end = real_start + qemu_host_page_size;
|
||||
host_start = g2h(real_start);
|
||||
|
||||
/* get the protection of the target pages outside the mapping */
|
||||
prot1 = 0;
|
||||
for(addr = real_start; addr < real_end; addr++) {
|
||||
if (addr < start || addr >= end)
|
||||
prot1 |= page_get_flags(addr);
|
||||
}
|
||||
|
||||
if (prot1 == 0) {
|
||||
/* no page was there, so we allocate one */
|
||||
void *p = mmap(host_start, qemu_host_page_size, prot,
|
||||
flags | MAP_ANON, -1, 0);
|
||||
if (p == MAP_FAILED)
|
||||
return -1;
|
||||
prot1 = prot;
|
||||
}
|
||||
prot1 &= PAGE_BITS;
|
||||
|
||||
prot_new = prot | prot1;
|
||||
if (!(flags & MAP_ANON)) {
|
||||
/* msync() won't work here, so we return an error if write is
|
||||
possible while it is a shared mapping */
|
||||
if ((flags & TARGET_BSD_MAP_FLAGMASK) == MAP_SHARED &&
|
||||
(prot & PROT_WRITE))
|
||||
return -EINVAL;
|
||||
|
||||
/* adjust protection to be able to read */
|
||||
if (!(prot1 & PROT_WRITE))
|
||||
mprotect(host_start, qemu_host_page_size, prot1 | PROT_WRITE);
|
||||
|
||||
/* read the corresponding file data */
|
||||
pread(fd, g2h(start), end - start, offset);
|
||||
|
||||
/* put final protection */
|
||||
if (prot_new != (prot1 | PROT_WRITE))
|
||||
mprotect(host_start, qemu_host_page_size, prot_new);
|
||||
} else {
|
||||
/* just update the protection */
|
||||
if (prot_new != prot1) {
|
||||
mprotect(host_start, qemu_host_page_size, prot_new);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(__CYGWIN__)
|
||||
/* Cygwin doesn't have a whole lot of address space. */
|
||||
static abi_ulong mmap_next_start = 0x18000000;
|
||||
#else
|
||||
static abi_ulong mmap_next_start = 0x40000000;
|
||||
#endif
|
||||
|
||||
unsigned long last_brk;
|
||||
|
||||
/* find a free memory area of size 'size'. The search starts at
|
||||
'start'. If 'start' == 0, then a default start address is used.
|
||||
Return -1 if error.
|
||||
*/
|
||||
/* page_init() marks pages used by the host as reserved to be sure not
|
||||
to use them. */
|
||||
static abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size)
|
||||
{
|
||||
abi_ulong addr, addr1, addr_start;
|
||||
int prot;
|
||||
unsigned long new_brk;
|
||||
|
||||
new_brk = (unsigned long)sbrk(0);
|
||||
if (last_brk && last_brk < new_brk && last_brk == (target_ulong)last_brk) {
|
||||
/* This is a hack to catch the host allocating memory with brk().
|
||||
If it uses mmap then we loose.
|
||||
FIXME: We really want to avoid the host allocating memory in
|
||||
the first place, and maybe leave some slack to avoid switching
|
||||
to mmap. */
|
||||
page_set_flags(last_brk & TARGET_PAGE_MASK,
|
||||
TARGET_PAGE_ALIGN(new_brk),
|
||||
PAGE_RESERVED);
|
||||
}
|
||||
last_brk = new_brk;
|
||||
|
||||
size = HOST_PAGE_ALIGN(size);
|
||||
start = start & qemu_host_page_mask;
|
||||
addr = start;
|
||||
if (addr == 0)
|
||||
addr = mmap_next_start;
|
||||
addr_start = addr;
|
||||
for(;;) {
|
||||
prot = 0;
|
||||
for(addr1 = addr; addr1 < (addr + size); addr1 += TARGET_PAGE_SIZE) {
|
||||
prot |= page_get_flags(addr1);
|
||||
}
|
||||
if (prot == 0)
|
||||
break;
|
||||
addr += qemu_host_page_size;
|
||||
/* we found nothing */
|
||||
if (addr == addr_start)
|
||||
return (abi_ulong)-1;
|
||||
}
|
||||
if (start == 0)
|
||||
mmap_next_start = addr + size;
|
||||
return addr;
|
||||
}
|
||||
|
||||
/* NOTE: all the constants are the HOST ones */
|
||||
abi_long target_mmap(abi_ulong start, abi_ulong len, int prot,
|
||||
int flags, int fd, abi_ulong offset)
|
||||
{
|
||||
abi_ulong ret, end, real_start, real_end, retaddr, host_offset, host_len;
|
||||
unsigned long host_start;
|
||||
|
||||
mmap_lock();
|
||||
#ifdef DEBUG_MMAP
|
||||
{
|
||||
printf("mmap: start=0x" TARGET_FMT_lx
|
||||
" len=0x" TARGET_FMT_lx " prot=%c%c%c flags=",
|
||||
start, len,
|
||||
prot & PROT_READ ? 'r' : '-',
|
||||
prot & PROT_WRITE ? 'w' : '-',
|
||||
prot & PROT_EXEC ? 'x' : '-');
|
||||
if (flags & MAP_FIXED)
|
||||
printf("MAP_FIXED ");
|
||||
if (flags & MAP_ANON)
|
||||
printf("MAP_ANON ");
|
||||
switch(flags & TARGET_BSD_MAP_FLAGMASK) {
|
||||
case MAP_PRIVATE:
|
||||
printf("MAP_PRIVATE ");
|
||||
break;
|
||||
case MAP_SHARED:
|
||||
printf("MAP_SHARED ");
|
||||
break;
|
||||
default:
|
||||
printf("[MAP_FLAGMASK=0x%x] ", flags & TARGET_BSD_MAP_FLAGMASK);
|
||||
break;
|
||||
}
|
||||
printf("fd=%d offset=" TARGET_FMT_lx "\n", fd, offset);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (offset & ~TARGET_PAGE_MASK) {
|
||||
errno = EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
len = TARGET_PAGE_ALIGN(len);
|
||||
if (len == 0)
|
||||
goto the_end;
|
||||
real_start = start & qemu_host_page_mask;
|
||||
|
||||
if (!(flags & MAP_FIXED)) {
|
||||
abi_ulong mmap_start;
|
||||
void *p;
|
||||
host_offset = offset & qemu_host_page_mask;
|
||||
host_len = len + offset - host_offset;
|
||||
host_len = HOST_PAGE_ALIGN(host_len);
|
||||
mmap_start = mmap_find_vma(real_start, host_len);
|
||||
if (mmap_start == (abi_ulong)-1) {
|
||||
errno = ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
/* Note: we prefer to control the mapping address. It is
|
||||
especially important if qemu_host_page_size >
|
||||
qemu_real_host_page_size */
|
||||
p = mmap(g2h(mmap_start),
|
||||
host_len, prot, flags | MAP_FIXED, fd, host_offset);
|
||||
if (p == MAP_FAILED)
|
||||
goto fail;
|
||||
/* update start so that it points to the file position at 'offset' */
|
||||
host_start = (unsigned long)p;
|
||||
if (!(flags & MAP_ANON))
|
||||
host_start += offset - host_offset;
|
||||
start = h2g(host_start);
|
||||
} else {
|
||||
int flg;
|
||||
target_ulong addr;
|
||||
|
||||
if (start & ~TARGET_PAGE_MASK) {
|
||||
errno = EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
end = start + len;
|
||||
real_end = HOST_PAGE_ALIGN(end);
|
||||
|
||||
for(addr = real_start; addr < real_end; addr += TARGET_PAGE_SIZE) {
|
||||
flg = page_get_flags(addr);
|
||||
if (flg & PAGE_RESERVED) {
|
||||
errno = ENXIO;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
/* worst case: we cannot map the file because the offset is not
|
||||
aligned, so we read it */
|
||||
if (!(flags & MAP_ANON) &&
|
||||
(offset & ~qemu_host_page_mask) != (start & ~qemu_host_page_mask)) {
|
||||
/* msync() won't work here, so we return an error if write is
|
||||
possible while it is a shared mapping */
|
||||
if ((flags & TARGET_BSD_MAP_FLAGMASK) == MAP_SHARED &&
|
||||
(prot & PROT_WRITE)) {
|
||||
errno = EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
retaddr = target_mmap(start, len, prot | PROT_WRITE,
|
||||
MAP_FIXED | MAP_PRIVATE | MAP_ANON,
|
||||
-1, 0);
|
||||
if (retaddr == -1)
|
||||
goto fail;
|
||||
pread(fd, g2h(start), len, offset);
|
||||
if (!(prot & PROT_WRITE)) {
|
||||
ret = target_mprotect(start, len, prot);
|
||||
if (ret != 0) {
|
||||
start = ret;
|
||||
goto the_end;
|
||||
}
|
||||
}
|
||||
goto the_end;
|
||||
}
|
||||
|
||||
/* handle the start of the mapping */
|
||||
if (start > real_start) {
|
||||
if (real_end == real_start + qemu_host_page_size) {
|
||||
/* one single host page */
|
||||
ret = mmap_frag(real_start, start, end,
|
||||
prot, flags, fd, offset);
|
||||
if (ret == -1)
|
||||
goto fail;
|
||||
goto the_end1;
|
||||
}
|
||||
ret = mmap_frag(real_start, start, real_start + qemu_host_page_size,
|
||||
prot, flags, fd, offset);
|
||||
if (ret == -1)
|
||||
goto fail;
|
||||
real_start += qemu_host_page_size;
|
||||
}
|
||||
/* handle the end of the mapping */
|
||||
if (end < real_end) {
|
||||
ret = mmap_frag(real_end - qemu_host_page_size,
|
||||
real_end - qemu_host_page_size, real_end,
|
||||
prot, flags, fd,
|
||||
offset + real_end - qemu_host_page_size - start);
|
||||
if (ret == -1)
|
||||
goto fail;
|
||||
real_end -= qemu_host_page_size;
|
||||
}
|
||||
|
||||
/* map the middle (easier) */
|
||||
if (real_start < real_end) {
|
||||
void *p;
|
||||
unsigned long offset1;
|
||||
if (flags & MAP_ANON)
|
||||
offset1 = 0;
|
||||
else
|
||||
offset1 = offset + real_start - start;
|
||||
p = mmap(g2h(real_start), real_end - real_start,
|
||||
prot, flags, fd, offset1);
|
||||
if (p == MAP_FAILED)
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
the_end1:
|
||||
page_set_flags(start, start + len, prot | PAGE_VALID);
|
||||
the_end:
|
||||
#ifdef DEBUG_MMAP
|
||||
printf("ret=0x" TARGET_FMT_lx "\n", start);
|
||||
page_dump(stdout);
|
||||
printf("\n");
|
||||
#endif
|
||||
mmap_unlock();
|
||||
return start;
|
||||
fail:
|
||||
mmap_unlock();
|
||||
return -1;
|
||||
}
|
||||
|
||||
int target_munmap(abi_ulong start, abi_ulong len)
|
||||
{
|
||||
abi_ulong end, real_start, real_end, addr;
|
||||
int prot, ret;
|
||||
|
||||
#ifdef DEBUG_MMAP
|
||||
printf("munmap: start=0x%lx len=0x%lx\n", start, len);
|
||||
#endif
|
||||
if (start & ~TARGET_PAGE_MASK)
|
||||
return -EINVAL;
|
||||
len = TARGET_PAGE_ALIGN(len);
|
||||
if (len == 0)
|
||||
return -EINVAL;
|
||||
mmap_lock();
|
||||
end = start + len;
|
||||
real_start = start & qemu_host_page_mask;
|
||||
real_end = HOST_PAGE_ALIGN(end);
|
||||
|
||||
if (start > real_start) {
|
||||
/* handle host page containing start */
|
||||
prot = 0;
|
||||
for(addr = real_start; addr < start; addr += TARGET_PAGE_SIZE) {
|
||||
prot |= page_get_flags(addr);
|
||||
}
|
||||
if (real_end == real_start + qemu_host_page_size) {
|
||||
for(addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) {
|
||||
prot |= page_get_flags(addr);
|
||||
}
|
||||
end = real_end;
|
||||
}
|
||||
if (prot != 0)
|
||||
real_start += qemu_host_page_size;
|
||||
}
|
||||
if (end < real_end) {
|
||||
prot = 0;
|
||||
for(addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) {
|
||||
prot |= page_get_flags(addr);
|
||||
}
|
||||
if (prot != 0)
|
||||
real_end -= qemu_host_page_size;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
/* unmap what we can */
|
||||
if (real_start < real_end) {
|
||||
ret = munmap(g2h(real_start), real_end - real_start);
|
||||
}
|
||||
|
||||
if (ret == 0)
|
||||
page_set_flags(start, start + len, 0);
|
||||
mmap_unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
int target_msync(abi_ulong start, abi_ulong len, int flags)
|
||||
{
|
||||
abi_ulong end;
|
||||
|
||||
if (start & ~TARGET_PAGE_MASK)
|
||||
return -EINVAL;
|
||||
len = TARGET_PAGE_ALIGN(len);
|
||||
end = start + len;
|
||||
if (end < start)
|
||||
return -EINVAL;
|
||||
if (end == start)
|
||||
return 0;
|
||||
|
||||
start &= qemu_host_page_mask;
|
||||
return msync(g2h(start), end - start, flags);
|
||||
}
|
||||
@@ -1,145 +0,0 @@
|
||||
{ TARGET_NETBSD_NR___getcwd, "__getcwd", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR___syscall, "__syscall", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR___sysctl, "__sysctl", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_accept, "accept", "%s(%d,%#x,%#x)", NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_access, "access", "%s(\"%s\",%#o)", NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_acct, "acct", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_adjtime, "adjtime", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_bind, "bind", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_break, "break", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_chdir, "chdir", "%s(\"%s\")", NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_chflags, "chflags", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_chmod, "chmod", "%s(\"%s\",%#o)", NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_chown, "chown", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_chroot, "chroot", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_clock_getres, "clock_getres", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_clock_gettime, "clock_gettime", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_clock_settime, "clock_settime", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_close, "close", "%s(%d)", NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_connect, "connect", "%s(%d,%#x,%d)", NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_dup, "dup", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_dup2, "dup2", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_execve, "execve", NULL, print_execve, NULL },
|
||||
{ TARGET_NETBSD_NR_exit, "exit", "%s(%d)\n", NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_fchdir, "fchdir", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_fchflags, "fchflags", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_fchmod, "fchmod", "%s(%d,%#o)", NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_fchown, "fchown", "%s(\"%s\",%d,%d)", NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_fcntl, "fcntl", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_flock, "flock", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_fork, "fork", "%s()", NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_fpathconf, "fpathconf", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_fsync, "fsync", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_ftruncate, "ftruncate", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_futimes, "futimes", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_getegid, "getegid", "%s()", NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_geteuid, "geteuid", "%s()", NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_getgid, "getgid", "%s()", NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_getgroups, "getgroups", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_getitimer, "getitimer", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_getpeername, "getpeername", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_getpgid, "getpgid", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_getpgrp, "getpgrp", "%s()", NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_getpid, "getpid", "%s()", NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_getppid, "getppid", "%s()", NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_getpriority, "getpriority", "%s(%#x,%#x)", NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_getrlimit, "getrlimit", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_getrusage, "getrusage", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_getsid, "getsid", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_getsockname, "getsockname", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_getsockopt, "getsockopt", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_gettimeofday, "gettimeofday", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_getuid, "getuid", "%s()", NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_ioctl, "ioctl", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_issetugid, "issetugid", "%s()", NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_kevent, "kevent", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_kill, "kill", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_kqueue, "kqueue", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_ktrace, "ktrace", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_lchown, "lchown", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_lfs_bmapv, "lfs_bmapv", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_lfs_markv, "lfs_markv", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_lfs_segclean, "lfs_segclean", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_lfs_segwait, "lfs_segwait", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_link, "link", "%s(\"%s\",\"%s\")", NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_listen, "listen", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_lseek, "lseek", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_madvise, "madvise", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_mincore, "mincore", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_minherit, "minherit", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_mkdir, "mkdir", "%s(\"%s\",%#o)", NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_mkfifo, "mkfifo", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_mknod, "mknod", "%s(\"%s\",%#o,%#x)", NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_mlock, "mlock", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_mlockall, "mlockall", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_mmap, "mmap", NULL, NULL, print_syscall_ret_addr },
|
||||
{ TARGET_NETBSD_NR_mprotect, "mprotect", "%s(%#x,%#x,%d)", NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_msgget, "msgget", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_msgrcv, "msgrcv", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_msgsnd, "msgsnd", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_munlock, "munlock", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_munlockall, "munlockall", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_munmap, "munmap", "%s(%p,%d)", NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_nanosleep, "nanosleep", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_nfssvc, "nfssvc", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_open, "open", "%s(\"%s\",%#x,%#o)", NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_pathconf, "pathconf", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_pipe, "pipe", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_poll, "poll", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_pread, "pread", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_preadv, "preadv", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_profil, "profil", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_ptrace, "ptrace", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_pwrite, "pwrite", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_pwritev, "pwritev", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_quotactl, "quotactl", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_read, "read", "%s(%d,%#x,%d)", NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_readlink, "readlink", "%s(\"%s\",%p,%d)", NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_readv, "readv", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_reboot, "reboot", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_recvfrom, "recvfrom", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_recvmsg, "recvmsg", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_rename, "rename", "%s(\"%s\",\"%s\")", NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_revoke, "revoke", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_rmdir, "rmdir", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_sbrk, "sbrk", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_sched_yield, "sched_yield", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_select, "select", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_semget, "semget", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_semop, "semop", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_sendmsg, "sendmsg", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_sendto, "sendto", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_setegid, "setegid", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_seteuid, "seteuid", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_setgid, "setgid", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_setgroups, "setgroups", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_setitimer, "setitimer", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_setpgid, "setpgid", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_setpriority, "setpriority", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_setregid, "setregid", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_setreuid, "setreuid", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_setrlimit, "setrlimit", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_setsid, "setsid", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_setsockopt, "setsockopt", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_settimeofday, "settimeofday", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_setuid, "setuid", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_shmat, "shmat", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_shmdt, "shmdt", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_shmget, "shmget", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_shutdown, "shutdown", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_socketpair, "socketpair", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_sstk, "sstk", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_swapctl, "swapctl", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_symlink, "symlink", "%s(\"%s\",\"%s\")", NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_sync, "sync", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_sysarch, "sysarch", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_syscall, "syscall", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_truncate, "truncate", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_umask, "umask", "%s(%#o)", NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_unlink, "unlink", "%s(\"%s\")", NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_unmount, "unmount", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_utimes, "utimes", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_vfork, "vfork", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_wait4, "wait4", NULL, NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_write, "write", "%s(%d,%#x,%d)", NULL, NULL },
|
||||
{ TARGET_NETBSD_NR_writev, "writev", "%s(%d,%p,%#x)", NULL, NULL },
|
||||
@@ -1,373 +0,0 @@
|
||||
/* $NetBSD: syscall.h,v 1.215 2008/06/17 16:07:57 tsutsui Exp $ */
|
||||
|
||||
/*
|
||||
* System call numbers.
|
||||
*
|
||||
* created from NetBSD: syscalls.master,v 1.204 2008/06/17 16:05:23 tsutsui Exp
|
||||
*/
|
||||
|
||||
#define TARGET_NETBSD_NR_syscall 0
|
||||
#define TARGET_NETBSD_NR_exit 1
|
||||
#define TARGET_NETBSD_NR_fork 2
|
||||
#define TARGET_NETBSD_NR_read 3
|
||||
#define TARGET_NETBSD_NR_write 4
|
||||
#define TARGET_NETBSD_NR_open 5
|
||||
#define TARGET_NETBSD_NR_close 6
|
||||
#define TARGET_NETBSD_NR_wait4 7
|
||||
#define TARGET_NETBSD_NR_compat_43_ocreat 8
|
||||
#define TARGET_NETBSD_NR_link 9
|
||||
#define TARGET_NETBSD_NR_unlink 10
|
||||
#define TARGET_NETBSD_NR_chdir 12
|
||||
#define TARGET_NETBSD_NR_fchdir 13
|
||||
#define TARGET_NETBSD_NR_mknod 14
|
||||
#define TARGET_NETBSD_NR_chmod 15
|
||||
#define TARGET_NETBSD_NR_chown 16
|
||||
#define TARGET_NETBSD_NR_break 17
|
||||
#define TARGET_NETBSD_NR_compat_20_getfsstat 18
|
||||
#define TARGET_NETBSD_NR_compat_43_olseek 19
|
||||
#define TARGET_NETBSD_NR_getpid 20
|
||||
#define TARGET_NETBSD_NR_getpid 20
|
||||
#define TARGET_NETBSD_NR_compat_40_mount 21
|
||||
#define TARGET_NETBSD_NR_unmount 22
|
||||
#define TARGET_NETBSD_NR_setuid 23
|
||||
#define TARGET_NETBSD_NR_getuid 24
|
||||
#define TARGET_NETBSD_NR_getuid 24
|
||||
#define TARGET_NETBSD_NR_geteuid 25
|
||||
#define TARGET_NETBSD_NR_ptrace 26
|
||||
#define TARGET_NETBSD_NR_recvmsg 27
|
||||
#define TARGET_NETBSD_NR_sendmsg 28
|
||||
#define TARGET_NETBSD_NR_recvfrom 29
|
||||
#define TARGET_NETBSD_NR_accept 30
|
||||
#define TARGET_NETBSD_NR_getpeername 31
|
||||
#define TARGET_NETBSD_NR_getsockname 32
|
||||
#define TARGET_NETBSD_NR_access 33
|
||||
#define TARGET_NETBSD_NR_chflags 34
|
||||
#define TARGET_NETBSD_NR_fchflags 35
|
||||
#define TARGET_NETBSD_NR_sync 36
|
||||
#define TARGET_NETBSD_NR_kill 37
|
||||
#define TARGET_NETBSD_NR_compat_43_stat43 38
|
||||
#define TARGET_NETBSD_NR_getppid 39
|
||||
#define TARGET_NETBSD_NR_compat_43_lstat43 40
|
||||
#define TARGET_NETBSD_NR_dup 41
|
||||
#define TARGET_NETBSD_NR_pipe 42
|
||||
#define TARGET_NETBSD_NR_getegid 43
|
||||
#define TARGET_NETBSD_NR_profil 44
|
||||
#define TARGET_NETBSD_NR_ktrace 45
|
||||
#define TARGET_NETBSD_NR_compat_13_sigaction13 46
|
||||
#define TARGET_NETBSD_NR_getgid 47
|
||||
#define TARGET_NETBSD_NR_getgid 47
|
||||
#define TARGET_NETBSD_NR_compat_13_sigprocmask13 48
|
||||
#define TARGET_NETBSD_NR___getlogin 49
|
||||
#define TARGET_NETBSD_NR___setlogin 50
|
||||
#define TARGET_NETBSD_NR_acct 51
|
||||
#define TARGET_NETBSD_NR_compat_13_sigpending13 52
|
||||
#define TARGET_NETBSD_NR_compat_13_sigaltstack13 53
|
||||
#define TARGET_NETBSD_NR_ioctl 54
|
||||
#define TARGET_NETBSD_NR_compat_12_oreboot 55
|
||||
#define TARGET_NETBSD_NR_revoke 56
|
||||
#define TARGET_NETBSD_NR_symlink 57
|
||||
#define TARGET_NETBSD_NR_readlink 58
|
||||
#define TARGET_NETBSD_NR_execve 59
|
||||
#define TARGET_NETBSD_NR_umask 60
|
||||
#define TARGET_NETBSD_NR_chroot 61
|
||||
#define TARGET_NETBSD_NR_compat_43_fstat43 62
|
||||
#define TARGET_NETBSD_NR_compat_43_ogetkerninfo 63
|
||||
#define TARGET_NETBSD_NR_compat_43_ogetpagesize 64
|
||||
#define TARGET_NETBSD_NR_compat_12_msync 65
|
||||
#define TARGET_NETBSD_NR_vfork 66
|
||||
#define TARGET_NETBSD_NR_sbrk 69
|
||||
#define TARGET_NETBSD_NR_sstk 70
|
||||
#define TARGET_NETBSD_NR_compat_43_ommap 71
|
||||
#define TARGET_NETBSD_NR_vadvise 72
|
||||
#define TARGET_NETBSD_NR_munmap 73
|
||||
#define TARGET_NETBSD_NR_mprotect 74
|
||||
#define TARGET_NETBSD_NR_madvise 75
|
||||
#define TARGET_NETBSD_NR_mincore 78
|
||||
#define TARGET_NETBSD_NR_getgroups 79
|
||||
#define TARGET_NETBSD_NR_setgroups 80
|
||||
#define TARGET_NETBSD_NR_getpgrp 81
|
||||
#define TARGET_NETBSD_NR_setpgid 82
|
||||
#define TARGET_NETBSD_NR_setitimer 83
|
||||
#define TARGET_NETBSD_NR_compat_43_owait 84
|
||||
#define TARGET_NETBSD_NR_compat_12_oswapon 85
|
||||
#define TARGET_NETBSD_NR_getitimer 86
|
||||
#define TARGET_NETBSD_NR_compat_43_ogethostname 87
|
||||
#define TARGET_NETBSD_NR_compat_43_osethostname 88
|
||||
#define TARGET_NETBSD_NR_compat_43_ogetdtablesize 89
|
||||
#define TARGET_NETBSD_NR_dup2 90
|
||||
#define TARGET_NETBSD_NR_fcntl 92
|
||||
#define TARGET_NETBSD_NR_select 93
|
||||
#define TARGET_NETBSD_NR_fsync 95
|
||||
#define TARGET_NETBSD_NR_setpriority 96
|
||||
#define TARGET_NETBSD_NR_compat_30_socket 97
|
||||
#define TARGET_NETBSD_NR_connect 98
|
||||
#define TARGET_NETBSD_NR_compat_43_oaccept 99
|
||||
#define TARGET_NETBSD_NR_getpriority 100
|
||||
#define TARGET_NETBSD_NR_compat_43_osend 101
|
||||
#define TARGET_NETBSD_NR_compat_43_orecv 102
|
||||
#define TARGET_NETBSD_NR_compat_13_sigreturn13 103
|
||||
#define TARGET_NETBSD_NR_bind 104
|
||||
#define TARGET_NETBSD_NR_setsockopt 105
|
||||
#define TARGET_NETBSD_NR_listen 106
|
||||
#define TARGET_NETBSD_NR_compat_43_osigvec 108
|
||||
#define TARGET_NETBSD_NR_compat_43_osigblock 109
|
||||
#define TARGET_NETBSD_NR_compat_43_osigsetmask 110
|
||||
#define TARGET_NETBSD_NR_compat_13_sigsuspend13 111
|
||||
#define TARGET_NETBSD_NR_compat_43_osigstack 112
|
||||
#define TARGET_NETBSD_NR_compat_43_orecvmsg 113
|
||||
#define TARGET_NETBSD_NR_compat_43_osendmsg 114
|
||||
#define TARGET_NETBSD_NR_gettimeofday 116
|
||||
#define TARGET_NETBSD_NR_getrusage 117
|
||||
#define TARGET_NETBSD_NR_getsockopt 118
|
||||
#define TARGET_NETBSD_NR_readv 120
|
||||
#define TARGET_NETBSD_NR_writev 121
|
||||
#define TARGET_NETBSD_NR_settimeofday 122
|
||||
#define TARGET_NETBSD_NR_fchown 123
|
||||
#define TARGET_NETBSD_NR_fchmod 124
|
||||
#define TARGET_NETBSD_NR_compat_43_orecvfrom 125
|
||||
#define TARGET_NETBSD_NR_setreuid 126
|
||||
#define TARGET_NETBSD_NR_setregid 127
|
||||
#define TARGET_NETBSD_NR_rename 128
|
||||
#define TARGET_NETBSD_NR_compat_43_otruncate 129
|
||||
#define TARGET_NETBSD_NR_compat_43_oftruncate 130
|
||||
#define TARGET_NETBSD_NR_flock 131
|
||||
#define TARGET_NETBSD_NR_mkfifo 132
|
||||
#define TARGET_NETBSD_NR_sendto 133
|
||||
#define TARGET_NETBSD_NR_shutdown 134
|
||||
#define TARGET_NETBSD_NR_socketpair 135
|
||||
#define TARGET_NETBSD_NR_mkdir 136
|
||||
#define TARGET_NETBSD_NR_rmdir 137
|
||||
#define TARGET_NETBSD_NR_utimes 138
|
||||
#define TARGET_NETBSD_NR_adjtime 140
|
||||
#define TARGET_NETBSD_NR_compat_43_ogetpeername 141
|
||||
#define TARGET_NETBSD_NR_compat_43_ogethostid 142
|
||||
#define TARGET_NETBSD_NR_compat_43_osethostid 143
|
||||
#define TARGET_NETBSD_NR_compat_43_ogetrlimit 144
|
||||
#define TARGET_NETBSD_NR_compat_43_osetrlimit 145
|
||||
#define TARGET_NETBSD_NR_compat_43_okillpg 146
|
||||
#define TARGET_NETBSD_NR_setsid 147
|
||||
#define TARGET_NETBSD_NR_quotactl 148
|
||||
#define TARGET_NETBSD_NR_compat_43_oquota 149
|
||||
#define TARGET_NETBSD_NR_compat_43_ogetsockname 150
|
||||
#define TARGET_NETBSD_NR_nfssvc 155
|
||||
#define TARGET_NETBSD_NR_compat_43_ogetdirentries 156
|
||||
#define TARGET_NETBSD_NR_compat_20_statfs 157
|
||||
#define TARGET_NETBSD_NR_compat_20_fstatfs 158
|
||||
#define TARGET_NETBSD_NR_compat_30_getfh 161
|
||||
#define TARGET_NETBSD_NR_compat_09_ogetdomainname 162
|
||||
#define TARGET_NETBSD_NR_compat_09_osetdomainname 163
|
||||
#define TARGET_NETBSD_NR_compat_09_ouname 164
|
||||
#define TARGET_NETBSD_NR_sysarch 165
|
||||
#define TARGET_NETBSD_NR_compat_10_osemsys 169
|
||||
#define TARGET_NETBSD_NR_compat_10_omsgsys 170
|
||||
#define TARGET_NETBSD_NR_compat_10_oshmsys 171
|
||||
#define TARGET_NETBSD_NR_pread 173
|
||||
#define TARGET_NETBSD_NR_pwrite 174
|
||||
#define TARGET_NETBSD_NR_compat_30_ntp_gettime 175
|
||||
#define TARGET_NETBSD_NR_ntp_adjtime 176
|
||||
#define TARGET_NETBSD_NR_setgid 181
|
||||
#define TARGET_NETBSD_NR_setegid 182
|
||||
#define TARGET_NETBSD_NR_seteuid 183
|
||||
#define TARGET_NETBSD_NR_lfs_bmapv 184
|
||||
#define TARGET_NETBSD_NR_lfs_markv 185
|
||||
#define TARGET_NETBSD_NR_lfs_segclean 186
|
||||
#define TARGET_NETBSD_NR_lfs_segwait 187
|
||||
#define TARGET_NETBSD_NR_compat_12_stat12 188
|
||||
#define TARGET_NETBSD_NR_compat_12_fstat12 189
|
||||
#define TARGET_NETBSD_NR_compat_12_lstat12 190
|
||||
#define TARGET_NETBSD_NR_pathconf 191
|
||||
#define TARGET_NETBSD_NR_fpathconf 192
|
||||
#define TARGET_NETBSD_NR_getrlimit 194
|
||||
#define TARGET_NETBSD_NR_setrlimit 195
|
||||
#define TARGET_NETBSD_NR_compat_12_getdirentries 196
|
||||
#define TARGET_NETBSD_NR_mmap 197
|
||||
#define TARGET_NETBSD_NR___syscall 198
|
||||
#define TARGET_NETBSD_NR_lseek 199
|
||||
#define TARGET_NETBSD_NR_truncate 200
|
||||
#define TARGET_NETBSD_NR_ftruncate 201
|
||||
#define TARGET_NETBSD_NR___sysctl 202
|
||||
#define TARGET_NETBSD_NR_mlock 203
|
||||
#define TARGET_NETBSD_NR_munlock 204
|
||||
#define TARGET_NETBSD_NR_undelete 205
|
||||
#define TARGET_NETBSD_NR_futimes 206
|
||||
#define TARGET_NETBSD_NR_getpgid 207
|
||||
#define TARGET_NETBSD_NR_reboot 208
|
||||
#define TARGET_NETBSD_NR_poll 209
|
||||
#define TARGET_NETBSD_NR_compat_14___semctl 220
|
||||
#define TARGET_NETBSD_NR_semget 221
|
||||
#define TARGET_NETBSD_NR_semop 222
|
||||
#define TARGET_NETBSD_NR_semconfig 223
|
||||
#define TARGET_NETBSD_NR_compat_14_msgctl 224
|
||||
#define TARGET_NETBSD_NR_msgget 225
|
||||
#define TARGET_NETBSD_NR_msgsnd 226
|
||||
#define TARGET_NETBSD_NR_msgrcv 227
|
||||
#define TARGET_NETBSD_NR_shmat 228
|
||||
#define TARGET_NETBSD_NR_compat_14_shmctl 229
|
||||
#define TARGET_NETBSD_NR_shmdt 230
|
||||
#define TARGET_NETBSD_NR_shmget 231
|
||||
#define TARGET_NETBSD_NR_clock_gettime 232
|
||||
#define TARGET_NETBSD_NR_clock_settime 233
|
||||
#define TARGET_NETBSD_NR_clock_getres 234
|
||||
#define TARGET_NETBSD_NR_timer_create 235
|
||||
#define TARGET_NETBSD_NR_timer_delete 236
|
||||
#define TARGET_NETBSD_NR_timer_settime 237
|
||||
#define TARGET_NETBSD_NR_timer_gettime 238
|
||||
#define TARGET_NETBSD_NR_timer_getoverrun 239
|
||||
#define TARGET_NETBSD_NR_nanosleep 240
|
||||
#define TARGET_NETBSD_NR_fdatasync 241
|
||||
#define TARGET_NETBSD_NR_mlockall 242
|
||||
#define TARGET_NETBSD_NR_munlockall 243
|
||||
#define TARGET_NETBSD_NR___sigtimedwait 244
|
||||
#define TARGET_NETBSD_NR_modctl 246
|
||||
#define TARGET_NETBSD_NR__ksem_init 247
|
||||
#define TARGET_NETBSD_NR__ksem_open 248
|
||||
#define TARGET_NETBSD_NR__ksem_unlink 249
|
||||
#define TARGET_NETBSD_NR__ksem_close 250
|
||||
#define TARGET_NETBSD_NR__ksem_post 251
|
||||
#define TARGET_NETBSD_NR__ksem_wait 252
|
||||
#define TARGET_NETBSD_NR__ksem_trywait 253
|
||||
#define TARGET_NETBSD_NR__ksem_getvalue 254
|
||||
#define TARGET_NETBSD_NR__ksem_destroy 255
|
||||
#define TARGET_NETBSD_NR_mq_open 257
|
||||
#define TARGET_NETBSD_NR_mq_close 258
|
||||
#define TARGET_NETBSD_NR_mq_unlink 259
|
||||
#define TARGET_NETBSD_NR_mq_getattr 260
|
||||
#define TARGET_NETBSD_NR_mq_setattr 261
|
||||
#define TARGET_NETBSD_NR_mq_notify 262
|
||||
#define TARGET_NETBSD_NR_mq_send 263
|
||||
#define TARGET_NETBSD_NR_mq_receive 264
|
||||
#define TARGET_NETBSD_NR_mq_timedsend 265
|
||||
#define TARGET_NETBSD_NR_mq_timedreceive 266
|
||||
#define TARGET_NETBSD_NR___posix_rename 270
|
||||
#define TARGET_NETBSD_NR_swapctl 271
|
||||
#define TARGET_NETBSD_NR_compat_30_getdents 272
|
||||
#define TARGET_NETBSD_NR_minherit 273
|
||||
#define TARGET_NETBSD_NR_lchmod 274
|
||||
#define TARGET_NETBSD_NR_lchown 275
|
||||
#define TARGET_NETBSD_NR_lutimes 276
|
||||
#define TARGET_NETBSD_NR___msync13 277
|
||||
#define TARGET_NETBSD_NR_compat_30___stat13 278
|
||||
#define TARGET_NETBSD_NR_compat_30___fstat13 279
|
||||
#define TARGET_NETBSD_NR_compat_30___lstat13 280
|
||||
#define TARGET_NETBSD_NR___sigaltstack14 281
|
||||
#define TARGET_NETBSD_NR___vfork14 282
|
||||
#define TARGET_NETBSD_NR___posix_chown 283
|
||||
#define TARGET_NETBSD_NR___posix_fchown 284
|
||||
#define TARGET_NETBSD_NR___posix_lchown 285
|
||||
#define TARGET_NETBSD_NR_getsid 286
|
||||
#define TARGET_NETBSD_NR___clone 287
|
||||
#define TARGET_NETBSD_NR_fktrace 288
|
||||
#define TARGET_NETBSD_NR_preadv 289
|
||||
#define TARGET_NETBSD_NR_pwritev 290
|
||||
#define TARGET_NETBSD_NR_compat_16___sigaction14 291
|
||||
#define TARGET_NETBSD_NR___sigpending14 292
|
||||
#define TARGET_NETBSD_NR___sigprocmask14 293
|
||||
#define TARGET_NETBSD_NR___sigsuspend14 294
|
||||
#define TARGET_NETBSD_NR_compat_16___sigreturn14 295
|
||||
#define TARGET_NETBSD_NR___getcwd 296
|
||||
#define TARGET_NETBSD_NR_fchroot 297
|
||||
#define TARGET_NETBSD_NR_compat_30_fhopen 298
|
||||
#define TARGET_NETBSD_NR_compat_30_fhstat 299
|
||||
#define TARGET_NETBSD_NR_compat_20_fhstatfs 300
|
||||
#define TARGET_NETBSD_NR_____semctl13 301
|
||||
#define TARGET_NETBSD_NR___msgctl13 302
|
||||
#define TARGET_NETBSD_NR___shmctl13 303
|
||||
#define TARGET_NETBSD_NR_lchflags 304
|
||||
#define TARGET_NETBSD_NR_issetugid 305
|
||||
#define TARGET_NETBSD_NR_utrace 306
|
||||
#define TARGET_NETBSD_NR_getcontext 307
|
||||
#define TARGET_NETBSD_NR_setcontext 308
|
||||
#define TARGET_NETBSD_NR__lwp_create 309
|
||||
#define TARGET_NETBSD_NR__lwp_exit 310
|
||||
#define TARGET_NETBSD_NR__lwp_self 311
|
||||
#define TARGET_NETBSD_NR__lwp_wait 312
|
||||
#define TARGET_NETBSD_NR__lwp_suspend 313
|
||||
#define TARGET_NETBSD_NR__lwp_continue 314
|
||||
#define TARGET_NETBSD_NR__lwp_wakeup 315
|
||||
#define TARGET_NETBSD_NR__lwp_getprivate 316
|
||||
#define TARGET_NETBSD_NR__lwp_setprivate 317
|
||||
#define TARGET_NETBSD_NR__lwp_kill 318
|
||||
#define TARGET_NETBSD_NR__lwp_detach 319
|
||||
#define TARGET_NETBSD_NR__lwp_park 320
|
||||
#define TARGET_NETBSD_NR__lwp_unpark 321
|
||||
#define TARGET_NETBSD_NR__lwp_unpark_all 322
|
||||
#define TARGET_NETBSD_NR__lwp_setname 323
|
||||
#define TARGET_NETBSD_NR__lwp_getname 324
|
||||
#define TARGET_NETBSD_NR__lwp_ctl 325
|
||||
#define TARGET_NETBSD_NR_sa_register 330
|
||||
#define TARGET_NETBSD_NR_sa_stacks 331
|
||||
#define TARGET_NETBSD_NR_sa_enable 332
|
||||
#define TARGET_NETBSD_NR_sa_setconcurrency 333
|
||||
#define TARGET_NETBSD_NR_sa_yield 334
|
||||
#define TARGET_NETBSD_NR_sa_preempt 335
|
||||
#define TARGET_NETBSD_NR_sa_unblockyield 336
|
||||
#define TARGET_NETBSD_NR___sigaction_sigtramp 340
|
||||
#define TARGET_NETBSD_NR_pmc_get_info 341
|
||||
#define TARGET_NETBSD_NR_pmc_control 342
|
||||
#define TARGET_NETBSD_NR_rasctl 343
|
||||
#define TARGET_NETBSD_NR_kqueue 344
|
||||
#define TARGET_NETBSD_NR_kevent 345
|
||||
#define TARGET_NETBSD_NR__sched_setparam 346
|
||||
#define TARGET_NETBSD_NR__sched_getparam 347
|
||||
#define TARGET_NETBSD_NR__sched_setaffinity 348
|
||||
#define TARGET_NETBSD_NR__sched_getaffinity 349
|
||||
#define TARGET_NETBSD_NR_sched_yield 350
|
||||
#define TARGET_NETBSD_NR_fsync_range 354
|
||||
#define TARGET_NETBSD_NR_uuidgen 355
|
||||
#define TARGET_NETBSD_NR_getvfsstat 356
|
||||
#define TARGET_NETBSD_NR_statvfs1 357
|
||||
#define TARGET_NETBSD_NR_fstatvfs1 358
|
||||
#define TARGET_NETBSD_NR_compat_30_fhstatvfs1 359
|
||||
#define TARGET_NETBSD_NR_extattrctl 360
|
||||
#define TARGET_NETBSD_NR_extattr_set_file 361
|
||||
#define TARGET_NETBSD_NR_extattr_get_file 362
|
||||
#define TARGET_NETBSD_NR_extattr_delete_file 363
|
||||
#define TARGET_NETBSD_NR_extattr_set_fd 364
|
||||
#define TARGET_NETBSD_NR_extattr_get_fd 365
|
||||
#define TARGET_NETBSD_NR_extattr_delete_fd 366
|
||||
#define TARGET_NETBSD_NR_extattr_set_link 367
|
||||
#define TARGET_NETBSD_NR_extattr_get_link 368
|
||||
#define TARGET_NETBSD_NR_extattr_delete_link 369
|
||||
#define TARGET_NETBSD_NR_extattr_list_fd 370
|
||||
#define TARGET_NETBSD_NR_extattr_list_file 371
|
||||
#define TARGET_NETBSD_NR_extattr_list_link 372
|
||||
#define TARGET_NETBSD_NR_pselect 373
|
||||
#define TARGET_NETBSD_NR_pollts 374
|
||||
#define TARGET_NETBSD_NR_setxattr 375
|
||||
#define TARGET_NETBSD_NR_lsetxattr 376
|
||||
#define TARGET_NETBSD_NR_fsetxattr 377
|
||||
#define TARGET_NETBSD_NR_getxattr 378
|
||||
#define TARGET_NETBSD_NR_lgetxattr 379
|
||||
#define TARGET_NETBSD_NR_fgetxattr 380
|
||||
#define TARGET_NETBSD_NR_listxattr 381
|
||||
#define TARGET_NETBSD_NR_llistxattr 382
|
||||
#define TARGET_NETBSD_NR_flistxattr 383
|
||||
#define TARGET_NETBSD_NR_removexattr 384
|
||||
#define TARGET_NETBSD_NR_lremovexattr 385
|
||||
#define TARGET_NETBSD_NR_fremovexattr 386
|
||||
#define TARGET_NETBSD_NR___stat30 387
|
||||
#define TARGET_NETBSD_NR___fstat30 388
|
||||
#define TARGET_NETBSD_NR___lstat30 389
|
||||
#define TARGET_NETBSD_NR___getdents30 390
|
||||
#define TARGET_NETBSD_NR_compat_30___fhstat30 392
|
||||
#define TARGET_NETBSD_NR___ntp_gettime30 393
|
||||
#define TARGET_NETBSD_NR___socket30 394
|
||||
#define TARGET_NETBSD_NR___getfh30 395
|
||||
#define TARGET_NETBSD_NR___fhopen40 396
|
||||
#define TARGET_NETBSD_NR___fhstatvfs140 397
|
||||
#define TARGET_NETBSD_NR___fhstat40 398
|
||||
#define TARGET_NETBSD_NR_aio_cancel 399
|
||||
#define TARGET_NETBSD_NR_aio_error 400
|
||||
#define TARGET_NETBSD_NR_aio_fsync 401
|
||||
#define TARGET_NETBSD_NR_aio_read 402
|
||||
#define TARGET_NETBSD_NR_aio_return 403
|
||||
#define TARGET_NETBSD_NR_aio_suspend 404
|
||||
#define TARGET_NETBSD_NR_aio_write 405
|
||||
#define TARGET_NETBSD_NR_lio_listio 406
|
||||
#define TARGET_NETBSD_NR___mount50 410
|
||||
#define TARGET_NETBSD_NR_mremap 411
|
||||
#define TARGET_NETBSD_NR_pset_create 412
|
||||
#define TARGET_NETBSD_NR_pset_destroy 413
|
||||
#define TARGET_NETBSD_NR_pset_assign 414
|
||||
#define TARGET_NETBSD_NR__pset_bind 415
|
||||
#define TARGET_NETBSD_NR___posix_fadvise50 416
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user