Compare commits
51
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5558e88318 | ||
|
|
06b393452f | ||
|
|
b7d11af2e0 | ||
|
|
8d47bb6162 | ||
|
|
80acab6c50 | ||
|
|
da0ac2bc13 | ||
|
|
d3c2632047 | ||
|
|
7cc73776e7 | ||
|
|
937e9a1c83 | ||
|
|
4498819404 | ||
|
|
c61ad61830 | ||
|
|
65297bc408 | ||
|
|
02d400ead7 | ||
|
|
20a9c6ac7f | ||
|
|
bd0b264c21 | ||
|
|
0f31aac44f | ||
|
|
30b15843b2 | ||
|
|
04ffdd7f94 | ||
|
|
7648bb760d | ||
|
|
ebb7184720 | ||
|
|
3f546bd0b1 | ||
|
|
00614a4cb4 | ||
|
|
7c14db42cb | ||
|
|
e58843d0f1 | ||
|
|
0e39d3f89b | ||
|
|
5389a9df53 | ||
|
|
21fd832512 | ||
|
|
15d4afd55b | ||
|
|
3df962a30d | ||
|
|
4827c90cef | ||
|
|
0f56231dce | ||
|
|
523faf1b92 | ||
|
|
414c078104 | ||
|
|
9cecf0f570 | ||
|
|
4ca9f3dd71 | ||
|
|
f902c4192e | ||
|
|
b96a313d3d | ||
|
|
d9aa1fce5a | ||
|
|
8a11f5ff08 | ||
|
|
b786b7ccbd | ||
|
|
d3be2b2f71 | ||
|
|
e36fb2a3a2 | ||
|
|
4978045781 | ||
|
|
cb5745c529 | ||
|
|
295b492cfa | ||
|
|
af8222c0f8 | ||
|
|
249139f4e6 | ||
|
|
8ae0978ed5 | ||
|
|
c0024a8257 | ||
|
|
e9af78a859 | ||
|
|
25c4fde177 |
No files matched your search
-13
@@ -1,15 +1,9 @@
|
||||
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
|
||||
@@ -22,11 +16,6 @@ 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
|
||||
@@ -46,6 +35,4 @@ qemu-monitor.texi
|
||||
patches
|
||||
pc-bios/bios-pq/status
|
||||
pc-bios/vgabios-pq/status
|
||||
pc-bios/optionrom/multiboot.bin
|
||||
pc-bios/optionrom/multiboot.raw
|
||||
.stgit-*
|
||||
@@ -1,6 +0,0 @@
|
||||
[submodule "roms/vgabios"]
|
||||
path = roms/vgabios
|
||||
url = ../vgabios.git
|
||||
[submodule "roms/seabios"]
|
||||
path = roms/seabios
|
||||
url = ../seabios.git
|
||||
@@ -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.
|
||||
@@ -1,163 +1,3 @@
|
||||
version 0.12.3
|
||||
- kvm: Fix eflags corruption in kvm mode (Jan Kiszka)
|
||||
- qcow2: Fix access after end of array (Kevin Wolf)
|
||||
- ide save/restore pio/atapi cmd transfer fields and io buffer (Marcelo Tosatti)
|
||||
- net: Monitor command set_link finds only VLAN clients, fix (Markus Armbruster)
|
||||
- net: info network shows only VLAN clients, fix (Markus Armbruster)
|
||||
- net: net_check_clients() checks only VLAN clients, fix (Markus Armbruster)
|
||||
- net: Fix bogus "Warning: vlan 0 with no nics" with -device (Markus Armbruster)
|
||||
- net: net_check_clients() runs too early to see -device, fix (Markus Armbruster)
|
||||
- net: Remove unused net_client_uninit() (Markus Armbruster)
|
||||
- don't dereference NULL after failed strdup (Jim Meyering)
|
||||
- virtio-net: fix network stall under load (Tom Lendacky)
|
||||
- json: fix PRId64 on Win32 (Roy Tam)
|
||||
- fix inet_parse typo (Marcelo Tosatti)
|
||||
- iothread: fix vcpu stop with smp tcg (Marcelo Tosatti)
|
||||
- segfault due to buffer overrun in usb-serial (David S. Ahern)
|
||||
- qcow2: Fix signedness bugs (Kevin Wolf)
|
||||
- Do not ignore error, if open file failed (-serial /dev/tty) (Evgeniy Dushistov)
|
||||
- pc-bios: update to newer version of (stable) seabios (Anthony Liguori)
|
||||
- target-mips: fix ROTR and DROTR by zero (Aurelien Jarno)
|
||||
- target-mips: fix CpU exception for coprocessor 0 (Nathan Froyd)
|
||||
- tcg/mips: fix crash in tcg_out_qemu_ld() (Aurelien Jarno)
|
||||
- target-mips: don't call cpu_loop_exit() from helper.c (Aurelien Jarno)
|
||||
- virtio-blk: Fix error cases which ignored rerror/werror (Kevin Wolf)
|
||||
- virtio-blk: Fix restart after read error (Kevin Wolf)
|
||||
- virtio_blk: Factor virtio_blk_handle_request out (Kevin Wolf)
|
||||
- cirrus: Properly re-register cirrus_linear_io_addr on vram unmap (Jan Kiszka)
|
||||
- qcow2: Don't ignore qcow2_alloc_clusters return value (Kevin Wolf)
|
||||
- qcow2: Don't ignore update_refcount return value (Kevin Wolf)
|
||||
- qcow2: Allow updating no refcounts (Kevin Wolf)
|
||||
- qcow2: Improve error handling in update_refcount (Kevin Wolf)
|
||||
- qcow2: Fix error handling in grow_refcount_table (Kevin Wolf)
|
||||
- block: Return original error codes in bdrv_pread/write (Kevin Wolf)
|
||||
- qcow2: Return 0/-errno in qcow2_alloc_cluster_offset (Kevin Wolf)
|
||||
- qcow2: Return 0/-errno in get_cluster_table (Kevin Wolf)
|
||||
- qcow2: Fix error handling in qcow_save_vmstate (Kevin Wolf)
|
||||
- qcow2: Fix error handling in qcow2_grow_l1_table (Kevin Wolf)
|
||||
- win32/sdl: Fix toggle full screen (Herve Poussineau)
|
||||
- win32: pair qemu_memalign() with qemu_vfree() (Herve Poussineau)
|
||||
- vnc_refresh: calling vnc_update_client might free vs (Stefano Stabellini)
|
||||
- Musicpal: Fix descriptor walk in eth_send (Jan Kiszka)
|
||||
- Musicpal: Fix wm8750 I2C address (Jan Kiszka)
|
||||
- fix savevm command without id or tag (Marcelo Tosatti)
|
||||
- reduce number of reinjects on ACK (Gleb Natapov)
|
||||
- QMP: Fix asynchronous events delivery (Luiz Capitulino)
|
||||
- Documentation: Add missing documentation for qdev related command line options (Stefan Weil)
|
||||
- pc: add driver version compat properties (Gerd Hoffmann)
|
||||
- scsi: device version property (Gerd Hoffmann)
|
||||
- ide: device version property (Gerd Hoffmann)
|
||||
- QMP: Emit asynchronous events on all QMP monitors (Adam Litke)
|
||||
- Fix QEMU_WARN_UNUSED_RESULT (Kevin Wolf)
|
||||
|
||||
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)
|
||||
@@ -176,6 +16,11 @@ version 0.10.2:
|
||||
|
||||
version 0.10.1:
|
||||
|
||||
- virtio-net: allow masking of notifications on empty queue (Alex Williamson)
|
||||
- e1000: fix rx descriptor low threshold logic (Alex Willaimson)
|
||||
- x86 tcg: add NULL checks to lsl instruction (Jan Kiszka)
|
||||
- kvm vga: fix screen corruption with -std-vga and Windows (Avi Kivity)
|
||||
- kvm vga: fix screen corruption with Ubuntu installations (Glauber Costa)
|
||||
- virtio-net: check right return size on sg list (Alex Williamson)
|
||||
- Make qemu_announce_self handle holes (live migration after hotplug)
|
||||
(Marcelo Tosatti)
|
||||
|
||||
+1
-7
@@ -19,8 +19,6 @@ M68K Paul Brook
|
||||
SH4 ?
|
||||
CRIS Edgar E. Iglesias
|
||||
Alpha ?
|
||||
MicroBlaze Edgar E. Iglesias
|
||||
S390 ?
|
||||
|
||||
Machines (sorted by CPU):
|
||||
-------------------------
|
||||
@@ -60,12 +58,7 @@ SH4
|
||||
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:
|
||||
-------------------
|
||||
@@ -73,6 +66,7 @@ Generic Subsystems:
|
||||
Dynamic translator Fabrice Bellard
|
||||
Main loop Fabrice Bellard (new maintainer needed)
|
||||
TCG Fabrice Bellard
|
||||
kqemu interface Fabrice Bellard
|
||||
IDE device ?
|
||||
SCSI device Paul Brook
|
||||
PCI layer ?
|
||||
|
||||
@@ -1,122 +1,68 @@
|
||||
# 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
|
||||
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
|
||||
recurse-all speed tar tarbin test
|
||||
|
||||
VPATH=$(SRC_PATH):$(SRC_PATH)/hw
|
||||
|
||||
LIBS+=-lz $(LIBS_TOOLS)
|
||||
|
||||
CFLAGS += $(OS_CFLAGS) $(ARCH_CFLAGS)
|
||||
LDFLAGS += $(OS_LDFLAGS) $(ARCH_LDFLAGS)
|
||||
|
||||
CPPFLAGS += -I. -I$(SRC_PATH) -MMD -MP -MT $@
|
||||
CPPFLAGS += -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE
|
||||
LIBS=
|
||||
ifdef CONFIG_STATIC
|
||||
LDFLAGS += -static
|
||||
endif
|
||||
ifdef BUILD_DOCS
|
||||
DOCS=qemu-doc.html qemu-tech.html qemu.1 qemu-img.1 qemu-nbd.8
|
||||
else
|
||||
DOCS=
|
||||
endif
|
||||
|
||||
SUBDIR_MAKEFLAGS=$(if $(V),,--no-print-directory)
|
||||
SUBDIR_DEVICES_MAK=$(patsubst %, %/config-devices.mak, $(TARGET_DIRS))
|
||||
LIBS+=$(AIOLIBS)
|
||||
|
||||
config-all-devices.mak: $(SUBDIR_DEVICES_MAK)
|
||||
$(call quiet-command,cat $(SUBDIR_DEVICES_MAK) | grep =y | sort -u > $@," GEN $@")
|
||||
ifdef CONFIG_SOLARIS
|
||||
LIBS+=-lsocket -lnsl -lresolv
|
||||
endif
|
||||
|
||||
%/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
|
||||
ifdef CONFIG_WIN32
|
||||
LIBS+=-lwinmm -lws2_32 -liphlpapi
|
||||
endif
|
||||
|
||||
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
|
||||
all: $(TOOLS) $(DOCS) recurse-all
|
||||
|
||||
SUBDIR_RULES=$(patsubst %,subdir-%, $(TARGET_DIRS))
|
||||
|
||||
subdir-%: $(GENERATED_HEADERS)
|
||||
$(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C $* V="$(V)" TARGET_DIR="$*/" all,)
|
||||
subdir-%:
|
||||
$(call quiet-command,$(MAKE) -C $* V="$(V)" TARGET_DIR="$*/" all,)
|
||||
|
||||
$(filter %-softmmu,$(SUBDIR_RULES)): libqemu_common.a
|
||||
$(filter %-user,$(SUBDIR_RULES)): libqemu_user.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)
|
||||
recurse-all: $(SUBDIR_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_OBJS is code used by both qemu system emulation and qemu-img
|
||||
|
||||
#######################################################################
|
||||
# block-obj-y is code used by both qemu system emulation and qemu-img
|
||||
BLOCK_OBJS=cutils.o qemu-malloc.o
|
||||
BLOCK_OBJS+=block-cow.o block-qcow.o aes.o block-vmdk.o block-cloop.o
|
||||
BLOCK_OBJS+=block-dmg.o block-bochs.o block-vpc.o block-vvfat.o
|
||||
BLOCK_OBJS+=block-qcow2.o block-parallels.o block-nbd.o
|
||||
BLOCK_OBJS+=nbd.o block.o aio.o
|
||||
|
||||
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))
|
||||
ifdef CONFIG_WIN32
|
||||
BLOCK_OBJS += block-raw-win32.o
|
||||
else
|
||||
ifdef CONFIG_AIO
|
||||
BLOCK_OBJS += posix-aio-compat.o
|
||||
endif
|
||||
BLOCK_OBJS += block-raw-posix.o
|
||||
endif
|
||||
|
||||
######################################################################
|
||||
# libqemu_common.a: Target independent part of system emulation. The
|
||||
@@ -124,150 +70,139 @@ net-obj-y += $(addprefix net/, $(net-nested-y))
|
||||
# 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
|
||||
OBJS=$(BLOCK_OBJS)
|
||||
OBJS+=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
|
||||
OBJS+=irq.o
|
||||
OBJS+=i2c.o smbus.o smbus_eeprom.o max7310.o max111x.o wm8750.o
|
||||
OBJS+=ssd0303.o ssd0323.o ads7846.o stellaris_input.o twl92230.o
|
||||
OBJS+=tmp105.o lm832x.o
|
||||
OBJS+=scsi-disk.o cdrom.o
|
||||
OBJS+=scsi-generic.o
|
||||
OBJS+=usb.o usb-hub.o usb-$(HOST_USB).o usb-hid.o usb-msd.o usb-wacom.o
|
||||
OBJS+=usb-serial.o usb-net.o
|
||||
OBJS+=sd.o ssi-sd.o
|
||||
OBJS+=bt.o bt-host.o bt-vhci.o bt-l2cap.o bt-sdp.o bt-hci.o bt-hid.o usb-bt.o
|
||||
OBJS+=buffered_file.o migration.o migration-tcp.o net.o qemu-sockets.o
|
||||
OBJS+=qemu-char.o aio.o net-checksum.o savevm.o cache-utils.o
|
||||
|
||||
obj-$(CONFIG_BRLAPI) += baum.o
|
||||
obj-$(CONFIG_POSIX) += migration-exec.o migration-unix.o migration-fd.o
|
||||
ifdef CONFIG_BRLAPI
|
||||
OBJS+= baum.o
|
||||
LIBS+=-lbrlapi
|
||||
endif
|
||||
|
||||
audio/audio.o audio/fmodaudio.o: QEMU_CFLAGS += $(FMOD_CFLAGS)
|
||||
ifdef CONFIG_WIN32
|
||||
OBJS+=tap-win32.o
|
||||
else
|
||||
OBJS+=migration-exec.o
|
||||
endif
|
||||
|
||||
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))
|
||||
AUDIO_OBJS = audio.o noaudio.o wavaudio.o mixeng.o
|
||||
ifdef CONFIG_SDL
|
||||
AUDIO_OBJS += sdlaudio.o
|
||||
endif
|
||||
ifdef CONFIG_OSS
|
||||
AUDIO_OBJS += ossaudio.o
|
||||
endif
|
||||
ifdef CONFIG_COREAUDIO
|
||||
AUDIO_OBJS += coreaudio.o
|
||||
AUDIO_PT = yes
|
||||
endif
|
||||
ifdef CONFIG_ALSA
|
||||
AUDIO_OBJS += alsaaudio.o
|
||||
endif
|
||||
ifdef CONFIG_DSOUND
|
||||
AUDIO_OBJS += dsoundaudio.o
|
||||
endif
|
||||
ifdef CONFIG_FMOD
|
||||
AUDIO_OBJS += fmodaudio.o
|
||||
audio/audio.o audio/fmodaudio.o: CPPFLAGS := -I$(CONFIG_FMOD_INC) $(CPPFLAGS)
|
||||
endif
|
||||
ifdef CONFIG_ESD
|
||||
AUDIO_PT = yes
|
||||
AUDIO_PT_INT = yes
|
||||
AUDIO_OBJS += esdaudio.o
|
||||
endif
|
||||
ifdef CONFIG_PA
|
||||
AUDIO_PT = yes
|
||||
AUDIO_PT_INT = yes
|
||||
AUDIO_OBJS += paaudio.o
|
||||
endif
|
||||
ifdef AUDIO_PT
|
||||
LDFLAGS += -pthread
|
||||
endif
|
||||
ifdef AUDIO_PT_INT
|
||||
AUDIO_OBJS += audio_pt_int.o
|
||||
endif
|
||||
AUDIO_OBJS+= wavcapture.o
|
||||
OBJS+=$(addprefix audio/, $(AUDIO_OBJS))
|
||||
|
||||
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
|
||||
ifdef CONFIG_SDL
|
||||
OBJS+=sdl.o x_keymap.o
|
||||
endif
|
||||
ifdef CONFIG_CURSES
|
||||
OBJS+=curses.o
|
||||
endif
|
||||
OBJS+=vnc.o d3des.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))
|
||||
ifdef CONFIG_COCOA
|
||||
OBJS+=cocoa.o
|
||||
endif
|
||||
|
||||
# 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
|
||||
ifdef CONFIG_SLIRP
|
||||
CPPFLAGS+=-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
|
||||
OBJS+=$(addprefix slirp/, $(SLIRP_OBJS))
|
||||
endif
|
||||
|
||||
QEMU_CFLAGS+=$(CURL_CFLAGS)
|
||||
LIBS+=$(VDE_LIBS)
|
||||
|
||||
cocoa.o: cocoa.m
|
||||
|
||||
keymaps.o: keymaps.c keymaps.h
|
||||
sdl.o: sdl.c keymaps.c sdl_keysym.h
|
||||
|
||||
sdl_zoom.o: sdl_zoom.c sdl_zoom.h sdl_zoom_template.h
|
||||
sdl.o audio/sdlaudio.o: CFLAGS += $(SDL_CFLAGS)
|
||||
|
||||
sdl.o: sdl.c keymaps.h sdl_keysym.h sdl_zoom.h
|
||||
vnc.o: vnc.c keymaps.c sdl_keysym.h vnchextile.h d3des.c d3des.h
|
||||
|
||||
sdl.o audio/sdlaudio.o sdl_zoom.o baum.o: QEMU_CFLAGS += $(SDL_CFLAGS)
|
||||
vnc.o: CFLAGS += $(CONFIG_VNC_TLS_CFLAGS)
|
||||
|
||||
acl.o: acl.h acl.c
|
||||
curses.o: curses.c keymaps.c curses_keys.h
|
||||
|
||||
vnc.h: vnc-tls.h vnc-auth-vencrypt.h vnc-auth-sasl.h keymaps.h
|
||||
bt-host.o: CFLAGS += $(CONFIG_BLUEZ_CFLAGS)
|
||||
|
||||
vnc.o: vnc.c vnc.h vnc_keysym.h vnchextile.h d3des.c d3des.h acl.h
|
||||
libqemu_common.a: $(OBJS)
|
||||
|
||||
vnc.o: QEMU_CFLAGS += $(VNC_TLS_CFLAGS)
|
||||
#######################################################################
|
||||
# USER_OBJS is code used by qemu userspace emulation
|
||||
USER_OBJS=cutils.o cache-utils.o
|
||||
|
||||
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)
|
||||
libqemu_user.a: $(USER_OBJS)
|
||||
|
||||
######################################################################
|
||||
|
||||
qemu-img.o: qemu-img-cmds.h
|
||||
qemu-img$(EXESUF): qemu-img.o qemu-tool.o osdep.o $(BLOCK_OBJS)
|
||||
|
||||
qemu-img$(EXESUF): qemu-img.o qemu-tool.o $(block-obj-y) $(qobject-obj-y)
|
||||
qemu-nbd$(EXESUF): qemu-nbd.o qemu-tool.o osdep.o $(BLOCK_OBJS)
|
||||
|
||||
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
|
||||
qemu-img$(EXESUF) qemu-nbd$(EXESUF): LIBS += -lz
|
||||
|
||||
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 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 *.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 slirp/*.o slirp/*.d audio/*.o audio/*.d
|
||||
$(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 config-host.mak config-host.h $(DOCS)
|
||||
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 \
|
||||
for d in $(TARGET_DIRS); do \
|
||||
rm -rf $$d || exit 1 ; \
|
||||
done
|
||||
|
||||
@@ -278,40 +213,39 @@ 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
|
||||
pxe-ne2k_pci.bin pxe-rtl8139.bin pxe-pcnet.bin pxe-e1000.bin \
|
||||
bamboo.dtb
|
||||
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) -m 644 qemu.1 qemu-img.1 "$(DESTDIR)$(mandir)/man1"
|
||||
mkdir -p "$(DESTDIR)$(mandir)/man8"
|
||||
$(INSTALL) -m 644 qemu-nbd.8 "$(DESTDIR)$(mandir)/man8"
|
||||
endif
|
||||
|
||||
install: all $(if $(BUILD_DOCS),install-doc)
|
||||
$(INSTALL_DIR) "$(DESTDIR)$(bindir)"
|
||||
mkdir -p "$(DESTDIR)$(bindir)"
|
||||
ifneq ($(TOOLS),)
|
||||
$(INSTALL_PROG) $(STRIP_OPT) $(TOOLS) "$(DESTDIR)$(bindir)"
|
||||
$(INSTALL) -m 755 -s $(TOOLS) "$(DESTDIR)$(bindir)"
|
||||
endif
|
||||
ifneq ($(BLOBS),)
|
||||
$(INSTALL_DIR) "$(DESTDIR)$(datadir)"
|
||||
mkdir -p "$(DESTDIR)$(datadir)"
|
||||
set -e; for x in $(BLOBS); do \
|
||||
$(INSTALL_DATA) $(SRC_PATH)/pc-bios/$$x "$(DESTDIR)$(datadir)"; \
|
||||
$(INSTALL) -m 644 $(SRC_PATH)/pc-bios/$$x "$(DESTDIR)$(datadir)"; \
|
||||
done
|
||||
endif
|
||||
$(INSTALL_DIR) "$(DESTDIR)$(datadir)/keymaps"
|
||||
ifndef CONFIG_WIN32
|
||||
mkdir -p "$(DESTDIR)$(datadir)/keymaps"
|
||||
set -e; for x in $(KEYMAPS); do \
|
||||
$(INSTALL_DATA) $(SRC_PATH)/pc-bios/keymaps/$$x "$(DESTDIR)$(datadir)/keymaps"; \
|
||||
$(INSTALL) -m 644 $(SRC_PATH)/keymaps/$$x "$(DESTDIR)$(datadir)/keymaps"; \
|
||||
done
|
||||
endif
|
||||
for d in $(TARGET_DIRS); do \
|
||||
$(MAKE) -C $$d $@ || exit 1 ; \
|
||||
done
|
||||
@@ -320,9 +254,8 @@ endif
|
||||
test speed: all
|
||||
$(MAKE) -C tests $@
|
||||
|
||||
.PHONY: TAGS
|
||||
TAGS:
|
||||
find "$(SRC_PATH)" -name '*.[hc]' -print0 | xargs -0 etags
|
||||
etags *.[ch] tests/*.[ch]
|
||||
|
||||
cscope:
|
||||
rm -f ./cscope.*
|
||||
@@ -331,40 +264,25 @@ cscope:
|
||||
|
||||
# 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-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-img.1: qemu-img.texi
|
||||
$(SRC_PATH)/texi2pod.pl $< qemu-img.pod
|
||||
pod2man --section=1 --center=" " --release=" " qemu-img.pod > $@
|
||||
|
||||
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 $@")
|
||||
$(SRC_PATH)/texi2pod.pl $< qemu-nbd.pod
|
||||
pod2man --section=8 --center=" " --release=" " qemu-nbd.pod > $@
|
||||
|
||||
info: qemu-doc.info qemu-tech.info
|
||||
|
||||
@@ -372,7 +290,7 @@ 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
|
||||
qemu-doc.dvi qemu-doc.html qemu-doc.info: qemu-img.texi qemu-nbd.texi
|
||||
|
||||
VERSION ?= $(shell cat VERSION)
|
||||
FILE = qemu-$(VERSION)
|
||||
@@ -392,7 +310,6 @@ tarbin:
|
||||
$(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 \
|
||||
@@ -410,7 +327,6 @@ tarbin:
|
||||
$(bindir)/qemu-armeb \
|
||||
$(bindir)/qemu-cris \
|
||||
$(bindir)/qemu-m68k \
|
||||
$(bindir)/qemu-microblaze \
|
||||
$(bindir)/qemu-mips \
|
||||
$(bindir)/qemu-mipsel \
|
||||
$(bindir)/qemu-ppc \
|
||||
@@ -442,4 +358,4 @@ tarbin:
|
||||
$(mandir)/man8/qemu-nbd.8
|
||||
|
||||
# Include automatically generated dependency files
|
||||
-include $(wildcard *.d audio/*.d slirp/*.d block/*.d net/*.d)
|
||||
-include $(wildcard *.d audio/*.d slirp/*.d)
|
||||
-53
@@ -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)
|
||||
+634
-218
@@ -1,16 +1,62 @@
|
||||
# -*- 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 config.mak
|
||||
include $(SRC_PATH)/rules.mak
|
||||
|
||||
TARGET_BASE_ARCH:=$(TARGET_ARCH)
|
||||
ifeq ($(TARGET_ARCH), x86_64)
|
||||
TARGET_BASE_ARCH:=i386
|
||||
endif
|
||||
ifeq ($(TARGET_ARCH), mipsn32)
|
||||
TARGET_BASE_ARCH:=mips
|
||||
endif
|
||||
ifeq ($(TARGET_ARCH), mips64)
|
||||
TARGET_BASE_ARCH:=mips
|
||||
endif
|
||||
ifeq ($(TARGET_ARCH), ppc64)
|
||||
TARGET_BASE_ARCH:=ppc
|
||||
endif
|
||||
ifeq ($(TARGET_ARCH), ppc64h)
|
||||
TARGET_BASE_ARCH:=ppc
|
||||
endif
|
||||
ifeq ($(TARGET_ARCH), ppcemb)
|
||||
TARGET_BASE_ARCH:=ppc
|
||||
endif
|
||||
ifeq ($(TARGET_ARCH), sparc64)
|
||||
TARGET_BASE_ARCH:=sparc
|
||||
endif
|
||||
TARGET_PATH=$(SRC_PATH)/target-$(TARGET_BASE_ARCH)
|
||||
VPATH=$(SRC_PATH):$(TARGET_PATH):$(SRC_PATH)/hw
|
||||
QEMU_CFLAGS+= -I.. -I$(TARGET_PATH) -DNEED_CPU_H
|
||||
CPPFLAGS=-I. -I.. -I$(TARGET_PATH) -I$(SRC_PATH) -MMD -MT $@ -MP -DNEED_CPU_H
|
||||
#CFLAGS+=-Werror
|
||||
LIBS=
|
||||
# user emulator name
|
||||
ifndef TARGET_ARCH2
|
||||
TARGET_ARCH2=$(TARGET_ARCH)
|
||||
endif
|
||||
ifeq ($(TARGET_ARCH),arm)
|
||||
ifeq ($(TARGET_WORDS_BIGENDIAN),yes)
|
||||
TARGET_ARCH2=armeb
|
||||
endif
|
||||
endif
|
||||
ifeq ($(TARGET_ARCH),sh4)
|
||||
ifeq ($(TARGET_WORDS_BIGENDIAN),yes)
|
||||
TARGET_ARCH2=sh4eb
|
||||
endif
|
||||
endif
|
||||
ifeq ($(TARGET_ARCH),mips)
|
||||
ifneq ($(TARGET_WORDS_BIGENDIAN),yes)
|
||||
TARGET_ARCH2=mipsel
|
||||
endif
|
||||
endif
|
||||
ifeq ($(TARGET_ARCH),mipsn32)
|
||||
ifneq ($(TARGET_WORDS_BIGENDIAN),yes)
|
||||
TARGET_ARCH2=mipsn32el
|
||||
endif
|
||||
endif
|
||||
ifeq ($(TARGET_ARCH),mips64)
|
||||
ifneq ($(TARGET_WORDS_BIGENDIAN),yes)
|
||||
TARGET_ARCH2=mips64el
|
||||
endif
|
||||
endif
|
||||
|
||||
ifdef CONFIG_USER_ONLY
|
||||
# user emulator name
|
||||
@@ -26,47 +72,145 @@ endif
|
||||
|
||||
PROGS=$(QEMU_PROG)
|
||||
|
||||
# cc-option
|
||||
# Usage: CFLAGS+=$(call cc-option, $(CFLAGS), -falign-functions=0, -malign-functions=0)
|
||||
|
||||
cc-option = $(shell if $(CC) $(1) $(2) -S -o /dev/null -xc /dev/null \
|
||||
> /dev/null 2>&1; then echo "$(2)"; else echo "$(3)"; fi ;)
|
||||
|
||||
HELPER_CFLAGS=
|
||||
|
||||
ifeq ($(ARCH),i386)
|
||||
HELPER_CFLAGS+=-fomit-frame-pointer
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),sparc)
|
||||
CFLAGS+=-ffixed-g2 -ffixed-g3
|
||||
ifneq ($(CONFIG_SOLARIS),yes)
|
||||
CFLAGS+=-ffixed-g1 -ffixed-g6
|
||||
HELPER_CFLAGS+=-ffixed-i0
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),sparc64)
|
||||
ifneq ($(CONFIG_SOLARIS),yes)
|
||||
CFLAGS+=-ffixed-g5 -ffixed-g6 -ffixed-g7
|
||||
else
|
||||
CFLAGS+=-ffixed-g1 -ffixed-g4 -ffixed-g5 -ffixed-g7
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),alpha)
|
||||
# Ensure there's only a single GP
|
||||
CFLAGS+=-msmall-data
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),hppa)
|
||||
BASE_LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),ia64)
|
||||
CFLAGS+=-mno-sdata
|
||||
endif
|
||||
|
||||
CFLAGS+=$(OS_CFLAGS) $(ARCH_CFLAGS)
|
||||
LDFLAGS+=$(OS_LDFLAGS) $(ARCH_LDFLAGS)
|
||||
|
||||
CPPFLAGS+=-D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE
|
||||
LIBS+=-lm
|
||||
ifdef CONFIG_WIN32
|
||||
LIBS+=-lwinmm -lws2_32 -liphlpapi
|
||||
endif
|
||||
ifdef CONFIG_SOLARIS
|
||||
LIBS+=-lsocket -lnsl -lresolv
|
||||
ifdef NEEDS_LIBSUNMATH
|
||||
LIBS+=-lsunmath
|
||||
LDFLAGS+=-L/opt/SUNWspro/prod/lib -R/opt/SUNWspro/prod/lib
|
||||
CFLAGS+=-I/opt/SUNWspro/prod/include/cc
|
||||
endif
|
||||
endif
|
||||
|
||||
kvm.o kvm-all.o: QEMU_CFLAGS+=$(KVM_CFLAGS)
|
||||
|
||||
config-target.h: config-target.h-timestamp
|
||||
config-target.h-timestamp: config-target.mak
|
||||
kvm.o: CFLAGS+=$(KVM_CFLAGS)
|
||||
kvm-all.o: CFLAGS+=$(KVM_CFLAGS)
|
||||
|
||||
all: $(PROGS)
|
||||
|
||||
# Dummy command so that make thinks it has done something
|
||||
@true
|
||||
|
||||
#########################################################
|
||||
# cpu emulator library
|
||||
libobj-y = exec.o translate-all.o cpu-exec.o translate.o
|
||||
libobj-y += tcg/tcg.o
|
||||
libobj-$(CONFIG_SOFTFLOAT) += fpu/softfloat.o
|
||||
libobj-$(CONFIG_NOSOFTFLOAT) += fpu/softfloat-native.o
|
||||
libobj-y += op_helper.o helper.o
|
||||
libobj-$(CONFIG_NEED_MMU) += mmu.o
|
||||
libobj-$(TARGET_ARM) += neon_helper.o iwmmxt_helper.o
|
||||
libobj-$(TARGET_ALPHA) += alpha_palcode.o
|
||||
LIBOBJS=exec.o kqemu.o translate-all.o cpu-exec.o\
|
||||
translate.o host-utils.o
|
||||
# TCG code generator
|
||||
LIBOBJS+= tcg/tcg.o tcg/tcg-runtime.o
|
||||
CPPFLAGS+=-I$(SRC_PATH)/tcg -I$(SRC_PATH)/tcg/$(ARCH)
|
||||
ifeq ($(ARCH),sparc64)
|
||||
CPPFLAGS+=-I$(SRC_PATH)/tcg/sparc
|
||||
endif
|
||||
ifdef CONFIG_SOFTFLOAT
|
||||
LIBOBJS+=fpu/softfloat.o
|
||||
else
|
||||
LIBOBJS+=fpu/softfloat-native.o
|
||||
endif
|
||||
CPPFLAGS+=-I$(SRC_PATH)/fpu
|
||||
LIBOBJS+= op_helper.o helper.o
|
||||
|
||||
ifeq ($(TARGET_BASE_ARCH), arm)
|
||||
LIBOBJS+= neon_helper.o iwmmxt_helper.o
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET_BASE_ARCH), alpha)
|
||||
LIBOBJS+= alpha_palcode.o
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET_BASE_ARCH), cris)
|
||||
LIBOBJS+= cris-dis.o
|
||||
|
||||
ifndef CONFIG_USER_ONLY
|
||||
LIBOBJS+= mmu.o
|
||||
endif
|
||||
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
|
||||
LIBOBJS+=disas.o
|
||||
ifeq ($(findstring i386, $(TARGET_ARCH) $(ARCH)),i386)
|
||||
USE_I386_DIS=y
|
||||
endif
|
||||
ifeq ($(findstring x86_64, $(TARGET_ARCH) $(ARCH)),x86_64)
|
||||
USE_I386_DIS=y
|
||||
endif
|
||||
ifdef USE_I386_DIS
|
||||
LIBOBJS+=i386-dis.o
|
||||
endif
|
||||
ifeq ($(findstring alpha, $(TARGET_ARCH) $(ARCH)),alpha)
|
||||
LIBOBJS+=alpha-dis.o
|
||||
endif
|
||||
ifeq ($(findstring ppc, $(TARGET_BASE_ARCH) $(ARCH)),ppc)
|
||||
LIBOBJS+=ppc-dis.o
|
||||
endif
|
||||
ifeq ($(findstring mips, $(TARGET_BASE_ARCH) $(ARCH)),mips)
|
||||
LIBOBJS+=mips-dis.o
|
||||
endif
|
||||
ifeq ($(findstring sparc, $(TARGET_BASE_ARCH) $(ARCH)),sparc)
|
||||
LIBOBJS+=sparc-dis.o
|
||||
endif
|
||||
ifeq ($(findstring arm, $(TARGET_ARCH) $(ARCH)),arm)
|
||||
LIBOBJS+=arm-dis.o
|
||||
endif
|
||||
ifeq ($(findstring m68k, $(TARGET_ARCH) $(ARCH)),m68k)
|
||||
LIBOBJS+=m68k-dis.o
|
||||
endif
|
||||
ifeq ($(findstring sh4, $(TARGET_ARCH) $(ARCH)),sh4)
|
||||
LIBOBJS+=sh4-dis.o
|
||||
endif
|
||||
ifeq ($(findstring hppa, $(TARGET_BASE_ARCH) $(ARCH)),hppa)
|
||||
LIBOBJS+=hppa-dis.o
|
||||
endif
|
||||
ifeq ($(findstring s390, $(TARGET_ARCH) $(ARCH)),s390)
|
||||
LIBOBJS+=s390-dis.o
|
||||
endif
|
||||
|
||||
# libqemu
|
||||
|
||||
libqemu.a: $(libobj-y)
|
||||
libqemu.a: $(LIBOBJS)
|
||||
|
||||
translate.o: translate.c cpu.h
|
||||
|
||||
@@ -76,37 +220,145 @@ 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)
|
||||
op_helper.o: CFLAGS += $(HELPER_CFLAGS) $(I386_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)
|
||||
cpu-exec.o: CFLAGS += $(HELPER_CFLAGS)
|
||||
|
||||
#########################################################
|
||||
# Linux user emulator target
|
||||
|
||||
ifdef CONFIG_LINUX_USER
|
||||
|
||||
ifndef TARGET_ABI_DIR
|
||||
TARGET_ABI_DIR=$(TARGET_ARCH)
|
||||
endif
|
||||
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
|
||||
CPPFLAGS+=-I$(SRC_PATH)/linux-user -I$(SRC_PATH)/linux-user/$(TARGET_ABI_DIR)
|
||||
|
||||
obj-$(TARGET_HAS_BFLT) += flatload.o
|
||||
obj-$(TARGET_HAS_ELFLOAD32) += elfload32.o
|
||||
ifdef CONFIG_STATIC
|
||||
LDFLAGS+=-static
|
||||
endif
|
||||
|
||||
obj-$(TARGET_I386) += vm86.o
|
||||
ifeq ($(ARCH),i386)
|
||||
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)/$(ARCH).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
|
||||
|
||||
obj-i386-y += ioport-user.o
|
||||
ifeq ($(ARCH),x86_64)
|
||||
LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld
|
||||
endif
|
||||
|
||||
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
|
||||
ifeq ($(ARCH),ppc)
|
||||
LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld
|
||||
endif
|
||||
|
||||
obj-m68k-y += m68k-sim.o m68k-semi.o
|
||||
ifeq ($(ARCH),ppc64)
|
||||
LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld
|
||||
endif
|
||||
|
||||
ARLIBS=../libuser/libuser.a libqemu.a
|
||||
ifeq ($(ARCH),s390)
|
||||
LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),sparc)
|
||||
# -static is used to avoid g1/g3 usage by the dynamic linker
|
||||
LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld -static
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),sparc64)
|
||||
LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),alpha)
|
||||
LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),ia64)
|
||||
LDFLAGS+=-Wl,-G0 -Wl,-T,$(SRC_PATH)/$(ARCH).ld
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),arm)
|
||||
LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),m68k)
|
||||
LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),mips)
|
||||
ifeq ($(WORDS_BIGENDIAN),yes)
|
||||
LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld
|
||||
else
|
||||
LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH)el.ld
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),mips64)
|
||||
ifeq ($(WORDS_BIGENDIAN),yes)
|
||||
LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld
|
||||
else
|
||||
LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH)el.ld
|
||||
endif
|
||||
endif
|
||||
|
||||
# profiling code
|
||||
ifdef TARGET_GPROF
|
||||
LDFLAGS+=-p
|
||||
CFLAGS+=-p
|
||||
endif
|
||||
|
||||
OBJS= main.o syscall.o strace.o mmap.o signal.o path.o thunk.o \
|
||||
elfload.o linuxload.o uaccess.o envlist.o
|
||||
LIBS+= $(AIOLIBS)
|
||||
ifdef TARGET_HAS_BFLT
|
||||
OBJS+= flatload.o
|
||||
endif
|
||||
ifdef TARGET_HAS_ELFLOAD32
|
||||
OBJS+= elfload32.o
|
||||
elfload32.o: elfload.c
|
||||
endif
|
||||
|
||||
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
|
||||
ifeq ($(TARGET_ARCH), m68k)
|
||||
OBJS+= m68k-sim.o m68k-semi.o
|
||||
endif
|
||||
|
||||
ifdef CONFIG_GDBSTUB
|
||||
OBJS+=gdbstub.o gdbstub-xml.o
|
||||
endif
|
||||
|
||||
OBJS+= libqemu.a
|
||||
|
||||
# Note: this is a workaround. The real fix is to avoid compiling
|
||||
# cpu_signal_handler() in cpu-exec.c.
|
||||
signal.o: CFLAGS += $(HELPER_CFLAGS)
|
||||
|
||||
$(QEMU_PROG): $(OBJS) ../libqemu_user.a
|
||||
$(LINK)
|
||||
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
|
||||
|
||||
endif #CONFIG_LINUX_USER
|
||||
|
||||
@@ -116,19 +368,27 @@ endif #CONFIG_LINUX_USER
|
||||
ifdef CONFIG_DARWIN_USER
|
||||
|
||||
VPATH+=:$(SRC_PATH)/darwin-user
|
||||
QEMU_CFLAGS+=-I$(SRC_PATH)/darwin-user -I$(SRC_PATH)/darwin-user/$(TARGET_ARCH)
|
||||
CPPFLAGS+=-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
|
||||
OBJS= main.o commpage.o machload.o mmap.o signal.o syscall.o thunk.o
|
||||
|
||||
obj-i386-y += ioport-user.o
|
||||
OBJS+= libqemu.a
|
||||
|
||||
ARLIBS=../libuser/libuser.a libqemu.a
|
||||
ifdef CONFIG_GDBSTUB
|
||||
OBJS+=gdbstub.o gdbstub-xml.o
|
||||
endif
|
||||
|
||||
# Note: this is a workaround. The real fix is to avoid compiling
|
||||
# cpu_signal_handler() in cpu-exec.c.
|
||||
signal.o: CFLAGS += $(HELPER_CFLAGS)
|
||||
|
||||
$(QEMU_PROG): $(OBJS)
|
||||
$(LINK)
|
||||
|
||||
endif #CONFIG_DARWIN_USER
|
||||
|
||||
@@ -138,198 +398,354 @@ endif #CONFIG_DARWIN_USER
|
||||
ifdef CONFIG_BSD_USER
|
||||
|
||||
VPATH+=:$(SRC_PATH)/bsd-user
|
||||
QEMU_CFLAGS+=-I$(SRC_PATH)/bsd-user -I$(SRC_PATH)/bsd-user/$(TARGET_ARCH)
|
||||
CPPFLAGS+=-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
|
||||
ifdef CONFIG_STATIC
|
||||
LDFLAGS+=-static
|
||||
endif
|
||||
|
||||
obj-i386-y += ioport-user.o
|
||||
ifeq ($(ARCH),i386)
|
||||
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)/$(ARCH).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
|
||||
|
||||
ARLIBS=../libuser/libuser.a libqemu.a
|
||||
ifeq ($(ARCH),x86_64)
|
||||
LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),ppc)
|
||||
LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),ppc64)
|
||||
LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),s390)
|
||||
LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),sparc)
|
||||
# -static is used to avoid g1/g3 usage by the dynamic linker
|
||||
LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld -static
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),sparc64)
|
||||
LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),alpha)
|
||||
LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),ia64)
|
||||
LDFLAGS+=-Wl,-G0 -Wl,-T,$(SRC_PATH)/$(ARCH).ld
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),arm)
|
||||
LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),m68k)
|
||||
LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),mips)
|
||||
ifeq ($(WORDS_BIGENDIAN),yes)
|
||||
LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld
|
||||
else
|
||||
LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH)el.ld
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),mips64)
|
||||
ifeq ($(WORDS_BIGENDIAN),yes)
|
||||
LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld
|
||||
else
|
||||
LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH)el.ld
|
||||
endif
|
||||
endif
|
||||
|
||||
OBJS= main.o bsdload.o elfload.o mmap.o path.o signal.o strace.o syscall.o
|
||||
OBJS+= uaccess.o
|
||||
|
||||
OBJS+= libqemu.a
|
||||
|
||||
ifdef CONFIG_GDBSTUB
|
||||
OBJS+=gdbstub.o
|
||||
endif
|
||||
|
||||
# Note: this is a workaround. The real fix is to avoid compiling
|
||||
# cpu_signal_handler() in cpu-exec.c.
|
||||
signal.o: CFLAGS += $(HELPER_CFLAGS)
|
||||
|
||||
$(QEMU_PROG): $(OBJS) ../libqemu_user.a
|
||||
$(LINK)
|
||||
|
||||
endif #CONFIG_BSD_USER
|
||||
|
||||
#########################################################
|
||||
# System emulator target
|
||||
ifdef CONFIG_SOFTMMU
|
||||
ifndef CONFIG_USER_ONLY
|
||||
|
||||
obj-y = vl.o async.o monitor.o pci.o pci_host.o pcie_host.o machine.o gdbstub.o
|
||||
OBJS=vl.o osdep.o monitor.o pci.o loader.o isa_mmio.o machine.o dma-helpers.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
|
||||
|
||||
# USB layer
|
||||
obj-$(CONFIG_USB_OHCI) += usb-ohci.o
|
||||
|
||||
# PCI network cards
|
||||
obj-y += eepro100.o
|
||||
obj-y += pcnet.o
|
||||
obj-y += rtl8139.o
|
||||
obj-y += e1000.o
|
||||
|
||||
# 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
|
||||
|
||||
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
|
||||
OBJS+=virtio.o virtio-blk.o virtio-balloon.o virtio-net.o virtio-console.o
|
||||
OBJS+=fw_cfg.o
|
||||
ifdef CONFIG_KVM
|
||||
OBJS+=kvm.o kvm-all.o
|
||||
endif
|
||||
ifdef CONFIG_WIN32
|
||||
OBJS+=block-raw-win32.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
|
||||
ifdef CONFIG_AIO
|
||||
OBJS+=posix-aio-compat.o
|
||||
endif
|
||||
OBJS+=block-raw-posix.o
|
||||
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
|
||||
LIBS+=-lz
|
||||
ifdef CONFIG_ALSA
|
||||
LIBS += -lasound
|
||||
endif
|
||||
ifdef CONFIG_ESD
|
||||
LIBS += -lesd
|
||||
endif
|
||||
ifdef CONFIG_PA
|
||||
LIBS += -lpulse-simple
|
||||
endif
|
||||
ifdef CONFIG_DSOUND
|
||||
LIBS += -lole32 -ldxguid
|
||||
endif
|
||||
ifdef CONFIG_FMOD
|
||||
LIBS += $(CONFIG_FMOD_LIB)
|
||||
endif
|
||||
ifdef CONFIG_OSS
|
||||
LIBS += $(CONFIG_OSS_LIB)
|
||||
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
|
||||
SOUND_HW = sb16.o es1370.o ac97.o
|
||||
ifdef CONFIG_ADLIB
|
||||
SOUND_HW += fmopl.o adlib.o
|
||||
adlib.o fmopl.o: CFLAGS := ${CFLAGS} -DBUILD_Y8950=0
|
||||
endif
|
||||
ifdef CONFIG_GUS
|
||||
SOUND_HW += gus.o gusemu_hal.o gusemu_mixer.o
|
||||
endif
|
||||
ifdef CONFIG_CS4231A
|
||||
SOUND_HW += cs4231a.o
|
||||
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_VNC_TLS
|
||||
CPPFLAGS += $(CONFIG_VNC_TLS_CFLAGS)
|
||||
LIBS += $(CONFIG_VNC_TLS_LIBS)
|
||||
endif
|
||||
|
||||
obj-s390x-y = s390-virtio-bus.o s390-virtio.o
|
||||
ifdef CONFIG_BLUEZ
|
||||
LIBS += $(CONFIG_BLUEZ_LIBS)
|
||||
endif
|
||||
|
||||
main.o vl.o: QEMU_CFLAGS+=$(GPROF_CFLAGS)
|
||||
# SCSI layer
|
||||
OBJS+= lsi53c895a.o esp.o
|
||||
|
||||
vl.o: QEMU_CFLAGS+=$(SDL_CFLAGS)
|
||||
# USB layer
|
||||
OBJS+= usb-ohci.o
|
||||
|
||||
vl.o: qemu-options.h
|
||||
# EEPROM emulation
|
||||
OBJS += eeprom93xx.o
|
||||
|
||||
monitor.o: qemu-monitor.h
|
||||
# PCI network cards
|
||||
OBJS += eepro100.o
|
||||
OBJS += ne2000.o
|
||||
OBJS += pcnet.o
|
||||
OBJS += rtl8139.o
|
||||
OBJS += e1000.o
|
||||
|
||||
ARLIBS=../libqemu_common.a libqemu.a $(HWLIB)
|
||||
# Serial mouse
|
||||
OBJS += msmouse.o
|
||||
|
||||
endif # CONFIG_SOFTMMU
|
||||
ifeq ($(TARGET_BASE_ARCH), i386)
|
||||
# Hardware support
|
||||
OBJS+= ide.o pckbd.o ps2.o vga.o $(SOUND_HW) dma.o
|
||||
OBJS+= fdc.o mc146818rtc.o serial.o i8259.o i8254.o pcspk.o pc.o
|
||||
OBJS+= cirrus_vga.o apic.o parallel.o acpi.o piix_pci.o
|
||||
OBJS+= usb-uhci.o vmmouse.o vmport.o vmware_vga.o hpet.o
|
||||
OBJS += device-hotplug.o pci-hotplug.o
|
||||
CPPFLAGS += -DHAS_AUDIO -DHAS_AUDIO_CHOICE
|
||||
endif
|
||||
ifeq ($(TARGET_BASE_ARCH), ppc)
|
||||
CPPFLAGS += -DHAS_AUDIO -DHAS_AUDIO_CHOICE
|
||||
# shared objects
|
||||
OBJS+= ppc.o ide.o vga.o $(SOUND_HW) dma.o openpic.o
|
||||
# PREP target
|
||||
OBJS+= pckbd.o ps2.o serial.o i8259.o i8254.o fdc.o m48t59.o mc146818rtc.o
|
||||
OBJS+= prep_pci.o ppc_prep.o
|
||||
# Mac shared devices
|
||||
OBJS+= macio.o cuda.o adb.o mac_nvram.o mac_dbdma.o escc.o
|
||||
# OldWorld PowerMac
|
||||
OBJS+= heathrow_pic.o grackle_pci.o ppc_oldworld.o
|
||||
# NewWorld PowerMac
|
||||
OBJS+= unin_pci.o ppc_newworld.o
|
||||
# PowerPC 4xx boards
|
||||
OBJS+= pflash_cfi02.o ppc4xx_devs.o ppc4xx_pci.o ppc405_uc.o ppc405_boards.o
|
||||
OBJS+= ppc440.o ppc440_bamboo.o
|
||||
# PowerPC E500 boards
|
||||
OBJS+= ppce500_pci.o ppce500_mpc8544ds.o
|
||||
ifdef FDT_LIBS
|
||||
OBJS+= device_tree.o
|
||||
LIBS+= $(FDT_LIBS)
|
||||
endif
|
||||
ifdef CONFIG_KVM
|
||||
OBJS+= kvm_ppc.o
|
||||
endif
|
||||
endif
|
||||
ifeq ($(TARGET_BASE_ARCH), mips)
|
||||
OBJS+= mips_r4k.o mips_jazz.o mips_malta.o mips_mipssim.o
|
||||
OBJS+= mips_timer.o mips_int.o dma.o vga.o serial.o i8254.o i8259.o rc4030.o
|
||||
OBJS+= g364fb.o jazz_led.o
|
||||
OBJS+= ide.o gt64xxx.o pckbd.o ps2.o fdc.o mc146818rtc.o usb-uhci.o acpi.o ds1225y.o
|
||||
OBJS+= piix_pci.o parallel.o cirrus_vga.o pcspk.o $(SOUND_HW)
|
||||
OBJS+= mipsnet.o
|
||||
OBJS+= pflash_cfi01.o
|
||||
OBJS+= vmware_vga.o
|
||||
CPPFLAGS += -DHAS_AUDIO -DHAS_AUDIO_CHOICE
|
||||
endif
|
||||
ifeq ($(TARGET_BASE_ARCH), cris)
|
||||
# Boards
|
||||
OBJS+= etraxfs.o axis_dev88.o
|
||||
|
||||
obj-$(CONFIG_GDBSTUB_XML) += gdbstub-xml.o
|
||||
# IO blocks
|
||||
OBJS+= etraxfs_dma.o
|
||||
OBJS+= etraxfs_pic.o
|
||||
OBJS+= etraxfs_eth.o
|
||||
OBJS+= etraxfs_timer.o
|
||||
OBJS+= etraxfs_ser.o
|
||||
|
||||
$(QEMU_PROG): $(obj-y) $(obj-$(TARGET_BASE_ARCH)-y) $(ARLIBS)
|
||||
$(call LINK,$(obj-y) $(obj-$(TARGET_BASE_ARCH)-y))
|
||||
OBJS+= ptimer.o
|
||||
OBJS+= pflash_cfi02.o nand.o
|
||||
endif
|
||||
ifeq ($(TARGET_BASE_ARCH), sparc)
|
||||
ifeq ($(TARGET_ARCH), sparc64)
|
||||
OBJS+= sun4u.o ide.o pckbd.o ps2.o vga.o apb_pci.o
|
||||
OBJS+= fdc.o mc146818rtc.o serial.o m48t59.o
|
||||
OBJS+= cirrus_vga.o parallel.o ptimer.o
|
||||
else
|
||||
OBJS+= sun4m.o tcx.o pcnet.o iommu.o m48t59.o slavio_intctl.o
|
||||
OBJS+= slavio_timer.o escc.o slavio_misc.o fdc.o sparc32_dma.o
|
||||
OBJS+= cs4231.o ptimer.o eccmemctl.o sbi.o sun4c_intctl.o
|
||||
endif
|
||||
endif
|
||||
ifeq ($(TARGET_BASE_ARCH), arm)
|
||||
OBJS+= integratorcp.o versatilepb.o ps2.o smc91c111.o arm_pic.o arm_timer.o
|
||||
OBJS+= arm_boot.o pl011.o pl031.o pl050.o pl080.o pl110.o pl181.o pl190.o
|
||||
OBJS+= versatile_pci.o ptimer.o
|
||||
OBJS+= realview_gic.o realview.o arm_sysctl.o mpcore.o
|
||||
OBJS+= armv7m.o armv7m_nvic.o stellaris.o pl022.o stellaris_enet.o
|
||||
OBJS+= pl061.o
|
||||
OBJS+= arm-semi.o
|
||||
OBJS+= pxa2xx.o pxa2xx_pic.o pxa2xx_gpio.o pxa2xx_timer.o pxa2xx_dma.o
|
||||
OBJS+= pxa2xx_lcd.o pxa2xx_mmci.o pxa2xx_pcmcia.o pxa2xx_keypad.o
|
||||
OBJS+= pflash_cfi01.o gumstix.o
|
||||
OBJS+= zaurus.o ide.o serial.o nand.o ecc.o spitz.o tosa.o tc6393xb.o
|
||||
OBJS+= omap1.o omap_lcdc.o omap_dma.o omap_clk.o omap_mmc.o omap_i2c.o
|
||||
OBJS+= omap2.o omap_dss.o soc_dma.o
|
||||
OBJS+= omap_sx1.o palm.o tsc210x.o
|
||||
OBJS+= nseries.o blizzard.o onenand.o vga.o cbus.o tusb6010.o usb-musb.o
|
||||
OBJS+= tsc2005.o bt-hci-csr.o
|
||||
OBJS+= mst_fpga.o mainstone.o
|
||||
OBJS+= musicpal.o pflash_cfi02.o
|
||||
CPPFLAGS += -DHAS_AUDIO
|
||||
endif
|
||||
ifeq ($(TARGET_BASE_ARCH), sh4)
|
||||
OBJS+= shix.o r2d.o sh7750.o sh7750_regnames.o tc58128.o
|
||||
OBJS+= sh_timer.o ptimer.o sh_serial.o sh_intc.o sh_pci.o sm501.o serial.o
|
||||
OBJS+= ide.o
|
||||
endif
|
||||
ifeq ($(TARGET_BASE_ARCH), m68k)
|
||||
OBJS+= an5206.o mcf5206.o ptimer.o mcf_uart.o mcf_intc.o mcf5208.o mcf_fec.o
|
||||
OBJS+= m68k-semi.o dummy_m68k.o
|
||||
endif
|
||||
ifdef CONFIG_GDBSTUB
|
||||
OBJS+=gdbstub.o gdbstub-xml.o
|
||||
endif
|
||||
ifdef CONFIG_COCOA
|
||||
COCOA_LIBS=-F/System/Library/Frameworks -framework Cocoa -framework IOKit
|
||||
ifdef CONFIG_COREAUDIO
|
||||
COCOA_LIBS+=-framework CoreAudio
|
||||
endif
|
||||
endif
|
||||
ifdef CONFIG_SLIRP
|
||||
CPPFLAGS+=-I$(SRC_PATH)/slirp
|
||||
endif
|
||||
|
||||
LIBS+=$(AIOLIBS)
|
||||
# specific flags are needed for non soft mmu emulator
|
||||
ifdef CONFIG_STATIC
|
||||
LDFLAGS+=-static
|
||||
endif
|
||||
ifndef CONFIG_DARWIN
|
||||
ifndef CONFIG_WIN32
|
||||
ifndef CONFIG_SOLARIS
|
||||
ifndef CONFIG_AIX
|
||||
LIBS+=-lutil
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
ifdef TARGET_GPROF
|
||||
vl.o: CFLAGS+=-p
|
||||
LDFLAGS+=-p
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),ia64)
|
||||
LDFLAGS+=-Wl,-G0 -Wl,-T,$(SRC_PATH)/ia64.ld
|
||||
endif
|
||||
|
||||
ifdef CONFIG_WIN32
|
||||
SDL_LIBS := $(filter-out -mwindows, $(SDL_LIBS)) -mconsole
|
||||
endif
|
||||
|
||||
# profiling code
|
||||
ifdef TARGET_GPROF
|
||||
LDFLAGS+=-p
|
||||
main.o: CFLAGS+=-p
|
||||
endif
|
||||
|
||||
$(QEMU_PROG): LIBS += $(SDL_LIBS) $(COCOA_LIBS) $(CURSES_LIBS) $(BRLAPI_LIBS) $(VDE_LIBS)
|
||||
|
||||
$(QEMU_PROG): $(OBJS) ../libqemu_common.a libqemu.a
|
||||
$(LINK)
|
||||
|
||||
endif # !CONFIG_USER_ONLY
|
||||
|
||||
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)$@")
|
||||
|
||||
qemu-options.h: $(SRC_PATH)/qemu-options.hx
|
||||
$(call quiet-command,sh $(SRC_PATH)/hxtool -h < $< > $@," GEN $(TARGET_DIR)$@")
|
||||
|
||||
qemu-monitor.h: $(SRC_PATH)/qemu-monitor.hx
|
||||
$(call quiet-command,sh $(SRC_PATH)/hxtool -h < $< > $@," GEN $(TARGET_DIR)$@")
|
||||
rm -f $@
|
||||
ifeq ($(TARGET_XML_FILES),)
|
||||
echo > $@
|
||||
else
|
||||
$(SHELL) $(SRC_PATH)/feature_to_c.sh $@ $(TARGET_XML_FILES)
|
||||
endif
|
||||
|
||||
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 *.d */*.d tcg/*.o
|
||||
|
||||
install: all
|
||||
ifneq ($(PROGS),)
|
||||
$(INSTALL) -m 755 $(STRIP_OPT) $(PROGS) "$(DESTDIR)$(bindir)"
|
||||
$(INSTALL) -m 755 -s $(PROGS) "$(DESTDIR)$(bindir)"
|
||||
endif
|
||||
|
||||
# Include automatically generated dependency files
|
||||
|
||||
@@ -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
@@ -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()
|
||||
@@ -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
@@ -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
@@ -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()
|
||||
@@ -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:
|
||||
*/
|
||||
@@ -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:
|
||||
*/
|
||||
@@ -34,10 +34,16 @@
|
||||
#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]))
|
||||
|
||||
@@ -13,13 +13,13 @@
|
||||
|
||||
#include "qemu-common.h"
|
||||
#include "block.h"
|
||||
#include "qemu-queue.h"
|
||||
#include "sys-queue.h"
|
||||
#include "qemu_socket.h"
|
||||
|
||||
typedef struct AioHandler AioHandler;
|
||||
|
||||
/* The list of registered AIO handlers */
|
||||
static QLIST_HEAD(, AioHandler) aio_handlers;
|
||||
static LIST_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
|
||||
@@ -33,20 +33,18 @@ struct AioHandler
|
||||
IOHandler *io_read;
|
||||
IOHandler *io_write;
|
||||
AioFlushHandler *io_flush;
|
||||
AioProcessQueue *io_process_queue;
|
||||
int deleted;
|
||||
void *opaque;
|
||||
QLIST_ENTRY(AioHandler) node;
|
||||
LIST_ENTRY(AioHandler) node;
|
||||
};
|
||||
|
||||
static AioHandler *find_aio_handler(int fd)
|
||||
{
|
||||
AioHandler *node;
|
||||
|
||||
QLIST_FOREACH(node, &aio_handlers, node) {
|
||||
LIST_FOREACH(node, &aio_handlers, node) {
|
||||
if (node->fd == fd)
|
||||
if (!node->deleted)
|
||||
return node;
|
||||
return node;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
@@ -56,7 +54,6 @@ 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;
|
||||
@@ -74,7 +71,7 @@ int qemu_aio_set_fd_handler(int fd,
|
||||
* deleted because deleted nodes are only cleaned up after
|
||||
* releasing the walking_handlers lock.
|
||||
*/
|
||||
QLIST_REMOVE(node, node);
|
||||
LIST_REMOVE(node, node);
|
||||
qemu_free(node);
|
||||
}
|
||||
}
|
||||
@@ -83,13 +80,12 @@ int qemu_aio_set_fd_handler(int fd,
|
||||
/* Alloc and insert if it's not already there */
|
||||
node = qemu_mallocz(sizeof(AioHandler));
|
||||
node->fd = fd;
|
||||
QLIST_INSERT_HEAD(&aio_handlers, node, node);
|
||||
LIST_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;
|
||||
}
|
||||
|
||||
@@ -106,36 +102,12 @@ void qemu_aio_flush(void)
|
||||
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) {
|
||||
LIST_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;
|
||||
qemu_aio_wait();
|
||||
} while (ret > 0);
|
||||
}
|
||||
|
||||
void qemu_aio_wait(void)
|
||||
@@ -145,13 +117,6 @@ void qemu_aio_wait(void)
|
||||
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;
|
||||
@@ -163,7 +128,7 @@ void qemu_aio_wait(void)
|
||||
FD_ZERO(&wrfds);
|
||||
|
||||
/* fill fd sets */
|
||||
QLIST_FOREACH(node, &aio_handlers, node) {
|
||||
LIST_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.
|
||||
@@ -198,7 +163,7 @@ void qemu_aio_wait(void)
|
||||
|
||||
/* we have to walk very carefully in case
|
||||
* qemu_aio_set_fd_handler is called while we're walking */
|
||||
node = QLIST_FIRST(&aio_handlers);
|
||||
node = LIST_FIRST(&aio_handlers);
|
||||
while (node) {
|
||||
AioHandler *tmp;
|
||||
|
||||
@@ -214,10 +179,10 @@ void qemu_aio_wait(void)
|
||||
}
|
||||
|
||||
tmp = node;
|
||||
node = QLIST_NEXT(node, node);
|
||||
node = LIST_NEXT(node, node);
|
||||
|
||||
if (tmp->deleted) {
|
||||
QLIST_REMOVE(tmp, node);
|
||||
LIST_REMOVE(tmp, node);
|
||||
qemu_free(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
+83
-40
@@ -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, 51 Franklin Street - Fifth Floor, Boston, MA
|
||||
02110-1301, USA. */
|
||||
|
||||
#include <stdio.h>
|
||||
#include "dis-asm.h"
|
||||
@@ -108,8 +109,8 @@ struct alpha_operand
|
||||
string (the operand will be inserted in any case). If the
|
||||
operand value is legal, *ERRMSG will be unchanged (most operands
|
||||
can accept any value). */
|
||||
unsigned (*insert) (unsigned instruction, int op,
|
||||
const char **errmsg);
|
||||
unsigned (*insert) PARAMS ((unsigned instruction, int op,
|
||||
const char **errmsg));
|
||||
|
||||
/* Extraction function. This is used by the disassembler. To
|
||||
extract this operand type from an instruction, check this field.
|
||||
@@ -128,7 +129,7 @@ struct alpha_operand
|
||||
non-zero if this operand type can not actually be extracted from
|
||||
this operand (i.e., the instruction does not match). If the
|
||||
operand is valid, *INVALID will not be changed. */
|
||||
int (*extract) (unsigned instruction, int *invalid);
|
||||
int (*extract) PARAMS ((unsigned instruction, int *invalid));
|
||||
};
|
||||
|
||||
/* Elements in the table are retrieved by indexing with values from
|
||||
@@ -158,7 +159,7 @@ extern const unsigned alpha_num_operands;
|
||||
instructions which want their operands to look like "Ra,disp(Rb)". */
|
||||
#define AXP_OPERAND_PARENS 02
|
||||
|
||||
/* Used in combination with PARENS, this suppresses the suppression of
|
||||
/* Used in combination with PARENS, this supresses the supression of
|
||||
the comma. This is used for "jmp Ra,(Rb),hint". */
|
||||
#define AXP_OPERAND_COMMA 04
|
||||
|
||||
@@ -179,7 +180,7 @@ extern const unsigned alpha_num_operands;
|
||||
a flags value of 0 can be treated as end-of-arguments. */
|
||||
#define AXP_OPERAND_UNSIGNED 0200
|
||||
|
||||
/* Suppress overflow detection on this field. This is used for hints. */
|
||||
/* Supress overflow detection on this field. This is used for hints. */
|
||||
#define AXP_OPERAND_NOOVERFLOW 0400
|
||||
|
||||
/* Mask for optional argument default value. */
|
||||
@@ -273,23 +274,23 @@ enum bfd_reloc_code_real {
|
||||
|
||||
/* Local insertion and extraction functions */
|
||||
|
||||
static unsigned insert_rba (unsigned, int, const char **);
|
||||
static unsigned insert_rca (unsigned, int, const char **);
|
||||
static unsigned insert_za (unsigned, int, const char **);
|
||||
static unsigned insert_zb (unsigned, int, const char **);
|
||||
static unsigned insert_zc (unsigned, int, const char **);
|
||||
static unsigned insert_bdisp (unsigned, int, const char **);
|
||||
static unsigned insert_jhint (unsigned, int, const char **);
|
||||
static unsigned insert_ev6hwjhint (unsigned, int, const char **);
|
||||
static unsigned insert_rba PARAMS((unsigned, int, const char **));
|
||||
static unsigned insert_rca PARAMS((unsigned, int, const char **));
|
||||
static unsigned insert_za PARAMS((unsigned, int, const char **));
|
||||
static unsigned insert_zb PARAMS((unsigned, int, const char **));
|
||||
static unsigned insert_zc PARAMS((unsigned, int, const char **));
|
||||
static unsigned insert_bdisp PARAMS((unsigned, int, const char **));
|
||||
static unsigned insert_jhint PARAMS((unsigned, int, const char **));
|
||||
static unsigned insert_ev6hwjhint PARAMS((unsigned, int, const char **));
|
||||
|
||||
static int extract_rba (unsigned, int *);
|
||||
static int extract_rca (unsigned, int *);
|
||||
static int extract_za (unsigned, int *);
|
||||
static int extract_zb (unsigned, int *);
|
||||
static int extract_zc (unsigned, int *);
|
||||
static int extract_bdisp (unsigned, int *);
|
||||
static int extract_jhint (unsigned, int *);
|
||||
static int extract_ev6hwjhint (unsigned, int *);
|
||||
static int extract_rba PARAMS((unsigned, int *));
|
||||
static int extract_rca PARAMS((unsigned, int *));
|
||||
static int extract_za PARAMS((unsigned, int *));
|
||||
static int extract_zb PARAMS((unsigned, int *));
|
||||
static int extract_zc PARAMS((unsigned, int *));
|
||||
static int extract_bdisp PARAMS((unsigned, int *));
|
||||
static int extract_jhint PARAMS((unsigned, int *));
|
||||
static int extract_ev6hwjhint PARAMS((unsigned, int *));
|
||||
|
||||
|
||||
/* The operands table */
|
||||
@@ -434,13 +435,18 @@ const unsigned alpha_num_operands = sizeof(alpha_operands)/sizeof(*alpha_operand
|
||||
|
||||
/*ARGSUSED*/
|
||||
static unsigned
|
||||
insert_rba(unsigned insn, int value ATTRIBUTE_UNUSED, const char **errmsg ATTRIBUTE_UNUSED)
|
||||
insert_rba(insn, value, errmsg)
|
||||
unsigned insn;
|
||||
int value ATTRIBUTE_UNUSED;
|
||||
const char **errmsg ATTRIBUTE_UNUSED;
|
||||
{
|
||||
return insn | (((insn >> 21) & 0x1f) << 16);
|
||||
}
|
||||
|
||||
static int
|
||||
extract_rba(unsigned insn, int *invalid)
|
||||
extract_rba(insn, invalid)
|
||||
unsigned insn;
|
||||
int *invalid;
|
||||
{
|
||||
if (invalid != (int *) NULL
|
||||
&& ((insn >> 21) & 0x1f) != ((insn >> 16) & 0x1f))
|
||||
@@ -453,13 +459,18 @@ extract_rba(unsigned insn, int *invalid)
|
||||
|
||||
/*ARGSUSED*/
|
||||
static unsigned
|
||||
insert_rca(unsigned insn, int value ATTRIBUTE_UNUSED, const char **errmsg ATTRIBUTE_UNUSED)
|
||||
insert_rca(insn, value, errmsg)
|
||||
unsigned insn;
|
||||
int value ATTRIBUTE_UNUSED;
|
||||
const char **errmsg ATTRIBUTE_UNUSED;
|
||||
{
|
||||
return insn | ((insn >> 21) & 0x1f);
|
||||
}
|
||||
|
||||
static int
|
||||
extract_rca(unsigned insn, int *invalid)
|
||||
extract_rca(insn, invalid)
|
||||
unsigned insn;
|
||||
int *invalid;
|
||||
{
|
||||
if (invalid != (int *) NULL
|
||||
&& ((insn >> 21) & 0x1f) != (insn & 0x1f))
|
||||
@@ -472,13 +483,18 @@ extract_rca(unsigned insn, int *invalid)
|
||||
|
||||
/*ARGSUSED*/
|
||||
static unsigned
|
||||
insert_za(unsigned insn, int value ATTRIBUTE_UNUSED, const char **errmsg ATTRIBUTE_UNUSED)
|
||||
insert_za(insn, value, errmsg)
|
||||
unsigned insn;
|
||||
int value ATTRIBUTE_UNUSED;
|
||||
const char **errmsg ATTRIBUTE_UNUSED;
|
||||
{
|
||||
return insn | (31 << 21);
|
||||
}
|
||||
|
||||
static int
|
||||
extract_za(unsigned insn, int *invalid)
|
||||
extract_za(insn, invalid)
|
||||
unsigned insn;
|
||||
int *invalid;
|
||||
{
|
||||
if (invalid != (int *) NULL && ((insn >> 21) & 0x1f) != 31)
|
||||
*invalid = 1;
|
||||
@@ -487,13 +503,18 @@ extract_za(unsigned insn, int *invalid)
|
||||
|
||||
/*ARGSUSED*/
|
||||
static unsigned
|
||||
insert_zb(unsigned insn, int value ATTRIBUTE_UNUSED, const char **errmsg ATTRIBUTE_UNUSED)
|
||||
insert_zb(insn, value, errmsg)
|
||||
unsigned insn;
|
||||
int value ATTRIBUTE_UNUSED;
|
||||
const char **errmsg ATTRIBUTE_UNUSED;
|
||||
{
|
||||
return insn | (31 << 16);
|
||||
}
|
||||
|
||||
static int
|
||||
extract_zb(unsigned insn, int *invalid)
|
||||
extract_zb(insn, invalid)
|
||||
unsigned insn;
|
||||
int *invalid;
|
||||
{
|
||||
if (invalid != (int *) NULL && ((insn >> 16) & 0x1f) != 31)
|
||||
*invalid = 1;
|
||||
@@ -502,13 +523,18 @@ extract_zb(unsigned insn, int *invalid)
|
||||
|
||||
/*ARGSUSED*/
|
||||
static unsigned
|
||||
insert_zc(unsigned insn, int value ATTRIBUTE_UNUSED, const char **errmsg ATTRIBUTE_UNUSED)
|
||||
insert_zc(insn, value, errmsg)
|
||||
unsigned insn;
|
||||
int value ATTRIBUTE_UNUSED;
|
||||
const char **errmsg ATTRIBUTE_UNUSED;
|
||||
{
|
||||
return insn | 31;
|
||||
}
|
||||
|
||||
static int
|
||||
extract_zc(unsigned insn, int *invalid)
|
||||
extract_zc(insn, invalid)
|
||||
unsigned insn;
|
||||
int *invalid;
|
||||
{
|
||||
if (invalid != (int *) NULL && (insn & 0x1f) != 31)
|
||||
*invalid = 1;
|
||||
@@ -519,7 +545,10 @@ extract_zc(unsigned insn, int *invalid)
|
||||
/* The displacement field of a Branch format insn. */
|
||||
|
||||
static unsigned
|
||||
insert_bdisp(unsigned insn, int value, const char **errmsg)
|
||||
insert_bdisp(insn, value, errmsg)
|
||||
unsigned insn;
|
||||
int value;
|
||||
const char **errmsg;
|
||||
{
|
||||
if (errmsg != (const char **)NULL && (value & 3))
|
||||
*errmsg = _("branch operand unaligned");
|
||||
@@ -528,7 +557,9 @@ insert_bdisp(unsigned insn, int value, const char **errmsg)
|
||||
|
||||
/*ARGSUSED*/
|
||||
static int
|
||||
extract_bdisp(unsigned insn, int *invalid ATTRIBUTE_UNUSED)
|
||||
extract_bdisp(insn, invalid)
|
||||
unsigned insn;
|
||||
int *invalid ATTRIBUTE_UNUSED;
|
||||
{
|
||||
return 4 * (((insn & 0x1FFFFF) ^ 0x100000) - 0x100000);
|
||||
}
|
||||
@@ -537,7 +568,10 @@ extract_bdisp(unsigned insn, int *invalid ATTRIBUTE_UNUSED)
|
||||
/* The hint field of a JMP/JSR insn. */
|
||||
|
||||
static unsigned
|
||||
insert_jhint(unsigned insn, int value, const char **errmsg)
|
||||
insert_jhint(insn, value, errmsg)
|
||||
unsigned insn;
|
||||
int value;
|
||||
const char **errmsg;
|
||||
{
|
||||
if (errmsg != (const char **)NULL && (value & 3))
|
||||
*errmsg = _("jump hint unaligned");
|
||||
@@ -546,7 +580,9 @@ insert_jhint(unsigned insn, int value, const char **errmsg)
|
||||
|
||||
/*ARGSUSED*/
|
||||
static int
|
||||
extract_jhint(unsigned insn, int *invalid ATTRIBUTE_UNUSED)
|
||||
extract_jhint(insn, invalid)
|
||||
unsigned insn;
|
||||
int *invalid ATTRIBUTE_UNUSED;
|
||||
{
|
||||
return 4 * (((insn & 0x3FFF) ^ 0x2000) - 0x2000);
|
||||
}
|
||||
@@ -554,7 +590,10 @@ extract_jhint(unsigned insn, int *invalid ATTRIBUTE_UNUSED)
|
||||
/* The hint field of an EV6 HW_JMP/JSR insn. */
|
||||
|
||||
static unsigned
|
||||
insert_ev6hwjhint(unsigned insn, int value, const char **errmsg)
|
||||
insert_ev6hwjhint(insn, value, errmsg)
|
||||
unsigned insn;
|
||||
int value;
|
||||
const char **errmsg;
|
||||
{
|
||||
if (errmsg != (const char **)NULL && (value & 3))
|
||||
*errmsg = _("jump hint unaligned");
|
||||
@@ -563,7 +602,9 @@ insert_ev6hwjhint(unsigned insn, int value, const char **errmsg)
|
||||
|
||||
/*ARGSUSED*/
|
||||
static int
|
||||
extract_ev6hwjhint(unsigned insn, int *invalid ATTRIBUTE_UNUSED)
|
||||
extract_ev6hwjhint(insn, invalid)
|
||||
unsigned insn;
|
||||
int *invalid ATTRIBUTE_UNUSED;
|
||||
{
|
||||
return 4 * (((insn & 0x1FFF) ^ 0x1000) - 0x1000);
|
||||
}
|
||||
@@ -1764,7 +1805,9 @@ static const char * const vms_regnames[64] = {
|
||||
/* Disassemble Alpha instructions. */
|
||||
|
||||
int
|
||||
print_insn_alpha (bfd_vma memaddr, struct disassemble_info *info)
|
||||
print_insn_alpha (memaddr, info)
|
||||
bfd_vma memaddr;
|
||||
struct disassemble_info *info;
|
||||
{
|
||||
static const struct alpha_opcode *opcode_index[AXP_NOPS+1];
|
||||
const char * const * regnames;
|
||||
|
||||
@@ -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: */
|
||||
|
||||
@@ -17,12 +17,15 @@
|
||||
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/>. */
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */
|
||||
|
||||
/* Start of qemu specific additions. Mostly this is stub definitions
|
||||
for things we don't care about. */
|
||||
|
||||
#include "dis-asm.h"
|
||||
#define FALSE 0
|
||||
#define TRUE (!FALSE)
|
||||
#define ATTRIBUTE_UNUSED __attribute__((unused))
|
||||
#define ISSPACE(x) ((x) == ' ' || (x) == '\t' || (x) == '\n')
|
||||
|
||||
@@ -1529,7 +1532,7 @@ static unsigned int regname_selected = 1;
|
||||
#define NUM_ARM_REGNAMES NUM_ELEM (regnames)
|
||||
#define arm_regnames regnames[regname_selected].reg_names
|
||||
|
||||
static bfd_boolean force_thumb = false;
|
||||
static bfd_boolean force_thumb = FALSE;
|
||||
|
||||
/* Current IT instruction state. This contains the same state as the IT
|
||||
bits in the CPSR. */
|
||||
@@ -1551,6 +1554,32 @@ enum map_type last_type;
|
||||
int last_mapping_sym = -1;
|
||||
bfd_vma last_mapping_addr = 0;
|
||||
|
||||
|
||||
/* Functions. */
|
||||
int
|
||||
get_arm_regname_num_options (void)
|
||||
{
|
||||
return NUM_ARM_REGNAMES;
|
||||
}
|
||||
|
||||
int
|
||||
set_arm_regname_option (int option)
|
||||
{
|
||||
int old = regname_selected;
|
||||
regname_selected = option;
|
||||
return old;
|
||||
}
|
||||
|
||||
int
|
||||
get_arm_regnames (int option, const char **setname, const char **setdescription,
|
||||
const char *const **register_names)
|
||||
{
|
||||
*setname = regnames[option].name;
|
||||
*setdescription = regnames[option].description;
|
||||
*register_names = regnames[option].reg_names;
|
||||
return 16;
|
||||
}
|
||||
|
||||
/* Decode a bitfield of the form matching regexp (N(-N)?,)*N(-N)?.
|
||||
Returns pointer to following character of the format string and
|
||||
fills in *VALUEP and *WIDTHP with the extracted value and number of
|
||||
@@ -1626,7 +1655,7 @@ arm_decode_shift (long given, fprintf_ftype func, void *stream,
|
||||
}
|
||||
|
||||
/* Print one coprocessor instruction on INFO->STREAM.
|
||||
Return true if the instuction matched, false if this is not a
|
||||
Return TRUE if the instuction matched, FALSE if this is not a
|
||||
recognised coprocessor instruction. */
|
||||
|
||||
static bfd_boolean
|
||||
@@ -2119,10 +2148,10 @@ print_insn_coprocessor (bfd_vma pc, struct disassemble_info *info, long given,
|
||||
else
|
||||
func (stream, "%c", *c);
|
||||
}
|
||||
return true;
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -2216,7 +2245,7 @@ print_arm_address (bfd_vma pc, struct disassemble_info *info, long given)
|
||||
}
|
||||
|
||||
/* Print one neon instruction on INFO->STREAM.
|
||||
Return true if the instuction matched, false if this is not a
|
||||
Return TRUE if the instuction matched, FALSE if this is not a
|
||||
recognised neon instruction. */
|
||||
|
||||
static bfd_boolean
|
||||
@@ -2242,7 +2271,7 @@ print_insn_neon (struct disassemble_info *info, long given, bfd_boolean thumb)
|
||||
else if ((given & 0xff000000) == 0xf9000000)
|
||||
given ^= 0xf9000000 ^ 0xf4000000;
|
||||
else
|
||||
return false;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
for (insn = neon_opcodes; insn->assembler; insn++)
|
||||
@@ -2332,34 +2361,34 @@ print_insn_neon (struct disassemble_info *info, long given, bfd_boolean thumb)
|
||||
{
|
||||
int amask = (1 << size) - 1;
|
||||
if ((idx_align & (1 << size)) != 0)
|
||||
return false;
|
||||
return FALSE;
|
||||
if (size > 0)
|
||||
{
|
||||
if ((idx_align & amask) == amask)
|
||||
align = 8 << size;
|
||||
else if ((idx_align & amask) != 0)
|
||||
return false;
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
if (size == 2 && (idx_align & 2) != 0)
|
||||
return false;
|
||||
return FALSE;
|
||||
align = (idx_align & 1) ? 16 << size : 0;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
if ((size == 2 && (idx_align & 3) != 0)
|
||||
|| (idx_align & 1) != 0)
|
||||
return false;
|
||||
return FALSE;
|
||||
break;
|
||||
|
||||
case 4:
|
||||
if (size == 2)
|
||||
{
|
||||
if ((idx_align & 3) == 3)
|
||||
return false;
|
||||
return FALSE;
|
||||
align = (idx_align & 3) * 64;
|
||||
}
|
||||
else
|
||||
@@ -2668,10 +2697,10 @@ print_insn_neon (struct disassemble_info *info, long given, bfd_boolean thumb)
|
||||
else
|
||||
func (stream, "%c", *c);
|
||||
}
|
||||
return true;
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Print one ARM instruction from PC on INFO->STREAM. */
|
||||
@@ -2683,10 +2712,10 @@ print_insn_arm_internal (bfd_vma pc, struct disassemble_info *info, long given)
|
||||
void *stream = info->stream;
|
||||
fprintf_ftype func = info->fprintf_func;
|
||||
|
||||
if (print_insn_coprocessor (pc, info, given, false))
|
||||
if (print_insn_coprocessor (pc, info, given, FALSE))
|
||||
return;
|
||||
|
||||
if (print_insn_neon (info, given, false))
|
||||
if (print_insn_neon (info, given, FALSE))
|
||||
return;
|
||||
|
||||
for (insn = arm_opcodes; insn->assembler; insn++)
|
||||
@@ -3319,10 +3348,10 @@ print_insn_thumb32 (bfd_vma pc, struct disassemble_info *info, long given)
|
||||
void *stream = info->stream;
|
||||
fprintf_ftype func = info->fprintf_func;
|
||||
|
||||
if (print_insn_coprocessor (pc, info, given, true))
|
||||
if (print_insn_coprocessor (pc, info, given, TRUE))
|
||||
return;
|
||||
|
||||
if (print_insn_neon (info, given, true))
|
||||
if (print_insn_neon (info, given, TRUE))
|
||||
return;
|
||||
|
||||
for (insn = thumb32_opcodes; insn->assembler; insn++)
|
||||
@@ -3457,7 +3486,7 @@ print_insn_thumb32 (bfd_vma pc, struct disassemble_info *info, long given)
|
||||
unsigned int op = (given & 0x00000f00) >> 8;
|
||||
unsigned int i12 = (given & 0x00000fff);
|
||||
unsigned int i8 = (given & 0x000000ff);
|
||||
bfd_boolean writeback = false, postind = false;
|
||||
bfd_boolean writeback = FALSE, postind = FALSE;
|
||||
int offset = 0;
|
||||
|
||||
func (stream, "[%s", arm_regnames[Rn]);
|
||||
@@ -3487,22 +3516,22 @@ print_insn_thumb32 (bfd_vma pc, struct disassemble_info *info, long given)
|
||||
|
||||
case 0xF: /* 8-bit + preindex with wb */
|
||||
offset = i8;
|
||||
writeback = true;
|
||||
writeback = TRUE;
|
||||
break;
|
||||
|
||||
case 0xD: /* 8-bit - preindex with wb */
|
||||
offset = -i8;
|
||||
writeback = true;
|
||||
writeback = TRUE;
|
||||
break;
|
||||
|
||||
case 0xB: /* 8-bit + postindex */
|
||||
offset = i8;
|
||||
postind = true;
|
||||
postind = TRUE;
|
||||
break;
|
||||
|
||||
case 0x9: /* 8-bit - postindex */
|
||||
offset = -i8;
|
||||
postind = true;
|
||||
postind = TRUE;
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -3875,12 +3904,12 @@ print_insn_arm (bfd_vma pc, struct disassemble_info *info)
|
||||
unsigned char b[4];
|
||||
long given;
|
||||
int status;
|
||||
int is_thumb = false;
|
||||
int is_data = false;
|
||||
int is_thumb = FALSE;
|
||||
int is_data = FALSE;
|
||||
unsigned int size = 4;
|
||||
void (*printer) (bfd_vma, struct disassemble_info *, long);
|
||||
#if 0
|
||||
bfd_boolean found = false;
|
||||
bfd_boolean found = FALSE;
|
||||
|
||||
if (info->disassembler_options)
|
||||
{
|
||||
@@ -3903,7 +3932,7 @@ print_insn_arm (bfd_vma pc, struct disassemble_info *info)
|
||||
if (pc <= last_mapping_addr)
|
||||
last_mapping_sym = -1;
|
||||
is_thumb = (last_type == MAP_THUMB);
|
||||
found = false;
|
||||
found = FALSE;
|
||||
/* Start scanning at the start of the function, or wherever
|
||||
we finished last time. */
|
||||
n = info->symtab_pos + 1;
|
||||
@@ -3921,7 +3950,7 @@ print_insn_arm (bfd_vma pc, struct disassemble_info *info)
|
||||
&& get_sym_code_type (info, n, &type))
|
||||
{
|
||||
last_sym = n;
|
||||
found = true;
|
||||
found = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3938,7 +3967,7 @@ print_insn_arm (bfd_vma pc, struct disassemble_info *info)
|
||||
if (get_sym_code_type (info, n, &type))
|
||||
{
|
||||
last_sym = n;
|
||||
found = true;
|
||||
found = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -4010,7 +4039,7 @@ print_insn_arm (bfd_vma pc, struct disassemble_info *info)
|
||||
#endif
|
||||
|
||||
if (force_thumb)
|
||||
is_thumb = true;
|
||||
is_thumb = TRUE;
|
||||
|
||||
info->bytes_per_line = 4;
|
||||
|
||||
@@ -4115,3 +4144,22 @@ print_insn_arm (bfd_vma pc, struct disassemble_info *info)
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
void
|
||||
print_arm_disassembler_options (FILE *stream)
|
||||
{
|
||||
int i;
|
||||
|
||||
fprintf (stream, _("\n\
|
||||
The following ARM specific disassembler options are supported for use with\n\
|
||||
the -M switch:\n"));
|
||||
|
||||
for (i = NUM_ARM_REGNAMES; i--;)
|
||||
fprintf (stream, " reg-names-%s %*c%s\n",
|
||||
regnames[i].name,
|
||||
(int)(14 - strlen (regnames[i].name)), ' ',
|
||||
regnames[i].description);
|
||||
|
||||
fprintf (stream, " force-thumb Assume all insns are Thumb insns\n");
|
||||
fprintf (stream, " no-force-thumb Examine preceeding label to determine an insn's type\n\n");
|
||||
}
|
||||
+3
-1
@@ -15,7 +15,9 @@
|
||||
* 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/>.
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
|
||||
* MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
@@ -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: */
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+146
-420
@@ -23,37 +23,21 @@
|
||||
*/
|
||||
#include <alsa/asoundlib.h>
|
||||
#include "qemu-common.h"
|
||||
#include "qemu-char.h"
|
||||
#include "audio.h"
|
||||
|
||||
#if QEMU_GNUC_PREREQ(4, 3)
|
||||
#pragma GCC diagnostic ignored "-Waddress"
|
||||
#endif
|
||||
|
||||
#define AUDIO_CAP "alsa"
|
||||
#include "audio_int.h"
|
||||
|
||||
struct pollhlp {
|
||||
snd_pcm_t *handle;
|
||||
struct pollfd *pfds;
|
||||
int count;
|
||||
int mask;
|
||||
};
|
||||
|
||||
typedef struct ALSAVoiceOut {
|
||||
HWVoiceOut hw;
|
||||
int wpos;
|
||||
int pending;
|
||||
void *pcm_buf;
|
||||
snd_pcm_t *handle;
|
||||
struct pollhlp pollhlp;
|
||||
} ALSAVoiceOut;
|
||||
|
||||
typedef struct ALSAVoiceIn {
|
||||
HWVoiceIn hw;
|
||||
snd_pcm_t *handle;
|
||||
void *pcm_buf;
|
||||
struct pollhlp pollhlp;
|
||||
} ALSAVoiceIn;
|
||||
|
||||
static struct {
|
||||
@@ -74,8 +58,7 @@ static struct {
|
||||
int period_size_out_overridden;
|
||||
int verbose;
|
||||
} conf = {
|
||||
.buffer_size_out = 4096,
|
||||
.period_size_out = 1024,
|
||||
.buffer_size_out = 1024,
|
||||
.pcm_name_out = "default",
|
||||
.pcm_name_in = "default",
|
||||
};
|
||||
@@ -127,23 +110,7 @@ static void GCC_FMT_ATTR (3, 4) alsa_logerr2 (
|
||||
AUD_log (AUDIO_CAP, "Reason: %s\n", snd_strerror (err));
|
||||
}
|
||||
|
||||
static void alsa_fini_poll (struct pollhlp *hlp)
|
||||
{
|
||||
int i;
|
||||
struct pollfd *pfds = hlp->pfds;
|
||||
|
||||
if (pfds) {
|
||||
for (i = 0; i < hlp->count; ++i) {
|
||||
qemu_set_fd_handler (pfds[i].fd, NULL, NULL, NULL);
|
||||
}
|
||||
qemu_free (pfds);
|
||||
}
|
||||
hlp->pfds = NULL;
|
||||
hlp->count = 0;
|
||||
hlp->handle = NULL;
|
||||
}
|
||||
|
||||
static void alsa_anal_close1 (snd_pcm_t **handlep)
|
||||
static void alsa_anal_close (snd_pcm_t **handlep)
|
||||
{
|
||||
int err = snd_pcm_close (*handlep);
|
||||
if (err) {
|
||||
@@ -152,163 +119,6 @@ static void alsa_anal_close1 (snd_pcm_t **handlep)
|
||||
*handlep = NULL;
|
||||
}
|
||||
|
||||
static void alsa_anal_close (snd_pcm_t **handlep, struct pollhlp *hlp)
|
||||
{
|
||||
alsa_fini_poll (hlp);
|
||||
alsa_anal_close1 (handlep);
|
||||
}
|
||||
|
||||
static int alsa_recover (snd_pcm_t *handle)
|
||||
{
|
||||
int err = snd_pcm_prepare (handle);
|
||||
if (err < 0) {
|
||||
alsa_logerr (err, "Failed to prepare handle %p\n", handle);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int alsa_resume (snd_pcm_t *handle)
|
||||
{
|
||||
int err = snd_pcm_resume (handle);
|
||||
if (err < 0) {
|
||||
alsa_logerr (err, "Failed to resume handle %p\n", handle);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void alsa_poll_handler (void *opaque)
|
||||
{
|
||||
int err, count;
|
||||
snd_pcm_state_t state;
|
||||
struct pollhlp *hlp = opaque;
|
||||
unsigned short revents;
|
||||
|
||||
count = poll (hlp->pfds, hlp->count, 0);
|
||||
if (count < 0) {
|
||||
dolog ("alsa_poll_handler: poll %s\n", strerror (errno));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!count) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* XXX: ALSA example uses initial count, not the one returned by
|
||||
poll, correct? */
|
||||
err = snd_pcm_poll_descriptors_revents (hlp->handle, hlp->pfds,
|
||||
hlp->count, &revents);
|
||||
if (err < 0) {
|
||||
alsa_logerr (err, "snd_pcm_poll_descriptors_revents");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(revents & hlp->mask)) {
|
||||
if (conf.verbose) {
|
||||
dolog ("revents = %d\n", revents);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
state = snd_pcm_state (hlp->handle);
|
||||
switch (state) {
|
||||
case SND_PCM_STATE_XRUN:
|
||||
alsa_recover (hlp->handle);
|
||||
break;
|
||||
|
||||
case SND_PCM_STATE_SUSPENDED:
|
||||
alsa_resume (hlp->handle);
|
||||
break;
|
||||
|
||||
case SND_PCM_STATE_PREPARED:
|
||||
audio_run ("alsa run (prepared)");
|
||||
break;
|
||||
|
||||
case SND_PCM_STATE_RUNNING:
|
||||
audio_run ("alsa run (running)");
|
||||
break;
|
||||
|
||||
default:
|
||||
dolog ("Unexpected state %d\n", state);
|
||||
}
|
||||
}
|
||||
|
||||
static int alsa_poll_helper (snd_pcm_t *handle, struct pollhlp *hlp, int mask)
|
||||
{
|
||||
int i, count, err;
|
||||
struct pollfd *pfds;
|
||||
|
||||
count = snd_pcm_poll_descriptors_count (handle);
|
||||
if (count <= 0) {
|
||||
dolog ("Could not initialize poll mode\n"
|
||||
"Invalid number of poll descriptors %d\n", count);
|
||||
return -1;
|
||||
}
|
||||
|
||||
pfds = audio_calloc ("alsa_poll_helper", count, sizeof (*pfds));
|
||||
if (!pfds) {
|
||||
dolog ("Could not initialize poll mode\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
err = snd_pcm_poll_descriptors (handle, pfds, count);
|
||||
if (err < 0) {
|
||||
alsa_logerr (err, "Could not initialize poll mode\n"
|
||||
"Could not obtain poll descriptors\n");
|
||||
qemu_free (pfds);
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 0; i < count; ++i) {
|
||||
if (pfds[i].events & POLLIN) {
|
||||
err = qemu_set_fd_handler (pfds[i].fd, alsa_poll_handler,
|
||||
NULL, hlp);
|
||||
}
|
||||
if (pfds[i].events & POLLOUT) {
|
||||
if (conf.verbose) {
|
||||
dolog ("POLLOUT %d %d\n", i, pfds[i].fd);
|
||||
}
|
||||
err = qemu_set_fd_handler (pfds[i].fd, NULL,
|
||||
alsa_poll_handler, hlp);
|
||||
}
|
||||
if (conf.verbose) {
|
||||
dolog ("Set handler events=%#x index=%d fd=%d err=%d\n",
|
||||
pfds[i].events, i, pfds[i].fd, err);
|
||||
}
|
||||
|
||||
if (err) {
|
||||
dolog ("Failed to set handler events=%#x index=%d fd=%d err=%d\n",
|
||||
pfds[i].events, i, pfds[i].fd, err);
|
||||
|
||||
while (i--) {
|
||||
qemu_set_fd_handler (pfds[i].fd, NULL, NULL, NULL);
|
||||
}
|
||||
qemu_free (pfds);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
hlp->pfds = pfds;
|
||||
hlp->count = count;
|
||||
hlp->handle = handle;
|
||||
hlp->mask = mask;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int alsa_poll_out (HWVoiceOut *hw)
|
||||
{
|
||||
ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
|
||||
|
||||
return alsa_poll_helper (alsa->handle, &alsa->pollhlp, POLLOUT);
|
||||
}
|
||||
|
||||
static int alsa_poll_in (HWVoiceIn *hw)
|
||||
{
|
||||
ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
|
||||
|
||||
return alsa_poll_helper (alsa->handle, &alsa->pollhlp, POLLIN);
|
||||
}
|
||||
|
||||
static int alsa_write (SWVoiceOut *sw, void *buf, int len)
|
||||
{
|
||||
return audio_pcm_sw_write (sw, buf, len);
|
||||
@@ -595,7 +405,7 @@ static int alsa_open (int in, struct alsa_params_req *req,
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (((req->override_mask & 1) && (obt - req->period_size)))
|
||||
if ((req->override_mask & 1) && (obt - req->period_size))
|
||||
dolog ("Requested period %s %u was rejected, using %lu\n",
|
||||
size_in_usec ? "time" : "size", req->period_size, obt);
|
||||
}
|
||||
@@ -675,10 +485,20 @@ static int alsa_open (int in, struct alsa_params_req *req,
|
||||
return 0;
|
||||
|
||||
err:
|
||||
alsa_anal_close1 (&handle);
|
||||
alsa_anal_close (&handle);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int alsa_recover (snd_pcm_t *handle)
|
||||
{
|
||||
int err = snd_pcm_prepare (handle);
|
||||
if (err < 0) {
|
||||
alsa_logerr (err, "Failed to prepare handle %p\n", handle);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static snd_pcm_sframes_t alsa_get_avail (snd_pcm_t *handle)
|
||||
{
|
||||
snd_pcm_sframes_t avail;
|
||||
@@ -701,75 +521,20 @@ static snd_pcm_sframes_t alsa_get_avail (snd_pcm_t *handle)
|
||||
return avail;
|
||||
}
|
||||
|
||||
static void alsa_write_pending (ALSAVoiceOut *alsa)
|
||||
{
|
||||
HWVoiceOut *hw = &alsa->hw;
|
||||
|
||||
while (alsa->pending) {
|
||||
int left_till_end_samples = hw->samples - alsa->wpos;
|
||||
int len = audio_MIN (alsa->pending, left_till_end_samples);
|
||||
char *src = advance (alsa->pcm_buf, alsa->wpos << hw->info.shift);
|
||||
|
||||
while (len) {
|
||||
snd_pcm_sframes_t written;
|
||||
|
||||
written = snd_pcm_writei (alsa->handle, src, len);
|
||||
|
||||
if (written <= 0) {
|
||||
switch (written) {
|
||||
case 0:
|
||||
if (conf.verbose) {
|
||||
dolog ("Failed to write %d frames (wrote zero)\n", len);
|
||||
}
|
||||
return;
|
||||
|
||||
case -EPIPE:
|
||||
if (alsa_recover (alsa->handle)) {
|
||||
alsa_logerr (written, "Failed to write %d frames\n",
|
||||
len);
|
||||
return;
|
||||
}
|
||||
if (conf.verbose) {
|
||||
dolog ("Recovering from playback xrun\n");
|
||||
}
|
||||
continue;
|
||||
|
||||
case -ESTRPIPE:
|
||||
/* stream is suspended and waiting for an
|
||||
application recovery */
|
||||
if (alsa_resume (alsa->handle)) {
|
||||
alsa_logerr (written, "Failed to write %d frames\n",
|
||||
len);
|
||||
return;
|
||||
}
|
||||
if (conf.verbose) {
|
||||
dolog ("Resuming suspended output stream\n");
|
||||
}
|
||||
continue;
|
||||
|
||||
case -EAGAIN:
|
||||
return;
|
||||
|
||||
default:
|
||||
alsa_logerr (written, "Failed to write %d frames from %p\n",
|
||||
len, src);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
alsa->wpos = (alsa->wpos + written) % hw->samples;
|
||||
alsa->pending -= written;
|
||||
len -= written;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int alsa_run_out (HWVoiceOut *hw, int live)
|
||||
static int alsa_run_out (HWVoiceOut *hw)
|
||||
{
|
||||
ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
|
||||
int decr;
|
||||
int rpos, live, decr;
|
||||
int samples;
|
||||
uint8_t *dst;
|
||||
struct st_sample *src;
|
||||
snd_pcm_sframes_t avail;
|
||||
|
||||
live = audio_pcm_hw_get_live_out (hw);
|
||||
if (!live) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
avail = alsa_get_avail (alsa->handle);
|
||||
if (avail < 0) {
|
||||
dolog ("Could not get number of available playback frames\n");
|
||||
@@ -777,9 +542,60 @@ static int alsa_run_out (HWVoiceOut *hw, int live)
|
||||
}
|
||||
|
||||
decr = audio_MIN (live, avail);
|
||||
decr = audio_pcm_hw_clip_out (hw, alsa->pcm_buf, decr, alsa->pending);
|
||||
alsa->pending += decr;
|
||||
alsa_write_pending (alsa);
|
||||
samples = decr;
|
||||
rpos = hw->rpos;
|
||||
while (samples) {
|
||||
int left_till_end_samples = hw->samples - rpos;
|
||||
int len = audio_MIN (samples, left_till_end_samples);
|
||||
snd_pcm_sframes_t written;
|
||||
|
||||
src = hw->mix_buf + rpos;
|
||||
dst = advance (alsa->pcm_buf, rpos << hw->info.shift);
|
||||
|
||||
hw->clip (dst, src, len);
|
||||
|
||||
while (len) {
|
||||
written = snd_pcm_writei (alsa->handle, dst, len);
|
||||
|
||||
if (written <= 0) {
|
||||
switch (written) {
|
||||
case 0:
|
||||
if (conf.verbose) {
|
||||
dolog ("Failed to write %d frames (wrote zero)\n", len);
|
||||
}
|
||||
goto exit;
|
||||
|
||||
case -EPIPE:
|
||||
if (alsa_recover (alsa->handle)) {
|
||||
alsa_logerr (written, "Failed to write %d frames\n",
|
||||
len);
|
||||
goto exit;
|
||||
}
|
||||
if (conf.verbose) {
|
||||
dolog ("Recovering from playback xrun\n");
|
||||
}
|
||||
continue;
|
||||
|
||||
case -EAGAIN:
|
||||
goto exit;
|
||||
|
||||
default:
|
||||
alsa_logerr (written, "Failed to write %d frames to %p\n",
|
||||
len, dst);
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
rpos = (rpos + written) % hw->samples;
|
||||
samples -= written;
|
||||
len -= written;
|
||||
dst = advance (dst, written << hw->info.shift);
|
||||
src += written;
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
hw->rpos = rpos;
|
||||
return decr;
|
||||
}
|
||||
|
||||
@@ -788,7 +604,7 @@ static void alsa_fini_out (HWVoiceOut *hw)
|
||||
ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
|
||||
|
||||
ldebug ("alsa_fini\n");
|
||||
alsa_anal_close (&alsa->handle, &alsa->pollhlp);
|
||||
alsa_anal_close (&alsa->handle);
|
||||
|
||||
if (alsa->pcm_buf) {
|
||||
qemu_free (alsa->pcm_buf);
|
||||
@@ -810,9 +626,8 @@ static int alsa_init_out (HWVoiceOut *hw, struct audsettings *as)
|
||||
req.period_size = conf.period_size_out;
|
||||
req.buffer_size = conf.buffer_size_out;
|
||||
req.size_in_usec = conf.size_in_usec_out;
|
||||
req.override_mask =
|
||||
(conf.period_size_out_overridden ? 1 : 0) |
|
||||
(conf.buffer_size_out_overridden ? 2 : 0);
|
||||
req.override_mask = !!conf.period_size_out_overridden
|
||||
| (!!conf.buffer_size_out_overridden << 1);
|
||||
|
||||
if (alsa_open (0, &req, &obt, &handle)) {
|
||||
return -1;
|
||||
@@ -830,7 +645,7 @@ static int alsa_init_out (HWVoiceOut *hw, struct audsettings *as)
|
||||
if (!alsa->pcm_buf) {
|
||||
dolog ("Could not allocate DAC buffer (%d samples, each %d bytes)\n",
|
||||
hw->samples, 1 << hw->info.shift);
|
||||
alsa_anal_close1 (&handle);
|
||||
alsa_anal_close (&handle);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -866,21 +681,8 @@ static int alsa_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||
|
||||
switch (cmd) {
|
||||
case VOICE_ENABLE:
|
||||
{
|
||||
va_list ap;
|
||||
int poll_mode;
|
||||
|
||||
va_start (ap, cmd);
|
||||
poll_mode = va_arg (ap, int);
|
||||
va_end (ap);
|
||||
|
||||
ldebug ("enabling voice\n");
|
||||
if (poll_mode && alsa_poll_out (hw)) {
|
||||
poll_mode = 0;
|
||||
}
|
||||
hw->poll_mode = poll_mode;
|
||||
return alsa_voice_ctl (alsa->handle, "playback", 0);
|
||||
}
|
||||
ldebug ("enabling voice\n");
|
||||
return alsa_voice_ctl (alsa->handle, "playback", 0);
|
||||
|
||||
case VOICE_DISABLE:
|
||||
ldebug ("disabling voice\n");
|
||||
@@ -904,9 +706,8 @@ static int alsa_init_in (HWVoiceIn *hw, struct audsettings *as)
|
||||
req.period_size = conf.period_size_in;
|
||||
req.buffer_size = conf.buffer_size_in;
|
||||
req.size_in_usec = conf.size_in_usec_in;
|
||||
req.override_mask =
|
||||
(conf.period_size_in_overridden ? 1 : 0) |
|
||||
(conf.buffer_size_in_overridden ? 2 : 0);
|
||||
req.override_mask = !!conf.period_size_in_overridden
|
||||
| (!!conf.buffer_size_in_overridden << 1);
|
||||
|
||||
if (alsa_open (1, &req, &obt, &handle)) {
|
||||
return -1;
|
||||
@@ -924,7 +725,7 @@ static int alsa_init_in (HWVoiceIn *hw, struct audsettings *as)
|
||||
if (!alsa->pcm_buf) {
|
||||
dolog ("Could not allocate ADC buffer (%d samples, each %d bytes)\n",
|
||||
hw->samples, 1 << hw->info.shift);
|
||||
alsa_anal_close1 (&handle);
|
||||
alsa_anal_close (&handle);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -936,7 +737,7 @@ static void alsa_fini_in (HWVoiceIn *hw)
|
||||
{
|
||||
ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
|
||||
|
||||
alsa_anal_close (&alsa->handle, &alsa->pollhlp);
|
||||
alsa_anal_close (&alsa->handle);
|
||||
|
||||
if (alsa->pcm_buf) {
|
||||
qemu_free (alsa->pcm_buf);
|
||||
@@ -956,8 +757,8 @@ static int alsa_run_in (HWVoiceIn *hw)
|
||||
int add;
|
||||
int len;
|
||||
} bufs[2] = {
|
||||
{ .add = hw->wpos, .len = 0 },
|
||||
{ .add = 0, .len = 0 }
|
||||
{ hw->wpos, 0 },
|
||||
{ 0, 0 }
|
||||
};
|
||||
snd_pcm_sframes_t avail;
|
||||
snd_pcm_uframes_t read_samples = 0;
|
||||
@@ -972,30 +773,8 @@ static int alsa_run_in (HWVoiceIn *hw)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!avail) {
|
||||
snd_pcm_state_t state;
|
||||
|
||||
state = snd_pcm_state (alsa->handle);
|
||||
switch (state) {
|
||||
case SND_PCM_STATE_PREPARED:
|
||||
avail = hw->samples;
|
||||
break;
|
||||
case SND_PCM_STATE_SUSPENDED:
|
||||
/* stream is suspended and waiting for an application recovery */
|
||||
if (alsa_resume (alsa->handle)) {
|
||||
dolog ("Failed to resume suspended input stream\n");
|
||||
return 0;
|
||||
}
|
||||
if (conf.verbose) {
|
||||
dolog ("Resuming suspended input stream\n");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (conf.verbose) {
|
||||
dolog ("No frames available and ALSA state is %d\n", state);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if (!avail && (snd_pcm_state (alsa->handle) == SND_PCM_STATE_PREPARED)) {
|
||||
avail = hw->samples;
|
||||
}
|
||||
|
||||
decr = audio_MIN (dead, avail);
|
||||
@@ -1083,29 +862,11 @@ static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...)
|
||||
|
||||
switch (cmd) {
|
||||
case VOICE_ENABLE:
|
||||
{
|
||||
va_list ap;
|
||||
int poll_mode;
|
||||
|
||||
va_start (ap, cmd);
|
||||
poll_mode = va_arg (ap, int);
|
||||
va_end (ap);
|
||||
|
||||
ldebug ("enabling voice\n");
|
||||
if (poll_mode && alsa_poll_in (hw)) {
|
||||
poll_mode = 0;
|
||||
}
|
||||
hw->poll_mode = poll_mode;
|
||||
|
||||
return alsa_voice_ctl (alsa->handle, "capture", 0);
|
||||
}
|
||||
ldebug ("enabling voice\n");
|
||||
return alsa_voice_ctl (alsa->handle, "capture", 0);
|
||||
|
||||
case VOICE_DISABLE:
|
||||
ldebug ("disabling voice\n");
|
||||
if (hw->poll_mode) {
|
||||
hw->poll_mode = 0;
|
||||
alsa_fini_poll (&alsa->pollhlp);
|
||||
}
|
||||
return alsa_voice_ctl (alsa->handle, "capture", 1);
|
||||
}
|
||||
|
||||
@@ -1123,98 +884,63 @@ static void alsa_audio_fini (void *opaque)
|
||||
}
|
||||
|
||||
static struct audio_option alsa_options[] = {
|
||||
{
|
||||
.name = "DAC_SIZE_IN_USEC",
|
||||
.tag = AUD_OPT_BOOL,
|
||||
.valp = &conf.size_in_usec_out,
|
||||
.descr = "DAC period/buffer size in microseconds (otherwise in frames)"
|
||||
},
|
||||
{
|
||||
.name = "DAC_PERIOD_SIZE",
|
||||
.tag = AUD_OPT_INT,
|
||||
.valp = &conf.period_size_out,
|
||||
.descr = "DAC period size (0 to go with system default)",
|
||||
.overriddenp = &conf.period_size_out_overridden
|
||||
},
|
||||
{
|
||||
.name = "DAC_BUFFER_SIZE",
|
||||
.tag = AUD_OPT_INT,
|
||||
.valp = &conf.buffer_size_out,
|
||||
.descr = "DAC buffer size (0 to go with system default)",
|
||||
.overriddenp = &conf.buffer_size_out_overridden
|
||||
},
|
||||
{
|
||||
.name = "ADC_SIZE_IN_USEC",
|
||||
.tag = AUD_OPT_BOOL,
|
||||
.valp = &conf.size_in_usec_in,
|
||||
.descr =
|
||||
"ADC period/buffer size in microseconds (otherwise in frames)"
|
||||
},
|
||||
{
|
||||
.name = "ADC_PERIOD_SIZE",
|
||||
.tag = AUD_OPT_INT,
|
||||
.valp = &conf.period_size_in,
|
||||
.descr = "ADC period size (0 to go with system default)",
|
||||
.overriddenp = &conf.period_size_in_overridden
|
||||
},
|
||||
{
|
||||
.name = "ADC_BUFFER_SIZE",
|
||||
.tag = AUD_OPT_INT,
|
||||
.valp = &conf.buffer_size_in,
|
||||
.descr = "ADC buffer size (0 to go with system default)",
|
||||
.overriddenp = &conf.buffer_size_in_overridden
|
||||
},
|
||||
{
|
||||
.name = "THRESHOLD",
|
||||
.tag = AUD_OPT_INT,
|
||||
.valp = &conf.threshold,
|
||||
.descr = "(undocumented)"
|
||||
},
|
||||
{
|
||||
.name = "DAC_DEV",
|
||||
.tag = AUD_OPT_STR,
|
||||
.valp = &conf.pcm_name_out,
|
||||
.descr = "DAC device name (for instance dmix)"
|
||||
},
|
||||
{
|
||||
.name = "ADC_DEV",
|
||||
.tag = AUD_OPT_STR,
|
||||
.valp = &conf.pcm_name_in,
|
||||
.descr = "ADC device name"
|
||||
},
|
||||
{
|
||||
.name = "VERBOSE",
|
||||
.tag = AUD_OPT_BOOL,
|
||||
.valp = &conf.verbose,
|
||||
.descr = "Behave in a more verbose way"
|
||||
},
|
||||
{ /* End of list */ }
|
||||
{"DAC_SIZE_IN_USEC", AUD_OPT_BOOL, &conf.size_in_usec_out,
|
||||
"DAC period/buffer size in microseconds (otherwise in frames)", NULL, 0},
|
||||
{"DAC_PERIOD_SIZE", AUD_OPT_INT, &conf.period_size_out,
|
||||
"DAC period size (0 to go with system default)",
|
||||
&conf.period_size_out_overridden, 0},
|
||||
{"DAC_BUFFER_SIZE", AUD_OPT_INT, &conf.buffer_size_out,
|
||||
"DAC buffer size (0 to go with system default)",
|
||||
&conf.buffer_size_out_overridden, 0},
|
||||
|
||||
{"ADC_SIZE_IN_USEC", AUD_OPT_BOOL, &conf.size_in_usec_in,
|
||||
"ADC period/buffer size in microseconds (otherwise in frames)", NULL, 0},
|
||||
{"ADC_PERIOD_SIZE", AUD_OPT_INT, &conf.period_size_in,
|
||||
"ADC period size (0 to go with system default)",
|
||||
&conf.period_size_in_overridden, 0},
|
||||
{"ADC_BUFFER_SIZE", AUD_OPT_INT, &conf.buffer_size_in,
|
||||
"ADC buffer size (0 to go with system default)",
|
||||
&conf.buffer_size_in_overridden, 0},
|
||||
|
||||
{"THRESHOLD", AUD_OPT_INT, &conf.threshold,
|
||||
"(undocumented)", NULL, 0},
|
||||
|
||||
{"DAC_DEV", AUD_OPT_STR, &conf.pcm_name_out,
|
||||
"DAC device name (for instance dmix)", NULL, 0},
|
||||
|
||||
{"ADC_DEV", AUD_OPT_STR, &conf.pcm_name_in,
|
||||
"ADC device name", NULL, 0},
|
||||
|
||||
{"VERBOSE", AUD_OPT_BOOL, &conf.verbose,
|
||||
"Behave in a more verbose way", NULL, 0},
|
||||
|
||||
{NULL, 0, NULL, NULL, NULL, 0}
|
||||
};
|
||||
|
||||
static struct audio_pcm_ops alsa_pcm_ops = {
|
||||
.init_out = alsa_init_out,
|
||||
.fini_out = alsa_fini_out,
|
||||
.run_out = alsa_run_out,
|
||||
.write = alsa_write,
|
||||
.ctl_out = alsa_ctl_out,
|
||||
alsa_init_out,
|
||||
alsa_fini_out,
|
||||
alsa_run_out,
|
||||
alsa_write,
|
||||
alsa_ctl_out,
|
||||
|
||||
.init_in = alsa_init_in,
|
||||
.fini_in = alsa_fini_in,
|
||||
.run_in = alsa_run_in,
|
||||
.read = alsa_read,
|
||||
.ctl_in = alsa_ctl_in,
|
||||
alsa_init_in,
|
||||
alsa_fini_in,
|
||||
alsa_run_in,
|
||||
alsa_read,
|
||||
alsa_ctl_in
|
||||
};
|
||||
|
||||
struct audio_driver alsa_audio_driver = {
|
||||
.name = "alsa",
|
||||
.descr = "ALSA http://www.alsa-project.org",
|
||||
.options = alsa_options,
|
||||
.init = alsa_audio_init,
|
||||
.fini = alsa_audio_fini,
|
||||
.pcm_ops = &alsa_pcm_ops,
|
||||
.can_be_default = 1,
|
||||
.max_voices_out = INT_MAX,
|
||||
.max_voices_in = INT_MAX,
|
||||
.voice_size_out = sizeof (ALSAVoiceOut),
|
||||
.voice_size_in = sizeof (ALSAVoiceIn)
|
||||
INIT_FIELD (name = ) "alsa",
|
||||
INIT_FIELD (descr = ) "ALSA http://www.alsa-project.org",
|
||||
INIT_FIELD (options = ) alsa_options,
|
||||
INIT_FIELD (init = ) alsa_audio_init,
|
||||
INIT_FIELD (fini = ) alsa_audio_fini,
|
||||
INIT_FIELD (pcm_ops = ) &alsa_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 (ALSAVoiceOut),
|
||||
INIT_FIELD (voice_size_in = ) sizeof (ALSAVoiceIn)
|
||||
};
|
||||
+216
-310
@@ -23,7 +23,7 @@
|
||||
*/
|
||||
#include "hw/hw.h"
|
||||
#include "audio.h"
|
||||
#include "monitor.h"
|
||||
#include "console.h"
|
||||
#include "qemu-timer.h"
|
||||
#include "sysemu.h"
|
||||
|
||||
@@ -34,17 +34,11 @@
|
||||
/* #define DEBUG_LIVE */
|
||||
/* #define DEBUG_OUT */
|
||||
/* #define DEBUG_CAPTURE */
|
||||
/* #define DEBUG_POLL */
|
||||
|
||||
#define SW_NAME(sw) (sw)->name ? (sw)->name : "unknown"
|
||||
|
||||
|
||||
/* Order of CONFIG_AUDIO_DRIVERS is import.
|
||||
The 1st one is the one used by default, that is the reason
|
||||
that we generate the list.
|
||||
*/
|
||||
static struct audio_driver *drvtab[] = {
|
||||
CONFIG_AUDIO_DRIVERS
|
||||
AUDIO_DRIVERS
|
||||
&no_audio_driver,
|
||||
&wav_audio_driver
|
||||
};
|
||||
@@ -65,53 +59,66 @@ static struct {
|
||||
} period;
|
||||
int plive;
|
||||
int log_to_monitor;
|
||||
int try_poll_in;
|
||||
int try_poll_out;
|
||||
} conf = {
|
||||
.fixed_out = { /* DAC fixed settings */
|
||||
.enabled = 1,
|
||||
.nb_voices = 1,
|
||||
.greedy = 1,
|
||||
.settings = {
|
||||
.freq = 44100,
|
||||
.nchannels = 2,
|
||||
.fmt = AUD_FMT_S16,
|
||||
.endianness = AUDIO_HOST_ENDIANNESS,
|
||||
{ /* DAC fixed settings */
|
||||
1, /* enabled */
|
||||
1, /* nb_voices */
|
||||
1, /* greedy */
|
||||
{
|
||||
44100, /* freq */
|
||||
2, /* nchannels */
|
||||
AUD_FMT_S16, /* fmt */
|
||||
AUDIO_HOST_ENDIANNESS
|
||||
}
|
||||
},
|
||||
|
||||
.fixed_in = { /* ADC fixed settings */
|
||||
.enabled = 1,
|
||||
.nb_voices = 1,
|
||||
.greedy = 1,
|
||||
.settings = {
|
||||
.freq = 44100,
|
||||
.nchannels = 2,
|
||||
.fmt = AUD_FMT_S16,
|
||||
.endianness = AUDIO_HOST_ENDIANNESS,
|
||||
{ /* ADC fixed settings */
|
||||
1, /* enabled */
|
||||
1, /* nb_voices */
|
||||
1, /* greedy */
|
||||
{
|
||||
44100, /* freq */
|
||||
2, /* nchannels */
|
||||
AUD_FMT_S16, /* fmt */
|
||||
AUDIO_HOST_ENDIANNESS
|
||||
}
|
||||
},
|
||||
|
||||
.period = { .hertz = 250 },
|
||||
.plive = 0,
|
||||
.log_to_monitor = 0,
|
||||
.try_poll_in = 1,
|
||||
.try_poll_out = 1,
|
||||
{ 250 }, /* period */
|
||||
0, /* plive */
|
||||
0 /* log_to_monitor */
|
||||
};
|
||||
|
||||
static AudioState glob_audio_state;
|
||||
|
||||
struct mixeng_volume nominal_volume = {
|
||||
.mute = 0,
|
||||
0,
|
||||
#ifdef FLOAT_MIXENG
|
||||
.r = 1.0,
|
||||
.l = 1.0,
|
||||
1.0,
|
||||
1.0
|
||||
#else
|
||||
.r = 1ULL << 32,
|
||||
.l = 1ULL << 32,
|
||||
1ULL << 32,
|
||||
1ULL << 32
|
||||
#endif
|
||||
};
|
||||
|
||||
/* http://www.df.lth.se/~john_e/gems/gem002d.html */
|
||||
/* http://www.multi-platforms.com/Tips/PopCount.htm */
|
||||
uint32_t popcount (uint32_t u)
|
||||
{
|
||||
u = ((u&0x55555555) + ((u>>1)&0x55555555));
|
||||
u = ((u&0x33333333) + ((u>>2)&0x33333333));
|
||||
u = ((u&0x0f0f0f0f) + ((u>>4)&0x0f0f0f0f));
|
||||
u = ((u&0x00ff00ff) + ((u>>8)&0x00ff00ff));
|
||||
u = ( u&0x0000ffff) + (u>>16);
|
||||
return u;
|
||||
}
|
||||
|
||||
inline uint32_t lsbindex (uint32_t u)
|
||||
{
|
||||
return popcount ((u&-u)-1);
|
||||
}
|
||||
|
||||
#ifdef AUDIO_IS_FLAWLESS_AND_NO_CHECKS_ARE_REQURIED
|
||||
#error No its not
|
||||
#else
|
||||
@@ -124,7 +131,7 @@ int audio_bug (const char *funcname, int cond)
|
||||
if (!shown) {
|
||||
shown = 1;
|
||||
AUD_log (NULL, "Save all your work and restart without audio\n");
|
||||
AUD_log (NULL, "Please send bug report to av1474@comtv.ru\n");
|
||||
AUD_log (NULL, "Please send bug report to malc@pulsesoft.com\n");
|
||||
AUD_log (NULL, "I am sorry\n");
|
||||
}
|
||||
AUD_log (NULL, "Context:\n");
|
||||
@@ -321,10 +328,10 @@ void AUD_vlog (const char *cap, const char *fmt, va_list ap)
|
||||
{
|
||||
if (conf.log_to_monitor) {
|
||||
if (cap) {
|
||||
monitor_printf(cur_mon, "%s: ", cap);
|
||||
term_printf ("%s: ", cap);
|
||||
}
|
||||
|
||||
monitor_vprintf(cur_mon, fmt, ap);
|
||||
term_vprintf (fmt, ap);
|
||||
}
|
||||
else {
|
||||
if (cap) {
|
||||
@@ -700,11 +707,11 @@ static void noop_conv (struct st_sample *dst, const void *src,
|
||||
}
|
||||
|
||||
static CaptureVoiceOut *audio_pcm_capture_find_specific (
|
||||
AudioState *s,
|
||||
struct audsettings *as
|
||||
)
|
||||
{
|
||||
CaptureVoiceOut *cap;
|
||||
AudioState *s = &glob_audio_state;
|
||||
|
||||
for (cap = s->cap_head.lh_first; cap; cap = cap->entries.le_next) {
|
||||
if (audio_pcm_info_eq (&cap->hw.info, as)) {
|
||||
@@ -766,8 +773,8 @@ static void audio_detach_capture (HWVoiceOut *hw)
|
||||
sw->rate = NULL;
|
||||
}
|
||||
|
||||
QLIST_REMOVE (sw, entries);
|
||||
QLIST_REMOVE (sc, entries);
|
||||
LIST_REMOVE (sw, entries);
|
||||
LIST_REMOVE (sc, entries);
|
||||
qemu_free (sc);
|
||||
if (was_active) {
|
||||
/* We have removed soft voice from the capture:
|
||||
@@ -779,9 +786,8 @@ static void audio_detach_capture (HWVoiceOut *hw)
|
||||
}
|
||||
}
|
||||
|
||||
static int audio_attach_capture (HWVoiceOut *hw)
|
||||
static int audio_attach_capture (AudioState *s, HWVoiceOut *hw)
|
||||
{
|
||||
AudioState *s = &glob_audio_state;
|
||||
CaptureVoiceOut *cap;
|
||||
|
||||
audio_detach_capture (hw);
|
||||
@@ -811,8 +817,8 @@ static int audio_attach_capture (HWVoiceOut *hw)
|
||||
qemu_free (sw);
|
||||
return -1;
|
||||
}
|
||||
QLIST_INSERT_HEAD (&hw_cap->sw_head, sw, entries);
|
||||
QLIST_INSERT_HEAD (&hw->cap_head, sc, entries);
|
||||
LIST_INSERT_HEAD (&hw_cap->sw_head, sw, entries);
|
||||
LIST_INSERT_HEAD (&hw->cap_head, sc, entries);
|
||||
#ifdef DEBUG_CAPTURE
|
||||
asprintf (&sw->name, "for %p %d,%d,%d",
|
||||
hw, sw->info.freq, sw->info.bits, sw->info.nchannels);
|
||||
@@ -851,28 +857,6 @@ int audio_pcm_hw_get_live_in (HWVoiceIn *hw)
|
||||
return live;
|
||||
}
|
||||
|
||||
int audio_pcm_hw_clip_out (HWVoiceOut *hw, void *pcm_buf,
|
||||
int live, int pending)
|
||||
{
|
||||
int left = hw->samples - pending;
|
||||
int len = audio_MIN (left, live);
|
||||
int clipped = 0;
|
||||
|
||||
while (len) {
|
||||
struct st_sample *src = hw->mix_buf + hw->rpos;
|
||||
uint8_t *dst = advance (pcm_buf, hw->rpos << hw->info.shift);
|
||||
int samples_till_end_of_buf = hw->samples - hw->rpos;
|
||||
int samples_to_clip = audio_MIN (len, samples_till_end_of_buf);
|
||||
|
||||
hw->clip (dst, src, samples_to_clip);
|
||||
|
||||
hw->rpos = (hw->rpos + samples_to_clip) % hw->samples;
|
||||
len -= samples_to_clip;
|
||||
clipped += samples_to_clip;
|
||||
}
|
||||
return clipped;
|
||||
}
|
||||
|
||||
/*
|
||||
* Soft voice (capture)
|
||||
*/
|
||||
@@ -969,17 +953,16 @@ static int audio_pcm_hw_find_min_out (HWVoiceOut *hw, int *nb_livep)
|
||||
return m;
|
||||
}
|
||||
|
||||
static int audio_pcm_hw_get_live_out (HWVoiceOut *hw, int *nb_live)
|
||||
int audio_pcm_hw_get_live_out2 (HWVoiceOut *hw, int *nb_live)
|
||||
{
|
||||
int smin;
|
||||
int nb_live1;
|
||||
|
||||
smin = audio_pcm_hw_find_min_out (hw, &nb_live1);
|
||||
if (nb_live) {
|
||||
*nb_live = nb_live1;
|
||||
smin = audio_pcm_hw_find_min_out (hw, nb_live);
|
||||
|
||||
if (!*nb_live) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (nb_live1) {
|
||||
else {
|
||||
int live = smin;
|
||||
|
||||
if (audio_bug (AUDIO_FUNC, live < 0 || live > hw->samples)) {
|
||||
@@ -988,7 +971,19 @@ static int audio_pcm_hw_get_live_out (HWVoiceOut *hw, int *nb_live)
|
||||
}
|
||||
return live;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int audio_pcm_hw_get_live_out (HWVoiceOut *hw)
|
||||
{
|
||||
int nb_live;
|
||||
int live;
|
||||
|
||||
live = audio_pcm_hw_get_live_out2 (hw, &nb_live);
|
||||
if (audio_bug (AUDIO_FUNC, live < 0 || live > hw->samples)) {
|
||||
dolog ("live=%d hw->samples=%d\n", live, hw->samples);
|
||||
return 0;
|
||||
}
|
||||
return live;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1081,47 +1076,6 @@ static void audio_pcm_print_info (const char *cap, struct audio_pcm_info *info)
|
||||
#undef DAC
|
||||
#include "audio_template.h"
|
||||
|
||||
/*
|
||||
* Timer
|
||||
*/
|
||||
static void audio_timer (void *opaque)
|
||||
{
|
||||
AudioState *s = opaque;
|
||||
|
||||
audio_run ("timer");
|
||||
qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + conf.period.ticks);
|
||||
}
|
||||
|
||||
|
||||
static int audio_is_timer_needed (void)
|
||||
{
|
||||
HWVoiceIn *hwi = NULL;
|
||||
HWVoiceOut *hwo = NULL;
|
||||
|
||||
while ((hwo = audio_pcm_hw_find_any_enabled_out (hwo))) {
|
||||
if (!hwo->poll_mode) return 1;
|
||||
}
|
||||
while ((hwi = audio_pcm_hw_find_any_enabled_in (hwi))) {
|
||||
if (!hwi->poll_mode) return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void audio_reset_timer (void)
|
||||
{
|
||||
AudioState *s = &glob_audio_state;
|
||||
|
||||
if (audio_is_timer_needed ()) {
|
||||
qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + 1);
|
||||
}
|
||||
else {
|
||||
qemu_del_timer (s->ts);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Public API
|
||||
*/
|
||||
int AUD_write (SWVoiceOut *sw, void *buf, int size)
|
||||
{
|
||||
int bytes;
|
||||
@@ -1182,8 +1136,7 @@ void AUD_set_active_out (SWVoiceOut *sw, int on)
|
||||
if (!hw->enabled) {
|
||||
hw->enabled = 1;
|
||||
if (s->vm_running) {
|
||||
hw->pcm_ops->ctl_out (hw, VOICE_ENABLE, conf.try_poll_out);
|
||||
audio_reset_timer ();
|
||||
hw->pcm_ops->ctl_out (hw, VOICE_ENABLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1227,7 +1180,7 @@ void AUD_set_active_in (SWVoiceIn *sw, int on)
|
||||
if (!hw->enabled) {
|
||||
hw->enabled = 1;
|
||||
if (s->vm_running) {
|
||||
hw->pcm_ops->ctl_in (hw, VOICE_ENABLE, conf.try_poll_in);
|
||||
hw->pcm_ops->ctl_in (hw, VOICE_ENABLE);
|
||||
}
|
||||
}
|
||||
sw->total_hw_samples_acquired = hw->total_samples_captured;
|
||||
@@ -1342,11 +1295,11 @@ static void audio_run_out (AudioState *s)
|
||||
HWVoiceOut *hw = NULL;
|
||||
SWVoiceOut *sw;
|
||||
|
||||
while ((hw = audio_pcm_hw_find_any_enabled_out (hw))) {
|
||||
while ((hw = audio_pcm_hw_find_any_enabled_out (s, hw))) {
|
||||
int played;
|
||||
int live, free, nb_live, cleanup_required, prev_rpos;
|
||||
|
||||
live = audio_pcm_hw_get_live_out (hw, &nb_live);
|
||||
live = audio_pcm_hw_get_live_out2 (hw, &nb_live);
|
||||
if (!nb_live) {
|
||||
live = 0;
|
||||
}
|
||||
@@ -1384,7 +1337,7 @@ static void audio_run_out (AudioState *s)
|
||||
}
|
||||
|
||||
prev_rpos = hw->rpos;
|
||||
played = hw->pcm_ops->run_out (hw, live);
|
||||
played = hw->pcm_ops->run_out (hw);
|
||||
if (audio_bug (AUDIO_FUNC, hw->rpos >= hw->samples)) {
|
||||
dolog ("hw->rpos=%d hw->samples=%d played=%d\n",
|
||||
hw->rpos, hw->samples, played);
|
||||
@@ -1437,7 +1390,7 @@ static void audio_run_out (AudioState *s)
|
||||
#ifdef DEBUG_PLIVE
|
||||
dolog ("Finishing with old voice\n");
|
||||
#endif
|
||||
audio_close_out (sw);
|
||||
audio_close_out (s, sw);
|
||||
}
|
||||
sw = sw1;
|
||||
}
|
||||
@@ -1449,7 +1402,7 @@ static void audio_run_in (AudioState *s)
|
||||
{
|
||||
HWVoiceIn *hw = NULL;
|
||||
|
||||
while ((hw = audio_pcm_hw_find_any_enabled_in (hw))) {
|
||||
while ((hw = audio_pcm_hw_find_any_enabled_in (s, hw))) {
|
||||
SWVoiceIn *sw;
|
||||
int captured, min;
|
||||
|
||||
@@ -1483,7 +1436,7 @@ static void audio_run_capture (AudioState *s)
|
||||
HWVoiceOut *hw = &cap->hw;
|
||||
SWVoiceOut *sw;
|
||||
|
||||
captured = live = audio_pcm_hw_get_live_out (hw, NULL);
|
||||
captured = live = audio_pcm_hw_get_live_out (hw);
|
||||
rpos = hw->rpos;
|
||||
while (live) {
|
||||
int left = hw->samples - rpos;
|
||||
@@ -1521,126 +1474,61 @@ static void audio_run_capture (AudioState *s)
|
||||
}
|
||||
}
|
||||
|
||||
void audio_run (const char *msg)
|
||||
static void audio_timer (void *opaque)
|
||||
{
|
||||
AudioState *s = &glob_audio_state;
|
||||
AudioState *s = opaque;
|
||||
|
||||
audio_run_out (s);
|
||||
audio_run_in (s);
|
||||
audio_run_capture (s);
|
||||
#ifdef DEBUG_POLL
|
||||
{
|
||||
static double prevtime;
|
||||
double currtime;
|
||||
struct timeval tv;
|
||||
|
||||
if (gettimeofday (&tv, NULL)) {
|
||||
perror ("audio_run: gettimeofday");
|
||||
return;
|
||||
}
|
||||
|
||||
currtime = tv.tv_sec + tv.tv_usec * 1e-6;
|
||||
dolog ("Elapsed since last %s: %f\n", msg, currtime - prevtime);
|
||||
prevtime = currtime;
|
||||
}
|
||||
#endif
|
||||
qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + conf.period.ticks);
|
||||
}
|
||||
|
||||
static struct audio_option audio_options[] = {
|
||||
/* DAC */
|
||||
{
|
||||
.name = "DAC_FIXED_SETTINGS",
|
||||
.tag = AUD_OPT_BOOL,
|
||||
.valp = &conf.fixed_out.enabled,
|
||||
.descr = "Use fixed settings for host DAC"
|
||||
},
|
||||
{
|
||||
.name = "DAC_FIXED_FREQ",
|
||||
.tag = AUD_OPT_INT,
|
||||
.valp = &conf.fixed_out.settings.freq,
|
||||
.descr = "Frequency for fixed host DAC"
|
||||
},
|
||||
{
|
||||
.name = "DAC_FIXED_FMT",
|
||||
.tag = AUD_OPT_FMT,
|
||||
.valp = &conf.fixed_out.settings.fmt,
|
||||
.descr = "Format for fixed host DAC"
|
||||
},
|
||||
{
|
||||
.name = "DAC_FIXED_CHANNELS",
|
||||
.tag = AUD_OPT_INT,
|
||||
.valp = &conf.fixed_out.settings.nchannels,
|
||||
.descr = "Number of channels for fixed DAC (1 - mono, 2 - stereo)"
|
||||
},
|
||||
{
|
||||
.name = "DAC_VOICES",
|
||||
.tag = AUD_OPT_INT,
|
||||
.valp = &conf.fixed_out.nb_voices,
|
||||
.descr = "Number of voices for DAC"
|
||||
},
|
||||
{
|
||||
.name = "DAC_TRY_POLL",
|
||||
.tag = AUD_OPT_BOOL,
|
||||
.valp = &conf.try_poll_out,
|
||||
.descr = "Attempt using poll mode for DAC"
|
||||
},
|
||||
{"DAC_FIXED_SETTINGS", AUD_OPT_BOOL, &conf.fixed_out.enabled,
|
||||
"Use fixed settings for host DAC", NULL, 0},
|
||||
|
||||
{"DAC_FIXED_FREQ", AUD_OPT_INT, &conf.fixed_out.settings.freq,
|
||||
"Frequency for fixed host DAC", NULL, 0},
|
||||
|
||||
{"DAC_FIXED_FMT", AUD_OPT_FMT, &conf.fixed_out.settings.fmt,
|
||||
"Format for fixed host DAC", NULL, 0},
|
||||
|
||||
{"DAC_FIXED_CHANNELS", AUD_OPT_INT, &conf.fixed_out.settings.nchannels,
|
||||
"Number of channels for fixed DAC (1 - mono, 2 - stereo)", NULL, 0},
|
||||
|
||||
{"DAC_VOICES", AUD_OPT_INT, &conf.fixed_out.nb_voices,
|
||||
"Number of voices for DAC", NULL, 0},
|
||||
|
||||
/* ADC */
|
||||
{
|
||||
.name = "ADC_FIXED_SETTINGS",
|
||||
.tag = AUD_OPT_BOOL,
|
||||
.valp = &conf.fixed_in.enabled,
|
||||
.descr = "Use fixed settings for host ADC"
|
||||
},
|
||||
{
|
||||
.name = "ADC_FIXED_FREQ",
|
||||
.tag = AUD_OPT_INT,
|
||||
.valp = &conf.fixed_in.settings.freq,
|
||||
.descr = "Frequency for fixed host ADC"
|
||||
},
|
||||
{
|
||||
.name = "ADC_FIXED_FMT",
|
||||
.tag = AUD_OPT_FMT,
|
||||
.valp = &conf.fixed_in.settings.fmt,
|
||||
.descr = "Format for fixed host ADC"
|
||||
},
|
||||
{
|
||||
.name = "ADC_FIXED_CHANNELS",
|
||||
.tag = AUD_OPT_INT,
|
||||
.valp = &conf.fixed_in.settings.nchannels,
|
||||
.descr = "Number of channels for fixed ADC (1 - mono, 2 - stereo)"
|
||||
},
|
||||
{
|
||||
.name = "ADC_VOICES",
|
||||
.tag = AUD_OPT_INT,
|
||||
.valp = &conf.fixed_in.nb_voices,
|
||||
.descr = "Number of voices for ADC"
|
||||
},
|
||||
{
|
||||
.name = "ADC_TRY_POLL",
|
||||
.tag = AUD_OPT_BOOL,
|
||||
.valp = &conf.try_poll_in,
|
||||
.descr = "Attempt using poll mode for ADC"
|
||||
},
|
||||
{"ADC_FIXED_SETTINGS", AUD_OPT_BOOL, &conf.fixed_in.enabled,
|
||||
"Use fixed settings for host ADC", NULL, 0},
|
||||
|
||||
{"ADC_FIXED_FREQ", AUD_OPT_INT, &conf.fixed_in.settings.freq,
|
||||
"Frequency for fixed host ADC", NULL, 0},
|
||||
|
||||
{"ADC_FIXED_FMT", AUD_OPT_FMT, &conf.fixed_in.settings.fmt,
|
||||
"Format for fixed host ADC", NULL, 0},
|
||||
|
||||
{"ADC_FIXED_CHANNELS", AUD_OPT_INT, &conf.fixed_in.settings.nchannels,
|
||||
"Number of channels for fixed ADC (1 - mono, 2 - stereo)", NULL, 0},
|
||||
|
||||
{"ADC_VOICES", AUD_OPT_INT, &conf.fixed_in.nb_voices,
|
||||
"Number of voices for ADC", NULL, 0},
|
||||
|
||||
/* Misc */
|
||||
{
|
||||
.name = "TIMER_PERIOD",
|
||||
.tag = AUD_OPT_INT,
|
||||
.valp = &conf.period.hertz,
|
||||
.descr = "Timer period in HZ (0 - use lowest possible)"
|
||||
},
|
||||
{
|
||||
.name = "PLIVE",
|
||||
.tag = AUD_OPT_BOOL,
|
||||
.valp = &conf.plive,
|
||||
.descr = "(undocumented)"
|
||||
},
|
||||
{
|
||||
.name = "LOG_TO_MONITOR",
|
||||
.tag = AUD_OPT_BOOL,
|
||||
.valp = &conf.log_to_monitor,
|
||||
.descr = "Print logging messages to monitor instead of stderr"
|
||||
},
|
||||
{ /* End of list */ }
|
||||
{"TIMER_PERIOD", AUD_OPT_INT, &conf.period.hertz,
|
||||
"Timer period in HZ (0 - use lowest possible)", NULL, 0},
|
||||
|
||||
{"PLIVE", AUD_OPT_BOOL, &conf.plive,
|
||||
"(undocumented)", NULL, 0},
|
||||
|
||||
{"LOG_TO_MONITOR", AUD_OPT_BOOL, &conf.log_to_monitor,
|
||||
"print logging messages to monitor instead of stderr", NULL, 0},
|
||||
|
||||
{NULL, 0, NULL, NULL, NULL, 0}
|
||||
};
|
||||
|
||||
static void audio_pp_nb_voices (const char *typ, int nb)
|
||||
@@ -1722,8 +1610,8 @@ static int audio_driver_init (AudioState *s, struct audio_driver *drv)
|
||||
s->drv_opaque = drv->init ();
|
||||
|
||||
if (s->drv_opaque) {
|
||||
audio_init_nb_voices_out (drv);
|
||||
audio_init_nb_voices_in (drv);
|
||||
audio_init_nb_voices_out (s, drv);
|
||||
audio_init_nb_voices_in (s, drv);
|
||||
s->drv = drv;
|
||||
return 0;
|
||||
}
|
||||
@@ -1742,14 +1630,13 @@ static void audio_vm_change_state_handler (void *opaque, int running,
|
||||
int op = running ? VOICE_ENABLE : VOICE_DISABLE;
|
||||
|
||||
s->vm_running = running;
|
||||
while ((hwo = audio_pcm_hw_find_any_enabled_out (hwo))) {
|
||||
hwo->pcm_ops->ctl_out (hwo, op, conf.try_poll_out);
|
||||
while ((hwo = audio_pcm_hw_find_any_enabled_out (s, hwo))) {
|
||||
hwo->pcm_ops->ctl_out (hwo, op);
|
||||
}
|
||||
|
||||
while ((hwi = audio_pcm_hw_find_any_enabled_in (hwi))) {
|
||||
hwi->pcm_ops->ctl_in (hwi, op, conf.try_poll_in);
|
||||
while ((hwi = audio_pcm_hw_find_any_enabled_in (s, hwi))) {
|
||||
hwi->pcm_ops->ctl_in (hwi, op);
|
||||
}
|
||||
audio_reset_timer ();
|
||||
}
|
||||
|
||||
static void audio_atexit (void)
|
||||
@@ -1758,7 +1645,7 @@ static void audio_atexit (void)
|
||||
HWVoiceOut *hwo = NULL;
|
||||
HWVoiceIn *hwi = NULL;
|
||||
|
||||
while ((hwo = audio_pcm_hw_find_any_enabled_out (hwo))) {
|
||||
while ((hwo = audio_pcm_hw_find_any_enabled_out (s, hwo))) {
|
||||
SWVoiceCap *sc;
|
||||
|
||||
hwo->pcm_ops->ctl_out (hwo, VOICE_DISABLE);
|
||||
@@ -1774,7 +1661,7 @@ static void audio_atexit (void)
|
||||
}
|
||||
}
|
||||
|
||||
while ((hwi = audio_pcm_hw_find_any_enabled_in (hwi))) {
|
||||
while ((hwi = audio_pcm_hw_find_any_enabled_in (s, hwi))) {
|
||||
hwi->pcm_ops->ctl_in (hwi, VOICE_DISABLE);
|
||||
hwi->pcm_ops->fini_in (hwi);
|
||||
}
|
||||
@@ -1784,36 +1671,55 @@ static void audio_atexit (void)
|
||||
}
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_audio = {
|
||||
.name = "audio",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.minimum_version_id_old = 1,
|
||||
.fields = (VMStateField []) {
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
static void audio_save (QEMUFile *f, void *opaque)
|
||||
{
|
||||
(void) f;
|
||||
(void) opaque;
|
||||
}
|
||||
|
||||
static void audio_init (void)
|
||||
static int audio_load (QEMUFile *f, void *opaque, int version_id)
|
||||
{
|
||||
(void) f;
|
||||
(void) opaque;
|
||||
|
||||
if (version_id != 1) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void AUD_register_card (AudioState *s, const char *name, QEMUSoundCard *card)
|
||||
{
|
||||
card->audio = s;
|
||||
card->name = qemu_strdup (name);
|
||||
memset (&card->entries, 0, sizeof (card->entries));
|
||||
LIST_INSERT_HEAD (&s->card_head, card, entries);
|
||||
}
|
||||
|
||||
void AUD_remove_card (QEMUSoundCard *card)
|
||||
{
|
||||
LIST_REMOVE (card, entries);
|
||||
card->audio = NULL;
|
||||
qemu_free (card->name);
|
||||
}
|
||||
|
||||
AudioState *AUD_init (void)
|
||||
{
|
||||
size_t i;
|
||||
int done = 0;
|
||||
const char *drvname;
|
||||
VMChangeStateEntry *e;
|
||||
AudioState *s = &glob_audio_state;
|
||||
|
||||
if (s->drv) {
|
||||
return;
|
||||
}
|
||||
|
||||
QLIST_INIT (&s->hw_head_out);
|
||||
QLIST_INIT (&s->hw_head_in);
|
||||
QLIST_INIT (&s->cap_head);
|
||||
LIST_INIT (&s->hw_head_out);
|
||||
LIST_INIT (&s->hw_head_in);
|
||||
LIST_INIT (&s->cap_head);
|
||||
atexit (audio_atexit);
|
||||
|
||||
s->ts = qemu_new_timer (vm_clock, audio_timer, s);
|
||||
if (!s->ts) {
|
||||
hw_error("Could not create audio timer\n");
|
||||
dolog ("Could not create audio timer\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
audio_process_options ("AUDIO", audio_options);
|
||||
@@ -1866,60 +1772,60 @@ static void audio_init (void)
|
||||
if (!done) {
|
||||
done = !audio_driver_init (s, &no_audio_driver);
|
||||
if (!done) {
|
||||
hw_error("Could not initialize audio subsystem\n");
|
||||
dolog ("Could not initialize audio subsystem\n");
|
||||
}
|
||||
else {
|
||||
dolog ("warning: Using timer based audio emulation\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (conf.period.hertz <= 0) {
|
||||
if (conf.period.hertz < 0) {
|
||||
dolog ("warning: Timer period is negative - %d "
|
||||
"treating as zero\n",
|
||||
conf.period.hertz);
|
||||
if (done) {
|
||||
VMChangeStateEntry *e;
|
||||
|
||||
if (conf.period.hertz <= 0) {
|
||||
if (conf.period.hertz < 0) {
|
||||
dolog ("warning: Timer period is negative - %d "
|
||||
"treating as zero\n",
|
||||
conf.period.hertz);
|
||||
}
|
||||
conf.period.ticks = 1;
|
||||
}
|
||||
conf.period.ticks = 1;
|
||||
} else {
|
||||
conf.period.ticks =
|
||||
muldiv64 (1, get_ticks_per_sec (), conf.period.hertz);
|
||||
else {
|
||||
conf.period.ticks = ticks_per_sec / conf.period.hertz;
|
||||
}
|
||||
|
||||
e = qemu_add_vm_change_state_handler (audio_vm_change_state_handler, s);
|
||||
if (!e) {
|
||||
dolog ("warning: Could not register change state handler\n"
|
||||
"(Audio can continue looping even after stopping the VM)\n");
|
||||
}
|
||||
}
|
||||
else {
|
||||
qemu_del_timer (s->ts);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
e = qemu_add_vm_change_state_handler (audio_vm_change_state_handler, s);
|
||||
if (!e) {
|
||||
dolog ("warning: Could not register change state handler\n"
|
||||
"(Audio can continue looping even after stopping the VM)\n");
|
||||
}
|
||||
|
||||
QLIST_INIT (&s->card_head);
|
||||
vmstate_register (0, &vmstate_audio, s);
|
||||
LIST_INIT (&s->card_head);
|
||||
register_savevm ("audio", 0, 1, audio_save, audio_load, s);
|
||||
qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + conf.period.ticks);
|
||||
return s;
|
||||
}
|
||||
|
||||
void AUD_register_card (const char *name, QEMUSoundCard *card)
|
||||
{
|
||||
audio_init ();
|
||||
card->name = qemu_strdup (name);
|
||||
memset (&card->entries, 0, sizeof (card->entries));
|
||||
QLIST_INSERT_HEAD (&glob_audio_state.card_head, card, entries);
|
||||
}
|
||||
|
||||
void AUD_remove_card (QEMUSoundCard *card)
|
||||
{
|
||||
QLIST_REMOVE (card, entries);
|
||||
qemu_free (card->name);
|
||||
}
|
||||
|
||||
|
||||
CaptureVoiceOut *AUD_add_capture (
|
||||
AudioState *s,
|
||||
struct audsettings *as,
|
||||
struct audio_capture_ops *ops,
|
||||
void *cb_opaque
|
||||
)
|
||||
{
|
||||
AudioState *s = &glob_audio_state;
|
||||
CaptureVoiceOut *cap;
|
||||
struct capture_callback *cb;
|
||||
|
||||
if (!s) {
|
||||
/* XXX suppress */
|
||||
s = &glob_audio_state;
|
||||
}
|
||||
|
||||
if (audio_validate_settings (as)) {
|
||||
dolog ("Invalid settings were passed when trying to add capture\n");
|
||||
audio_print_settings (as);
|
||||
@@ -1935,9 +1841,9 @@ CaptureVoiceOut *AUD_add_capture (
|
||||
cb->ops = *ops;
|
||||
cb->opaque = cb_opaque;
|
||||
|
||||
cap = audio_pcm_capture_find_specific (as);
|
||||
cap = audio_pcm_capture_find_specific (s, as);
|
||||
if (cap) {
|
||||
QLIST_INSERT_HEAD (&cap->cb_head, cb, entries);
|
||||
LIST_INSERT_HEAD (&cap->cb_head, cb, entries);
|
||||
return cap;
|
||||
}
|
||||
else {
|
||||
@@ -1952,8 +1858,8 @@ CaptureVoiceOut *AUD_add_capture (
|
||||
}
|
||||
|
||||
hw = &cap->hw;
|
||||
QLIST_INIT (&hw->sw_head);
|
||||
QLIST_INIT (&cap->cb_head);
|
||||
LIST_INIT (&hw->sw_head);
|
||||
LIST_INIT (&cap->cb_head);
|
||||
|
||||
/* XXX find a more elegant way */
|
||||
hw->samples = 4096 * 4;
|
||||
@@ -1981,12 +1887,12 @@ CaptureVoiceOut *AUD_add_capture (
|
||||
[hw->info.swap_endianness]
|
||||
[audio_bits_to_index (hw->info.bits)];
|
||||
|
||||
QLIST_INSERT_HEAD (&s->cap_head, cap, entries);
|
||||
QLIST_INSERT_HEAD (&cap->cb_head, cb, entries);
|
||||
LIST_INSERT_HEAD (&s->cap_head, cap, entries);
|
||||
LIST_INSERT_HEAD (&cap->cb_head, cb, entries);
|
||||
|
||||
hw = NULL;
|
||||
while ((hw = audio_pcm_hw_find_any_out (hw))) {
|
||||
audio_attach_capture (hw);
|
||||
while ((hw = audio_pcm_hw_find_any_out (s, hw))) {
|
||||
audio_attach_capture (s, hw);
|
||||
}
|
||||
return cap;
|
||||
|
||||
@@ -2008,7 +1914,7 @@ void AUD_del_capture (CaptureVoiceOut *cap, void *cb_opaque)
|
||||
for (cb = cap->cb_head.lh_first; cb; cb = cb->entries.le_next) {
|
||||
if (cb->opaque == cb_opaque) {
|
||||
cb->ops.destroy (cb_opaque);
|
||||
QLIST_REMOVE (cb, entries);
|
||||
LIST_REMOVE (cb, entries);
|
||||
qemu_free (cb);
|
||||
|
||||
if (!cap->cb_head.lh_first) {
|
||||
@@ -2025,12 +1931,12 @@ void AUD_del_capture (CaptureVoiceOut *cap, void *cb_opaque)
|
||||
st_rate_stop (sw->rate);
|
||||
sw->rate = NULL;
|
||||
}
|
||||
QLIST_REMOVE (sw, entries);
|
||||
QLIST_REMOVE (sc, entries);
|
||||
LIST_REMOVE (sw, entries);
|
||||
LIST_REMOVE (sc, entries);
|
||||
qemu_free (sc);
|
||||
sw = sw1;
|
||||
}
|
||||
QLIST_REMOVE (cap, entries);
|
||||
LIST_REMOVE (cap, entries);
|
||||
qemu_free (cap);
|
||||
}
|
||||
return;
|
||||
|
||||
+14
-8
@@ -25,9 +25,9 @@
|
||||
#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,
|
||||
@@ -38,7 +38,7 @@ typedef enum {
|
||||
AUD_FMT_S32
|
||||
} audfmt_e;
|
||||
|
||||
#ifdef HOST_WORDS_BIGENDIAN
|
||||
#ifdef WORDS_BIGENDIAN
|
||||
#define AUDIO_HOST_ENDIANNESS 1
|
||||
#else
|
||||
#define AUDIO_HOST_ENDIANNESS 0
|
||||
@@ -70,7 +70,7 @@ struct capture_ops {
|
||||
typedef struct CaptureState {
|
||||
void *opaque;
|
||||
struct capture_ops ops;
|
||||
QLIST_ENTRY (CaptureState) entries;
|
||||
LIST_ENTRY (CaptureState) entries;
|
||||
} CaptureState;
|
||||
|
||||
typedef struct SWVoiceOut SWVoiceOut;
|
||||
@@ -78,8 +78,9 @@ 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,10 +94,12 @@ 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 (
|
||||
AudioState *s,
|
||||
struct audsettings *as,
|
||||
struct audio_capture_ops *ops,
|
||||
void *opaque
|
||||
@@ -108,7 +111,7 @@ SWVoiceOut *AUD_open_out (
|
||||
SWVoiceOut *sw,
|
||||
const char *name,
|
||||
void *callback_opaque,
|
||||
audio_callback_fn callback_fn,
|
||||
audio_callback_fn_t callback_fn,
|
||||
struct audsettings *settings
|
||||
);
|
||||
|
||||
@@ -129,7 +132,7 @@ SWVoiceIn *AUD_open_in (
|
||||
SWVoiceIn *sw,
|
||||
const char *name,
|
||||
void *callback_opaque,
|
||||
audio_callback_fn callback_fn,
|
||||
audio_callback_fn_t callback_fn,
|
||||
struct audsettings *settings
|
||||
);
|
||||
|
||||
@@ -147,6 +150,9 @@ static inline void *advance (void *p, int incr)
|
||||
return (d + incr);
|
||||
}
|
||||
|
||||
uint32_t popcount (uint32_t u);
|
||||
uint32_t lsbindex (uint32_t u);
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define audio_MIN(a, b) ( __extension__ ({ \
|
||||
__typeof (a) ta = a; \
|
||||
|
||||
+21
-27
@@ -50,7 +50,7 @@ struct audio_option {
|
||||
|
||||
struct audio_callback {
|
||||
void *opaque;
|
||||
audio_callback_fn fn;
|
||||
audio_callback_fn_t fn;
|
||||
};
|
||||
|
||||
struct audio_pcm_info {
|
||||
@@ -68,7 +68,6 @@ typedef struct SWVoiceCap SWVoiceCap;
|
||||
|
||||
typedef struct HWVoiceOut {
|
||||
int enabled;
|
||||
int poll_mode;
|
||||
int pending_disable;
|
||||
struct audio_pcm_info info;
|
||||
|
||||
@@ -80,15 +79,14 @@ typedef struct HWVoiceOut {
|
||||
struct st_sample *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;
|
||||
LIST_HEAD (sw_cap_listhead, SWVoiceCap) cap_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;
|
||||
@@ -100,13 +98,12 @@ typedef struct HWVoiceIn {
|
||||
struct st_sample *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;
|
||||
@@ -119,11 +116,10 @@ struct SWVoiceOut {
|
||||
char *name;
|
||||
struct mixeng_volume 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;
|
||||
@@ -135,7 +131,7 @@ struct SWVoiceIn {
|
||||
char *name;
|
||||
struct mixeng_volume vol;
|
||||
struct audio_callback callback;
|
||||
QLIST_ENTRY (SWVoiceIn) entries;
|
||||
LIST_ENTRY (SWVoiceIn) entries;
|
||||
};
|
||||
|
||||
struct audio_driver {
|
||||
@@ -155,7 +151,7 @@ struct audio_driver {
|
||||
struct audio_pcm_ops {
|
||||
int (*init_out)(HWVoiceOut *hw, struct audsettings *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, ...);
|
||||
|
||||
@@ -169,20 +165,20 @@ struct audio_pcm_ops {
|
||||
struct capture_callback {
|
||||
struct audio_capture_ops ops;
|
||||
void *opaque;
|
||||
QLIST_ENTRY (capture_callback) entries;
|
||||
LIST_ENTRY (capture_callback) entries;
|
||||
};
|
||||
|
||||
struct CaptureVoiceOut {
|
||||
HWVoiceOut hw;
|
||||
void *buf;
|
||||
QLIST_HEAD (cb_listhead, capture_callback) cb_head;
|
||||
QLIST_ENTRY (CaptureVoiceOut) entries;
|
||||
LIST_HEAD (cb_listhead, capture_callback) cb_head;
|
||||
LIST_ENTRY (CaptureVoiceOut) entries;
|
||||
};
|
||||
|
||||
struct SWVoiceCap {
|
||||
SWVoiceOut sw;
|
||||
CaptureVoiceOut *cap;
|
||||
QLIST_ENTRY (SWVoiceCap) entries;
|
||||
LIST_ENTRY (SWVoiceCap) entries;
|
||||
};
|
||||
|
||||
struct AudioState {
|
||||
@@ -190,10 +186,10 @@ struct AudioState {
|
||||
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_listhead, QEMUSoundCard) card_head;
|
||||
LIST_HEAD (hw_in_listhead, HWVoiceIn) hw_head_in;
|
||||
LIST_HEAD (hw_out_listhead, HWVoiceOut) hw_head_out;
|
||||
LIST_HEAD (cap_listhead, CaptureVoiceOut) cap_head;
|
||||
int nb_hw_voices_out;
|
||||
int nb_hw_voices_in;
|
||||
int vm_running;
|
||||
@@ -209,7 +205,6 @@ 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;
|
||||
|
||||
void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as);
|
||||
@@ -219,15 +214,12 @@ 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
|
||||
|
||||
@@ -238,9 +230,11 @@ static inline int audio_ring_dist (int dst, int src, int len)
|
||||
|
||||
#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
|
||||
|
||||
|
||||
+64
-56
@@ -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);
|
||||
|
||||
@@ -184,24 +186,23 @@ 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 +211,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 +227,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
|
||||
)
|
||||
{
|
||||
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 +240,10 @@ 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,
|
||||
struct audsettings *as)
|
||||
{
|
||||
HW *hw;
|
||||
AudioState *s = &glob_audio_state;
|
||||
struct audio_driver *drv = s->drv;
|
||||
|
||||
if (!glue (s->nb_hw_voices_, TYPE)) {
|
||||
@@ -267,9 +268,9 @@ static HW *glue (audio_pcm_hw_add_new_, TYPE) (struct audsettings *as)
|
||||
}
|
||||
|
||||
hw->pcm_ops = drv->pcm_ops;
|
||||
QLIST_INIT (&hw->sw_head);
|
||||
LIST_INIT (&hw->sw_head);
|
||||
#ifdef DAC
|
||||
QLIST_INIT (&hw->cap_head);
|
||||
LIST_INIT (&hw->cap_head);
|
||||
#endif
|
||||
if (glue (hw->pcm_ops->init_, TYPE) (hw, as)) {
|
||||
goto err0;
|
||||
@@ -294,10 +295,10 @@ static HW *glue (audio_pcm_hw_add_new_, TYPE) (struct audsettings *as)
|
||||
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);
|
||||
audio_attach_capture (s, hw);
|
||||
#endif
|
||||
return hw;
|
||||
|
||||
@@ -308,31 +309,33 @@ 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,
|
||||
struct audsettings *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
|
||||
)
|
||||
@@ -355,7 +358,7 @@ 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;
|
||||
}
|
||||
@@ -370,30 +373,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 +406,11 @@ SW *glue (AUD_open_, TYPE) (
|
||||
SW *sw,
|
||||
const char *name,
|
||||
void *callback_opaque ,
|
||||
audio_callback_fn callback_fn,
|
||||
audio_callback_fn_t callback_fn,
|
||||
struct audsettings *as
|
||||
)
|
||||
{
|
||||
AudioState *s = &glob_audio_state;
|
||||
AudioState *s;
|
||||
#ifdef DAC
|
||||
int live = 0;
|
||||
SW *old_sw = NULL;
|
||||
@@ -415,12 +419,15 @@ 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;
|
||||
}
|
||||
|
||||
s = card->audio;
|
||||
|
||||
if (audio_bug (AUDIO_FUNC, audio_validate_settings (as))) {
|
||||
audio_print_settings (as);
|
||||
goto fail;
|
||||
@@ -445,9 +452,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) {
|
||||
@@ -478,37 +485,38 @@ SW *glue (AUD_open_, TYPE) (
|
||||
}
|
||||
}
|
||||
else {
|
||||
sw = glue (audio_pcm_create_voice_pair_, TYPE) (name, as);
|
||||
sw = glue (audio_pcm_create_voice_pair_, TYPE) (s, name, as);
|
||||
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 +562,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 */
|
||||
+32
-31
@@ -190,15 +190,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,
|
||||
@@ -511,39 +513,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
|
||||
};
|
||||
+155
-96
@@ -32,13 +32,12 @@
|
||||
#define AUDIO_CAP "dsound"
|
||||
#include "audio_int.h"
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#include <mmsystem.h>
|
||||
#include <objbase.h>
|
||||
#include <dsound.h>
|
||||
|
||||
#include "audio_win_int.h"
|
||||
|
||||
/* #define DEBUG_DSOUND */
|
||||
|
||||
static struct {
|
||||
@@ -51,16 +50,18 @@ static struct {
|
||||
struct audsettings 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 {
|
||||
@@ -306,6 +307,101 @@ static int dsound_restore_out (LPDIRECTSOUNDBUFFER dsb)
|
||||
return -1;
|
||||
}
|
||||
|
||||
static 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;
|
||||
}
|
||||
|
||||
static 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;
|
||||
}
|
||||
|
||||
#include "dsound_template.h"
|
||||
#define DSBTYPE_IN
|
||||
#include "dsound_template.h"
|
||||
@@ -565,13 +661,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 +683,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,
|
||||
@@ -938,93 +1036,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)
|
||||
};
|
||||
+44
-52
@@ -58,8 +58,10 @@ static struct {
|
||||
char *dac_host;
|
||||
char *adc_host;
|
||||
} conf = {
|
||||
.samples = 1024,
|
||||
.divisor = 2,
|
||||
1024,
|
||||
2,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
static void GCC_FMT_ATTR (2, 3) qesd_logerr (int err, const char *fmt, ...)
|
||||
@@ -131,7 +133,7 @@ static void *qesd_thread_out (void *arg)
|
||||
int wsamples = written >> hw->info.shift;
|
||||
int wbytes = wsamples << hw->info.shift;
|
||||
if (wbytes != written) {
|
||||
dolog ("warning: Misaligned write %d (requested %zd), "
|
||||
dolog ("warning: Misaligned write %d (requested %d), "
|
||||
"alignment %d\n",
|
||||
wbytes, written, hw->info.align + 1);
|
||||
}
|
||||
@@ -158,15 +160,16 @@ static void *qesd_thread_out (void *arg)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int qesd_run_out (HWVoiceOut *hw, int live)
|
||||
static int qesd_run_out (HWVoiceOut *hw)
|
||||
{
|
||||
int decr;
|
||||
int live, decr;
|
||||
ESDVoiceOut *esd = (ESDVoiceOut *) hw;
|
||||
|
||||
if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
live = audio_pcm_hw_get_live_out (hw);
|
||||
decr = audio_MIN (live, esd->decr);
|
||||
esd->decr -= decr;
|
||||
esd->live = live - decr;
|
||||
@@ -360,7 +363,7 @@ static void *qesd_thread_in (void *arg)
|
||||
int rsamples = nread >> hw->info.shift;
|
||||
int rbytes = rsamples << hw->info.shift;
|
||||
if (rbytes != nread) {
|
||||
dolog ("warning: Misaligned write %d (requested %zd), "
|
||||
dolog ("warning: Misaligned write %d (requested %d), "
|
||||
"alignment %d\n",
|
||||
rbytes, nread, hw->info.align + 1);
|
||||
}
|
||||
@@ -548,57 +551,46 @@ static void qesd_audio_fini (void *opaque)
|
||||
}
|
||||
|
||||
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 */ }
|
||||
{"SAMPLES", AUD_OPT_INT, &conf.samples,
|
||||
"buffer size in samples", NULL, 0},
|
||||
|
||||
{"DIVISOR", AUD_OPT_INT, &conf.divisor,
|
||||
"threshold divisor", NULL, 0},
|
||||
|
||||
{"DAC_HOST", AUD_OPT_STR, &conf.dac_host,
|
||||
"playback host", NULL, 0},
|
||||
|
||||
{"ADC_HOST", AUD_OPT_STR, &conf.adc_host,
|
||||
"capture host", NULL, 0},
|
||||
|
||||
{NULL, 0, NULL, NULL, NULL, 0}
|
||||
};
|
||||
|
||||
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,
|
||||
qesd_init_out,
|
||||
qesd_fini_out,
|
||||
qesd_run_out,
|
||||
qesd_write,
|
||||
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,
|
||||
qesd_init_in,
|
||||
qesd_fini_in,
|
||||
qesd_run_in,
|
||||
qesd_read,
|
||||
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)
|
||||
INIT_FIELD (name = ) "esd",
|
||||
INIT_FIELD (descr = )
|
||||
"http://en.wikipedia.org/wiki/Esound",
|
||||
INIT_FIELD (options = ) qesd_options,
|
||||
INIT_FIELD (init = ) qesd_audio_init,
|
||||
INIT_FIELD (fini = ) qesd_audio_fini,
|
||||
INIT_FIELD (pcm_ops = ) &qesd_pcm_ops,
|
||||
INIT_FIELD (can_be_default = ) 0,
|
||||
INIT_FIELD (max_voices_out = ) INT_MAX,
|
||||
INIT_FIELD (max_voices_in = ) INT_MAX,
|
||||
INIT_FIELD (voice_size_out = ) sizeof (ESDVoiceOut),
|
||||
INIT_FIELD (voice_size_in = ) sizeof (ESDVoiceIn)
|
||||
};
|
||||
+70
-71
@@ -47,11 +47,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, ...)
|
||||
@@ -224,15 +229,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;
|
||||
}
|
||||
|
||||
@@ -503,27 +517,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)
|
||||
@@ -625,63 +639,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)
|
||||
};
|
||||
+1
-1
@@ -27,7 +27,7 @@
|
||||
#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; };
|
||||
struct mixeng_sample { mixeng_real l; mixeng_real r; };
|
||||
#else
|
||||
struct mixeng_volume { int mute; int64_t r; int64_t l; };
|
||||
struct st_sample { int64_t l; int64_t r; };
|
||||
|
||||
+30
-26
@@ -38,17 +38,22 @@ 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;
|
||||
int live, decr, samples;
|
||||
int64_t now;
|
||||
int64_t ticks;
|
||||
int64_t bytes;
|
||||
|
||||
live = audio_pcm_hw_get_live_out (&no->hw);
|
||||
if (!live) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
now = qemu_get_clock (vm_clock);
|
||||
ticks = now - no->old_ticks;
|
||||
bytes = muldiv64 (ticks, hw->info.bytes_per_second, get_ticks_per_sec ());
|
||||
bytes = (ticks * hw->info.bytes_per_second) / ticks_per_sec;
|
||||
bytes = audio_MIN (bytes, INT_MAX);
|
||||
samples = bytes >> hw->info.shift;
|
||||
|
||||
@@ -104,8 +109,7 @@ static int no_run_in (HWVoiceIn *hw)
|
||||
if (dead) {
|
||||
int64_t now = qemu_get_clock (vm_clock);
|
||||
int64_t ticks = now - no->old_ticks;
|
||||
int64_t bytes =
|
||||
muldiv64 (ticks, hw->info.bytes_per_second, get_ticks_per_sec ());
|
||||
int64_t bytes = (ticks * hw->info.bytes_per_second) / ticks_per_sec;
|
||||
|
||||
no->old_ticks = now;
|
||||
bytes = audio_MIN (bytes, INT_MAX);
|
||||
@@ -142,29 +146,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)
|
||||
};
|
||||
+137
-260
@@ -31,8 +31,6 @@
|
||||
#include <sys/soundcard.h>
|
||||
#endif
|
||||
#include "qemu-common.h"
|
||||
#include "host-utils.h"
|
||||
#include "qemu-char.h"
|
||||
#include "audio.h"
|
||||
|
||||
#define AUDIO_CAP "oss"
|
||||
@@ -42,11 +40,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 +52,7 @@ typedef struct OSSVoiceIn {
|
||||
int fd;
|
||||
int nfrags;
|
||||
int fragsize;
|
||||
int old_optr;
|
||||
} OSSVoiceIn;
|
||||
|
||||
static struct {
|
||||
@@ -64,17 +62,13 @@ static struct {
|
||||
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
|
||||
.debug = 0
|
||||
};
|
||||
|
||||
struct oss_params {
|
||||
@@ -116,42 +110,13 @@ static void GCC_FMT_ATTR (3, 4) oss_logerr2 (
|
||||
|
||||
static void oss_anal_close (int *fdp)
|
||||
{
|
||||
int err;
|
||||
|
||||
qemu_set_fd_handler (*fdp, NULL, NULL, NULL);
|
||||
err = close (*fdp);
|
||||
int err = close (*fdp);
|
||||
if (err) {
|
||||
oss_logerr (errno, "Failed to close file(fd=%d)\n", *fdp);
|
||||
}
|
||||
*fdp = -1;
|
||||
}
|
||||
|
||||
static void oss_helper_poll_out (void *opaque)
|
||||
{
|
||||
(void) opaque;
|
||||
audio_run ("oss_poll_out");
|
||||
}
|
||||
|
||||
static void oss_helper_poll_in (void *opaque)
|
||||
{
|
||||
(void) opaque;
|
||||
audio_run ("oss_poll_in");
|
||||
}
|
||||
|
||||
static int oss_poll_out (HWVoiceOut *hw)
|
||||
{
|
||||
OSSVoiceOut *oss = (OSSVoiceOut *) hw;
|
||||
|
||||
return qemu_set_fd_handler (oss->fd, NULL, oss_helper_poll_out, NULL);
|
||||
}
|
||||
|
||||
static int oss_poll_in (HWVoiceIn *hw)
|
||||
{
|
||||
OSSVoiceIn *oss = (OSSVoiceIn *) hw;
|
||||
|
||||
return qemu_set_fd_handler (oss->fd, oss_helper_poll_in, NULL, NULL);
|
||||
}
|
||||
|
||||
static int oss_write (SWVoiceOut *sw, void *buf, int len)
|
||||
{
|
||||
return audio_pcm_sw_write (sw, buf, len);
|
||||
@@ -240,17 +205,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;
|
||||
@@ -281,33 +242,11 @@ static int oss_open (int in, struct oss_params *req,
|
||||
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)) {
|
||||
@@ -349,58 +288,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;
|
||||
struct st_sample *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 +315,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 {
|
||||
@@ -422,7 +341,7 @@ static int oss_run_out (HWVoiceOut *hw, int live)
|
||||
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",
|
||||
"please report your OS/audio hw to malc@pulsesoft.com\n",
|
||||
abinfo.bytes, bufsize);
|
||||
}
|
||||
abinfo.bytes = bufsize;
|
||||
@@ -442,10 +361,53 @@ 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);
|
||||
}
|
||||
decr -= wsamples;
|
||||
rpos = (rpos + wsamples) % hw->samples;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
rpos = (rpos + convert_samples) % hw->samples;
|
||||
samples -= convert_samples;
|
||||
}
|
||||
if (oss->mmapped) {
|
||||
oss->old_optr = cntinfo.ptr;
|
||||
}
|
||||
|
||||
hw->rpos = rpos;
|
||||
return decr;
|
||||
}
|
||||
|
||||
@@ -519,7 +481,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 +491,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 +546,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) {
|
||||
@@ -716,8 +654,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 +670,7 @@ static int oss_run_in (HWVoiceIn *hw)
|
||||
bufs[0].len = dead << hwshift;
|
||||
}
|
||||
|
||||
|
||||
for (i = 0; i < 2; ++i) {
|
||||
ssize_t nread;
|
||||
|
||||
@@ -781,32 +720,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 +736,45 @@ 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},
|
||||
{"DEBUG", AUD_OPT_BOOL, &conf.debug,
|
||||
"Turn on some debugging messages", 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)
|
||||
};
|
||||
+47
-59
@@ -38,8 +38,11 @@ static struct {
|
||||
char *sink;
|
||||
char *source;
|
||||
} conf = {
|
||||
.samples = 1024,
|
||||
.divisor = 2,
|
||||
1024,
|
||||
2,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
static void GCC_FMT_ATTR (2, 3) qpa_logerr (int err, const char *fmt, ...)
|
||||
@@ -120,15 +123,16 @@ static void *qpa_thread_out (void *arg)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int qpa_run_out (HWVoiceOut *hw, int live)
|
||||
static int qpa_run_out (HWVoiceOut *hw)
|
||||
{
|
||||
int decr;
|
||||
int live, decr;
|
||||
PAVoiceOut *pa = (PAVoiceOut *) hw;
|
||||
|
||||
if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
live = audio_pcm_hw_get_live_out (hw);
|
||||
decr = audio_MIN (live, pa->decr);
|
||||
pa->decr -= decr;
|
||||
pa->live = live - decr;
|
||||
@@ -336,7 +340,7 @@ static int qpa_init_out (HWVoiceOut *hw, struct audsettings *as)
|
||||
return 0;
|
||||
|
||||
fail3:
|
||||
qemu_free (pa->pcm_buf);
|
||||
free (pa->pcm_buf);
|
||||
pa->pcm_buf = NULL;
|
||||
fail2:
|
||||
pa_simple_free (pa->s);
|
||||
@@ -390,7 +394,7 @@ static int qpa_init_in (HWVoiceIn *hw, struct audsettings *as)
|
||||
return 0;
|
||||
|
||||
fail3:
|
||||
qemu_free (pa->pcm_buf);
|
||||
free (pa->pcm_buf);
|
||||
pa->pcm_buf = NULL;
|
||||
fail2:
|
||||
pa_simple_free (pa->s);
|
||||
@@ -465,63 +469,47 @@ static void qpa_audio_fini (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 */ }
|
||||
{"SAMPLES", AUD_OPT_INT, &conf.samples,
|
||||
"buffer size in samples", NULL, 0},
|
||||
|
||||
{"DIVISOR", AUD_OPT_INT, &conf.divisor,
|
||||
"threshold divisor", NULL, 0},
|
||||
|
||||
{"SERVER", AUD_OPT_STR, &conf.server,
|
||||
"server address", NULL, 0},
|
||||
|
||||
{"SINK", AUD_OPT_STR, &conf.sink,
|
||||
"sink device name", NULL, 0},
|
||||
|
||||
{"SOURCE", AUD_OPT_STR, &conf.source,
|
||||
"source device name", NULL, 0},
|
||||
|
||||
{NULL, 0, NULL, NULL, NULL, 0}
|
||||
};
|
||||
|
||||
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
|
||||
qpa_init_out,
|
||||
qpa_fini_out,
|
||||
qpa_run_out,
|
||||
qpa_write,
|
||||
qpa_ctl_out,
|
||||
qpa_init_in,
|
||||
qpa_fini_in,
|
||||
qpa_run_in,
|
||||
qpa_read,
|
||||
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)
|
||||
INIT_FIELD (name = ) "pa",
|
||||
INIT_FIELD (descr = ) "http://www.pulseaudio.org/",
|
||||
INIT_FIELD (options = ) qpa_options,
|
||||
INIT_FIELD (init = ) qpa_audio_init,
|
||||
INIT_FIELD (fini = ) qpa_audio_fini,
|
||||
INIT_FIELD (pcm_ops = ) &qpa_pcm_ops,
|
||||
INIT_FIELD (can_be_default = ) 0,
|
||||
INIT_FIELD (max_voices_out = ) INT_MAX,
|
||||
INIT_FIELD (max_voices_in = ) INT_MAX,
|
||||
INIT_FIELD (voice_size_out = ) sizeof (PAVoiceOut),
|
||||
INIT_FIELD (voice_size_in = ) sizeof (PAVoiceIn)
|
||||
};
|
||||
+87
-63
@@ -29,7 +29,7 @@
|
||||
#ifndef _WIN32
|
||||
#ifdef __sun__
|
||||
#define _POSIX_PTHREAD_SEMANTICS 1
|
||||
#elif defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__DragonFly__)
|
||||
#elif defined(__OpenBSD__) || defined(__FreeBSD__)
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
#include <signal.h>
|
||||
@@ -41,14 +41,14 @@
|
||||
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 {
|
||||
@@ -201,7 +201,7 @@ static int sdl_open (SDL_AudioSpec *req, SDL_AudioSpec *obt)
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
pthread_sigmask (SIG_SETMASK, &old, NULL);
|
||||
pthread_sigmask (SIG_SETMASK, &old, 0);
|
||||
#endif
|
||||
return status;
|
||||
}
|
||||
@@ -225,10 +225,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 +232,49 @@ 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);
|
||||
struct st_sample *src = hw->mix_buf + hw->rpos;
|
||||
|
||||
/* dolog ("in callback to_mix %d, chunk %d\n", to_mix, chunk); */
|
||||
hw->clip (buf, src, chunk);
|
||||
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 +282,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;
|
||||
}
|
||||
@@ -398,33 +420,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
|
||||
};
|
||||
+50
-49
@@ -40,22 +40,24 @@ static struct {
|
||||
struct audsettings 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,
|
||||
0
|
||||
},
|
||||
"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;
|
||||
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 +66,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;
|
||||
@@ -212,51 +219,45 @@ static void wav_audio_fini (void *opaque)
|
||||
}
|
||||
|
||||
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 */ }
|
||||
{"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,
|
||||
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
|
||||
};
|
||||
+11
-12
@@ -1,5 +1,5 @@
|
||||
#include "hw/hw.h"
|
||||
#include "monitor.h"
|
||||
#include "console.h"
|
||||
#include "audio.h"
|
||||
|
||||
typedef struct {
|
||||
@@ -71,9 +71,9 @@ 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);
|
||||
term_printf ("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 = {
|
||||
@@ -84,7 +84,6 @@ static struct capture_ops wav_capture_ops = {
|
||||
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,
|
||||
@@ -98,13 +97,13 @@ int wav_start_capture (CaptureState *s, const char *path, int freq,
|
||||
CaptureVoiceOut *cap;
|
||||
|
||||
if (bits != 8 && bits != 16) {
|
||||
monitor_printf(mon, "incorrect bit count %d, must be 8 or 16\n", bits);
|
||||
term_printf ("incorrect bit count %d, must be 8 or 16\n", bits);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (nchannels != 1 && nchannels != 2) {
|
||||
monitor_printf(mon, "incorrect channel count %d, must be 1 or 2\n",
|
||||
nchannels);
|
||||
term_printf ("incorrect channel count %d, must be 1 or 2\n",
|
||||
nchannels);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -132,8 +131,8 @@ int wav_start_capture (CaptureState *s, const char *path, int freq,
|
||||
|
||||
wav->f = qemu_fopen (path, "wb");
|
||||
if (!wav->f) {
|
||||
monitor_printf(mon, "Failed to open wave file `%s'\nReason: %s\n",
|
||||
path, strerror (errno));
|
||||
term_printf ("Failed to open wave file `%s'\nReason: %s\n",
|
||||
path, strerror (errno));
|
||||
qemu_free (wav);
|
||||
return -1;
|
||||
}
|
||||
@@ -145,9 +144,9 @@ int wav_start_capture (CaptureState *s, const char *path, int freq,
|
||||
|
||||
qemu_put_buffer (wav->f, hdr, sizeof (hdr));
|
||||
|
||||
cap = AUD_add_capture (&as, &ops, wav);
|
||||
cap = AUD_add_capture (NULL, &as, &ops, wav);
|
||||
if (!cap) {
|
||||
monitor_printf(mon, "Failed to add audio capture\n");
|
||||
term_printf ("Failed to add audio capture\n");
|
||||
qemu_free (wav->path);
|
||||
qemu_fclose (wav->f);
|
||||
qemu_free (wav);
|
||||
|
||||
@@ -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)
|
||||
};
|
||||
@@ -24,7 +24,6 @@
|
||||
*/
|
||||
#include "qemu-common.h"
|
||||
#include "block_int.h"
|
||||
#include "module.h"
|
||||
|
||||
/**************************************************************/
|
||||
|
||||
@@ -242,18 +241,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);
|
||||
@@ -23,7 +23,6 @@
|
||||
*/
|
||||
#include "qemu-common.h"
|
||||
#include "block_int.h"
|
||||
#include "module.h"
|
||||
#include <zlib.h>
|
||||
|
||||
typedef struct BDRVCloopState {
|
||||
@@ -154,18 +153,12 @@ 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);
|
||||
+21
-53
@@ -24,7 +24,6 @@
|
||||
#ifndef _WIN32
|
||||
#include "qemu-common.h"
|
||||
#include "block_int.h"
|
||||
#include "module.h"
|
||||
#include <sys/mman.h>
|
||||
|
||||
/**************************************************************/
|
||||
@@ -96,10 +95,10 @@ static int cow_open(BlockDriverState *bs, const char *filename, int flags)
|
||||
|
||||
/* 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);
|
||||
@@ -198,27 +197,19 @@ 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,
|
||||
0644);
|
||||
@@ -258,42 +249,19 @@ static int cow_create(const char *filename, QEMUOptionParameter *options)
|
||||
static void cow_flush(BlockDriverState *bs)
|
||||
{
|
||||
BDRVCowState *s = bs->opaque;
|
||||
qemu_fdatasync(s->fd);
|
||||
fsync(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_flush,
|
||||
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
|
||||
+23
-30
@@ -24,7 +24,6 @@
|
||||
#include "qemu-common.h"
|
||||
#include "block_int.h"
|
||||
#include "bswap.h"
|
||||
#include "module.h"
|
||||
#include <zlib.h>
|
||||
|
||||
typedef struct BDRVDMGState {
|
||||
@@ -86,25 +85,28 @@ static int dmg_open(BlockDriverState *bs, const char *filename, int flags)
|
||||
return -errno;
|
||||
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, flags);
|
||||
}
|
||||
|
||||
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,14 +115,14 @@ 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);
|
||||
@@ -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);
|
||||
@@ -160,14 +162,11 @@ static int dmg_open(BlockDriverState *bs, const char *filename, int flags)
|
||||
s->compressed_chunk = qemu_malloc(max_compressed_size+1);
|
||||
s->uncompressed_chunk = qemu_malloc(512*max_sectors_per_chunk);
|
||||
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,
|
||||
@@ -284,18 +283,12 @@ 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 */
|
||||
+10
-17
@@ -28,7 +28,6 @@
|
||||
|
||||
#include "qemu-common.h"
|
||||
#include "nbd.h"
|
||||
#include "module.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
@@ -177,20 +176,14 @@ static int64_t nbd_getlength(BlockDriverState *bs)
|
||||
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",
|
||||
BlockDriver bdrv_nbd = {
|
||||
"nbd",
|
||||
sizeof(BDRVNBDState),
|
||||
NULL, /* no probe for protocols */
|
||||
nbd_open,
|
||||
nbd_read,
|
||||
nbd_write,
|
||||
nbd_close,
|
||||
.bdrv_getlength = nbd_getlength,
|
||||
.protocol_name = "nbd",
|
||||
};
|
||||
|
||||
static void bdrv_nbd_init(void)
|
||||
{
|
||||
bdrv_register(&bdrv_nbd);
|
||||
}
|
||||
|
||||
block_init(bdrv_nbd_init);
|
||||
@@ -25,7 +25,6 @@
|
||||
*/
|
||||
#include "qemu-common.h"
|
||||
#include "block_int.h"
|
||||
#include "module.h"
|
||||
|
||||
/**************************************************************/
|
||||
|
||||
@@ -119,8 +118,7 @@ fail:
|
||||
static inline int seek_to_sector(BlockDriverState *bs, int64_t sector_num)
|
||||
{
|
||||
BDRVParallelsState *s = bs->opaque;
|
||||
uint32_t index, offset;
|
||||
uint64_t position;
|
||||
uint32_t index, offset, position;
|
||||
|
||||
index = sector_num / s->tracks;
|
||||
offset = sector_num % s->tracks;
|
||||
@@ -129,7 +127,7 @@ static inline int seek_to_sector(BlockDriverState *bs, int64_t sector_num)
|
||||
if ((index > s->catalog_size) || (s->catalog_bitmap[index] == 0))
|
||||
return -1;
|
||||
|
||||
position = (uint64_t)(s->catalog_bitmap[index] + offset) * 512;
|
||||
position = (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);
|
||||
@@ -165,18 +163,12 @@ static void parallels_close(BlockDriverState *bs)
|
||||
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,
|
||||
BlockDriver bdrv_parallels = {
|
||||
"parallels",
|
||||
sizeof(BDRVParallelsState),
|
||||
parallels_probe,
|
||||
parallels_open,
|
||||
parallels_read,
|
||||
NULL,
|
||||
parallels_close,
|
||||
};
|
||||
|
||||
static void bdrv_parallels_init(void)
|
||||
{
|
||||
bdrv_register(&bdrv_parallels);
|
||||
}
|
||||
|
||||
block_init(bdrv_parallels_init);
|
||||
+114
-159
@@ -23,7 +23,6 @@
|
||||
*/
|
||||
#include "qemu-common.h"
|
||||
#include "block_int.h"
|
||||
#include "module.h"
|
||||
#include <zlib.h>
|
||||
#include "aes.h"
|
||||
|
||||
@@ -488,59 +487,52 @@ static int qcow_read(BlockDriverState *bs, int64_t sector_num,
|
||||
}
|
||||
#endif
|
||||
|
||||
static int qcow_write(BlockDriverState *bs, int64_t sector_num,
|
||||
const uint8_t *buf, int nb_sectors)
|
||||
{
|
||||
BDRVQcowState *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, 0,
|
||||
index_in_cluster,
|
||||
index_in_cluster + n);
|
||||
if (!cluster_offset)
|
||||
return -1;
|
||||
if (s->crypt_method) {
|
||||
encrypt_sectors(s, sector_num, s->cluster_data, buf, n, 1,
|
||||
&s->aes_encrypt_key);
|
||||
ret = bdrv_pwrite(s->hd, cluster_offset + index_in_cluster * 512,
|
||||
s->cluster_data, n * 512);
|
||||
} else {
|
||||
ret = bdrv_pwrite(s->hd, cluster_offset + index_in_cluster * 512, buf, n * 512);
|
||||
}
|
||||
if (ret != n * 512)
|
||||
return -1;
|
||||
nb_sectors -= n;
|
||||
sector_num += n;
|
||||
buf += n * 512;
|
||||
}
|
||||
s->cluster_cache_offset = -1; /* disable compressed cache */
|
||||
return 0;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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;
|
||||
@@ -549,8 +541,12 @@ static void qcow_aio_read_cb(void *opaque, int ret)
|
||||
int index_in_cluster;
|
||||
|
||||
acb->hd_aiocb = NULL;
|
||||
if (ret < 0)
|
||||
goto done;
|
||||
if (ret < 0) {
|
||||
fail:
|
||||
acb->common.cb(acb->common.opaque, ret);
|
||||
qemu_aio_release(acb);
|
||||
return;
|
||||
}
|
||||
|
||||
redo:
|
||||
/* post process the read buffer */
|
||||
@@ -572,8 +568,9 @@ static void qcow_aio_read_cb(void *opaque, int ret)
|
||||
|
||||
if (acb->nb_sectors == 0) {
|
||||
/* request completed */
|
||||
ret = 0;
|
||||
goto done;
|
||||
acb->common.cb(acb->common.opaque, 0);
|
||||
qemu_aio_release(acb);
|
||||
return;
|
||||
}
|
||||
|
||||
/* prepare next AIO request */
|
||||
@@ -587,13 +584,10 @@ static void qcow_aio_read_cb(void *opaque, int ret)
|
||||
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);
|
||||
acb->hd_aiocb = bdrv_aio_read(bs->backing_hd,
|
||||
acb->sector_num, acb->buf, acb->n, qcow_aio_read_cb, acb);
|
||||
if (acb->hd_aiocb == NULL)
|
||||
goto done;
|
||||
goto fail;
|
||||
} else {
|
||||
/* Note: in this case, no need to wait */
|
||||
memset(acb->buf, 0, 512 * acb->n);
|
||||
@@ -602,45 +596,38 @@ static void qcow_aio_read_cb(void *opaque, int ret)
|
||||
} else if (acb->cluster_offset & QCOW_OFLAG_COMPRESSED) {
|
||||
/* add AIO support for compressed blocks ? */
|
||||
if (decompress_cluster(s, acb->cluster_offset) < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
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;
|
||||
goto fail;
|
||||
}
|
||||
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->hd_aiocb = bdrv_aio_read(s->hd,
|
||||
(acb->cluster_offset >> 9) + index_in_cluster,
|
||||
&acb->hd_qiov, acb->n, qcow_aio_read_cb, acb);
|
||||
acb->buf, acb->n, qcow_aio_read_cb, acb);
|
||||
if (acb->hd_aiocb == NULL)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
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,
|
||||
static BlockDriverAIOCB *qcow_aio_read(BlockDriverState *bs,
|
||||
int64_t sector_num, uint8_t *buf, int nb_sectors,
|
||||
BlockDriverCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
QCowAIOCB *acb;
|
||||
|
||||
acb = qcow_aio_setup(bs, sector_num, qiov, nb_sectors, cb, opaque, 0);
|
||||
acb = qemu_aio_get(bs, cb, opaque);
|
||||
if (!acb)
|
||||
return NULL;
|
||||
acb->hd_aiocb = NULL;
|
||||
acb->sector_num = sector_num;
|
||||
acb->buf = buf;
|
||||
acb->nb_sectors = nb_sectors;
|
||||
acb->n = 0;
|
||||
acb->cluster_offset = 0;
|
||||
|
||||
qcow_aio_read_cb(acb, 0);
|
||||
return &acb->common;
|
||||
@@ -657,8 +644,12 @@ static void qcow_aio_write_cb(void *opaque, int ret)
|
||||
|
||||
acb->hd_aiocb = NULL;
|
||||
|
||||
if (ret < 0)
|
||||
goto done;
|
||||
if (ret < 0) {
|
||||
fail:
|
||||
acb->common.cb(acb->common.opaque, ret);
|
||||
qemu_aio_release(acb);
|
||||
return;
|
||||
}
|
||||
|
||||
acb->nb_sectors -= acb->n;
|
||||
acb->sector_num += acb->n;
|
||||
@@ -666,8 +657,9 @@ static void qcow_aio_write_cb(void *opaque, int ret)
|
||||
|
||||
if (acb->nb_sectors == 0) {
|
||||
/* request completed */
|
||||
ret = 0;
|
||||
goto done;
|
||||
acb->common.cb(acb->common.opaque, 0);
|
||||
qemu_aio_release(acb);
|
||||
return;
|
||||
}
|
||||
|
||||
index_in_cluster = acb->sector_num & (s->cluster_sectors - 1);
|
||||
@@ -679,14 +671,14 @@ static void qcow_aio_write_cb(void *opaque, int ret)
|
||||
index_in_cluster + acb->n);
|
||||
if (!cluster_offset || (cluster_offset & 511) != 0) {
|
||||
ret = -EIO;
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
if (s->crypt_method) {
|
||||
if (!acb->cluster_data) {
|
||||
acb->cluster_data = qemu_mallocz(s->cluster_size);
|
||||
if (!acb->cluster_data) {
|
||||
ret = -ENOMEM;
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
encrypt_sectors(s, acb->sector_num, acb->cluster_data, acb->buf,
|
||||
@@ -695,27 +687,16 @@ static void qcow_aio_write_cb(void *opaque, int ret)
|
||||
} 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);
|
||||
acb->hd_aiocb = bdrv_aio_write(s->hd,
|
||||
(cluster_offset >> 9) + index_in_cluster,
|
||||
src_buf, 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);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
static BlockDriverAIOCB *qcow_aio_writev(BlockDriverState *bs,
|
||||
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
|
||||
static BlockDriverAIOCB *qcow_aio_write(BlockDriverState *bs,
|
||||
int64_t sector_num, const uint8_t *buf, int nb_sectors,
|
||||
BlockDriverCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
@@ -723,15 +704,27 @@ static BlockDriverAIOCB *qcow_aio_writev(BlockDriverState *bs,
|
||||
|
||||
s->cluster_cache_offset = -1; /* disable compressed cache */
|
||||
|
||||
acb = qcow_aio_setup(bs, sector_num, qiov, nb_sectors, cb, opaque, 1);
|
||||
acb = qemu_aio_get(bs, cb, opaque);
|
||||
if (!acb)
|
||||
return NULL;
|
||||
|
||||
acb->hd_aiocb = NULL;
|
||||
acb->sector_num = sector_num;
|
||||
acb->buf = (uint8_t *)buf;
|
||||
acb->nb_sectors = nb_sectors;
|
||||
acb->n = 0;
|
||||
|
||||
qcow_aio_write_cb(acb, 0);
|
||||
return &acb->common;
|
||||
}
|
||||
|
||||
static void qcow_aio_cancel(BlockDriverAIOCB *blockacb)
|
||||
{
|
||||
QCowAIOCB *acb = (QCowAIOCB *)blockacb;
|
||||
if (acb->hd_aiocb)
|
||||
bdrv_aio_cancel(acb->hd_aiocb);
|
||||
qemu_aio_release(acb);
|
||||
}
|
||||
|
||||
static void qcow_close(BlockDriverState *bs)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
@@ -742,26 +735,12 @@ static void qcow_close(BlockDriverState *bs)
|
||||
bdrv_delete(s->hd);
|
||||
}
|
||||
|
||||
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;
|
||||
uint64_t tmp;
|
||||
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_ENCRYPT)) {
|
||||
flags |= options->value.n ? BLOCK_FLAG_ENCRYPT : 0;
|
||||
}
|
||||
options++;
|
||||
}
|
||||
|
||||
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
|
||||
if (fd < 0)
|
||||
@@ -879,7 +858,7 @@ static int qcow_write_compressed(BlockDriverState *bs, int64_t sector_num,
|
||||
|
||||
if (ret != Z_STREAM_END || out_len >= s->cluster_size) {
|
||||
/* could not compress: write normal cluster */
|
||||
bdrv_write(bs, sector_num, buf, s->cluster_sectors);
|
||||
qcow_write(bs, sector_num, buf, s->cluster_sectors);
|
||||
} else {
|
||||
cluster_offset = get_cluster_offset(bs, sector_num << 9, 2,
|
||||
out_len, 0, 0);
|
||||
@@ -907,48 +886,24 @@ static int qcow_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
|
||||
return 0;
|
||||
}
|
||||
|
||||
BlockDriver bdrv_qcow = {
|
||||
"qcow",
|
||||
sizeof(BDRVQcowState),
|
||||
qcow_probe,
|
||||
qcow_open,
|
||||
NULL,
|
||||
NULL,
|
||||
qcow_close,
|
||||
qcow_create,
|
||||
qcow_flush,
|
||||
qcow_is_allocated,
|
||||
qcow_set_key,
|
||||
qcow_make_empty,
|
||||
|
||||
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 }
|
||||
};
|
||||
|
||||
static BlockDriver bdrv_qcow = {
|
||||
.format_name = "qcow",
|
||||
.instance_size = sizeof(BDRVQcowState),
|
||||
.bdrv_probe = qcow_probe,
|
||||
.bdrv_open = qcow_open,
|
||||
.bdrv_close = qcow_close,
|
||||
.bdrv_create = qcow_create,
|
||||
.bdrv_flush = qcow_flush,
|
||||
.bdrv_is_allocated = qcow_is_allocated,
|
||||
.bdrv_set_key = qcow_set_key,
|
||||
.bdrv_make_empty = qcow_make_empty,
|
||||
.bdrv_aio_readv = qcow_aio_readv,
|
||||
.bdrv_aio_writev = qcow_aio_writev,
|
||||
.bdrv_aio_read = qcow_aio_read,
|
||||
.bdrv_aio_write = qcow_aio_write,
|
||||
.bdrv_aio_cancel = qcow_aio_cancel,
|
||||
.aiocb_size = sizeof(QCowAIOCB),
|
||||
.bdrv_write_compressed = qcow_write_compressed,
|
||||
.bdrv_get_info = qcow_get_info,
|
||||
|
||||
.create_options = qcow_create_options,
|
||||
.bdrv_get_info = qcow_get_info,
|
||||
};
|
||||
|
||||
static void bdrv_qcow_init(void)
|
||||
{
|
||||
bdrv_register(&bdrv_qcow);
|
||||
}
|
||||
|
||||
block_init(bdrv_qcow_init);
|
||||
+2657
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -24,10 +24,11 @@
|
||||
#include "qemu-common.h"
|
||||
#include "qemu-timer.h"
|
||||
#include "block_int.h"
|
||||
#include "module.h"
|
||||
#include <windows.h>
|
||||
#include <assert.h>
|
||||
#include <winioctl.h>
|
||||
|
||||
//#define WIN32_AIO
|
||||
|
||||
#define FTYPE_FILE 0
|
||||
#define FTYPE_CD 1
|
||||
#define FTYPE_HARDDISK 2
|
||||
@@ -38,6 +39,13 @@ typedef struct BDRVRawState {
|
||||
char drive_path[16]; /* format: "d:\" */
|
||||
} BDRVRawState;
|
||||
|
||||
typedef struct RawAIOCB {
|
||||
BlockDriverAIOCB common;
|
||||
HANDLE hEvent;
|
||||
OVERLAPPED ov;
|
||||
int count;
|
||||
} RawAIOCB;
|
||||
|
||||
int qemu_ftruncate64(int fd, int64_t length)
|
||||
{
|
||||
LARGE_INTEGER li;
|
||||
@@ -91,7 +99,11 @@ static int raw_open(BlockDriverState *bs, const char *filename, int flags)
|
||||
} else {
|
||||
create_flags = OPEN_EXISTING;
|
||||
}
|
||||
#ifdef WIN32_AIO
|
||||
overlapped = FILE_FLAG_OVERLAPPED;
|
||||
#else
|
||||
overlapped = FILE_ATTRIBUTE_NORMAL;
|
||||
#endif
|
||||
if ((flags & BDRV_O_NOCACHE))
|
||||
overlapped |= FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH;
|
||||
else if (!(flags & BDRV_O_CACHE_WB))
|
||||
@@ -109,48 +121,149 @@ static int raw_open(BlockDriverState *bs, const char *filename, int flags)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int raw_read(BlockDriverState *bs, int64_t sector_num,
|
||||
uint8_t *buf, int nb_sectors)
|
||||
static int raw_pread(BlockDriverState *bs, int64_t offset,
|
||||
uint8_t *buf, int count)
|
||||
{
|
||||
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;
|
||||
if (!ret) {
|
||||
#ifdef WIN32_AIO
|
||||
ret = GetOverlappedResult(s->hfile, &ov, &ret_count, TRUE);
|
||||
if (!ret)
|
||||
return -EIO;
|
||||
else
|
||||
#endif
|
||||
return ret_count;
|
||||
}
|
||||
return ret_count;
|
||||
}
|
||||
|
||||
static int raw_write(BlockDriverState *bs, int64_t sector_num,
|
||||
const uint8_t *buf, int nb_sectors)
|
||||
static int raw_pwrite(BlockDriverState *bs, int64_t offset,
|
||||
const uint8_t *buf, int count)
|
||||
{
|
||||
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;
|
||||
if (!ret) {
|
||||
#ifdef WIN32_AIO
|
||||
ret = GetOverlappedResult(s->hfile, &ov, &ret_count, TRUE);
|
||||
if (!ret)
|
||||
return -EIO;
|
||||
else
|
||||
#endif
|
||||
return ret_count;
|
||||
}
|
||||
return ret_count;
|
||||
}
|
||||
|
||||
#ifdef WIN32_AIO
|
||||
static void raw_aio_cb(void *opaque)
|
||||
{
|
||||
RawAIOCB *acb = opaque;
|
||||
BlockDriverState *bs = acb->common.bs;
|
||||
BDRVRawState *s = bs->opaque;
|
||||
DWORD ret_count;
|
||||
int ret;
|
||||
|
||||
ret = GetOverlappedResult(s->hfile, &acb->ov, &ret_count, TRUE);
|
||||
if (!ret || ret_count != acb->count) {
|
||||
acb->common.cb(acb->common.opaque, -EIO);
|
||||
} else {
|
||||
acb->common.cb(acb->common.opaque, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static RawAIOCB *raw_aio_setup(BlockDriverState *bs,
|
||||
int64_t sector_num, uint8_t *buf, int nb_sectors,
|
||||
BlockDriverCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
RawAIOCB *acb;
|
||||
int64_t offset;
|
||||
|
||||
acb = qemu_aio_get(bs, cb, opaque);
|
||||
if (acb->hEvent) {
|
||||
acb->hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
if (!acb->hEvent) {
|
||||
qemu_aio_release(acb);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
memset(&acb->ov, 0, sizeof(acb->ov));
|
||||
offset = sector_num * 512;
|
||||
acb->ov.Offset = offset;
|
||||
acb->ov.OffsetHigh = offset >> 32;
|
||||
acb->ov.hEvent = acb->hEvent;
|
||||
acb->count = nb_sectors * 512;
|
||||
qemu_add_wait_object(acb->ov.hEvent, raw_aio_cb, acb);
|
||||
return acb;
|
||||
}
|
||||
|
||||
static BlockDriverAIOCB *raw_aio_read(BlockDriverState *bs,
|
||||
int64_t sector_num, uint8_t *buf, int nb_sectors,
|
||||
BlockDriverCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
RawAIOCB *acb;
|
||||
int ret;
|
||||
|
||||
acb = raw_aio_setup(bs, sector_num, buf, nb_sectors, cb, opaque);
|
||||
if (!acb)
|
||||
return NULL;
|
||||
ret = ReadFile(s->hfile, buf, acb->count, NULL, &acb->ov);
|
||||
if (!ret) {
|
||||
qemu_aio_release(acb);
|
||||
return NULL;
|
||||
}
|
||||
qemu_aio_release(acb);
|
||||
return (BlockDriverAIOCB *)acb;
|
||||
}
|
||||
|
||||
static BlockDriverAIOCB *raw_aio_write(BlockDriverState *bs,
|
||||
int64_t sector_num, uint8_t *buf, int nb_sectors,
|
||||
BlockDriverCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
RawAIOCB *acb;
|
||||
int ret;
|
||||
|
||||
acb = raw_aio_setup(bs, sector_num, buf, nb_sectors, cb, opaque);
|
||||
if (!acb)
|
||||
return NULL;
|
||||
ret = WriteFile(s->hfile, buf, acb->count, NULL, &acb->ov);
|
||||
if (!ret) {
|
||||
qemu_aio_release(acb);
|
||||
return NULL;
|
||||
}
|
||||
qemu_aio_release(acb);
|
||||
return (BlockDriverAIOCB *)acb;
|
||||
}
|
||||
|
||||
static void raw_aio_cancel(BlockDriverAIOCB *blockacb)
|
||||
{
|
||||
RawAIOCB *acb = (RawAIOCB *)blockacb;
|
||||
BlockDriverState *bs = acb->common.bs;
|
||||
BDRVRawState *s = bs->opaque;
|
||||
|
||||
qemu_del_wait_object(acb->ov.hEvent, raw_aio_cb, acb);
|
||||
/* XXX: if more than one async I/O it is not correct */
|
||||
CancelIo(s->hfile);
|
||||
qemu_aio_release(acb);
|
||||
}
|
||||
#endif /* #if WIN32_AIO */
|
||||
|
||||
static void raw_flush(BlockDriverState *bs)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
@@ -166,7 +279,7 @@ static void raw_close(BlockDriverState *bs)
|
||||
static int raw_truncate(BlockDriverState *bs, int64_t offset)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
LONG low, high;
|
||||
DWORD low, high;
|
||||
|
||||
low = offset;
|
||||
high = offset >> 32;
|
||||
@@ -188,7 +301,7 @@ static int64_t raw_getlength(BlockDriverState *bs)
|
||||
|
||||
switch(s->type) {
|
||||
case FTYPE_FILE:
|
||||
l.LowPart = GetFileSize(s->hfile, (PDWORD)&l.HighPart);
|
||||
l.LowPart = GetFileSize(s->hfile, &l.HighPart);
|
||||
if (l.LowPart == 0xffffffffUL && GetLastError() != NO_ERROR)
|
||||
return -EIO;
|
||||
break;
|
||||
@@ -210,18 +323,13 @@ static int64_t raw_getlength(BlockDriverState *bs)
|
||||
return l.QuadPart;
|
||||
}
|
||||
|
||||
static int raw_create(const char *filename, QEMUOptionParameter *options)
|
||||
static int raw_create(const char *filename, int64_t total_size,
|
||||
const char *backing_file, int flags)
|
||||
{
|
||||
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++;
|
||||
}
|
||||
if (flags || backing_file)
|
||||
return -ENOTSUP;
|
||||
|
||||
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
|
||||
0644);
|
||||
@@ -233,28 +341,27 @@ static int raw_create(const char *filename, QEMUOptionParameter *options)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static QEMUOptionParameter raw_create_options[] = {
|
||||
{
|
||||
.name = BLOCK_OPT_SIZE,
|
||||
.type = OPT_SIZE,
|
||||
.help = "Virtual disk size"
|
||||
},
|
||||
{ NULL }
|
||||
};
|
||||
BlockDriver bdrv_raw = {
|
||||
"raw",
|
||||
sizeof(BDRVRawState),
|
||||
NULL, /* no probe for protocols */
|
||||
raw_open,
|
||||
NULL,
|
||||
NULL,
|
||||
raw_close,
|
||||
raw_create,
|
||||
raw_flush,
|
||||
|
||||
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,
|
||||
#ifdef WIN32_AIO
|
||||
.bdrv_aio_read = raw_aio_read,
|
||||
.bdrv_aio_write = raw_aio_write,
|
||||
.bdrv_aio_cancel = raw_aio_cancel,
|
||||
.aiocb_size = sizeof(RawAIOCB);
|
||||
#endif
|
||||
.bdrv_pread = raw_pread,
|
||||
.bdrv_pwrite = raw_pwrite,
|
||||
.bdrv_truncate = raw_truncate,
|
||||
.bdrv_getlength = raw_getlength,
|
||||
};
|
||||
|
||||
/***********************************************/
|
||||
@@ -306,15 +413,6 @@ static int find_device_type(BlockDriverState *bs, const char *filename)
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -344,7 +442,11 @@ static int hdev_open(BlockDriverState *bs, const char *filename, int flags)
|
||||
}
|
||||
create_flags = OPEN_EXISTING;
|
||||
|
||||
#ifdef WIN32_AIO
|
||||
overlapped = FILE_FLAG_OVERLAPPED;
|
||||
#else
|
||||
overlapped = FILE_ATTRIBUTE_NORMAL;
|
||||
#endif
|
||||
if ((flags & BDRV_O_NOCACHE))
|
||||
overlapped |= FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH;
|
||||
else if (!(flags & BDRV_O_CACHE_WB))
|
||||
@@ -397,23 +499,24 @@ static int raw_set_locked(BlockDriverState *bs, int locked)
|
||||
}
|
||||
#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,
|
||||
BlockDriver bdrv_host_device = {
|
||||
"host_device",
|
||||
sizeof(BDRVRawState),
|
||||
NULL, /* no probe for protocols */
|
||||
hdev_open,
|
||||
NULL,
|
||||
NULL,
|
||||
raw_close,
|
||||
NULL,
|
||||
raw_flush,
|
||||
|
||||
.bdrv_read = raw_read,
|
||||
.bdrv_write = raw_write,
|
||||
.bdrv_getlength = raw_getlength,
|
||||
#ifdef WIN32_AIO
|
||||
.bdrv_aio_read = raw_aio_read,
|
||||
.bdrv_aio_write = raw_aio_write,
|
||||
.bdrv_aio_cancel = raw_aio_cancel,
|
||||
.aiocb_size = sizeof(RawAIOCB);
|
||||
#endif
|
||||
.bdrv_pread = raw_pread,
|
||||
.bdrv_pwrite = raw_pwrite,
|
||||
.bdrv_getlength = raw_getlength,
|
||||
};
|
||||
|
||||
static void bdrv_raw_init(void)
|
||||
{
|
||||
bdrv_register(&bdrv_raw);
|
||||
bdrv_register(&bdrv_host_device);
|
||||
}
|
||||
|
||||
block_init(bdrv_raw_init);
|
||||
+32
-77
@@ -25,7 +25,6 @@
|
||||
|
||||
#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')
|
||||
@@ -135,7 +134,7 @@ static uint32_t vmdk_read_cid(BlockDriverState *bs, int parent)
|
||||
cid_str_size = sizeof("CID");
|
||||
}
|
||||
|
||||
if ((p_name = strstr(desc,cid_str)) != NULL) {
|
||||
if ((p_name = strstr(desc,cid_str)) != 0) {
|
||||
p_name += cid_str_size;
|
||||
sscanf(p_name,"%x",&cid);
|
||||
}
|
||||
@@ -155,7 +154,7 @@ static int vmdk_write_cid(BlockDriverState *bs, uint32_t cid)
|
||||
|
||||
tmp_str = strstr(desc,"parentCID");
|
||||
pstrcpy(tmp_desc, sizeof(tmp_desc), tmp_str);
|
||||
if ((p_name = strstr(desc,"CID")) != NULL) {
|
||||
if ((p_name = strstr(desc,"CID")) != 0) {
|
||||
p_name += sizeof("CID");
|
||||
snprintf(p_name, sizeof(desc) - (p_name - desc), "%x\n", cid);
|
||||
pstrcat(desc, sizeof(desc), tmp_desc);
|
||||
@@ -170,7 +169,7 @@ static int vmdk_is_cid_valid(BlockDriverState *bs)
|
||||
{
|
||||
#ifdef CHECK_CID
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
BlockDriverState *p_bs = bs->backing_hd;
|
||||
BlockDriverState *p_bs = s->hd->backing_hd;
|
||||
uint32_t cur_pcid;
|
||||
|
||||
if (p_bs) {
|
||||
@@ -240,7 +239,7 @@ static int vmdk_snapshot_create(const char *filename, const char *backing_file)
|
||||
if (read(p_fd, p_desc, DESC_SIZE) != DESC_SIZE)
|
||||
goto fail;
|
||||
|
||||
if ((p_name = strstr(p_desc,"CID")) != NULL) {
|
||||
if ((p_name = strstr(p_desc,"CID")) != 0) {
|
||||
p_name += sizeof("CID");
|
||||
sscanf(p_name,"%x",&p_cid);
|
||||
}
|
||||
@@ -331,33 +330,33 @@ static int vmdk_parent_open(BlockDriverState *bs, const char * filename)
|
||||
if (bdrv_pread(s->hd, 0x200, desc, DESC_SIZE) != DESC_SIZE)
|
||||
return -1;
|
||||
|
||||
if ((p_name = strstr(desc,"parentFileNameHint")) != NULL) {
|
||||
if ((p_name = strstr(desc,"parentFileNameHint")) != 0) {
|
||||
char *end_name;
|
||||
struct stat file_buf;
|
||||
|
||||
p_name += sizeof("parentFileNameHint") + 1;
|
||||
if ((end_name = strchr(p_name,'\"')) == NULL)
|
||||
if ((end_name = strchr(p_name,'\"')) == 0)
|
||||
return -1;
|
||||
if ((end_name - p_name) > sizeof (bs->backing_file) - 1)
|
||||
if ((end_name - p_name) > sizeof (s->hd->backing_file) - 1)
|
||||
return -1;
|
||||
|
||||
pstrcpy(bs->backing_file, end_name - p_name + 1, p_name);
|
||||
if (stat(bs->backing_file, &file_buf) != 0) {
|
||||
pstrcpy(s->hd->backing_file, end_name - p_name + 1, p_name);
|
||||
if (stat(s->hd->backing_file, &file_buf) != 0) {
|
||||
path_combine(parent_img_name, sizeof(parent_img_name),
|
||||
filename, bs->backing_file);
|
||||
filename, s->hd->backing_file);
|
||||
} else {
|
||||
pstrcpy(parent_img_name, sizeof(parent_img_name),
|
||||
bs->backing_file);
|
||||
s->hd->backing_file);
|
||||
}
|
||||
|
||||
bs->backing_hd = bdrv_new("");
|
||||
if (!bs->backing_hd) {
|
||||
s->hd->backing_hd = bdrv_new("");
|
||||
if (!s->hd->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)
|
||||
if (bdrv_open(s->hd->backing_hd, parent_img_name, BDRV_O_RDONLY) < 0)
|
||||
goto failure;
|
||||
parent_open = 0;
|
||||
}
|
||||
@@ -464,14 +463,13 @@ static int get_whole_cluster(BlockDriverState *bs, uint64_t cluster_offset,
|
||||
|
||||
// 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 (s->hd->backing_hd) {
|
||||
BDRVVmdkState *ps = s->hd->backing_hd->opaque;
|
||||
|
||||
if (!vmdk_is_cid_valid(bs))
|
||||
return -1;
|
||||
|
||||
parent_cluster_offset = get_cluster_offset(bs->backing_hd, NULL,
|
||||
offset, allocate);
|
||||
parent_cluster_offset = get_cluster_offset(s->hd->backing_hd, NULL, offset, allocate);
|
||||
|
||||
if (parent_cluster_offset) {
|
||||
BDRVVmdkState *act_s = activeBDRV.hd->opaque;
|
||||
@@ -622,10 +620,10 @@ static int vmdk_read(BlockDriverState *bs, int64_t sector_num,
|
||||
n = nb_sectors;
|
||||
if (!cluster_offset) {
|
||||
// try to read from parent image, if exist
|
||||
if (bs->backing_hd) {
|
||||
if (s->hd->backing_hd) {
|
||||
if (!vmdk_is_cid_valid(bs))
|
||||
return -1;
|
||||
ret = bdrv_read(bs->backing_hd, sector_num, buf, n);
|
||||
ret = bdrv_read(s->hd->backing_hd, sector_num, buf, n);
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
} else {
|
||||
@@ -688,7 +686,8 @@ static int vmdk_write(BlockDriverState *bs, int64_t sector_num,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vmdk_create(const char *filename, QEMUOptionParameter *options)
|
||||
static int vmdk_create(const char *filename, int64_t total_size,
|
||||
const char *backing_file, int flags)
|
||||
{
|
||||
int fd, i;
|
||||
VMDK4Header header;
|
||||
@@ -713,21 +712,6 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options)
|
||||
"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) {
|
||||
@@ -827,44 +811,15 @@ static void vmdk_flush(BlockDriverState *bs)
|
||||
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 }
|
||||
BlockDriver bdrv_vmdk = {
|
||||
"vmdk",
|
||||
sizeof(BDRVVmdkState),
|
||||
vmdk_probe,
|
||||
vmdk_open,
|
||||
vmdk_read,
|
||||
vmdk_write,
|
||||
vmdk_close,
|
||||
vmdk_create,
|
||||
vmdk_flush,
|
||||
vmdk_is_allocated,
|
||||
};
|
||||
|
||||
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);
|
||||
+21
-51
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Block driver for Connectix / Microsoft Virtual PC images
|
||||
* Block driver for Conectix/Microsoft Virtual PC images
|
||||
*
|
||||
* Copyright (c) 2005 Alex Beregszaszi
|
||||
* Copyright (c) 2009 Kevin Wolf <kwolf@suse.de>
|
||||
@@ -24,7 +24,6 @@
|
||||
*/
|
||||
#include "qemu-common.h"
|
||||
#include "block_int.h"
|
||||
#include "module.h"
|
||||
|
||||
/**************************************************************/
|
||||
|
||||
@@ -434,16 +433,14 @@ static int vpc_write(BlockDriverState *bs, int64_t sector_num,
|
||||
*
|
||||
* 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,
|
||||
static void 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;
|
||||
total_sectors = 65535 * 16 * 255;
|
||||
|
||||
if (total_sectors > 65535 * 16 * 63) {
|
||||
*secs_per_cyl = 255;
|
||||
@@ -473,11 +470,10 @@ static int calculate_geometry(int64_t total_sectors, uint16_t* cyls,
|
||||
// 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)
|
||||
static int vpc_create(const char *filename, int64_t total_sectors,
|
||||
const char *backing_file, int flags)
|
||||
{
|
||||
uint8_t buf[1024];
|
||||
struct vhd_footer* footer = (struct vhd_footer*) buf;
|
||||
@@ -488,33 +484,25 @@ static int vpc_create(const char *filename, QEMUOptionParameter *options)
|
||||
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++;
|
||||
}
|
||||
if (backing_file != NULL)
|
||||
return -ENOTSUP;
|
||||
|
||||
// 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;
|
||||
calculate_geometry(total_sectors, &cyls, &heads, &secs_per_cyl);
|
||||
total_sectors = (int64_t) cyls * heads * secs_per_cyl;
|
||||
|
||||
// Prepare the Hard Disk Footer
|
||||
memset(buf, 0, 1024);
|
||||
|
||||
memcpy(footer->creator, "conectix", 8);
|
||||
strncpy(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);
|
||||
strncpy(footer->creator_app, "qemu", 4);
|
||||
strncpy(footer->creator_os, "Wi2k", 4);
|
||||
|
||||
footer->features = be32_to_cpu(0x02);
|
||||
footer->version = be32_to_cpu(0x00010000);
|
||||
@@ -563,7 +551,7 @@ static int vpc_create(const char *filename, QEMUOptionParameter *options)
|
||||
// Prepare the Dynamic Disk Header
|
||||
memset(buf, 0, 1024);
|
||||
|
||||
memcpy(dyndisk_header->magic, "cxsparse", 8);
|
||||
strncpy(dyndisk_header->magic, "cxsparse", 8);
|
||||
|
||||
dyndisk_header->data_offset = be64_to_cpu(0xFFFFFFFF);
|
||||
dyndisk_header->table_offset = be64_to_cpu(3 * 512);
|
||||
@@ -593,31 +581,13 @@ static void vpc_close(BlockDriverState *bs)
|
||||
bdrv_delete(s->hd);
|
||||
}
|
||||
|
||||
static QEMUOptionParameter vpc_create_options[] = {
|
||||
{
|
||||
.name = BLOCK_OPT_SIZE,
|
||||
.type = OPT_SIZE,
|
||||
.help = "Virtual disk size"
|
||||
},
|
||||
{ NULL }
|
||||
BlockDriver bdrv_vpc = {
|
||||
"vpc",
|
||||
sizeof(BDRVVPCState),
|
||||
vpc_probe,
|
||||
vpc_open,
|
||||
vpc_read,
|
||||
vpc_write,
|
||||
vpc_close,
|
||||
vpc_create,
|
||||
};
|
||||
|
||||
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);
|
||||
@@ -24,9 +24,9 @@
|
||||
*/
|
||||
#include <sys/stat.h>
|
||||
#include <dirent.h>
|
||||
#include <assert.h>
|
||||
#include "qemu-common.h"
|
||||
#include "block_int.h"
|
||||
#include "module.h"
|
||||
|
||||
#ifndef S_IWGRP
|
||||
#define S_IWGRP 0
|
||||
@@ -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;
|
||||
@@ -129,7 +129,7 @@ static inline void* array_insert(array_t* array,unsigned int index,unsigned int
|
||||
int increment=count*array->item_size;
|
||||
array->pointer=qemu_realloc(array->pointer,array->size+increment);
|
||||
if(!array->pointer)
|
||||
return NULL;
|
||||
return 0;
|
||||
array->size+=increment;
|
||||
}
|
||||
memmove(array->pointer+(index+count)*array->item_size,
|
||||
@@ -379,7 +379,7 @@ 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]);
|
||||
partition_t* partition=&(real_mbr->partition[0]);
|
||||
int lba;
|
||||
|
||||
memset(s->first_sectors,0,512);
|
||||
@@ -509,12 +509,9 @@ static inline uint8_t fat_chksum(const direntry_t* entry)
|
||||
uint8_t chksum=0;
|
||||
int i;
|
||||
|
||||
for(i=0;i<11;i++) {
|
||||
unsigned char c;
|
||||
|
||||
c = (i <= 8) ? entry->name[i] : entry->extension[i-8];
|
||||
chksum=(((chksum&0xfe)>>1)|((chksum&0x01)?0x80:0)) + c;
|
||||
}
|
||||
for(i=0;i<11;i++)
|
||||
chksum=(((chksum&0xfe)>>1)|((chksum&0x01)?0x80:0))
|
||||
+(unsigned char)entry->name[i];
|
||||
|
||||
return chksum;
|
||||
}
|
||||
@@ -526,7 +523,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)
|
||||
@@ -607,8 +604,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));
|
||||
@@ -699,7 +696,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);
|
||||
@@ -882,7 +879,7 @@ static int init_directories(BDRVVVFATState* s,
|
||||
mapping->dir_index = 0;
|
||||
mapping->info.dir.parent_mapping_index = -1;
|
||||
mapping->first_mapping_index = -1;
|
||||
mapping->path = qemu_strdup(dirname);
|
||||
mapping->path = strdup(dirname);
|
||||
i = strlen(mapping->path);
|
||||
if (i > 0 && mapping->path[i - 1] == '/')
|
||||
mapping->path[i - 1] = '\0';
|
||||
@@ -1128,10 +1125,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;
|
||||
}
|
||||
@@ -1632,10 +1629,10 @@ static uint32_t get_cluster_count_for_direntry(BDRVVVFATState* s,
|
||||
|
||||
/* rename */
|
||||
if (strcmp(basename, basename2))
|
||||
schedule_rename(s, cluster_num, qemu_strdup(path));
|
||||
schedule_rename(s, cluster_num, strdup(path));
|
||||
} else if (is_file(direntry))
|
||||
/* new file */
|
||||
schedule_new_file(s, qemu_strdup(path), cluster_num);
|
||||
schedule_new_file(s, strdup(path), cluster_num);
|
||||
else {
|
||||
assert(0);
|
||||
return 0;
|
||||
@@ -1752,10 +1749,10 @@ static int check_directory_consistency(BDRVVVFATState *s,
|
||||
mapping->mode &= ~MODE_DELETED;
|
||||
|
||||
if (strcmp(basename, basename2))
|
||||
schedule_rename(s, cluster_num, qemu_strdup(path));
|
||||
schedule_rename(s, cluster_num, strdup(path));
|
||||
} else
|
||||
/* new directory */
|
||||
schedule_mkdir(s, cluster_num, qemu_strdup(path));
|
||||
schedule_mkdir(s, cluster_num, strdup(path));
|
||||
|
||||
lfn_init(&lfn);
|
||||
do {
|
||||
@@ -1781,7 +1778,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) ||
|
||||
@@ -2762,15 +2759,14 @@ 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);
|
||||
|
||||
@@ -2778,13 +2774,8 @@ static int enable_write_target(BDRVVVFATState *s)
|
||||
|
||||
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)
|
||||
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,24 +2804,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),
|
||||
NULL, /* no probe for protocols */
|
||||
vvfat_open,
|
||||
vvfat_read,
|
||||
vvfat_write,
|
||||
vvfat_close,
|
||||
NULL, /* ??? Not sure if we can do any meaningful flushing. */
|
||||
NULL,
|
||||
vvfat_is_allocated,
|
||||
.protocol_name = "fat",
|
||||
};
|
||||
|
||||
static void bdrv_vvfat_init(void)
|
||||
{
|
||||
bdrv_register(&bdrv_vvfat);
|
||||
}
|
||||
|
||||
block_init(bdrv_vvfat_init);
|
||||
|
||||
#ifdef DEBUG
|
||||
static void checkpoint(void) {
|
||||
assert(((mapping_t*)array_get(&(vvv->mapping), 0))->end == 2);
|
||||
@@ -3,12 +3,24 @@
|
||||
|
||||
#include "qemu-aio.h"
|
||||
#include "qemu-common.h"
|
||||
#include "qemu-option.h"
|
||||
#include "qobject.h"
|
||||
|
||||
/* block.c */
|
||||
typedef struct BlockDriver BlockDriver;
|
||||
|
||||
extern BlockDriver bdrv_raw;
|
||||
extern BlockDriver bdrv_host_device;
|
||||
extern BlockDriver bdrv_cow;
|
||||
extern BlockDriver bdrv_qcow;
|
||||
extern BlockDriver bdrv_vmdk;
|
||||
extern BlockDriver bdrv_cloop;
|
||||
extern BlockDriver bdrv_dmg;
|
||||
extern BlockDriver bdrv_bochs;
|
||||
extern BlockDriver bdrv_vpc;
|
||||
extern BlockDriver bdrv_vvfat;
|
||||
extern BlockDriver bdrv_qcow2;
|
||||
extern BlockDriver bdrv_parallels;
|
||||
extern BlockDriver bdrv_nbd;
|
||||
|
||||
typedef struct BlockDriverInfo {
|
||||
/* in bytes, 0 if irrelevant */
|
||||
int cluster_size;
|
||||
@@ -38,29 +50,18 @@ typedef struct QEMUSnapshotInfo {
|
||||
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_DEF 0x0080 /* use default caching */
|
||||
|
||||
#define BDRV_O_CACHE_MASK (BDRV_O_NOCACHE | BDRV_O_CACHE_WB)
|
||||
#define BDRV_O_CACHE_MASK (BDRV_O_NOCACHE | BDRV_O_CACHE_WB | BDRV_O_CACHE_DEF)
|
||||
|
||||
#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_info(void);
|
||||
void bdrv_info_stats(void);
|
||||
|
||||
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);
|
||||
int bdrv_create(BlockDriver *drv,
|
||||
const char *filename, int64_t size_in_sectors,
|
||||
const char *backing_file, 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);
|
||||
@@ -68,7 +69,6 @@ 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,
|
||||
@@ -82,44 +82,25 @@ 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);
|
||||
|
||||
BlockDriverAIOCB *bdrv_aio_read(BlockDriverState *bs, int64_t sector_num,
|
||||
uint8_t *buf, int nb_sectors,
|
||||
BlockDriverCompletionFunc *cb, void *opaque);
|
||||
BlockDriverAIOCB *bdrv_aio_write(BlockDriverState *bs, int64_t sector_num,
|
||||
const uint8_t *buf, int nb_sectors,
|
||||
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);
|
||||
@@ -146,14 +127,12 @@ 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_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);
|
||||
@@ -182,6 +161,7 @@ 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);
|
||||
int bdrv_ioctl(BlockDriverState *bs, unsigned long int req, void *buf);
|
||||
|
||||
char *get_human_readable_size(char *buf, int buf_size, int64_t size);
|
||||
int path_is_absolute(const char *path);
|
||||
@@ -189,16 +169,9 @@ 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_put_buffer(BlockDriverState *bs, const uint8_t *buf,
|
||||
int64_t pos, int size);
|
||||
|
||||
int bdrv_load_vmstate(BlockDriverState *bs, uint8_t *buf,
|
||||
int64_t pos, int size);
|
||||
int bdrv_get_buffer(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
@@ -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 = qemu_strdup(filename);
|
||||
s->readahead_size = READ_AHEAD_SIZE;
|
||||
|
||||
/* Parse a trailing ":readahead=#:" param, if present. */
|
||||
ra = file + strlen(file) - 1;
|
||||
while (ra >= file) {
|
||||
if (parse_state == 0) {
|
||||
if (*ra == ':')
|
||||
parse_state++;
|
||||
else
|
||||
break;
|
||||
} else if (parse_state == 1) {
|
||||
if (*ra > '9' || *ra < '0') {
|
||||
char *opt_start = ra - strlen(RA_OPTSTR) + 1;
|
||||
if (opt_start > file &&
|
||||
strncmp(opt_start, RA_OPTSTR, strlen(RA_OPTSTR)) == 0) {
|
||||
ra_val = ra + 1;
|
||||
ra -= strlen(RA_OPTSTR) - 1;
|
||||
*ra = '\0';
|
||||
s->readahead_size = atoi(ra_val);
|
||||
break;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
ra--;
|
||||
}
|
||||
|
||||
if ((s->readahead_size & 0x1ff) != 0) {
|
||||
fprintf(stderr, "HTTP_READAHEAD_SIZE %Zd is not a multiple of 512\n",
|
||||
s->readahead_size);
|
||||
goto out_noclean;
|
||||
}
|
||||
|
||||
if (!inited) {
|
||||
curl_global_init(CURL_GLOBAL_ALL);
|
||||
inited = 1;
|
||||
}
|
||||
|
||||
dprintf("CURL: Opening %s\n", file);
|
||||
s->url = file;
|
||||
state = curl_init_state(s);
|
||||
if (!state)
|
||||
goto out_noclean;
|
||||
|
||||
// Get file size
|
||||
|
||||
curl_easy_setopt(state->curl, CURLOPT_NOBODY, 1);
|
||||
curl_easy_setopt(state->curl, CURLOPT_WRITEFUNCTION, (void *)curl_size_cb);
|
||||
if (curl_easy_perform(state->curl))
|
||||
goto out;
|
||||
curl_easy_getinfo(state->curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &d);
|
||||
curl_easy_setopt(state->curl, CURLOPT_WRITEFUNCTION, (void *)curl_read_cb);
|
||||
curl_easy_setopt(state->curl, CURLOPT_NOBODY, 0);
|
||||
if (d)
|
||||
s->len = (size_t)d;
|
||||
else if(!s->len)
|
||||
goto out;
|
||||
dprintf("CURL: Size = %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);
|
||||
@@ -1,879 +0,0 @@
|
||||
/*
|
||||
* Block driver for the QCOW version 2 format
|
||||
*
|
||||
* Copyright (c) 2004-2006 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <zlib.h>
|
||||
|
||||
#include "qemu-common.h"
|
||||
#include "block_int.h"
|
||||
#include "block/qcow2.h"
|
||||
|
||||
int qcow2_grow_l1_table(BlockDriverState *bs, int min_size)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int new_l1_size, new_l1_size2, ret, i;
|
||||
uint64_t *new_l1_table;
|
||||
int64_t new_l1_table_offset;
|
||||
uint8_t data[12];
|
||||
|
||||
new_l1_size = s->l1_size;
|
||||
if (min_size <= new_l1_size)
|
||||
return 0;
|
||||
if (new_l1_size == 0) {
|
||||
new_l1_size = 1;
|
||||
}
|
||||
while (min_size > new_l1_size) {
|
||||
new_l1_size = (new_l1_size * 3 + 1) / 2;
|
||||
}
|
||||
#ifdef DEBUG_ALLOC2
|
||||
printf("grow l1_table from %d to %d\n", s->l1_size, new_l1_size);
|
||||
#endif
|
||||
|
||||
new_l1_size2 = sizeof(uint64_t) * new_l1_size;
|
||||
new_l1_table = qemu_mallocz(align_offset(new_l1_size2, 512));
|
||||
memcpy(new_l1_table, s->l1_table, s->l1_size * sizeof(uint64_t));
|
||||
|
||||
/* write new table (align to cluster) */
|
||||
new_l1_table_offset = qcow2_alloc_clusters(bs, new_l1_size2);
|
||||
if (new_l1_table_offset < 0) {
|
||||
qemu_free(new_l1_table);
|
||||
return new_l1_table_offset;
|
||||
}
|
||||
|
||||
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);
|
||||
ret = bdrv_pwrite(s->hd, offsetof(QCowHeader, l1_size), data,sizeof(data));
|
||||
if (ret != 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(new_l1_table);
|
||||
qcow2_free_clusters(bs, new_l1_table_offset, new_l1_size2);
|
||||
return ret < 0 ? ret : -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;
|
||||
int64_t 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));
|
||||
if (l2_offset < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* 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.
|
||||
*
|
||||
* Returns 0 on success, -errno in failure case
|
||||
*/
|
||||
static int get_cluster_table(BlockDriverState *bs, uint64_t offset,
|
||||
uint64_t **new_l2_table,
|
||||
uint64_t *new_l2_offset,
|
||||
int *new_l2_index)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
unsigned int l1_index, l2_index;
|
||||
uint64_t l2_offset, *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 ret;
|
||||
}
|
||||
}
|
||||
l2_offset = s->l1_table[l1_index];
|
||||
|
||||
/* seek the l2 table of the given l2 offset */
|
||||
|
||||
if (l2_offset & QCOW_OFLAG_COPIED) {
|
||||
/* load the l2 table in memory */
|
||||
l2_offset &= ~QCOW_OFLAG_COPIED;
|
||||
l2_table = l2_load(bs, l2_offset);
|
||||
if (l2_table == NULL) {
|
||||
return -EIO;
|
||||
}
|
||||
} 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 -EIO;
|
||||
}
|
||||
l2_offset = s->l1_table[l1_index] & ~QCOW_OFLAG_COPIED;
|
||||
}
|
||||
|
||||
/* find the cluster offset for the given disk offset */
|
||||
|
||||
l2_index = (offset >> s->cluster_bits) & (s->l2_size - 1);
|
||||
|
||||
*new_l2_table = l2_table;
|
||||
*new_l2_offset = l2_offset;
|
||||
*new_l2_index = l2_index;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* alloc_compressed_cluster_offset
|
||||
*
|
||||
* For a given offset of the disk image, return cluster offset in
|
||||
* qcow2 file.
|
||||
*
|
||||
* If the offset is not found, allocate a new compressed cluster.
|
||||
*
|
||||
* Return the cluster offset if successful,
|
||||
* Return 0, otherwise.
|
||||
*
|
||||
*/
|
||||
|
||||
uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs,
|
||||
uint64_t offset,
|
||||
int compressed_size)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int l2_index, ret;
|
||||
uint64_t l2_offset, *l2_table;
|
||||
int64_t cluster_offset;
|
||||
int nb_csectors;
|
||||
|
||||
ret = get_cluster_table(bs, offset, &l2_table, &l2_offset, &l2_index);
|
||||
if (ret < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
cluster_offset = be64_to_cpu(l2_table[l2_index]);
|
||||
if (cluster_offset & QCOW_OFLAG_COPIED)
|
||||
return cluster_offset & ~QCOW_OFLAG_COPIED;
|
||||
|
||||
if (cluster_offset)
|
||||
qcow2_free_any_clusters(bs, cluster_offset, 1);
|
||||
|
||||
cluster_offset = qcow2_alloc_bytes(bs, compressed_size);
|
||||
if (cluster_offset < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
nb_csectors = ((cluster_offset + compressed_size - 1) >> 9) -
|
||||
(cluster_offset >> 9);
|
||||
|
||||
cluster_offset |= QCOW_OFLAG_COMPRESSED |
|
||||
((uint64_t)nb_csectors << s->csize_shift);
|
||||
|
||||
/* update L2 table */
|
||||
|
||||
/* compressed clusters never have the copied flag */
|
||||
|
||||
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, QCowL2Meta *m)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int i, j = 0, l2_index, ret;
|
||||
uint64_t *old_cluster, start_sect, l2_offset, *l2_table;
|
||||
uint64_t cluster_offset = m->cluster_offset;
|
||||
|
||||
if (m->nb_clusters == 0)
|
||||
return 0;
|
||||
|
||||
old_cluster = qemu_malloc(m->nb_clusters * sizeof(uint64_t));
|
||||
|
||||
/* copy content of unmodified sectors */
|
||||
start_sect = (m->offset & ~(s->cluster_size - 1)) >> 9;
|
||||
if (m->n_start) {
|
||||
ret = copy_sectors(bs, start_sect, cluster_offset, 0, m->n_start);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (m->nb_available & (s->cluster_sectors - 1)) {
|
||||
uint64_t end = m->nb_available & ~(uint64_t)(s->cluster_sectors - 1);
|
||||
ret = copy_sectors(bs, start_sect + end, cluster_offset + (end << 9),
|
||||
m->nb_available - end, s->cluster_sectors);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* update L2 table */
|
||||
ret = get_cluster_table(bs, m->offset, &l2_table, &l2_offset, &l2_index);
|
||||
if (ret < 0) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
for (i = 0; i < m->nb_clusters; i++) {
|
||||
/* if two concurrent writes happen to the same unallocated cluster
|
||||
* each write allocates separate cluster and writes data concurrently.
|
||||
* The first one to complete updates l2 table with pointer to its
|
||||
* cluster the second one has to do RMW (which is done above by
|
||||
* copy_sectors()), update l2 table with its cluster pointer and free
|
||||
* old cluster. This is what this loop does */
|
||||
if(l2_table[l2_index + i] != 0)
|
||||
old_cluster[j++] = l2_table[l2_index + i];
|
||||
|
||||
l2_table[l2_index + i] = cpu_to_be64((cluster_offset +
|
||||
(i << s->cluster_bits)) | QCOW_OFLAG_COPIED);
|
||||
}
|
||||
|
||||
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.
|
||||
*
|
||||
* If the cluster was already allocated, m->nb_clusters is set to 0,
|
||||
* m->depends_on is set to NULL and the other fields in m are meaningless.
|
||||
*
|
||||
* If the cluster is newly allocated, m->nb_clusters is set to the number of
|
||||
* contiguous clusters that have been allocated. This may be 0 if the request
|
||||
* conflict with another write request in flight; in this case, m->depends_on
|
||||
* is set and the remaining fields of m are meaningless.
|
||||
*
|
||||
* If m->nb_clusters is non-zero, the other fields of m are valid and contain
|
||||
* information about the first allocated cluster.
|
||||
*
|
||||
* Return 0 on success and -errno in error cases
|
||||
*/
|
||||
int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset,
|
||||
int n_start, int n_end, int *num, QCowL2Meta *m)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int l2_index, ret;
|
||||
uint64_t l2_offset, *l2_table;
|
||||
int64_t cluster_offset;
|
||||
unsigned int nb_clusters, i = 0;
|
||||
QCowL2Meta *old_alloc;
|
||||
|
||||
ret = get_cluster_table(bs, offset, &l2_table, &l2_offset, &l2_index);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
nb_clusters = size_to_clusters(s, n_end << 9);
|
||||
|
||||
nb_clusters = MIN(nb_clusters, s->l2_size - l2_index);
|
||||
|
||||
cluster_offset = be64_to_cpu(l2_table[l2_index]);
|
||||
|
||||
/* We keep all QCOW_OFLAG_COPIED clusters */
|
||||
|
||||
if (cluster_offset & QCOW_OFLAG_COPIED) {
|
||||
nb_clusters = count_contiguous_clusters(nb_clusters, s->cluster_size,
|
||||
&l2_table[l2_index], 0, 0);
|
||||
|
||||
cluster_offset &= ~QCOW_OFLAG_COPIED;
|
||||
m->nb_clusters = 0;
|
||||
m->depends_on = NULL;
|
||||
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* for the moment, multiple compressed clusters are not managed */
|
||||
|
||||
if (cluster_offset & QCOW_OFLAG_COMPRESSED)
|
||||
nb_clusters = 1;
|
||||
|
||||
/* how many available clusters ? */
|
||||
|
||||
while (i < nb_clusters) {
|
||||
i += count_contiguous_clusters(nb_clusters - i, s->cluster_size,
|
||||
&l2_table[l2_index], i, 0);
|
||||
if ((i >= nb_clusters) || be64_to_cpu(l2_table[l2_index + i])) {
|
||||
break;
|
||||
}
|
||||
|
||||
i += count_contiguous_free_clusters(nb_clusters - i,
|
||||
&l2_table[l2_index + i]);
|
||||
if (i >= nb_clusters) {
|
||||
break;
|
||||
}
|
||||
|
||||
cluster_offset = be64_to_cpu(l2_table[l2_index + i]);
|
||||
|
||||
if ((cluster_offset & QCOW_OFLAG_COPIED) ||
|
||||
(cluster_offset & QCOW_OFLAG_COMPRESSED))
|
||||
break;
|
||||
}
|
||||
assert(i <= nb_clusters);
|
||||
nb_clusters = i;
|
||||
|
||||
/*
|
||||
* Check if there already is an AIO write request in flight which allocates
|
||||
* the same cluster. In this case we need to wait until the previous
|
||||
* request has completed and updated the L2 table accordingly.
|
||||
*/
|
||||
QLIST_FOREACH(old_alloc, &s->cluster_allocs, next_in_flight) {
|
||||
|
||||
uint64_t end_offset = offset + nb_clusters * s->cluster_size;
|
||||
uint64_t old_offset = old_alloc->offset;
|
||||
uint64_t old_end_offset = old_alloc->offset +
|
||||
old_alloc->nb_clusters * s->cluster_size;
|
||||
|
||||
if (end_offset < old_offset || offset > old_end_offset) {
|
||||
/* No intersection */
|
||||
} else {
|
||||
if (offset < old_offset) {
|
||||
/* Stop at the start of a running allocation */
|
||||
nb_clusters = (old_offset - offset) >> s->cluster_bits;
|
||||
} else {
|
||||
nb_clusters = 0;
|
||||
}
|
||||
|
||||
if (nb_clusters == 0) {
|
||||
/* Set dependency and wait for a callback */
|
||||
m->depends_on = old_alloc;
|
||||
m->nb_clusters = 0;
|
||||
*num = 0;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!nb_clusters) {
|
||||
abort();
|
||||
}
|
||||
|
||||
QLIST_INSERT_HEAD(&s->cluster_allocs, m, next_in_flight);
|
||||
|
||||
/* allocate a new cluster */
|
||||
|
||||
cluster_offset = qcow2_alloc_clusters(bs, nb_clusters * s->cluster_size);
|
||||
if (cluster_offset < 0) {
|
||||
return cluster_offset;
|
||||
}
|
||||
|
||||
/* save info needed for meta data update */
|
||||
m->offset = offset;
|
||||
m->n_start = n_start;
|
||||
m->nb_clusters = nb_clusters;
|
||||
|
||||
out:
|
||||
m->nb_available = MIN(nb_clusters << (s->cluster_bits - 9), n_end);
|
||||
m->cluster_offset = cluster_offset;
|
||||
|
||||
*num = m->nb_available - n_start;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int decompress_buffer(uint8_t *out_buf, int out_buf_size,
|
||||
const uint8_t *buf, int buf_size)
|
||||
{
|
||||
z_stream strm1, *strm = &strm1;
|
||||
int ret, out_len;
|
||||
|
||||
memset(strm, 0, sizeof(*strm));
|
||||
|
||||
strm->next_in = (uint8_t *)buf;
|
||||
strm->avail_in = buf_size;
|
||||
strm->next_out = out_buf;
|
||||
strm->avail_out = out_buf_size;
|
||||
|
||||
ret = inflateInit2(strm, -12);
|
||||
if (ret != Z_OK)
|
||||
return -1;
|
||||
ret = inflate(strm, Z_FINISH);
|
||||
out_len = strm->next_out - out_buf;
|
||||
if ((ret != Z_STREAM_END && ret != Z_BUF_ERROR) ||
|
||||
out_len != out_buf_size) {
|
||||
inflateEnd(strm);
|
||||
return -1;
|
||||
}
|
||||
inflateEnd(strm);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int qcow2_decompress_cluster(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,948 +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);
|
||||
ret = bdrv_pwrite(s->hd, offsetof(QCowHeader, refcount_table_offset),
|
||||
data, sizeof(data));
|
||||
if (ret != 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 ret < 0 ? ret : -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 QEMU_WARN_UNUSED_RESULT 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;
|
||||
int ret;
|
||||
|
||||
#ifdef DEBUG_ALLOC2
|
||||
printf("update_refcount: offset=%" PRId64 " size=%" PRId64 " addend=%d\n",
|
||||
offset, length, addend);
|
||||
#endif
|
||||
if (length < 0) {
|
||||
return -EINVAL;
|
||||
} else if (length == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
int64_t new_block;
|
||||
|
||||
/* 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 */
|
||||
new_block = alloc_refcount_block(bs, cluster_index);
|
||||
if (new_block < 0) {
|
||||
ret = new_block;
|
||||
goto fail;
|
||||
}
|
||||
refcount_block_offset = new_block;
|
||||
|
||||
/* 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) {
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
fail:
|
||||
|
||||
/* 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 ret < 0 ? ret : -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Try do undo any updates if an error is returned (This may succeed in
|
||||
* some cases like ENOSPC for allocating a new refcount block)
|
||||
*/
|
||||
if (ret < 0) {
|
||||
int dummy;
|
||||
dummy = update_refcount(bs, offset, cluster_offset - offset, -addend);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
int ret;
|
||||
|
||||
offset = alloc_clusters_noref(bs, size);
|
||||
ret = update_refcount(bs, offset, size, 1);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
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);
|
||||
if (s->free_byte_offset < 0) {
|
||||
return s->free_byte_offset;
|
||||
}
|
||||
}
|
||||
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);
|
||||
if (offset < 0) {
|
||||
return offset;
|
||||
}
|
||||
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)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = update_refcount(bs, offset, size, -1);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "qcow2_free_clusters failed: %s\n", strerror(-ret));
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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) {
|
||||
int ret;
|
||||
ret = update_refcount(bs,
|
||||
(offset & s->cluster_offset_mask) & ~511,
|
||||
nb_csectors * 512, addend);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
/* 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,419 +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;
|
||||
if (offset < 0) {
|
||||
return offset;
|
||||
}
|
||||
|
||||
for(i = 0; i < s->nb_snapshots; i++) {
|
||||
sn = s->snapshots + i;
|
||||
memset(&h, 0, sizeof(h));
|
||||
h.l1_table_offset = cpu_to_be64(sn->l1_table_offset);
|
||||
h.l1_size = cpu_to_be32(sn->l1_size);
|
||||
h.vm_state_size = cpu_to_be32(sn->vm_state_size);
|
||||
h.date_sec = cpu_to_be32(sn->date_sec);
|
||||
h.date_nsec = cpu_to_be32(sn->date_nsec);
|
||||
h.vm_clock_nsec = cpu_to_be64(sn->vm_clock_nsec);
|
||||
|
||||
id_str_size = strlen(sn->id_str);
|
||||
name_size = strlen(sn->name);
|
||||
h.id_str_size = cpu_to_be16(id_str_size);
|
||||
h.name_size = cpu_to_be16(name_size);
|
||||
offset = align_offset(offset, 8);
|
||||
if (bdrv_pwrite(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;
|
||||
int64_t l1_table_offset;
|
||||
|
||||
memset(sn, 0, sizeof(*sn));
|
||||
|
||||
if (sn_info->id_str[0] == '\0') {
|
||||
/* compute a new id */
|
||||
find_new_snapshot_id(bs, sn_info->id_str, sizeof(sn_info->id_str));
|
||||
}
|
||||
|
||||
/* check that the ID is unique */
|
||||
if (find_snapshot_by_id(bs, sn_info->id_str) >= 0)
|
||||
return -ENOENT;
|
||||
|
||||
sn->id_str = qemu_strdup(sn_info->id_str);
|
||||
if (!sn->id_str)
|
||||
goto fail;
|
||||
sn->name = qemu_strdup(sn_info->name);
|
||||
if (!sn->name)
|
||||
goto fail;
|
||||
sn->vm_state_size = sn_info->vm_state_size;
|
||||
sn->date_sec = sn_info->date_sec;
|
||||
sn->date_nsec = sn_info->date_nsec;
|
||||
sn->vm_clock_nsec = sn_info->vm_clock_nsec;
|
||||
|
||||
ret = qcow2_update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, 1);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
|
||||
/* create the L1 table of the snapshot */
|
||||
l1_table_offset = qcow2_alloc_clusters(bs, s->l1_size * sizeof(uint64_t));
|
||||
if (l1_table_offset < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
sn->l1_table_offset = l1_table_offset;
|
||||
sn->l1_size = s->l1_size;
|
||||
|
||||
if (s->l1_size != 0) {
|
||||
l1_table = qemu_malloc(s->l1_size * sizeof(uint64_t));
|
||||
} else {
|
||||
l1_table = NULL;
|
||||
}
|
||||
|
||||
for(i = 0; i < s->l1_size; i++) {
|
||||
l1_table[i] = cpu_to_be64(s->l1_table[i]);
|
||||
}
|
||||
if (bdrv_pwrite(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;
|
||||
}
|
||||
|
||||
-1152
File diff suppressed because it is too large.
Load diff
-212
@@ -1,212 +0,0 @@
|
||||
/*
|
||||
* Block driver for the QCOW version 2 format
|
||||
*
|
||||
* Copyright (c) 2004-2006 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef BLOCK_QCOW2_H
|
||||
#define BLOCK_QCOW2_H
|
||||
|
||||
#include "aes.h"
|
||||
|
||||
//#define DEBUG_ALLOC
|
||||
//#define DEBUG_ALLOC2
|
||||
//#define DEBUG_EXT
|
||||
|
||||
#define QCOW_MAGIC (('Q' << 24) | ('F' << 16) | ('I' << 8) | 0xfb)
|
||||
#define QCOW_VERSION 2
|
||||
|
||||
#define QCOW_CRYPT_NONE 0
|
||||
#define QCOW_CRYPT_AES 1
|
||||
|
||||
#define QCOW_MAX_CRYPT_CLUSTERS 32
|
||||
|
||||
/* indicate that the refcount of the referenced cluster is exactly one. */
|
||||
#define QCOW_OFLAG_COPIED (1LL << 63)
|
||||
/* indicate that the cluster is compressed (they never have the copied flag) */
|
||||
#define QCOW_OFLAG_COMPRESSED (1LL << 62)
|
||||
|
||||
#define REFCOUNT_SHIFT 1 /* refcount size is 2 bytes */
|
||||
|
||||
#define MIN_CLUSTER_BITS 9
|
||||
#define MAX_CLUSTER_BITS 21
|
||||
|
||||
#define L2_CACHE_SIZE 16
|
||||
|
||||
typedef struct QCowHeader {
|
||||
uint32_t magic;
|
||||
uint32_t version;
|
||||
uint64_t backing_file_offset;
|
||||
uint32_t backing_file_size;
|
||||
uint32_t cluster_bits;
|
||||
uint64_t size; /* in bytes */
|
||||
uint32_t crypt_method;
|
||||
uint32_t l1_size; /* XXX: save number of clusters instead ? */
|
||||
uint64_t l1_table_offset;
|
||||
uint64_t refcount_table_offset;
|
||||
uint32_t refcount_table_clusters;
|
||||
uint32_t nb_snapshots;
|
||||
uint64_t snapshots_offset;
|
||||
} QCowHeader;
|
||||
|
||||
typedef struct QCowSnapshot {
|
||||
uint64_t l1_table_offset;
|
||||
uint32_t l1_size;
|
||||
char *id_str;
|
||||
char *name;
|
||||
uint32_t vm_state_size;
|
||||
uint32_t date_sec;
|
||||
uint32_t date_nsec;
|
||||
uint64_t vm_clock_nsec;
|
||||
} QCowSnapshot;
|
||||
|
||||
typedef struct BDRVQcowState {
|
||||
BlockDriverState *hd;
|
||||
int cluster_bits;
|
||||
int cluster_size;
|
||||
int cluster_sectors;
|
||||
int l2_bits;
|
||||
int l2_size;
|
||||
int l1_size;
|
||||
int l1_vm_state_index;
|
||||
int csize_shift;
|
||||
int csize_mask;
|
||||
uint64_t cluster_offset_mask;
|
||||
uint64_t l1_table_offset;
|
||||
uint64_t *l1_table;
|
||||
uint64_t *l2_cache;
|
||||
uint64_t l2_cache_offsets[L2_CACHE_SIZE];
|
||||
uint32_t l2_cache_counts[L2_CACHE_SIZE];
|
||||
uint8_t *cluster_cache;
|
||||
uint8_t *cluster_data;
|
||||
uint64_t cluster_cache_offset;
|
||||
QLIST_HEAD(QCowClusterAlloc, QCowL2Meta) cluster_allocs;
|
||||
|
||||
uint64_t *refcount_table;
|
||||
uint64_t refcount_table_offset;
|
||||
uint32_t refcount_table_size;
|
||||
uint64_t refcount_block_cache_offset;
|
||||
uint16_t *refcount_block_cache;
|
||||
int64_t free_cluster_index;
|
||||
int64_t free_byte_offset;
|
||||
|
||||
uint32_t crypt_method; /* current crypt method, 0 if no key yet */
|
||||
uint32_t crypt_method_header;
|
||||
AES_KEY aes_encrypt_key;
|
||||
AES_KEY aes_decrypt_key;
|
||||
uint64_t snapshots_offset;
|
||||
int snapshots_size;
|
||||
int nb_snapshots;
|
||||
QCowSnapshot *snapshots;
|
||||
} BDRVQcowState;
|
||||
|
||||
/* XXX: use std qcow open function ? */
|
||||
typedef struct QCowCreateState {
|
||||
int cluster_size;
|
||||
int cluster_bits;
|
||||
uint16_t *refcount_block;
|
||||
uint64_t *refcount_table;
|
||||
int64_t l1_table_offset;
|
||||
int64_t refcount_table_offset;
|
||||
int64_t refcount_block_offset;
|
||||
} QCowCreateState;
|
||||
|
||||
struct QCowAIOCB;
|
||||
|
||||
/* XXX This could be private for qcow2-cluster.c */
|
||||
typedef struct QCowL2Meta
|
||||
{
|
||||
uint64_t offset;
|
||||
uint64_t cluster_offset;
|
||||
int n_start;
|
||||
int nb_available;
|
||||
int nb_clusters;
|
||||
struct QCowL2Meta *depends_on;
|
||||
QLIST_HEAD(QCowAioDependencies, QCowAIOCB) dependent_requests;
|
||||
|
||||
QLIST_ENTRY(QCowL2Meta) next_in_flight;
|
||||
} QCowL2Meta;
|
||||
|
||||
static inline int size_to_clusters(BDRVQcowState *s, int64_t size)
|
||||
{
|
||||
return (size + (s->cluster_size - 1)) >> s->cluster_bits;
|
||||
}
|
||||
|
||||
static inline 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);
|
||||
int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset,
|
||||
int n_start, int n_end, int *num, QCowL2Meta *m);
|
||||
uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs,
|
||||
uint64_t offset,
|
||||
int compressed_size);
|
||||
|
||||
int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m);
|
||||
|
||||
/* qcow2-snapshot.c functions */
|
||||
int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info);
|
||||
int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id);
|
||||
int qcow2_snapshot_delete(BlockDriverState *bs, const char *snapshot_id);
|
||||
int qcow2_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab);
|
||||
|
||||
void qcow2_free_snapshots(BlockDriverState *bs);
|
||||
int qcow2_read_snapshots(BlockDriverState *bs);
|
||||
|
||||
#endif
|
||||
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* QEMU Posix block I/O backend AIO support
|
||||
*
|
||||
* Copyright IBM, Corp. 2008
|
||||
*
|
||||
* Authors:
|
||||
* Anthony Liguori <aliguori@us.ibm.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2. See
|
||||
* the COPYING file in the top-level directory.
|
||||
*
|
||||
*/
|
||||
#ifndef QEMU_RAW_POSIX_AIO_H
|
||||
#define QEMU_RAW_POSIX_AIO_H
|
||||
|
||||
/* AIO request types */
|
||||
#define QEMU_AIO_READ 0x0001
|
||||
#define QEMU_AIO_WRITE 0x0002
|
||||
#define QEMU_AIO_IOCTL 0x0004
|
||||
#define QEMU_AIO_FLUSH 0x0008
|
||||
#define QEMU_AIO_TYPE_MASK \
|
||||
(QEMU_AIO_READ|QEMU_AIO_WRITE|QEMU_AIO_IOCTL|QEMU_AIO_FLUSH)
|
||||
|
||||
/* AIO flags */
|
||||
#define QEMU_AIO_MISALIGNED 0x1000
|
||||
|
||||
|
||||
/* posix-aio-compat.c - thread pool based implementation */
|
||||
int paio_init(void);
|
||||
BlockDriverAIOCB *paio_submit(BlockDriverState *bs, int fd,
|
||||
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
|
||||
BlockDriverCompletionFunc *cb, void *opaque, int type);
|
||||
BlockDriverAIOCB *paio_ioctl(BlockDriverState *bs, int fd,
|
||||
unsigned long int req, void *buf,
|
||||
BlockDriverCompletionFunc *cb, void *opaque);
|
||||
|
||||
/* linux-aio.c - Linux native implementation */
|
||||
void *laio_init(void);
|
||||
BlockDriverAIOCB *laio_submit(BlockDriverState *bs, void *aio_ctx, int fd,
|
||||
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
|
||||
BlockDriverCompletionFunc *cb, void *opaque, int type);
|
||||
|
||||
#endif /* QEMU_RAW_POSIX_AIO_H */
|
||||
-956
@@ -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);
|
||||
+19
-63
@@ -25,60 +25,43 @@
|
||||
#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,
|
||||
uint8_t *buf, int nb_sectors);
|
||||
int (*bdrv_write)(BlockDriverState *bs, int64_t sector_num,
|
||||
const uint8_t *buf, int nb_sectors);
|
||||
void (*bdrv_close)(BlockDriverState *bs);
|
||||
int (*bdrv_create)(const char *filename, QEMUOptionParameter *options);
|
||||
int (*bdrv_create)(const char *filename, int64_t total_sectors,
|
||||
const char *backing_file, int flags);
|
||||
void (*bdrv_flush)(BlockDriverState *bs);
|
||||
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,
|
||||
BlockDriverAIOCB *(*bdrv_aio_read)(BlockDriverState *bs,
|
||||
int64_t sector_num, uint8_t *buf, int nb_sectors,
|
||||
BlockDriverCompletionFunc *cb, void *opaque);
|
||||
BlockDriverAIOCB *(*bdrv_aio_writev)(BlockDriverState *bs,
|
||||
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
|
||||
BlockDriverAIOCB *(*bdrv_aio_write)(BlockDriverState *bs,
|
||||
int64_t sector_num, const uint8_t *buf, 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);
|
||||
|
||||
void (*bdrv_aio_cancel)(BlockDriverAIOCB *acb);
|
||||
int aiocb_size;
|
||||
|
||||
const char *protocol_name;
|
||||
int (*bdrv_pread)(BlockDriverState *bs, int64_t offset,
|
||||
uint8_t *buf, int count);
|
||||
int (*bdrv_pwrite)(BlockDriverState *bs, int64_t offset,
|
||||
const uint8_t *buf, int count);
|
||||
int (*bdrv_truncate)(BlockDriverState *bs, int64_t offset);
|
||||
int64_t (*bdrv_getlength)(BlockDriverState *bs);
|
||||
int (*bdrv_write_compressed)(BlockDriverState *bs, int64_t sector_num,
|
||||
@@ -93,10 +76,10 @@ struct BlockDriver {
|
||||
QEMUSnapshotInfo **psn_info);
|
||||
int (*bdrv_get_info)(BlockDriverState *bs, BlockDriverInfo *bdi);
|
||||
|
||||
int (*bdrv_save_vmstate)(BlockDriverState *bs, const uint8_t *buf,
|
||||
int64_t pos, int size);
|
||||
int (*bdrv_load_vmstate)(BlockDriverState *bs, uint8_t *buf,
|
||||
int64_t pos, int size);
|
||||
int (*bdrv_put_buffer)(BlockDriverState *bs, const uint8_t *buf,
|
||||
int64_t pos, int size);
|
||||
int (*bdrv_get_buffer)(BlockDriverState *bs, uint8_t *buf,
|
||||
int64_t pos, int size);
|
||||
|
||||
/* removable device specific */
|
||||
int (*bdrv_is_inserted)(BlockDriverState *bs);
|
||||
@@ -106,20 +89,8 @@ struct BlockDriver {
|
||||
|
||||
/* 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;
|
||||
|
||||
BlockDriverAIOCB *free_aiocb;
|
||||
struct BlockDriver *next;
|
||||
};
|
||||
|
||||
@@ -142,7 +113,6 @@ struct BlockDriverState {
|
||||
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;
|
||||
|
||||
@@ -160,24 +130,16 @@ struct BlockDriverState {
|
||||
/* 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;
|
||||
@@ -186,16 +148,10 @@ struct BlockDriverAIOCB {
|
||||
|
||||
void get_tmp_filename(char *filename, int size);
|
||||
|
||||
void *qemu_aio_get(AIOPool *pool, BlockDriverState *bs,
|
||||
BlockDriverCompletionFunc *cb, void *opaque);
|
||||
void *qemu_aio_get(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 */
|
||||
+4
-4
@@ -10,7 +10,7 @@
|
||||
|
||||
#include "qemu.h"
|
||||
|
||||
#define TARGET_NGROUPS 32
|
||||
#define NGROUPS 32
|
||||
|
||||
/* ??? This should really be somewhere else. */
|
||||
abi_long memcpy_to_target(abi_ulong dest, const void *src,
|
||||
@@ -31,9 +31,9 @@ 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];
|
||||
gid_t grouplist[NGROUPS];
|
||||
|
||||
ngroup = getgroups(TARGET_NGROUPS, grouplist);
|
||||
ngroup = getgroups(NGROUPS, grouplist);
|
||||
for(i = 0; i < ngroup; i++) {
|
||||
if(grouplist[i] == g) {
|
||||
return 1;
|
||||
@@ -163,7 +163,7 @@ int loader_exec(const char * filename, char ** argv, char ** envp,
|
||||
|
||||
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;
|
||||
bprm.page[i] = 0;
|
||||
retval = open(filename, O_RDONLY);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
|
||||
+9
-38
@@ -126,9 +126,6 @@ static inline void init_thread(struct target_pt_regs *regs, struct image_info *i
|
||||
regs->rax = 0;
|
||||
regs->rsp = infop->start_stack;
|
||||
regs->rip = infop->entry;
|
||||
if (bsd_type == target_freebsd) {
|
||||
regs->rdi = infop->start_stack;
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
@@ -252,13 +249,8 @@ static inline void init_thread(struct target_pt_regs *regs, struct image_info *i
|
||||
#else
|
||||
if (personality(infop->personality) == PER_LINUX32)
|
||||
regs->u_regs[14] = infop->start_stack - 16 * 4;
|
||||
else {
|
||||
else
|
||||
regs->u_regs[14] = infop->start_stack - 16 * 8 - STACK_BIAS;
|
||||
if (bsd_type == target_freebsd) {
|
||||
regs->u_regs[8] = infop->start_stack;
|
||||
regs->u_regs[11] = infop->start_stack;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -553,6 +545,8 @@ static inline void memcpy_fromfs(void * to, const void * from, unsigned long n)
|
||||
memcpy(to, from, n);
|
||||
}
|
||||
|
||||
extern unsigned long x86_stack_size;
|
||||
|
||||
static int load_aout_interp(void * exptr, int interp_fd);
|
||||
|
||||
#ifdef BSWAP_NEEDED
|
||||
@@ -1020,7 +1014,7 @@ static const char *lookup_symbolxx(struct syminfo *s, target_ulong orig_addr)
|
||||
key.st_value = orig_addr;
|
||||
|
||||
sym = bsearch(&key, syms, s->disas_num_syms, sizeof(*syms), symfind);
|
||||
if (sym != NULL) {
|
||||
if (sym != 0) {
|
||||
return s->disas_strtab + sym->st_name;
|
||||
}
|
||||
|
||||
@@ -1115,10 +1109,10 @@ static void load_symbols(struct elfhdr *hdr, int fd)
|
||||
s->disas_num_syms = nsyms;
|
||||
#if ELF_CLASS == ELFCLASS32
|
||||
s->disas_symtab.elf32 = syms;
|
||||
s->lookup_symbol = (lookup_symbol_t)lookup_symbolxx;
|
||||
s->lookup_symbol = lookup_symbolxx;
|
||||
#else
|
||||
s->disas_symtab.elf64 = syms;
|
||||
s->lookup_symbol = (lookup_symbol_t)lookup_symbolxx;
|
||||
s->lookup_symbol = lookup_symbolxx;
|
||||
#endif
|
||||
s->next = syminfos;
|
||||
syminfos = s;
|
||||
@@ -1254,7 +1248,7 @@ int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * regs,
|
||||
}
|
||||
|
||||
#if 0
|
||||
printf("Using ELF interpreter %s\n", path(elf_interpreter));
|
||||
printf("Using ELF interpreter %s\n", elf_interpreter);
|
||||
#endif
|
||||
if (retval >= 0) {
|
||||
retval = open(path(elf_interpreter), O_RDONLY);
|
||||
@@ -1276,7 +1270,7 @@ int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * regs,
|
||||
}
|
||||
if (retval >= 0) {
|
||||
interp_ex = *((struct exec *) bprm->buf); /* aout exec-header */
|
||||
interp_elf_ex = *((struct elfhdr *) bprm->buf); /* elf exec-header */
|
||||
interp_elf_ex=*((struct elfhdr *) bprm->buf); /* elf exec-header */
|
||||
}
|
||||
if (retval < 0) {
|
||||
perror("load_elf_binary3");
|
||||
@@ -1301,7 +1295,7 @@ int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * regs,
|
||||
}
|
||||
|
||||
if (interp_elf_ex.e_ident[0] != 0x7f ||
|
||||
strncmp((char *)&interp_elf_ex.e_ident[1], "ELF",3) != 0) {
|
||||
strncmp(&interp_elf_ex.e_ident[1], "ELF",3) != 0) {
|
||||
interpreter_type &= ~INTERPRETER_ELF;
|
||||
}
|
||||
|
||||
@@ -1345,29 +1339,6 @@ int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * regs,
|
||||
info->mmap = 0;
|
||||
elf_entry = (abi_ulong) elf_ex.e_entry;
|
||||
|
||||
#if defined(CONFIG_USE_GUEST_BASE)
|
||||
/*
|
||||
* In case where user has not explicitly set the guest_base, we
|
||||
* probe here that should we set it automatically.
|
||||
*/
|
||||
if (!have_guest_base) {
|
||||
/*
|
||||
* Go through ELF program header table and find out whether
|
||||
* any of the segments drop below our current mmap_min_addr and
|
||||
* in that case set guest_base to corresponding address.
|
||||
*/
|
||||
for (i = 0, elf_ppnt = elf_phdata; i < elf_ex.e_phnum;
|
||||
i++, elf_ppnt++) {
|
||||
if (elf_ppnt->p_type != PT_LOAD)
|
||||
continue;
|
||||
if (HOST_PAGE_ALIGN(elf_ppnt->p_vaddr) < mmap_min_addr) {
|
||||
guest_base = HOST_PAGE_ALIGN(mmap_min_addr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_USE_GUEST_BASE */
|
||||
|
||||
/* Do this so that we can load the interpreter, if need be. We will
|
||||
change some of these later */
|
||||
info->rss = 0;
|
||||
|
||||
@@ -39,7 +39,6 @@
|
||||
{ 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 },
|
||||
|
||||
@@ -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 */
|
||||
+23
-553
@@ -14,7 +14,9 @@
|
||||
* 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/>.
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
|
||||
* MA 02110-1301, USA.
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
@@ -23,30 +25,17 @@
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <machine/trap.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include "qemu.h"
|
||||
#include "qemu-common.h"
|
||||
/* For tb_lock */
|
||||
#include "exec-all.h"
|
||||
|
||||
|
||||
#include "envlist.h"
|
||||
|
||||
#define DEBUG_LOGFILE "/tmp/qemu.log"
|
||||
|
||||
int singlestep;
|
||||
#if defined(CONFIG_USE_GUEST_BASE)
|
||||
unsigned long mmap_min_addr;
|
||||
unsigned long guest_base;
|
||||
int have_guest_base;
|
||||
#endif
|
||||
|
||||
static const char *interp_prefix = CONFIG_QEMU_PREFIX;
|
||||
const char *qemu_uname_release = CONFIG_UNAME_RELEASE;
|
||||
extern char **environ;
|
||||
enum BSDType bsd_type;
|
||||
|
||||
/* XXX: on x86 MAP_GROWSDOWN only works if ESP <= address + 32, so
|
||||
we allocate a bigger stack. Need a better solution, for example
|
||||
@@ -61,333 +50,6 @@ void gemu_log(const char *fmt, ...)
|
||||
vfprintf(stderr, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
#if defined(TARGET_I386)
|
||||
int cpu_get_pic_interrupt(CPUState *env)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* These are no-ops because we are not threadsafe. */
|
||||
static inline void cpu_exec_start(CPUState *env)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void cpu_exec_end(CPUState *env)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void start_exclusive(void)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void end_exclusive(void)
|
||||
{
|
||||
}
|
||||
|
||||
void fork_start(void)
|
||||
{
|
||||
}
|
||||
|
||||
void fork_end(int child)
|
||||
{
|
||||
if (child) {
|
||||
gdbserver_fork(thread_env);
|
||||
}
|
||||
}
|
||||
|
||||
void cpu_list_lock(void)
|
||||
{
|
||||
}
|
||||
|
||||
void cpu_list_unlock(void)
|
||||
{
|
||||
}
|
||||
|
||||
#ifdef TARGET_I386
|
||||
/***********************************************************/
|
||||
/* CPUX86 core interface */
|
||||
|
||||
void cpu_smm_update(CPUState *env)
|
||||
{
|
||||
}
|
||||
|
||||
uint64_t cpu_get_tsc(CPUX86State *env)
|
||||
{
|
||||
return cpu_get_real_ticks();
|
||||
}
|
||||
|
||||
static void write_dt(void *ptr, unsigned long addr, unsigned long limit,
|
||||
int flags)
|
||||
{
|
||||
unsigned int e1, e2;
|
||||
uint32_t *p;
|
||||
e1 = (addr << 16) | (limit & 0xffff);
|
||||
e2 = ((addr >> 16) & 0xff) | (addr & 0xff000000) | (limit & 0x000f0000);
|
||||
e2 |= flags;
|
||||
p = ptr;
|
||||
p[0] = tswap32(e1);
|
||||
p[1] = tswap32(e2);
|
||||
}
|
||||
|
||||
static uint64_t *idt_table;
|
||||
#ifdef TARGET_X86_64
|
||||
static void set_gate64(void *ptr, unsigned int type, unsigned int dpl,
|
||||
uint64_t addr, unsigned int sel)
|
||||
{
|
||||
uint32_t *p, e1, e2;
|
||||
e1 = (addr & 0xffff) | (sel << 16);
|
||||
e2 = (addr & 0xffff0000) | 0x8000 | (dpl << 13) | (type << 8);
|
||||
p = ptr;
|
||||
p[0] = tswap32(e1);
|
||||
p[1] = tswap32(e2);
|
||||
p[2] = tswap32(addr >> 32);
|
||||
p[3] = 0;
|
||||
}
|
||||
/* only dpl matters as we do only user space emulation */
|
||||
static void set_idt(int n, unsigned int dpl)
|
||||
{
|
||||
set_gate64(idt_table + n * 2, 0, dpl, 0, 0);
|
||||
}
|
||||
#else
|
||||
static void set_gate(void *ptr, unsigned int type, unsigned int dpl,
|
||||
uint32_t addr, unsigned int sel)
|
||||
{
|
||||
uint32_t *p, e1, e2;
|
||||
e1 = (addr & 0xffff) | (sel << 16);
|
||||
e2 = (addr & 0xffff0000) | 0x8000 | (dpl << 13) | (type << 8);
|
||||
p = ptr;
|
||||
p[0] = tswap32(e1);
|
||||
p[1] = tswap32(e2);
|
||||
}
|
||||
|
||||
/* only dpl matters as we do only user space emulation */
|
||||
static void set_idt(int n, unsigned int dpl)
|
||||
{
|
||||
set_gate(idt_table + n, 0, dpl, 0, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
void cpu_loop(CPUX86State *env)
|
||||
{
|
||||
int trapnr;
|
||||
abi_ulong pc;
|
||||
//target_siginfo_t info;
|
||||
|
||||
for(;;) {
|
||||
trapnr = cpu_x86_exec(env);
|
||||
switch(trapnr) {
|
||||
case 0x80:
|
||||
/* syscall from int $0x80 */
|
||||
if (bsd_type == target_freebsd) {
|
||||
abi_ulong params = (abi_ulong) env->regs[R_ESP] +
|
||||
sizeof(int32_t);
|
||||
int32_t syscall_nr = env->regs[R_EAX];
|
||||
int32_t arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8;
|
||||
|
||||
if (syscall_nr == TARGET_FREEBSD_NR_syscall) {
|
||||
get_user_s32(syscall_nr, params);
|
||||
params += sizeof(int32_t);
|
||||
} else if (syscall_nr == TARGET_FREEBSD_NR___syscall) {
|
||||
get_user_s32(syscall_nr, params);
|
||||
params += sizeof(int64_t);
|
||||
}
|
||||
get_user_s32(arg1, params);
|
||||
params += sizeof(int32_t);
|
||||
get_user_s32(arg2, params);
|
||||
params += sizeof(int32_t);
|
||||
get_user_s32(arg3, params);
|
||||
params += sizeof(int32_t);
|
||||
get_user_s32(arg4, params);
|
||||
params += sizeof(int32_t);
|
||||
get_user_s32(arg5, params);
|
||||
params += sizeof(int32_t);
|
||||
get_user_s32(arg6, params);
|
||||
params += sizeof(int32_t);
|
||||
get_user_s32(arg7, params);
|
||||
params += sizeof(int32_t);
|
||||
get_user_s32(arg8, params);
|
||||
env->regs[R_EAX] = do_freebsd_syscall(env,
|
||||
syscall_nr,
|
||||
arg1,
|
||||
arg2,
|
||||
arg3,
|
||||
arg4,
|
||||
arg5,
|
||||
arg6,
|
||||
arg7,
|
||||
arg8);
|
||||
} else { //if (bsd_type == target_openbsd)
|
||||
env->regs[R_EAX] = do_openbsd_syscall(env,
|
||||
env->regs[R_EAX],
|
||||
env->regs[R_EBX],
|
||||
env->regs[R_ECX],
|
||||
env->regs[R_EDX],
|
||||
env->regs[R_ESI],
|
||||
env->regs[R_EDI],
|
||||
env->regs[R_EBP]);
|
||||
}
|
||||
if (((abi_ulong)env->regs[R_EAX]) >= (abi_ulong)(-515)) {
|
||||
env->regs[R_EAX] = -env->regs[R_EAX];
|
||||
env->eflags |= CC_C;
|
||||
} else {
|
||||
env->eflags &= ~CC_C;
|
||||
}
|
||||
break;
|
||||
#ifndef TARGET_ABI32
|
||||
case EXCP_SYSCALL:
|
||||
/* syscall from syscall intruction */
|
||||
if (bsd_type == target_freebsd)
|
||||
env->regs[R_EAX] = do_freebsd_syscall(env,
|
||||
env->regs[R_EAX],
|
||||
env->regs[R_EDI],
|
||||
env->regs[R_ESI],
|
||||
env->regs[R_EDX],
|
||||
env->regs[R_ECX],
|
||||
env->regs[8],
|
||||
env->regs[9], 0, 0);
|
||||
else { //if (bsd_type == target_openbsd)
|
||||
env->regs[R_EAX] = do_openbsd_syscall(env,
|
||||
env->regs[R_EAX],
|
||||
env->regs[R_EDI],
|
||||
env->regs[R_ESI],
|
||||
env->regs[R_EDX],
|
||||
env->regs[10],
|
||||
env->regs[8],
|
||||
env->regs[9]);
|
||||
}
|
||||
env->eip = env->exception_next_eip;
|
||||
if (((abi_ulong)env->regs[R_EAX]) >= (abi_ulong)(-515)) {
|
||||
env->regs[R_EAX] = -env->regs[R_EAX];
|
||||
env->eflags |= CC_C;
|
||||
} else {
|
||||
env->eflags &= ~CC_C;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
#if 0
|
||||
case EXCP0B_NOSEG:
|
||||
case EXCP0C_STACK:
|
||||
info.si_signo = SIGBUS;
|
||||
info.si_errno = 0;
|
||||
info.si_code = TARGET_SI_KERNEL;
|
||||
info._sifields._sigfault._addr = 0;
|
||||
queue_signal(env, info.si_signo, &info);
|
||||
break;
|
||||
case EXCP0D_GPF:
|
||||
/* XXX: potential problem if ABI32 */
|
||||
#ifndef TARGET_X86_64
|
||||
if (env->eflags & VM_MASK) {
|
||||
handle_vm86_fault(env);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
info.si_signo = SIGSEGV;
|
||||
info.si_errno = 0;
|
||||
info.si_code = TARGET_SI_KERNEL;
|
||||
info._sifields._sigfault._addr = 0;
|
||||
queue_signal(env, info.si_signo, &info);
|
||||
}
|
||||
break;
|
||||
case EXCP0E_PAGE:
|
||||
info.si_signo = SIGSEGV;
|
||||
info.si_errno = 0;
|
||||
if (!(env->error_code & 1))
|
||||
info.si_code = TARGET_SEGV_MAPERR;
|
||||
else
|
||||
info.si_code = TARGET_SEGV_ACCERR;
|
||||
info._sifields._sigfault._addr = env->cr[2];
|
||||
queue_signal(env, info.si_signo, &info);
|
||||
break;
|
||||
case EXCP00_DIVZ:
|
||||
#ifndef TARGET_X86_64
|
||||
if (env->eflags & VM_MASK) {
|
||||
handle_vm86_trap(env, trapnr);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
/* division by zero */
|
||||
info.si_signo = SIGFPE;
|
||||
info.si_errno = 0;
|
||||
info.si_code = TARGET_FPE_INTDIV;
|
||||
info._sifields._sigfault._addr = env->eip;
|
||||
queue_signal(env, info.si_signo, &info);
|
||||
}
|
||||
break;
|
||||
case EXCP01_DB:
|
||||
case EXCP03_INT3:
|
||||
#ifndef TARGET_X86_64
|
||||
if (env->eflags & VM_MASK) {
|
||||
handle_vm86_trap(env, trapnr);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
info.si_signo = SIGTRAP;
|
||||
info.si_errno = 0;
|
||||
if (trapnr == EXCP01_DB) {
|
||||
info.si_code = TARGET_TRAP_BRKPT;
|
||||
info._sifields._sigfault._addr = env->eip;
|
||||
} else {
|
||||
info.si_code = TARGET_SI_KERNEL;
|
||||
info._sifields._sigfault._addr = 0;
|
||||
}
|
||||
queue_signal(env, info.si_signo, &info);
|
||||
}
|
||||
break;
|
||||
case EXCP04_INTO:
|
||||
case EXCP05_BOUND:
|
||||
#ifndef TARGET_X86_64
|
||||
if (env->eflags & VM_MASK) {
|
||||
handle_vm86_trap(env, trapnr);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
info.si_signo = SIGSEGV;
|
||||
info.si_errno = 0;
|
||||
info.si_code = TARGET_SI_KERNEL;
|
||||
info._sifields._sigfault._addr = 0;
|
||||
queue_signal(env, info.si_signo, &info);
|
||||
}
|
||||
break;
|
||||
case EXCP06_ILLOP:
|
||||
info.si_signo = SIGILL;
|
||||
info.si_errno = 0;
|
||||
info.si_code = TARGET_ILL_ILLOPN;
|
||||
info._sifields._sigfault._addr = env->eip;
|
||||
queue_signal(env, info.si_signo, &info);
|
||||
break;
|
||||
#endif
|
||||
case EXCP_INTERRUPT:
|
||||
/* just indicate that signals should be handled asap */
|
||||
break;
|
||||
#if 0
|
||||
case EXCP_DEBUG:
|
||||
{
|
||||
int sig;
|
||||
|
||||
sig = gdb_handlesig (env, TARGET_SIGTRAP);
|
||||
if (sig)
|
||||
{
|
||||
info.si_signo = sig;
|
||||
info.si_errno = 0;
|
||||
info.si_code = TARGET_TRAP_BRKPT;
|
||||
queue_signal(env, info.si_signo, &info);
|
||||
}
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
pc = env->segs[R_CS].base + env->eip;
|
||||
fprintf(stderr, "qemu: 0x%08lx: unhandled CPU exception 0x%x - aborting\n",
|
||||
(long)pc, trapnr);
|
||||
abort();
|
||||
}
|
||||
process_pending_signals(env);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef TARGET_SPARC
|
||||
#define SPARC64_STACK_BIAS 2047
|
||||
|
||||
@@ -510,7 +172,7 @@ static void flush_windows(CPUSPARCState *env)
|
||||
#endif
|
||||
}
|
||||
|
||||
void cpu_loop(CPUSPARCState *env)
|
||||
void cpu_loop(CPUSPARCState *env, enum BSDType bsd_type)
|
||||
{
|
||||
int trapnr, ret, syscall_nr;
|
||||
//target_siginfo_t info;
|
||||
@@ -522,10 +184,6 @@ void cpu_loop(CPUSPARCState *env)
|
||||
#ifndef TARGET_SPARC64
|
||||
case 0x80:
|
||||
#else
|
||||
/* FreeBSD uses 0x141 for syscalls too */
|
||||
case 0x141:
|
||||
if (bsd_type != target_freebsd)
|
||||
goto badtrap;
|
||||
case 0x100:
|
||||
#endif
|
||||
syscall_nr = env->gregs[1];
|
||||
@@ -533,7 +191,7 @@ void cpu_loop(CPUSPARCState *env)
|
||||
ret = do_freebsd_syscall(env, syscall_nr,
|
||||
env->regwptr[0], env->regwptr[1],
|
||||
env->regwptr[2], env->regwptr[3],
|
||||
env->regwptr[4], env->regwptr[5], 0, 0);
|
||||
env->regwptr[4], env->regwptr[5]);
|
||||
else if (bsd_type == target_netbsd)
|
||||
ret = do_netbsd_syscall(env, syscall_nr,
|
||||
env->regwptr[0], env->regwptr[1],
|
||||
@@ -550,7 +208,6 @@ void cpu_loop(CPUSPARCState *env)
|
||||
env->regwptr[4], env->regwptr[5]);
|
||||
}
|
||||
if ((unsigned int)ret >= (unsigned int)(-515)) {
|
||||
ret = -ret;
|
||||
#if defined(TARGET_SPARC64) && !defined(TARGET_ABI32)
|
||||
env->xcc |= PSR_CARRY;
|
||||
#else
|
||||
@@ -656,9 +313,6 @@ void cpu_loop(CPUSPARCState *env)
|
||||
}
|
||||
break;
|
||||
default:
|
||||
#ifdef TARGET_SPARC64
|
||||
badtrap:
|
||||
#endif
|
||||
printf ("Unhandled trap: 0x%x\n", trapnr);
|
||||
cpu_dump_state(env, stderr, fprintf, 0);
|
||||
exit (1);
|
||||
@@ -682,28 +336,16 @@ static void usage(void)
|
||||
"-s size set the stack size in bytes (default=%ld)\n"
|
||||
"-cpu model select CPU (-cpu ? for list)\n"
|
||||
"-drop-ld-preload drop LD_PRELOAD for target process\n"
|
||||
"-E var=value sets/modifies targets environment variable(s)\n"
|
||||
"-U var unsets targets environment variable(s)\n"
|
||||
#if defined(CONFIG_USE_GUEST_BASE)
|
||||
"-B address set guest_base address to address\n"
|
||||
#endif
|
||||
"-bsd type select emulated BSD type FreeBSD/NetBSD/OpenBSD (default)\n"
|
||||
"\n"
|
||||
"Debug options:\n"
|
||||
"-d options activate log (logfile=%s)\n"
|
||||
"-p pagesize set the host page size to 'pagesize'\n"
|
||||
"-singlestep always run in singlestep mode\n"
|
||||
"-strace log system calls\n"
|
||||
"\n"
|
||||
"Environment variables:\n"
|
||||
"QEMU_STRACE Print system calls and arguments similar to the\n"
|
||||
" 'strace' program. Enable by setting to any value.\n"
|
||||
"You can use -E and -U options to set/unset environment variables\n"
|
||||
"for target process. It is possible to provide several variables\n"
|
||||
"by repeating the option. For example:\n"
|
||||
" -E var1=val2 -E var2=val2 -U LD_PRELOAD -U LD_DEBUG\n"
|
||||
"Note that if you provide several changes to single variable\n"
|
||||
"last change will stay in effect.\n"
|
||||
,
|
||||
TARGET_ARCH,
|
||||
interp_prefix,
|
||||
@@ -738,9 +380,9 @@ int main(int argc, char **argv)
|
||||
int optind;
|
||||
const char *r;
|
||||
int gdbstub_port = 0;
|
||||
char **target_environ, **wrk;
|
||||
envlist_t *envlist = NULL;
|
||||
bsd_type = target_openbsd;
|
||||
int drop_ld_preload = 0, environ_count = 0;
|
||||
char **target_environ, **wrk, **dst;
|
||||
enum BSDType bsd_type = target_openbsd;
|
||||
|
||||
if (argc <= 1)
|
||||
usage();
|
||||
@@ -748,16 +390,6 @@ int main(int argc, char **argv)
|
||||
/* init debug */
|
||||
cpu_set_log_filename(DEBUG_LOGFILE);
|
||||
|
||||
if ((envlist = envlist_create()) == NULL) {
|
||||
(void) fprintf(stderr, "Unable to allocate envlist\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* add current environment into the list */
|
||||
for (wrk = environ; *wrk != NULL; wrk++) {
|
||||
(void) envlist_setenv(envlist, *wrk);
|
||||
}
|
||||
|
||||
cpu_model = NULL;
|
||||
optind = 1;
|
||||
for(;;) {
|
||||
@@ -787,14 +419,6 @@ int main(int argc, char **argv)
|
||||
exit(1);
|
||||
}
|
||||
cpu_set_log(mask);
|
||||
} else if (!strcmp(r, "E")) {
|
||||
r = argv[optind++];
|
||||
if (envlist_setenv(envlist, r) != 0)
|
||||
usage();
|
||||
} else if (!strcmp(r, "U")) {
|
||||
r = argv[optind++];
|
||||
if (envlist_unsetenv(envlist, r) != 0)
|
||||
usage();
|
||||
} else if (!strcmp(r, "s")) {
|
||||
r = argv[optind++];
|
||||
x86_stack_size = strtol(r, (char **)&r, 0);
|
||||
@@ -826,13 +450,8 @@ int main(int argc, char **argv)
|
||||
#endif
|
||||
exit(1);
|
||||
}
|
||||
#if defined(CONFIG_USE_GUEST_BASE)
|
||||
} else if (!strcmp(r, "B")) {
|
||||
guest_base = strtol(argv[optind++], NULL, 0);
|
||||
have_guest_base = 1;
|
||||
#endif
|
||||
} else if (!strcmp(r, "drop-ld-preload")) {
|
||||
(void) envlist_unsetenv(envlist, "LD_PRELOAD");
|
||||
drop_ld_preload = 1;
|
||||
} else if (!strcmp(r, "bsd")) {
|
||||
if (!strcasecmp(argv[optind], "freebsd")) {
|
||||
bsd_type = target_freebsd;
|
||||
@@ -844,8 +463,6 @@ int main(int argc, char **argv)
|
||||
usage();
|
||||
}
|
||||
optind++;
|
||||
} else if (!strcmp(r, "singlestep")) {
|
||||
singlestep = 1;
|
||||
} else if (!strcmp(r, "strace")) {
|
||||
do_strace = 1;
|
||||
} else
|
||||
@@ -867,13 +484,7 @@ int main(int argc, char **argv)
|
||||
init_paths(interp_prefix);
|
||||
|
||||
if (cpu_model == NULL) {
|
||||
#if defined(TARGET_I386)
|
||||
#ifdef TARGET_X86_64
|
||||
cpu_model = "qemu64";
|
||||
#else
|
||||
cpu_model = "qemu32";
|
||||
#endif
|
||||
#elif defined(TARGET_SPARC)
|
||||
#if defined(TARGET_SPARC)
|
||||
#ifdef TARGET_SPARC64
|
||||
cpu_model = "TI UltraSparc II";
|
||||
#else
|
||||
@@ -891,46 +502,25 @@ int main(int argc, char **argv)
|
||||
fprintf(stderr, "Unable to find CPU definition\n");
|
||||
exit(1);
|
||||
}
|
||||
#if defined(TARGET_I386) || defined(TARGET_SPARC) || defined(TARGET_PPC)
|
||||
cpu_reset(env);
|
||||
#endif
|
||||
thread_env = env;
|
||||
|
||||
if (getenv("QEMU_STRACE")) {
|
||||
do_strace = 1;
|
||||
}
|
||||
|
||||
target_environ = envlist_to_environ(envlist, NULL);
|
||||
envlist_free(envlist);
|
||||
wrk = environ;
|
||||
while (*(wrk++))
|
||||
environ_count++;
|
||||
|
||||
#if defined(CONFIG_USE_GUEST_BASE)
|
||||
/*
|
||||
* Now that page sizes are configured in cpu_init() we can do
|
||||
* proper page alignment for guest_base.
|
||||
*/
|
||||
guest_base = HOST_PAGE_ALIGN(guest_base);
|
||||
|
||||
/*
|
||||
* Read in mmap_min_addr kernel parameter. This value is used
|
||||
* When loading the ELF image to determine whether guest_base
|
||||
* is needed.
|
||||
*
|
||||
* When user has explicitly set the quest base, we skip this
|
||||
* test.
|
||||
*/
|
||||
if (!have_guest_base) {
|
||||
FILE *fp;
|
||||
|
||||
if ((fp = fopen("/proc/sys/vm/mmap_min_addr", "r")) != NULL) {
|
||||
unsigned long tmp;
|
||||
if (fscanf(fp, "%lu", &tmp) == 1) {
|
||||
mmap_min_addr = tmp;
|
||||
qemu_log("host mmap_min_addr=0x%lx\n", mmap_min_addr);
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
target_environ = malloc((environ_count + 1) * sizeof(char *));
|
||||
if (!target_environ)
|
||||
abort();
|
||||
for (wrk = environ, dst = target_environ; *wrk; wrk++) {
|
||||
if (drop_ld_preload && !strncmp(*wrk, "LD_PRELOAD=", 11))
|
||||
continue;
|
||||
*(dst++) = strdup(*wrk);
|
||||
}
|
||||
#endif /* CONFIG_USE_GUEST_BASE */
|
||||
*dst = NULL; /* NULL terminate target_environ */
|
||||
|
||||
if (loader_exec(filename, argv+optind, target_environ, regs, info) != 0) {
|
||||
printf("Error loading %s\n", filename);
|
||||
@@ -944,9 +534,6 @@ int main(int argc, char **argv)
|
||||
free(target_environ);
|
||||
|
||||
if (qemu_log_enabled()) {
|
||||
#if defined(CONFIG_USE_GUEST_BASE)
|
||||
qemu_log("guest_base 0x%lx\n", guest_base);
|
||||
#endif
|
||||
log_page_dump();
|
||||
|
||||
qemu_log("start_brk 0x" TARGET_ABI_FMT_lx "\n", info->start_brk);
|
||||
@@ -972,124 +559,7 @@ int main(int argc, char **argv)
|
||||
ts->info = info;
|
||||
env->opaque = ts;
|
||||
|
||||
#if defined(TARGET_I386)
|
||||
cpu_x86_set_cpl(env, 3);
|
||||
|
||||
env->cr[0] = CR0_PG_MASK | CR0_WP_MASK | CR0_PE_MASK;
|
||||
env->hflags |= HF_PE_MASK;
|
||||
if (env->cpuid_features & CPUID_SSE) {
|
||||
env->cr[4] |= CR4_OSFXSR_MASK;
|
||||
env->hflags |= HF_OSFXSR_MASK;
|
||||
}
|
||||
#ifndef TARGET_ABI32
|
||||
/* enable 64 bit mode if possible */
|
||||
if (!(env->cpuid_ext2_features & CPUID_EXT2_LM)) {
|
||||
fprintf(stderr, "The selected x86 CPU does not support 64 bit mode\n");
|
||||
exit(1);
|
||||
}
|
||||
env->cr[4] |= CR4_PAE_MASK;
|
||||
env->efer |= MSR_EFER_LMA | MSR_EFER_LME;
|
||||
env->hflags |= HF_LMA_MASK;
|
||||
#endif
|
||||
|
||||
/* flags setup : we activate the IRQs by default as in user mode */
|
||||
env->eflags |= IF_MASK;
|
||||
|
||||
/* linux register setup */
|
||||
#ifndef TARGET_ABI32
|
||||
env->regs[R_EAX] = regs->rax;
|
||||
env->regs[R_EBX] = regs->rbx;
|
||||
env->regs[R_ECX] = regs->rcx;
|
||||
env->regs[R_EDX] = regs->rdx;
|
||||
env->regs[R_ESI] = regs->rsi;
|
||||
env->regs[R_EDI] = regs->rdi;
|
||||
env->regs[R_EBP] = regs->rbp;
|
||||
env->regs[R_ESP] = regs->rsp;
|
||||
env->eip = regs->rip;
|
||||
#else
|
||||
env->regs[R_EAX] = regs->eax;
|
||||
env->regs[R_EBX] = regs->ebx;
|
||||
env->regs[R_ECX] = regs->ecx;
|
||||
env->regs[R_EDX] = regs->edx;
|
||||
env->regs[R_ESI] = regs->esi;
|
||||
env->regs[R_EDI] = regs->edi;
|
||||
env->regs[R_EBP] = regs->ebp;
|
||||
env->regs[R_ESP] = regs->esp;
|
||||
env->eip = regs->eip;
|
||||
#endif
|
||||
|
||||
/* linux interrupt setup */
|
||||
#ifndef TARGET_ABI32
|
||||
env->idt.limit = 511;
|
||||
#else
|
||||
env->idt.limit = 255;
|
||||
#endif
|
||||
env->idt.base = target_mmap(0, sizeof(uint64_t) * (env->idt.limit + 1),
|
||||
PROT_READ|PROT_WRITE,
|
||||
MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
|
||||
idt_table = g2h(env->idt.base);
|
||||
set_idt(0, 0);
|
||||
set_idt(1, 0);
|
||||
set_idt(2, 0);
|
||||
set_idt(3, 3);
|
||||
set_idt(4, 3);
|
||||
set_idt(5, 0);
|
||||
set_idt(6, 0);
|
||||
set_idt(7, 0);
|
||||
set_idt(8, 0);
|
||||
set_idt(9, 0);
|
||||
set_idt(10, 0);
|
||||
set_idt(11, 0);
|
||||
set_idt(12, 0);
|
||||
set_idt(13, 0);
|
||||
set_idt(14, 0);
|
||||
set_idt(15, 0);
|
||||
set_idt(16, 0);
|
||||
set_idt(17, 0);
|
||||
set_idt(18, 0);
|
||||
set_idt(19, 0);
|
||||
set_idt(0x80, 3);
|
||||
|
||||
/* linux segment setup */
|
||||
{
|
||||
uint64_t *gdt_table;
|
||||
env->gdt.base = target_mmap(0, sizeof(uint64_t) * TARGET_GDT_ENTRIES,
|
||||
PROT_READ|PROT_WRITE,
|
||||
MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
|
||||
env->gdt.limit = sizeof(uint64_t) * TARGET_GDT_ENTRIES - 1;
|
||||
gdt_table = g2h(env->gdt.base);
|
||||
#ifdef TARGET_ABI32
|
||||
write_dt(&gdt_table[__USER_CS >> 3], 0, 0xfffff,
|
||||
DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK |
|
||||
(3 << DESC_DPL_SHIFT) | (0xa << DESC_TYPE_SHIFT));
|
||||
#else
|
||||
/* 64 bit code segment */
|
||||
write_dt(&gdt_table[__USER_CS >> 3], 0, 0xfffff,
|
||||
DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK |
|
||||
DESC_L_MASK |
|
||||
(3 << DESC_DPL_SHIFT) | (0xa << DESC_TYPE_SHIFT));
|
||||
#endif
|
||||
write_dt(&gdt_table[__USER_DS >> 3], 0, 0xfffff,
|
||||
DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK |
|
||||
(3 << DESC_DPL_SHIFT) | (0x2 << DESC_TYPE_SHIFT));
|
||||
}
|
||||
|
||||
cpu_x86_load_seg(env, R_CS, __USER_CS);
|
||||
cpu_x86_load_seg(env, R_SS, __USER_DS);
|
||||
#ifdef TARGET_ABI32
|
||||
cpu_x86_load_seg(env, R_DS, __USER_DS);
|
||||
cpu_x86_load_seg(env, R_ES, __USER_DS);
|
||||
cpu_x86_load_seg(env, R_FS, __USER_DS);
|
||||
cpu_x86_load_seg(env, R_GS, __USER_DS);
|
||||
/* This hack makes Wine work... */
|
||||
env->segs[R_FS].selector = 0;
|
||||
#else
|
||||
cpu_x86_load_seg(env, R_DS, 0);
|
||||
cpu_x86_load_seg(env, R_ES, 0);
|
||||
cpu_x86_load_seg(env, R_FS, 0);
|
||||
cpu_x86_load_seg(env, R_GS, 0);
|
||||
#endif
|
||||
#elif defined(TARGET_SPARC)
|
||||
#if defined(TARGET_SPARC)
|
||||
{
|
||||
int i;
|
||||
env->pc = regs->pc;
|
||||
@@ -1108,7 +578,7 @@ int main(int argc, char **argv)
|
||||
gdbserver_start (gdbstub_port);
|
||||
gdb_handlesig(env, 0);
|
||||
}
|
||||
cpu_loop(env);
|
||||
cpu_loop(env, bsd_type);
|
||||
/* never exits */
|
||||
return 0;
|
||||
}
|
||||
+4
-2
@@ -14,7 +14,9 @@
|
||||
* 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/>.
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
|
||||
* MA 02110-1301, USA.
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
@@ -30,7 +32,7 @@
|
||||
|
||||
//#define DEBUG_MMAP
|
||||
|
||||
#if defined(CONFIG_USE_NPTL)
|
||||
#if defined(USE_NPTL)
|
||||
pthread_mutex_t mmap_mutex;
|
||||
static int __thread mmap_lock_count;
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include "qemu.h"
|
||||
#include "qemu-common.h"
|
||||
|
||||
struct pathelem
|
||||
@@ -155,7 +156,7 @@ const char *path(const char *name)
|
||||
{
|
||||
/* Only do absolute paths: quick and dirty, but should mostly be OK.
|
||||
Could do relative by tracking cwd. */
|
||||
if (!base || !name || name[0] != '/')
|
||||
if (!base || name[0] != '/')
|
||||
return name;
|
||||
|
||||
return follow_path(base, name) ?: name;
|
||||
+7
-15
@@ -18,14 +18,13 @@ enum BSDType {
|
||||
target_netbsd,
|
||||
target_openbsd,
|
||||
};
|
||||
extern enum BSDType bsd_type;
|
||||
|
||||
#include "syscall_defs.h"
|
||||
#include "syscall.h"
|
||||
#include "target_signal.h"
|
||||
#include "gdbstub.h"
|
||||
|
||||
#if defined(CONFIG_USE_NPTL)
|
||||
#if defined(USE_NPTL)
|
||||
#define THREAD __thread
|
||||
#else
|
||||
#define THREAD
|
||||
@@ -85,9 +84,6 @@ typedef struct TaskState {
|
||||
|
||||
void init_task_state(TaskState *ts);
|
||||
extern const char *qemu_uname_release;
|
||||
#if defined(CONFIG_USE_GUEST_BASE)
|
||||
extern unsigned long mmap_min_addr;
|
||||
#endif
|
||||
|
||||
/* ??? See if we can avoid exposing so much of the loader internals. */
|
||||
/*
|
||||
@@ -131,8 +127,7 @@ abi_long do_brk(abi_ulong new_brk);
|
||||
void syscall_init(void);
|
||||
abi_long do_freebsd_syscall(void *cpu_env, int num, abi_long arg1,
|
||||
abi_long arg2, abi_long arg3, abi_long arg4,
|
||||
abi_long arg5, abi_long arg6, abi_long arg7,
|
||||
abi_long arg8);
|
||||
abi_long arg5, abi_long arg6);
|
||||
abi_long do_netbsd_syscall(void *cpu_env, int num, abi_long arg1,
|
||||
abi_long arg2, abi_long arg3, abi_long arg4,
|
||||
abi_long arg5, abi_long arg6);
|
||||
@@ -141,7 +136,9 @@ abi_long do_openbsd_syscall(void *cpu_env, int num, abi_long arg1,
|
||||
abi_long arg5, abi_long arg6);
|
||||
void gemu_log(const char *fmt, ...) __attribute__((format(printf,1,2)));
|
||||
extern THREAD CPUState *thread_env;
|
||||
void cpu_loop(CPUState *env);
|
||||
void cpu_loop(CPUState *env, enum BSDType bsd_type);
|
||||
void init_paths(const char *prefix);
|
||||
const char *path(const char *pathname);
|
||||
char *target_strerror(int err);
|
||||
int get_osversion(void);
|
||||
void fork_start(void);
|
||||
@@ -189,16 +186,11 @@ int target_msync(abi_ulong start, abi_ulong len, int flags);
|
||||
extern unsigned long last_brk;
|
||||
void mmap_lock(void);
|
||||
void mmap_unlock(void);
|
||||
void cpu_list_lock(void);
|
||||
void cpu_list_unlock(void);
|
||||
#if defined(CONFIG_USE_NPTL)
|
||||
#if defined(USE_NPTL)
|
||||
void mmap_fork_start(void);
|
||||
void mmap_fork_end(int child);
|
||||
#endif
|
||||
|
||||
/* main.c */
|
||||
extern unsigned long x86_stack_size;
|
||||
|
||||
/* user access */
|
||||
|
||||
#define VERIFY_READ 0
|
||||
@@ -388,7 +380,7 @@ static inline void *lock_user_string(abi_ulong guest_addr)
|
||||
#define unlock_user_struct(host_ptr, guest_addr, copy) \
|
||||
unlock_user(host_ptr, guest_addr, (copy) ? sizeof(*host_ptr) : 0)
|
||||
|
||||
#if defined(CONFIG_USE_NPTL)
|
||||
#if defined(USE_NPTL)
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
|
||||
|
||||
+3
-1
@@ -14,7 +14,9 @@
|
||||
* 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/>.
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
|
||||
* MA 02110-1301, USA.
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
+2
-2
@@ -36,7 +36,7 @@ print_execve(const struct syscallname *name,
|
||||
unlock_user(s, arg1, 0);
|
||||
|
||||
for (arg_ptr_addr = arg2; ; arg_ptr_addr += sizeof(abi_ulong)) {
|
||||
abi_ulong *arg_ptr, arg_addr;
|
||||
abi_ulong *arg_ptr, arg_addr, s_addr;
|
||||
|
||||
arg_ptr = lock_user(VERIFY_READ, arg_ptr_addr, sizeof(abi_ulong), 1);
|
||||
if (!arg_ptr)
|
||||
@@ -47,7 +47,7 @@ print_execve(const struct syscallname *name,
|
||||
break;
|
||||
if ((s = lock_user_string(arg_addr))) {
|
||||
gemu_log("\"%s\",", s);
|
||||
unlock_user(s, arg_addr, 0);
|
||||
unlock_user(s, s_addr, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+10
-296
@@ -14,7 +14,9 @@
|
||||
* 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/>.
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
|
||||
* MA 02110-1301, USA.
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
@@ -29,8 +31,6 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <signal.h>
|
||||
#include <utime.h>
|
||||
|
||||
@@ -42,283 +42,20 @@
|
||||
static abi_ulong target_brk;
|
||||
static abi_ulong target_original_brk;
|
||||
|
||||
static inline abi_long get_errno(abi_long ret)
|
||||
{
|
||||
if (ret == -1)
|
||||
/* XXX need to translate host -> target errnos here */
|
||||
return -(errno);
|
||||
else
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define get_errno(x) (x)
|
||||
#define target_to_host_bitmask(x, tbl) (x)
|
||||
|
||||
static inline int is_error(abi_long ret)
|
||||
{
|
||||
return (abi_ulong)ret >= (abi_ulong)(-4096);
|
||||
}
|
||||
|
||||
void target_set_brk(abi_ulong new_brk)
|
||||
{
|
||||
target_original_brk = target_brk = HOST_PAGE_ALIGN(new_brk);
|
||||
}
|
||||
|
||||
/* do_obreak() must return target errnos. */
|
||||
static abi_long do_obreak(abi_ulong new_brk)
|
||||
{
|
||||
abi_ulong brk_page;
|
||||
abi_long mapped_addr;
|
||||
int new_alloc_size;
|
||||
|
||||
if (!new_brk)
|
||||
return 0;
|
||||
if (new_brk < target_original_brk)
|
||||
return -TARGET_EINVAL;
|
||||
|
||||
brk_page = HOST_PAGE_ALIGN(target_brk);
|
||||
|
||||
/* If the new brk is less than this, set it and we're done... */
|
||||
if (new_brk < brk_page) {
|
||||
target_brk = new_brk;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* We need to allocate more memory after the brk... */
|
||||
new_alloc_size = HOST_PAGE_ALIGN(new_brk - brk_page + 1);
|
||||
mapped_addr = get_errno(target_mmap(brk_page, new_alloc_size,
|
||||
PROT_READ|PROT_WRITE,
|
||||
MAP_ANON|MAP_FIXED|MAP_PRIVATE, -1, 0));
|
||||
|
||||
if (!is_error(mapped_addr))
|
||||
target_brk = new_brk;
|
||||
else
|
||||
return mapped_addr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(TARGET_I386)
|
||||
static abi_long do_freebsd_sysarch(CPUX86State *env, int op, abi_ulong parms)
|
||||
{
|
||||
abi_long ret = 0;
|
||||
abi_ulong val;
|
||||
int idx;
|
||||
|
||||
switch(op) {
|
||||
#ifdef TARGET_ABI32
|
||||
case TARGET_FREEBSD_I386_SET_GSBASE:
|
||||
case TARGET_FREEBSD_I386_SET_FSBASE:
|
||||
if (op == TARGET_FREEBSD_I386_SET_GSBASE)
|
||||
#else
|
||||
case TARGET_FREEBSD_AMD64_SET_GSBASE:
|
||||
case TARGET_FREEBSD_AMD64_SET_FSBASE:
|
||||
if (op == TARGET_FREEBSD_AMD64_SET_GSBASE)
|
||||
#endif
|
||||
idx = R_GS;
|
||||
else
|
||||
idx = R_FS;
|
||||
if (get_user(val, parms, abi_ulong))
|
||||
return -TARGET_EFAULT;
|
||||
cpu_x86_load_seg(env, idx, 0);
|
||||
env->segs[idx].base = val;
|
||||
break;
|
||||
#ifdef TARGET_ABI32
|
||||
case TARGET_FREEBSD_I386_GET_GSBASE:
|
||||
case TARGET_FREEBSD_I386_GET_FSBASE:
|
||||
if (op == TARGET_FREEBSD_I386_GET_GSBASE)
|
||||
#else
|
||||
case TARGET_FREEBSD_AMD64_GET_GSBASE:
|
||||
case TARGET_FREEBSD_AMD64_GET_FSBASE:
|
||||
if (op == TARGET_FREEBSD_AMD64_GET_GSBASE)
|
||||
#endif
|
||||
idx = R_GS;
|
||||
else
|
||||
idx = R_FS;
|
||||
val = env->segs[idx].base;
|
||||
if (put_user(val, parms, abi_ulong))
|
||||
return -TARGET_EFAULT;
|
||||
break;
|
||||
/* XXX handle the others... */
|
||||
default:
|
||||
ret = -TARGET_EINVAL;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef TARGET_SPARC
|
||||
static abi_long do_freebsd_sysarch(void *env, int op, abi_ulong parms)
|
||||
{
|
||||
/* XXX handle
|
||||
* TARGET_FREEBSD_SPARC_UTRAP_INSTALL,
|
||||
* TARGET_FREEBSD_SPARC_SIGTRAMP_INSTALL
|
||||
*/
|
||||
return -TARGET_EINVAL;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __FreeBSD__
|
||||
/*
|
||||
* XXX this uses the undocumented oidfmt interface to find the kind of
|
||||
* a requested sysctl, see /sys/kern/kern_sysctl.c:sysctl_sysctl_oidfmt()
|
||||
* (this is mostly copied from src/sbin/sysctl/sysctl.c)
|
||||
*/
|
||||
static int
|
||||
oidfmt(int *oid, int len, char *fmt, uint32_t *kind)
|
||||
{
|
||||
int qoid[CTL_MAXNAME+2];
|
||||
uint8_t buf[BUFSIZ];
|
||||
int i;
|
||||
size_t j;
|
||||
|
||||
qoid[0] = 0;
|
||||
qoid[1] = 4;
|
||||
memcpy(qoid + 2, oid, len * sizeof(int));
|
||||
|
||||
j = sizeof(buf);
|
||||
i = sysctl(qoid, len + 2, buf, &j, 0, 0);
|
||||
if (i)
|
||||
return i;
|
||||
|
||||
if (kind)
|
||||
*kind = *(uint32_t *)buf;
|
||||
|
||||
if (fmt)
|
||||
strcpy(fmt, (char *)(buf + sizeof(uint32_t)));
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* try and convert sysctl return data for the target.
|
||||
* XXX doesn't handle CTLTYPE_OPAQUE and CTLTYPE_STRUCT.
|
||||
*/
|
||||
static int sysctl_oldcvt(void *holdp, size_t holdlen, uint32_t kind)
|
||||
{
|
||||
switch (kind & CTLTYPE) {
|
||||
case CTLTYPE_INT:
|
||||
case CTLTYPE_UINT:
|
||||
*(uint32_t *)holdp = tswap32(*(uint32_t *)holdp);
|
||||
break;
|
||||
#ifdef TARGET_ABI32
|
||||
case CTLTYPE_LONG:
|
||||
case CTLTYPE_ULONG:
|
||||
*(uint32_t *)holdp = tswap32(*(long *)holdp);
|
||||
break;
|
||||
#else
|
||||
case CTLTYPE_LONG:
|
||||
*(uint64_t *)holdp = tswap64(*(long *)holdp);
|
||||
case CTLTYPE_ULONG:
|
||||
*(uint64_t *)holdp = tswap64(*(unsigned long *)holdp);
|
||||
break;
|
||||
#endif
|
||||
case CTLTYPE_QUAD:
|
||||
*(uint64_t *)holdp = tswap64(*(uint64_t *)holdp);
|
||||
break;
|
||||
case CTLTYPE_STRING:
|
||||
break;
|
||||
default:
|
||||
/* XXX unhandled */
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* XXX this needs to be emulated on non-FreeBSD hosts... */
|
||||
static abi_long do_freebsd_sysctl(abi_ulong namep, int32_t namelen, abi_ulong oldp,
|
||||
abi_ulong oldlenp, abi_ulong newp, abi_ulong newlen)
|
||||
{
|
||||
abi_long ret;
|
||||
void *hnamep, *holdp, *hnewp = NULL;
|
||||
size_t holdlen;
|
||||
abi_ulong oldlen = 0;
|
||||
int32_t *snamep = qemu_malloc(sizeof(int32_t) * namelen), *p, *q, i;
|
||||
uint32_t kind = 0;
|
||||
|
||||
if (oldlenp)
|
||||
get_user_ual(oldlen, oldlenp);
|
||||
if (!(hnamep = lock_user(VERIFY_READ, namep, namelen, 1)))
|
||||
return -TARGET_EFAULT;
|
||||
if (newp && !(hnewp = lock_user(VERIFY_READ, newp, newlen, 1)))
|
||||
return -TARGET_EFAULT;
|
||||
if (!(holdp = lock_user(VERIFY_WRITE, oldp, oldlen, 0)))
|
||||
return -TARGET_EFAULT;
|
||||
holdlen = oldlen;
|
||||
for (p = hnamep, q = snamep, i = 0; i < namelen; p++, i++)
|
||||
*q++ = tswap32(*p);
|
||||
oidfmt(snamep, namelen, NULL, &kind);
|
||||
/* XXX swap hnewp */
|
||||
ret = get_errno(sysctl(snamep, namelen, holdp, &holdlen, hnewp, newlen));
|
||||
if (!ret)
|
||||
sysctl_oldcvt(holdp, holdlen, kind);
|
||||
put_user_ual(holdlen, oldlenp);
|
||||
unlock_user(hnamep, namep, 0);
|
||||
unlock_user(holdp, oldp, holdlen);
|
||||
if (hnewp)
|
||||
unlock_user(hnewp, newp, 0);
|
||||
qemu_free(snamep);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* FIXME
|
||||
* lock_iovec()/unlock_iovec() have a return code of 0 for success where
|
||||
* other lock functions have a return code of 0 for failure.
|
||||
*/
|
||||
static abi_long lock_iovec(int type, struct iovec *vec, abi_ulong target_addr,
|
||||
int count, int copy)
|
||||
{
|
||||
struct target_iovec *target_vec;
|
||||
abi_ulong base;
|
||||
int i;
|
||||
|
||||
target_vec = lock_user(VERIFY_READ, target_addr, count * sizeof(struct target_iovec), 1);
|
||||
if (!target_vec)
|
||||
return -TARGET_EFAULT;
|
||||
for(i = 0;i < count; i++) {
|
||||
base = tswapl(target_vec[i].iov_base);
|
||||
vec[i].iov_len = tswapl(target_vec[i].iov_len);
|
||||
if (vec[i].iov_len != 0) {
|
||||
vec[i].iov_base = lock_user(type, base, vec[i].iov_len, copy);
|
||||
/* Don't check lock_user return value. We must call writev even
|
||||
if a element has invalid base address. */
|
||||
} else {
|
||||
/* zero length pointer is ignored */
|
||||
vec[i].iov_base = NULL;
|
||||
}
|
||||
}
|
||||
unlock_user (target_vec, target_addr, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static abi_long unlock_iovec(struct iovec *vec, abi_ulong target_addr,
|
||||
int count, int copy)
|
||||
{
|
||||
struct target_iovec *target_vec;
|
||||
abi_ulong base;
|
||||
int i;
|
||||
|
||||
target_vec = lock_user(VERIFY_READ, target_addr, count * sizeof(struct target_iovec), 1);
|
||||
if (!target_vec)
|
||||
return -TARGET_EFAULT;
|
||||
for(i = 0;i < count; i++) {
|
||||
if (target_vec[i].iov_base) {
|
||||
base = tswapl(target_vec[i].iov_base);
|
||||
unlock_user(vec[i].iov_base, base, copy ? vec[i].iov_len : 0);
|
||||
}
|
||||
}
|
||||
unlock_user (target_vec, target_addr, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* do_syscall() should always have a single exit point at the end so
|
||||
that actions, such as logging of syscall results, can be performed.
|
||||
All errnos that do_syscall() returns must be -TARGET_<errcode>. */
|
||||
abi_long do_freebsd_syscall(void *cpu_env, int num, abi_long arg1,
|
||||
abi_long arg2, abi_long arg3, abi_long arg4,
|
||||
abi_long arg5, abi_long arg6, abi_long arg7,
|
||||
abi_long arg8)
|
||||
abi_long arg5, abi_long arg6)
|
||||
{
|
||||
abi_long ret;
|
||||
void *p;
|
||||
@@ -331,7 +68,7 @@ abi_long do_freebsd_syscall(void *cpu_env, int num, abi_long arg1,
|
||||
|
||||
switch(num) {
|
||||
case TARGET_FREEBSD_NR_exit:
|
||||
#ifdef TARGET_GPROF
|
||||
#ifdef HAVE_GPROF
|
||||
_mcleanup();
|
||||
#endif
|
||||
gdb_exit(cpu_env, arg1);
|
||||
@@ -351,18 +88,6 @@ abi_long do_freebsd_syscall(void *cpu_env, int num, abi_long arg1,
|
||||
ret = get_errno(write(arg1, p, arg3));
|
||||
unlock_user(p, arg2, 0);
|
||||
break;
|
||||
case TARGET_FREEBSD_NR_writev:
|
||||
{
|
||||
int count = arg3;
|
||||
struct iovec *vec;
|
||||
|
||||
vec = alloca(count * sizeof(struct iovec));
|
||||
if (lock_iovec(VERIFY_READ, vec, arg2, count, 1) < 0)
|
||||
goto efault;
|
||||
ret = get_errno(writev(arg1, vec, count));
|
||||
unlock_iovec(vec, arg2, count, 0);
|
||||
}
|
||||
break;
|
||||
case TARGET_FREEBSD_NR_open:
|
||||
if (!(p = lock_user_string(arg1)))
|
||||
goto efault;
|
||||
@@ -380,23 +105,12 @@ abi_long do_freebsd_syscall(void *cpu_env, int num, abi_long arg1,
|
||||
case TARGET_FREEBSD_NR_mprotect:
|
||||
ret = get_errno(target_mprotect(arg1, arg2, arg3));
|
||||
break;
|
||||
case TARGET_FREEBSD_NR_break:
|
||||
ret = do_obreak(arg1);
|
||||
break;
|
||||
#ifdef __FreeBSD__
|
||||
case TARGET_FREEBSD_NR___sysctl:
|
||||
ret = do_freebsd_sysctl(arg1, arg2, arg3, arg4, arg5, arg6);
|
||||
break;
|
||||
#endif
|
||||
case TARGET_FREEBSD_NR_sysarch:
|
||||
ret = do_freebsd_sysarch(cpu_env, arg1, arg2);
|
||||
break;
|
||||
case TARGET_FREEBSD_NR_syscall:
|
||||
case TARGET_FREEBSD_NR___syscall:
|
||||
ret = do_freebsd_syscall(cpu_env,arg1 & 0xffff,arg2,arg3,arg4,arg5,arg6,arg7,arg8,0);
|
||||
ret = do_freebsd_syscall(cpu_env,arg1 & 0xffff,arg2,arg3,arg4,arg5,arg6,0);
|
||||
break;
|
||||
default:
|
||||
ret = get_errno(syscall(num, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8));
|
||||
ret = syscall(num, arg1, arg2, arg3, arg4, arg5, arg6);
|
||||
break;
|
||||
}
|
||||
fail:
|
||||
@@ -426,7 +140,7 @@ abi_long do_netbsd_syscall(void *cpu_env, int num, abi_long arg1,
|
||||
|
||||
switch(num) {
|
||||
case TARGET_NETBSD_NR_exit:
|
||||
#ifdef TARGET_GPROF
|
||||
#ifdef HAVE_GPROF
|
||||
_mcleanup();
|
||||
#endif
|
||||
gdb_exit(cpu_env, arg1);
|
||||
@@ -498,7 +212,7 @@ abi_long do_openbsd_syscall(void *cpu_env, int num, abi_long arg1,
|
||||
|
||||
switch(num) {
|
||||
case TARGET_OPENBSD_NR_exit:
|
||||
#ifdef TARGET_GPROF
|
||||
#ifdef HAVE_GPROF
|
||||
_mcleanup();
|
||||
#endif
|
||||
gdb_exit(cpu_env, arg1);
|
||||
|
||||
@@ -106,9 +106,3 @@
|
||||
#include "freebsd/syscall_nr.h"
|
||||
#include "netbsd/syscall_nr.h"
|
||||
#include "openbsd/syscall_nr.h"
|
||||
|
||||
struct target_iovec {
|
||||
abi_long iov_base; /* Starting address */
|
||||
abi_long iov_len; /* Number of bytes */
|
||||
};
|
||||
|
||||
+12
-1
@@ -37,6 +37,17 @@ abi_long copy_to_user(abi_ulong gaddr, void *hptr, size_t len)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* XXX: use host strnlen if available ? */
|
||||
static int qemu_strnlen(const char *s, int max_len)
|
||||
{
|
||||
int i;
|
||||
for(i = 0; i < max_len; i++) {
|
||||
if (s[i] == '\0')
|
||||
break;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
/* Return the length of a string in target memory or -TARGET_EFAULT if
|
||||
access error */
|
||||
abi_long target_strlen(abi_ulong guest_addr1)
|
||||
@@ -51,7 +62,7 @@ abi_long target_strlen(abi_ulong guest_addr1)
|
||||
ptr = lock_user(VERIFY_READ, guest_addr, max_len, 1);
|
||||
if (!ptr)
|
||||
return -TARGET_EFAULT;
|
||||
len = qemu_strnlen((char *)ptr, max_len);
|
||||
len = qemu_strnlen(ptr, max_len);
|
||||
unlock_user(ptr, guest_addr, 0);
|
||||
guest_addr += len;
|
||||
/* we don't allow wrapping or integer overflow */
|
||||
|
||||
@@ -1,116 +0,0 @@
|
||||
#define __USER_CS (0x33)
|
||||
#define __USER_DS (0x2B)
|
||||
|
||||
struct target_pt_regs {
|
||||
abi_ulong r15;
|
||||
abi_ulong r14;
|
||||
abi_ulong r13;
|
||||
abi_ulong r12;
|
||||
abi_ulong rbp;
|
||||
abi_ulong rbx;
|
||||
/* arguments: non interrupts/non tracing syscalls only save upto here*/
|
||||
abi_ulong r11;
|
||||
abi_ulong r10;
|
||||
abi_ulong r9;
|
||||
abi_ulong r8;
|
||||
abi_ulong rax;
|
||||
abi_ulong rcx;
|
||||
abi_ulong rdx;
|
||||
abi_ulong rsi;
|
||||
abi_ulong rdi;
|
||||
abi_ulong orig_rax;
|
||||
/* end of arguments */
|
||||
/* cpu exception frame or undefined */
|
||||
abi_ulong rip;
|
||||
abi_ulong cs;
|
||||
abi_ulong eflags;
|
||||
abi_ulong rsp;
|
||||
abi_ulong ss;
|
||||
/* top of stack page */
|
||||
};
|
||||
|
||||
/* Maximum number of LDT entries supported. */
|
||||
#define TARGET_LDT_ENTRIES 8192
|
||||
/* The size of each LDT entry. */
|
||||
#define TARGET_LDT_ENTRY_SIZE 8
|
||||
|
||||
#define TARGET_GDT_ENTRIES 16
|
||||
#define TARGET_GDT_ENTRY_TLS_ENTRIES 3
|
||||
#define TARGET_GDT_ENTRY_TLS_MIN 12
|
||||
#define TARGET_GDT_ENTRY_TLS_MAX 14
|
||||
|
||||
#if 0 // Redefine this
|
||||
struct target_modify_ldt_ldt_s {
|
||||
unsigned int entry_number;
|
||||
abi_ulong base_addr;
|
||||
unsigned int limit;
|
||||
unsigned int seg_32bit:1;
|
||||
unsigned int contents:2;
|
||||
unsigned int read_exec_only:1;
|
||||
unsigned int limit_in_pages:1;
|
||||
unsigned int seg_not_present:1;
|
||||
unsigned int useable:1;
|
||||
unsigned int lm:1;
|
||||
};
|
||||
#else
|
||||
struct target_modify_ldt_ldt_s {
|
||||
unsigned int entry_number;
|
||||
abi_ulong base_addr;
|
||||
unsigned int limit;
|
||||
unsigned int flags;
|
||||
};
|
||||
#endif
|
||||
|
||||
struct target_ipc64_perm
|
||||
{
|
||||
int key;
|
||||
uint32_t uid;
|
||||
uint32_t gid;
|
||||
uint32_t cuid;
|
||||
uint32_t cgid;
|
||||
unsigned short mode;
|
||||
unsigned short __pad1;
|
||||
unsigned short seq;
|
||||
unsigned short __pad2;
|
||||
abi_ulong __unused1;
|
||||
abi_ulong __unused2;
|
||||
};
|
||||
|
||||
struct target_msqid64_ds {
|
||||
struct target_ipc64_perm msg_perm;
|
||||
unsigned int msg_stime; /* last msgsnd time */
|
||||
unsigned int msg_rtime; /* last msgrcv time */
|
||||
unsigned int msg_ctime; /* last change time */
|
||||
abi_ulong msg_cbytes; /* current number of bytes on queue */
|
||||
abi_ulong msg_qnum; /* number of messages in queue */
|
||||
abi_ulong msg_qbytes; /* max number of bytes on queue */
|
||||
unsigned int msg_lspid; /* pid of last msgsnd */
|
||||
unsigned int msg_lrpid; /* last receive pid */
|
||||
abi_ulong __unused4;
|
||||
abi_ulong __unused5;
|
||||
};
|
||||
|
||||
/* 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_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 TARGET_FREEBSD_AMD64_GET_FSBASE 128
|
||||
#define TARGET_FREEBSD_AMD64_SET_FSBASE 129
|
||||
#define TARGET_FREEBSD_AMD64_GET_GSBASE 130
|
||||
#define TARGET_FREEBSD_AMD64_SET_GSBASE 131
|
||||
|
||||
|
||||
#define UNAME_MACHINE "x86_64"
|
||||
|
||||
#define TARGET_ARCH_SET_GS 0x1001
|
||||
#define TARGET_ARCH_SET_FS 0x1002
|
||||
#define TARGET_ARCH_GET_FS 0x1003
|
||||
#define TARGET_ARCH_GET_GS 0x1004
|
||||
@@ -1,19 +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 */
|
||||
@@ -5,13 +5,13 @@
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#ifdef CONFIG_MACHINE_BSWAP_H
|
||||
#ifdef HAVE_MACHINE_BSWAP_H
|
||||
#include <sys/endian.h>
|
||||
#include <sys/types.h>
|
||||
#include <machine/bswap.h>
|
||||
#else
|
||||
|
||||
#ifdef CONFIG_BYTESWAP_H
|
||||
#ifdef HAVE_BYTESWAP_H
|
||||
#include <byteswap.h>
|
||||
#else
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
(uint64_t)(((uint64_t)(__x) & (uint64_t)0xff00000000000000ULL) >> 56) )); \
|
||||
})
|
||||
|
||||
#endif /* !CONFIG_BYTESWAP_H */
|
||||
#endif /* !HAVE_BYTESWAP_H */
|
||||
|
||||
static inline uint16_t bswap16(uint16_t x)
|
||||
{
|
||||
@@ -64,7 +64,7 @@ static inline uint64_t bswap64(uint64_t x)
|
||||
return bswap_64(x);
|
||||
}
|
||||
|
||||
#endif /* ! CONFIG_MACHINE_BSWAP_H */
|
||||
#endif /* ! HAVE_MACHINE_BSWAP_H */
|
||||
|
||||
static inline void bswap16s(uint16_t *s)
|
||||
{
|
||||
@@ -81,7 +81,7 @@ static inline void bswap64s(uint64_t *s)
|
||||
*s = bswap64(*s);
|
||||
}
|
||||
|
||||
#if defined(HOST_WORDS_BIGENDIAN)
|
||||
#if defined(WORDS_BIGENDIAN)
|
||||
#define be_bswap(v, size) (v)
|
||||
#define le_bswap(v, size) bswap ## size(v)
|
||||
#define be_bswaps(v, size)
|
||||
@@ -151,7 +151,7 @@ static inline void cpu_to_le16wu(uint16_t *p, uint16_t v)
|
||||
{
|
||||
uint8_t *p1 = (uint8_t *)p;
|
||||
|
||||
p1[0] = v & 0xff;
|
||||
p1[0] = v;
|
||||
p1[1] = v >> 8;
|
||||
}
|
||||
|
||||
@@ -159,7 +159,7 @@ static inline void cpu_to_le32wu(uint32_t *p, uint32_t v)
|
||||
{
|
||||
uint8_t *p1 = (uint8_t *)p;
|
||||
|
||||
p1[0] = v & 0xff;
|
||||
p1[0] = v;
|
||||
p1[1] = v >> 8;
|
||||
p1[2] = v >> 16;
|
||||
p1[3] = v >> 24;
|
||||
@@ -188,7 +188,7 @@ static inline void cpu_to_be16wu(uint16_t *p, uint16_t v)
|
||||
uint8_t *p1 = (uint8_t *)p;
|
||||
|
||||
p1[0] = v >> 8;
|
||||
p1[1] = v & 0xff;
|
||||
p1[1] = v;
|
||||
}
|
||||
|
||||
static inline void cpu_to_be32wu(uint32_t *p, uint32_t v)
|
||||
@@ -198,12 +198,12 @@ static inline void cpu_to_be32wu(uint32_t *p, uint32_t v)
|
||||
p1[0] = v >> 24;
|
||||
p1[1] = v >> 16;
|
||||
p1[2] = v >> 8;
|
||||
p1[3] = v & 0xff;
|
||||
p1[3] = v;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HOST_WORDS_BIGENDIAN
|
||||
#ifdef WORDS_BIGENDIAN
|
||||
#define cpu_to_32wu cpu_to_be32wu
|
||||
#else
|
||||
#define cpu_to_32wu cpu_to_le32wu
|
||||
|
||||
@@ -14,14 +14,14 @@
|
||||
* 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/>.
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "qemu-common.h"
|
||||
#include "qemu-char.h"
|
||||
#include "sysemu.h"
|
||||
#include "net.h"
|
||||
#include "bt-host.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
# include <errno.h>
|
||||
@@ -52,7 +52,7 @@ static void bt_host_send(struct HCIInfo *hci,
|
||||
struct iovec iv[2];
|
||||
int ret;
|
||||
|
||||
iv[0].iov_base = (void *)&pkt;
|
||||
iv[0].iov_base = &pkt;
|
||||
iv[0].iov_len = 1;
|
||||
iv[1].iov_base = (void *) data;
|
||||
iv[1].iov_len = len;
|
||||
@@ -171,7 +171,7 @@ struct HCIInfo *bt_host_hci(const char *id)
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "qemu: Can't open `%s': %s (%i)\n",
|
||||
id, strerror(errno), errno);
|
||||
return NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
# ifdef CONFIG_BLUEZ
|
||||
@@ -192,7 +192,7 @@ struct HCIInfo *bt_host_hci(const char *id)
|
||||
s->hci.acl_send = bt_host_acl;
|
||||
s->hci.bdaddr_set = bt_host_bdaddr_set;
|
||||
|
||||
qemu_set_fd_handler2(s->fd, bt_host_read_poll, bt_host_read, NULL, s);
|
||||
qemu_set_fd_handler2(s->fd, bt_host_read_poll, bt_host_read, 0, s);
|
||||
|
||||
return &s->hci;
|
||||
}
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
#ifndef BT_HOST_H
|
||||
#define BT_HOST_H
|
||||
|
||||
struct HCIInfo;
|
||||
|
||||
/* bt-host.c */
|
||||
struct HCIInfo *bt_host_hci(const char *id);
|
||||
|
||||
#endif
|
||||
@@ -14,7 +14,8 @@
|
||||
* 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/>.
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "qemu-common.h"
|
||||
@@ -164,5 +165,5 @@ void bt_vhci_init(struct HCIInfo *info)
|
||||
s->info->evt_recv = vhci_out_hci_packet_event;
|
||||
s->info->acl_recv = vhci_out_hci_packet_acl;
|
||||
|
||||
qemu_set_fd_handler(s->fd, vhci_read, NULL, s);
|
||||
qemu_set_fd_handler(s->fd, vhci_read, 0, s);
|
||||
}
|
||||
+9
-31
@@ -52,7 +52,7 @@ static void buffered_append(QEMUFileBuffered *s,
|
||||
if (size > (s->buffer_capacity - s->buffer_size)) {
|
||||
void *tmp;
|
||||
|
||||
dprintf("increasing buffer capacity from %zu by %zu\n",
|
||||
dprintf("increasing buffer capacity from %ld by %ld\n",
|
||||
s->buffer_capacity, size + 1024);
|
||||
|
||||
s->buffer_capacity += size + 1024;
|
||||
@@ -79,7 +79,7 @@ static void buffered_flush(QEMUFileBuffered *s)
|
||||
return;
|
||||
}
|
||||
|
||||
dprintf("flushing %zu byte(s) of data\n", s->buffer_size);
|
||||
dprintf("flushing %ld byte(s) of data\n", s->buffer_size);
|
||||
|
||||
while (offset < s->buffer_size) {
|
||||
ssize_t ret;
|
||||
@@ -93,16 +93,16 @@ static void buffered_flush(QEMUFileBuffered *s)
|
||||
}
|
||||
|
||||
if (ret <= 0) {
|
||||
dprintf("error flushing data, %zd\n", ret);
|
||||
dprintf("error flushing data, %ld\n", ret);
|
||||
s->has_error = 1;
|
||||
break;
|
||||
} else {
|
||||
dprintf("flushed %zd byte(s)\n", ret);
|
||||
dprintf("flushed %ld byte(s)\n", ret);
|
||||
offset += ret;
|
||||
}
|
||||
}
|
||||
|
||||
dprintf("flushed %zu of %zu byte(s)\n", offset, s->buffer_size);
|
||||
dprintf("flushed %ld of %ld byte(s)\n", offset, s->buffer_size);
|
||||
memmove(s->buffer, s->buffer + offset, s->buffer_size - offset);
|
||||
s->buffer_size -= offset;
|
||||
}
|
||||
@@ -113,7 +113,7 @@ static int buffered_put_buffer(void *opaque, const uint8_t *buf, int64_t pos, in
|
||||
int offset = 0;
|
||||
ssize_t ret;
|
||||
|
||||
dprintf("putting %d bytes at %" PRId64 "\n", size, pos);
|
||||
dprintf("putting %ld bytes at %Ld\n", size, pos);
|
||||
|
||||
if (s->has_error) {
|
||||
dprintf("flush when error, bailing\n");
|
||||
@@ -145,13 +145,13 @@ static int buffered_put_buffer(void *opaque, const uint8_t *buf, int64_t pos, in
|
||||
break;
|
||||
}
|
||||
|
||||
dprintf("put %zd byte(s)\n", ret);
|
||||
dprintf("put %ld byte(s)\n", ret);
|
||||
offset += ret;
|
||||
s->bytes_xfer += ret;
|
||||
}
|
||||
|
||||
if (offset >= 0) {
|
||||
dprintf("buffering %d bytes\n", size - offset);
|
||||
dprintf("buffering %ld bytes\n", size - offset);
|
||||
buffered_append(s, buf + offset, size - offset);
|
||||
offset = size;
|
||||
}
|
||||
@@ -198,26 +198,6 @@ static int buffered_rate_limit(void *opaque)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static size_t buffered_set_rate_limit(void *opaque, size_t new_rate)
|
||||
{
|
||||
QEMUFileBuffered *s = opaque;
|
||||
|
||||
if (s->has_error)
|
||||
goto out;
|
||||
|
||||
s->xfer_limit = new_rate / 10;
|
||||
|
||||
out:
|
||||
return s->xfer_limit;
|
||||
}
|
||||
|
||||
static size_t buffered_get_rate_limit(void *opaque)
|
||||
{
|
||||
QEMUFileBuffered *s = opaque;
|
||||
|
||||
return s->xfer_limit;
|
||||
}
|
||||
|
||||
static void buffered_rate_tick(void *opaque)
|
||||
{
|
||||
QEMUFileBuffered *s = opaque;
|
||||
@@ -257,9 +237,7 @@ QEMUFile *qemu_fopen_ops_buffered(void *opaque,
|
||||
s->close = close;
|
||||
|
||||
s->file = qemu_fopen_ops(s, buffered_put_buffer, NULL,
|
||||
buffered_close, buffered_rate_limit,
|
||||
buffered_set_rate_limit,
|
||||
buffered_get_rate_limit);
|
||||
buffered_close, buffered_rate_limit);
|
||||
|
||||
s->timer = qemu_new_timer(rt_clock, buffered_rate_tick, s);
|
||||
|
||||
|
||||
-367
@@ -1,367 +0,0 @@
|
||||
/*
|
||||
* QDict unit-tests.
|
||||
*
|
||||
* Copyright (C) 2009 Red Hat Inc.
|
||||
*
|
||||
* Authors:
|
||||
* Luiz Capitulino <lcapitulino@redhat.com>
|
||||
*/
|
||||
#include <check.h>
|
||||
|
||||
#include "qint.h"
|
||||
#include "qdict.h"
|
||||
#include "qstring.h"
|
||||
#include "qemu-common.h"
|
||||
|
||||
/*
|
||||
* Public Interface test-cases
|
||||
*
|
||||
* (with some violations to access 'private' data)
|
||||
*/
|
||||
|
||||
START_TEST(qdict_new_test)
|
||||
{
|
||||
QDict *qdict;
|
||||
|
||||
qdict = qdict_new();
|
||||
fail_unless(qdict != NULL);
|
||||
fail_unless(qdict_size(qdict) == 0);
|
||||
fail_unless(qdict->base.refcnt == 1);
|
||||
fail_unless(qobject_type(QOBJECT(qdict)) == QTYPE_QDICT);
|
||||
|
||||
// destroy doesn't exit yet
|
||||
free(qdict);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(qdict_put_obj_test)
|
||||
{
|
||||
QInt *qi;
|
||||
QDict *qdict;
|
||||
QDictEntry *ent;
|
||||
const int num = 42;
|
||||
|
||||
qdict = qdict_new();
|
||||
|
||||
// key "" will have tdb hash 12345
|
||||
qdict_put_obj(qdict, "", QOBJECT(qint_from_int(num)));
|
||||
|
||||
fail_unless(qdict_size(qdict) == 1);
|
||||
ent = QLIST_FIRST(&qdict->table[12345 % QDICT_HASH_SIZE]);
|
||||
qi = qobject_to_qint(ent->value);
|
||||
fail_unless(qint_get_int(qi) == num);
|
||||
|
||||
// destroy doesn't exit yet
|
||||
QDECREF(qi);
|
||||
qemu_free(ent->key);
|
||||
qemu_free(ent);
|
||||
qemu_free(qdict);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(qdict_destroy_simple_test)
|
||||
{
|
||||
QDict *qdict;
|
||||
|
||||
qdict = qdict_new();
|
||||
qdict_put_obj(qdict, "num", QOBJECT(qint_from_int(0)));
|
||||
qdict_put_obj(qdict, "str", QOBJECT(qstring_from_str("foo")));
|
||||
|
||||
QDECREF(qdict);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
static QDict *tests_dict = NULL;
|
||||
|
||||
static void qdict_setup(void)
|
||||
{
|
||||
tests_dict = qdict_new();
|
||||
fail_unless(tests_dict != NULL);
|
||||
}
|
||||
|
||||
static void qdict_teardown(void)
|
||||
{
|
||||
QDECREF(tests_dict);
|
||||
tests_dict = NULL;
|
||||
}
|
||||
|
||||
START_TEST(qdict_get_test)
|
||||
{
|
||||
QInt *qi;
|
||||
QObject *obj;
|
||||
const int value = -42;
|
||||
const char *key = "test";
|
||||
|
||||
qdict_put(tests_dict, key, qint_from_int(value));
|
||||
|
||||
obj = qdict_get(tests_dict, key);
|
||||
fail_unless(obj != NULL);
|
||||
|
||||
qi = qobject_to_qint(obj);
|
||||
fail_unless(qint_get_int(qi) == value);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(qdict_get_int_test)
|
||||
{
|
||||
int ret;
|
||||
const int value = 100;
|
||||
const char *key = "int";
|
||||
|
||||
qdict_put(tests_dict, key, qint_from_int(value));
|
||||
|
||||
ret = qdict_get_int(tests_dict, key);
|
||||
fail_unless(ret == value);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(qdict_get_try_int_test)
|
||||
{
|
||||
int ret;
|
||||
const int value = 100;
|
||||
const char *key = "int";
|
||||
|
||||
qdict_put(tests_dict, key, qint_from_int(value));
|
||||
|
||||
ret = qdict_get_try_int(tests_dict, key, 0);
|
||||
fail_unless(ret == value);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(qdict_get_str_test)
|
||||
{
|
||||
const char *p;
|
||||
const char *key = "key";
|
||||
const char *str = "string";
|
||||
|
||||
qdict_put(tests_dict, key, qstring_from_str(str));
|
||||
|
||||
p = qdict_get_str(tests_dict, key);
|
||||
fail_unless(p != NULL);
|
||||
fail_unless(strcmp(p, str) == 0);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(qdict_get_try_str_test)
|
||||
{
|
||||
const char *p;
|
||||
const char *key = "key";
|
||||
const char *str = "string";
|
||||
|
||||
qdict_put(tests_dict, key, qstring_from_str(str));
|
||||
|
||||
p = qdict_get_try_str(tests_dict, key);
|
||||
fail_unless(p != NULL);
|
||||
fail_unless(strcmp(p, str) == 0);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(qdict_haskey_not_test)
|
||||
{
|
||||
fail_unless(qdict_haskey(tests_dict, "test") == 0);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(qdict_haskey_test)
|
||||
{
|
||||
const char *key = "test";
|
||||
|
||||
qdict_put(tests_dict, key, qint_from_int(0));
|
||||
fail_unless(qdict_haskey(tests_dict, key) == 1);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(qdict_del_test)
|
||||
{
|
||||
const char *key = "key test";
|
||||
|
||||
qdict_put(tests_dict, key, qstring_from_str("foo"));
|
||||
fail_unless(qdict_size(tests_dict) == 1);
|
||||
|
||||
qdict_del(tests_dict, key);
|
||||
|
||||
fail_unless(qdict_size(tests_dict) == 0);
|
||||
fail_unless(qdict_haskey(tests_dict, key) == 0);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(qobject_to_qdict_test)
|
||||
{
|
||||
fail_unless(qobject_to_qdict(QOBJECT(tests_dict)) == tests_dict);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
/*
|
||||
* Errors test-cases
|
||||
*/
|
||||
|
||||
START_TEST(qdict_put_exists_test)
|
||||
{
|
||||
int value;
|
||||
const char *key = "exists";
|
||||
|
||||
qdict_put(tests_dict, key, qint_from_int(1));
|
||||
qdict_put(tests_dict, key, qint_from_int(2));
|
||||
|
||||
value = qdict_get_int(tests_dict, key);
|
||||
fail_unless(value == 2);
|
||||
|
||||
fail_unless(qdict_size(tests_dict) == 1);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(qdict_get_not_exists_test)
|
||||
{
|
||||
fail_unless(qdict_get(tests_dict, "foo") == NULL);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
/*
|
||||
* Stress test-case
|
||||
*
|
||||
* This is a lot big for a unit-test, but there is no other place
|
||||
* to have it.
|
||||
*/
|
||||
|
||||
static void remove_dots(char *string)
|
||||
{
|
||||
char *p = strchr(string, ':');
|
||||
if (p)
|
||||
*p = '\0';
|
||||
}
|
||||
|
||||
static QString *read_line(FILE *file, char *key)
|
||||
{
|
||||
char value[128];
|
||||
|
||||
if (fscanf(file, "%s%s", key, value) == EOF)
|
||||
return NULL;
|
||||
remove_dots(key);
|
||||
return qstring_from_str(value);
|
||||
}
|
||||
|
||||
#define reset_file(file) fseek(file, 0L, SEEK_SET)
|
||||
|
||||
START_TEST(qdict_stress_test)
|
||||
{
|
||||
size_t lines;
|
||||
char key[128];
|
||||
FILE *test_file;
|
||||
QDict *qdict;
|
||||
QString *value;
|
||||
const char *test_file_path = "qdict-test-data.txt";
|
||||
|
||||
test_file = fopen(test_file_path, "r");
|
||||
fail_unless(test_file != NULL);
|
||||
|
||||
// Create the dict
|
||||
qdict = qdict_new();
|
||||
fail_unless(qdict != NULL);
|
||||
|
||||
// Add everything from the test file
|
||||
for (lines = 0;; lines++) {
|
||||
value = read_line(test_file, key);
|
||||
if (!value)
|
||||
break;
|
||||
|
||||
qdict_put(qdict, key, value);
|
||||
}
|
||||
fail_unless(qdict_size(qdict) == lines);
|
||||
|
||||
// Check if everything is really in there
|
||||
reset_file(test_file);
|
||||
for (;;) {
|
||||
const char *str1, *str2;
|
||||
|
||||
value = read_line(test_file, key);
|
||||
if (!value)
|
||||
break;
|
||||
|
||||
str1 = qstring_get_str(value);
|
||||
|
||||
str2 = qdict_get_str(qdict, key);
|
||||
fail_unless(str2 != NULL);
|
||||
|
||||
fail_unless(strcmp(str1, str2) == 0);
|
||||
|
||||
QDECREF(value);
|
||||
}
|
||||
|
||||
// Delete everything
|
||||
reset_file(test_file);
|
||||
for (;;) {
|
||||
value = read_line(test_file, key);
|
||||
if (!value)
|
||||
break;
|
||||
|
||||
qdict_del(qdict, key);
|
||||
QDECREF(value);
|
||||
|
||||
fail_unless(qdict_haskey(qdict, key) == 0);
|
||||
}
|
||||
fclose(test_file);
|
||||
|
||||
fail_unless(qdict_size(qdict) == 0);
|
||||
QDECREF(qdict);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
static Suite *qdict_suite(void)
|
||||
{
|
||||
Suite *s;
|
||||
TCase *qdict_public_tcase;
|
||||
TCase *qdict_public2_tcase;
|
||||
TCase *qdict_stress_tcase;
|
||||
TCase *qdict_errors_tcase;
|
||||
|
||||
s = suite_create("QDict test-suite");
|
||||
|
||||
qdict_public_tcase = tcase_create("Public Interface");
|
||||
suite_add_tcase(s, qdict_public_tcase);
|
||||
tcase_add_test(qdict_public_tcase, qdict_new_test);
|
||||
tcase_add_test(qdict_public_tcase, qdict_put_obj_test);
|
||||
tcase_add_test(qdict_public_tcase, qdict_destroy_simple_test);
|
||||
|
||||
/* Continue, but now with fixtures */
|
||||
qdict_public2_tcase = tcase_create("Public Interface (2)");
|
||||
suite_add_tcase(s, qdict_public2_tcase);
|
||||
tcase_add_checked_fixture(qdict_public2_tcase, qdict_setup, qdict_teardown);
|
||||
tcase_add_test(qdict_public2_tcase, qdict_get_test);
|
||||
tcase_add_test(qdict_public2_tcase, qdict_get_int_test);
|
||||
tcase_add_test(qdict_public2_tcase, qdict_get_try_int_test);
|
||||
tcase_add_test(qdict_public2_tcase, qdict_get_str_test);
|
||||
tcase_add_test(qdict_public2_tcase, qdict_get_try_str_test);
|
||||
tcase_add_test(qdict_public2_tcase, qdict_haskey_not_test);
|
||||
tcase_add_test(qdict_public2_tcase, qdict_haskey_test);
|
||||
tcase_add_test(qdict_public2_tcase, qdict_del_test);
|
||||
tcase_add_test(qdict_public2_tcase, qobject_to_qdict_test);
|
||||
|
||||
qdict_errors_tcase = tcase_create("Errors");
|
||||
suite_add_tcase(s, qdict_errors_tcase);
|
||||
tcase_add_checked_fixture(qdict_errors_tcase, qdict_setup, qdict_teardown);
|
||||
tcase_add_test(qdict_errors_tcase, qdict_put_exists_test);
|
||||
tcase_add_test(qdict_errors_tcase, qdict_get_not_exists_test);
|
||||
|
||||
/* The Big one */
|
||||
qdict_stress_tcase = tcase_create("Stress Test");
|
||||
suite_add_tcase(s, qdict_stress_tcase);
|
||||
tcase_add_test(qdict_stress_tcase, qdict_stress_test);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int nf;
|
||||
Suite *s;
|
||||
SRunner *sr;
|
||||
|
||||
s = qdict_suite();
|
||||
sr = srunner_create(s);
|
||||
|
||||
srunner_run_all(sr, CK_NORMAL);
|
||||
nf = srunner_ntests_failed(sr);
|
||||
srunner_free(sr);
|
||||
|
||||
return (nf == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
/*
|
||||
* QFloat unit-tests.
|
||||
*
|
||||
* Copyright (C) 2009 Red Hat Inc.
|
||||
*
|
||||
* Authors:
|
||||
* Luiz Capitulino <lcapitulino@redhat.com>
|
||||
*
|
||||
* Copyright IBM, Corp. 2009
|
||||
*
|
||||
* Authors:
|
||||
* Anthony Liguori <aliguori@us.ibm.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
|
||||
* See the COPYING.LIB file in the top-level directory.
|
||||
*
|
||||
*/
|
||||
#include <check.h>
|
||||
|
||||
#include "qfloat.h"
|
||||
#include "qemu-common.h"
|
||||
|
||||
/*
|
||||
* Public Interface test-cases
|
||||
*
|
||||
* (with some violations to access 'private' data)
|
||||
*/
|
||||
|
||||
START_TEST(qfloat_from_double_test)
|
||||
{
|
||||
QFloat *qf;
|
||||
const double value = -42.23423;
|
||||
|
||||
qf = qfloat_from_double(value);
|
||||
fail_unless(qf != NULL);
|
||||
fail_unless(qf->value == value);
|
||||
fail_unless(qf->base.refcnt == 1);
|
||||
fail_unless(qobject_type(QOBJECT(qf)) == QTYPE_QFLOAT);
|
||||
|
||||
// destroy doesn't exit yet
|
||||
qemu_free(qf);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(qfloat_destroy_test)
|
||||
{
|
||||
QFloat *qf = qfloat_from_double(0.0);
|
||||
QDECREF(qf);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
static Suite *qfloat_suite(void)
|
||||
{
|
||||
Suite *s;
|
||||
TCase *qfloat_public_tcase;
|
||||
|
||||
s = suite_create("QFloat test-suite");
|
||||
|
||||
qfloat_public_tcase = tcase_create("Public Interface");
|
||||
suite_add_tcase(s, qfloat_public_tcase);
|
||||
tcase_add_test(qfloat_public_tcase, qfloat_from_double_test);
|
||||
tcase_add_test(qfloat_public_tcase, qfloat_destroy_test);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int nf;
|
||||
Suite *s;
|
||||
SRunner *sr;
|
||||
|
||||
s = qfloat_suite();
|
||||
sr = srunner_create(s);
|
||||
|
||||
srunner_run_all(sr, CK_NORMAL);
|
||||
nf = srunner_ntests_failed(sr);
|
||||
srunner_free(sr);
|
||||
|
||||
return (nf == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
-110
@@ -1,110 +0,0 @@
|
||||
/*
|
||||
* QInt unit-tests.
|
||||
*
|
||||
* Copyright (C) 2009 Red Hat Inc.
|
||||
*
|
||||
* Authors:
|
||||
* Luiz Capitulino <lcapitulino@redhat.com>
|
||||
*/
|
||||
#include <check.h>
|
||||
|
||||
#include "qint.h"
|
||||
#include "qemu-common.h"
|
||||
|
||||
/*
|
||||
* Public Interface test-cases
|
||||
*
|
||||
* (with some violations to access 'private' data)
|
||||
*/
|
||||
|
||||
START_TEST(qint_from_int_test)
|
||||
{
|
||||
QInt *qi;
|
||||
const int value = -42;
|
||||
|
||||
qi = qint_from_int(value);
|
||||
fail_unless(qi != NULL);
|
||||
fail_unless(qi->value == value);
|
||||
fail_unless(qi->base.refcnt == 1);
|
||||
fail_unless(qobject_type(QOBJECT(qi)) == QTYPE_QINT);
|
||||
|
||||
// destroy doesn't exit yet
|
||||
qemu_free(qi);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(qint_destroy_test)
|
||||
{
|
||||
QInt *qi = qint_from_int(0);
|
||||
QDECREF(qi);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(qint_from_int64_test)
|
||||
{
|
||||
QInt *qi;
|
||||
const int64_t value = 0x1234567890abcdefLL;
|
||||
|
||||
qi = qint_from_int(value);
|
||||
fail_unless((int64_t) qi->value == value);
|
||||
|
||||
QDECREF(qi);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(qint_get_int_test)
|
||||
{
|
||||
QInt *qi;
|
||||
const int value = 123456;
|
||||
|
||||
qi = qint_from_int(value);
|
||||
fail_unless(qint_get_int(qi) == value);
|
||||
|
||||
QDECREF(qi);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(qobject_to_qint_test)
|
||||
{
|
||||
QInt *qi;
|
||||
|
||||
qi = qint_from_int(0);
|
||||
fail_unless(qobject_to_qint(QOBJECT(qi)) == qi);
|
||||
|
||||
QDECREF(qi);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
static Suite *qint_suite(void)
|
||||
{
|
||||
Suite *s;
|
||||
TCase *qint_public_tcase;
|
||||
|
||||
s = suite_create("QInt test-suite");
|
||||
|
||||
qint_public_tcase = tcase_create("Public Interface");
|
||||
suite_add_tcase(s, qint_public_tcase);
|
||||
tcase_add_test(qint_public_tcase, qint_from_int_test);
|
||||
tcase_add_test(qint_public_tcase, qint_destroy_test);
|
||||
tcase_add_test(qint_public_tcase, qint_from_int64_test);
|
||||
tcase_add_test(qint_public_tcase, qint_get_int_test);
|
||||
tcase_add_test(qint_public_tcase, qobject_to_qint_test);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int nf;
|
||||
Suite *s;
|
||||
SRunner *sr;
|
||||
|
||||
s = qint_suite();
|
||||
sr = srunner_create(s);
|
||||
|
||||
srunner_run_all(sr, CK_NORMAL);
|
||||
nf = srunner_ntests_failed(sr);
|
||||
srunner_free(sr);
|
||||
|
||||
return (nf == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
-687
@@ -1,687 +0,0 @@
|
||||
/*
|
||||
* Copyright IBM, Corp. 2009
|
||||
*
|
||||
* Authors:
|
||||
* Anthony Liguori <aliguori@us.ibm.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
|
||||
* See the COPYING.LIB file in the top-level directory.
|
||||
*
|
||||
*/
|
||||
#include <check.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "qstring.h"
|
||||
#include "qint.h"
|
||||
#include "qdict.h"
|
||||
#include "qlist.h"
|
||||
#include "qfloat.h"
|
||||
#include "qbool.h"
|
||||
#include "qjson.h"
|
||||
|
||||
#include "qemu-common.h"
|
||||
|
||||
START_TEST(escaped_string)
|
||||
{
|
||||
int i;
|
||||
struct {
|
||||
const char *encoded;
|
||||
const char *decoded;
|
||||
int skip;
|
||||
} test_cases[] = {
|
||||
{ "\"\\\"\"", "\"" },
|
||||
{ "\"hello world \\\"embedded string\\\"\"",
|
||||
"hello world \"embedded string\"" },
|
||||
{ "\"hello world\\nwith new line\"", "hello world\nwith new line" },
|
||||
{ "\"single byte utf-8 \\u0020\"", "single byte utf-8 ", .skip = 1 },
|
||||
{ "\"double byte utf-8 \\u00A2\"", "double byte utf-8 \xc2\xa2" },
|
||||
{ "\"triple byte utf-8 \\u20AC\"", "triple byte utf-8 \xe2\x82\xac" },
|
||||
{}
|
||||
};
|
||||
|
||||
for (i = 0; test_cases[i].encoded; i++) {
|
||||
QObject *obj;
|
||||
QString *str;
|
||||
|
||||
obj = qobject_from_json(test_cases[i].encoded);
|
||||
|
||||
fail_unless(obj != NULL);
|
||||
fail_unless(qobject_type(obj) == QTYPE_QSTRING);
|
||||
|
||||
str = qobject_to_qstring(obj);
|
||||
fail_unless(strcmp(qstring_get_str(str), test_cases[i].decoded) == 0);
|
||||
|
||||
if (test_cases[i].skip == 0) {
|
||||
str = qobject_to_json(obj);
|
||||
fail_unless(strcmp(qstring_get_str(str), test_cases[i].encoded) == 0);
|
||||
|
||||
qobject_decref(obj);
|
||||
}
|
||||
|
||||
QDECREF(str);
|
||||
}
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(simple_string)
|
||||
{
|
||||
int i;
|
||||
struct {
|
||||
const char *encoded;
|
||||
const char *decoded;
|
||||
} test_cases[] = {
|
||||
{ "\"hello world\"", "hello world" },
|
||||
{ "\"the quick brown fox jumped over the fence\"",
|
||||
"the quick brown fox jumped over the fence" },
|
||||
{}
|
||||
};
|
||||
|
||||
for (i = 0; test_cases[i].encoded; i++) {
|
||||
QObject *obj;
|
||||
QString *str;
|
||||
|
||||
obj = qobject_from_json(test_cases[i].encoded);
|
||||
|
||||
fail_unless(obj != NULL);
|
||||
fail_unless(qobject_type(obj) == QTYPE_QSTRING);
|
||||
|
||||
str = qobject_to_qstring(obj);
|
||||
fail_unless(strcmp(qstring_get_str(str), test_cases[i].decoded) == 0);
|
||||
|
||||
str = qobject_to_json(obj);
|
||||
fail_unless(strcmp(qstring_get_str(str), test_cases[i].encoded) == 0);
|
||||
|
||||
qobject_decref(obj);
|
||||
|
||||
QDECREF(str);
|
||||
}
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(single_quote_string)
|
||||
{
|
||||
int i;
|
||||
struct {
|
||||
const char *encoded;
|
||||
const char *decoded;
|
||||
} test_cases[] = {
|
||||
{ "'hello world'", "hello world" },
|
||||
{ "'the quick brown fox \\' jumped over the fence'",
|
||||
"the quick brown fox ' jumped over the fence" },
|
||||
{}
|
||||
};
|
||||
|
||||
for (i = 0; test_cases[i].encoded; i++) {
|
||||
QObject *obj;
|
||||
QString *str;
|
||||
|
||||
obj = qobject_from_json(test_cases[i].encoded);
|
||||
|
||||
fail_unless(obj != NULL);
|
||||
fail_unless(qobject_type(obj) == QTYPE_QSTRING);
|
||||
|
||||
str = qobject_to_qstring(obj);
|
||||
fail_unless(strcmp(qstring_get_str(str), test_cases[i].decoded) == 0);
|
||||
|
||||
QDECREF(str);
|
||||
}
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(vararg_string)
|
||||
{
|
||||
int i;
|
||||
struct {
|
||||
const char *decoded;
|
||||
} test_cases[] = {
|
||||
{ "hello world" },
|
||||
{ "the quick brown fox jumped over the fence" },
|
||||
{}
|
||||
};
|
||||
|
||||
for (i = 0; test_cases[i].decoded; i++) {
|
||||
QObject *obj;
|
||||
QString *str;
|
||||
|
||||
obj = qobject_from_jsonf("%s", test_cases[i].decoded);
|
||||
|
||||
fail_unless(obj != NULL);
|
||||
fail_unless(qobject_type(obj) == QTYPE_QSTRING);
|
||||
|
||||
str = qobject_to_qstring(obj);
|
||||
fail_unless(strcmp(qstring_get_str(str), test_cases[i].decoded) == 0);
|
||||
|
||||
QDECREF(str);
|
||||
}
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(simple_number)
|
||||
{
|
||||
int i;
|
||||
struct {
|
||||
const char *encoded;
|
||||
int64_t decoded;
|
||||
int skip;
|
||||
} test_cases[] = {
|
||||
{ "0", 0 },
|
||||
{ "1234", 1234 },
|
||||
{ "1", 1 },
|
||||
{ "-32", -32 },
|
||||
{ "-0", 0, .skip = 1 },
|
||||
{ },
|
||||
};
|
||||
|
||||
for (i = 0; test_cases[i].encoded; i++) {
|
||||
QObject *obj;
|
||||
QInt *qint;
|
||||
|
||||
obj = qobject_from_json(test_cases[i].encoded);
|
||||
fail_unless(obj != NULL);
|
||||
fail_unless(qobject_type(obj) == QTYPE_QINT);
|
||||
|
||||
qint = qobject_to_qint(obj);
|
||||
fail_unless(qint_get_int(qint) == test_cases[i].decoded);
|
||||
if (test_cases[i].skip == 0) {
|
||||
QString *str;
|
||||
|
||||
str = qobject_to_json(obj);
|
||||
fail_unless(strcmp(qstring_get_str(str), test_cases[i].encoded) == 0);
|
||||
QDECREF(str);
|
||||
}
|
||||
|
||||
QDECREF(qint);
|
||||
}
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(float_number)
|
||||
{
|
||||
int i;
|
||||
struct {
|
||||
const char *encoded;
|
||||
double decoded;
|
||||
int skip;
|
||||
} test_cases[] = {
|
||||
{ "32.43", 32.43 },
|
||||
{ "0.222", 0.222 },
|
||||
{ "-32.12313", -32.12313 },
|
||||
{ "-32.20e-10", -32.20e-10, .skip = 1 },
|
||||
{ },
|
||||
};
|
||||
|
||||
for (i = 0; test_cases[i].encoded; i++) {
|
||||
QObject *obj;
|
||||
QFloat *qfloat;
|
||||
|
||||
obj = qobject_from_json(test_cases[i].encoded);
|
||||
fail_unless(obj != NULL);
|
||||
fail_unless(qobject_type(obj) == QTYPE_QFLOAT);
|
||||
|
||||
qfloat = qobject_to_qfloat(obj);
|
||||
fail_unless(qfloat_get_double(qfloat) == test_cases[i].decoded);
|
||||
|
||||
if (test_cases[i].skip == 0) {
|
||||
QString *str;
|
||||
|
||||
str = qobject_to_json(obj);
|
||||
fail_unless(strcmp(qstring_get_str(str), test_cases[i].encoded) == 0);
|
||||
QDECREF(str);
|
||||
}
|
||||
|
||||
QDECREF(qfloat);
|
||||
}
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(vararg_number)
|
||||
{
|
||||
QObject *obj;
|
||||
QInt *qint;
|
||||
QFloat *qfloat;
|
||||
int value = 0x2342;
|
||||
int64_t value64 = 0x2342342343LL;
|
||||
double valuef = 2.323423423;
|
||||
|
||||
obj = qobject_from_jsonf("%d", value);
|
||||
fail_unless(obj != NULL);
|
||||
fail_unless(qobject_type(obj) == QTYPE_QINT);
|
||||
|
||||
qint = qobject_to_qint(obj);
|
||||
fail_unless(qint_get_int(qint) == value);
|
||||
|
||||
QDECREF(qint);
|
||||
|
||||
obj = qobject_from_jsonf("%" PRId64, value64);
|
||||
fail_unless(obj != NULL);
|
||||
fail_unless(qobject_type(obj) == QTYPE_QINT);
|
||||
|
||||
qint = qobject_to_qint(obj);
|
||||
fail_unless(qint_get_int(qint) == value64);
|
||||
|
||||
QDECREF(qint);
|
||||
|
||||
obj = qobject_from_jsonf("%f", valuef);
|
||||
fail_unless(obj != NULL);
|
||||
fail_unless(qobject_type(obj) == QTYPE_QFLOAT);
|
||||
|
||||
qfloat = qobject_to_qfloat(obj);
|
||||
fail_unless(qfloat_get_double(qfloat) == valuef);
|
||||
|
||||
QDECREF(qfloat);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(keyword_literal)
|
||||
{
|
||||
QObject *obj;
|
||||
QBool *qbool;
|
||||
QString *str;
|
||||
|
||||
obj = qobject_from_json("true");
|
||||
fail_unless(obj != NULL);
|
||||
fail_unless(qobject_type(obj) == QTYPE_QBOOL);
|
||||
|
||||
qbool = qobject_to_qbool(obj);
|
||||
fail_unless(qbool_get_int(qbool) != 0);
|
||||
|
||||
str = qobject_to_json(obj);
|
||||
fail_unless(strcmp(qstring_get_str(str), "true") == 0);
|
||||
QDECREF(str);
|
||||
|
||||
QDECREF(qbool);
|
||||
|
||||
obj = qobject_from_json("false");
|
||||
fail_unless(obj != NULL);
|
||||
fail_unless(qobject_type(obj) == QTYPE_QBOOL);
|
||||
|
||||
qbool = qobject_to_qbool(obj);
|
||||
fail_unless(qbool_get_int(qbool) == 0);
|
||||
|
||||
str = qobject_to_json(obj);
|
||||
fail_unless(strcmp(qstring_get_str(str), "false") == 0);
|
||||
QDECREF(str);
|
||||
|
||||
QDECREF(qbool);
|
||||
|
||||
obj = qobject_from_jsonf("%i", false);
|
||||
fail_unless(obj != NULL);
|
||||
fail_unless(qobject_type(obj) == QTYPE_QBOOL);
|
||||
|
||||
qbool = qobject_to_qbool(obj);
|
||||
fail_unless(qbool_get_int(qbool) == 0);
|
||||
|
||||
QDECREF(qbool);
|
||||
|
||||
obj = qobject_from_jsonf("%i", true);
|
||||
fail_unless(obj != NULL);
|
||||
fail_unless(qobject_type(obj) == QTYPE_QBOOL);
|
||||
|
||||
qbool = qobject_to_qbool(obj);
|
||||
fail_unless(qbool_get_int(qbool) != 0);
|
||||
|
||||
QDECREF(qbool);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
typedef struct LiteralQDictEntry LiteralQDictEntry;
|
||||
typedef struct LiteralQObject LiteralQObject;
|
||||
|
||||
struct LiteralQObject
|
||||
{
|
||||
int type;
|
||||
union {
|
||||
int64_t qint;
|
||||
const char *qstr;
|
||||
LiteralQDictEntry *qdict;
|
||||
LiteralQObject *qlist;
|
||||
} value;
|
||||
};
|
||||
|
||||
struct LiteralQDictEntry
|
||||
{
|
||||
const char *key;
|
||||
LiteralQObject value;
|
||||
};
|
||||
|
||||
#define QLIT_QINT(val) (LiteralQObject){.type = QTYPE_QINT, .value.qint = (val)}
|
||||
#define QLIT_QSTR(val) (LiteralQObject){.type = QTYPE_QSTRING, .value.qstr = (val)}
|
||||
#define QLIT_QDICT(val) (LiteralQObject){.type = QTYPE_QDICT, .value.qdict = (val)}
|
||||
#define QLIT_QLIST(val) (LiteralQObject){.type = QTYPE_QLIST, .value.qlist = (val)}
|
||||
|
||||
typedef struct QListCompareHelper
|
||||
{
|
||||
int index;
|
||||
LiteralQObject *objs;
|
||||
int result;
|
||||
} QListCompareHelper;
|
||||
|
||||
static int compare_litqobj_to_qobj(LiteralQObject *lhs, QObject *rhs);
|
||||
|
||||
static void compare_helper(QObject *obj, void *opaque)
|
||||
{
|
||||
QListCompareHelper *helper = opaque;
|
||||
|
||||
if (helper->result == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (helper->objs[helper->index].type == QTYPE_NONE) {
|
||||
helper->result = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
helper->result = compare_litqobj_to_qobj(&helper->objs[helper->index++], obj);
|
||||
}
|
||||
|
||||
static int compare_litqobj_to_qobj(LiteralQObject *lhs, QObject *rhs)
|
||||
{
|
||||
if (lhs->type != qobject_type(rhs)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (lhs->type) {
|
||||
case QTYPE_QINT:
|
||||
return lhs->value.qint == qint_get_int(qobject_to_qint(rhs));
|
||||
case QTYPE_QSTRING:
|
||||
return (strcmp(lhs->value.qstr, qstring_get_str(qobject_to_qstring(rhs))) == 0);
|
||||
case QTYPE_QDICT: {
|
||||
int i;
|
||||
|
||||
for (i = 0; lhs->value.qdict[i].key; i++) {
|
||||
QObject *obj = qdict_get(qobject_to_qdict(rhs), lhs->value.qdict[i].key);
|
||||
|
||||
if (!compare_litqobj_to_qobj(&lhs->value.qdict[i].value, obj)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
case QTYPE_QLIST: {
|
||||
QListCompareHelper helper;
|
||||
|
||||
helper.index = 0;
|
||||
helper.objs = lhs->value.qlist;
|
||||
helper.result = 1;
|
||||
|
||||
qlist_iter(qobject_to_qlist(rhs), compare_helper, &helper);
|
||||
|
||||
return helper.result;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
START_TEST(simple_dict)
|
||||
{
|
||||
int i;
|
||||
struct {
|
||||
const char *encoded;
|
||||
LiteralQObject decoded;
|
||||
} test_cases[] = {
|
||||
{
|
||||
.encoded = "{\"foo\": 42, \"bar\": \"hello world\"}",
|
||||
.decoded = QLIT_QDICT(((LiteralQDictEntry[]){
|
||||
{ "foo", QLIT_QINT(42) },
|
||||
{ "bar", QLIT_QSTR("hello world") },
|
||||
{ }
|
||||
})),
|
||||
}, {
|
||||
.encoded = "{}",
|
||||
.decoded = QLIT_QDICT(((LiteralQDictEntry[]){
|
||||
{ }
|
||||
})),
|
||||
}, {
|
||||
.encoded = "{\"foo\": 43}",
|
||||
.decoded = QLIT_QDICT(((LiteralQDictEntry[]){
|
||||
{ "foo", QLIT_QINT(43) },
|
||||
{ }
|
||||
})),
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
for (i = 0; test_cases[i].encoded; i++) {
|
||||
QObject *obj;
|
||||
QString *str;
|
||||
|
||||
obj = qobject_from_json(test_cases[i].encoded);
|
||||
fail_unless(obj != NULL);
|
||||
fail_unless(qobject_type(obj) == QTYPE_QDICT);
|
||||
|
||||
fail_unless(compare_litqobj_to_qobj(&test_cases[i].decoded, obj) == 1);
|
||||
|
||||
str = qobject_to_json(obj);
|
||||
qobject_decref(obj);
|
||||
|
||||
obj = qobject_from_json(qstring_get_str(str));
|
||||
fail_unless(obj != NULL);
|
||||
fail_unless(qobject_type(obj) == QTYPE_QDICT);
|
||||
|
||||
fail_unless(compare_litqobj_to_qobj(&test_cases[i].decoded, obj) == 1);
|
||||
qobject_decref(obj);
|
||||
QDECREF(str);
|
||||
}
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(simple_list)
|
||||
{
|
||||
int i;
|
||||
struct {
|
||||
const char *encoded;
|
||||
LiteralQObject decoded;
|
||||
} test_cases[] = {
|
||||
{
|
||||
.encoded = "[43,42]",
|
||||
.decoded = QLIT_QLIST(((LiteralQObject[]){
|
||||
QLIT_QINT(43),
|
||||
QLIT_QINT(42),
|
||||
{ }
|
||||
})),
|
||||
},
|
||||
{
|
||||
.encoded = "[43]",
|
||||
.decoded = QLIT_QLIST(((LiteralQObject[]){
|
||||
QLIT_QINT(43),
|
||||
{ }
|
||||
})),
|
||||
},
|
||||
{
|
||||
.encoded = "[]",
|
||||
.decoded = QLIT_QLIST(((LiteralQObject[]){
|
||||
{ }
|
||||
})),
|
||||
},
|
||||
{
|
||||
.encoded = "[{}]",
|
||||
.decoded = QLIT_QLIST(((LiteralQObject[]){
|
||||
QLIT_QDICT(((LiteralQDictEntry[]){
|
||||
{},
|
||||
})),
|
||||
{},
|
||||
})),
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
for (i = 0; test_cases[i].encoded; i++) {
|
||||
QObject *obj;
|
||||
QString *str;
|
||||
|
||||
obj = qobject_from_json(test_cases[i].encoded);
|
||||
fail_unless(obj != NULL);
|
||||
fail_unless(qobject_type(obj) == QTYPE_QLIST);
|
||||
|
||||
fail_unless(compare_litqobj_to_qobj(&test_cases[i].decoded, obj) == 1);
|
||||
|
||||
str = qobject_to_json(obj);
|
||||
qobject_decref(obj);
|
||||
|
||||
obj = qobject_from_json(qstring_get_str(str));
|
||||
fail_unless(obj != NULL);
|
||||
fail_unless(qobject_type(obj) == QTYPE_QLIST);
|
||||
|
||||
fail_unless(compare_litqobj_to_qobj(&test_cases[i].decoded, obj) == 1);
|
||||
qobject_decref(obj);
|
||||
QDECREF(str);
|
||||
}
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(simple_whitespace)
|
||||
{
|
||||
int i;
|
||||
struct {
|
||||
const char *encoded;
|
||||
LiteralQObject decoded;
|
||||
} test_cases[] = {
|
||||
{
|
||||
.encoded = " [ 43 , 42 ]",
|
||||
.decoded = QLIT_QLIST(((LiteralQObject[]){
|
||||
QLIT_QINT(43),
|
||||
QLIT_QINT(42),
|
||||
{ }
|
||||
})),
|
||||
},
|
||||
{
|
||||
.encoded = " [ 43 , { 'h' : 'b' }, [ ], 42 ]",
|
||||
.decoded = QLIT_QLIST(((LiteralQObject[]){
|
||||
QLIT_QINT(43),
|
||||
QLIT_QDICT(((LiteralQDictEntry[]){
|
||||
{ "h", QLIT_QSTR("b") },
|
||||
{ }})),
|
||||
QLIT_QLIST(((LiteralQObject[]){
|
||||
{ }})),
|
||||
QLIT_QINT(42),
|
||||
{ }
|
||||
})),
|
||||
},
|
||||
{
|
||||
.encoded = " [ 43 , { 'h' : 'b' , 'a' : 32 }, [ ], 42 ]",
|
||||
.decoded = QLIT_QLIST(((LiteralQObject[]){
|
||||
QLIT_QINT(43),
|
||||
QLIT_QDICT(((LiteralQDictEntry[]){
|
||||
{ "h", QLIT_QSTR("b") },
|
||||
{ "a", QLIT_QINT(32) },
|
||||
{ }})),
|
||||
QLIT_QLIST(((LiteralQObject[]){
|
||||
{ }})),
|
||||
QLIT_QINT(42),
|
||||
{ }
|
||||
})),
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
for (i = 0; test_cases[i].encoded; i++) {
|
||||
QObject *obj;
|
||||
QString *str;
|
||||
|
||||
obj = qobject_from_json(test_cases[i].encoded);
|
||||
fail_unless(obj != NULL);
|
||||
fail_unless(qobject_type(obj) == QTYPE_QLIST);
|
||||
|
||||
fail_unless(compare_litqobj_to_qobj(&test_cases[i].decoded, obj) == 1);
|
||||
|
||||
str = qobject_to_json(obj);
|
||||
qobject_decref(obj);
|
||||
|
||||
obj = qobject_from_json(qstring_get_str(str));
|
||||
fail_unless(obj != NULL);
|
||||
fail_unless(qobject_type(obj) == QTYPE_QLIST);
|
||||
|
||||
fail_unless(compare_litqobj_to_qobj(&test_cases[i].decoded, obj) == 1);
|
||||
|
||||
qobject_decref(obj);
|
||||
QDECREF(str);
|
||||
}
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(simple_varargs)
|
||||
{
|
||||
QObject *embedded_obj;
|
||||
QObject *obj;
|
||||
LiteralQObject decoded = QLIT_QLIST(((LiteralQObject[]){
|
||||
QLIT_QINT(1),
|
||||
QLIT_QINT(2),
|
||||
QLIT_QLIST(((LiteralQObject[]){
|
||||
QLIT_QINT(32),
|
||||
QLIT_QINT(42),
|
||||
{}})),
|
||||
{}}));
|
||||
|
||||
embedded_obj = qobject_from_json("[32, 42]");
|
||||
fail_unless(embedded_obj != NULL);
|
||||
|
||||
obj = qobject_from_jsonf("[%d, 2, %p]", 1, embedded_obj);
|
||||
fail_unless(obj != NULL);
|
||||
|
||||
fail_unless(compare_litqobj_to_qobj(&decoded, obj) == 1);
|
||||
|
||||
qobject_decref(obj);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
static Suite *qjson_suite(void)
|
||||
{
|
||||
Suite *suite;
|
||||
TCase *string_literals, *number_literals, *keyword_literals;
|
||||
TCase *dicts, *lists, *whitespace, *varargs;
|
||||
|
||||
string_literals = tcase_create("String Literals");
|
||||
tcase_add_test(string_literals, simple_string);
|
||||
tcase_add_test(string_literals, escaped_string);
|
||||
tcase_add_test(string_literals, single_quote_string);
|
||||
tcase_add_test(string_literals, vararg_string);
|
||||
|
||||
number_literals = tcase_create("Number Literals");
|
||||
tcase_add_test(number_literals, simple_number);
|
||||
tcase_add_test(number_literals, float_number);
|
||||
tcase_add_test(number_literals, vararg_number);
|
||||
|
||||
keyword_literals = tcase_create("Keywords");
|
||||
tcase_add_test(keyword_literals, keyword_literal);
|
||||
dicts = tcase_create("Objects");
|
||||
tcase_add_test(dicts, simple_dict);
|
||||
lists = tcase_create("Lists");
|
||||
tcase_add_test(lists, simple_list);
|
||||
|
||||
whitespace = tcase_create("Whitespace");
|
||||
tcase_add_test(whitespace, simple_whitespace);
|
||||
|
||||
varargs = tcase_create("Varargs");
|
||||
tcase_add_test(varargs, simple_varargs);
|
||||
|
||||
suite = suite_create("QJSON test-suite");
|
||||
suite_add_tcase(suite, string_literals);
|
||||
suite_add_tcase(suite, number_literals);
|
||||
suite_add_tcase(suite, keyword_literals);
|
||||
suite_add_tcase(suite, dicts);
|
||||
suite_add_tcase(suite, lists);
|
||||
suite_add_tcase(suite, whitespace);
|
||||
suite_add_tcase(suite, varargs);
|
||||
|
||||
return suite;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int nf;
|
||||
Suite *s;
|
||||
SRunner *sr;
|
||||
|
||||
s = qjson_suite();
|
||||
sr = srunner_create(s);
|
||||
|
||||
srunner_run_all(sr, CK_NORMAL);
|
||||
nf = srunner_ntests_failed(sr);
|
||||
srunner_free(sr);
|
||||
|
||||
return (nf == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
-153
@@ -1,153 +0,0 @@
|
||||
/*
|
||||
* QList unit-tests.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#include <check.h>
|
||||
|
||||
#include "qint.h"
|
||||
#include "qlist.h"
|
||||
|
||||
/*
|
||||
* Public Interface test-cases
|
||||
*
|
||||
* (with some violations to access 'private' data)
|
||||
*/
|
||||
|
||||
START_TEST(qlist_new_test)
|
||||
{
|
||||
QList *qlist;
|
||||
|
||||
qlist = qlist_new();
|
||||
fail_unless(qlist != NULL);
|
||||
fail_unless(qlist->base.refcnt == 1);
|
||||
fail_unless(qobject_type(QOBJECT(qlist)) == QTYPE_QLIST);
|
||||
|
||||
// destroy doesn't exist yet
|
||||
qemu_free(qlist);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(qlist_append_test)
|
||||
{
|
||||
QInt *qi;
|
||||
QList *qlist;
|
||||
QListEntry *entry;
|
||||
|
||||
qi = qint_from_int(42);
|
||||
|
||||
qlist = qlist_new();
|
||||
qlist_append(qlist, qi);
|
||||
|
||||
entry = QTAILQ_FIRST(&qlist->head);
|
||||
fail_unless(entry != NULL);
|
||||
fail_unless(entry->value == QOBJECT(qi));
|
||||
|
||||
// destroy doesn't exist yet
|
||||
QDECREF(qi);
|
||||
qemu_free(entry);
|
||||
qemu_free(qlist);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(qobject_to_qlist_test)
|
||||
{
|
||||
QList *qlist;
|
||||
|
||||
qlist = qlist_new();
|
||||
|
||||
fail_unless(qobject_to_qlist(QOBJECT(qlist)) == qlist);
|
||||
|
||||
// destroy doesn't exist yet
|
||||
qemu_free(qlist);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(qlist_destroy_test)
|
||||
{
|
||||
int i;
|
||||
QList *qlist;
|
||||
|
||||
qlist = qlist_new();
|
||||
|
||||
for (i = 0; i < 42; i++)
|
||||
qlist_append(qlist, qint_from_int(i));
|
||||
|
||||
QDECREF(qlist);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
static int iter_called;
|
||||
static const int iter_max = 42;
|
||||
|
||||
static void iter_func(QObject *obj, void *opaque)
|
||||
{
|
||||
QInt *qi;
|
||||
|
||||
fail_unless(opaque == NULL);
|
||||
|
||||
qi = qobject_to_qint(obj);
|
||||
fail_unless(qi != NULL);
|
||||
fail_unless((qint_get_int(qi) >= 0) && (qint_get_int(qi) <= iter_max));
|
||||
|
||||
iter_called++;
|
||||
}
|
||||
|
||||
START_TEST(qlist_iter_test)
|
||||
{
|
||||
int i;
|
||||
QList *qlist;
|
||||
|
||||
qlist = qlist_new();
|
||||
|
||||
for (i = 0; i < iter_max; i++)
|
||||
qlist_append(qlist, qint_from_int(i));
|
||||
|
||||
iter_called = 0;
|
||||
qlist_iter(qlist, iter_func, NULL);
|
||||
|
||||
fail_unless(iter_called == iter_max);
|
||||
|
||||
QDECREF(qlist);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
static Suite *QList_suite(void)
|
||||
{
|
||||
Suite *s;
|
||||
TCase *qlist_public_tcase;
|
||||
|
||||
s = suite_create("QList suite");
|
||||
|
||||
qlist_public_tcase = tcase_create("Public Interface");
|
||||
suite_add_tcase(s, qlist_public_tcase);
|
||||
tcase_add_test(qlist_public_tcase, qlist_new_test);
|
||||
tcase_add_test(qlist_public_tcase, qlist_append_test);
|
||||
tcase_add_test(qlist_public_tcase, qobject_to_qlist_test);
|
||||
tcase_add_test(qlist_public_tcase, qlist_destroy_test);
|
||||
tcase_add_test(qlist_public_tcase, qlist_iter_test);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int nf;
|
||||
Suite *s;
|
||||
SRunner *sr;
|
||||
|
||||
s = QList_suite();
|
||||
sr = srunner_create(s);
|
||||
|
||||
srunner_run_all(sr, CK_NORMAL);
|
||||
nf = srunner_ntests_failed(sr);
|
||||
srunner_free(sr);
|
||||
|
||||
return (nf == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
-131
@@ -1,131 +0,0 @@
|
||||
/*
|
||||
* QString unit-tests.
|
||||
*
|
||||
* Copyright (C) 2009 Red Hat Inc.
|
||||
*
|
||||
* Authors:
|
||||
* Luiz Capitulino <lcapitulino@redhat.com>
|
||||
*/
|
||||
#include <check.h>
|
||||
|
||||
#include "qstring.h"
|
||||
#include "qemu-common.h"
|
||||
|
||||
/*
|
||||
* Public Interface test-cases
|
||||
*
|
||||
* (with some violations to access 'private' data)
|
||||
*/
|
||||
|
||||
START_TEST(qstring_from_str_test)
|
||||
{
|
||||
QString *qstring;
|
||||
const char *str = "QEMU";
|
||||
|
||||
qstring = qstring_from_str(str);
|
||||
fail_unless(qstring != NULL);
|
||||
fail_unless(qstring->base.refcnt == 1);
|
||||
fail_unless(strcmp(str, qstring->string) == 0);
|
||||
fail_unless(qobject_type(QOBJECT(qstring)) == QTYPE_QSTRING);
|
||||
|
||||
// destroy doesn't exit yet
|
||||
qemu_free(qstring->string);
|
||||
qemu_free(qstring);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(qstring_destroy_test)
|
||||
{
|
||||
QString *qstring = qstring_from_str("destroy test");
|
||||
QDECREF(qstring);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(qstring_get_str_test)
|
||||
{
|
||||
QString *qstring;
|
||||
const char *ret_str;
|
||||
const char *str = "QEMU/KVM";
|
||||
|
||||
qstring = qstring_from_str(str);
|
||||
ret_str = qstring_get_str(qstring);
|
||||
fail_unless(strcmp(ret_str, str) == 0);
|
||||
|
||||
QDECREF(qstring);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(qstring_append_chr_test)
|
||||
{
|
||||
int i;
|
||||
QString *qstring;
|
||||
const char *str = "qstring append char unit-test";
|
||||
|
||||
qstring = qstring_new();
|
||||
|
||||
for (i = 0; str[i]; i++)
|
||||
qstring_append_chr(qstring, str[i]);
|
||||
|
||||
fail_unless(strcmp(str, qstring_get_str(qstring)) == 0);
|
||||
QDECREF(qstring);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(qstring_from_substr_test)
|
||||
{
|
||||
QString *qs;
|
||||
|
||||
qs = qstring_from_substr("virtualization", 3, 9);
|
||||
fail_unless(qs != NULL);
|
||||
fail_unless(strcmp(qstring_get_str(qs), "tualiza") == 0);
|
||||
|
||||
QDECREF(qs);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
|
||||
START_TEST(qobject_to_qstring_test)
|
||||
{
|
||||
QString *qstring;
|
||||
|
||||
qstring = qstring_from_str("foo");
|
||||
fail_unless(qobject_to_qstring(QOBJECT(qstring)) == qstring);
|
||||
|
||||
QDECREF(qstring);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
static Suite *qstring_suite(void)
|
||||
{
|
||||
Suite *s;
|
||||
TCase *qstring_public_tcase;
|
||||
|
||||
s = suite_create("QString test-suite");
|
||||
|
||||
qstring_public_tcase = tcase_create("Public Interface");
|
||||
suite_add_tcase(s, qstring_public_tcase);
|
||||
tcase_add_test(qstring_public_tcase, qstring_from_str_test);
|
||||
tcase_add_test(qstring_public_tcase, qstring_destroy_test);
|
||||
tcase_add_test(qstring_public_tcase, qstring_get_str_test);
|
||||
tcase_add_test(qstring_public_tcase, qstring_append_chr_test);
|
||||
tcase_add_test(qstring_public_tcase, qstring_from_substr_test);
|
||||
tcase_add_test(qstring_public_tcase, qobject_to_qstring_test);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int nf;
|
||||
Suite *s;
|
||||
SRunner *sr;
|
||||
|
||||
s = qstring_suite();
|
||||
sr = srunner_create(s);
|
||||
|
||||
srunner_run_all(sr, CK_NORMAL);
|
||||
nf = srunner_ntests_failed(sr);
|
||||
srunner_free(sr);
|
||||
|
||||
return (nf == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
@@ -1,582 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2003-2005 Silicon Graphics, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it would 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <sys/time.h>
|
||||
#include <getopt.h>
|
||||
|
||||
#include "cmd.h"
|
||||
|
||||
#define _(x) x /* not gettext support yet */
|
||||
|
||||
/* from libxcmd/command.c */
|
||||
|
||||
cmdinfo_t *cmdtab;
|
||||
int ncmds;
|
||||
|
||||
static argsfunc_t args_func;
|
||||
static checkfunc_t check_func;
|
||||
static int ncmdline;
|
||||
static char **cmdline;
|
||||
|
||||
static int
|
||||
compare(const void *a, const void *b)
|
||||
{
|
||||
return strcmp(((const cmdinfo_t *)a)->name,
|
||||
((const cmdinfo_t *)b)->name);
|
||||
}
|
||||
|
||||
void
|
||||
add_command(
|
||||
const cmdinfo_t *ci)
|
||||
{
|
||||
cmdtab = realloc((void *)cmdtab, ++ncmds * sizeof(*cmdtab));
|
||||
cmdtab[ncmds - 1] = *ci;
|
||||
qsort(cmdtab, ncmds, sizeof(*cmdtab), compare);
|
||||
}
|
||||
|
||||
static int
|
||||
check_command(
|
||||
const cmdinfo_t *ci)
|
||||
{
|
||||
if (check_func)
|
||||
return check_func(ci);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
add_check_command(
|
||||
checkfunc_t cf)
|
||||
{
|
||||
check_func = cf;
|
||||
}
|
||||
|
||||
int
|
||||
command_usage(
|
||||
const cmdinfo_t *ci)
|
||||
{
|
||||
printf("%s %s -- %s\n", ci->name, ci->args, ci->oneline);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
command(
|
||||
const cmdinfo_t *ct,
|
||||
int argc,
|
||||
char **argv)
|
||||
{
|
||||
char *cmd = argv[0];
|
||||
|
||||
if (!check_command(ct))
|
||||
return 0;
|
||||
|
||||
if (argc-1 < ct->argmin || (ct->argmax != -1 && argc-1 > ct->argmax)) {
|
||||
if (ct->argmax == -1)
|
||||
fprintf(stderr,
|
||||
_("bad argument count %d to %s, expected at least %d arguments\n"),
|
||||
argc-1, cmd, ct->argmin);
|
||||
else if (ct->argmin == ct->argmax)
|
||||
fprintf(stderr,
|
||||
_("bad argument count %d to %s, expected %d arguments\n"),
|
||||
argc-1, cmd, ct->argmin);
|
||||
else
|
||||
fprintf(stderr,
|
||||
_("bad argument count %d to %s, expected between %d and %d arguments\n"),
|
||||
argc-1, cmd, ct->argmin, ct->argmax);
|
||||
return 0;
|
||||
}
|
||||
optind = 0;
|
||||
return ct->cfunc(argc, argv);
|
||||
}
|
||||
|
||||
const cmdinfo_t *
|
||||
find_command(
|
||||
const char *cmd)
|
||||
{
|
||||
cmdinfo_t *ct;
|
||||
|
||||
for (ct = cmdtab; ct < &cmdtab[ncmds]; ct++) {
|
||||
if (strcmp(ct->name, cmd) == 0 ||
|
||||
(ct->altname && strcmp(ct->altname, cmd) == 0))
|
||||
return (const cmdinfo_t *)ct;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
add_user_command(char *optarg)
|
||||
{
|
||||
ncmdline++;
|
||||
cmdline = realloc(cmdline, sizeof(char*) * (ncmdline));
|
||||
if (!cmdline) {
|
||||
perror("realloc");
|
||||
exit(1);
|
||||
}
|
||||
cmdline[ncmdline-1] = optarg;
|
||||
}
|
||||
|
||||
static int
|
||||
args_command(
|
||||
int index)
|
||||
{
|
||||
if (args_func)
|
||||
return args_func(index);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
add_args_command(
|
||||
argsfunc_t af)
|
||||
{
|
||||
args_func = af;
|
||||
}
|
||||
|
||||
void
|
||||
command_loop(void)
|
||||
{
|
||||
int c, i, j = 0, done = 0;
|
||||
char *input;
|
||||
char **v;
|
||||
const cmdinfo_t *ct;
|
||||
|
||||
for (i = 0; !done && i < ncmdline; i++) {
|
||||
input = strdup(cmdline[i]);
|
||||
if (!input) {
|
||||
fprintf(stderr,
|
||||
_("cannot strdup command '%s': %s\n"),
|
||||
cmdline[i], strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
v = breakline(input, &c);
|
||||
if (c) {
|
||||
ct = find_command(v[0]);
|
||||
if (ct) {
|
||||
if (ct->flags & CMD_FLAG_GLOBAL)
|
||||
done = command(ct, c, v);
|
||||
else {
|
||||
j = 0;
|
||||
while (!done && (j = args_command(j)))
|
||||
done = command(ct, c, v);
|
||||
}
|
||||
} else
|
||||
fprintf(stderr, _("command \"%s\" not found\n"),
|
||||
v[0]);
|
||||
}
|
||||
doneline(input, v);
|
||||
}
|
||||
if (cmdline) {
|
||||
free(cmdline);
|
||||
return;
|
||||
}
|
||||
while (!done) {
|
||||
if ((input = fetchline()) == NULL)
|
||||
break;
|
||||
v = breakline(input, &c);
|
||||
if (c) {
|
||||
ct = find_command(v[0]);
|
||||
if (ct)
|
||||
done = command(ct, c, v);
|
||||
else
|
||||
fprintf(stderr, _("command \"%s\" not found\n"),
|
||||
v[0]);
|
||||
}
|
||||
doneline(input, v);
|
||||
}
|
||||
}
|
||||
|
||||
/* from libxcmd/input.c */
|
||||
|
||||
#if defined(ENABLE_READLINE)
|
||||
# include <readline/history.h>
|
||||
# include <readline/readline.h>
|
||||
#elif defined(ENABLE_EDITLINE)
|
||||
# include <histedit.h>
|
||||
#endif
|
||||
|
||||
static char *
|
||||
get_prompt(void)
|
||||
{
|
||||
static char prompt[FILENAME_MAX + 2 /*"> "*/ + 1 /*"\0"*/ ];
|
||||
|
||||
if (!prompt[0])
|
||||
snprintf(prompt, sizeof(prompt), "%s> ", progname);
|
||||
return prompt;
|
||||
}
|
||||
|
||||
#if defined(ENABLE_READLINE)
|
||||
char *
|
||||
fetchline(void)
|
||||
{
|
||||
char *line;
|
||||
|
||||
line = readline(get_prompt());
|
||||
if (line && *line)
|
||||
add_history(line);
|
||||
return line;
|
||||
}
|
||||
#elif defined(ENABLE_EDITLINE)
|
||||
static char *el_get_prompt(EditLine *e) { return get_prompt(); }
|
||||
char *
|
||||
fetchline(void)
|
||||
{
|
||||
static EditLine *el;
|
||||
static History *hist;
|
||||
HistEvent hevent;
|
||||
char *line;
|
||||
int count;
|
||||
|
||||
if (!el) {
|
||||
hist = history_init();
|
||||
history(hist, &hevent, H_SETSIZE, 100);
|
||||
el = el_init(progname, stdin, stdout, stderr);
|
||||
el_source(el, NULL);
|
||||
el_set(el, EL_SIGNAL, 1);
|
||||
el_set(el, EL_PROMPT, el_get_prompt);
|
||||
el_set(el, EL_HIST, history, (const char *)hist);
|
||||
}
|
||||
line = strdup(el_gets(el, &count));
|
||||
if (line) {
|
||||
if (count > 0)
|
||||
line[count-1] = '\0';
|
||||
if (*line)
|
||||
history(hist, &hevent, H_ENTER, line);
|
||||
}
|
||||
return line;
|
||||
}
|
||||
#else
|
||||
# define MAXREADLINESZ 1024
|
||||
char *
|
||||
fetchline(void)
|
||||
{
|
||||
char *p, *line = malloc(MAXREADLINESZ);
|
||||
|
||||
if (!line)
|
||||
return NULL;
|
||||
printf("%s", get_prompt());
|
||||
fflush(stdout);
|
||||
if (!fgets(line, MAXREADLINESZ, stdin)) {
|
||||
free(line);
|
||||
return NULL;
|
||||
}
|
||||
p = line + strlen(line);
|
||||
if (p != line && p[-1] == '\n')
|
||||
p[-1] = '\0';
|
||||
return line;
|
||||
}
|
||||
#endif
|
||||
|
||||
static char *qemu_strsep(char **input, const char *delim)
|
||||
{
|
||||
char *result = *input;
|
||||
if (result != NULL) {
|
||||
char *p = result;
|
||||
for (p = result; *p != '\0'; p++) {
|
||||
if (strchr(delim, *p)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (*p == '\0') {
|
||||
*input = NULL;
|
||||
} else {
|
||||
*p = '\0';
|
||||
*input = p + 1;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
char **
|
||||
breakline(
|
||||
char *input,
|
||||
int *count)
|
||||
{
|
||||
int c = 0;
|
||||
char *p;
|
||||
char **rval = calloc(sizeof(char *), 1);
|
||||
|
||||
while (rval && (p = qemu_strsep(&input, " ")) != NULL) {
|
||||
if (!*p)
|
||||
continue;
|
||||
c++;
|
||||
rval = realloc(rval, sizeof(*rval) * (c + 1));
|
||||
if (!rval) {
|
||||
c = 0;
|
||||
break;
|
||||
}
|
||||
rval[c - 1] = p;
|
||||
rval[c] = NULL;
|
||||
}
|
||||
*count = c;
|
||||
return rval;
|
||||
}
|
||||
|
||||
void
|
||||
doneline(
|
||||
char *input,
|
||||
char **vec)
|
||||
{
|
||||
free(input);
|
||||
free(vec);
|
||||
}
|
||||
|
||||
#define EXABYTES(x) ((long long)(x) << 60)
|
||||
#define PETABYTES(x) ((long long)(x) << 50)
|
||||
#define TERABYTES(x) ((long long)(x) << 40)
|
||||
#define GIGABYTES(x) ((long long)(x) << 30)
|
||||
#define MEGABYTES(x) ((long long)(x) << 20)
|
||||
#define KILOBYTES(x) ((long long)(x) << 10)
|
||||
|
||||
long long
|
||||
cvtnum(
|
||||
char *s)
|
||||
{
|
||||
long long i;
|
||||
char *sp;
|
||||
int c;
|
||||
|
||||
i = strtoll(s, &sp, 0);
|
||||
if (i == 0 && sp == s)
|
||||
return -1LL;
|
||||
if (*sp == '\0')
|
||||
return i;
|
||||
|
||||
if (sp[1] != '\0')
|
||||
return -1LL;
|
||||
|
||||
c = tolower(*sp);
|
||||
switch (c) {
|
||||
default:
|
||||
return i;
|
||||
case 'k':
|
||||
return KILOBYTES(i);
|
||||
case 'm':
|
||||
return MEGABYTES(i);
|
||||
case 'g':
|
||||
return GIGABYTES(i);
|
||||
case 't':
|
||||
return TERABYTES(i);
|
||||
case 'p':
|
||||
return PETABYTES(i);
|
||||
case 'e':
|
||||
return EXABYTES(i);
|
||||
}
|
||||
return -1LL;
|
||||
}
|
||||
|
||||
#define TO_EXABYTES(x) ((x) / EXABYTES(1))
|
||||
#define TO_PETABYTES(x) ((x) / PETABYTES(1))
|
||||
#define TO_TERABYTES(x) ((x) / TERABYTES(1))
|
||||
#define TO_GIGABYTES(x) ((x) / GIGABYTES(1))
|
||||
#define TO_MEGABYTES(x) ((x) / MEGABYTES(1))
|
||||
#define TO_KILOBYTES(x) ((x) / KILOBYTES(1))
|
||||
|
||||
void
|
||||
cvtstr(
|
||||
double value,
|
||||
char *str,
|
||||
size_t size)
|
||||
{
|
||||
const char *fmt;
|
||||
int precise;
|
||||
|
||||
precise = ((double)value * 1000 == (double)(int)value * 1000);
|
||||
|
||||
if (value >= EXABYTES(1)) {
|
||||
fmt = precise ? "%.f EiB" : "%.3f EiB";
|
||||
snprintf(str, size, fmt, TO_EXABYTES(value));
|
||||
} else if (value >= PETABYTES(1)) {
|
||||
fmt = precise ? "%.f PiB" : "%.3f PiB";
|
||||
snprintf(str, size, fmt, TO_PETABYTES(value));
|
||||
} else if (value >= TERABYTES(1)) {
|
||||
fmt = precise ? "%.f TiB" : "%.3f TiB";
|
||||
snprintf(str, size, fmt, TO_TERABYTES(value));
|
||||
} else if (value >= GIGABYTES(1)) {
|
||||
fmt = precise ? "%.f GiB" : "%.3f GiB";
|
||||
snprintf(str, size, fmt, TO_GIGABYTES(value));
|
||||
} else if (value >= MEGABYTES(1)) {
|
||||
fmt = precise ? "%.f MiB" : "%.3f MiB";
|
||||
snprintf(str, size, fmt, TO_MEGABYTES(value));
|
||||
} else if (value >= KILOBYTES(1)) {
|
||||
fmt = precise ? "%.f KiB" : "%.3f KiB";
|
||||
snprintf(str, size, fmt, TO_KILOBYTES(value));
|
||||
} else {
|
||||
snprintf(str, size, "%f bytes", value);
|
||||
}
|
||||
}
|
||||
|
||||
struct timeval
|
||||
tsub(struct timeval t1, struct timeval t2)
|
||||
{
|
||||
t1.tv_usec -= t2.tv_usec;
|
||||
if (t1.tv_usec < 0) {
|
||||
t1.tv_usec += 1000000;
|
||||
t1.tv_sec--;
|
||||
}
|
||||
t1.tv_sec -= t2.tv_sec;
|
||||
return t1;
|
||||
}
|
||||
|
||||
double
|
||||
tdiv(double value, struct timeval tv)
|
||||
{
|
||||
return value / ((double)tv.tv_sec + ((double)tv.tv_usec / 1000000.0));
|
||||
}
|
||||
|
||||
#define HOURS(sec) ((sec) / (60 * 60))
|
||||
#define MINUTES(sec) (((sec) % (60 * 60)) / 60)
|
||||
#define SECONDS(sec) ((sec) % 60)
|
||||
|
||||
void
|
||||
timestr(
|
||||
struct timeval *tv,
|
||||
char *ts,
|
||||
size_t size,
|
||||
int format)
|
||||
{
|
||||
double usec = (double)tv->tv_usec / 1000000.0;
|
||||
|
||||
if (format & TERSE_FIXED_TIME) {
|
||||
if (!HOURS(tv->tv_sec)) {
|
||||
snprintf(ts, size, "%u:%02u.%02u",
|
||||
(unsigned int) MINUTES(tv->tv_sec),
|
||||
(unsigned int) SECONDS(tv->tv_sec),
|
||||
(unsigned int) usec * 100);
|
||||
return;
|
||||
}
|
||||
format |= VERBOSE_FIXED_TIME; /* fallback if hours needed */
|
||||
}
|
||||
|
||||
if ((format & VERBOSE_FIXED_TIME) || tv->tv_sec) {
|
||||
snprintf(ts, size, "%u:%02u:%02u.%02u",
|
||||
(unsigned int) HOURS(tv->tv_sec),
|
||||
(unsigned int) MINUTES(tv->tv_sec),
|
||||
(unsigned int) SECONDS(tv->tv_sec),
|
||||
(unsigned int) usec * 100);
|
||||
} else {
|
||||
snprintf(ts, size, "0.%04u sec", (unsigned int) usec * 10000);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* from libxcmd/quit.c */
|
||||
|
||||
static cmdinfo_t quit_cmd;
|
||||
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
quit_f(
|
||||
int argc,
|
||||
char **argv)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
quit_init(void)
|
||||
{
|
||||
quit_cmd.name = _("quit");
|
||||
quit_cmd.altname = _("q");
|
||||
quit_cmd.cfunc = quit_f;
|
||||
quit_cmd.argmin = -1;
|
||||
quit_cmd.argmax = -1;
|
||||
quit_cmd.flags = CMD_FLAG_GLOBAL;
|
||||
quit_cmd.oneline = _("exit the program");
|
||||
|
||||
add_command(&quit_cmd);
|
||||
}
|
||||
|
||||
/* from libxcmd/help.c */
|
||||
|
||||
static cmdinfo_t help_cmd;
|
||||
static void help_onecmd(const char *cmd, const cmdinfo_t *ct);
|
||||
static void help_oneline(const char *cmd, const cmdinfo_t *ct);
|
||||
|
||||
static void
|
||||
help_all(void)
|
||||
{
|
||||
const cmdinfo_t *ct;
|
||||
|
||||
for (ct = cmdtab; ct < &cmdtab[ncmds]; ct++)
|
||||
help_oneline(ct->name, ct);
|
||||
printf(_("\nUse 'help commandname' for extended help.\n"));
|
||||
}
|
||||
|
||||
static int
|
||||
help_f(
|
||||
int argc,
|
||||
char **argv)
|
||||
{
|
||||
const cmdinfo_t *ct;
|
||||
|
||||
if (argc == 1) {
|
||||
help_all();
|
||||
return 0;
|
||||
}
|
||||
ct = find_command(argv[1]);
|
||||
if (ct == NULL) {
|
||||
printf(_("command %s not found\n"), argv[1]);
|
||||
return 0;
|
||||
}
|
||||
help_onecmd(argv[1], ct);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
help_onecmd(
|
||||
const char *cmd,
|
||||
const cmdinfo_t *ct)
|
||||
{
|
||||
help_oneline(cmd, ct);
|
||||
if (ct->help)
|
||||
ct->help();
|
||||
}
|
||||
|
||||
static void
|
||||
help_oneline(
|
||||
const char *cmd,
|
||||
const cmdinfo_t *ct)
|
||||
{
|
||||
if (cmd)
|
||||
printf("%s ", cmd);
|
||||
else {
|
||||
printf("%s ", ct->name);
|
||||
if (ct->altname)
|
||||
printf("(or %s) ", ct->altname);
|
||||
}
|
||||
if (ct->args)
|
||||
printf("%s ", ct->args);
|
||||
printf("-- %s\n", ct->oneline);
|
||||
}
|
||||
|
||||
void
|
||||
help_init(void)
|
||||
{
|
||||
help_cmd.name = _("help");
|
||||
help_cmd.altname = _("?");
|
||||
help_cmd.cfunc = help_f;
|
||||
help_cmd.argmin = 0;
|
||||
help_cmd.argmax = 1;
|
||||
help_cmd.flags = CMD_FLAG_GLOBAL;
|
||||
help_cmd.args = _("[command]");
|
||||
help_cmd.oneline = _("help for one or all commands");
|
||||
|
||||
add_command(&help_cmd);
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2000-2005 Silicon Graphics, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it would 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/>.
|
||||
*/
|
||||
#ifndef __COMMAND_H__
|
||||
#define __COMMAND_H__
|
||||
|
||||
#define CMD_FLAG_GLOBAL ((int)0x80000000) /* don't iterate "args" */
|
||||
|
||||
typedef int (*cfunc_t)(int argc, char **argv);
|
||||
typedef void (*helpfunc_t)(void);
|
||||
|
||||
typedef struct cmdinfo {
|
||||
const char *name;
|
||||
const char *altname;
|
||||
cfunc_t cfunc;
|
||||
int argmin;
|
||||
int argmax;
|
||||
int canpush;
|
||||
int flags;
|
||||
const char *args;
|
||||
const char *oneline;
|
||||
helpfunc_t help;
|
||||
} cmdinfo_t;
|
||||
|
||||
extern cmdinfo_t *cmdtab;
|
||||
extern int ncmds;
|
||||
|
||||
extern void help_init(void);
|
||||
extern void quit_init(void);
|
||||
|
||||
typedef int (*argsfunc_t)(int index);
|
||||
typedef int (*checkfunc_t)(const cmdinfo_t *ci);
|
||||
|
||||
extern void add_command(const cmdinfo_t *ci);
|
||||
extern void add_user_command(char *optarg);
|
||||
extern void add_args_command(argsfunc_t af);
|
||||
extern void add_check_command(checkfunc_t cf);
|
||||
|
||||
extern const cmdinfo_t *find_command(const char *cmd);
|
||||
|
||||
extern void command_loop(void);
|
||||
extern int command_usage(const cmdinfo_t *ci);
|
||||
extern int command(const cmdinfo_t *ci, int argc, char **argv);
|
||||
|
||||
/* from input.h */
|
||||
extern char **breakline(char *input, int *count);
|
||||
extern void doneline(char *input, char **vec);
|
||||
extern char *fetchline(void);
|
||||
|
||||
extern long long cvtnum(char *s);
|
||||
extern void cvtstr(double value, char *str, size_t sz);
|
||||
|
||||
extern struct timeval tsub(struct timeval t1, struct timeval t2);
|
||||
extern double tdiv(double value, struct timeval tv);
|
||||
|
||||
enum {
|
||||
DEFAULT_TIME = 0x0,
|
||||
TERSE_FIXED_TIME = 0x1,
|
||||
VERBOSE_FIXED_TIME = 0x2
|
||||
};
|
||||
|
||||
extern void timestr(struct timeval *tv, char *str, size_t sz, int flags);
|
||||
|
||||
extern char *progname;
|
||||
|
||||
#endif /* __COMMAND_H__ */
|
||||
Loaded 100 of 1010 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user