[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-changelog] [xen-unstable] [ioemu] Update to qemu 0.90.
# HG changeset patch # User Christian Limpach <Christian.Limpach@xxxxxxxxxxxxx> # Date 1178716635 -3600 # Node ID 00618037d37d04e614080d3067cc5ba6b1b1ef9e # Parent d2ef85c6bf84cc619ca2d42c2edfc6229e70a6ad [ioemu] Update to qemu 0.90. Signed-off-by: Christian Limpach <Christian.Limpach@xxxxxxxxxxxxx> --- tools/ioemu/.CVS/Entries.Log | 7 tools/ioemu/README.distrib | 16 tools/ioemu/hw/acpi-dsdt.dsl | 559 ---- tools/ioemu/hw/acpi-dsdt.hex | 278 -- tools/ioemu/hw/lance.c | 462 ---- tools/ioemu/.CVS/Entries | 208 - tools/ioemu/.CVS/Tag | 2 tools/ioemu/.cvsignore | 21 tools/ioemu/Changelog | 18 tools/ioemu/LICENSE | 15 tools/ioemu/Makefile | 48 tools/ioemu/Makefile.target | 225 +- tools/ioemu/VERSION | 2 tools/ioemu/audio/.CVS/Entries | 38 tools/ioemu/audio/.CVS/Tag | 2 tools/ioemu/audio/wavaudio.c | 4 tools/ioemu/audio/wavcapture.c | 31 tools/ioemu/block-bochs.c | 48 tools/ioemu/block-cloop.c | 6 tools/ioemu/block-cow.c | 38 tools/ioemu/block-dmg.c | 8 tools/ioemu/block-qcow.c | 382 ++- tools/ioemu/block-qcow2.c | 2246 ++++++++++++++++++++ tools/ioemu/block-raw.c | 1353 ++++++++++++ tools/ioemu/block-vmdk.c | 408 +++ tools/ioemu/block-vpc.c | 13 tools/ioemu/block-vvfat.c | 26 tools/ioemu/block.c | 1330 ++++++++--- tools/ioemu/block_int.h | 61 tools/ioemu/check_ops.sh | 47 tools/ioemu/configure | 179 + tools/ioemu/console.c | 208 + tools/ioemu/cpu-all.h | 18 tools/ioemu/cpu-defs.h | 8 tools/ioemu/cpu-exec.c | 238 +- tools/ioemu/cutils.c | 83 tools/ioemu/disas.c | 14 tools/ioemu/dyngen-exec.h | 5 tools/ioemu/dyngen.c | 301 ++ tools/ioemu/dyngen.h | 60 tools/ioemu/elf.h | 2 tools/ioemu/elf_ops.h | 4 tools/ioemu/exec-all.h | 12 tools/ioemu/exec.c | 33 tools/ioemu/fpu/.CVS/Entries | 12 tools/ioemu/fpu/.CVS/Tag | 2 tools/ioemu/fpu/softfloat-native.c | 34 tools/ioemu/fpu/softfloat-native.h | 62 tools/ioemu/fpu/softfloat-specialize.h | 16 tools/ioemu/fpu/softfloat.c | 65 tools/ioemu/fpu/softfloat.h | 68 tools/ioemu/gdbstub.c | 411 +++ tools/ioemu/gdbstub.h | 10 tools/ioemu/hostregs_helper.h | 98 tools/ioemu/hw/.CVS/Entries | 185 - tools/ioemu/hw/.CVS/Tag | 2 tools/ioemu/hw/acpi.c | 670 ++--- tools/ioemu/hw/adb.c | 4 tools/ioemu/hw/apb_pci.c | 49 tools/ioemu/hw/apic.c | 16 tools/ioemu/hw/arm_boot.c | 54 tools/ioemu/hw/arm_gic.c | 547 ++++ tools/ioemu/hw/arm_sysctl.c | 208 + tools/ioemu/hw/arm_timer.c | 12 tools/ioemu/hw/cirrus_vga.c | 32 tools/ioemu/hw/cs4231.c | 183 + tools/ioemu/hw/esp.c | 392 +-- tools/ioemu/hw/fdc.c | 43 tools/ioemu/hw/grackle_pci.c | 15 tools/ioemu/hw/gt64xxx.c | 648 +++++ tools/ioemu/hw/i8259.c | 7 tools/ioemu/hw/ide.c | 813 +++---- tools/ioemu/hw/integratorcp.c | 6 tools/ioemu/hw/iommu.c | 45 tools/ioemu/hw/isa_mmio.c | 102 tools/ioemu/hw/lsi53c895a.c | 499 +++- tools/ioemu/hw/mc146818rtc.c | 26 tools/ioemu/hw/mips_int.c | 39 tools/ioemu/hw/mips_malta.c | 590 +++++ tools/ioemu/hw/mips_r4k.c | 384 +-- tools/ioemu/hw/mips_timer.c | 83 tools/ioemu/hw/ne2000.c | 36 tools/ioemu/hw/pc.c | 251 -- tools/ioemu/hw/pci.c | 167 + tools/ioemu/hw/pcnet.c | 601 +++-- tools/ioemu/hw/pflash_cfi02.c | 1 tools/ioemu/hw/piix4acpi.c | 16 tools/ioemu/hw/piix_pci.c | 523 ++-- tools/ioemu/hw/pl011.c | 4 tools/ioemu/hw/pl080.c | 32 tools/ioemu/hw/pl110.c | 5 tools/ioemu/hw/pl110_template.h | 2 tools/ioemu/hw/ppc.c | 58 tools/ioemu/hw/ppc_chrp.c | 26 tools/ioemu/hw/ppc_prep.c | 5 tools/ioemu/hw/prep_pci.c | 14 tools/ioemu/hw/ps2.c | 2 tools/ioemu/hw/realview.c | 138 + tools/ioemu/hw/rtl8139.c | 20 tools/ioemu/hw/scsi-disk.c | 507 ++-- tools/ioemu/hw/serial.c | 23 tools/ioemu/hw/sh7750.c | 10 tools/ioemu/hw/slavio_misc.c | 3 tools/ioemu/hw/slavio_serial.c | 282 +- tools/ioemu/hw/smbus.h | 38 tools/ioemu/hw/smbus_eeprom.c | 94 tools/ioemu/hw/smc91c111.c | 5 tools/ioemu/hw/sparc32_dma.c | 283 ++ tools/ioemu/hw/sun4m.c | 28 tools/ioemu/hw/sun4u.c | 2 tools/ioemu/hw/tcx.c | 88 tools/ioemu/hw/unin_pci.c | 17 tools/ioemu/hw/usb-hid.c | 26 tools/ioemu/hw/usb-hub.c | 48 tools/ioemu/hw/usb-msd.c | 244 +- tools/ioemu/hw/usb-ohci.c | 178 + tools/ioemu/hw/usb-uhci.c | 263 +- tools/ioemu/hw/usb.c | 37 tools/ioemu/hw/usb.h | 58 tools/ioemu/hw/versatile_pci.c | 41 tools/ioemu/hw/versatilepb.c | 198 - tools/ioemu/hw/vga.c | 187 + tools/ioemu/hw/vga_int.h | 7 tools/ioemu/hw/xen_platform.c | 19 tools/ioemu/keymaps.c | 63 tools/ioemu/keymaps/.CVS/Entries | 70 tools/ioemu/keymaps/.CVS/Tag | 2 tools/ioemu/keymaps/ja | 1 tools/ioemu/kqemu.c | 11 tools/ioemu/loader.c | 12 tools/ioemu/monitor.c | 133 - tools/ioemu/osdep.c | 458 ---- tools/ioemu/osdep.h | 39 tools/ioemu/patches/acpi-poweroff-support | 2 tools/ioemu/patches/acpi-support | 101 tools/ioemu/patches/acpi-timer-support | 2 tools/ioemu/patches/domain-destroy | 10 tools/ioemu/patches/domain-reset | 20 tools/ioemu/patches/domain-timeoffset | 66 tools/ioemu/patches/fix-interrupt-routing | 31 tools/ioemu/patches/fix-vga-scanning-code-overflow | 8 tools/ioemu/patches/hypervisor-pit | 18 tools/ioemu/patches/hypervisor-rtc | 12 tools/ioemu/patches/ide-error-reporting | 36 tools/ioemu/patches/ioemu-buffer-pio-ia64 | 47 tools/ioemu/patches/ioemu-ia64 | 21 tools/ioemu/patches/ioemu-save-restore | 85 tools/ioemu/patches/ioemu-save-restore-acpi | 11 tools/ioemu/patches/ioemu-save-restore-ide | 16 tools/ioemu/patches/ioemu-save-restore-logdirty | 12 tools/ioemu/patches/ioemu-save-restore-ne2000 | 21 tools/ioemu/patches/ioemu-save-restore-pcnet | 84 tools/ioemu/patches/ioemu-save-restore-rtl8139 | 10 tools/ioemu/patches/ioemu-save-restore-timer | 4 tools/ioemu/patches/ioemu-save-restore-usb | 54 tools/ioemu/patches/ne2000-bounds-checks | 2 tools/ioemu/patches/qemu-64bit | 32 tools/ioemu/patches/qemu-allow-disable-sdl | 8 tools/ioemu/patches/qemu-block-device-bounds-checks | 22 tools/ioemu/patches/qemu-bootorder | 79 tools/ioemu/patches/qemu-bugfixes | 19 tools/ioemu/patches/qemu-cirrus-bounds-checks | 6 tools/ioemu/patches/qemu-cleanup | 36 tools/ioemu/patches/qemu-daemonize | 4 tools/ioemu/patches/qemu-dm | 176 - tools/ioemu/patches/qemu-dma-null-pointer-check | 2 tools/ioemu/patches/qemu-hvm-banner | 20 tools/ioemu/patches/qemu-infrastructure | 7 tools/ioemu/patches/qemu-init-vgabios | 12 tools/ioemu/patches/qemu-logging | 18 tools/ioemu/patches/qemu-no-apic | 18 tools/ioemu/patches/qemu-nobios | 17 tools/ioemu/patches/qemu-pci | 20 tools/ioemu/patches/qemu-pci-vendor-ids | 16 tools/ioemu/patches/qemu-serial-fixes | 18 tools/ioemu/patches/qemu-smp | 24 tools/ioemu/patches/qemu-target-i386-dm | 59 tools/ioemu/patches/qemu-timer | 10 tools/ioemu/patches/qemu-tunable-ide-write-cache | 21 tools/ioemu/patches/remove-pci-bridge-setup | 35 tools/ioemu/patches/rtl8139-bound-chaining | 2 tools/ioemu/patches/scsi | 70 tools/ioemu/patches/serial-non-block | 4 tools/ioemu/patches/serial-port-rate-limit | 20 tools/ioemu/patches/series | 11 tools/ioemu/patches/shadow-vram | 16 tools/ioemu/patches/shared-vram | 44 tools/ioemu/patches/support-xm-console | 31 tools/ioemu/patches/tpm-tis-device | 19 tools/ioemu/patches/usb-mouse-tablet-status-check | 52 tools/ioemu/patches/vnc-access-monitor-vt | 92 tools/ioemu/patches/vnc-backoff-screen-scan | 32 tools/ioemu/patches/vnc-cleanup | 20 tools/ioemu/patches/vnc-display-find-unused | 106 tools/ioemu/patches/vnc-fix-signedness | 70 tools/ioemu/patches/vnc-fix-version-check | 4 tools/ioemu/patches/vnc-fixes | 85 tools/ioemu/patches/vnc-listen-specific-interface | 115 - tools/ioemu/patches/vnc-password | 38 tools/ioemu/patches/vnc-protocol-fixes | 8 tools/ioemu/patches/vnc-start-vncviewer | 48 tools/ioemu/patches/vnc-title-domain-name | 6 tools/ioemu/patches/xen-build | 98 tools/ioemu/patches/xen-domain-name | 30 tools/ioemu/patches/xen-domid | 27 tools/ioemu/patches/xen-mapcache | 26 tools/ioemu/patches/xen-mm | 45 tools/ioemu/patches/xen-network | 22 tools/ioemu/patches/xen-platform-device | 20 tools/ioemu/patches/xen-support-buffered-ioreqs | 12 tools/ioemu/patches/xenstore | 21 tools/ioemu/patches/xenstore-block-device-config | 84 tools/ioemu/patches/xenstore-device-info-functions | 6 tools/ioemu/patches/xenstore-write-vnc-port | 15 tools/ioemu/pc-bios/.CVS/Entries | 29 tools/ioemu/pc-bios/.CVS/Tag | 2 tools/ioemu/pc-bios/README | 6 tools/ioemu/pc-bios/bios.diff | 252 -- tools/ioemu/qemu-doc.texi | 604 +++-- tools/ioemu/qemu-img.c | 132 - tools/ioemu/qemu-img.texi | 20 tools/ioemu/qemu_socket.h | 1 tools/ioemu/sdl.c | 87 tools/ioemu/tap-win32.c | 31 tools/ioemu/target-i386-dm/helper2.c | 6 tools/ioemu/target-i386-dm/piix_pci-dm.c | 102 tools/ioemu/target-i386-dm/rtc-dm.c | 19 tools/ioemu/target-i386/.CVS/Entries | 24 tools/ioemu/target-i386/.CVS/Tag | 2 tools/ioemu/target-i386/cpu.h | 19 tools/ioemu/target-i386/exec.h | 2 tools/ioemu/target-i386/helper.c | 326 ++ tools/ioemu/target-i386/helper2.c | 11 tools/ioemu/target-i386/op.c | 9 tools/ioemu/target-i386/ops_sse.h | 19 tools/ioemu/target-i386/translate.c | 120 - tools/ioemu/tests/.CVS/Entries | 35 tools/ioemu/tests/.CVS/Tag | 2 tools/ioemu/tests/Makefile | 11 tools/ioemu/tests/hello-mips.c | 64 tools/ioemu/tests/test-i386.c | 36 tools/ioemu/translate-all.c | 2 tools/ioemu/usb-linux.c | 11 tools/ioemu/vl.c | 1794 ++++++++++++--- tools/ioemu/vl.h | 349 ++- tools/ioemu/vnc.c | 406 ++- tools/ioemu/vnc_keysym.h | 14 tools/ioemu/vnchextile.h | 2 tools/ioemu/x_keymap.c | 110 ioemu/pc-bios/pxe-ne2k_pci.bin | 0 ioemu/pc-bios/pxe-pcnet.bin | 0 ioemu/pc-bios/pxe-rtl8139.bin | 0 252 files changed, 18815 insertions(+), 9370 deletions(-) diff -r d2ef85c6bf84 -r 00618037d37d tools/ioemu/.CVS/Entries --- a/tools/ioemu/.CVS/Entries Tue May 08 10:38:06 2007 +0100 +++ b/tools/ioemu/.CVS/Entries Wed May 09 14:17:15 2007 +0100 @@ -1,109 +1,117 @@ D/audio//// D/audio//// D/hw//// -D/linux-user//// D/pc-bios//// -D/slirp//// -D/target-arm//// D/target-i386//// -D/target-ppc//// -D/target-sparc//// D/tests//// D/fpu//// D/keymaps//// +/.cvsignore/1.16/Thu May 3 17:17:53 2007//Trelease_0_9_0 +/COPYING/1.1/Thu Jul 13 09:23:22 2006//Trelease_0_9_0 +/COPYING.LIB/1.2/Thu Jul 13 09:23:22 2006//Trelease_0_9_0 +/Changelog/1.128/Thu May 3 17:17:53 2007//Trelease_0_9_0 +/LICENSE/1.3/Thu May 3 17:17:53 2007//Trelease_0_9_0 +/Makefile/1.112/Thu May 3 17:17:53 2007//Trelease_0_9_0 +/Makefile.target/1.144/Thu May 3 17:17:53 2007//Trelease_0_9_0 +/README/1.12/Wed Oct 18 10:11:20 2006//Trelease_0_9_0 +/TODO/1.39/Fri Jan 5 14:34:35 2007//Trelease_0_9_0 +/VERSION/1.30/Thu May 3 17:17:53 2007//Trelease_0_9_0 +/a.out.h/1.2/Wed Oct 18 10:11:20 2006//Trelease_0_9_0 +/aes.c/1.1/Thu Jul 13 09:23:22 2006//Trelease_0_9_0 +/aes.h/1.1/Thu Jul 13 09:23:22 2006//Trelease_0_9_0 +/alpha-dis.c/1.3/Thu May 3 17:17:54 2007//Trelease_0_9_0 +/alpha.ld/1.1/Thu May 3 17:17:54 2007//Trelease_0_9_0 +/arm-dis.c/1.3/Thu May 3 17:17:54 2007//Trelease_0_9_0 +/arm-semi.c/1.2/Sun Jan 28 03:10:55 2007//Trelease_0_9_0 +/arm.ld/1.2/Thu May 3 17:17:54 2007//Trelease_0_9_0 +/block-bochs.c/1.3/Thu May 3 17:17:54 2007//Trelease_0_9_0 +/block-cloop.c/1.4/Thu May 3 17:17:54 2007//Trelease_0_9_0 +/block-cow.c/1.7/Thu May 3 17:17:54 2007//Trelease_0_9_0 +/block-dmg.c/1.5/Thu May 3 17:17:54 2007//Trelease_0_9_0 +/block-qcow.c/1.11/Thu May 3 17:17:54 2007//Trelease_0_9_0 +/block-qcow2.c/1.4/Mon Aug 7 02:38:06 2006//Trelease_0_9_0 +/block-raw.c/1.17/Thu Jan 18 00:22:11 2007//Trelease_0_9_0 +/block-vmdk.c/1.10/Thu May 3 17:17:55 2007//Trelease_0_9_0 +/block-vpc.c/1.4/Thu May 3 17:17:55 2007//Trelease_0_9_0 +/block-vvfat.c/1.8/Thu May 3 17:17:55 2007//Trelease_0_9_0 +/block.c/1.42/Thu May 3 17:17:55 2007//Trelease_0_9_0 +/block_int.h/1.10/Thu May 3 17:17:55 2007//Trelease_0_9_0 +/bswap.h/1.5/Thu Jul 13 09:23:22 2006//Trelease_0_9_0 +/check_ops.sh/1.1/Sun Jan 7 19:38:08 2007//Trelease_0_9_0 +/cocoa.m/1.10/Fri Jan 5 14:34:35 2007//Trelease_0_9_0 +/configure/1.120/Thu May 3 17:17:55 2007//Trelease_0_9_0 +/console.c/1.11/Thu May 3 17:17:55 2007//Trelease_0_9_0 +/cpu-all.h/1.60/Thu May 3 17:17:55 2007//Trelease_0_9_0 +/cpu-defs.h/1.17/Thu May 3 17:17:55 2007//Trelease_0_9_0 +/cpu-exec.c/1.93/Thu May 3 17:17:55 2007//Trelease_0_9_0 +/cutils.c/1.1/Sun Jan 7 22:04:40 2007//Trelease_0_9_0 +/dis-asm.h/1.11/Wed Oct 18 10:11:20 2006//Trelease_0_9_0 +/disas.c/1.34/Thu May 3 17:17:55 2007//Trelease_0_9_0 +/disas.h/1.7/Thu May 3 17:17:36 2007//Trelease_0_9_0 +/dyngen-exec.h/1.31/Thu May 3 17:17:55 2007//Trelease_0_9_0 +/dyngen-op.h/1.1/Wed Oct 18 10:11:20 2006//Trelease_0_9_0 +/dyngen.c/1.47/Thu May 3 17:17:55 2007//Trelease_0_9_0 +/dyngen.h/1.12/Thu May 3 17:17:55 2007//Trelease_0_9_0 +/elf.h/1.8/Thu May 3 17:17:55 2007//Trelease_0_9_0 +/elf_ops.h/1.5/Thu May 3 17:17:55 2007//Trelease_0_9_0 +/exec-all.h/1.49/Thu May 3 17:17:55 2007//Trelease_0_9_0 +/exec.c/1.85/Thu May 3 17:17:55 2007//Trelease_0_9_0 +/gdbstub.c/1.47/Thu May 3 17:17:55 2007//Trelease_0_9_0 +/gdbstub.h/1.5/Thu May 3 17:17:55 2007//Trelease_0_9_0 +/hostregs_helper.h/1.1/Sun Feb 4 13:37:44 2007//Trelease_0_9_0 +/i386-dis.c/1.5/Wed Oct 18 10:11:21 2006//Trelease_0_9_0 +/i386-vl.ld/1.3/Wed Oct 18 10:11:21 2006//Trelease_0_9_0 +/i386.ld/1.2/Wed Oct 18 10:11:21 2006//Trelease_0_9_0 +/ia64.ld/1.1/Wed Oct 18 10:11:21 2006//Trelease_0_9_0 +/keymaps.c/1.2/Thu May 3 17:17:34 2007//Trelease_0_9_0 +/kqemu.c/1.15/Thu May 3 17:17:55 2007//Trelease_0_9_0 +/kqemu.h/1.1/Wed Oct 18 10:11:21 2006//Trelease_0_9_0 +/loader.c/1.4/Thu May 3 17:17:55 2007//Trelease_0_9_0 +/m68k-dis.c/1.1/Thu May 3 17:17:56 2007//Trelease_0_9_0 +/m68k.ld/1.1/Thu May 3 17:17:56 2007//Trelease_0_9_0 +/mips-dis.c/1.4/Thu May 3 17:17:57 2007//Trelease_0_9_0 +/monitor.c/1.64/Thu May 3 17:17:57 2007//Trelease_0_9_0 +/osdep.c/1.15/Thu May 3 17:17:57 2007//Trelease_0_9_0 +/osdep.h/1.8/Thu May 3 17:17:57 2007//Trelease_0_9_0 +/ppc-dis.c/1.7/Thu May 3 17:17:58 2007//Trelease_0_9_0 +/ppc.ld/1.2/Thu May 3 17:17:58 2007//Trelease_0_9_0 +/qemu-binfmt-conf.sh/1.4/Wed Oct 18 10:11:21 2006//Trelease_0_9_0 +/qemu-doc.texi/1.128/Thu May 3 17:17:58 2007//Trelease_0_9_0 +/qemu-img.c/1.16/Thu May 3 17:17:58 2007//Trelease_0_9_0 +/qemu-img.texi/1.3/Thu May 3 17:17:58 2007//Trelease_0_9_0 +/qemu-tech.texi/1.9/Wed Oct 18 10:11:21 2006//Trelease_0_9_0 +/qemu_socket.h/1.2/Thu May 3 17:17:58 2007//Trelease_0_9_0 +/readline.c/1.1/Wed Oct 18 10:11:21 2006//Trelease_0_9_0 +/s390.ld/1.1/Thu May 3 17:17:58 2007//Trelease_0_9_0 +/sdl.c/1.34/Thu May 3 17:17:58 2007//Trelease_0_9_0 +/sdl_keysym.h/1.3/Wed Oct 18 10:11:21 2006//Trelease_0_9_0 +/sh4-dis.c/1.1/Thu May 3 17:17:58 2007//Trelease_0_9_0 +/softmmu_exec.h/1.1/Wed Oct 18 10:11:21 2006//Trelease_0_9_0 +/softmmu_header.h/1.13/Wed Oct 18 10:11:21 2006//Trelease_0_9_0 +/softmmu_template.h/1.16/Wed Oct 18 10:11:21 2006//Trelease_0_9_0 +/sparc-dis.c/1.3/Thu May 3 17:17:59 2007//Trelease_0_9_0 +/sparc.ld/1.1/Thu May 3 17:17:59 2007//Trelease_0_9_0 +/sparc64.ld/1.1/Fri Aug 4 21:55:15 2006//Trelease_0_9_0 +/tap-win32.c/1.4/Thu May 3 17:17:59 2007//Trelease_0_9_0 +/texi2pod.pl/1.1/Wed Oct 18 10:11:21 2006//Trelease_0_9_0 +/thunk.c/1.6/Wed Oct 18 10:11:21 2006//Trelease_0_9_0 +/thunk.h/1.13/Thu Jul 13 09:23:22 2006//Trelease_0_9_0 +/translate-all.c/1.15/Thu May 3 17:17:59 2007//Trelease_0_9_0 +/translate-op.c/1.1/Wed Oct 18 10:11:21 2006//Trelease_0_9_0 +/usb-linux.c/1.10/Thu May 3 17:17:59 2007//Trelease_0_9_0 +/vgafont.h/1.1/Thu Jul 13 09:23:22 2006//Trelease_0_9_0 +/vl.c/1.248/Thu May 3 17:17:59 2007//Trelease_0_9_0 +/vl.h/1.184/Thu May 3 17:17:59 2007//Trelease_0_9_0 +/vnc.c/1.12/Thu May 3 17:17:59 2007//Trelease_0_9_0 +/vnc_keysym.h/1.2/Thu May 3 17:17:59 2007//Trelease_0_9_0 +/vnchextile.h/1.3/Thu May 3 17:17:59 2007//Trelease_0_9_0 +/x86_64.ld/1.1/Thu Jul 13 09:23:22 2006//Trelease_0_9_0 +/x_keymap.c/1.1/Wed Jan 24 21:40:21 2007//Trelease_0_9_0 +D/darwin-user//// +D/linux-user//// +D/slirp//// +D/target-arm//// +D/target-m68k//// D/target-mips//// +D/target-ppc//// D/target-sh4//// -/.cvsignore/1.14/Sun Aug 6 01:03:44 2006//Trelease_0_8_2 -/COPYING/1.1/Thu Jul 13 09:23:22 2006//Trelease_0_8_2 -/COPYING.LIB/1.2/Thu Jul 13 09:23:22 2006//Trelease_0_8_2 -/Changelog/1.121/Sun Aug 6 01:03:44 2006//Trelease_0_8_2 -/LICENSE/1.2/Fri Jul 14 12:43:45 2006//Trelease_0_8_2 -/Makefile/1.104/Sun Aug 6 01:03:44 2006//Trelease_0_8_2 -/Makefile.target/1.121/Sun Aug 6 01:03:44 2006//Trelease_0_8_2 -/README/1.12/Fri Jul 14 12:43:45 2006//Trelease_0_8_2 -/README.distrib/1.2/Fri Jul 14 12:43:45 2006//Trelease_0_8_2 -/TODO/1.39/Sun Aug 6 01:03:44 2006//Trelease_0_8_2 -/VERSION/1.29/Sun Aug 6 01:03:44 2006//Trelease_0_8_2 -/a.out.h/1.2/Fri Jul 14 12:43:45 2006//Trelease_0_8_2 -/aes.c/1.1/Thu Jul 13 09:23:22 2006//Trelease_0_8_2 -/aes.h/1.1/Thu Jul 13 09:23:22 2006//Trelease_0_8_2 -/alpha-dis.c/1.3/Sun Aug 6 01:03:45 2006//Trelease_0_8_2 -/alpha.ld/1.1/Sun Aug 6 01:03:45 2006//Trelease_0_8_2 -/arm-dis.c/1.3/Sun Aug 6 01:03:45 2006//Trelease_0_8_2 -/arm.ld/1.1/Sun Aug 6 01:03:45 2006//Trelease_0_8_2 -/block-bochs.c/1.1/Sun Aug 6 00:55:02 2006//Trelease_0_8_2 -/block-cloop.c/1.3/Sun Aug 6 00:55:02 2006//Trelease_0_8_2 -/block-cow.c/1.6/Sun Aug 6 01:03:45 2006//Trelease_0_8_2 -/block-dmg.c/1.4/Fri Jul 14 12:43:45 2006//Trelease_0_8_2 -/block-qcow.c/1.7/Sun Aug 6 01:03:45 2006//Trelease_0_8_2 -/block-vmdk.c/1.8/Sun Aug 6 01:03:45 2006//Trelease_0_8_2 -/block-vpc.c/1.3/Sun Aug 6 01:03:45 2006//Trelease_0_8_2 -/block-vvfat.c/1.6/Sun Aug 6 01:03:45 2006//Trelease_0_8_2 -/block.c/1.28/Sun Aug 6 01:03:45 2006//Trelease_0_8_2 -/block_int.h/1.5/Sun Aug 6 01:03:45 2006//Trelease_0_8_2 -/bswap.h/1.5/Thu Jul 13 09:23:22 2006//Trelease_0_8_2 -/cocoa.m/1.10/Sun Aug 6 01:03:45 2006//Trelease_0_8_2 -/configure/1.110/Sun Aug 6 01:03:45 2006//Trelease_0_8_2 -/console.c/1.8/Sun Aug 6 01:03:45 2006//Trelease_0_8_2 -/cpu-all.h/1.57/Sun Aug 6 01:03:45 2006//Trelease_0_8_2 -/cpu-defs.h/1.16/Sun Aug 6 01:03:45 2006//Trelease_0_8_2 -/cpu-exec.c/1.83/Sun Aug 6 01:03:45 2006//Trelease_0_8_2 -/dis-asm.h/1.11/Fri Jul 14 12:43:45 2006//Trelease_0_8_2 -/disas.c/1.31/Sun Aug 6 01:03:45 2006//Trelease_0_8_2 -/disas.h/1.7/Sun Aug 6 00:55:03 2006//Trelease_0_8_2 -/dyngen-exec.h/1.29/Sun Aug 6 01:03:45 2006//Trelease_0_8_2 -/dyngen-op.h/1.1/Fri Jul 14 12:43:45 2006//Trelease_0_8_2 -/dyngen.c/1.45/Sun Aug 6 01:03:45 2006//Trelease_0_8_2 -/dyngen.h/1.11/Sun Aug 6 01:03:45 2006//Trelease_0_8_2 -/elf.h/1.7/Sun Aug 6 01:03:45 2006//Trelease_0_8_2 -/elf_ops.h/1.3/Fri Jul 14 12:43:45 2006//Trelease_0_8_2 -/exec-all.h/1.48/Sun Aug 6 01:03:45 2006//Trelease_0_8_2 -/exec.c/1.82/Sun Aug 6 01:03:45 2006//Trelease_0_8_2 -/gdbstub.c/1.40/Sun Aug 6 01:03:45 2006//Trelease_0_8_2 -/gdbstub.h/1.2/Fri Jul 14 12:43:45 2006//Trelease_0_8_2 -/i386-dis.c/1.5/Fri Jul 14 12:43:45 2006//Trelease_0_8_2 -/i386-vl.ld/1.3/Fri Jul 14 12:43:45 2006//Trelease_0_8_2 -/i386.ld/1.2/Fri Jul 14 12:43:45 2006//Trelease_0_8_2 -/ia64.ld/1.1/Fri Jul 14 12:43:45 2006//Trelease_0_8_2 -/keymaps.c/1.2/Fri Jul 14 12:43:45 2006//Trelease_0_8_2 -/kqemu.c/1.12/Sun Aug 6 01:03:45 2006//Trelease_0_8_2 -/kqemu.h/1.1/Fri Jul 14 12:43:45 2006//Trelease_0_8_2 -/linux-2.6.9-qemu-fast.patch/1.1/Sun Aug 6 01:03:45 2006//Trelease_0_8_2 -/loader.c/1.3/Sun Aug 6 01:03:45 2006//Trelease_0_8_2 -/m68k-dis.c/1.1/Sun Aug 6 01:03:46 2006//Trelease_0_8_2 -/m68k.ld/1.1/Sun Aug 6 01:03:46 2006//Trelease_0_8_2 -/mips-dis.c/1.4/Sun Aug 6 01:03:47 2006//Trelease_0_8_2 -/monitor.c/1.54/Sun Aug 6 01:03:47 2006//Trelease_0_8_2 -/osdep.c/1.11/Sun Aug 6 01:03:47 2006//Trelease_0_8_2 -/osdep.h/1.6/Sun Aug 6 01:03:47 2006//Trelease_0_8_2 -/ppc-dis.c/1.7/Sun Aug 6 01:03:48 2006//Trelease_0_8_2 -/ppc.ld/1.2/Sun Aug 6 01:03:48 2006//Trelease_0_8_2 -/qemu-binfmt-conf.sh/1.4/Fri Jul 14 12:43:46 2006//Trelease_0_8_2 -/qemu-doc.texi/1.100/Sun Aug 6 01:03:48 2006//Trelease_0_8_2 -/qemu-img.c/1.10/Sun Aug 6 01:03:48 2006//Trelease_0_8_2 -/qemu-img.texi/1.2/Fri Jul 14 12:43:46 2006//Trelease_0_8_2 -/qemu-tech.texi/1.9/Fri Jul 14 12:43:46 2006//Trelease_0_8_2 -/qemu_socket.h/1.1/Fri Jul 14 12:43:46 2006//Trelease_0_8_2 -/readline.c/1.1/Fri Jul 14 12:43:46 2006//Trelease_0_8_2 -/s390.ld/1.1/Sun Aug 6 01:03:48 2006//Trelease_0_8_2 -/sdl.c/1.29/Sun Aug 6 01:03:48 2006//Trelease_0_8_2 -/sdl_keysym.h/1.3/Fri Jul 14 12:43:46 2006//Trelease_0_8_2 -/sh4-dis.c/1.1/Sun Aug 6 01:03:48 2006//Trelease_0_8_2 -/softmmu_exec.h/1.1/Fri Jul 14 12:43:46 2006//Trelease_0_8_2 -/softmmu_header.h/1.13/Fri Jul 14 12:43:46 2006//Trelease_0_8_2 -/softmmu_template.h/1.16/Fri Jul 14 12:43:46 2006//Trelease_0_8_2 -/sparc-dis.c/1.3/Sun Aug 6 01:03:49 2006//Trelease_0_8_2 -/sparc.ld/1.1/Sun Aug 6 01:03:49 2006//Trelease_0_8_2 -/tap-win32.c/1.3/Sun Aug 6 01:03:49 2006//Trelease_0_8_2 -/texi2pod.pl/1.1/Fri Jul 14 12:43:46 2006//Trelease_0_8_2 -/thunk.c/1.6/Fri Jul 14 12:43:46 2006//Trelease_0_8_2 -/thunk.h/1.13/Thu Jul 13 09:23:22 2006//Trelease_0_8_2 -/translate-all.c/1.14/Fri Jul 14 12:43:46 2006//Trelease_0_8_2 -/translate-op.c/1.1/Fri Jul 14 12:43:46 2006//Trelease_0_8_2 -/usb-linux.c/1.8/Sun Aug 6 01:03:49 2006//Trelease_0_8_2 -/vgafont.h/1.1/Thu Jul 13 09:23:22 2006//Trelease_0_8_2 -/vl.c/1.202/Sun Aug 6 01:03:49 2006//Trelease_0_8_2 -/vl.h/1.136/Sun Aug 6 01:03:49 2006//Trelease_0_8_2 -/vnc.c/1.7/Sun Aug 6 01:03:49 2006//Trelease_0_8_2 -/vnc_keysym.h/1.1/Fri Jul 14 12:43:46 2006//Trelease_0_8_2 -/vnchextile.h/1.2/Sun Aug 6 01:03:49 2006//Trelease_0_8_2 -/x86_64.ld/1.1/Thu Jul 13 09:23:22 2006//Trelease_0_8_2 +D/target-sparc//// diff -r d2ef85c6bf84 -r 00618037d37d tools/ioemu/.CVS/Entries.Log --- a/tools/ioemu/.CVS/Entries.Log Tue May 08 10:38:06 2007 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,7 +0,0 @@ -A D/linux-user//// -A D/slirp//// -A D/target-arm//// -A D/target-mips//// -A D/target-ppc//// -A D/target-sh4//// -A D/target-sparc//// diff -r d2ef85c6bf84 -r 00618037d37d tools/ioemu/.CVS/Tag --- a/tools/ioemu/.CVS/Tag Tue May 08 10:38:06 2007 +0100 +++ b/tools/ioemu/.CVS/Tag Wed May 09 14:17:15 2007 +0100 @@ -1,1 +1,1 @@ Nrelease_0_8_2 -Nrelease_0_8_2 +Nrelease_0_9_0 diff -r d2ef85c6bf84 -r 00618037d37d tools/ioemu/.cvsignore --- a/tools/ioemu/.cvsignore Tue May 08 10:38:06 2007 +0100 +++ b/tools/ioemu/.cvsignore Wed May 09 14:17:15 2007 +0100 @@ -1,14 +1,16 @@ arm-user -arm-user +arm-linux-user arm-softmmu -armeb-user +armeb-linux-user config-host.* dyngen i386 i386-softmmu -i386-user +i386-darwin-user +i386-linux-user ppc-softmmu ppc64-softmmu -ppc-user +ppc-darwin-user +ppc-linux-user qemu-doc.html qemu-tech.html qemu-doc.info @@ -17,18 +19,19 @@ qemu.pod qemu.pod qemu-img.1 qemu-img.pod -sparc-user +sparc-linux-user qemu-img sparc-softmmu x86_64-softmmu -sparc64-user +sparc64-linux-user sparc64-softmmu mips-softmmu mipsel-softmmu -mips-user -mipsel-user +mips-linux-user +mipsel-linux-user +m68k-linux-user .gdbinit -sh4-user +sh4-linux-user sh4-softmmu *.aux *.cp diff -r d2ef85c6bf84 -r 00618037d37d tools/ioemu/Changelog --- a/tools/ioemu/Changelog Tue May 08 10:38:06 2007 +0100 +++ b/tools/ioemu/Changelog Wed May 09 14:17:15 2007 +0100 @@ -1,3 +1,21 @@ version 0.8.2: +version 0.9.0: + + - Support for relative paths in backing files for disk images + - Async file I/O API + - New qcow2 disk image format + - Support of multiple VM snapshots + - Linux: specific host CDROM and floppy support + - SMM support + - Moved PCI init, MP table init and ACPI table init to Bochs BIOS + - Support for MIPS32 Release 2 instruction set (Thiemo Seufer) + - MIPS Malta system emulation (Aurelien Jarno, Stefan Weil) + - Darwin userspace emulation (Pierre d'Herbemont) + - m68k user support (Paul Brook) + - several x86 and x86_64 emulation fixes + - Mouse relative offset VNC extension (Anthony Liguori) + - PXE boot support (Anthony Liguori) + - '-daemonize' option (Anthony Liguori) + version 0.8.2: - ACPI support diff -r d2ef85c6bf84 -r 00618037d37d tools/ioemu/LICENSE --- a/tools/ioemu/LICENSE Tue May 08 10:38:06 2007 +0100 +++ b/tools/ioemu/LICENSE Wed May 09 14:17:15 2007 +0100 @@ -1,11 +1,14 @@ The following points clarify the QEMU li -The following points clarify the QEMU licenses: +The following points clarify the QEMU license: -1) The QEMU virtual CPU core library (libqemu.a) and the QEMU PC - system emulator are released under the GNU Lesser General Public - License. +1) QEMU as a whole is released under the GNU General Public License -2) The Linux user mode QEMU emulator is released under the GNU General - Public License. +2) Parts of QEMU have specific licenses which are compatible with the +GNU General Public License. Hence each source file contains its own +licensing information. + +In particular, the QEMU virtual CPU core library (libqemu.a) is +released under the GNU Lesser General Public License. Many hardware +device emulation sources are released under the BSD license. 3) QEMU is a trademark of Fabrice Bellard. diff -r d2ef85c6bf84 -r 00618037d37d tools/ioemu/Makefile --- a/tools/ioemu/Makefile Tue May 08 10:38:06 2007 +0100 +++ b/tools/ioemu/Makefile Wed May 09 14:17:15 2007 +0100 @@ -8,24 +8,31 @@ include $(XEN_ROOT)/tools/Rules.mk .PHONY: all clean distclean dvi info install install-doc tar tarbin \ speed test test2 html dvi info -CFLAGS+=-Wall -O2 -g -fno-strict-aliasing -I. -ifdef CONFIG_DARWIN -CFLAGS+= -mdynamic-no-pic +BASE_CFLAGS= +BASE_LDFLAGS= + +BASE_CFLAGS += $(OS_CFLAGS) +ifeq ($(ARCH),sparc) +BASE_CFLAGS += -mcpu=ultrasparc endif -ifeq ($(ARCH),sparc) -CFLAGS+=-mcpu=ultrasparc -endif -LDFLAGS=-g +CPPFLAGS += -I. -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE LIBS= -DEFINES+=-D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE TOOLS=qemu-img$(EXESUF) ifdef CONFIG_STATIC -LDFLAGS+=-static +BASE_LDFLAGS += -static endif ifdef BUILD_DOCS DOCS=qemu-doc.html qemu-tech.html qemu.1 qemu-img.1 else DOCS= +endif + +ifndef CONFIG_DARWIN +ifndef CONFIG_WIN32 +ifndef CONFIG_SOLARIS +LIBS+=-lrt +endif +endif endif TOOLS= @@ -36,12 +43,12 @@ subdir-%: $(MAKE) -C $(subst subdir-,,$@) all recurse-all: $(patsubst %,subdir-%, $(TARGET_DIRS)) - -qemu-img$(EXESUF): qemu-img.c block.c block-cow.c block-qcow.c aes.c block-vmdk.c block-cloop.c block-dmg.c block-bochs.c block-vpc.c block-vvfat.c - $(CC) -DQEMU_TOOL $(CFLAGS) $(LDFLAGS) $(DEFINES) -o $@ $^ -lz $(LIBS) + +qemu-img$(EXESUF): qemu-img.c cutils.c block.c block-raw.c block-cow.c block-qcow.c aes.c block-vmdk.c block-cloop.c block-dmg.c block-bochs.c block-vpc.c block-vvfat.c block-qcow2.c + $(CC) -DQEMU_TOOL $(CFLAGS) $(CPPFLAGS) $(BASE_CFLAGS) $(LDFLAGS) $(BASE_LDFLAGS) -o $@ $^ -lz $(LIBS) dyngen$(EXESUF): dyngen.c - $(HOST_CC) $(CFLAGS) $(DEFINES) -o $@ $^ + $(HOST_CC) $(CFLAGS) $(CPPFLAGS) $(BASE_CFLAGS) -o $@ $^ clean: # avoid old build problems by removing potentially incorrect old files @@ -65,7 +72,7 @@ common de-ch es fo fr-ca hu install-doc: $(DOCS) mkdir -p "$(DESTDIR)$(docdir)" - $(INSTALL_DATA) qemu-doc.html qemu-tech.html "$(DESTDIR)$(docdir)" + $(INSTALL_DATA) -m 644 qemu-doc.html qemu-tech.html "$(DESTDIR)$(docdir)" ifndef CONFIG_WIN32 mkdir -p "$(DESTDIR)$(mandir)/man1" $(INSTALL_DATA) qemu.1 qemu-img.1 "$(DESTDIR)$(mandir)/man1" @@ -76,13 +83,14 @@ install: all $(if $(BUILD_DOCS),install- # $(INSTALL) -m 755 -s $(TOOLS) "$(DESTDIR)$(bindir)" # mkdir -p "$(DESTDIR)$(datadir)" # for x in bios.bin vgabios.bin vgabios-cirrus.bin ppc_rom.bin \ -# video.x openbios-sparc32 linux_boot.bin; do \ -# $(INSTALL_DATA) $(SRC_PATH)/pc-bios/$$x "$(DESTDIR)$(datadir)"; \ +# video.x openbios-sparc32 linux_boot.bin pxe-ne2k_pci.bin \ +# pxe-rtl8139.bin pxe-pcnet.bin; do \ +# $(INSTALL) -m 644 $(SRC_PATH)/pc-bios/$$x "$(DESTDIR)$(datadir)"; \ # done ifndef CONFIG_WIN32 mkdir -p "$(DESTDIR)$(datadir)/keymaps" for x in $(KEYMAPS); do \ - $(INSTALL_DATA) $(SRC_PATH)/keymaps/$$x "$(DESTDIR)$(datadir)/keymaps"; \ + $(INSTALL_DATA) -m 644 $(SRC_PATH)/keymaps/$$x "$(DESTDIR)$(datadir)/keymaps"; \ done endif for d in $(TARGET_DIRS); do \ @@ -125,7 +133,8 @@ dvi: qemu-doc.dvi qemu-tech.dvi html: qemu-doc.html qemu-tech.html -FILE=qemu-$(shell cat VERSION) +VERSION ?= $(shell cat VERSION) +FILE = qemu-$(VERSION) # tar release (use 'make -k tar' on a checkouted tree) tar: @@ -159,6 +168,9 @@ tarbin: $(datadir)/video.x \ $(datadir)/openbios-sparc32 \ $(datadir)/linux_boot.bin \ + $(datadir)/pxe-ne2k_pci.bin \ + $(datadir)/pxe-rtl8139.bin \ + $(datadir)/pxe-pcnet.bin \ $(docdir)/qemu-doc.html \ $(docdir)/qemu-tech.html \ $(mandir)/man1/qemu.1 $(mandir)/man1/qemu-img.1 ) diff -r d2ef85c6bf84 -r 00618037d37d tools/ioemu/Makefile.target --- a/tools/ioemu/Makefile.target Tue May 08 10:38:06 2007 +0100 +++ b/tools/ioemu/Makefile.target Wed May 09 14:17:15 2007 +0100 @@ -15,21 +15,24 @@ endif endif TARGET_PATH=$(SRC_PATH)/target-$(TARGET_BASE_ARCH)$(TARGET_SUB) VPATH=$(SRC_PATH):$(TARGET_PATH):$(SRC_PATH)/hw:$(SRC_PATH)/audio -DEFINES=-I. -I.. -I$(TARGET_PATH) -I$(SRC_PATH) -DEFINES+= -I$(XEN_ROOT)/tools/libxc -DEFINES+= -I$(XEN_ROOT)/tools/xenstore -ifdef CONFIG_USER_ONLY +CPPFLAGS=-I. -I.. -I$(TARGET_PATH) -I$(SRC_PATH) +CPPFLAGS+= -I$(XEN_ROOT)/tools/libxc +CPPFLAGS+= -I$(XEN_ROOT)/tools/xenstore +ifdef CONFIG_DARWIN_USER +VPATH+=:$(SRC_PATH)/darwin-user +CPPFLAGS+=-I$(SRC_PATH)/darwin-user -I$(SRC_PATH)/darwin-user/$(TARGET_ARCH) +endif +ifdef CONFIG_LINUX_USER VPATH+=:$(SRC_PATH)/linux-user -DEFINES+=-I$(SRC_PATH)/linux-user -I$(SRC_PATH)/linux-user/$(TARGET_ARCH) -endif -CFLAGS+=-Wall -O2 -g -fno-strict-aliasing +CPPFLAGS+=-I$(SRC_PATH)/linux-user -I$(SRC_PATH)/linux-user/$(TARGET_ARCH) +endif +BASE_CFLAGS= +BASE_LDFLAGS= SSE2 := $(call cc-option,$(CC),-msse2,) ifeq ($(SSE2),-msse2) CFLAGS += -DUSE_SSE2=1 -msse2 endif -CFLAGS+= $(LOCAL_CFLAGS) #CFLAGS+=-Werror -LDFLAGS=-g LIBS= HELPER_CFLAGS=$(CFLAGS) DYNGEN=../dyngen$(EXESUF) @@ -74,18 +77,20 @@ endif # !CONFIG_USER_ONLY endif # !CONFIG_USER_ONLY ifdef CONFIG_STATIC -LDFLAGS+=-static -endif +BASE_LDFLAGS+=-static +endif + +# We require -O2 to avoid the stack setup prologue in EXIT_TB +OP_CFLAGS = -Wall -O2 -g -fno-strict-aliasing ifeq ($(ARCH),i386) -CFLAGS+=-fomit-frame-pointer -OP_CFLAGS=$(CFLAGS) -mpreferred-stack-boundary=2 +HELPER_CFLAGS+=-fomit-frame-pointer +OP_CFLAGS+=-mpreferred-stack-boundary=2 -fomit-frame-pointer ifeq ($(HAVE_GCC3_OPTIONS),yes) OP_CFLAGS+= -falign-functions=0 -fno-gcse else OP_CFLAGS+= -malign-functions=0 endif - ifdef TARGET_GPROF USE_I386_LD=y endif @@ -93,76 +98,80 @@ USE_I386_LD=y USE_I386_LD=y endif ifdef USE_I386_LD -LDFLAGS+=-Wl,-T,$(SRC_PATH)/i386.ld -else +BASE_LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld +else +ifdef CONFIG_LINUX_USER # WARNING: this LDFLAGS is _very_ tricky : qemu is an ELF shared object # that the kernel ELF loader considers as an executable. I think this # is the simplest way to make it self virtualizable! -LDFLAGS+=-Wl,-shared +BASE_LDFLAGS+=-Wl,-shared +endif endif endif ifeq ($(ARCH),x86_64) -OP_CFLAGS=$(CFLAGS) -falign-functions=0 -LDFLAGS+=-Wl,-T,$(SRC_PATH)/x86_64.ld +BASE_LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld endif ifeq ($(ARCH),ppc) -CFLAGS+= -D__powerpc__ -OP_CFLAGS=$(CFLAGS) -LDFLAGS+=-Wl,-T,$(SRC_PATH)/ppc.ld +CPPFLAGS+= -D__powerpc__ +ifdef CONFIG_LINUX_USER +BASE_LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld +endif endif ifeq ($(ARCH),s390) -OP_CFLAGS=$(CFLAGS) -LDFLAGS+=-Wl,-T,$(SRC_PATH)/s390.ld +BASE_LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld endif ifeq ($(ARCH),sparc) ifeq ($(CONFIG_SOLARIS),yes) -CFLAGS+=-mcpu=ultrasparc -m32 -ffixed-g2 -ffixed-g3 -LDFLAGS+=-m32 -OP_CFLAGS=$(CFLAGS) -fno-delayed-branch -fno-omit-frame-pointer -ffixed-i0 -else -CFLAGS+=-mcpu=ultrasparc -m32 -ffixed-g1 -ffixed-g2 -ffixed-g3 -ffixed-g6 -LDFLAGS+=-m32 -OP_CFLAGS=$(CFLAGS) -fno-delayed-branch -ffixed-i0 +BASE_CFLAGS+=-mcpu=ultrasparc -m32 -ffixed-g2 -ffixed-g3 +BASE_LDFLAGS+=-m32 +OP_CFLAGS+=-fno-delayed-branch -fno-omit-frame-pointer -ffixed-i0 +else +BASE_CFLAGS+=-mcpu=ultrasparc -m32 -ffixed-g1 -ffixed-g2 -ffixed-g3 -ffixed-g6 +BASE_LDFLAGS+=-m32 +OP_CFLAGS+=-fno-delayed-branch -ffixed-i0 HELPER_CFLAGS=$(CFLAGS) -ffixed-i0 -mflat # -static is used to avoid g1/g3 usage by the dynamic linker -LDFLAGS+=-Wl,-T,$(SRC_PATH)/sparc.ld -static +BASE_LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld -static endif endif ifeq ($(ARCH),sparc64) -CFLAGS+=-mcpu=ultrasparc -m64 -ffixed-g1 -ffixed-g4 -ffixed-g5 -ffixed-g7 -LDFLAGS+=-m64 -LDFLAGS+=-Wl,-T,$(SRC_PATH)/sparc64.ld -OP_CFLAGS=$(CFLAGS) -fno-delayed-branch -ffixed-i0 +BASE_CFLAGS+=-mcpu=ultrasparc -m64 -ffixed-g1 -ffixed-g4 -ffixed-g5 -ffixed-g7 +BASE_LDFLAGS+=-m64 +BASE_LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld +OP_CFLAGS+=-mcpu=ultrasparc -m64 -ffixed-g1 -ffixed-g4 -ffixed-g5 -ffixed-g7 -fno-delayed-branch -ffixed-i0 endif ifeq ($(ARCH),alpha) -# -msmall-data is not used because we want two-instruction relocations -# for the constant constructions -OP_CFLAGS=-Wall -O2 -g +# -msmall-data is not used for OP_CFLAGS because we want two-instruction +# relocations for the constant constructions # Ensure there's only a single GP -CFLAGS += -msmall-data -LDFLAGS+=-Wl,-T,$(SRC_PATH)/alpha.ld +BASE_CFLAGS+=-msmall-data +BASE_LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld endif ifeq ($(ARCH),ia64) -CFLAGS += -mno-sdata -OP_CFLAGS=$(CFLAGS) -LDFLAGS+=-Wl,-G0 -Wl,-T,$(SRC_PATH)/ia64.ld +BASE_CFLAGS+=-mno-sdata +OP_CFLAGS+=-mno-sdata +BASE_LDFLAGS+=-Wl,-G0 -Wl,-T,$(SRC_PATH)/$(ARCH).ld endif ifeq ($(ARCH),arm) -OP_CFLAGS=$(CFLAGS) -mno-sched-prolog -fno-omit-frame-pointer -LDFLAGS+=-Wl,-T,$(SRC_PATH)/arm.ld +OP_CFLAGS+=-mno-sched-prolog -fno-omit-frame-pointer +BASE_LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld endif ifeq ($(ARCH),m68k) -OP_CFLAGS=$(CFLAGS) -fomit-frame-pointer -LDFLAGS+=-Wl,-T,m68k.ld +OP_CFLAGS+=-fomit-frame-pointer +BASE_LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld +endif + +ifeq ($(ARCH),mips) +BASE_LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld endif ifeq ($(HAVE_GCC3_OPTIONS),yes) @@ -171,14 +180,20 @@ endif endif ifeq ($(CONFIG_DARWIN),yes) -OP_CFLAGS+= -mdynamic-no-pic LIBS+=-lmx endif +ifdef CONFIG_DARWIN_USER +# Leave some space for the regular program loading zone +BASE_LDFLAGS+=-Wl,-segaddr,__STD_PROG_ZONE,0x1000 -image_base 0x0e000000 +endif + +OP_CFLAGS+=$(OS_CFLAGS) + ######################################################### -DEFINES+=-D_GNU_SOURCE -#-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE +CPPFLAGS+=-D_GNU_SOURCE +# -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE LIBS+=-lm LIBS+=-L../../libxc -lxenctrl -lxenguest LIBS+=-L../../xenstore -lxenstore @@ -195,10 +210,11 @@ endif # profiling code ifdef TARGET_GPROF -LDFLAGS+=-p -main.o: CFLAGS+=-p -endif - +BASE_LDFLAGS+=-p +main.o: BASE_CFLAGS+=-p +endif + +ifdef CONFIG_LINUX_USER OBJS= main.o syscall.o mmap.o signal.o path.o osdep.o thunk.o \ elfload.o linuxload.o ifdef TARGET_HAS_BFLT @@ -213,6 +229,15 @@ nwfpe/fpa11_cpdt.o nwfpe/fpa11_cprt.o nw nwfpe/fpa11_cpdt.o nwfpe/fpa11_cprt.o nwfpe/fpopcode.o nwfpe/single_cpdo.o \ nwfpe/double_cpdo.o nwfpe/extended_cpdo.o arm-semi.o endif +ifeq ($(TARGET_ARCH), m68k) +OBJS+= m68k-sim.o m68k-semi.o +endif +endif #CONFIG_LINUX_USER + +ifdef CONFIG_DARWIN_USER +OBJS= main.o commpage.o machload.o mmap.o osdep.o signal.o syscall.o thunk.o +endif + SRCS:= $(OBJS:.o=.c) OBJS+= libqemu.a @@ -224,7 +249,7 @@ else else LIBOBJS+=fpu/softfloat-native.o endif -DEFINES+=-I$(SRC_PATH)/fpu +CPPFLAGS+=-I$(SRC_PATH)/fpu ifeq ($(TARGET_ARCH), i386) LIBOBJS+=helper.o helper2.o @@ -257,6 +282,10 @@ LIBOBJS+= op_helper.o helper.o LIBOBJS+= op_helper.o helper.o endif +ifeq ($(TARGET_BASE_ARCH), m68k) +LIBOBJS+= helper.o +endif + # NOTE: the disassembler code is only needed for debugging LIBOBJS+=disas.o ifeq ($(findstring i386, $(TARGET_ARCH) $(ARCH)),i386) @@ -304,7 +333,7 @@ all: $(PROGS) all: $(PROGS) $(QEMU_USER): $(OBJS) - $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(LIBS) + $(CC) $(CFLAGS) $(LDFLAGS) $(BASE_LDFLAGS) -o $@ $^ $(LIBS) ifeq ($(ARCH),alpha) # Mark as 32 bit binary, i. e. it will be mapped into the low 31 bit of # the address space (31 bit so sign extending doesn't matter) @@ -312,8 +341,10 @@ endif endif # must use static linking to avoid leaving stuff in virtual address space -VL_OBJS=vl.o osdep.o block.o readline.o monitor.o pci.o console.o -VL_OBJS+=block-cow.o block-qcow.o aes.o block-vmdk.o block-cloop.o block-dmg.o block-bochs.o block-vpc.o block-vvfat.o +VL_OBJS=vl.o osdep.o readline.o monitor.o pci.o console.o isa_mmio.o +VL_OBJS+=cutils.o +VL_OBJS+=block.o block-raw.o +VL_OBJS+=block-cow.o block-qcow.o aes.o block-vmdk.o block-cloop.o block-dmg.o block-bochs.o block-vpc.o block-vvfat.o block-qcow2.o ifdef CONFIG_WIN32 VL_OBJS+=tap-win32.o endif @@ -339,7 +370,7 @@ endif endif ifdef CONFIG_FMOD AUDIODRV += fmodaudio.o -audio.o fmodaudio.o: DEFINES := -I$(CONFIG_FMOD_INC) $(DEFINES) +audio.o fmodaudio.o: CPPFLAGS := -I$(CONFIG_FMOD_INC) $(CPPFLAGS) LIBS += $(CONFIG_FMOD_LIB) endif ifdef CONFIG_ADLIB @@ -365,23 +396,25 @@ VL_OBJS+= fdc.o serial.o pc.o VL_OBJS+= fdc.o serial.o pc.o endif VL_OBJS+= cirrus_vga.o mixeng.o parallel.o acpi.o -VL_OBJS+= usb-uhci.o +VL_OBJS+= usb-uhci.o smbus_eeprom.o VL_OBJS+= piix4acpi.o VL_OBJS+= xenstore.o VL_OBJS+= xen_platform.o VL_OBJS+= tpm_tis.o -DEFINES += -DHAS_AUDIO +CPPFLAGS += -DHAS_AUDIO endif ifeq ($(TARGET_BASE_ARCH), ppc) VL_OBJS+= ppc.o ide.o pckbd.o ps2.o vga.o $(SOUND_HW) dma.o $(AUDIODRV) VL_OBJS+= mc146818rtc.o serial.o i8259.o i8254.o fdc.o m48t59.o VL_OBJS+= ppc_prep.o ppc_chrp.o cuda.o adb.o openpic.o heathrow_pic.o mixeng.o VL_OBJS+= grackle_pci.o prep_pci.o unin_pci.o +CPPFLAGS += -DHAS_AUDIO +endif +ifeq ($(TARGET_ARCH), mips) +VL_OBJS+= mips_r4k.o mips_malta.o mips_timer.o mips_int.o dma.o vga.o serial.o i8254.o i8259.o +VL_OBJS+= ide.o gt64xxx.o pckbd.o ps2.o fdc.o mc146818rtc.o usb-uhci.o acpi.o +VL_OBJS+= piix_pci.o parallel.o mixeng.o cirrus_vga.o $(SOUND_HW) $(AUDIODRV) DEFINES += -DHAS_AUDIO -endif -ifeq ($(TARGET_ARCH), mips) -VL_OBJS+= mips_r4k.o dma.o vga.o serial.o i8254.o i8259.o -#VL_OBJS+= #ide.o pckbd.o fdc.o m48t59.o endif ifeq ($(TARGET_BASE_ARCH), sparc) ifeq ($(TARGET_ARCH), sparc64) @@ -389,14 +422,17 @@ VL_OBJS+= fdc.o mc146818rtc.o serial.o m VL_OBJS+= fdc.o mc146818rtc.o serial.o m48t59.o VL_OBJS+= cirrus_vga.o parallel.o else -VL_OBJS+= sun4m.o tcx.o lance.o iommu.o m48t59.o slavio_intctl.o -VL_OBJS+= slavio_timer.o slavio_serial.o slavio_misc.o fdc.o esp.o +VL_OBJS+= sun4m.o tcx.o pcnet.o iommu.o m48t59.o slavio_intctl.o +VL_OBJS+= slavio_timer.o slavio_serial.o slavio_misc.o fdc.o esp.o sparc32_dma.o +VL_OBJS+= cs4231.o endif endif ifeq ($(TARGET_BASE_ARCH), arm) VL_OBJS+= integratorcp.o versatilepb.o ps2.o smc91c111.o arm_pic.o arm_timer.o VL_OBJS+= arm_boot.o pl011.o pl050.o pl080.o pl110.o pl190.o VL_OBJS+= versatile_pci.o +VL_OBJS+= arm_gic.o realview.o arm_sysctl.o +VL_OBJS+= arm-semi.o endif ifeq ($(TARGET_BASE_ARCH), sh4) VL_OBJS+= shix.o sh7750.o sh7750_regnames.o tc58128.o @@ -405,7 +441,7 @@ VL_OBJS+=gdbstub.o VL_OBJS+=gdbstub.o endif ifdef CONFIG_SDL -VL_OBJS+=sdl.o +VL_OBJS+=sdl.o x_keymap.o endif VL_OBJS+=vnc.o VL_OBJS+=d3des.o @@ -417,7 +453,7 @@ endif endif endif ifdef CONFIG_SLIRP -DEFINES+=-I$(SRC_PATH)/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 @@ -440,7 +476,7 @@ endif endif endif ifdef TARGET_GPROF -vl.o: CFLAGS+=-p +vl.o: BASE_CFLAGS+=-p VL_LDFLAGS+=-p endif @@ -461,25 +497,25 @@ endif $(CC) $(VL_LDFLAGS) -o $@ $^ $(LIBS) $(SDL_LIBS) $(COCOA_LIBS) $(VL_LIBS) cocoa.o: cocoa.m - $(CC) $(CFLAGS) $(DEFINES) -c -o $@ $< + $(CC) $(CFLAGS) $(CPPFLAGS) $(BASE_CFLAGS) -c -o $@ $< sdl.o: sdl.c keymaps.c sdl_keysym.h - $(CC) $(CFLAGS) $(DEFINES) $(SDL_CFLAGS) -c -o $@ $< + $(CC) $(CFLAGS) $(CPPFLAGS) $(SDL_CFLAGS) $(BASE_CFLAGS) -c -o $@ $< vnc.o: vnc.c keymaps.c sdl_keysym.h vnchextile.h - $(CC) $(CFLAGS) $(DEFINES) -c -o $@ $< + $(CC) $(CFLAGS) $(CPPFLAGS) $(BASE_CFLAGS) -c -o $@ $< d3des.o: d3des.c d3des.h $(CC) $(CFLAGS) $(DEFINES) -c -o $@ $< sdlaudio.o: sdlaudio.c - $(CC) $(CFLAGS) $(DEFINES) $(SDL_CFLAGS) -c -o $@ $< + $(CC) $(CFLAGS) $(CPPFLAGS) $(SDL_CFLAGS) $(BASE_CFLAGS) -c -o $@ $< depend: $(SRCS) - $(CC) -MM $(CFLAGS) $(DEFINES) $^ 1>.depend + $(CC) -MM $(CFLAGS) $(CPPFLAGS) $(BASE_CFLAGS) $^ 1>.depend vldepend: $(VL_OBJS:.o=.c) - $(CC) -MM $(CFLAGS) $(DEFINES) $^ 1>.depend + $(CC) -MM $(CFLAGS) $(CPPFLAGS) $(BASE_CFLAGS) $^ 1>.depend # libqemu @@ -503,10 +539,26 @@ gen-op.h: op.o $(DYNGEN) $(DYNGEN) -g -o $@ $< op.o: op.c - $(CC) $(OP_CFLAGS) $(DEFINES) -c -o $@ $< - + $(CC) $(OP_CFLAGS) $(CPPFLAGS) -c -o $@ $< + +# HELPER_CFLAGS is used for all the code compiled with static register +# variables +ifeq ($(TARGET_BASE_ARCH), i386) +# XXX: rename helper.c to op_helper.c helper.o: helper.c - $(CC) $(HELPER_CFLAGS) $(DEFINES) -c -o $@ $< + $(CC) $(HELPER_CFLAGS) $(CPPFLAGS) $(BASE_CFLAGS) -c -o $@ $< +else +op_helper.o: op_helper.c + $(CC) $(HELPER_CFLAGS) $(CPPFLAGS) $(BASE_CFLAGS) -c -o $@ $< +endif + +cpu-exec.o: cpu-exec.c + $(CC) $(HELPER_CFLAGS) $(CPPFLAGS) $(BASE_CFLAGS) -c -o $@ $< + +# Note: this is a workaround. The real fix is to avoid compiling +# cpu_signal_handler() in cpu-exec.c. +signal.o: signal.c + $(CC) $(HELPER_CFLAGS) $(CPPFLAGS) $(BASE_CFLAGS) -c -o $@ $< ifeq ($(TARGET_BASE_ARCH), i386) op.o: op.c opreg_template.h ops_template.h ops_template_mem.h ops_mem.h ops_sse.h @@ -529,18 +581,11 @@ endif endif ifeq ($(TARGET_ARCH), mips) -op.o: op.c op_template.c op_mem.c +op.o: op.c op_template.c fop_template.c op_mem.c op_helper.o: op_helper_mem.c endif loader.o: loader.c elf_ops.h - -acpi.o: acpi.c acpi-dsdt.hex - -ifdef BUILD_ACPI_TABLES -$(SRC_PATH)/hw/acpi-dsdt.hex: acpi-dsdt.dsl - iasl -tc -p $@ $< -endif ifeq ($(TARGET_ARCH), sh4) op.o: op.c op_mem.c cpu.h @@ -555,10 +600,10 @@ endif $(OBJS) $(LIBOBJS) $(VL_OBJS): config.h ../config-host.h %.o: %.c - $(CC) $(CFLAGS) $(DEFINES) -c -o $@ $< + $(CC) $(CFLAGS) $(CPPFLAGS) $(BASE_CFLAGS) -c -o $@ $< %.o: %.S - $(CC) $(DEFINES) -c -o $@ $< + $(CC) $(CPPFLAGS) -c -o $@ $< clean: rm -f *.o *.a *~ $(PROGS) gen-op.h opc.h op.h nwfpe/*.o slirp/*.o fpu/*.o diff -r d2ef85c6bf84 -r 00618037d37d tools/ioemu/README.distrib --- a/tools/ioemu/README.distrib Tue May 08 10:38:06 2007 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,16 +0,0 @@ -Information about the various packages used to build the current qemu -x86 binary distribution: - -* gcc 2.95.2 was used for the build. A glibc 2.1.3 Debian distribution - was used to get most of the binary packages. - -* wine-20020411 tarball - - ./configure --prefix=/usr/local/wine-i386 - - All exe and libs were stripped. Some compile time tools and the - includes were deleted. - -* ldconfig was launched to build the library links: - - qemu-i386 /usr/gnemul/qemu-i386/bin/ldconfig-i386 -C /usr/gnemul/qemu-i386/etc/ld.so.cache diff -r d2ef85c6bf84 -r 00618037d37d tools/ioemu/VERSION --- a/tools/ioemu/VERSION Tue May 08 10:38:06 2007 +0100 +++ b/tools/ioemu/VERSION Wed May 09 14:17:15 2007 +0100 @@ -1,1 +1,1 @@ 0.8.2 -0.8.2 \ No newline at end of file +0.9.0 \ No newline at end of file diff -r d2ef85c6bf84 -r 00618037d37d tools/ioemu/audio/.CVS/Entries --- a/tools/ioemu/audio/.CVS/Entries Tue May 08 10:38:06 2007 +0100 +++ b/tools/ioemu/audio/.CVS/Entries Wed May 09 14:17:15 2007 +0100 @@ -1,20 +1,20 @@ -/alsaaudio.c/1.7/Sun Aug 6 01:03:49 2006//Trelease_0_8_2 -/audio.c/1.14/Sun Aug 6 01:03:50 2006//Trelease_0_8_2 -/audio.h/1.8/Sun Aug 6 01:03:50 2006//Trelease_0_8_2 -/audio_int.h/1.10/Sun Aug 6 01:03:50 2006//Trelease_0_8_2 -/audio_template.h/1.8/Sun Aug 6 01:03:50 2006//Trelease_0_8_2 -/coreaudio.c/1.7/Sun Aug 6 01:03:50 2006//Trelease_0_8_2 -/dsound_template.h/1.4/Sun Aug 6 01:03:50 2006//Trelease_0_8_2 -/dsoundaudio.c/1.3/Sun Aug 6 01:03:50 2006//Trelease_0_8_2 -/fmodaudio.c/1.7/Sun Aug 6 01:03:50 2006//Trelease_0_8_2 -/mixeng.c/1.4/Fri Jul 14 12:43:45 2006//Trelease_0_8_2 -/mixeng.h/1.2/Fri Jul 14 12:43:45 2006//Trelease_0_8_2 -/mixeng_template.h/1.2/Fri Jul 14 12:43:45 2006//Trelease_0_8_2 -/noaudio.c/1.7/Sun Aug 6 01:03:50 2006//Trelease_0_8_2 -/ossaudio.c/1.11/Sun Aug 6 01:03:50 2006//Trelease_0_8_2 -/rate_template.h/1.3/Sun Aug 6 01:03:50 2006//Trelease_0_8_2 -/sdlaudio.c/1.8/Sun Aug 6 01:03:50 2006//Trelease_0_8_2 -/sys-queue.h/1.1/Fri Jul 14 12:43:45 2006//Trelease_0_8_2 -/wavaudio.c/1.8/Sun Aug 6 01:03:50 2006//Trelease_0_8_2 -/wavcapture.c/1.5/Sat Jul 22 17:06:44 2006//Trelease_0_8_2 +/alsaaudio.c/1.7/Fri Jan 5 14:34:35 2007//Trelease_0_9_0 +/audio.c/1.15/Thu May 3 17:17:59 2007//Trelease_0_9_0 +/audio.h/1.8/Fri Jan 5 14:34:35 2007//Trelease_0_9_0 +/audio_int.h/1.10/Fri Jan 5 14:34:35 2007//Trelease_0_9_0 +/audio_template.h/1.8/Fri Jan 5 14:34:35 2007//Trelease_0_9_0 +/coreaudio.c/1.7/Fri Jan 5 14:34:35 2007//Trelease_0_9_0 +/dsound_template.h/1.4/Fri Jan 5 14:34:35 2007//Trelease_0_9_0 +/dsoundaudio.c/1.3/Fri Jan 5 14:34:35 2007//Trelease_0_9_0 +/fmodaudio.c/1.7/Fri Jan 5 14:34:35 2007//Trelease_0_9_0 +/mixeng.c/1.4/Wed Oct 18 10:11:20 2006//Trelease_0_9_0 +/mixeng.h/1.2/Wed Oct 18 10:11:20 2006//Trelease_0_9_0 +/mixeng_template.h/1.2/Wed Oct 18 10:11:20 2006//Trelease_0_9_0 +/noaudio.c/1.7/Fri Jan 5 14:34:35 2007//Trelease_0_9_0 +/ossaudio.c/1.11/Fri Jan 5 14:34:35 2007//Trelease_0_9_0 +/rate_template.h/1.3/Fri Jan 5 14:34:35 2007//Trelease_0_9_0 +/sdlaudio.c/1.8/Fri Jan 5 14:34:35 2007//Trelease_0_9_0 +/sys-queue.h/1.1/Wed Oct 18 10:11:20 2006//Trelease_0_9_0 +/wavaudio.c/1.9/Thu May 3 17:17:59 2007//Trelease_0_9_0 +/wavcapture.c/1.7/Thu May 3 17:17:59 2007//Trelease_0_9_0 D diff -r d2ef85c6bf84 -r 00618037d37d tools/ioemu/audio/.CVS/Tag --- a/tools/ioemu/audio/.CVS/Tag Tue May 08 10:38:06 2007 +0100 +++ b/tools/ioemu/audio/.CVS/Tag Wed May 09 14:17:15 2007 +0100 @@ -1,1 +1,1 @@ Nrelease_0_8_2 -Nrelease_0_8_2 +Nrelease_0_9_0 diff -r d2ef85c6bf84 -r 00618037d37d tools/ioemu/audio/wavaudio.c --- a/tools/ioemu/audio/wavaudio.c Tue May 08 10:38:06 2007 +0100 +++ b/tools/ioemu/audio/wavaudio.c Wed May 09 14:17:15 2007 +0100 @@ -151,7 +151,7 @@ static int wav_init_out (HWVoiceOut *hw, le_store (hdr + 28, hw->info.freq << (bits16 + stereo), 4); le_store (hdr + 32, 1 << (bits16 + stereo), 2); - wav->f = fopen (conf.wav_path, "wb"); + wav->f = qemu_fopen (conf.wav_path, "wb"); if (!wav->f) { dolog ("Failed to open wave file `%s'\nReason: %s\n", conf.wav_path, strerror (errno)); @@ -185,7 +185,7 @@ static void wav_fini_out (HWVoiceOut *hw qemu_fseek (wav->f, 32, SEEK_CUR); qemu_put_buffer (wav->f, dlen, 4); - fclose (wav->f); + qemu_fclose (wav->f); wav->f = NULL; qemu_free (wav->pcm_buf); diff -r d2ef85c6bf84 -r 00618037d37d tools/ioemu/audio/wavcapture.c --- a/tools/ioemu/audio/wavcapture.c Tue May 08 10:38:06 2007 +0100 +++ b/tools/ioemu/audio/wavcapture.c Wed May 09 14:17:15 2007 +0100 @@ -34,22 +34,19 @@ static void wav_destroy (void *opaque) uint32_t datalen = wav->bytes; uint32_t rifflen = datalen + 36; - if (!wav->f) { - return; + if (wav->f) { + le_store (rlen, rifflen, 4); + le_store (dlen, datalen, 4); + + qemu_fseek (wav->f, 4, SEEK_SET); + qemu_put_buffer (wav->f, rlen, 4); + + qemu_fseek (wav->f, 32, SEEK_CUR); + qemu_put_buffer (wav->f, dlen, 4); + qemu_fclose (wav->f); } - - le_store (rlen, rifflen, 4); - le_store (dlen, datalen, 4); - - qemu_fseek (wav->f, 4, SEEK_SET); - qemu_put_buffer (wav->f, rlen, 4); - - qemu_fseek (wav->f, 32, SEEK_CUR); - qemu_put_buffer (wav->f, dlen, 4); - fclose (wav->f); - if (wav->path) { - qemu_free (wav->path); - } + + qemu_free (wav->path); } static void wav_capture (void *opaque, void *buf, int size) @@ -135,7 +132,7 @@ int wav_start_capture (CaptureState *s, le_store (hdr + 28, freq << shift, 4); le_store (hdr + 32, 1 << shift, 2); - wav->f = fopen (path, "wb"); + wav->f = qemu_fopen (path, "wb"); if (!wav->f) { term_printf ("Failed to open wave file `%s'\nReason: %s\n", path, strerror (errno)); @@ -153,6 +150,8 @@ int wav_start_capture (CaptureState *s, cap = AUD_add_capture (NULL, &as, &ops, wav); if (!cap) { term_printf ("Failed to add audio capture\n"); + qemu_free (wav->path); + qemu_fclose (wav->f); qemu_free (wav); return -1; } diff -r d2ef85c6bf84 -r 00618037d37d tools/ioemu/block-bochs.c --- a/tools/ioemu/block-bochs.c Tue May 08 10:38:06 2007 +0100 +++ b/tools/ioemu/block-bochs.c Wed May 09 14:17:15 2007 +0100 @@ -28,7 +28,8 @@ /**************************************************************/ #define HEADER_MAGIC "Bochs Virtual HD Image" -#define HEADER_VERSION 0x00010000 +#define HEADER_VERSION 0x00020000 +#define HEADER_V1 0x00010000 #define HEADER_SIZE 512 #define REDOLOG_TYPE "Redolog" @@ -37,7 +38,7 @@ // not allocated: 0xffffffff // always little-endian -struct bochs_header { +struct bochs_header_v1 { char magic[32]; // "Bochs Virtual HD Image" char type[16]; // "Redolog" char subtype[16]; // "Undoable" / "Volatile" / "Growing" @@ -56,6 +57,27 @@ struct bochs_header { } extra; }; +// always little-endian +struct bochs_header { + char magic[32]; // "Bochs Virtual HD Image" + char type[16]; // "Redolog" + char subtype[16]; // "Undoable" / "Volatile" / "Growing" + uint32_t version; + uint32_t header; // size of header + + union { + struct { + uint32_t catalog; // num of entries + uint32_t bitmap; // bitmap size + uint32_t extent; // extent size + uint32_t reserved; // for ??? + uint64_t disk; // disk size + char padding[HEADER_SIZE - 64 - 8 - 24]; + } redolog; + char padding[HEADER_SIZE - 64 - 8]; + } extra; +}; + typedef struct BDRVBochsState { int fd; @@ -79,21 +101,23 @@ static int bochs_probe(const uint8_t *bu if (!strcmp(bochs->magic, HEADER_MAGIC) && !strcmp(bochs->type, REDOLOG_TYPE) && !strcmp(bochs->subtype, GROWING_TYPE) && - (le32_to_cpu(bochs->version) == HEADER_VERSION)) + ((le32_to_cpu(bochs->version) == HEADER_VERSION) || + (le32_to_cpu(bochs->version) == HEADER_V1))) return 100; return 0; } -static int bochs_open(BlockDriverState *bs, const char *filename) +static int bochs_open(BlockDriverState *bs, const char *filename, int flags) { BDRVBochsState *s = bs->opaque; int fd, i; struct bochs_header bochs; - - fd = open(filename, O_RDWR | O_BINARY | O_LARGEFILE); + struct bochs_header_v1 header_v1; + + fd = open(filename, O_RDWR | O_BINARY); if (fd < 0) { - fd = open(filename, O_RDONLY | O_BINARY | O_LARGEFILE); + fd = open(filename, O_RDONLY | O_BINARY); if (fd < 0) return -1; } @@ -109,11 +133,17 @@ static int bochs_open(BlockDriverState * if (strcmp(bochs.magic, HEADER_MAGIC) || strcmp(bochs.type, REDOLOG_TYPE) || strcmp(bochs.subtype, GROWING_TYPE) || - (le32_to_cpu(bochs.version) != HEADER_VERSION)) { + ((le32_to_cpu(bochs.version) != HEADER_VERSION) && + (le32_to_cpu(bochs.version) != HEADER_V1))) { goto fail; } - bs->total_sectors = le64_to_cpu(bochs.extra.redolog.disk) / 512; + if (le32_to_cpu(bochs.version) == HEADER_V1) { + memcpy(&header_v1, &bochs, sizeof(bochs)); + bs->total_sectors = le64_to_cpu(header_v1.extra.redolog.disk) / 512; + } else { + bs->total_sectors = le64_to_cpu(bochs.extra.redolog.disk) / 512; + } lseek(s->fd, le32_to_cpu(bochs.header), SEEK_SET); diff -r d2ef85c6bf84 -r 00618037d37d tools/ioemu/block-cloop.c --- a/tools/ioemu/block-cloop.c Tue May 08 10:38:06 2007 +0100 +++ b/tools/ioemu/block-cloop.c Wed May 09 14:17:15 2007 +0100 @@ -50,14 +50,14 @@ static int cloop_probe(const uint8_t *bu return 0; } -static int cloop_open(BlockDriverState *bs, const char *filename) +static int cloop_open(BlockDriverState *bs, const char *filename, int flags) { BDRVCloopState *s = bs->opaque; uint32_t offsets_size,max_compressed_block_size=1,i; - s->fd = open(filename, O_RDONLY | O_BINARY | O_LARGEFILE); + s->fd = open(filename, O_RDONLY | O_BINARY); if (s->fd < 0) - return -1; + return -errno; bs->read_only = 1; /* read header */ diff -r d2ef85c6bf84 -r 00618037d37d tools/ioemu/block-cow.c --- a/tools/ioemu/block-cow.c Tue May 08 10:38:06 2007 +0100 +++ b/tools/ioemu/block-cow.c Wed May 09 14:17:15 2007 +0100 @@ -62,7 +62,7 @@ static int cow_probe(const uint8_t *buf, return 0; } -static int cow_open(BlockDriverState *bs, const char *filename) +static int cow_open(BlockDriverState *bs, const char *filename, int flags) { BDRVCowState *s = bs->opaque; int fd; @@ -93,22 +93,6 @@ static int cow_open(BlockDriverState *bs pstrcpy(bs->backing_file, sizeof(bs->backing_file), cow_header.backing_file); -#if 0 - if (cow_header.backing_file[0] != '\0') { - if (stat(cow_header.backing_file, &st) != 0) { - fprintf(stderr, "%s: could not find original disk image '%s'\n", filename, cow_header.backing_file); - goto fail; - } - if (st.st_mtime != be32_to_cpu(cow_header.mtime)) { - fprintf(stderr, "%s: original raw disk image '%s' does not match saved timestamp\n", filename, cow_header.backing_file); - goto fail; - } - fd = open(cow_header.backing_file, O_RDONLY | O_LARGEFILE); - if (fd < 0) - goto fail; - bs->fd = fd; - } -#endif /* mmap the bitmap */ s->cow_bitmap_size = ((bs->total_sectors + 7) >> 3) + sizeof(cow_header); s->cow_bitmap_addr = mmap(get_mmap_addr(s->cow_bitmap_size), @@ -179,7 +163,14 @@ static int cow_read(BlockDriverState *bs if (ret != n * 512) return -1; } else { + if (bs->backing_hd) { + /* read from the base image */ + ret = bdrv_read(bs->backing_hd, sector_num, buf, n); + if (ret < 0) + return -1; + } else { memset(buf, 0, n * 512); + } } nb_sectors -= n; sector_num += n; @@ -220,7 +211,7 @@ static int cow_create(const char *filena if (flags) return -ENOTSUP; - cow_fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE, + cow_fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644); if (cow_fd < 0) return -1; @@ -228,18 +219,23 @@ static int cow_create(const char *filena cow_header.magic = cpu_to_be32(COW_MAGIC); cow_header.version = cpu_to_be32(COW_VERSION); if (image_filename) { + /* Note: if no file, we put a dummy mtime */ + cow_header.mtime = cpu_to_be32(0); + fd = open(image_filename, O_RDONLY | O_BINARY); if (fd < 0) { close(cow_fd); - return -1; + goto mtime_fail; } if (fstat(fd, &st) != 0) { close(fd); - return -1; + goto mtime_fail; } close(fd); cow_header.mtime = cpu_to_be32(st.st_mtime); - realpath(image_filename, cow_header.backing_file); + mtime_fail: + pstrcpy(cow_header.backing_file, sizeof(cow_header.backing_file), + image_filename); } cow_header.sectorsize = cpu_to_be32(512); cow_header.size = cpu_to_be64(image_sectors * 512); diff -r d2ef85c6bf84 -r 00618037d37d tools/ioemu/block-dmg.c --- a/tools/ioemu/block-dmg.c Tue May 08 10:38:06 2007 +0100 +++ b/tools/ioemu/block-dmg.c Wed May 09 14:17:15 2007 +0100 @@ -73,16 +73,16 @@ static off_t read_uint32(int fd) return be32_to_cpu(buffer); } -static int dmg_open(BlockDriverState *bs, const char *filename) +static int dmg_open(BlockDriverState *bs, const char *filename, int flags) { BDRVDMGState *s = bs->opaque; off_t info_begin,info_end,last_in_offset,last_out_offset; uint32_t count; uint32_t max_compressed_size=1,max_sectors_per_chunk=1,i; - s->fd = open(filename, O_RDONLY | O_BINARY | O_LARGEFILE); + s->fd = open(filename, O_RDONLY | O_BINARY); if (s->fd < 0) - return -1; + return -errno; bs->read_only = 1; s->n_chunks = 0; s->offsets = s->lengths = s->sectors = s->sectorcounts = 0; @@ -93,7 +93,7 @@ dmg_close: close(s->fd); /* open raw instead */ bs->drv=&bdrv_raw; - return bs->drv->bdrv_open(bs,filename); + return bs->drv->bdrv_open(bs, filename, flags); } info_begin=read_off(s->fd); if(info_begin==0) diff -r d2ef85c6bf84 -r 00618037d37d tools/ioemu/block-qcow.c --- a/tools/ioemu/block-qcow.c Tue May 08 10:38:06 2007 +0100 +++ b/tools/ioemu/block-qcow.c Wed May 09 14:17:15 2007 +0100 @@ -1,7 +1,7 @@ /* * Block driver for the QCOW format * - * Copyright (c) 2004 Fabrice Bellard + * 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 @@ -53,7 +53,7 @@ typedef struct QCowHeader { #define L2_CACHE_SIZE 16 typedef struct BDRVQcowState { - int fd; + BlockDriverState *hd; int cluster_bits; int cluster_size; int cluster_sectors; @@ -89,20 +89,16 @@ static int qcow_probe(const uint8_t *buf return 0; } -static int qcow_open(BlockDriverState *bs, const char *filename) -{ - BDRVQcowState *s = bs->opaque; - int fd, len, i, shift; +static int qcow_open(BlockDriverState *bs, const char *filename, int flags) +{ + BDRVQcowState *s = bs->opaque; + int len, i, shift, ret; QCowHeader header; - - fd = open(filename, O_RDWR | O_BINARY | O_LARGEFILE); - if (fd < 0) { - fd = open(filename, O_RDONLY | O_BINARY | O_LARGEFILE); - if (fd < 0) - return -1; - } - s->fd = fd; - if (read(fd, &header, sizeof(header)) != sizeof(header)) + + ret = bdrv_file_open(&s->hd, filename, flags); + if (ret < 0) + return ret; + if (bdrv_pread(s->hd, 0, &header, sizeof(header)) != sizeof(header)) goto fail; be32_to_cpus(&header.magic); be32_to_cpus(&header.version); @@ -138,8 +134,7 @@ static int qcow_open(BlockDriverState *b s->l1_table = qemu_malloc(s->l1_size * sizeof(uint64_t)); if (!s->l1_table) goto fail; - lseek(fd, s->l1_table_offset, SEEK_SET); - if (read(fd, s->l1_table, s->l1_size * sizeof(uint64_t)) != + if (bdrv_pread(s->hd, s->l1_table_offset, s->l1_table, s->l1_size * sizeof(uint64_t)) != s->l1_size * sizeof(uint64_t)) goto fail; for(i = 0;i < s->l1_size; i++) { @@ -162,8 +157,7 @@ static int qcow_open(BlockDriverState *b len = header.backing_file_size; if (len > 1023) len = 1023; - lseek(fd, header.backing_file_offset, SEEK_SET); - if (read(fd, bs->backing_file, len) != len) + if (bdrv_pread(s->hd, header.backing_file_offset, bs->backing_file, len) != len) goto fail; bs->backing_file[len] = '\0'; } @@ -174,7 +168,7 @@ static int qcow_open(BlockDriverState *b qemu_free(s->l2_cache); qemu_free(s->cluster_cache); qemu_free(s->cluster_data); - close(fd); + bdrv_delete(s->hd); return -1; } @@ -276,14 +270,14 @@ static uint64_t get_cluster_offset(Block if (!allocate) return 0; /* allocate a new l2 entry */ - l2_offset = lseek(s->fd, 0, SEEK_END); + l2_offset = bdrv_getlength(s->hd); /* round to cluster size */ l2_offset = (l2_offset + s->cluster_size - 1) & ~(s->cluster_size - 1); /* update the L1 entry */ s->l1_table[l1_index] = l2_offset; tmp = cpu_to_be64(l2_offset); - lseek(s->fd, s->l1_table_offset + l1_index * sizeof(tmp), SEEK_SET); - if (write(s->fd, &tmp, sizeof(tmp)) != sizeof(tmp)) + if (bdrv_pwrite(s->hd, s->l1_table_offset + l1_index * sizeof(tmp), + &tmp, sizeof(tmp)) != sizeof(tmp)) return 0; new_l2_table = 1; } @@ -309,14 +303,13 @@ static uint64_t get_cluster_offset(Block } } l2_table = s->l2_cache + (min_index << s->l2_bits); - lseek(s->fd, l2_offset, SEEK_SET); if (new_l2_table) { memset(l2_table, 0, s->l2_size * sizeof(uint64_t)); - if (write(s->fd, l2_table, s->l2_size * sizeof(uint64_t)) != + if (bdrv_pwrite(s->hd, l2_offset, l2_table, s->l2_size * sizeof(uint64_t)) != s->l2_size * sizeof(uint64_t)) return 0; } else { - if (read(s->fd, l2_table, s->l2_size * sizeof(uint64_t)) != + if (bdrv_pread(s->hd, l2_offset, l2_table, s->l2_size * sizeof(uint64_t)) != s->l2_size * sizeof(uint64_t)) return 0; } @@ -337,36 +330,35 @@ static uint64_t get_cluster_offset(Block overwritten */ if (decompress_cluster(s, cluster_offset) < 0) return 0; - cluster_offset = lseek(s->fd, 0, SEEK_END); + cluster_offset = bdrv_getlength(s->hd); cluster_offset = (cluster_offset + s->cluster_size - 1) & ~(s->cluster_size - 1); /* write the cluster content */ - lseek(s->fd, cluster_offset, SEEK_SET); - if (write(s->fd, s->cluster_cache, s->cluster_size) != + if (bdrv_pwrite(s->hd, cluster_offset, s->cluster_cache, s->cluster_size) != s->cluster_size) return -1; } else { - cluster_offset = lseek(s->fd, 0, SEEK_END); + cluster_offset = bdrv_getlength(s->hd); if (allocate == 1) { /* round to cluster size */ cluster_offset = (cluster_offset + s->cluster_size - 1) & ~(s->cluster_size - 1); - ftruncate(s->fd, cluster_offset + s->cluster_size); + bdrv_truncate(s->hd, cluster_offset + s->cluster_size); /* if encrypted, we must initialize the cluster content which won't be written */ if (s->crypt_method && (n_end - n_start) < s->cluster_sectors) { uint64_t start_sect; start_sect = (offset & ~(s->cluster_size - 1)) >> 9; - memset(s->cluster_data + 512, 0xaa, 512); + memset(s->cluster_data + 512, 0x00, 512); for(i = 0; i < s->cluster_sectors; i++) { if (i < n_start || i >= n_end) { encrypt_sectors(s, start_sect + i, s->cluster_data, s->cluster_data + 512, 1, 1, &s->aes_encrypt_key); - lseek(s->fd, cluster_offset + i * 512, SEEK_SET); - if (write(s->fd, s->cluster_data, 512) != 512) + if (bdrv_pwrite(s->hd, cluster_offset + i * 512, + s->cluster_data, 512) != 512) return -1; } } @@ -379,8 +371,8 @@ static uint64_t get_cluster_offset(Block /* update L2 table */ tmp = cpu_to_be64(cluster_offset); l2_table[l2_index] = tmp; - lseek(s->fd, l2_offset + l2_index * sizeof(tmp), SEEK_SET); - if (write(s->fd, &tmp, sizeof(tmp)) != sizeof(tmp)) + if (bdrv_pwrite(s->hd, + l2_offset + l2_index * sizeof(tmp), &tmp, sizeof(tmp)) != sizeof(tmp)) return 0; } return cluster_offset; @@ -438,8 +430,7 @@ static int decompress_cluster(BDRVQcowSt if (s->cluster_cache_offset != coffset) { csize = cluster_offset >> (63 - s->cluster_bits); csize &= (s->cluster_size - 1); - lseek(s->fd, coffset, SEEK_SET); - ret = read(s->fd, s->cluster_data, csize); + ret = bdrv_pread(s->hd, coffset, s->cluster_data, csize); if (ret != csize) return -1; if (decompress_buffer(s->cluster_cache, s->cluster_size, @@ -450,6 +441,8 @@ static int decompress_cluster(BDRVQcowSt } return 0; } + +#if 0 static int qcow_read(BlockDriverState *bs, int64_t sector_num, uint8_t *buf, int nb_sectors) @@ -465,14 +458,20 @@ static int qcow_read(BlockDriverState *b if (n > nb_sectors) n = nb_sectors; if (!cluster_offset) { - memset(buf, 0, 512 * n); + if (bs->backing_hd) { + /* read from the base image */ + ret = bdrv_read(bs->backing_hd, sector_num, buf, n); + if (ret < 0) + return -1; + } else { + memset(buf, 0, 512 * n); + } } else if (cluster_offset & QCOW_OFLAG_COMPRESSED) { if (decompress_cluster(s, cluster_offset) < 0) return -1; memcpy(buf, s->cluster_cache + index_in_cluster * 512, 512 * n); } else { - lseek(s->fd, cluster_offset + index_in_cluster * 512, SEEK_SET); - ret = read(s->fd, buf, n * 512); + ret = bdrv_pread(s->hd, cluster_offset + index_in_cluster * 512, buf, n * 512); if (ret != n * 512) return -1; if (s->crypt_method) { @@ -486,6 +485,7 @@ static int qcow_read(BlockDriverState *b } return 0; } +#endif static int qcow_write(BlockDriverState *bs, int64_t sector_num, const uint8_t *buf, int nb_sectors) @@ -504,13 +504,13 @@ static int qcow_write(BlockDriverState * index_in_cluster + n); if (!cluster_offset) return -1; - lseek(s->fd, cluster_offset + index_in_cluster * 512, SEEK_SET); if (s->crypt_method) { encrypt_sectors(s, sector_num, s->cluster_data, buf, n, 1, &s->aes_encrypt_key); - ret = write(s->fd, s->cluster_data, n * 512); + ret = bdrv_pwrite(s->hd, cluster_offset + index_in_cluster * 512, + s->cluster_data, n * 512); } else { - ret = write(s->fd, buf, n * 512); + ret = bdrv_pwrite(s->hd, cluster_offset + index_in_cluster * 512, buf, n * 512); } if (ret != n * 512) return -1; @@ -522,6 +522,209 @@ static int qcow_write(BlockDriverState * return 0; } +typedef struct QCowAIOCB { + BlockDriverAIOCB common; + int64_t sector_num; + uint8_t *buf; + int nb_sectors; + int n; + uint64_t cluster_offset; + uint8_t *cluster_data; + BlockDriverAIOCB *hd_aiocb; +} QCowAIOCB; + +static void qcow_aio_read_cb(void *opaque, int ret) +{ + QCowAIOCB *acb = opaque; + BlockDriverState *bs = acb->common.bs; + BDRVQcowState *s = bs->opaque; + int index_in_cluster; + + acb->hd_aiocb = NULL; + if (ret < 0) { + fail: + acb->common.cb(acb->common.opaque, ret); + qemu_aio_release(acb); + return; + } + + redo: + /* post process the read buffer */ + if (!acb->cluster_offset) { + /* nothing to do */ + } else if (acb->cluster_offset & QCOW_OFLAG_COMPRESSED) { + /* nothing to do */ + } else { + if (s->crypt_method) { + encrypt_sectors(s, acb->sector_num, acb->buf, acb->buf, + acb->n, 0, + &s->aes_decrypt_key); + } + } + + acb->nb_sectors -= acb->n; + acb->sector_num += acb->n; + acb->buf += acb->n * 512; + + if (acb->nb_sectors == 0) { + /* request completed */ + acb->common.cb(acb->common.opaque, 0); + qemu_aio_release(acb); + return; + } + + /* prepare next AIO request */ + acb->cluster_offset = get_cluster_offset(bs, acb->sector_num << 9, + 0, 0, 0, 0); + index_in_cluster = acb->sector_num & (s->cluster_sectors - 1); + acb->n = s->cluster_sectors - index_in_cluster; + if (acb->n > acb->nb_sectors) + acb->n = acb->nb_sectors; + + if (!acb->cluster_offset) { + if (bs->backing_hd) { + /* read from the base image */ + acb->hd_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 fail; + } else { + /* Note: in this case, no need to wait */ + memset(acb->buf, 0, 512 * acb->n); + goto redo; + } + } else if (acb->cluster_offset & QCOW_OFLAG_COMPRESSED) { + /* add AIO support for compressed blocks ? */ + if (decompress_cluster(s, acb->cluster_offset) < 0) + 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 fail; + } + acb->hd_aiocb = bdrv_aio_read(s->hd, + (acb->cluster_offset >> 9) + index_in_cluster, + acb->buf, acb->n, qcow_aio_read_cb, acb); + if (acb->hd_aiocb == NULL) + goto fail; + } +} + +static BlockDriverAIOCB *qcow_aio_read(BlockDriverState *bs, + int64_t sector_num, uint8_t *buf, int nb_sectors, + BlockDriverCompletionFunc *cb, void *opaque) +{ + QCowAIOCB *acb; + + 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; +} + +static void qcow_aio_write_cb(void *opaque, int ret) +{ + QCowAIOCB *acb = opaque; + BlockDriverState *bs = acb->common.bs; + BDRVQcowState *s = bs->opaque; + int index_in_cluster; + uint64_t cluster_offset; + const uint8_t *src_buf; + + acb->hd_aiocb = NULL; + + if (ret < 0) { + fail: + acb->common.cb(acb->common.opaque, ret); + qemu_aio_release(acb); + return; + } + + acb->nb_sectors -= acb->n; + acb->sector_num += acb->n; + acb->buf += acb->n * 512; + + if (acb->nb_sectors == 0) { + /* request completed */ + acb->common.cb(acb->common.opaque, 0); + qemu_aio_release(acb); + return; + } + + index_in_cluster = acb->sector_num & (s->cluster_sectors - 1); + acb->n = s->cluster_sectors - index_in_cluster; + if (acb->n > acb->nb_sectors) + acb->n = acb->nb_sectors; + cluster_offset = get_cluster_offset(bs, acb->sector_num << 9, 1, 0, + index_in_cluster, + index_in_cluster + acb->n); + if (!cluster_offset || (cluster_offset & 511) != 0) { + ret = -EIO; + goto fail; + } + if (s->crypt_method) { + if (!acb->cluster_data) { + acb->cluster_data = qemu_mallocz(s->cluster_size); + if (!acb->cluster_data) { + ret = -ENOMEM; + goto fail; + } + } + encrypt_sectors(s, acb->sector_num, acb->cluster_data, acb->buf, + acb->n, 1, &s->aes_encrypt_key); + src_buf = acb->cluster_data; + } else { + src_buf = acb->buf; + } + acb->hd_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 fail; +} + +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; + QCowAIOCB *acb; + + s->cluster_cache_offset = -1; /* disable compressed cache */ + + 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; @@ -529,7 +732,7 @@ static void qcow_close(BlockDriverState qemu_free(s->l2_cache); qemu_free(s->cluster_cache); qemu_free(s->cluster_data); - close(s->fd); + bdrv_delete(s->hd); } static int qcow_create(const char *filename, int64_t total_size, @@ -537,12 +740,9 @@ static int qcow_create(const char *filen { int fd, header_size, backing_filename_len, l1_size, i, shift; QCowHeader header; - char backing_filename[1024]; uint64_t tmp; - struct stat st; - - fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE, - 0644); + + fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644); if (fd < 0) return -1; memset(&header, 0, sizeof(header)); @@ -552,28 +752,11 @@ static int qcow_create(const char *filen header_size = sizeof(header); backing_filename_len = 0; if (backing_file) { - if (strcmp(backing_file, "fat:")) { - const char *p; - /* XXX: this is a hack: we do not attempt to check for URL - like syntax */ - p = strchr(backing_file, ':'); - if (p && (p - backing_file) >= 2) { - /* URL like but exclude "c:" like filenames */ - pstrcpy(backing_filename, sizeof(backing_filename), - backing_file); - } else { - realpath(backing_file, backing_filename); - if (stat(backing_filename, &st) != 0) { - return -1; - } - } - header.backing_file_offset = cpu_to_be64(header_size); - backing_filename_len = strlen(backing_filename); - header.backing_file_size = cpu_to_be32(backing_filename_len); - header_size += backing_filename_len; - } else - backing_file = NULL; - header.mtime = cpu_to_be32(st.st_mtime); + header.backing_file_offset = cpu_to_be64(header_size); + backing_filename_len = strlen(backing_file); + header.backing_file_size = cpu_to_be32(backing_filename_len); + header_size += backing_filename_len; + header.mtime = cpu_to_be32(0); header.cluster_bits = 9; /* 512 byte cluster to avoid copying unmodifyed sectors */ header.l2_bits = 12; /* 32 KB L2 tables */ @@ -595,7 +778,7 @@ static int qcow_create(const char *filen /* write all the data */ write(fd, &header, sizeof(header)); if (backing_file) { - write(fd, backing_filename, backing_filename_len); + write(fd, backing_file, backing_filename_len); } lseek(fd, header_size, SEEK_SET); tmp = 0; @@ -606,16 +789,18 @@ static int qcow_create(const char *filen return 0; } -int qcow_make_empty(BlockDriverState *bs) +static int qcow_make_empty(BlockDriverState *bs) { BDRVQcowState *s = bs->opaque; uint32_t l1_length = s->l1_size * sizeof(uint64_t); + int ret; memset(s->l1_table, 0, l1_length); - lseek(s->fd, s->l1_table_offset, SEEK_SET); - if (write(s->fd, s->l1_table, l1_length) < 0) + if (bdrv_pwrite(s->hd, s->l1_table_offset, s->l1_table, l1_length) < 0) return -1; - ftruncate(s->fd, s->l1_table_offset + l1_length); + ret = bdrv_truncate(s->hd, s->l1_table_offset + l1_length); + if (ret < 0) + return ret; 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)); @@ -624,18 +809,10 @@ int qcow_make_empty(BlockDriverState *bs return 0; } -int qcow_get_cluster_size(BlockDriverState *bs) -{ - BDRVQcowState *s = bs->opaque; - if (bs->drv != &bdrv_qcow) - return -1; - return s->cluster_size; -} - /* XXX: put compressed sectors first, then all the cluster aligned tables to avoid losing bytes in alignment */ -int qcow_compress_cluster(BlockDriverState *bs, int64_t sector_num, - const uint8_t *buf) +static int qcow_write_compressed(BlockDriverState *bs, int64_t sector_num, + const uint8_t *buf, int nb_sectors) { BDRVQcowState *s = bs->opaque; z_stream strm; @@ -643,8 +820,8 @@ int qcow_compress_cluster(BlockDriverSta uint8_t *out_buf; uint64_t cluster_offset; - if (bs->drv != &bdrv_qcow) - return -1; + if (nb_sectors != s->cluster_sectors) + return -EINVAL; out_buf = qemu_malloc(s->cluster_size + (s->cluster_size / 1000) + 128); if (!out_buf) @@ -682,8 +859,7 @@ int qcow_compress_cluster(BlockDriverSta cluster_offset = get_cluster_offset(bs, sector_num << 9, 2, out_len, 0, 0); cluster_offset &= s->cluster_offset_mask; - lseek(s->fd, cluster_offset, SEEK_SET); - if (write(s->fd, out_buf, out_len) != out_len) { + if (bdrv_pwrite(s->hd, cluster_offset, out_buf, out_len) != out_len) { qemu_free(out_buf); return -1; } @@ -696,7 +872,14 @@ static void qcow_flush(BlockDriverState static void qcow_flush(BlockDriverState *bs) { BDRVQcowState *s = bs->opaque; - fsync(s->fd); + bdrv_flush(s->hd); +} + +static int qcow_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) +{ + BDRVQcowState *s = bs->opaque; + bdi->cluster_size = s->cluster_size; + return 0; } BlockDriver bdrv_qcow = { @@ -704,14 +887,19 @@ BlockDriver bdrv_qcow = { sizeof(BDRVQcowState), qcow_probe, qcow_open, - qcow_read, - qcow_write, + NULL, + NULL, qcow_close, qcow_create, qcow_flush, qcow_is_allocated, qcow_set_key, - qcow_make_empty + qcow_make_empty, + + .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, }; - - diff -r d2ef85c6bf84 -r 00618037d37d tools/ioemu/block-qcow2.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/ioemu/block-qcow2.c Wed May 09 14:17:15 2007 +0100 @@ -0,0 +1,2246 @@ +/* + * 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 "vl.h" +#include "block_int.h" +#include <zlib.h> +#include "aes.h" +#include <assert.h> + +/* + Differences with QCOW: + + - Support for multiple incremental snapshots. + - Memory management by reference counts. + - Clusters which have a reference count of one have the bit + QCOW_OFLAG_COPIED to optimize write performance. + - Size of compressed clusters is stored in sectors to reduce bit usage + in the cluster offsets. + - Support for storing additional data (such as the VM state) in the + snapshots. + - If a backing store is used, the cluster size is not constrained + (could be backported to QCOW). + - L2 tables have always a size of one cluster. +*/ + +//#define DEBUG_ALLOC +//#define DEBUG_ALLOC2 + +#define QCOW_MAGIC (('Q' << 24) | ('F' << 16) | ('I' << 8) | 0xfb) +#define QCOW_VERSION 2 + +#define QCOW_CRYPT_NONE 0 +#define QCOW_CRYPT_AES 1 + +/* 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 */ + +#ifndef offsetof +#define offsetof(type, field) ((size_t) &((type *)0)->field) +#endif + +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 __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; + +#define L2_CACHE_SIZE 16 + +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; + + 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; + +static int decompress_cluster(BDRVQcowState *s, uint64_t cluster_offset); +static int qcow_read(BlockDriverState *bs, int64_t sector_num, + uint8_t *buf, int nb_sectors); +static int qcow_read_snapshots(BlockDriverState *bs); +static void qcow_free_snapshots(BlockDriverState *bs); +static int refcount_init(BlockDriverState *bs); +static void refcount_close(BlockDriverState *bs); +static int get_refcount(BlockDriverState *bs, int64_t cluster_index); +static int update_cluster_refcount(BlockDriverState *bs, + int64_t cluster_index, + int addend); +static void update_refcount(BlockDriverState *bs, + int64_t offset, int64_t length, + int addend); +static int64_t alloc_clusters(BlockDriverState *bs, int64_t size); +static int64_t alloc_bytes(BlockDriverState *bs, int size); +static void free_clusters(BlockDriverState *bs, + int64_t offset, int64_t size); +#ifdef DEBUG_ALLOC +static void check_refcounts(BlockDriverState *bs); +#endif + +static int qcow_probe(const uint8_t *buf, int buf_size, const char *filename) +{ + const QCowHeader *cow_header = (const void *)buf; + + if (buf_size >= sizeof(QCowHeader) && + be32_to_cpu(cow_header->magic) == QCOW_MAGIC && + be32_to_cpu(cow_header->version) == QCOW_VERSION) + return 100; + else + return 0; +} + +static int qcow_open(BlockDriverState *bs, const char *filename, int flags) +{ + BDRVQcowState *s = bs->opaque; + int len, i, shift, ret; + QCowHeader header; + + ret = bdrv_file_open(&s->hd, filename, flags); + if (ret < 0) + return ret; + if (bdrv_pread(s->hd, 0, &header, sizeof(header)) != sizeof(header)) + goto fail; + be32_to_cpus(&header.magic); + be32_to_cpus(&header.version); + be64_to_cpus(&header.backing_file_offset); + be32_to_cpus(&header.backing_file_size); + be64_to_cpus(&header.size); + be32_to_cpus(&header.cluster_bits); + be32_to_cpus(&header.crypt_method); + be64_to_cpus(&header.l1_table_offset); + be32_to_cpus(&header.l1_size); + be64_to_cpus(&header.refcount_table_offset); + be32_to_cpus(&header.refcount_table_clusters); + be64_to_cpus(&header.snapshots_offset); + be32_to_cpus(&header.nb_snapshots); + + if (header.magic != QCOW_MAGIC || header.version != QCOW_VERSION) + goto fail; + if (header.size <= 1 || + header.cluster_bits < 9 || + header.cluster_bits > 16) + goto fail; + if (header.crypt_method > QCOW_CRYPT_AES) + goto fail; + s->crypt_method_header = header.crypt_method; + if (s->crypt_method_header) + bs->encrypted = 1; + s->cluster_bits = header.cluster_bits; + s->cluster_size = 1 << s->cluster_bits; + s->cluster_sectors = 1 << (s->cluster_bits - 9); + s->l2_bits = s->cluster_bits - 3; /* L2 is always one cluster */ + s->l2_size = 1 << s->l2_bits; + bs->total_sectors = header.size / 512; + s->csize_shift = (62 - (s->cluster_bits - 8)); + s->csize_mask = (1 << (s->cluster_bits - 8)) - 1; + s->cluster_offset_mask = (1LL << s->csize_shift) - 1; + s->refcount_table_offset = header.refcount_table_offset; + s->refcount_table_size = + header.refcount_table_clusters << (s->cluster_bits - 3); + + s->snapshots_offset = header.snapshots_offset; + s->nb_snapshots = header.nb_snapshots; + + /* read the level 1 table */ + s->l1_size = header.l1_size; + shift = s->cluster_bits + s->l2_bits; + s->l1_vm_state_index = (header.size + (1LL << shift) - 1) >> shift; + /* the L1 table must contain at least enough entries to put + header.size bytes */ + if (s->l1_size < s->l1_vm_state_index) + goto fail; + s->l1_table_offset = header.l1_table_offset; + s->l1_table = qemu_malloc(s->l1_size * sizeof(uint64_t)); + if (!s->l1_table) + goto fail; + if (bdrv_pread(s->hd, s->l1_table_offset, s->l1_table, s->l1_size * sizeof(uint64_t)) != + s->l1_size * sizeof(uint64_t)) + goto fail; + for(i = 0;i < s->l1_size; i++) { + be64_to_cpus(&s->l1_table[i]); + } + /* alloc L2 cache */ + s->l2_cache = qemu_malloc(s->l2_size * L2_CACHE_SIZE * sizeof(uint64_t)); + if (!s->l2_cache) + goto fail; + s->cluster_cache = qemu_malloc(s->cluster_size); + if (!s->cluster_cache) + goto fail; + /* one more sector for decompressed data alignment */ + s->cluster_data = qemu_malloc(s->cluster_size + 512); + if (!s->cluster_data) + goto fail; + s->cluster_cache_offset = -1; + + if (refcount_init(bs) < 0) + goto fail; + + /* read the backing file name */ + if (header.backing_file_offset != 0) { + len = header.backing_file_size; + if (len > 1023) + len = 1023; + if (bdrv_pread(s->hd, header.backing_file_offset, bs->backing_file, len) != len) + goto fail; + bs->backing_file[len] = '\0'; + } + if (qcow_read_snapshots(bs) < 0) + goto fail; + +#ifdef DEBUG_ALLOC + check_refcounts(bs); +#endif + return 0; + + fail: + qcow_free_snapshots(bs); + refcount_close(bs); + qemu_free(s->l1_table); + qemu_free(s->l2_cache); + qemu_free(s->cluster_cache); + qemu_free(s->cluster_data); + bdrv_delete(s->hd); + return -1; +} + +static int qcow_set_key(BlockDriverState *bs, const char *key) +{ + BDRVQcowState *s = bs->opaque; + uint8_t keybuf[16]; + int len, i; + + memset(keybuf, 0, 16); + len = strlen(key); + if (len > 16) + len = 16; + /* XXX: we could compress the chars to 7 bits to increase + entropy */ + for(i = 0;i < len;i++) { + keybuf[i] = key[i]; + } + s->crypt_method = s->crypt_method_header; + + if (AES_set_encrypt_key(keybuf, 128, &s->aes_encrypt_key) != 0) + return -1; + if (AES_set_decrypt_key(keybuf, 128, &s->aes_decrypt_key) != 0) + return -1; +#if 0 + /* test */ + { + uint8_t in[16]; + uint8_t out[16]; + uint8_t tmp[16]; + for(i=0;i<16;i++) + in[i] = i; + AES_encrypt(in, tmp, &s->aes_encrypt_key); + AES_decrypt(tmp, out, &s->aes_decrypt_key); + for(i = 0; i < 16; i++) + printf(" %02x", tmp[i]); + printf("\n"); + for(i = 0; i < 16; i++) + printf(" %02x", out[i]); + printf("\n"); + } +#endif + return 0; +} + +/* The crypt function is compatible with the linux cryptoloop + algorithm for < 4 GB images. NOTE: out_buf == in_buf is + supported */ +static void 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 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) { + 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; +} + +static void 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; +} + +static int64_t align_offset(int64_t offset, int n) +{ + offset = (offset + n - 1) & ~(n - 1); + return offset; +} + +static int grow_l1_table(BlockDriverState *bs, int min_size) +{ + BDRVQcowState *s = bs->opaque; + int new_l1_size, new_l1_size2, ret, i; + uint64_t *new_l1_table; + uint64_t new_l1_table_offset; + uint64_t data64; + uint32_t data32; + + new_l1_size = s->l1_size; + if (min_size <= new_l1_size) + return 0; + 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(new_l1_size2); + if (!new_l1_table) + return -ENOMEM; + memcpy(new_l1_table, s->l1_table, s->l1_size * sizeof(uint64_t)); + + /* write new table (align to cluster) */ + new_l1_table_offset = alloc_clusters(bs, new_l1_size2); + + for(i = 0; i < s->l1_size; i++) + new_l1_table[i] = cpu_to_be64(new_l1_table[i]); + ret = bdrv_pwrite(s->hd, new_l1_table_offset, new_l1_table, new_l1_size2); + if (ret != new_l1_size2) + goto fail; + for(i = 0; i < s->l1_size; i++) + new_l1_table[i] = be64_to_cpu(new_l1_table[i]); + + /* set new table */ + data64 = cpu_to_be64(new_l1_table_offset); + if (bdrv_pwrite(s->hd, offsetof(QCowHeader, l1_table_offset), + &data64, sizeof(data64)) != sizeof(data64)) + goto fail; + data32 = cpu_to_be32(new_l1_size); + if (bdrv_pwrite(s->hd, offsetof(QCowHeader, l1_size), + &data32, sizeof(data32)) != sizeof(data32)) + goto fail; + qemu_free(s->l1_table); + free_clusters(bs, s->l1_table_offset, s->l1_size * sizeof(uint64_t)); + s->l1_table_offset = new_l1_table_offset; + s->l1_table = new_l1_table; + s->l1_size = new_l1_size; + return 0; + fail: + qemu_free(s->l1_table); + return -EIO; +} + +/* 'allocate' is: + * + * 0 not to allocate. + * + * 1 to allocate a normal cluster (for sector indexes 'n_start' to + * 'n_end') + * + * 2 to allocate a compressed cluster of size + * 'compressed_size'. 'compressed_size' must be > 0 and < + * cluster_size + * + * return 0 if not allocated. + */ +static uint64_t get_cluster_offset(BlockDriverState *bs, + uint64_t offset, int allocate, + int compressed_size, + int n_start, int n_end) +{ + BDRVQcowState *s = bs->opaque; + int min_index, i, j, l1_index, l2_index, ret; + uint64_t l2_offset, *l2_table, cluster_offset, tmp, old_l2_offset; + + l1_index = offset >> (s->l2_bits + s->cluster_bits); + if (l1_index >= s->l1_size) { + /* outside l1 table is allowed: we grow the table if needed */ + if (!allocate) + return 0; + if (grow_l1_table(bs, l1_index + 1) < 0) + return 0; + } + l2_offset = s->l1_table[l1_index]; + if (!l2_offset) { + if (!allocate) + return 0; + l2_allocate: + old_l2_offset = l2_offset; + /* allocate a new l2 entry */ + l2_offset = alloc_clusters(bs, s->l2_size * sizeof(uint64_t)); + /* update the L1 entry */ + s->l1_table[l1_index] = l2_offset | QCOW_OFLAG_COPIED; + tmp = cpu_to_be64(l2_offset | QCOW_OFLAG_COPIED); + if (bdrv_pwrite(s->hd, s->l1_table_offset + l1_index * sizeof(tmp), + &tmp, sizeof(tmp)) != sizeof(tmp)) + return 0; + min_index = l2_cache_new_entry(bs); + l2_table = s->l2_cache + (min_index << s->l2_bits); + + if (old_l2_offset == 0) { + memset(l2_table, 0, s->l2_size * sizeof(uint64_t)); + } else { + if (bdrv_pread(s->hd, old_l2_offset, + l2_table, s->l2_size * sizeof(uint64_t)) != + s->l2_size * sizeof(uint64_t)) + return 0; + } + if (bdrv_pwrite(s->hd, l2_offset, + l2_table, s->l2_size * sizeof(uint64_t)) != + s->l2_size * sizeof(uint64_t)) + return 0; + } else { + if (!(l2_offset & QCOW_OFLAG_COPIED)) { + if (allocate) { + free_clusters(bs, l2_offset, s->l2_size * sizeof(uint64_t)); + goto l2_allocate; + } + } else { + l2_offset &= ~QCOW_OFLAG_COPIED; + } + for(i = 0; i < L2_CACHE_SIZE; i++) { + if (l2_offset == s->l2_cache_offsets[i]) { + /* increment the hit count */ + if (++s->l2_cache_counts[i] == 0xffffffff) { + for(j = 0; j < L2_CACHE_SIZE; j++) { + s->l2_cache_counts[j] >>= 1; + } + } + l2_table = s->l2_cache + (i << s->l2_bits); + goto found; + } + } + /* 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 0; + } + s->l2_cache_offsets[min_index] = l2_offset; + s->l2_cache_counts[min_index] = 1; + found: + l2_index = (offset >> s->cluster_bits) & (s->l2_size - 1); + cluster_offset = be64_to_cpu(l2_table[l2_index]); + if (!cluster_offset) { + if (!allocate) + return cluster_offset; + } else if (!(cluster_offset & QCOW_OFLAG_COPIED)) { + if (!allocate) + return cluster_offset; + /* free the cluster */ + if (cluster_offset & QCOW_OFLAG_COMPRESSED) { + int nb_csectors; + nb_csectors = ((cluster_offset >> s->csize_shift) & + s->csize_mask) + 1; + free_clusters(bs, (cluster_offset & s->cluster_offset_mask) & ~511, + nb_csectors * 512); + } else { + free_clusters(bs, cluster_offset, s->cluster_size); + } + } else { + cluster_offset &= ~QCOW_OFLAG_COPIED; + return cluster_offset; + } + if (allocate == 1) { + /* allocate a new cluster */ + cluster_offset = alloc_clusters(bs, s->cluster_size); + + /* we must initialize the cluster content which won't be + written */ + if ((n_end - n_start) < s->cluster_sectors) { + uint64_t start_sect; + + start_sect = (offset & ~(s->cluster_size - 1)) >> 9; + ret = copy_sectors(bs, start_sect, + cluster_offset, 0, n_start); + if (ret < 0) + return 0; + ret = copy_sectors(bs, start_sect, + cluster_offset, n_end, s->cluster_sectors); + if (ret < 0) + return 0; + } + tmp = cpu_to_be64(cluster_offset | QCOW_OFLAG_COPIED); + } else { + int nb_csectors; + cluster_offset = alloc_bytes(bs, compressed_size); + nb_csectors = ((cluster_offset + compressed_size - 1) >> 9) - + (cluster_offset >> 9); + cluster_offset |= QCOW_OFLAG_COMPRESSED | + ((uint64_t)nb_csectors << s->csize_shift); + /* compressed clusters never have the copied flag */ + tmp = cpu_to_be64(cluster_offset); + } + /* update L2 table */ + l2_table[l2_index] = tmp; + if (bdrv_pwrite(s->hd, + l2_offset + l2_index * sizeof(tmp), &tmp, sizeof(tmp)) != sizeof(tmp)) + return 0; + return cluster_offset; +} + +static int qcow_is_allocated(BlockDriverState *bs, int64_t sector_num, + int nb_sectors, int *pnum) +{ + BDRVQcowState *s = bs->opaque; + int index_in_cluster, n; + uint64_t cluster_offset; + + cluster_offset = get_cluster_offset(bs, sector_num << 9, 0, 0, 0, 0); + index_in_cluster = sector_num & (s->cluster_sectors - 1); + n = s->cluster_sectors - index_in_cluster; + if (n > nb_sectors) + n = nb_sectors; + *pnum = n; + return (cluster_offset != 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; +} + +static int 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; +} + +/* handle reading after the end of the backing file */ +static int backing_read1(BlockDriverState *bs, + int64_t sector_num, uint8_t *buf, int nb_sectors) +{ + int n1; + if ((sector_num + nb_sectors) <= bs->total_sectors) + return nb_sectors; + if (sector_num >= bs->total_sectors) + n1 = 0; + else + n1 = bs->total_sectors - sector_num; + memset(buf + n1 * 512, 0, 512 * (nb_sectors - n1)); + return n1; +} + +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) { + cluster_offset = get_cluster_offset(bs, sector_num << 9, 0, 0, 0, 0); + index_in_cluster = sector_num & (s->cluster_sectors - 1); + n = s->cluster_sectors - index_in_cluster; + if (n > nb_sectors) + n = nb_sectors; + if (!cluster_offset) { + if (bs->backing_hd) { + /* read from the base image */ + n1 = 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 (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) { + 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 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; + uint8_t *buf; + int nb_sectors; + int n; + uint64_t cluster_offset; + uint8_t *cluster_data; + BlockDriverAIOCB *hd_aiocb; +} QCowAIOCB; + +static void qcow_aio_read_cb(void *opaque, int ret) +{ + QCowAIOCB *acb = opaque; + BlockDriverState *bs = acb->common.bs; + BDRVQcowState *s = bs->opaque; + int index_in_cluster, n1; + + acb->hd_aiocb = NULL; + if (ret < 0) { + fail: + acb->common.cb(acb->common.opaque, ret); + qemu_aio_release(acb); + return; + } + + redo: + /* post process the read buffer */ + if (!acb->cluster_offset) { + /* nothing to do */ + } else if (acb->cluster_offset & QCOW_OFLAG_COMPRESSED) { + /* nothing to do */ + } else { + if (s->crypt_method) { + encrypt_sectors(s, acb->sector_num, acb->buf, acb->buf, + acb->n, 0, + &s->aes_decrypt_key); + } + } + + acb->nb_sectors -= acb->n; + acb->sector_num += acb->n; + acb->buf += acb->n * 512; + + if (acb->nb_sectors == 0) { + /* request completed */ + acb->common.cb(acb->common.opaque, 0); + qemu_aio_release(acb); + return; + } + + /* prepare next AIO request */ + acb->cluster_offset = get_cluster_offset(bs, acb->sector_num << 9, + 0, 0, 0, 0); + index_in_cluster = acb->sector_num & (s->cluster_sectors - 1); + acb->n = s->cluster_sectors - index_in_cluster; + if (acb->n > acb->nb_sectors) + acb->n = acb->nb_sectors; + + if (!acb->cluster_offset) { + if (bs->backing_hd) { + /* read from the base image */ + n1 = backing_read1(bs->backing_hd, acb->sector_num, + acb->buf, acb->n); + if (n1 > 0) { + 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 fail; + } else { + goto redo; + } + } else { + /* Note: in this case, no need to wait */ + memset(acb->buf, 0, 512 * acb->n); + goto redo; + } + } else if (acb->cluster_offset & QCOW_OFLAG_COMPRESSED) { + /* add AIO support for compressed blocks ? */ + if (decompress_cluster(s, acb->cluster_offset) < 0) + 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 fail; + } + acb->hd_aiocb = bdrv_aio_read(s->hd, + (acb->cluster_offset >> 9) + index_in_cluster, + acb->buf, acb->n, qcow_aio_read_cb, acb); + if (acb->hd_aiocb == NULL) + goto fail; + } +} + +static QCowAIOCB *qcow_aio_setup(BlockDriverState *bs, + int64_t sector_num, uint8_t *buf, int nb_sectors, + BlockDriverCompletionFunc *cb, void *opaque) +{ + QCowAIOCB *acb; + + 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; + return acb; +} + +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, buf, nb_sectors, cb, opaque); + if (!acb) + return NULL; + + qcow_aio_read_cb(acb, 0); + return &acb->common; +} + +static void qcow_aio_write_cb(void *opaque, int ret) +{ + QCowAIOCB *acb = opaque; + BlockDriverState *bs = acb->common.bs; + BDRVQcowState *s = bs->opaque; + int index_in_cluster; + uint64_t cluster_offset; + const uint8_t *src_buf; + + acb->hd_aiocb = NULL; + + if (ret < 0) { + fail: + acb->common.cb(acb->common.opaque, ret); + qemu_aio_release(acb); + return; + } + + acb->nb_sectors -= acb->n; + acb->sector_num += acb->n; + acb->buf += acb->n * 512; + + if (acb->nb_sectors == 0) { + /* request completed */ + acb->common.cb(acb->common.opaque, 0); + qemu_aio_release(acb); + return; + } + + index_in_cluster = acb->sector_num & (s->cluster_sectors - 1); + acb->n = s->cluster_sectors - index_in_cluster; + if (acb->n > acb->nb_sectors) + acb->n = acb->nb_sectors; + cluster_offset = get_cluster_offset(bs, acb->sector_num << 9, 1, 0, + index_in_cluster, + index_in_cluster + acb->n); + if (!cluster_offset || (cluster_offset & 511) != 0) { + ret = -EIO; + goto fail; + } + if (s->crypt_method) { + if (!acb->cluster_data) { + acb->cluster_data = qemu_mallocz(s->cluster_size); + if (!acb->cluster_data) { + ret = -ENOMEM; + goto fail; + } + } + encrypt_sectors(s, acb->sector_num, acb->cluster_data, acb->buf, + acb->n, 1, &s->aes_encrypt_key); + src_buf = acb->cluster_data; + } else { + src_buf = acb->buf; + } + acb->hd_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 fail; +} + +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; + QCowAIOCB *acb; + + s->cluster_cache_offset = -1; /* disable compressed cache */ + + acb = qcow_aio_setup(bs, sector_num, (uint8_t*)buf, nb_sectors, cb, opaque); + if (!acb) + return NULL; + + 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; + qemu_free(s->l1_table); + qemu_free(s->l2_cache); + qemu_free(s->cluster_cache); + qemu_free(s->cluster_data); + refcount_close(bs); + bdrv_delete(s->hd); +} + +/* 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; + +static void 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); + } +} + +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, l2_bits; + QCowHeader header; + uint64_t tmp, offset; + QCowCreateState s1, *s = &s1; + + memset(s, 0, sizeof(*s)); + + fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644); + if (fd < 0) + return -1; + memset(&header, 0, sizeof(header)); + header.magic = cpu_to_be32(QCOW_MAGIC); + header.version = cpu_to_be32(QCOW_VERSION); + header.size = cpu_to_be64(total_size * 512); + header_size = sizeof(header); + backing_filename_len = 0; + if (backing_file) { + header.backing_file_offset = cpu_to_be64(header_size); + backing_filename_len = strlen(backing_file); + header.backing_file_size = cpu_to_be32(backing_filename_len); + header_size += backing_filename_len; + } + s->cluster_bits = 12; /* 4 KB clusters */ + s->cluster_size = 1 << s->cluster_bits; + header.cluster_bits = cpu_to_be32(s->cluster_bits); + header_size = (header_size + 7) & ~7; + if (flags) { + header.crypt_method = cpu_to_be32(QCOW_CRYPT_AES); + } else { + header.crypt_method = cpu_to_be32(QCOW_CRYPT_NONE); + } + l2_bits = s->cluster_bits - 3; + shift = s->cluster_bits + l2_bits; + l1_size = (((total_size * 512) + (1LL << shift) - 1) >> shift); + offset = align_offset(header_size, s->cluster_size); + s->l1_table_offset = offset; + header.l1_table_offset = cpu_to_be64(s->l1_table_offset); + header.l1_size = cpu_to_be32(l1_size); + offset += align_offset(l1_size * sizeof(uint64_t), s->cluster_size); + + s->refcount_table = qemu_mallocz(s->cluster_size); + if (!s->refcount_table) + goto fail; + s->refcount_block = qemu_mallocz(s->cluster_size); + if (!s->refcount_block) + goto fail; + + s->refcount_table_offset = offset; + header.refcount_table_offset = cpu_to_be64(offset); + header.refcount_table_clusters = cpu_to_be32(1); + offset += s->cluster_size; + + s->refcount_table[0] = cpu_to_be64(offset); + s->refcount_block_offset = offset; + offset += s->cluster_size; + + /* update refcounts */ + create_refcount_update(s, 0, header_size); + create_refcount_update(s, s->l1_table_offset, l1_size * sizeof(uint64_t)); + create_refcount_update(s, s->refcount_table_offset, s->cluster_size); + create_refcount_update(s, s->refcount_block_offset, s->cluster_size); + + /* write all the data */ + write(fd, &header, sizeof(header)); + if (backing_file) { + write(fd, backing_file, backing_filename_len); + } + lseek(fd, s->l1_table_offset, SEEK_SET); + tmp = 0; + for(i = 0;i < l1_size; i++) { + write(fd, &tmp, sizeof(tmp)); + } + lseek(fd, s->refcount_table_offset, SEEK_SET); + write(fd, s->refcount_table, s->cluster_size); + + lseek(fd, s->refcount_block_offset, SEEK_SET); + write(fd, s->refcount_block, s->cluster_size); + + qemu_free(s->refcount_table); + qemu_free(s->refcount_block); + close(fd); + return 0; + fail: + qemu_free(s->refcount_table); + qemu_free(s->refcount_block); + close(fd); + return -ENOMEM; +} + +static int qcow_make_empty(BlockDriverState *bs) +{ +#if 0 + /* XXX: not correct */ + BDRVQcowState *s = bs->opaque; + uint32_t l1_length = s->l1_size * sizeof(uint64_t); + int ret; + + memset(s->l1_table, 0, l1_length); + if (bdrv_pwrite(s->hd, s->l1_table_offset, s->l1_table, l1_length) < 0) + return -1; + ret = bdrv_truncate(s->hd, s->l1_table_offset + l1_length); + if (ret < 0) + return ret; + + l2_cache_reset(bs); +#endif + return 0; +} + +/* XXX: put compressed sectors first, then all the cluster aligned + tables to avoid losing bytes in alignment */ +static int qcow_write_compressed(BlockDriverState *bs, int64_t sector_num, + const uint8_t *buf, int nb_sectors) +{ + BDRVQcowState *s = bs->opaque; + z_stream strm; + int ret, out_len; + uint8_t *out_buf; + uint64_t cluster_offset; + + if (nb_sectors == 0) { + /* align end of file to a sector boundary to ease reading with + sector based I/Os */ + cluster_offset = bdrv_getlength(s->hd); + cluster_offset = (cluster_offset + 511) & ~511; + bdrv_truncate(s->hd, cluster_offset); + return 0; + } + + if (nb_sectors != s->cluster_sectors) + return -EINVAL; + + out_buf = qemu_malloc(s->cluster_size + (s->cluster_size / 1000) + 128); + if (!out_buf) + return -ENOMEM; + + /* best compression, small window, no zlib header */ + memset(&strm, 0, sizeof(strm)); + ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION, + Z_DEFLATED, -12, + 9, Z_DEFAULT_STRATEGY); + if (ret != 0) { + qemu_free(out_buf); + return -1; + } + + strm.avail_in = s->cluster_size; + strm.next_in = (uint8_t *)buf; + strm.avail_out = s->cluster_size; + strm.next_out = out_buf; + + ret = deflate(&strm, Z_FINISH); + if (ret != Z_STREAM_END && ret != Z_OK) { + qemu_free(out_buf); + deflateEnd(&strm); + return -1; + } + out_len = strm.next_out - out_buf; + + deflateEnd(&strm); + + if (ret != Z_STREAM_END || out_len >= s->cluster_size) { + /* could not compress: write normal cluster */ + qcow_write(bs, sector_num, buf, s->cluster_sectors); + } else { + cluster_offset = get_cluster_offset(bs, sector_num << 9, 2, + out_len, 0, 0); + cluster_offset &= s->cluster_offset_mask; + if (bdrv_pwrite(s->hd, cluster_offset, out_buf, out_len) != out_len) { + qemu_free(out_buf); + return -1; + } + } + + qemu_free(out_buf); + return 0; +} + +static void qcow_flush(BlockDriverState *bs) +{ + BDRVQcowState *s = bs->opaque; + bdrv_flush(s->hd); +} + +static int qcow_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) +{ + BDRVQcowState *s = bs->opaque; + bdi->cluster_size = s->cluster_size; + bdi->vm_state_offset = (int64_t)s->l1_vm_state_index << + (s->cluster_bits + s->l2_bits); + return 0; +} + +/*********************************************************/ +/* snapshot support */ + +/* update the refcounts of snapshots and the copied flag */ +static int 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; + + l2_cache_reset(bs); + + l2_table = NULL; + l1_table = NULL; + l1_size2 = l1_size * sizeof(uint64_t); + l1_allocated = 0; + if (l1_table_offset != s->l1_table_offset) { + l1_table = qemu_malloc(l1_size2); + if (!l1_table) + goto fail; + 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); + if (!l2_table) + goto fail; + l1_modified = 0; + for(i = 0; i < l1_size; i++) { + l2_offset = l1_table[i]; + if (l2_offset) { + old_l2_offset = l2_offset; + l2_offset &= ~QCOW_OFLAG_COPIED; + l2_modified = 0; + if (bdrv_pread(s->hd, l2_offset, l2_table, l2_size) != l2_size) + goto fail; + for(j = 0; j < s->l2_size; j++) { + offset = be64_to_cpu(l2_table[j]); + if (offset != 0) { + old_offset = offset; + offset &= ~QCOW_OFLAG_COPIED; + if (offset & QCOW_OFLAG_COMPRESSED) { + nb_csectors = ((offset >> s->csize_shift) & + s->csize_mask) + 1; + if (addend != 0) + update_refcount(bs, (offset & s->cluster_offset_mask) & ~511, + nb_csectors * 512, addend); + /* compressed clusters are never modified */ + refcount = 2; + } else { + if (addend != 0) { + refcount = update_cluster_refcount(bs, offset >> s->cluster_bits, addend); + } else { + refcount = get_refcount(bs, offset >> s->cluster_bits); + } + } + + if (refcount == 1) { + offset |= QCOW_OFLAG_COPIED; + } + if (offset != old_offset) { + l2_table[j] = cpu_to_be64(offset); + l2_modified = 1; + } + } + } + if (l2_modified) { + if (bdrv_pwrite(s->hd, + l2_offset, l2_table, l2_size) != l2_size) + goto fail; + } + + if (addend != 0) { + refcount = update_cluster_refcount(bs, l2_offset >> s->cluster_bits, addend); + } else { + refcount = get_refcount(bs, l2_offset >> s->cluster_bits); + } + if (refcount == 1) { + l2_offset |= QCOW_OFLAG_COPIED; + } + if (l2_offset != old_l2_offset) { + l1_table[i] = l2_offset; + l1_modified = 1; + } + } + } + if (l1_modified) { + for(i = 0; i < l1_size; i++) + cpu_to_be64s(&l1_table[i]); + if (bdrv_pwrite(s->hd, l1_table_offset, l1_table, + l1_size2) != l1_size2) + goto fail; + for(i = 0; i < l1_size; i++) + be64_to_cpus(&l1_table[i]); + } + if (l1_allocated) + qemu_free(l1_table); + qemu_free(l2_table); + return 0; + fail: + if (l1_allocated) + qemu_free(l1_table); + qemu_free(l2_table); + return -EIO; +} + +static void qcow_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; +} + +static int qcow_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; + + offset = s->snapshots_offset; + s->snapshots = qemu_mallocz(s->nb_snapshots * sizeof(QCowSnapshot)); + if (!s->snapshots) + goto fail; + 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 (!sn->id_str) + goto fail; + 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 (!sn->name) + goto fail; + 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: + qcow_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 = alloc_clusters(bs, snapshots_size); + offset = snapshots_offset; + + for(i = 0; i < s->nb_snapshots; i++) { + sn = s->snapshots + i; + memset(&h, 0, sizeof(h)); + h.l1_table_offset = cpu_to_be64(sn->l1_table_offset); + h.l1_size = cpu_to_be32(sn->l1_size); + h.vm_state_size = cpu_to_be32(sn->vm_state_size); + h.date_sec = cpu_to_be32(sn->date_sec); + h.date_nsec = cpu_to_be32(sn->date_nsec); + h.vm_clock_nsec = cpu_to_be64(sn->vm_clock_nsec); + + id_str_size = strlen(sn->id_str); + name_size = strlen(sn->name); + h.id_str_size = cpu_to_be16(id_str_size); + h.name_size = cpu_to_be16(name_size); + offset = align_offset(offset, 8); + if (bdrv_pwrite(s->hd, offset, &h, sizeof(h)) != sizeof(h)) + goto fail; + offset += sizeof(h); + if (bdrv_pwrite(s->hd, offset, sn->id_str, id_str_size) != id_str_size) + goto fail; + offset += id_str_size; + if (bdrv_pwrite(s->hd, offset, sn->name, name_size) != name_size) + goto fail; + offset += name_size; + } + + /* update the various header fields */ + data64 = cpu_to_be64(snapshots_offset); + if (bdrv_pwrite(s->hd, offsetof(QCowHeader, snapshots_offset), + &data64, sizeof(data64)) != sizeof(data64)) + goto fail; + data32 = cpu_to_be32(s->nb_snapshots); + if (bdrv_pwrite(s->hd, offsetof(QCowHeader, nb_snapshots), + &data32, sizeof(data32)) != sizeof(data32)) + goto fail; + + /* free the old snapshot table */ + 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 */ +static int qcow_snapshot_create(BlockDriverState *bs, + QEMUSnapshotInfo *sn_info) +{ + BDRVQcowState *s = bs->opaque; + QCowSnapshot *snapshots1, sn1, *sn = &sn1; + int i, ret; + uint64_t *l1_table = NULL; + + memset(sn, 0, sizeof(*sn)); + + if (sn_info->id_str[0] == '\0') { + /* compute a new id */ + find_new_snapshot_id(bs, sn_info->id_str, sizeof(sn_info->id_str)); + } + + /* check that the ID is unique */ + if (find_snapshot_by_id(bs, sn_info->id_str) >= 0) + return -ENOENT; + + sn->id_str = qemu_strdup(sn_info->id_str); + if (!sn->id_str) + goto fail; + sn->name = qemu_strdup(sn_info->name); + if (!sn->name) + goto fail; + sn->vm_state_size = sn_info->vm_state_size; + sn->date_sec = sn_info->date_sec; + sn->date_nsec = sn_info->date_nsec; + sn->vm_clock_nsec = sn_info->vm_clock_nsec; + + ret = update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, 1); + if (ret < 0) + goto fail; + + /* create the L1 table of the snapshot */ + sn->l1_table_offset = alloc_clusters(bs, s->l1_size * sizeof(uint64_t)); + sn->l1_size = s->l1_size; + + l1_table = qemu_malloc(s->l1_size * sizeof(uint64_t)); + if (!l1_table) + goto fail; + 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 (!snapshots1) + goto fail; + memcpy(snapshots1, s->snapshots, s->nb_snapshots * sizeof(QCowSnapshot)); + s->snapshots = snapshots1; + s->snapshots[s->nb_snapshots++] = *sn; + + if (qcow_write_snapshots(bs) < 0) + goto fail; +#ifdef DEBUG_ALLOC + 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 */ +static int qcow_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 (update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, -1) < 0) + goto fail; + + if (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 (update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, 1) < 0) + goto fail; + +#ifdef DEBUG_ALLOC + check_refcounts(bs); +#endif + return 0; + fail: + return -EIO; +} + +static int qcow_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 = 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 = update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, 0); + if (ret < 0) + return ret; + 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 + check_refcounts(bs); +#endif + return 0; +} + +static int qcow_snapshot_list(BlockDriverState *bs, + QEMUSnapshotInfo **psn_tab) +{ + BDRVQcowState *s = bs->opaque; + QEMUSnapshotInfo *sn_tab, *sn_info; + QCowSnapshot *sn; + int i; + + sn_tab = qemu_mallocz(s->nb_snapshots * sizeof(QEMUSnapshotInfo)); + if (!sn_tab) + goto fail; + 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; + fail: + qemu_free(sn_tab); + *psn_tab = NULL; + return -ENOMEM; +} + +/*********************************************************/ +/* refcount handling */ + +static int refcount_init(BlockDriverState *bs) +{ + BDRVQcowState *s = bs->opaque; + int ret, refcount_table_size2, i; + + s->refcount_block_cache = qemu_malloc(s->cluster_size); + if (!s->refcount_block_cache) + goto fail; + refcount_table_size2 = s->refcount_table_size * sizeof(uint64_t); + s->refcount_table = qemu_malloc(refcount_table_size2); + if (!s->refcount_table) + goto fail; + 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; +} + +static void 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; + 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]); +} + +/* 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 + s->cluster_size - 1) >> s->cluster_bits; + for(;;) { + if (get_refcount(bs, s->free_cluster_index) == 0) { + s->free_cluster_index++; + for(i = 1; i < nb_clusters; i++) { + if (get_refcount(bs, s->free_cluster_index) != 0) + goto not_found; + s->free_cluster_index++; + } +#ifdef DEBUG_ALLOC2 + printf("alloc_clusters: size=%lld -> %lld\n", + size, + (s->free_cluster_index - nb_clusters) << s->cluster_bits); +#endif + return (s->free_cluster_index - nb_clusters) << s->cluster_bits; + } else { + not_found: + s->free_cluster_index++; + } + } +} + +static int64_t alloc_clusters(BlockDriverState *bs, int64_t size) +{ + int64_t offset; + + offset = alloc_clusters_noref(bs, size); + update_refcount(bs, offset, size, 1); + return offset; +} + +/* only used to allocate compressed sectors. We try to allocate + contiguous sectors. size must be <= cluster_size */ +static int64_t 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 = alloc_clusters(bs, s->cluster_size); + } + redo: + free_in_cluster = s->cluster_size - + (s->free_byte_offset & (s->cluster_size - 1)); + if (size <= free_in_cluster) { + /* enough space in current cluster */ + offset = s->free_byte_offset; + s->free_byte_offset += size; + free_in_cluster -= size; + if (free_in_cluster == 0) + s->free_byte_offset = 0; + if ((offset & (s->cluster_size - 1)) != 0) + update_cluster_refcount(bs, offset >> s->cluster_bits, 1); + } else { + offset = alloc_clusters(bs, s->cluster_size); + cluster_offset = s->free_byte_offset & ~(s->cluster_size - 1); + if ((cluster_offset + s->cluster_size) == offset) { + /* we are lucky: contiguous data */ + offset = s->free_byte_offset; + update_cluster_refcount(bs, offset >> s->cluster_bits, 1); + s->free_byte_offset += size; + } else { + s->free_byte_offset = offset; + goto redo; + } + } + return offset; +} + +static void free_clusters(BlockDriverState *bs, + int64_t offset, int64_t size) +{ + update_refcount(bs, offset, size, -1); +} + +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; + uint64_t data64; + uint32_t data32; + + 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); + if (!new_table) + return -ENOMEM; + 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]); + + data64 = cpu_to_be64(table_offset); + if (bdrv_pwrite(s->hd, offsetof(QCowHeader, refcount_table_offset), + &data64, sizeof(data64)) != sizeof(data64)) + goto fail; + data32 = cpu_to_be32(refcount_table_clusters); + if (bdrv_pwrite(s->hd, offsetof(QCowHeader, refcount_table_clusters), + &data32, sizeof(data32)) != sizeof(data32)) + goto fail; + qemu_free(s->refcount_table); + s->refcount_table = new_table; + s->refcount_table_size = new_table_size; + + update_refcount(bs, table_offset, new_table_size2, 1); + return 0; + fail: + free_clusters(bs, table_offset, new_table_size2); + qemu_free(new_table); + return -EIO; +} + +/* addend must be 1 or -1 */ +/* XXX: cache several refcount block clusters ? */ +static int update_cluster_refcount(BlockDriverState *bs, + int64_t cluster_index, + int addend) +{ + BDRVQcowState *s = bs->opaque; + int64_t offset, refcount_block_offset; + int ret, refcount_table_index, block_index, refcount; + uint64_t data64; + + refcount_table_index = cluster_index >> (s->cluster_bits - REFCOUNT_SHIFT); + if (refcount_table_index >= s->refcount_table_size) { + if (addend < 0) + return -EINVAL; + ret = grow_refcount_table(bs, refcount_table_index + 1); + if (ret < 0) + return ret; + } + refcount_block_offset = s->refcount_table[refcount_table_index]; + if (!refcount_block_offset) { + if (addend < 0) + return -EINVAL; + /* 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); + } else { + if (refcount_block_offset != s->refcount_block_cache_offset) { + if (load_refcount_block(bs, refcount_block_offset) < 0) + return -EIO; + } + } + /* we can update the count and save it */ + block_index = cluster_index & + ((1 << (s->cluster_bits - REFCOUNT_SHIFT)) - 1); + refcount = be16_to_cpu(s->refcount_block_cache[block_index]); + refcount += addend; + if (refcount < 0 || refcount > 0xffff) + return -EINVAL; + if (refcount == 0 && cluster_index < s->free_cluster_index) { + s->free_cluster_index = cluster_index; + } + s->refcount_block_cache[block_index] = cpu_to_be16(refcount); + if (bdrv_pwrite(s->hd, + refcount_block_offset + (block_index << REFCOUNT_SHIFT), + &s->refcount_block_cache[block_index], 2) != 2) + return -EIO; + return refcount; +} + +static void update_refcount(BlockDriverState *bs, + int64_t offset, int64_t length, + int addend) +{ + BDRVQcowState *s = bs->opaque; + int64_t start, last, cluster_offset; + +#ifdef DEBUG_ALLOC2 + printf("update_refcount: offset=%lld size=%lld addend=%d\n", + offset, length, addend); +#endif + if (length <= 0) + return; + 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) { + update_cluster_refcount(bs, cluster_offset >> s->cluster_bits, addend); + } +} + +#ifdef DEBUG_ALLOC +static void 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; + + if (size <= 0) + return; + + 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) { + printf("ERROR: invalid cluster offset=0x%llx\n", cluster_offset); + } else { + if (++refcount_table[k] == 0) { + printf("ERROR: overflow cluster offset=0x%llx\n", cluster_offset); + } + } + } +} + +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_table, l2_offset, offset, l1_size2; + int l2_size, i, j, nb_csectors, refcount; + + l2_table = NULL; + l1_size2 = l1_size * sizeof(uint64_t); + + inc_refcounts(bs, refcount_table, refcount_table_size, + l1_table_offset, l1_size2); + + l1_table = qemu_malloc(l1_size2); + if (!l1_table) + goto fail; + 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]); + + l2_size = s->l2_size * sizeof(uint64_t); + l2_table = qemu_malloc(l2_size); + if (!l2_table) + goto fail; + for(i = 0; i < l1_size; i++) { + l2_offset = l1_table[i]; + if (l2_offset) { + if (check_copied) { + refcount = get_refcount(bs, (l2_offset & ~QCOW_OFLAG_COPIED) >> s->cluster_bits); + if ((refcount == 1) != ((l2_offset & QCOW_OFLAG_COPIED) != 0)) { + printf("ERROR OFLAG_COPIED: l2_offset=%llx refcount=%d\n", + l2_offset, refcount); + } + } + l2_offset &= ~QCOW_OFLAG_COPIED; + 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) { + if (offset & QCOW_OFLAG_COMPRESSED) { + if (offset & QCOW_OFLAG_COPIED) { + printf("ERROR: cluster %lld: copied flag must never be set for compressed clusters\n", + offset >> s->cluster_bits); + offset &= ~QCOW_OFLAG_COPIED; + } + nb_csectors = ((offset >> s->csize_shift) & + s->csize_mask) + 1; + offset &= s->cluster_offset_mask; + inc_refcounts(bs, refcount_table, + refcount_table_size, + offset & ~511, nb_csectors * 512); + } else { + if (check_copied) { + refcount = get_refcount(bs, (offset & ~QCOW_OFLAG_COPIED) >> s->cluster_bits); + if ((refcount == 1) != ((offset & QCOW_OFLAG_COPIED) != 0)) { + printf("ERROR OFLAG_COPIED: offset=%llx refcount=%d\n", + offset, refcount); + } + } + offset &= ~QCOW_OFLAG_COPIED; + inc_refcounts(bs, refcount_table, + refcount_table_size, + offset, s->cluster_size); + } + } + } + inc_refcounts(bs, refcount_table, + refcount_table_size, + l2_offset, + s->cluster_size); + } + } + qemu_free(l1_table); + qemu_free(l2_table); + return 0; + fail: + printf("ERROR: I/O error in check_refcounts_l1\n"); + qemu_free(l1_table); + qemu_free(l2_table); + return -EIO; +} + +static void check_refcounts(BlockDriverState *bs) +{ + BDRVQcowState *s = bs->opaque; + int64_t size; + int nb_clusters, refcount1, refcount2, i; + QCowSnapshot *sn; + uint16_t *refcount_table; + + size = bdrv_getlength(s->hd); + nb_clusters = (size + s->cluster_size - 1) >> s->cluster_bits; + refcount_table = qemu_mallocz(nb_clusters * sizeof(uint16_t)); + + /* header */ + inc_refcounts(bs, refcount_table, nb_clusters, + 0, s->cluster_size); + + check_refcounts_l1(bs, refcount_table, nb_clusters, + s->l1_table_offset, s->l1_size, 1); + + /* 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); + } + inc_refcounts(bs, refcount_table, nb_clusters, + s->snapshots_offset, s->snapshots_size); + + /* refcount data */ + 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) { + 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) + printf("ERROR cluster %d refcount=%d reference=%d\n", + i, refcount1, refcount2); + } + + qemu_free(refcount_table); +} + +#if 0 +static void dump_refcounts(BlockDriverState *bs) +{ + BDRVQcowState *s = bs->opaque; + int64_t nb_clusters, k, k1, size; + int refcount; + + size = bdrv_getlength(s->hd); + nb_clusters = (size + s->cluster_size - 1) >> s->cluster_bits; + for(k = 0; k < nb_clusters;) { + k1 = k; + refcount = get_refcount(bs, k); + k++; + while (k < nb_clusters && get_refcount(bs, k) == refcount) + k++; + printf("%lld: refcount=%d nb=%lld\n", k, refcount, k - k1); + } +} +#endif +#endif + +BlockDriver bdrv_qcow2 = { + "qcow2", + sizeof(BDRVQcowState), + qcow_probe, + qcow_open, + NULL, + NULL, + qcow_close, + qcow_create, + qcow_flush, + qcow_is_allocated, + qcow_set_key, + qcow_make_empty, + + .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_snapshot_create = qcow_snapshot_create, + .bdrv_snapshot_goto = qcow_snapshot_goto, + .bdrv_snapshot_delete = qcow_snapshot_delete, + .bdrv_snapshot_list = qcow_snapshot_list, + .bdrv_get_info = qcow_get_info, +}; diff -r d2ef85c6bf84 -r 00618037d37d tools/ioemu/block-raw.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/ioemu/block-raw.c Wed May 09 14:17:15 2007 +0100 @@ -0,0 +1,1353 @@ +/* + * Block driver for RAW files + * + * Copyright (c) 2006 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" +#include "block_int.h" +#include <assert.h> +#ifndef _WIN32 +#include <aio.h> + +#ifndef QEMU_TOOL +#include "exec-all.h" +#endif + +#ifdef CONFIG_COCOA +#include <paths.h> +#include <sys/param.h> +#include <IOKit/IOKitLib.h> +#include <IOKit/IOBSD.h> +#include <IOKit/storage/IOMediaBSDClient.h> +#include <IOKit/storage/IOMedia.h> +#include <IOKit/storage/IOCDMedia.h> +//#include <IOKit/storage/IOCDTypes.h> +#include <CoreFoundation/CoreFoundation.h> +#endif + +#ifdef __sun__ +#define _POSIX_PTHREAD_SEMANTICS 1 +#include <signal.h> +#include <sys/dkio.h> +#endif +#ifdef __linux__ +#include <sys/ioctl.h> +#include <linux/cdrom.h> +#include <linux/fd.h> +#endif +#ifdef __FreeBSD__ +#include <sys/disk.h> +#endif + +//#define DEBUG_FLOPPY + +#define FTYPE_FILE 0 +#define FTYPE_CD 1 +#define FTYPE_FD 2 + +/* if the FD is not accessed during that time (in ms), we try to + reopen it to see if the disk has been changed */ +#define FD_OPEN_TIMEOUT 1000 + +typedef struct BDRVRawState { + int fd; + int type; +#if defined(__linux__) + /* linux floppy specific */ + int fd_open_flags; + int64_t fd_open_time; + int64_t fd_error_time; + int fd_got_error; + int fd_media_changed; +#endif +} BDRVRawState; + +static int fd_open(BlockDriverState *bs); + +static int raw_open(BlockDriverState *bs, const char *filename, int flags) +{ + BDRVRawState *s = bs->opaque; + int fd, open_flags, ret; + + open_flags = O_BINARY; + if ((flags & BDRV_O_ACCESS) == O_RDWR) { + open_flags |= O_RDWR; + } else { + open_flags |= O_RDONLY; + bs->read_only = 1; + } + if (flags & BDRV_O_CREAT) + open_flags |= O_CREAT | O_TRUNC; + + s->type = FTYPE_FILE; + + fd = open(filename, open_flags, 0644); + if (fd < 0) { + ret = -errno; + if (ret == -EROFS) + ret = -EACCES; + return ret; + } + s->fd = fd; + return 0; +} + +/* XXX: use host sector size if necessary with: +#ifdef DIOCGSECTORSIZE + { + unsigned int sectorsize = 512; + if (!ioctl(fd, DIOCGSECTORSIZE, §orsize) && + sectorsize > bufsize) + bufsize = sectorsize; + } +#endif +#ifdef CONFIG_COCOA + u_int32_t blockSize = 512; + if ( !ioctl( fd, DKIOCGETBLOCKSIZE, &blockSize ) && blockSize > bufsize) { + bufsize = blockSize; + } +#endif +*/ + +static int raw_pread(BlockDriverState *bs, int64_t offset, + uint8_t *buf, int count) +{ + BDRVRawState *s = bs->opaque; + int ret; + + ret = fd_open(bs); + if (ret < 0) + return ret; + + lseek(s->fd, offset, SEEK_SET); + ret = read(s->fd, buf, count); + return ret; +} + +static int raw_pwrite(BlockDriverState *bs, int64_t offset, + const uint8_t *buf, int count) +{ + BDRVRawState *s = bs->opaque; + int ret; + + ret = fd_open(bs); + if (ret < 0) + return ret; + + lseek(s->fd, offset, SEEK_SET); + ret = write(s->fd, buf, count); + return ret; +} + +/***********************************************************/ +/* Unix AIO using POSIX AIO */ + +typedef struct RawAIOCB { + BlockDriverAIOCB common; + struct aiocb aiocb; + struct RawAIOCB *next; +} RawAIOCB; + +static int aio_sig_num = SIGUSR2; +static RawAIOCB *first_aio; /* AIO issued */ +static int aio_initialized = 0; + +static void aio_signal_handler(int signum) +{ +#ifndef QEMU_TOOL + CPUState *env = cpu_single_env; + if (env) { + /* stop the currently executing cpu because a timer occured */ + cpu_interrupt(env, CPU_INTERRUPT_EXIT); +#ifdef USE_KQEMU + if (env->kqemu_enabled) { + kqemu_cpu_interrupt(env); + } +#endif + } +#endif +} + +void qemu_aio_init(void) +{ + struct sigaction act; + + aio_initialized = 1; + + sigfillset(&act.sa_mask); + act.sa_flags = 0; /* do not restart syscalls to interrupt select() */ + act.sa_handler = aio_signal_handler; + sigaction(aio_sig_num, &act, NULL); + +#if defined(__GLIBC__) && defined(__linux__) + { + /* XXX: aio thread exit seems to hang on RedHat 9 and this init + seems to fix the problem. */ + struct aioinit ai; + memset(&ai, 0, sizeof(ai)); + ai.aio_threads = 1; + ai.aio_num = 1; + ai.aio_idle_time = 365 * 100000; + aio_init(&ai); + } +#endif +} + +void qemu_aio_poll(void) +{ + RawAIOCB *acb, **pacb; + int ret; + + for(;;) { + pacb = &first_aio; + for(;;) { + acb = *pacb; + if (!acb) + goto the_end; + ret = aio_error(&acb->aiocb); + if (ret == ECANCELED) { + /* remove the request */ + *pacb = acb->next; + qemu_aio_release(acb); + } else if (ret != EINPROGRESS) { + /* end of aio */ + if (ret == 0) { + ret = aio_return(&acb->aiocb); + if (ret == acb->aiocb.aio_nbytes) + ret = 0; + else + ret = -EINVAL; + } else { + ret = -ret; + } + /* remove the request */ + *pacb = acb->next; + /* call the callback */ + acb->common.cb(acb->common.opaque, ret); + qemu_aio_release(acb); + break; + } else { + pacb = &acb->next; + } + } + } + the_end: ; +} + +/* Wait for all IO requests to complete. */ +void qemu_aio_flush(void) +{ + qemu_aio_wait_start(); + qemu_aio_poll(); + while (first_aio) { + qemu_aio_wait(); + } + qemu_aio_wait_end(); +} + +/* wait until at least one AIO was handled */ +static sigset_t wait_oset; + +void qemu_aio_wait_start(void) +{ + sigset_t set; + + if (!aio_initialized) + qemu_aio_init(); + sigemptyset(&set); + sigaddset(&set, aio_sig_num); + sigprocmask(SIG_BLOCK, &set, &wait_oset); +} + +void qemu_aio_wait(void) +{ + sigset_t set; + int nb_sigs; + +#ifndef QEMU_TOOL + if (qemu_bh_poll()) + return; +#endif + sigemptyset(&set); + sigaddset(&set, aio_sig_num); + sigwait(&set, &nb_sigs); + qemu_aio_poll(); +} + +void qemu_aio_wait_end(void) +{ + sigprocmask(SIG_SETMASK, &wait_oset, NULL); +} + +static RawAIOCB *raw_aio_setup(BlockDriverState *bs, + int64_t sector_num, uint8_t *buf, int nb_sectors, + BlockDriverCompletionFunc *cb, void *opaque) +{ + BDRVRawState *s = bs->opaque; + RawAIOCB *acb; + + if (fd_open(bs) < 0) + return NULL; + + acb = qemu_aio_get(bs, cb, opaque); + if (!acb) + return NULL; + acb->aiocb.aio_fildes = s->fd; + acb->aiocb.aio_sigevent.sigev_signo = aio_sig_num; + acb->aiocb.aio_sigevent.sigev_notify = SIGEV_SIGNAL; + acb->aiocb.aio_buf = buf; + acb->aiocb.aio_nbytes = nb_sectors * 512; + acb->aiocb.aio_offset = sector_num * 512; + acb->next = first_aio; + first_aio = acb; + return acb; +} + +static BlockDriverAIOCB *raw_aio_read(BlockDriverState *bs, + int64_t sector_num, uint8_t *buf, int nb_sectors, + BlockDriverCompletionFunc *cb, void *opaque) +{ + RawAIOCB *acb; + + acb = raw_aio_setup(bs, sector_num, buf, nb_sectors, cb, opaque); + if (!acb) + return NULL; + if (aio_read(&acb->aiocb) < 0) { + qemu_aio_release(acb); + return NULL; + } + return &acb->common; +} + +static BlockDriverAIOCB *raw_aio_write(BlockDriverState *bs, + int64_t sector_num, const uint8_t *buf, int nb_sectors, + BlockDriverCompletionFunc *cb, void *opaque) +{ + RawAIOCB *acb; + + acb = raw_aio_setup(bs, sector_num, (uint8_t*)buf, nb_sectors, cb, opaque); + if (!acb) + return NULL; + if (aio_write(&acb->aiocb) < 0) { + qemu_aio_release(acb); + return NULL; + } + return &acb->common; +} + +static void raw_aio_cancel(BlockDriverAIOCB *blockacb) +{ + int ret; + RawAIOCB *acb = (RawAIOCB *)blockacb; + RawAIOCB **pacb; + + ret = aio_cancel(acb->aiocb.aio_fildes, &acb->aiocb); + if (ret == AIO_NOTCANCELED) { + /* fail safe: if the aio could not be canceled, we wait for + it */ + while (aio_error(&acb->aiocb) == EINPROGRESS); + } + + /* remove the callback from the queue */ + pacb = &first_aio; + for(;;) { + if (*pacb == NULL) { + break; + } else if (*pacb == acb) { + *pacb = acb->next; + qemu_aio_release(acb); + break; + } + pacb = &acb->next; + } +} + +static void raw_close(BlockDriverState *bs) +{ + BDRVRawState *s = bs->opaque; + bs->total_sectors = 0; + if (s->fd >= 0) { + close(s->fd); + s->fd = -1; + } +} + +static int raw_truncate(BlockDriverState *bs, int64_t offset) +{ + BDRVRawState *s = bs->opaque; + if (s->type != FTYPE_FILE) + return -ENOTSUP; + if (ftruncate(s->fd, offset) < 0) + return -errno; + return 0; +} + +static int64_t raw_getlength(BlockDriverState *bs) +{ + BDRVRawState *s = bs->opaque; + int fd = s->fd; + int64_t size; +#ifdef _BSD + struct stat sb; +#endif +#ifdef __sun__ + struct dk_minfo minfo; + int rv; +#endif + int ret; + + ret = fd_open(bs); + if (ret < 0) + return ret; + +#ifdef _BSD + if (!fstat(fd, &sb) && (S_IFCHR & sb.st_mode)) { +#ifdef DIOCGMEDIASIZE + if (ioctl(fd, DIOCGMEDIASIZE, (off_t *)&size)) +#endif +#ifdef CONFIG_COCOA + size = LONG_LONG_MAX; +#else + size = lseek(fd, 0LL, SEEK_END); +#endif + } else +#endif +#ifdef __sun__ + /* + * use the DKIOCGMEDIAINFO ioctl to read the size. + */ + rv = ioctl ( fd, DKIOCGMEDIAINFO, &minfo ); + if ( rv != -1 ) { + size = minfo.dki_lbsize * minfo.dki_capacity; + } else /* there are reports that lseek on some devices + fails, but irc discussion said that contingency + on contingency was overkill */ +#endif + { + size = lseek(fd, 0, SEEK_END); + } + return size; +} + +static int raw_create(const char *filename, int64_t total_size, + const char *backing_file, int flags) +{ + int fd; + + if (flags || backing_file) + return -ENOTSUP; + + fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, + 0644); + if (fd < 0) + return -EIO; + ftruncate(fd, total_size * 512); + close(fd); + return 0; +} + +static void raw_flush(BlockDriverState *bs) +{ + BDRVRawState *s = bs->opaque; + fsync(s->fd); +} + +BlockDriver bdrv_raw = { + "raw", + sizeof(BDRVRawState), + NULL, /* no probe for protocols */ + raw_open, + NULL, + NULL, + raw_close, + raw_create, + raw_flush, + + .bdrv_aio_read = raw_aio_read, + .bdrv_aio_write = raw_aio_write, + .bdrv_aio_cancel = raw_aio_cancel, + .aiocb_size = sizeof(RawAIOCB), + .protocol_name = "file", + .bdrv_pread = raw_pread, + .bdrv_pwrite = raw_pwrite, + .bdrv_truncate = raw_truncate, + .bdrv_getlength = raw_getlength, +}; + +/***********************************************/ +/* host device */ + +#ifdef CONFIG_COCOA +static kern_return_t FindEjectableCDMedia( io_iterator_t *mediaIterator ); +static kern_return_t GetBSDPath( io_iterator_t mediaIterator, char *bsdPath, CFIndex maxPathSize ); + +kern_return_t FindEjectableCDMedia( io_iterator_t *mediaIterator ) +{ + kern_return_t kernResult; + mach_port_t masterPort; + CFMutableDictionaryRef classesToMatch; + + kernResult = IOMasterPort( MACH_PORT_NULL, &masterPort ); + if ( KERN_SUCCESS != kernResult ) { + printf( "IOMasterPort returned %d\n", kernResult ); + } + + classesToMatch = IOServiceMatching( kIOCDMediaClass ); + if ( classesToMatch == NULL ) { + printf( "IOServiceMatching returned a NULL dictionary.\n" ); + } else { + CFDictionarySetValue( classesToMatch, CFSTR( kIOMediaEjectableKey ), kCFBooleanTrue ); + } + kernResult = IOServiceGetMatchingServices( masterPort, classesToMatch, mediaIterator ); + if ( KERN_SUCCESS != kernResult ) + { + printf( "IOServiceGetMatchingServices returned %d\n", kernResult ); + } + + return kernResult; +} + +kern_return_t GetBSDPath( io_iterator_t mediaIterator, char *bsdPath, CFIndex maxPathSize ) +{ + io_object_t nextMedia; + kern_return_t kernResult = KERN_FAILURE; + *bsdPath = '\0'; + nextMedia = IOIteratorNext( mediaIterator ); + if ( nextMedia ) + { + CFTypeRef bsdPathAsCFString; + bsdPathAsCFString = IORegistryEntryCreateCFProperty( nextMedia, CFSTR( kIOBSDNameKey ), kCFAllocatorDefault, 0 ); + if ( bsdPathAsCFString ) { + size_t devPathLength; + strcpy( bsdPath, _PATH_DEV ); + strcat( bsdPath, "r" ); + devPathLength = strlen( bsdPath ); + if ( CFStringGetCString( bsdPathAsCFString, bsdPath + devPathLength, maxPathSize - devPathLength, kCFStringEncodingASCII ) ) { + kernResult = KERN_SUCCESS; + } + CFRelease( bsdPathAsCFString ); + } + IOObjectRelease( nextMedia ); + } + + return kernResult; +} + +#endif + +static int hdev_open(BlockDriverState *bs, const char *filename, int flags) +{ + BDRVRawState *s = bs->opaque; + int fd, open_flags, ret; + +#ifdef CONFIG_COCOA + if (strstart(filename, "/dev/cdrom", NULL)) { + kern_return_t kernResult; + io_iterator_t mediaIterator; + char bsdPath[ MAXPATHLEN ]; + int fd; + + kernResult = FindEjectableCDMedia( &mediaIterator ); + kernResult = GetBSDPath( mediaIterator, bsdPath, sizeof( bsdPath ) ); + + if ( bsdPath[ 0 ] != '\0' ) { + strcat(bsdPath,"s0"); + /* some CDs don't have a partition 0 */ + fd = open(bsdPath, O_RDONLY | O_BINARY | O_LARGEFILE); + if (fd < 0) { + bsdPath[strlen(bsdPath)-1] = '1'; + } else { + close(fd); + } + filename = bsdPath; + } + + if ( mediaIterator ) + IOObjectRelease( mediaIterator ); + } +#endif + open_flags = O_BINARY; + if ((flags & BDRV_O_ACCESS) == O_RDWR) { + open_flags |= O_RDWR; + } else { + open_flags |= O_RDONLY; + bs->read_only = 1; + } + + s->type = FTYPE_FILE; +#if defined(__linux__) + if (strstart(filename, "/dev/cd", NULL)) { + /* open will not fail even if no CD is inserted */ + open_flags |= O_NONBLOCK; + s->type = FTYPE_CD; + } else if (strstart(filename, "/dev/fd", NULL)) { + s->type = FTYPE_FD; + s->fd_open_flags = open_flags; + /* open will not fail even if no floppy is inserted */ + open_flags |= O_NONBLOCK; + } +#endif + fd = open(filename, open_flags, 0644); + if (fd < 0) { + ret = -errno; + if (ret == -EROFS) + ret = -EACCES; + return ret; + } + s->fd = fd; +#if defined(__linux__) + /* close fd so that we can reopen it as needed */ + if (s->type == FTYPE_FD) { + close(s->fd); + s->fd = -1; + s->fd_media_changed = 1; + } +#endif + return 0; +} + +#if defined(__linux__) && !defined(QEMU_TOOL) + +/* Note: we do not have a reliable method to detect if the floppy is + present. The current method is to try to open the floppy at every + I/O and to keep it opened during a few hundreds of ms. */ +static int fd_open(BlockDriverState *bs) +{ + BDRVRawState *s = bs->opaque; + int last_media_present; + + if (s->type != FTYPE_FD) + return 0; + last_media_present = (s->fd >= 0); + if (s->fd >= 0 && + (qemu_get_clock(rt_clock) - s->fd_open_time) >= FD_OPEN_TIMEOUT) { + close(s->fd); + s->fd = -1; +#ifdef DEBUG_FLOPPY + printf("Floppy closed\n"); +#endif + } + if (s->fd < 0) { + if (s->fd_got_error && + (qemu_get_clock(rt_clock) - s->fd_error_time) < FD_OPEN_TIMEOUT) { +#ifdef DEBUG_FLOPPY + printf("No floppy (open delayed)\n"); +#endif + return -EIO; + } + s->fd = open(bs->filename, s->fd_open_flags); + if (s->fd < 0) { + s->fd_error_time = qemu_get_clock(rt_clock); + s->fd_got_error = 1; + if (last_media_present) + s->fd_media_changed = 1; +#ifdef DEBUG_FLOPPY + printf("No floppy\n"); +#endif + return -EIO; + } +#ifdef DEBUG_FLOPPY + printf("Floppy opened\n"); +#endif + } + if (!last_media_present) + s->fd_media_changed = 1; + s->fd_open_time = qemu_get_clock(rt_clock); + s->fd_got_error = 0; + return 0; +} +#else +static int fd_open(BlockDriverState *bs) +{ + return 0; +} +#endif + +#if defined(__linux__) + +static int raw_is_inserted(BlockDriverState *bs) +{ + BDRVRawState *s = bs->opaque; + int ret; + + switch(s->type) { + case FTYPE_CD: + ret = ioctl(s->fd, CDROM_DRIVE_STATUS, CDSL_CURRENT); + if (ret == CDS_DISC_OK) + return 1; + else + return 0; + break; + case FTYPE_FD: + ret = fd_open(bs); + return (ret >= 0); + default: + return 1; + } +} + +/* currently only used by fdc.c, but a CD version would be good too */ +static int raw_media_changed(BlockDriverState *bs) +{ + BDRVRawState *s = bs->opaque; + + switch(s->type) { + case FTYPE_FD: + { + int ret; + /* XXX: we do not have a true media changed indication. It + does not work if the floppy is changed without trying + to read it */ + fd_open(bs); + ret = s->fd_media_changed; + s->fd_media_changed = 0; +#ifdef DEBUG_FLOPPY + printf("Floppy changed=%d\n", ret); +#endif + return ret; + } + default: + return -ENOTSUP; + } +} + +static int raw_eject(BlockDriverState *bs, int eject_flag) +{ + BDRVRawState *s = bs->opaque; + + switch(s->type) { + case FTYPE_CD: + if (eject_flag) { + if (ioctl (s->fd, CDROMEJECT, NULL) < 0) + perror("CDROMEJECT"); + } else { + if (ioctl (s->fd, CDROMCLOSETRAY, NULL) < 0) + perror("CDROMEJECT"); + } + break; + case FTYPE_FD: + { + int fd; + if (s->fd >= 0) { + close(s->fd); + s->fd = -1; + } + fd = open(bs->filename, s->fd_open_flags | O_NONBLOCK); + if (fd >= 0) { + if (ioctl(fd, FDEJECT, 0) < 0) + perror("FDEJECT"); + close(fd); + } + } + break; + default: + return -ENOTSUP; + } + return 0; +} + +static int raw_set_locked(BlockDriverState *bs, int locked) +{ + BDRVRawState *s = bs->opaque; + + switch(s->type) { + case FTYPE_CD: + if (ioctl (s->fd, CDROM_LOCKDOOR, locked) < 0) { + /* Note: an error can happen if the distribution automatically + mounts the CD-ROM */ + // perror("CDROM_LOCKDOOR"); + } + break; + default: + return -ENOTSUP; + } + return 0; +} + +#else + +static int raw_is_inserted(BlockDriverState *bs) +{ + return 1; +} + +static int raw_media_changed(BlockDriverState *bs) +{ + return -ENOTSUP; +} + +static int raw_eject(BlockDriverState *bs, int eject_flag) +{ + return -ENOTSUP; +} + +static int raw_set_locked(BlockDriverState *bs, int locked) +{ + return -ENOTSUP; +} + +#endif /* !linux */ + +BlockDriver bdrv_host_device = { + "host_device", + sizeof(BDRVRawState), + NULL, /* no probe for protocols */ + hdev_open, + NULL, + NULL, + raw_close, + NULL, + raw_flush, + + .bdrv_aio_read = raw_aio_read, + .bdrv_aio_write = raw_aio_write, + .bdrv_aio_cancel = raw_aio_cancel, + .aiocb_size = sizeof(RawAIOCB), + .bdrv_pread = raw_pread, + .bdrv_pwrite = raw_pwrite, + .bdrv_getlength = raw_getlength, + + /* removable device support */ + .bdrv_is_inserted = raw_is_inserted, + .bdrv_media_changed = raw_media_changed, + .bdrv_eject = raw_eject, + .bdrv_set_locked = raw_set_locked, +}; + +#else /* _WIN32 */ + +/* XXX: use another file ? */ +#include <winioctl.h> + +#define FTYPE_FILE 0 +#define FTYPE_CD 1 +#define FTYPE_HARDDISK 2 + +typedef struct BDRVRawState { + HANDLE hfile; + int type; + char drive_path[16]; /* format: "d:\" */ +} BDRVRawState; + +typedef struct RawAIOCB { + BlockDriverAIOCB common; + HANDLE hEvent; + OVERLAPPED ov; + int count; +} RawAIOCB; + +int qemu_ftruncate64(int fd, int64_t length) +{ + LARGE_INTEGER li; + LONG high; + HANDLE h; + BOOL res; + + if ((GetVersion() & 0x80000000UL) && (length >> 32) != 0) + return -1; + + h = (HANDLE)_get_osfhandle(fd); + + /* get current position, ftruncate do not change position */ + li.HighPart = 0; + li.LowPart = SetFilePointer (h, 0, &li.HighPart, FILE_CURRENT); + if (li.LowPart == 0xffffffffUL && GetLastError() != NO_ERROR) + return -1; + + high = length >> 32; + if (!SetFilePointer(h, (DWORD) length, &high, FILE_BEGIN)) + return -1; + res = SetEndOfFile(h); + + /* back to old position */ + SetFilePointer(h, li.LowPart, &li.HighPart, FILE_BEGIN); + return res ? 0 : -1; +} + +static int set_sparse(int fd) +{ + DWORD returned; + return (int) DeviceIoControl((HANDLE)_get_osfhandle(fd), FSCTL_SET_SPARSE, + NULL, 0, NULL, 0, &returned, NULL); +} + +static int raw_open(BlockDriverState *bs, const char *filename, int flags) +{ + BDRVRawState *s = bs->opaque; + int access_flags, create_flags; + DWORD overlapped; + + s->type = FTYPE_FILE; + + if ((flags & BDRV_O_ACCESS) == O_RDWR) { + access_flags = GENERIC_READ | GENERIC_WRITE; + } else { + access_flags = GENERIC_READ; + } + if (flags & BDRV_O_CREAT) { + create_flags = CREATE_ALWAYS; + } else { + create_flags = OPEN_EXISTING; + } +#ifdef QEMU_TOOL + overlapped = FILE_ATTRIBUTE_NORMAL; +#else + overlapped = FILE_FLAG_OVERLAPPED; +#endif + s->hfile = CreateFile(filename, access_flags, + FILE_SHARE_READ, NULL, + create_flags, overlapped, NULL); + if (s->hfile == INVALID_HANDLE_VALUE) { + int err = GetLastError(); + + if (err == ERROR_ACCESS_DENIED) + return -EACCES; + return -1; + } + return 0; +} + +static int raw_pread(BlockDriverState *bs, int64_t offset, + uint8_t *buf, int count) +{ + BDRVRawState *s = bs->opaque; + OVERLAPPED ov; + DWORD ret_count; + int ret; + + memset(&ov, 0, sizeof(ov)); + ov.Offset = offset; + ov.OffsetHigh = offset >> 32; + ret = ReadFile(s->hfile, buf, count, &ret_count, &ov); + if (!ret) { + ret = GetOverlappedResult(s->hfile, &ov, &ret_count, TRUE); + if (!ret) + return -EIO; + else + return ret_count; + } + return ret_count; +} + +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; + + memset(&ov, 0, sizeof(ov)); + ov.Offset = offset; + ov.OffsetHigh = offset >> 32; + ret = WriteFile(s->hfile, buf, count, &ret_count, &ov); + if (!ret) { + ret = GetOverlappedResult(s->hfile, &ov, &ret_count, TRUE); + if (!ret) + return -EIO; + else + return ret_count; + } + return ret_count; +} + +#if 0 +#ifndef QEMU_TOOL +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); + } +} +#endif + +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; +#ifndef QEMU_TOOL + qemu_add_wait_object(acb->ov.hEvent, raw_aio_cb, acb); +#endif + 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; + } +#ifdef QEMU_TOOL + qemu_aio_release(acb); +#endif + 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; + } +#ifdef QEMU_TOOL + qemu_aio_release(acb); +#endif + return (BlockDriverAIOCB *)acb; +} + +static void raw_aio_cancel(BlockDriverAIOCB *blockacb) +{ +#ifndef QEMU_TOOL + 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 +} +#endif /* #if 0 */ + +static void raw_flush(BlockDriverState *bs) +{ + BDRVRawState *s = bs->opaque; + FlushFileBuffers(s->hfile); +} + +static void raw_close(BlockDriverState *bs) +{ + BDRVRawState *s = bs->opaque; + CloseHandle(s->hfile); +} + +static int raw_truncate(BlockDriverState *bs, int64_t offset) +{ + BDRVRawState *s = bs->opaque; + DWORD low, high; + + low = offset; + high = offset >> 32; + if (!SetFilePointer(s->hfile, low, &high, FILE_BEGIN)) + return -EIO; + if (!SetEndOfFile(s->hfile)) + return -EIO; + return 0; +} + +static int64_t raw_getlength(BlockDriverState *bs) +{ + BDRVRawState *s = bs->opaque; + LARGE_INTEGER l; + ULARGE_INTEGER available, total, total_free; + DISK_GEOMETRY dg; + DWORD count; + BOOL status; + + switch(s->type) { + case FTYPE_FILE: + l.LowPart = GetFileSize(s->hfile, &l.HighPart); + if (l.LowPart == 0xffffffffUL && GetLastError() != NO_ERROR) + return -EIO; + break; + case FTYPE_CD: + if (!GetDiskFreeSpaceEx(s->drive_path, &available, &total, &total_free)) + return -EIO; + l.QuadPart = total.QuadPart; + break; + case FTYPE_HARDDISK: + status = DeviceIoControl(s->hfile, IOCTL_DISK_GET_DRIVE_GEOMETRY, + NULL, 0, &dg, sizeof(dg), &count, NULL); + if (status != FALSE) { + l.QuadPart = dg.Cylinders.QuadPart * dg.TracksPerCylinder + * dg.SectorsPerTrack * dg.BytesPerSector; + } + break; + default: + return -EIO; + } + return l.QuadPart; +} + +static int raw_create(const char *filename, int64_t total_size, + const char *backing_file, int flags) +{ + int fd; + + if (flags || backing_file) + return -ENOTSUP; + + fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, + 0644); + if (fd < 0) + return -EIO; + set_sparse(fd); + ftruncate(fd, total_size * 512); + close(fd); + return 0; +} + +void qemu_aio_init(void) +{ +} + +void qemu_aio_poll(void) +{ +} + +void qemu_aio_flush(void) +{ +} + +void qemu_aio_wait_start(void) +{ +} + +void qemu_aio_wait(void) +{ +#ifndef QEMU_TOOL + qemu_bh_poll(); +#endif +} + +void qemu_aio_wait_end(void) +{ +} + +BlockDriver bdrv_raw = { + "raw", + sizeof(BDRVRawState), + NULL, /* no probe for protocols */ + raw_open, + NULL, + NULL, + raw_close, + raw_create, + raw_flush, + +#if 0 + .bdrv_aio_read = raw_aio_read, + .bdrv_aio_write = raw_aio_write, + .bdrv_aio_cancel = raw_aio_cancel, + .aiocb_size = sizeof(RawAIOCB); +#endif + .protocol_name = "file", + .bdrv_pread = raw_pread, + .bdrv_pwrite = raw_pwrite, + .bdrv_truncate = raw_truncate, + .bdrv_getlength = raw_getlength, +}; + +/***********************************************/ +/* host device */ + +static int find_cdrom(char *cdrom_name, int cdrom_name_size) +{ + char drives[256], *pdrv = drives; + UINT type; + + memset(drives, 0, sizeof(drives)); + GetLogicalDriveStrings(sizeof(drives), drives); + while(pdrv[0] != '\0') { + type = GetDriveType(pdrv); + switch(type) { + case DRIVE_CDROM: + snprintf(cdrom_name, cdrom_name_size, "\\\\.\\%c:", pdrv[0]); + return 0; + break; + } + pdrv += lstrlen(pdrv) + 1; + } + return -1; +} + +static int find_device_type(BlockDriverState *bs, const char *filename) +{ + BDRVRawState *s = bs->opaque; + UINT type; + const char *p; + + if (strstart(filename, "\\\\.\\", &p) || + strstart(filename, "//./", &p)) { + if (stristart(p, "PhysicalDrive", NULL)) + return FTYPE_HARDDISK; + snprintf(s->drive_path, sizeof(s->drive_path), "%c:\\", p[0]); + type = GetDriveType(s->drive_path); + if (type == DRIVE_CDROM) + return FTYPE_CD; + else + return FTYPE_FILE; + } else { + return FTYPE_FILE; + } +} + +static int hdev_open(BlockDriverState *bs, const char *filename, int flags) +{ + BDRVRawState *s = bs->opaque; + int access_flags, create_flags; + DWORD overlapped; + char device_name[64]; + + if (strstart(filename, "/dev/cdrom", NULL)) { + if (find_cdrom(device_name, sizeof(device_name)) < 0) + return -ENOENT; + filename = device_name; + } else { + /* transform drive letters into device name */ + if (((filename[0] >= 'a' && filename[0] <= 'z') || + (filename[0] >= 'A' && filename[0] <= 'Z')) && + filename[1] == ':' && filename[2] == '\0') { + snprintf(device_name, sizeof(device_name), "\\\\.\\%c:", filename[0]); + filename = device_name; + } + } + s->type = find_device_type(bs, filename); + + if ((flags & BDRV_O_ACCESS) == O_RDWR) { + access_flags = GENERIC_READ | GENERIC_WRITE; + } else { + access_flags = GENERIC_READ; + } + create_flags = OPEN_EXISTING; + +#ifdef QEMU_TOOL + overlapped = FILE_ATTRIBUTE_NORMAL; +#else + overlapped = FILE_FLAG_OVERLAPPED; +#endif + s->hfile = CreateFile(filename, access_flags, + FILE_SHARE_READ, NULL, + create_flags, overlapped, NULL); + if (s->hfile == INVALID_HANDLE_VALUE) { + int err = GetLastError(); + + if (err == ERROR_ACCESS_DENIED) + return -EACCES; + return -1; + } + return 0; +} + +#if 0 +/***********************************************/ +/* removable device additionnal commands */ + +static int raw_is_inserted(BlockDriverState *bs) +{ + return 1; +} + +static int raw_media_changed(BlockDriverState *bs) +{ + return -ENOTSUP; +} + +static int raw_eject(BlockDriverState *bs, int eject_flag) +{ + DWORD ret_count; + + if (s->type == FTYPE_FILE) + return -ENOTSUP; + if (eject_flag) { + DeviceIoControl(s->hfile, IOCTL_STORAGE_EJECT_MEDIA, + NULL, 0, NULL, 0, &lpBytesReturned, NULL); + } else { + DeviceIoControl(s->hfile, IOCTL_STORAGE_LOAD_MEDIA, + NULL, 0, NULL, 0, &lpBytesReturned, NULL); + } +} + +static int raw_set_locked(BlockDriverState *bs, int locked) +{ + return -ENOTSUP; +} +#endif + +BlockDriver bdrv_host_device = { + "host_device", + sizeof(BDRVRawState), + NULL, /* no probe for protocols */ + hdev_open, + NULL, + NULL, + raw_close, + NULL, + raw_flush, + +#if 0 + .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, +}; +#endif /* _WIN32 */ diff -r d2ef85c6bf84 -r 00618037d37d tools/ioemu/block-vmdk.c --- a/tools/ioemu/block-vmdk.c Tue May 08 10:38:06 2007 +0100 +++ b/tools/ioemu/block-vmdk.c Wed May 09 14:17:15 2007 +0100 @@ -22,6 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ + #include "vl.h" #include "block_int.h" @@ -59,7 +60,7 @@ typedef struct { #define L2_CACHE_SIZE 16 typedef struct BDRVVmdkState { - int fd; + BlockDriverState *hd; int64_t l1_table_offset; int64_t l1_backup_table_offset; uint32_t *l1_table; @@ -73,6 +74,7 @@ typedef struct BDRVVmdkState { uint32_t l2_cache_counts[L2_CACHE_SIZE]; unsigned int cluster_sectors; + uint32_t parent_cid; } BDRVVmdkState; static int vmdk_probe(const uint8_t *buf, int buf_size, const char *filename) @@ -89,27 +91,278 @@ static int vmdk_probe(const uint8_t *buf return 0; } -static int vmdk_open(BlockDriverState *bs, const char *filename) -{ - BDRVVmdkState *s = bs->opaque; - int fd, i; +#define CHECK_CID 1 + +#define SECTOR_SIZE 512 +#define DESC_SIZE 20*SECTOR_SIZE // 20 sectors of 512 bytes each +#define HEADER_SIZE 512 // first sector of 512 bytes + +static uint32_t vmdk_read_cid(BlockDriverState *bs, int parent) +{ + BDRVVmdkState *s = bs->opaque; + char desc[DESC_SIZE]; + uint32_t cid; + char *p_name, *cid_str; + size_t cid_str_size; + + /* the descriptor offset = 0x200 */ + if (bdrv_pread(s->hd, 0x200, desc, DESC_SIZE) != DESC_SIZE) + return 0; + + if (parent) { + cid_str = "parentCID"; + cid_str_size = sizeof("parentCID"); + } else { + cid_str = "CID"; + cid_str_size = sizeof("CID"); + } + + if ((p_name = strstr(desc,cid_str)) != 0) { + p_name += cid_str_size; + sscanf(p_name,"%x",&cid); + } + + return cid; +} + +static int vmdk_write_cid(BlockDriverState *bs, uint32_t cid) +{ + BDRVVmdkState *s = bs->opaque; + char desc[DESC_SIZE], tmp_desc[DESC_SIZE]; + char *p_name, *tmp_str; + + /* the descriptor offset = 0x200 */ + if (bdrv_pread(s->hd, 0x200, desc, DESC_SIZE) != DESC_SIZE) + return -1; + + tmp_str = strstr(desc,"parentCID"); + strcpy(tmp_desc, tmp_str); + if ((p_name = strstr(desc,"CID")) != 0) { + p_name += sizeof("CID"); + sprintf(p_name,"%x\n",cid); + strcat(desc,tmp_desc); + } + + if (bdrv_pwrite(s->hd, 0x200, desc, DESC_SIZE) != DESC_SIZE) + return -1; + return 0; +} + +static int vmdk_is_cid_valid(BlockDriverState *bs) +{ +#ifdef CHECK_CID + BDRVVmdkState *s = bs->opaque; + BlockDriverState *p_bs = s->hd->backing_hd; + uint32_t cur_pcid; + + if (p_bs) { + cur_pcid = vmdk_read_cid(p_bs,0); + if (s->parent_cid != cur_pcid) + // CID not valid + return 0; + } +#endif + // CID valid + return 1; +} + +static int vmdk_snapshot_create(const char *filename, const char *backing_file) +{ + int snp_fd, p_fd; + uint32_t p_cid; + char *p_name, *gd_buf, *rgd_buf; + const char *real_filename, *temp_str; + VMDK4Header header; + uint32_t gde_entries, gd_size; + int64_t gd_offset, rgd_offset, capacity, gt_size; + char p_desc[DESC_SIZE], s_desc[DESC_SIZE], hdr[HEADER_SIZE]; + char *desc_template = + "# Disk DescriptorFile\n" + "version=1\n" + "CID=%x\n" + "parentCID=%x\n" + "createType=\"monolithicSparse\"\n" + "parentFileNameHint=\"%s\"\n" + "\n" + "# Extent description\n" + "RW %lu SPARSE \"%s\"\n" + "\n" + "# The Disk Data Base \n" + "#DDB\n" + "\n"; + + snp_fd = open(filename, O_RDWR | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE, 0644); + if (snp_fd < 0) + return -1; + p_fd = open(backing_file, O_RDONLY | O_BINARY | O_LARGEFILE); + if (p_fd < 0) { + close(snp_fd); + return -1; + } + + /* read the header */ + if (lseek(p_fd, 0x0, SEEK_SET) == -1) + goto fail; + if (read(p_fd, hdr, HEADER_SIZE) != HEADER_SIZE) + goto fail; + + /* write the header */ + if (lseek(snp_fd, 0x0, SEEK_SET) == -1) + goto fail; + if (write(snp_fd, hdr, HEADER_SIZE) == -1) + goto fail; + + memset(&header, 0, sizeof(header)); + memcpy(&header,&hdr[4], sizeof(header)); // skip the VMDK4_MAGIC + + ftruncate(snp_fd, header.grain_offset << 9); + /* the descriptor offset = 0x200 */ + if (lseek(p_fd, 0x200, SEEK_SET) == -1) + goto fail; + if (read(p_fd, p_desc, DESC_SIZE) != DESC_SIZE) + goto fail; + + if ((p_name = strstr(p_desc,"CID")) != 0) { + p_name += sizeof("CID"); + sscanf(p_name,"%x",&p_cid); + } + + real_filename = filename; + if ((temp_str = strrchr(real_filename, '\\')) != NULL) + real_filename = temp_str + 1; + if ((temp_str = strrchr(real_filename, '/')) != NULL) + real_filename = temp_str + 1; + if ((temp_str = strrchr(real_filename, ':')) != NULL) + real_filename = temp_str + 1; + + sprintf(s_desc, desc_template, p_cid, p_cid, backing_file + , (uint32_t)header.capacity, real_filename); + + /* write the descriptor */ + if (lseek(snp_fd, 0x200, SEEK_SET) == -1) + goto fail; + if (write(snp_fd, s_desc, strlen(s_desc)) == -1) + goto fail; + + gd_offset = header.gd_offset * SECTOR_SIZE; // offset of GD table + rgd_offset = header.rgd_offset * SECTOR_SIZE; // offset of RGD table + capacity = header.capacity * SECTOR_SIZE; // Extent size + /* + * Each GDE span 32M disk, means: + * 512 GTE per GT, each GTE points to grain + */ + gt_size = (int64_t)header.num_gtes_per_gte * header.granularity * SECTOR_SIZE; + if (!gt_size) + goto fail; + gde_entries = (uint32_t)(capacity / gt_size); // number of gde/rgde + gd_size = gde_entries * sizeof(uint32_t); + + /* write RGD */ + rgd_buf = qemu_malloc(gd_size); + if (!rgd_buf) + goto fail; + if (lseek(p_fd, rgd_offset, SEEK_SET) == -1) + goto fail_rgd; + if (read(p_fd, rgd_buf, gd_size) != gd_size) + goto fail_rgd; + if (lseek(snp_fd, rgd_offset, SEEK_SET) == -1) + goto fail_rgd; + if (write(snp_fd, rgd_buf, gd_size) == -1) + goto fail_rgd; + qemu_free(rgd_buf); + + /* write GD */ + gd_buf = qemu_malloc(gd_size); + if (!gd_buf) + goto fail_rgd; + if (lseek(p_fd, gd_offset, SEEK_SET) == -1) + goto fail_gd; + if (read(p_fd, gd_buf, gd_size) != gd_size) + goto fail_gd; + if (lseek(snp_fd, gd_offset, SEEK_SET) == -1) + goto fail_gd; + if (write(snp_fd, gd_buf, gd_size) == -1) + goto fail_gd; + qemu_free(gd_buf); + + close(p_fd); + close(snp_fd); + return 0; + + fail_gd: + qemu_free(gd_buf); + fail_rgd: + qemu_free(rgd_buf); + fail: + close(p_fd); + close(snp_fd); + return -1; +} + +static void vmdk_parent_close(BlockDriverState *bs) +{ + if (bs->backing_hd) + bdrv_close(bs->backing_hd); +} + + +static int vmdk_parent_open(BlockDriverState *bs, const char * filename) +{ + BDRVVmdkState *s = bs->opaque; + char *p_name; + char desc[DESC_SIZE]; + char parent_img_name[1024]; + + /* the descriptor offset = 0x200 */ + if (bdrv_pread(s->hd, 0x200, desc, DESC_SIZE) != DESC_SIZE) + return -1; + + if ((p_name = strstr(desc,"parentFileNameHint")) != 0) { + char *end_name; + struct stat file_buf; + + p_name += sizeof("parentFileNameHint") + 1; + if ((end_name = strchr(p_name,'\"')) == 0) + return -1; + + strncpy(s->hd->backing_file, p_name, end_name - p_name); + if (stat(s->hd->backing_file, &file_buf) != 0) { + path_combine(parent_img_name, sizeof(parent_img_name), + filename, s->hd->backing_file); + } else { + strcpy(parent_img_name, s->hd->backing_file); + } + + s->hd->backing_hd = bdrv_new(""); + if (!s->hd->backing_hd) { + failure: + bdrv_close(s->hd); + return -1; + } + if (bdrv_open(s->hd->backing_hd, parent_img_name, 0) < 0) + goto failure; + } + + return 0; +} + +static int vmdk_open(BlockDriverState *bs, const char *filename, int flags) +{ + BDRVVmdkState *s = bs->opaque; uint32_t magic; - int l1_size; - - fd = open(filename, O_RDWR | O_BINARY | O_LARGEFILE); - if (fd < 0) { - fd = open(filename, O_RDONLY | O_BINARY | O_LARGEFILE); - if (fd < 0) - return -1; - bs->read_only = 1; - } - if (read(fd, &magic, sizeof(magic)) != sizeof(magic)) - goto fail; + int l1_size, i, ret; + + ret = bdrv_file_open(&s->hd, filename, flags); + if (ret < 0) + return ret; + if (bdrv_pread(s->hd, 0, &magic, sizeof(magic)) != sizeof(magic)) + goto fail; + magic = be32_to_cpu(magic); if (magic == VMDK3_MAGIC) { VMDK3Header header; - if (read(fd, &header, sizeof(header)) != - sizeof(header)) + + if (bdrv_pread(s->hd, sizeof(magic), &header, sizeof(header)) != sizeof(header)) goto fail; s->cluster_sectors = le32_to_cpu(header.granularity); s->l2_size = 1 << 9; @@ -120,8 +373,8 @@ static int vmdk_open(BlockDriverState *b s->l1_entry_sectors = s->l2_size * s->cluster_sectors; } else if (magic == VMDK4_MAGIC) { VMDK4Header header; - - if (read(fd, &header, sizeof(header)) != sizeof(header)) + + if (bdrv_pread(s->hd, sizeof(magic), &header, sizeof(header)) != sizeof(header)) goto fail; bs->total_sectors = le64_to_cpu(header.capacity); s->cluster_sectors = le64_to_cpu(header.granularity); @@ -133,17 +386,22 @@ static int vmdk_open(BlockDriverState *b / s->l1_entry_sectors; s->l1_table_offset = le64_to_cpu(header.rgd_offset) << 9; s->l1_backup_table_offset = le64_to_cpu(header.gd_offset) << 9; + + // try to open parent images, if exist + if (vmdk_parent_open(bs, filename) != 0) + goto fail; + // write the CID once after the image creation + s->parent_cid = vmdk_read_cid(bs,1); } else { goto fail; } + /* read the L1 table */ l1_size = s->l1_size * sizeof(uint32_t); s->l1_table = qemu_malloc(l1_size); if (!s->l1_table) goto fail; - if (lseek(fd, s->l1_table_offset, SEEK_SET) == -1) - goto fail; - if (read(fd, s->l1_table, l1_size) != l1_size) + if (bdrv_pread(s->hd, s->l1_table_offset, s->l1_table, l1_size) != l1_size) goto fail; for(i = 0; i < s->l1_size; i++) { le32_to_cpus(&s->l1_table[i]); @@ -153,9 +411,7 @@ static int vmdk_open(BlockDriverState *b s->l1_backup_table = qemu_malloc(l1_size); if (!s->l1_backup_table) goto fail; - if (lseek(fd, s->l1_backup_table_offset, SEEK_SET) == -1) - goto fail; - if (read(fd, s->l1_backup_table, l1_size) != l1_size) + if (bdrv_pread(s->hd, s->l1_backup_table_offset, s->l1_backup_table, l1_size) != l1_size) goto fail; for(i = 0; i < s->l1_size; i++) { le32_to_cpus(&s->l1_backup_table[i]); @@ -165,14 +421,41 @@ static int vmdk_open(BlockDriverState *b s->l2_cache = qemu_malloc(s->l2_size * L2_CACHE_SIZE * sizeof(uint32_t)); if (!s->l2_cache) goto fail; - s->fd = fd; return 0; fail: qemu_free(s->l1_backup_table); qemu_free(s->l1_table); qemu_free(s->l2_cache); - close(fd); + bdrv_delete(s->hd); return -1; +} + +static uint64_t get_cluster_offset(BlockDriverState *bs, uint64_t offset, int allocate); + +static int get_whole_cluster(BlockDriverState *bs, uint64_t cluster_offset, + uint64_t offset, int allocate) +{ + uint64_t parent_cluster_offset; + BDRVVmdkState *s = bs->opaque; + uint8_t whole_grain[s->cluster_sectors*512]; // 128 sectors * 512 bytes each = grain size 64KB + + // we will be here if it's first write on non-exist grain(cluster). + // try to read from parent image, if exist + if (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(s->hd->backing_hd, offset, allocate); + if (bdrv_pread(ps->hd, parent_cluster_offset, whole_grain, ps->cluster_sectors*512) != + ps->cluster_sectors*512) + return -1; + + if (bdrv_pwrite(s->hd, cluster_offset << 9, whole_grain, sizeof(whole_grain)) != + sizeof(whole_grain)) + return -1; + } + return 0; } static uint64_t get_cluster_offset(BlockDriverState *bs, @@ -212,34 +495,41 @@ static uint64_t get_cluster_offset(Block } } l2_table = s->l2_cache + (min_index * s->l2_size); - lseek(s->fd, (int64_t)l2_offset * 512, SEEK_SET); - if (read(s->fd, l2_table, s->l2_size * sizeof(uint32_t)) != - s->l2_size * sizeof(uint32_t)) + if (bdrv_pread(s->hd, (int64_t)l2_offset * 512, l2_table, s->l2_size * sizeof(uint32_t)) != + s->l2_size * sizeof(uint32_t)) return 0; + s->l2_cache_offsets[min_index] = l2_offset; s->l2_cache_counts[min_index] = 1; found: l2_index = ((offset >> 9) / s->cluster_sectors) % s->l2_size; cluster_offset = le32_to_cpu(l2_table[l2_index]); if (!cluster_offset) { + struct stat file_buf; + if (!allocate) return 0; - cluster_offset = lseek(s->fd, 0, SEEK_END); - ftruncate(s->fd, cluster_offset + (s->cluster_sectors << 9)); + stat(s->hd->filename, &file_buf); + cluster_offset = file_buf.st_size; + bdrv_truncate(s->hd, cluster_offset + (s->cluster_sectors << 9)); + cluster_offset >>= 9; /* update L2 table */ tmp = cpu_to_le32(cluster_offset); l2_table[l2_index] = tmp; - lseek(s->fd, ((int64_t)l2_offset * 512) + (l2_index * sizeof(tmp)), SEEK_SET); - if (write(s->fd, &tmp, sizeof(tmp)) != sizeof(tmp)) + if (bdrv_pwrite(s->hd, ((int64_t)l2_offset * 512) + (l2_index * sizeof(tmp)), + &tmp, sizeof(tmp)) != sizeof(tmp)) return 0; /* update backup L2 table */ if (s->l1_backup_table_offset != 0) { l2_offset = s->l1_backup_table[l1_index]; - lseek(s->fd, ((int64_t)l2_offset * 512) + (l2_index * sizeof(tmp)), SEEK_SET); - if (write(s->fd, &tmp, sizeof(tmp)) != sizeof(tmp)) + if (bdrv_pwrite(s->hd, ((int64_t)l2_offset * 512) + (l2_index * sizeof(tmp)), + &tmp, sizeof(tmp)) != sizeof(tmp)) return 0; } + + if (get_whole_cluster(bs, cluster_offset, offset, allocate) == -1) + return 0; } cluster_offset <<= 9; return cluster_offset; @@ -265,9 +555,9 @@ static int vmdk_read(BlockDriverState *b uint8_t *buf, int nb_sectors) { BDRVVmdkState *s = bs->opaque; - int ret, index_in_cluster, n; + int index_in_cluster, n, ret; uint64_t cluster_offset; - + while (nb_sectors > 0) { cluster_offset = get_cluster_offset(bs, sector_num << 9, 0); index_in_cluster = sector_num % s->cluster_sectors; @@ -275,11 +565,18 @@ static int vmdk_read(BlockDriverState *b if (n > nb_sectors) n = nb_sectors; if (!cluster_offset) { - memset(buf, 0, 512 * n); + // try to read from parent image, if exist + if (s->hd->backing_hd) { + if (!vmdk_is_cid_valid(bs)) + return -1; + ret = bdrv_read(s->hd->backing_hd, sector_num, buf, n); + if (ret < 0) + return -1; + } else { + memset(buf, 0, 512 * n); + } } else { - lseek(s->fd, cluster_offset + index_in_cluster * 512, SEEK_SET); - ret = read(s->fd, buf, n * 512); - if (ret != n * 512) + if(bdrv_pread(s->hd, cluster_offset + index_in_cluster * 512, buf, n * 512) != n * 512) return -1; } nb_sectors -= n; @@ -293,8 +590,9 @@ static int vmdk_write(BlockDriverState * const uint8_t *buf, int nb_sectors) { BDRVVmdkState *s = bs->opaque; - int ret, index_in_cluster, n; + int index_in_cluster, n; uint64_t cluster_offset; + static int cid_update = 0; while (nb_sectors > 0) { index_in_cluster = sector_num & (s->cluster_sectors - 1); @@ -304,13 +602,17 @@ static int vmdk_write(BlockDriverState * cluster_offset = get_cluster_offset(bs, sector_num << 9, 1); if (!cluster_offset) return -1; - lseek(s->fd, cluster_offset + index_in_cluster * 512, SEEK_SET); - ret = write(s->fd, buf, n * 512); - if (ret != n * 512) + if (bdrv_pwrite(s->hd, cluster_offset + index_in_cluster * 512, buf, n * 512) != n * 512) return -1; nb_sectors -= n; sector_num += n; buf += n * 512; + + // update CID on the first write every time the virtual disk is opened + if (!cid_update) { + vmdk_write_cid(bs, time(NULL)); + cid_update++; + } } return 0; } @@ -334,7 +636,7 @@ static int vmdk_create(const char *filen "# The Disk Data Base \n" "#DDB\n" "\n" - "ddb.virtualHWVersion = \"3\"\n" + "ddb.virtualHWVersion = \"4\"\n" "ddb.geometry.cylinders = \"%lu\"\n" "ddb.geometry.heads = \"16\"\n" "ddb.geometry.sectors = \"63\"\n" @@ -343,6 +645,9 @@ static int vmdk_create(const char *filen const char *real_filename, *temp_str; /* XXX: add support for backing file */ + if (backing_file) { + return vmdk_snapshot_create(filename, backing_file); + } fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE, 0644); @@ -421,15 +726,18 @@ static void vmdk_close(BlockDriverState static void vmdk_close(BlockDriverState *bs) { BDRVVmdkState *s = bs->opaque; + qemu_free(s->l1_table); qemu_free(s->l2_cache); - close(s->fd); + bdrv_delete(s->hd); + // try to close parent image, if exist + vmdk_parent_close(s->hd); } static void vmdk_flush(BlockDriverState *bs) { BDRVVmdkState *s = bs->opaque; - fsync(s->fd); + bdrv_flush(s->hd); } BlockDriver bdrv_vmdk = { diff -r d2ef85c6bf84 -r 00618037d37d tools/ioemu/block-vpc.c --- a/tools/ioemu/block-vpc.c Tue May 08 10:38:06 2007 +0100 +++ b/tools/ioemu/block-vpc.c Wed May 09 14:17:15 2007 +0100 @@ -86,19 +86,16 @@ static int vpc_probe(const uint8_t *buf, return 0; } -static int vpc_open(BlockDriverState *bs, const char *filename) +static int vpc_open(BlockDriverState *bs, const char *filename, int flags) { BDRVVPCState *s = bs->opaque; int fd, i; struct vpc_subheader header; - fd = open(filename, O_RDWR | O_BINARY | O_LARGEFILE); - if (fd < 0) { - fd = open(filename, O_RDONLY | O_BINARY | O_LARGEFILE); - if (fd < 0) - return -1; - } - + fd = open(filename, O_RDONLY | O_BINARY); + if (fd < 0) + return -1; + bs->read_only = 1; // no write support yet s->fd = fd; diff -r d2ef85c6bf84 -r 00618037d37d tools/ioemu/block-vvfat.c --- a/tools/ioemu/block-vvfat.c Tue May 08 10:38:06 2007 +0100 +++ b/tools/ioemu/block-vvfat.c Wed May 09 14:17:15 2007 +0100 @@ -61,7 +61,7 @@ void nonono(const char* file, int line, exit(-5); } #undef assert -#define assert(a) if (!(a)) nonono(__FILE__, __LINE__, #a) +#define assert(a) do {if (!(a)) nonono(__FILE__, __LINE__, #a);}while(0) #endif #else @@ -351,13 +351,6 @@ typedef struct BDRVVVFATState { } BDRVVVFATState; -static int vvfat_probe(const uint8_t *buf, int buf_size, const char *filename) -{ - if (strstart(filename, "fat:", NULL)) - return 100; - return 0; -} - static void init_mbr(BDRVVVFATState* s) { /* TODO: if the files mbr.img and bootsect.img exist, use them */ @@ -954,18 +947,22 @@ static int init_directories(BDRVVVFATSta return 0; } +#ifdef DEBUG static BDRVVVFATState *vvv = NULL; +#endif static int enable_write_target(BDRVVVFATState *s); static int is_consistent(BDRVVVFATState *s); -static int vvfat_open(BlockDriverState *bs, const char* dirname) +static int vvfat_open(BlockDriverState *bs, const char* dirname, int flags) { BDRVVVFATState *s = bs->opaque; int floppy = 0; int i; +#ifdef DEBUG vvv = s; +#endif DLOG(if (stderr == NULL) { stderr = fopen("vvfat.log", "a"); @@ -1040,7 +1037,6 @@ DLOG(if (stderr == NULL) { bs->heads = bs->cyls = bs->secs = 0; // assert(is_consistent(s)); - return 0; } @@ -2178,7 +2174,7 @@ static int commit_one_file(BDRVVVFATStat for (i = s->cluster_size; i < offset; i += s->cluster_size) c = modified_fat_get(s, c); - fd = open(mapping->path, O_RDWR | O_CREAT, 0666); + fd = open(mapping->path, O_RDWR | O_CREAT | O_BINARY, 0666); if (fd < 0) { fprintf(stderr, "Could not open %s... (%s, %d)\n", mapping->path, strerror(errno), errno); @@ -2732,8 +2728,7 @@ static int enable_write_target(BDRVVVFAT array_init(&(s->commits), sizeof(commit_t)); s->qcow_filename = malloc(1024); - strcpy(s->qcow_filename, "/tmp/vl.XXXXXX"); - get_tmp_filename(s->qcow_filename, strlen(s->qcow_filename) + 1); + get_tmp_filename(s->qcow_filename, 1024); if (bdrv_create(&bdrv_qcow, s->qcow_filename, s->sector_count, "fat:", 0) < 0) return -1; @@ -2767,14 +2762,15 @@ BlockDriver bdrv_vvfat = { BlockDriver bdrv_vvfat = { "vvfat", sizeof(BDRVVVFATState), - vvfat_probe, + 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 + vvfat_is_allocated, + .protocol_name = "fat", }; #ifdef DEBUG diff -r d2ef85c6bf84 -r 00618037d37d tools/ioemu/block.c --- a/tools/ioemu/block.c Tue May 08 10:38:06 2007 +0100 +++ b/tools/ioemu/block.c Wed May 09 14:17:15 2007 +0100 @@ -32,85 +32,108 @@ #include <sys/disk.h> #endif -#ifdef CONFIG_COCOA -#include <paths.h> -#include <sys/param.h> -#include <IOKit/IOKitLib.h> -#include <IOKit/IOBSD.h> -#include <IOKit/storage/IOMediaBSDClient.h> -#include <IOKit/storage/IOMedia.h> -#include <IOKit/storage/IOCDMedia.h> -//#include <IOKit/storage/IOCDTypes.h> -#include <CoreFoundation/CoreFoundation.h> -#endif - -#ifdef __sun__ -#include <sys/dkio.h> -#endif +#define SECTOR_BITS 9 +#define SECTOR_SIZE (1 << SECTOR_BITS) + +typedef struct BlockDriverAIOCBSync { + BlockDriverAIOCB common; + QEMUBH *bh; + int ret; +} BlockDriverAIOCBSync; + +static BlockDriverAIOCB *bdrv_aio_read_em(BlockDriverState *bs, + int64_t sector_num, uint8_t *buf, int nb_sectors, + BlockDriverCompletionFunc *cb, void *opaque); +static BlockDriverAIOCB *bdrv_aio_write_em(BlockDriverState *bs, + int64_t sector_num, const uint8_t *buf, int nb_sectors, + BlockDriverCompletionFunc *cb, void *opaque); +static void bdrv_aio_cancel_em(BlockDriverAIOCB *acb); +static int bdrv_read_em(BlockDriverState *bs, int64_t sector_num, + uint8_t *buf, int nb_sectors); +static int bdrv_write_em(BlockDriverState *bs, int64_t sector_num, + const uint8_t *buf, int nb_sectors); static BlockDriverState *bdrv_first; static BlockDriver *first_drv; -#ifdef CONFIG_COCOA -static kern_return_t FindEjectableCDMedia( io_iterator_t *mediaIterator ); -static kern_return_t GetBSDPath( io_iterator_t mediaIterator, char *bsdPath, CFIndex maxPathSize ); - -kern_return_t FindEjectableCDMedia( io_iterator_t *mediaIterator ) -{ - kern_return_t kernResult; - mach_port_t masterPort; - CFMutableDictionaryRef classesToMatch; - - kernResult = IOMasterPort( MACH_PORT_NULL, &masterPort ); - if ( KERN_SUCCESS != kernResult ) { - printf( "IOMasterPort returned %d\n", kernResult ); - } - - classesToMatch = IOServiceMatching( kIOCDMediaClass ); - if ( classesToMatch == NULL ) { - printf( "IOServiceMatching returned a NULL dictionary.\n" ); +int path_is_absolute(const char *path) +{ + const char *p; +#ifdef _WIN32 + /* specific case for names like: "\\.\d:" */ + if (*path == '/' || *path == '\\') + return 1; +#endif + p = strchr(path, ':'); + if (p) + p++; + else + p = path; +#ifdef _WIN32 + return (*p == '/' || *p == '\\'); +#else + return (*p == '/'); +#endif +} + +/* if filename is absolute, just copy it to dest. Otherwise, build a + path to it by considering it is relative to base_path. URL are + supported. */ +void path_combine(char *dest, int dest_size, + const char *base_path, + const char *filename) +{ + const char *p, *p1; + int len; + + if (dest_size <= 0) + return; + if (path_is_absolute(filename)) { + pstrcpy(dest, dest_size, filename); } else { - CFDictionarySetValue( classesToMatch, CFSTR( kIOMediaEjectableKey ), kCFBooleanTrue ); - } - kernResult = IOServiceGetMatchingServices( masterPort, classesToMatch, mediaIterator ); - if ( KERN_SUCCESS != kernResult ) - { - printf( "IOServiceGetMatchingServices returned %d\n", kernResult ); - } - - return kernResult; -} - -kern_return_t GetBSDPath( io_iterator_t mediaIterator, char *bsdPath, CFIndex maxPathSize ) -{ - io_object_t nextMedia; - kern_return_t kernResult = KERN_FAILURE; - *bsdPath = '\0'; - nextMedia = IOIteratorNext( mediaIterator ); - if ( nextMedia ) - { - CFTypeRef bsdPathAsCFString; - bsdPathAsCFString = IORegistryEntryCreateCFProperty( nextMedia, CFSTR( kIOBSDNameKey ), kCFAllocatorDefault, 0 ); - if ( bsdPathAsCFString ) { - size_t devPathLength; - strcpy( bsdPath, _PATH_DEV ); - strcat( bsdPath, "r" ); - devPathLength = strlen( bsdPath ); - if ( CFStringGetCString( bsdPathAsCFString, bsdPath + devPathLength, maxPathSize - devPathLength, kCFStringEncodingASCII ) ) { - kernResult = KERN_SUCCESS; - } - CFRelease( bsdPathAsCFString ); - } - IOObjectRelease( nextMedia ); - } - - return kernResult; -} - + p = strchr(base_path, ':'); + if (p) + p++; + else + p = base_path; + p1 = strrchr(base_path, '/'); +#ifdef _WIN32 + { + const char *p2; + p2 = strrchr(base_path, '\\'); + if (!p1 || p2 > p1) + p1 = p2; + } #endif + if (p1) + p1++; + else + p1 = base_path; + if (p1 > p) + p = p1; + len = p - base_path; + if (len > dest_size - 1) + len = dest_size - 1; + memcpy(dest, base_path, len); + dest[len] = '\0'; + pstrcat(dest, dest_size, filename); + } +} + void bdrv_register(BlockDriver *bdrv) { + if (!bdrv->bdrv_aio_read) { + /* add AIO emulation layer */ + bdrv->bdrv_aio_read = bdrv_aio_read_em; + bdrv->bdrv_aio_write = bdrv_aio_write_em; + bdrv->bdrv_aio_cancel = bdrv_aio_cancel_em; + bdrv->aiocb_size = sizeof(BlockDriverAIOCBSync); + } else if (!bdrv->bdrv_read && !bdrv->bdrv_pread) { + /* add synchronous IO emulation layer */ + bdrv->bdrv_read = bdrv_read_em; + bdrv->bdrv_write = bdrv_write_em; + } bdrv->next = first_drv; first_drv = bdrv; } @@ -156,14 +179,10 @@ int bdrv_create(BlockDriver *drv, #ifdef _WIN32 void get_tmp_filename(char *filename, int size) { - char* p = strrchr(filename, '/'); - - if (p == NULL) - return; - - /* XXX: find a better function */ - tmpnam(p); - *p = '/'; + char temp_dir[MAX_PATH]; + + GetTempPath(MAX_PATH, temp_dir); + GetTempFileName(temp_dir, "qem", 0, filename); } #else void get_tmp_filename(char *filename, int size) @@ -176,101 +195,141 @@ void get_tmp_filename(char *filename, in } #endif +#ifdef _WIN32 +static int is_windows_drive_prefix(const char *filename) +{ + return (((filename[0] >= 'a' && filename[0] <= 'z') || + (filename[0] >= 'A' && filename[0] <= 'Z')) && + filename[1] == ':'); +} + +static int is_windows_drive(const char *filename) +{ + if (is_windows_drive_prefix(filename) && + filename[2] == '\0') + return 1; + if (strstart(filename, "\\\\.\\", NULL) || + strstart(filename, "//./", NULL)) + return 1; + return 0; +} +#endif + +static BlockDriver *find_protocol(const char *filename) +{ + BlockDriver *drv1; + char protocol[128]; + int len; + const char *p; + +#ifdef _WIN32 + if (is_windows_drive(filename) || + is_windows_drive_prefix(filename)) + return &bdrv_raw; +#endif + p = strchr(filename, ':'); + if (!p) + return &bdrv_raw; + len = p - filename; + if (len > sizeof(protocol) - 1) + len = sizeof(protocol) - 1; + memcpy(protocol, filename, len); + protocol[len] = '\0'; + for(drv1 = first_drv; drv1 != NULL; drv1 = drv1->next) { + if (drv1->protocol_name && + !strcmp(drv1->protocol_name, protocol)) + return drv1; + } + return NULL; +} + /* XXX: force raw format if block or character device ? It would simplify the BSD case */ static BlockDriver *find_image_format(const char *filename) { - int fd, ret, score, score_max; + int ret, score, score_max; BlockDriver *drv1, *drv; - uint8_t *buf; - size_t bufsize = 1024; - - fd = open(filename, O_RDONLY | O_BINARY | O_LARGEFILE); - if (fd < 0) { - buf = NULL; - ret = 0; - } else { -#ifdef DIOCGSECTORSIZE - { - unsigned int sectorsize = 512; - if (!ioctl(fd, DIOCGSECTORSIZE, §orsize) && - sectorsize > bufsize) - bufsize = sectorsize; - } + uint8_t buf[2048]; + BlockDriverState *bs; + + /* detect host devices. By convention, /dev/cdrom[N] is always + recognized as a host CDROM */ + if (strstart(filename, "/dev/cdrom", NULL)) + return &bdrv_host_device; +#ifdef _WIN32 + if (is_windows_drive(filename)) + return &bdrv_host_device; +#else + { + struct stat st; + if (stat(filename, &st) >= 0 && + (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode))) { + return &bdrv_host_device; + } + } #endif -#ifdef CONFIG_COCOA - u_int32_t blockSize = 512; - if ( !ioctl( fd, DKIOCGETBLOCKSIZE, &blockSize ) && blockSize > bufsize) { - bufsize = blockSize; - } -#endif - buf = qemu_malloc(bufsize); - if (!buf) - return NULL; - ret = read(fd, buf, bufsize); - if (ret < 0) { - close(fd); - qemu_free(buf); - return NULL; - } - close(fd); - } - drv = NULL; + drv = find_protocol(filename); + /* no need to test disk image formats for vvfat */ + if (drv == &bdrv_vvfat) + return drv; + + ret = bdrv_file_open(&bs, filename, BDRV_O_RDONLY); + if (ret < 0) + return NULL; + ret = bdrv_pread(bs, 0, buf, sizeof(buf)); + bdrv_delete(bs); + if (ret < 0) { + return NULL; + } + score_max = 0; for(drv1 = first_drv; drv1 != NULL; drv1 = drv1->next) { - score = drv1->bdrv_probe(buf, ret, filename); - if (score > score_max) { - score_max = score; - drv = drv1; - } - } - qemu_free(buf); + if (drv1->bdrv_probe) { + score = drv1->bdrv_probe(buf, ret, filename); + if (score > score_max) { + score_max = score; + drv = drv1; + } + } + } return drv; } -int bdrv_open(BlockDriverState *bs, const char *filename, int snapshot) -{ -#ifdef CONFIG_COCOA - if ( strncmp( filename, "/dev/cdrom", 10 ) == 0 ) { - kern_return_t kernResult; - io_iterator_t mediaIterator; - char bsdPath[ MAXPATHLEN ]; - int fd; - - kernResult = FindEjectableCDMedia( &mediaIterator ); - kernResult = GetBSDPath( mediaIterator, bsdPath, sizeof( bsdPath ) ); - - if ( bsdPath[ 0 ] != '\0' ) { - strcat(bsdPath,"s0"); - /* some CDs don't have a partition 0 */ - fd = open(bsdPath, O_RDONLY | O_BINARY | O_LARGEFILE); - if (fd < 0) { - bsdPath[strlen(bsdPath)-1] = '1'; - } else { - close(fd); - } - filename = bsdPath; - } - - if ( mediaIterator ) - IOObjectRelease( mediaIterator ); - } -#endif - return bdrv_open2(bs, filename, snapshot, NULL); -} - -int bdrv_open2(BlockDriverState *bs, const char *filename, int snapshot, +int bdrv_file_open(BlockDriverState **pbs, const char *filename, int flags) +{ + BlockDriverState *bs; + int ret; + + bs = bdrv_new(""); + if (!bs) + return -ENOMEM; + ret = bdrv_open2(bs, filename, flags | BDRV_O_FILE, NULL); + if (ret < 0) { + bdrv_delete(bs); + return ret; + } + *pbs = bs; + return 0; +} + +int bdrv_open(BlockDriverState *bs, const char *filename, int flags) +{ + return bdrv_open2(bs, filename, flags, NULL); +} + +int bdrv_open2(BlockDriverState *bs, const char *filename, int flags, BlockDriver *drv) { - int ret; + int ret, open_flags; char tmp_filename[1024]; + char backing_filename[1024]; bs->read_only = 0; bs->is_temporary = 0; bs->encrypted = 0; - if (snapshot) { + if (flags & BDRV_O_SNAPSHOT) { BlockDriverState *bs1; int64_t total_size; @@ -280,19 +339,19 @@ int bdrv_open2(BlockDriverState *bs, con /* if there is a backing file, use it */ bs1 = bdrv_new(""); if (!bs1) { - return -1; + return -ENOMEM; } if (bdrv_open(bs1, filename, 0) < 0) { bdrv_delete(bs1); return -1; } - total_size = bs1->total_sectors; + total_size = bdrv_getlength(bs1) >> SECTOR_BITS; bdrv_delete(bs1); get_tmp_filename(tmp_filename, sizeof(tmp_filename)); - /* XXX: use cow for linux as it is more efficient ? */ - if (bdrv_create(&bdrv_qcow, tmp_filename, - total_size, filename, 0) < 0) { + realpath(filename, backing_filename); + if (bdrv_create(&bdrv_qcow2, tmp_filename, + total_size, backing_filename, 0) < 0) { return -1; } filename = tmp_filename; @@ -300,41 +359,62 @@ int bdrv_open2(BlockDriverState *bs, con } pstrcpy(bs->filename, sizeof(bs->filename), filename); - if (!drv) { - drv = find_image_format(filename); + if (flags & BDRV_O_FILE) { + drv = find_protocol(filename); if (!drv) - return -1; + return -ENOENT; + } else { + if (!drv) { + drv = find_image_format(filename); + if (!drv) + return -1; + } } bs->drv = drv; bs->opaque = qemu_mallocz(drv->instance_size); if (bs->opaque == NULL && drv->instance_size > 0) return -1; - - ret = drv->bdrv_open(bs, filename); + /* Note: for compatibility, we open disk image files as RDWR, and + RDONLY as fallback */ + if (!(flags & BDRV_O_FILE)) + open_flags = BDRV_O_RDWR; + else + open_flags = flags & ~(BDRV_O_FILE | BDRV_O_SNAPSHOT); + ret = drv->bdrv_open(bs, filename, open_flags); + if (ret == -EACCES && !(flags & BDRV_O_FILE)) { + ret = drv->bdrv_open(bs, filename, BDRV_O_RDONLY); + bs->read_only = 1; + } if (ret < 0) { qemu_free(bs->opaque); - return -1; + bs->opaque = NULL; + bs->drv = NULL; + return ret; + } + if (drv->bdrv_getlength) { + bs->total_sectors = bdrv_getlength(bs) >> SECTOR_BITS; } #ifndef _WIN32 if (bs->is_temporary) { unlink(filename); } #endif - if (bs->backing_file[0] != '\0' && drv->bdrv_is_allocated) { + if (bs->backing_file[0] != '\0') { /* if there is a backing file, use it */ bs->backing_hd = bdrv_new(""); if (!bs->backing_hd) { fail: bdrv_close(bs); - return -1; - } - if (bdrv_open(bs->backing_hd, bs->backing_file, 0) < 0) + return -ENOMEM; + } + path_combine(backing_filename, sizeof(backing_filename), + filename, bs->backing_file); + if (bdrv_open(bs->backing_hd, backing_filename, 0) < 0) goto fail; } - bs->inserted = 1; - /* call the change callback */ + bs->media_changed = 1; if (bs->change_cb) bs->change_cb(bs->change_opaque); @@ -343,7 +423,7 @@ int bdrv_open2(BlockDriverState *bs, con void bdrv_close(BlockDriverState *bs) { - if (bs->inserted) { + if (bs->drv) { if (bs->backing_hd) bdrv_delete(bs->backing_hd); bs->drv->bdrv_close(bs); @@ -355,9 +435,9 @@ void bdrv_close(BlockDriverState *bs) #endif bs->opaque = NULL; bs->drv = NULL; - bs->inserted = 0; /* call the change callback */ + bs->media_changed = 1; if (bs->change_cb) bs->change_cb(bs->change_opaque); } @@ -373,12 +453,13 @@ void bdrv_delete(BlockDriverState *bs) /* commit COW file into the raw image */ int bdrv_commit(BlockDriverState *bs) { - int64_t i; + BlockDriver *drv = bs->drv; + int64_t i, total_sectors; int n, j; unsigned char sector[512]; - if (!bs->inserted) - return -ENOENT; + if (!drv) + return -ENOMEDIUM; if (bs->read_only) { return -EACCES; @@ -388,8 +469,9 @@ int bdrv_commit(BlockDriverState *bs) return -ENOTSUP; } - for (i = 0; i < bs->total_sectors;) { - if (bs->drv->bdrv_is_allocated(bs, i, 65536, &n)) { + total_sectors = bdrv_getlength(bs) >> SECTOR_BITS; + for (i = 0; i < total_sectors;) { + if (drv->bdrv_is_allocated(bs, i, 65536, &n)) { for(j = 0; j < n; j++) { if (bdrv_read(bs, i, sector, 1) != 0) { return -EIO; @@ -405,72 +487,241 @@ int bdrv_commit(BlockDriverState *bs) } } - if (bs->drv->bdrv_make_empty) - return bs->drv->bdrv_make_empty(bs); + if (drv->bdrv_make_empty) + return drv->bdrv_make_empty(bs); return 0; } -/* return -1 if error */ +/* return < 0 if error. See bdrv_write() for the return codes */ int bdrv_read(BlockDriverState *bs, int64_t sector_num, uint8_t *buf, int nb_sectors) { - int ret, n; - BlockDriver *drv = bs->drv; - - if (!bs->inserted) - return -1; + BlockDriver *drv = bs->drv; + + if (!drv) + return -ENOMEDIUM; + if (sector_num < 0) - return -1; - - while (nb_sectors > 0) { - if (sector_num == 0 && bs->boot_sector_enabled) { + return -EINVAL; + + if (sector_num == 0 && bs->boot_sector_enabled && nb_sectors > 0) { memcpy(buf, bs->boot_sector_data, 512); - n = 1; - } else if (bs->backing_hd) { - if (drv->bdrv_is_allocated(bs, sector_num, nb_sectors, &n)) { - ret = drv->bdrv_read(bs, sector_num, buf, n); - if (ret < 0) - return -1; - } else { - /* read from the base image */ - ret = bdrv_read(bs->backing_hd, sector_num, buf, n); - if (ret < 0) - return -1; - } - } else { - ret = drv->bdrv_read(bs, sector_num, buf, nb_sectors); - if (ret < 0) - return -1; - /* no need to loop */ - break; - } - nb_sectors -= n; - sector_num += n; - buf += n * 512; - } - return 0; -} - -/* return -1 if error */ + sector_num++; + nb_sectors--; + buf += 512; + if (nb_sectors == 0) + return 0; + } + if (drv->bdrv_pread) { + int ret, len; + len = nb_sectors * 512; + ret = drv->bdrv_pread(bs, sector_num * 512, buf, len); + if (ret < 0) + return ret; + else if (ret != len) + return -EINVAL; + else + return 0; + } else { + return drv->bdrv_read(bs, sector_num, buf, nb_sectors); + } +} + +/* Return < 0 if error. Important errors are: + -EIO generic I/O error (may happen for all errors) + -ENOMEDIUM No media inserted. + -EINVAL Invalid sector number or nb_sectors + -EACCES Trying to write a read-only device +*/ int bdrv_write(BlockDriverState *bs, int64_t sector_num, const uint8_t *buf, int nb_sectors) { - if (!bs->inserted) - return -1; + BlockDriver *drv = bs->drv; + if (!bs->drv) + return -ENOMEDIUM; if (bs->read_only) - return -1; + return -EACCES; if (sector_num < 0) - return -1; + return -EINVAL; if (sector_num == 0 && bs->boot_sector_enabled && nb_sectors > 0) { memcpy(bs->boot_sector_data, buf, 512); } - return bs->drv->bdrv_write(bs, sector_num, buf, nb_sectors); -} - + if (drv->bdrv_pwrite) { + int ret, len; + len = nb_sectors * 512; + ret = drv->bdrv_pwrite(bs, sector_num * 512, buf, len); + if (ret < 0) + return ret; + else if (ret != len) + return -EIO; + else + return 0; + } else { + return drv->bdrv_write(bs, sector_num, buf, nb_sectors); + } +} + +static int bdrv_pread_em(BlockDriverState *bs, int64_t offset, + uint8_t *buf, int count1) +{ + uint8_t tmp_buf[SECTOR_SIZE]; + int len, nb_sectors, count; + int64_t sector_num; + + count = count1; + /* first read to align to sector start */ + len = (SECTOR_SIZE - offset) & (SECTOR_SIZE - 1); + if (len > count) + len = count; + sector_num = offset >> SECTOR_BITS; + if (len > 0) { + if (bdrv_read(bs, sector_num, tmp_buf, 1) < 0) + return -EIO; + memcpy(buf, tmp_buf + (offset & (SECTOR_SIZE - 1)), len); + count -= len; + if (count == 0) + return count1; + sector_num++; + buf += len; + } + + /* read the sectors "in place" */ + nb_sectors = count >> SECTOR_BITS; + if (nb_sectors > 0) { + if (bdrv_read(bs, sector_num, buf, nb_sectors) < 0) + return -EIO; + sector_num += nb_sectors; + len = nb_sectors << SECTOR_BITS; + buf += len; + count -= len; + } + + /* add data from the last sector */ + if (count > 0) { + if (bdrv_read(bs, sector_num, tmp_buf, 1) < 0) + return -EIO; + memcpy(buf, tmp_buf, count); + } + return count1; +} + +static int bdrv_pwrite_em(BlockDriverState *bs, int64_t offset, + const uint8_t *buf, int count1) +{ + uint8_t tmp_buf[SECTOR_SIZE]; + int len, nb_sectors, count; + int64_t sector_num; + + count = count1; + /* first write to align to sector start */ + len = (SECTOR_SIZE - offset) & (SECTOR_SIZE - 1); + if (len > count) + len = count; + sector_num = offset >> SECTOR_BITS; + if (len > 0) { + if (bdrv_read(bs, sector_num, tmp_buf, 1) < 0) + return -EIO; + memcpy(tmp_buf + (offset & (SECTOR_SIZE - 1)), buf, len); + if (bdrv_write(bs, sector_num, tmp_buf, 1) < 0) + return -EIO; + count -= len; + if (count == 0) + return count1; + sector_num++; + buf += len; + } + + /* write the sectors "in place" */ + nb_sectors = count >> SECTOR_BITS; + if (nb_sectors > 0) { + if (bdrv_write(bs, sector_num, buf, nb_sectors) < 0) + return -EIO; + sector_num += nb_sectors; + len = nb_sectors << SECTOR_BITS; + buf += len; + count -= len; + } + + /* add data from the last sector */ + if (count > 0) { + if (bdrv_read(bs, sector_num, tmp_buf, 1) < 0) + return -EIO; + memcpy(tmp_buf, buf, count); + if (bdrv_write(bs, sector_num, tmp_buf, 1) < 0) + return -EIO; + } + return count1; +} + +/** + * Read with byte offsets (needed only for file protocols) + */ +int bdrv_pread(BlockDriverState *bs, int64_t offset, + void *buf1, int count1) +{ + BlockDriver *drv = bs->drv; + + if (!drv) + return -ENOMEDIUM; + if (!drv->bdrv_pread) + return bdrv_pread_em(bs, offset, buf1, count1); + return drv->bdrv_pread(bs, offset, buf1, count1); +} + +/** + * Write with byte offsets (needed only for file protocols) + */ +int bdrv_pwrite(BlockDriverState *bs, int64_t offset, + const void *buf1, int count1) +{ + BlockDriver *drv = bs->drv; + + if (!drv) + return -ENOMEDIUM; + if (!drv->bdrv_pwrite) + return bdrv_pwrite_em(bs, offset, buf1, count1); + return drv->bdrv_pwrite(bs, offset, buf1, count1); +} + +/** + * Truncate file to 'offset' bytes (needed only for file protocols) + */ +int bdrv_truncate(BlockDriverState *bs, int64_t offset) +{ + BlockDriver *drv = bs->drv; + if (!drv) + return -ENOMEDIUM; + if (!drv->bdrv_truncate) + return -ENOTSUP; + return drv->bdrv_truncate(bs, offset); +} + +/** + * Length of a file in bytes. Return < 0 if error or unknown. + */ +int64_t bdrv_getlength(BlockDriverState *bs) +{ + BlockDriver *drv = bs->drv; + if (!drv) + return -ENOMEDIUM; + if (!drv->bdrv_getlength) { + /* legacy mode */ + return bs->total_sectors * SECTOR_SIZE; + } + return drv->bdrv_getlength(bs); +} + +/* return 0 as number of sectors if no device present or error */ void bdrv_get_geometry(BlockDriverState *bs, int64_t *nb_sectors_ptr) { - *nb_sectors_ptr = bs->total_sectors; + int64_t length; + length = bdrv_getlength(bs); + if (length < 0) + length = 0; + else + length = length >> SECTOR_BITS; + *nb_sectors_ptr = length; } /* force a given boot sector. */ @@ -531,21 +782,7 @@ int bdrv_is_read_only(BlockDriverState * return bs->read_only; } -int bdrv_is_inserted(BlockDriverState *bs) -{ - return bs->inserted; -} - -int bdrv_is_locked(BlockDriverState *bs) -{ - return bs->locked; -} - -void bdrv_set_locked(BlockDriverState *bs, int locked) -{ - bs->locked = locked; -} - +/* XXX: no longer used */ void bdrv_set_change_cb(BlockDriverState *bs, void (*change_cb)(void *opaque), void *opaque) { @@ -577,7 +814,7 @@ int bdrv_set_key(BlockDriverState *bs, c void bdrv_get_format(BlockDriverState *bs, char *buf, int buf_size) { - if (!bs->inserted || !bs->drv) { + if (!bs->drv) { buf[0] = '\0'; } else { pstrcpy(buf, buf_size, bs->drv->format_name); @@ -649,10 +886,13 @@ void bdrv_info(void) if (bs->removable) { term_printf(" locked=%d", bs->locked); } - if (bs->inserted) { - term_printf(" file=%s", bs->filename); - if (bs->backing_file[0] != '\0') - term_printf(" backing_file=%s", bs->backing_file); + if (bs->drv) { + term_printf(" file="); + term_print_filename(bs->filename); + if (bs->backing_file[0] != '\0') { + term_printf(" backing_file="); + term_print_filename(bs->backing_file); + } term_printf(" ro=%d", bs->read_only); term_printf(" drv=%s", bs->drv->format_name); if (bs->encrypted) @@ -664,192 +904,337 @@ void bdrv_info(void) } } +void bdrv_get_backing_filename(BlockDriverState *bs, + char *filename, int filename_size) +{ + if (!bs->backing_hd) { + pstrcpy(filename, filename_size, ""); + } else { + pstrcpy(filename, filename_size, bs->backing_file); + } +} + +int bdrv_write_compressed(BlockDriverState *bs, int64_t sector_num, + const uint8_t *buf, int nb_sectors) +{ + BlockDriver *drv = bs->drv; + if (!drv) + return -ENOMEDIUM; + if (!drv->bdrv_write_compressed) + return -ENOTSUP; + return drv->bdrv_write_compressed(bs, sector_num, buf, nb_sectors); +} + +int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) +{ + BlockDriver *drv = bs->drv; + if (!drv) + return -ENOMEDIUM; + if (!drv->bdrv_get_info) + return -ENOTSUP; + memset(bdi, 0, sizeof(*bdi)); + return drv->bdrv_get_info(bs, bdi); +} + /**************************************************************/ -/* RAW block driver */ - -typedef struct BDRVRawState { - int fd; -} BDRVRawState; - -static int raw_probe(const uint8_t *buf, int buf_size, const char *filename) -{ - return 1; /* maybe */ -} - -static int raw_open(BlockDriverState *bs, const char *filename) -{ - BDRVRawState *s = bs->opaque; - int fd; - int64_t size; -#ifdef _BSD - struct stat sb; +/* handling of snapshots */ + +int bdrv_snapshot_create(BlockDriverState *bs, + QEMUSnapshotInfo *sn_info) +{ + BlockDriver *drv = bs->drv; + if (!drv) + return -ENOMEDIUM; + if (!drv->bdrv_snapshot_create) + return -ENOTSUP; + return drv->bdrv_snapshot_create(bs, sn_info); +} + +int bdrv_snapshot_goto(BlockDriverState *bs, + const char *snapshot_id) +{ + BlockDriver *drv = bs->drv; + if (!drv) + return -ENOMEDIUM; + if (!drv->bdrv_snapshot_goto) + return -ENOTSUP; + return drv->bdrv_snapshot_goto(bs, snapshot_id); +} + +int bdrv_snapshot_delete(BlockDriverState *bs, const char *snapshot_id) +{ + BlockDriver *drv = bs->drv; + if (!drv) + return -ENOMEDIUM; + if (!drv->bdrv_snapshot_delete) + return -ENOTSUP; + return drv->bdrv_snapshot_delete(bs, snapshot_id); +} + +int bdrv_snapshot_list(BlockDriverState *bs, + QEMUSnapshotInfo **psn_info) +{ + BlockDriver *drv = bs->drv; + if (!drv) + return -ENOMEDIUM; + if (!drv->bdrv_snapshot_list) + return -ENOTSUP; + return drv->bdrv_snapshot_list(bs, psn_info); +} + +#define NB_SUFFIXES 4 + +char *get_human_readable_size(char *buf, int buf_size, int64_t size) +{ + static const char suffixes[NB_SUFFIXES] = "KMGT"; + int64_t base; + int i; + + if (size <= 999) { + snprintf(buf, buf_size, "%" PRId64, size); + } else { + base = 1024; + for(i = 0; i < NB_SUFFIXES; i++) { + if (size < (10 * base)) { + snprintf(buf, buf_size, "%0.1f%c", + (double)size / base, + suffixes[i]); + break; + } else if (size < (1000 * base) || i == (NB_SUFFIXES - 1)) { + snprintf(buf, buf_size, "%" PRId64 "%c", + ((size + (base >> 1)) / base), + suffixes[i]); + break; + } + base = base * 1024; + } + } + return buf; +} + +char *bdrv_snapshot_dump(char *buf, int buf_size, QEMUSnapshotInfo *sn) +{ + char buf1[128], date_buf[128], clock_buf[128]; +#ifdef _WIN32 + struct tm *ptm; +#else + struct tm tm; #endif -#ifdef __sun__ - struct dk_minfo minfo; - int rv; + time_t ti; + int64_t secs; + + if (!sn) { + snprintf(buf, buf_size, + "%-10s%-20s%7s%20s%15s", + "ID", "TAG", "VM SIZE", "DATE", "VM CLOCK"); + } else { + ti = sn->date_sec; +#ifdef _WIN32 + ptm = localtime(&ti); + strftime(date_buf, sizeof(date_buf), + "%Y-%m-%d %H:%M:%S", ptm); +#else + localtime_r(&ti, &tm); + strftime(date_buf, sizeof(date_buf), + "%Y-%m-%d %H:%M:%S", &tm); #endif - - fd = open(filename, O_RDWR | O_BINARY | O_LARGEFILE); - if (fd < 0) { - fd = open(filename, O_RDONLY | O_BINARY | O_LARGEFILE); - if (fd < 0) - return -1; - bs->read_only = 1; - } -#ifdef _BSD - if (!fstat(fd, &sb) && (S_IFCHR & sb.st_mode)) { -#ifdef DIOCGMEDIASIZE - if (ioctl(fd, DIOCGMEDIASIZE, (off_t *)&size)) -#endif -#ifdef CONFIG_COCOA - size = LONG_LONG_MAX; + secs = sn->vm_clock_nsec / 1000000000; + snprintf(clock_buf, sizeof(clock_buf), + "%02d:%02d:%02d.%03d", + (int)(secs / 3600), + (int)((secs / 60) % 60), + (int)(secs % 60), + (int)((sn->vm_clock_nsec / 1000000) % 1000)); + snprintf(buf, buf_size, + "%-10s%-20s%7s%20s%15s", + sn->id_str, sn->name, + get_human_readable_size(buf1, sizeof(buf1), sn->vm_state_size), + date_buf, + clock_buf); + } + return buf; +} + + +/**************************************************************/ +/* async I/Os */ + +BlockDriverAIOCB *bdrv_aio_read(BlockDriverState *bs, int64_t sector_num, + uint8_t *buf, int nb_sectors, + BlockDriverCompletionFunc *cb, void *opaque) +{ + BlockDriver *drv = bs->drv; + + if (!drv) + return NULL; + + /* XXX: we assume that nb_sectors == 0 is suppored by the async read */ + if (sector_num == 0 && bs->boot_sector_enabled && nb_sectors > 0) { + memcpy(buf, bs->boot_sector_data, 512); + sector_num++; + nb_sectors--; + buf += 512; + } + + return drv->bdrv_aio_read(bs, sector_num, buf, nb_sectors, cb, opaque); +} + +BlockDriverAIOCB *bdrv_aio_write(BlockDriverState *bs, int64_t sector_num, + const uint8_t *buf, int nb_sectors, + BlockDriverCompletionFunc *cb, void *opaque) +{ + BlockDriver *drv = bs->drv; + + if (!drv) + return NULL; + if (bs->read_only) + return NULL; + if (sector_num == 0 && bs->boot_sector_enabled && nb_sectors > 0) { + memcpy(bs->boot_sector_data, buf, 512); + } + + return drv->bdrv_aio_write(bs, sector_num, buf, nb_sectors, cb, opaque); +} + +void bdrv_aio_cancel(BlockDriverAIOCB *acb) +{ + BlockDriver *drv = acb->bs->drv; + + drv->bdrv_aio_cancel(acb); +} + + +/**************************************************************/ +/* async block device emulation */ + +#ifdef QEMU_TOOL +static BlockDriverAIOCB *bdrv_aio_read_em(BlockDriverState *bs, + int64_t sector_num, uint8_t *buf, int nb_sectors, + BlockDriverCompletionFunc *cb, void *opaque) +{ + int ret; + ret = bdrv_read(bs, sector_num, buf, nb_sectors); + cb(opaque, ret); + return NULL; +} + +static BlockDriverAIOCB *bdrv_aio_write_em(BlockDriverState *bs, + int64_t sector_num, const uint8_t *buf, int nb_sectors, + BlockDriverCompletionFunc *cb, void *opaque) +{ + int ret; + ret = bdrv_write(bs, sector_num, buf, nb_sectors); + cb(opaque, ret); + return NULL; +} + +static void bdrv_aio_cancel_em(BlockDriverAIOCB *acb) +{ +} #else - size = lseek(fd, 0LL, SEEK_END); -#endif - } else -#endif -#ifdef __sun__ - /* - * use the DKIOCGMEDIAINFO ioctl to read the size. - */ - rv = ioctl ( fd, DKIOCGMEDIAINFO, &minfo ); - if ( rv != -1 ) { - size = minfo.dki_lbsize * minfo.dki_capacity; - } else /* there are reports that lseek on some devices - fails, but irc discussion said that contingency - on contingency was overkill */ -#endif - { - size = lseek(fd, 0, SEEK_END); - } -#ifdef _WIN32 - /* On Windows hosts it can happen that we're unable to get file size - for CD-ROM raw device (it's inherent limitation of the CDFS driver). */ - if (size == -1) - size = LONG_LONG_MAX; -#endif - bs->total_sectors = size / 512; - s->fd = fd; - return 0; -} - -static int raw_read(BlockDriverState *bs, int64_t sector_num, - uint8_t *buf, int nb_sectors) -{ - BDRVRawState *s = bs->opaque; +static void bdrv_aio_bh_cb(void *opaque) +{ + BlockDriverAIOCBSync *acb = opaque; + acb->common.cb(acb->common.opaque, acb->ret); + qemu_aio_release(acb); +} + +static BlockDriverAIOCB *bdrv_aio_read_em(BlockDriverState *bs, + int64_t sector_num, uint8_t *buf, int nb_sectors, + BlockDriverCompletionFunc *cb, void *opaque) +{ + BlockDriverAIOCBSync *acb; int ret; - - lseek(s->fd, sector_num * 512, SEEK_SET); - ret = read(s->fd, buf, nb_sectors * 512); - if (ret != nb_sectors * 512) + + acb = qemu_aio_get(bs, cb, opaque); + if (!acb->bh) + acb->bh = qemu_bh_new(bdrv_aio_bh_cb, acb); + ret = bdrv_read(bs, sector_num, buf, nb_sectors); + acb->ret = ret; + qemu_bh_schedule(acb->bh); + return &acb->common; +} + +static BlockDriverAIOCB *bdrv_aio_write_em(BlockDriverState *bs, + int64_t sector_num, const uint8_t *buf, int nb_sectors, + BlockDriverCompletionFunc *cb, void *opaque) +{ + BlockDriverAIOCBSync *acb; + int ret; + + acb = qemu_aio_get(bs, cb, opaque); + if (!acb->bh) + acb->bh = qemu_bh_new(bdrv_aio_bh_cb, acb); + ret = bdrv_write(bs, sector_num, buf, nb_sectors); + acb->ret = ret; + qemu_bh_schedule(acb->bh); + return &acb->common; +} + +static void bdrv_aio_cancel_em(BlockDriverAIOCB *blockacb) +{ + BlockDriverAIOCBSync *acb = (BlockDriverAIOCBSync *)blockacb; + qemu_bh_cancel(acb->bh); + qemu_aio_release(acb); +} +#endif /* !QEMU_TOOL */ + +/**************************************************************/ +/* sync block device emulation */ + +static void bdrv_rw_em_cb(void *opaque, int ret) +{ + *(int *)opaque = ret; +} + +#define NOT_DONE 0x7fffffff + +static int bdrv_read_em(BlockDriverState *bs, int64_t sector_num, + uint8_t *buf, int nb_sectors) +{ + int async_ret; + BlockDriverAIOCB *acb; + + async_ret = NOT_DONE; + qemu_aio_wait_start(); + acb = bdrv_aio_read(bs, sector_num, buf, nb_sectors, + bdrv_rw_em_cb, &async_ret); + if (acb == NULL) { + qemu_aio_wait_end(); return -1; - return 0; -} - -static int raw_write(BlockDriverState *bs, int64_t sector_num, - const uint8_t *buf, int nb_sectors) -{ - BDRVRawState *s = bs->opaque; - int ret; - - lseek(s->fd, sector_num * 512, SEEK_SET); - ret = write(s->fd, buf, nb_sectors * 512); - if (ret != nb_sectors * 512) + } + while (async_ret == NOT_DONE) { + qemu_aio_wait(); + } + qemu_aio_wait_end(); + return async_ret; +} + +static int bdrv_write_em(BlockDriverState *bs, int64_t sector_num, + const uint8_t *buf, int nb_sectors) +{ + int async_ret; + BlockDriverAIOCB *acb; + + async_ret = NOT_DONE; + qemu_aio_wait_start(); + acb = bdrv_aio_write(bs, sector_num, buf, nb_sectors, + bdrv_rw_em_cb, &async_ret); + if (acb == NULL) { + qemu_aio_wait_end(); return -1; - return 0; -} - -static void raw_close(BlockDriverState *bs) -{ - BDRVRawState *s = bs->opaque; - bs->total_sectors = 0; - close(s->fd); -} - -#ifdef _WIN32 -#include <windows.h> -#include <winioctl.h> - -int qemu_ftruncate64(int fd, int64_t length) -{ - LARGE_INTEGER li; - LONG high; - HANDLE h; - BOOL res; - - if ((GetVersion() & 0x80000000UL) && (length >> 32) != 0) - return -1; - - h = (HANDLE)_get_osfhandle(fd); - - /* get current position, ftruncate do not change position */ - li.HighPart = 0; - li.LowPart = SetFilePointer (h, 0, &li.HighPart, FILE_CURRENT); - if (li.LowPart == 0xffffffffUL && GetLastError() != NO_ERROR) - return -1; - - high = length >> 32; - if (!SetFilePointer(h, (DWORD) length, &high, FILE_BEGIN)) - return -1; - res = SetEndOfFile(h); - - /* back to old position */ - SetFilePointer(h, li.LowPart, &li.HighPart, FILE_BEGIN); - return res ? 0 : -1; -} - -static int set_sparse(int fd) -{ - DWORD returned; - return (int) DeviceIoControl((HANDLE)_get_osfhandle(fd), FSCTL_SET_SPARSE, - NULL, 0, NULL, 0, &returned, NULL); -} -#else -static inline int set_sparse(int fd) -{ - return 1; -} -#endif - -static int raw_create(const char *filename, int64_t total_size, - const char *backing_file, int flags) -{ - int fd; - - if (flags || backing_file) - return -ENOTSUP; - - fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE, - 0644); - if (fd < 0) - return -EIO; - set_sparse(fd); - ftruncate(fd, total_size * 512); - close(fd); - return 0; -} - -static void raw_flush(BlockDriverState *bs) -{ - BDRVRawState *s = bs->opaque; - fsync(s->fd); -} - -BlockDriver bdrv_raw = { - "raw", - sizeof(BDRVRawState), - raw_probe, - raw_open, - raw_read, - raw_write, - raw_close, - raw_create, - raw_flush, -}; + } + while (async_ret == NOT_DONE) { + qemu_aio_wait(); + } + qemu_aio_wait_end(); + return async_ret; +} void bdrv_init(void) { bdrv_register(&bdrv_raw); + bdrv_register(&bdrv_host_device); #ifndef _WIN32 bdrv_register(&bdrv_cow); #endif @@ -860,4 +1245,109 @@ void bdrv_init(void) bdrv_register(&bdrv_bochs); bdrv_register(&bdrv_vpc); bdrv_register(&bdrv_vvfat); -} + bdrv_register(&bdrv_qcow2); +} + +void *qemu_aio_get(BlockDriverState *bs, BlockDriverCompletionFunc *cb, + void *opaque) +{ + BlockDriver *drv; + BlockDriverAIOCB *acb; + + drv = bs->drv; + if (drv->free_aiocb) { + acb = drv->free_aiocb; + drv->free_aiocb = acb->next; + } else { + acb = qemu_mallocz(drv->aiocb_size); + if (!acb) + return NULL; + } + acb->bs = bs; + acb->cb = cb; + acb->opaque = opaque; + return acb; +} + +void qemu_aio_release(void *p) +{ + BlockDriverAIOCB *acb = p; + BlockDriver *drv = acb->bs->drv; + acb->next = drv->free_aiocb; + drv->free_aiocb = acb; +} + +/**************************************************************/ +/* removable device support */ + +/** + * Return TRUE if the media is present + */ +int bdrv_is_inserted(BlockDriverState *bs) +{ + BlockDriver *drv = bs->drv; + int ret; + if (!drv) + return 0; + if (!drv->bdrv_is_inserted) + return 1; + ret = drv->bdrv_is_inserted(bs); + return ret; +} + +/** + * Return TRUE if the media changed since the last call to this + * function. It is currently only used for floppy disks + */ +int bdrv_media_changed(BlockDriverState *bs) +{ + BlockDriver *drv = bs->drv; + int ret; + + if (!drv || !drv->bdrv_media_changed) + ret = -ENOTSUP; + else + ret = drv->bdrv_media_changed(bs); + if (ret == -ENOTSUP) + ret = bs->media_changed; + bs->media_changed = 0; + return ret; +} + +/** + * If eject_flag is TRUE, eject the media. Otherwise, close the tray + */ +void bdrv_eject(BlockDriverState *bs, int eject_flag) +{ + BlockDriver *drv = bs->drv; + int ret; + + if (!drv || !drv->bdrv_eject) { + ret = -ENOTSUP; + } else { + ret = drv->bdrv_eject(bs, eject_flag); + } + if (ret == -ENOTSUP) { + if (eject_flag) + bdrv_close(bs); + } +} + +int bdrv_is_locked(BlockDriverState *bs) +{ + return bs->locked; +} + +/** + * Lock or unlock the media (if it is locked, the user won't be able + * to eject it manually). + */ +void bdrv_set_locked(BlockDriverState *bs, int locked) +{ + BlockDriver *drv = bs->drv; + + bs->locked = locked; + if (drv && drv->bdrv_set_locked) { + drv->bdrv_set_locked(bs, locked); + } +} diff -r d2ef85c6bf84 -r 00618037d37d tools/ioemu/block_int.h --- a/tools/ioemu/block_int.h Tue May 08 10:38:06 2007 +0100 +++ b/tools/ioemu/block_int.h Wed May 09 14:17:15 2007 +0100 @@ -28,7 +28,7 @@ struct BlockDriver { const char *format_name; int instance_size; int (*bdrv_probe)(const uint8_t *buf, int buf_size, const char *filename); - int (*bdrv_open)(BlockDriverState *bs, 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, @@ -41,13 +41,49 @@ struct BlockDriver { int nb_sectors, int *pnum); int (*bdrv_set_key)(BlockDriverState *bs, const char *key); int (*bdrv_make_empty)(BlockDriverState *bs); + /* aio */ + 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); + 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, + const uint8_t *buf, int nb_sectors); + + int (*bdrv_snapshot_create)(BlockDriverState *bs, + QEMUSnapshotInfo *sn_info); + int (*bdrv_snapshot_goto)(BlockDriverState *bs, + const char *snapshot_id); + int (*bdrv_snapshot_delete)(BlockDriverState *bs, const char *snapshot_id); + int (*bdrv_snapshot_list)(BlockDriverState *bs, + QEMUSnapshotInfo **psn_info); + int (*bdrv_get_info)(BlockDriverState *bs, BlockDriverInfo *bdi); + + /* removable device specific */ + int (*bdrv_is_inserted)(BlockDriverState *bs); + int (*bdrv_media_changed)(BlockDriverState *bs); + int (*bdrv_eject)(BlockDriverState *bs, int eject_flag); + int (*bdrv_set_locked)(BlockDriverState *bs, int locked); + + BlockDriverAIOCB *free_aiocb; struct BlockDriver *next; }; struct BlockDriverState { - int64_t total_sectors; + int64_t total_sectors; /* if we are reading a disk image, give its + size in sectors */ int read_only; /* if true, the media is read only */ - int inserted; /* if true, the media is present */ int removable; /* if true, the media can be removed */ int locked; /* if true, the media cannot temporarily be ejected */ int encrypted; /* if true, the media is encrypted */ @@ -55,7 +91,7 @@ struct BlockDriverState { void (*change_cb)(void *opaque); void *change_opaque; - BlockDriver *drv; + BlockDriver *drv; /* NULL means no media */ void *opaque; int boot_sector_enabled; @@ -65,8 +101,12 @@ struct BlockDriverState { char backing_file[1024]; /* if non zero, the image is a diff of this file image */ int is_temporary; - + int media_changed; + BlockDriverState *backing_hd; + /* async read/write emulation */ + + void *sync_aiocb; /* NOTE: the following infos are only hints for real hardware drivers. They are not used by the block driver */ @@ -76,6 +116,17 @@ struct BlockDriverState { BlockDriverState *next; }; +struct BlockDriverAIOCB { + BlockDriverState *bs; + BlockDriverCompletionFunc *cb; + void *opaque; + BlockDriverAIOCB *next; +}; + void get_tmp_filename(char *filename, int size); +void *qemu_aio_get(BlockDriverState *bs, BlockDriverCompletionFunc *cb, + void *opaque); +void qemu_aio_release(void *p); + #endif /* BLOCK_INT_H */ diff -r d2ef85c6bf84 -r 00618037d37d tools/ioemu/check_ops.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/ioemu/check_ops.sh Wed May 09 14:17:15 2007 +0100 @@ -0,0 +1,47 @@ +#! /bin/sh +# Script to check for duplicate function prologues in op.o +# Typically this indicates missing FORCE_RET(); +# This script does not detect other errors that may be present. + +# Usage: check_ops.sh [-m machine] [op.o] +# machine and op.o are guessed if not specified. + +if [ "x$1" = "x-m" ]; then + machine=$2 + shift 2 +else + machine=`uname -m` +fi +if [ -z "$1" ]; then + for f in `find . -name op.o`; do + /bin/sh "$0" -m $machine $f + done + exit 0 +fi + +case $machine in + i?86) + ret='\tret' + ;; + x86_64) + ret='\tretq' + ;; + arm) + ret='\tldm.*pc' + ;; + ppc* | powerpc*) + ret='\tblr' + ;; + mips*) + ret='\tjr.*ra' + ;; + *) + echo "Unknown machine `uname -m`" + ;; +esac +echo $1 +# op_exit_tb causes false positives on some hosts. +${CROSS}objdump -dr $1 | \ + sed -e '/>:$\|'"$ret"'/!d' -e 's/.*<\(.*\)>:/~\1:/' -e 's/.*'"$ret"'.*/!/' | \ + sed -e ':1;N;s/\n//;t1' | sed -e 's/~/\n/g' | grep -v '^op_exit_tb' | \ + grep '^op_.*!!' diff -r d2ef85c6bf84 -r 00618037d37d tools/ioemu/configure --- a/tools/ioemu/configure Tue May 08 10:38:06 2007 +0100 +++ b/tools/ioemu/configure Wed May 09 14:17:15 2007 +0100 @@ -22,6 +22,8 @@ libdir="lib" libdir="lib" cross_prefix="" cc="gcc" +gcc3_search="yes" +gcc3_list="gcc-3.4 gcc34 gcc-3.3 gcc33 gcc-3.2 gcc32" host_cc="gcc" ar="ar" make="make" @@ -89,14 +91,13 @@ linux="no" linux="no" kqemu="no" profiler="no" -kernel_path="" cocoa="no" check_gfx="yes" check_gcc="no" softmmu="yes" -user="no" +linux_user="no" +darwin_user="no" build_docs="no" -build_acpi_tables="no" uname_release="" # OS specific @@ -104,7 +105,7 @@ case $targetos in case $targetos in CYGWIN*) mingw32="yes" -CFLAGS="-O2 -mno-cygwin" +OS_CFLAGS="-mno-cygwin" ;; MINGW32*) mingw32="yes" @@ -127,6 +128,10 @@ Darwin) Darwin) bsd="yes" darwin="yes" +darwin_user="yes" +cocoa="yes" +coreaudio="yes" +OS_CFLAGS="-mdynamic-no-pic" ;; SunOS) solaris="yes" @@ -134,7 +139,7 @@ solaris="yes" *) oss="yes" linux="yes" -user="yes" +linux_user="yes" if [ "$cpu" = "i386" -o "$cpu" = "x86_64" ] ; then kqemu="yes" fi @@ -151,6 +156,11 @@ if [ "$solaris" = "yes" ] ; then make="gmake" install="ginstall" solarisrev=`uname -r | cut -f2 -d.` + if [ "$cpu" = "i386" -o "$cpu" = "x86_64" ] ; then + if test "$solarisrev" -gt 10 ; then + kqemu="yes" + fi + fi fi # find source path @@ -181,6 +191,7 @@ for opt do --cross-prefix=*) cross_prefix="$optarg" ;; --cc=*) cc="$optarg" + gcc3_search="no" ;; --host-cc=*) host_cc="$optarg" ;; @@ -224,8 +235,6 @@ for opt do ;; --enable-profiler) profiler="yes" ;; - --kernel-path=*) kernel_path="$optarg" - ;; --enable-cocoa) cocoa="yes" ; coreaudio="yes" ; sdl="no" ;; --disable-gcc-check) check_gcc="no" @@ -234,21 +243,22 @@ for opt do ;; --enable-system) softmmu="yes" ;; - --disable-user) user="no" - ;; - --enable-user) user="yes" + --disable-linux-user) linux_user="no" + ;; + --enable-linux-user) linux_user="yes" + ;; + --disable-darwin-user) darwin_user="no" + ;; + --enable-darwin-user) darwin_user="yes" ;; --enable-uname-release=*) uname_release="$optarg" - ;; - --enable-iasl) build_acpi_tables="yes" ;; esac done -# Checking for CFLAGS -if test -z "$CFLAGS"; then - CFLAGS="-O2" -fi +# default flags for all hosts +CFLAGS="$CFLAGS -Wall -O2 -g -fno-strict-aliasing" +LDFLAGS="$LDFLAGS -g" if test x"$show_help" = x"yes" ; then cat << EOF @@ -266,7 +276,6 @@ echo "" echo "" echo "kqemu kernel acceleration support:" echo " --disable-kqemu disable kqemu support" -echo " --kernel-path=PATH set the kernel path (configure probes it)" echo "" echo "Advanced options (experts only):" echo " --source-path=PATH path of source code [$source_path]" @@ -285,14 +294,15 @@ echo " --enabled-dsound enable echo " --enabled-dsound enable DirectSound audio driver" echo " --enable-system enable all system emulation targets" echo " --disable-system disable all system emulation targets" -echo " --enable-user enable all linux usermode emulation targets" -echo " --disable-user disable all linux usermode emulation targets" +echo " --enable-linux-user enable all linux usermode emulation targets" +echo " --disable-linux-user disable all linux usermode emulation targets" +echo " --enable-darwin-user enable all darwin usermode emulation targets" +echo " --disable-darwin-user disable all darwin usermode emulation targets" echo " --fmod-lib path to FMOD library" echo " --fmod-inc path to FMOD includes" echo " --enable-uname-release=R Return R for uname -r in usermode emulation" -echo " --enable-iasl compilation of ACPI tables with the IASL compiler" echo "" -echo "NOTE: The object files are build at the place where configure is launched" +echo "NOTE: The object files are built at the place where configure is launched" exit 1 fi @@ -318,6 +328,45 @@ if test "$mingw32" = "yes" ; then oss="no" if [ "$cpu" = "i386" ] ; then kqemu="yes" + fi +fi + +# Check for gcc4, error if pre-gcc4 +if test "$check_gcc" = "yes" ; then + cat > $TMPC <<EOF +#if __GNUC__ < 4 +#error gcc3 +#endif +int main(){return 0;} +EOF + check_cc() { + which "$1" >&/dev/null + return $? + } + + if "$cc" -o $TMPE $TMPC 2>/dev/null ; then + echo "WARNING: \"$cc\" looks like gcc 4.x" + found_compat_cc="no" + if test "$gcc3_search" = "yes" ; then + echo "Looking for gcc 3.x" + for compat_cc in $gcc3_list ; do + if check_cc "$compat_cc" ; then + echo "Found \"$compat_cc\"" + cc="$compat_cc" + found_compat_cc="yes" + break + fi + done + if test "$found_compat_cc" = "no" ; then + echo "gcc 3.x not found!" + fi + fi + if test "$found_compat_cc" = "no" ; then + echo "QEMU is known to have problems when compiled with gcc 4.x" + echo "It is recommended that you use gcc 3.x to build QEMU" + echo "To use this compiler anyway, configure with --disable-gcc-check" + exit 1; + fi fi fi @@ -368,8 +417,12 @@ if test -z "$target_list" ; then target_list="i386-softmmu ppc-softmmu sparc-softmmu x86_64-softmmu mips-softmmu mipsel-softmmu arm-softmmu" fi # the following are Linux specific - if [ "$user" = "yes" ] ; then - target_list="i386-user arm-user armeb-user sparc-user ppc-user mips-user mipsel-user $target_list" + if [ "$linux_user" = "yes" ] ; then + target_list="i386-linux-user arm-linux-user armeb-linux-user sparc-linux-user ppc-linux-user mips-linux-user mipsel-linux-user m68k-linux-user $target_list" + fi +# the following are Darwin specific + if [ "$darwin_user" = "yes" ] ; then + target_list="i386-darwin-user ppc-darwin-user $target_list" fi # the i386-dm target target_list="i386-dm" @@ -427,23 +480,6 @@ if $cc -fno-reorder-blocks -fno-optimize have_gcc3_options="yes" fi -# Check for gcc4, error if pre-gcc4 -if test "$check_gcc" = "yes" ; then - cat > $TMPC <<EOF -#if __GNUC__ < 4 -#error gcc3 -#endif -int main(){return 0;} -EOF - if $cc -o $TMPO $TMPC 2>/dev/null ; then - echo "ERROR: \"$cc\" looks like gcc 4.x" - echo "QEMU is known to have problems when compiled with gcc 4.x" - echo "It is recommended that you use gcc 3.x to build QEMU" - echo "To use this compiler anyway, configure with --disable-gcc-check" - exit 1; - fi -fi - ########################################## # SDL probe @@ -472,7 +508,9 @@ if test "$_sdlversion" -lt 121 ; then if test "$_sdlversion" -lt 121 ; then sdl_too_old=yes else -sdl=yes + if test "$cocoa" = "no" ; then + sdl=yes + fi fi # static link with sdl ? @@ -493,7 +531,33 @@ fi # sdl compile test fi # sdl compile test fi # cross compilation + +else + # Make sure to disable cocoa if sdl was set + if test "$sdl" = "yes" ; then + cocoa="no" + coreaudio="no" + fi fi # -z $sdl + +########################################## +# alsa sound support libraries + +if test "$alsa" = "yes" ; then + cat > $TMPC << EOF +#include <alsa/asoundlib.h> +int main(void) { snd_pcm_t **handle; return snd_pcm_close(*handle); } +EOF + if $cc -o $TMPE $TMPC -lasound 2> /dev/null ; then + : + else + echo + echo "Error: Could not find alsa" + echo "Make sure to have the alsa libs and headers installed." + echo + exit 1 + fi +fi # Check if tools are available to build documentation. if [ -x "`which texi2html`" ] && [ -x "`which pod2man`" ]; then @@ -600,6 +664,7 @@ echo "HOST_CC=$host_cc" >> $config_mak echo "HOST_CC=$host_cc" >> $config_mak echo "AR=$ar" >> $config_mak echo "STRIP=$strip -s -R .comment -R .note" >> $config_mak +echo "OS_CFLAGS=$OS_CFLAGS" >> $config_mak echo "CFLAGS=$CFLAGS" >> $config_mak echo "LDFLAGS=$LDFLAGS" >> $config_mak echo "EXESUF=$EXESUF" >> $config_mak @@ -719,9 +784,6 @@ if [ "$build_docs" = "yes" ] ; then if [ "$build_docs" = "yes" ] ; then echo "BUILD_DOCS=yes" >> $config_mak fi -if [ "$build_acpi_tables" = "yes" ] ; then - echo "BUILD_ACPI_TABLES=yes" >> $config_mak -fi # XXX: suppress that if [ "$bsd" = "yes" ] ; then @@ -745,6 +807,7 @@ target_bigendian="no" [ "$target_cpu" = "ppc64" ] && target_bigendian=yes [ "$target_cpu" = "mips" ] && target_bigendian=yes [ "$target_cpu" = "sh4eb" ] && target_bigendian=yes +[ "$target_cpu" = "m68k" ] && target_bigendian=yes target_softmmu="no" if expr $target : '.*-softmmu' > /dev/null ; then target_softmmu="yes" @@ -756,11 +819,21 @@ if expr $target : '.*-user' > /dev/null target_user_only="yes" fi +target_linux_user="no" +if expr $target : '.*-linux-user' > /dev/null ; then + target_linux_user="yes" +fi + +target_darwin_user="no" +if expr $target : '.*-darwin-user' > /dev/null ; then + target_darwin_user="yes" +fi + #echo "Creating $config_mak, $config_h and $target_dir/Makefile" mkdir -p $target_dir mkdir -p $target_dir/fpu -if test "$target" = "arm-user" -o "$target" = "armeb-user" ; then +if test "$target" = "arm-linux-user" -o "$target" = "armeb-linux-user" ; then mkdir -p $target_dir/nwfpe fi if test "$target_user_only" = "no" ; then @@ -840,6 +913,11 @@ elif test "$target_cpu" = "sh4" -o "$tar echo "#define TARGET_ARCH \"sh4\"" >> $config_h echo "#define TARGET_SH4 1" >> $config_h bflt="yes" +elif test "$target_cpu" = "m68k" ; then + echo "TARGET_ARCH=m68k" >> $config_mak + echo "#define TARGET_ARCH \"m68k\"" >> $config_h + echo "#define TARGET_M68K 1" >> $config_h + bflt="yes" else echo "Unsupported target CPU" exit 1 @@ -856,12 +934,19 @@ if test "$target_user_only" = "yes" ; th echo "CONFIG_USER_ONLY=yes" >> $config_mak echo "#define CONFIG_USER_ONLY 1" >> $config_h fi - +if test "$target_linux_user" = "yes" ; then + echo "CONFIG_LINUX_USER=yes" >> $config_mak + echo "#define CONFIG_LINUX_USER 1" >> $config_h +fi +if test "$target_darwin_user" = "yes" ; then + echo "CONFIG_DARWIN_USER=yes" >> $config_mak + echo "#define CONFIG_DARWIN_USER 1" >> $config_h +fi if expr $target : '.*-dm' > /dev/null ; then echo "#define CONFIG_DM 1" >> $config_h fi -if test "$target_cpu" = "arm" -o "$target_cpu" = "armeb" -o "$target_cpu" = "sparc" -o "$target_cpu" = "sparc64"; then +if test "$target_cpu" = "arm" -o "$target_cpu" = "armeb" -o "$target_cpu" = "sparc" -o "$target_cpu" = "sparc64" -o "$target_cpu" = "m68k"; then echo "CONFIG_SOFTFLOAT=yes" >> $config_mak echo "#define CONFIG_SOFTFLOAT 1" >> $config_h fi diff -r d2ef85c6bf84 -r 00618037d37d tools/ioemu/console.c --- a/tools/ioemu/console.c Tue May 08 10:38:06 2007 +0100 +++ b/tools/ioemu/console.c Wed May 09 14:17:15 2007 +0100 @@ -121,6 +121,7 @@ struct TextConsole { int total_height; int backscroll_height; int x, y; + int x_saved, y_saved; int y_displayed; int y_base; TextAttributes t_attrib_default; /* default text attributes */ @@ -131,10 +132,7 @@ struct TextConsole { int esc_params[MAX_ESC_PARAMS]; int nb_esc_params; - /* kbd read handler */ - IOCanRWHandler *fd_can_read; - IOReadHandler *fd_read; - void *fd_opaque; + CharDriverState *chr; /* fifo for key pressed */ QEMUFIFO out_fifo; uint8_t out_fifo_buf[16]; @@ -147,7 +145,7 @@ static int nb_consoles = 0; void vga_hw_update(void) { - if (active_console->hw_update) + if (active_console && active_console->hw_update) active_console->hw_update(active_console->hw); } @@ -659,10 +657,6 @@ static void console_handle_escape(TextCo { int i; - if (s->nb_esc_params == 0) { /* ESC[m sets all attributes to default */ - s->t_attrib = s->t_attrib_default; - return; - } for (i=0; i<s->nb_esc_params; i++) { switch (s->esc_params[i]) { case 0: /* reset all console attributes to default */ @@ -752,10 +746,21 @@ static void console_handle_escape(TextCo } } +static void console_clear_xy(TextConsole *s, int x, int y) +{ + int y1 = (s->y_base + y) % s->total_height; + TextCell *c = &s->cells[y1 * s->width + x]; + c->ch = ' '; + c->t_attrib = s->t_attrib_default; + c++; + update_xy(s, x, y); +} + static void console_putchar(TextConsole *s, int ch) { TextCell *c; - int y1, i, x; + int y1, i; + int x, y; switch(s->state) { case TTY_STATE_NORM: @@ -781,20 +786,31 @@ static void console_putchar(TextConsole case '\a': /* alert aka. bell */ /* TODO: has to be implemented */ break; + case 14: + /* SI (shift in), character set 0 (ignored) */ + break; + case 15: + /* SO (shift out), character set 1 (ignored) */ + break; case 27: /* esc (introducing an escape sequence) */ s->state = TTY_STATE_ESC; break; default: + if (s->x >= s->width - 1) { + break; + } y1 = (s->y_base + s->y) % s->total_height; c = &s->cells[y1 * s->width + s->x]; c->ch = ch; c->t_attrib = s->t_attrib; update_xy(s, s->x, s->y); s->x++; +#if 0 /* line wrap disabled */ if (s->x >= s->width) { s->x = 0; console_put_lf(s); } +#endif break; } break; @@ -818,31 +834,149 @@ static void console_putchar(TextConsole s->nb_esc_params++; if (ch == ';') break; +#ifdef DEBUG_CONSOLE + fprintf(stderr, "escape sequence CSI%d;%d%c, %d parameters\n", + s->esc_params[0], s->esc_params[1], ch, s->nb_esc_params); +#endif s->state = TTY_STATE_NORM; switch(ch) { + case 'A': + /* move cursor up */ + if (s->esc_params[0] == 0) { + s->esc_params[0] = 1; + } + s->y -= s->esc_params[0]; + if (s->y < 0) { + s->y = 0; + } + break; + case 'B': + /* move cursor down */ + if (s->esc_params[0] == 0) { + s->esc_params[0] = 1; + } + s->y += s->esc_params[0]; + if (s->y >= s->height) { + s->y = s->height - 1; + } + break; + case 'C': + /* move cursor right */ + if (s->esc_params[0] == 0) { + s->esc_params[0] = 1; + } + s->x += s->esc_params[0]; + if (s->x >= s->width) { + s->x = s->width - 1; + } + break; case 'D': - if (s->x > 0) - s->x--; - break; - case 'C': - if (s->x < (s->width - 1)) - s->x++; - break; + /* move cursor left */ + if (s->esc_params[0] == 0) { + s->esc_params[0] = 1; + } + s->x -= s->esc_params[0]; + if (s->x < 0) { + s->x = 0; + } + break; + case 'G': + /* move cursor to column */ + s->x = s->esc_params[0] - 1; + if (s->x < 0) { + s->x = 0; + } + break; + case 'f': + case 'H': + /* move cursor to row, column */ + s->x = s->esc_params[1] - 1; + if (s->x < 0) { + s->x = 0; + } + s->y = s->esc_params[0] - 1; + if (s->y < 0) { + s->y = 0; + } + break; + case 'J': + switch (s->esc_params[0]) { + case 0: + /* clear to end of screen */ + for (y = s->y; y < s->height; y++) { + for (x = 0; x < s->width; x++) { + if (y == s->y && x < s->x) { + continue; + } + console_clear_xy(s, x, y); + } + } + break; + case 1: + /* clear from beginning of screen */ + for (y = 0; y <= s->y; y++) { + for (x = 0; x < s->width; x++) { + if (y == s->y && x > s->x) { + break; + } + console_clear_xy(s, x, y); + } + } + break; + case 2: + /* clear entire screen */ + for (y = 0; y <= s->height; y++) { + for (x = 0; x < s->width; x++) { + console_clear_xy(s, x, y); + } + } + break; + } case 'K': + switch (s->esc_params[0]) { + case 0: /* clear to eol */ - y1 = (s->y_base + s->y) % s->total_height; for(x = s->x; x < s->width; x++) { - c = &s->cells[y1 * s->width + x]; - c->ch = ' '; - c->t_attrib = s->t_attrib_default; - c++; - update_xy(s, x, s->y); - } + console_clear_xy(s, x, s->y); + } + break; + case 1: + /* clear from beginning of line */ + for (x = 0; x <= s->x; x++) { + console_clear_xy(s, x, s->y); + } + break; + case 2: + /* clear entire line */ + for(x = 0; x < s->width; x++) { + console_clear_xy(s, x, s->y); + } + break; + } + break; + case 'm': + console_handle_escape(s); + break; + case 'n': + /* report cursor position */ + /* TODO: send ESC[row;colR */ + break; + case 's': + /* save cursor position */ + s->x_saved = s->x; + s->y_saved = s->y; + break; + case 'u': + /* restore cursor position */ + s->x = s->x_saved; + s->y = s->y_saved; break; default: - break; - } - console_handle_escape(s); +#ifdef DEBUG_CONSOLE + fprintf(stderr, "unhandled escape character '%c'\n", ch); +#endif + break; + } break; } } @@ -884,16 +1018,6 @@ static int console_puts(CharDriverState return len; } -static void console_chr_add_read_handler(CharDriverState *chr, - IOCanRWHandler *fd_can_read, - IOReadHandler *fd_read, void *opaque) -{ - TextConsole *s = chr->opaque; - s->fd_can_read = fd_can_read; - s->fd_read = fd_read; - s->fd_opaque = opaque; -} - static void console_send_event(CharDriverState *chr, int event) { TextConsole *s = chr->opaque; @@ -915,14 +1039,14 @@ static void kbd_send_chars(void *opaque) int len; uint8_t buf[16]; - len = s->fd_can_read(s->fd_opaque); + len = qemu_chr_can_read(s->chr); if (len > s->out_fifo.count) len = s->out_fifo.count; if (len > 0) { if (len > sizeof(buf)) len = sizeof(buf); qemu_fifo_read(&s->out_fifo, buf, len); - s->fd_read(s->fd_opaque, buf, len); + qemu_chr_read(s->chr, buf, len); } /* characters are pending: we send them a bit later (XXX: horrible, should change char device API) */ @@ -973,7 +1097,7 @@ void kbd_put_keysym(int keysym) } else { *q++ = keysym; } - if (s->fd_read) { + if (s->chr->chr_read) { qemu_fifo_write(&s->out_fifo, buf, q - buf); kbd_send_chars(s); } @@ -1059,9 +1183,9 @@ CharDriverState *text_console_init(Displ } chr->opaque = s; chr->chr_write = console_puts; - chr->chr_add_read_handler = console_chr_add_read_handler; chr->chr_send_event = console_send_event; + s->chr = chr; s->out_fifo.buf = s->out_fifo_buf; s->out_fifo.buf_size = sizeof(s->out_fifo_buf); s->kbd_timer = qemu_new_timer(rt_clock, kbd_send_chars, s); @@ -1091,5 +1215,7 @@ CharDriverState *text_console_init(Displ s->t_attrib = s->t_attrib_default; text_console_resize(s); + qemu_chr_reset(chr); + return chr; } diff -r d2ef85c6bf84 -r 00618037d37d tools/ioemu/cpu-all.h --- a/tools/ioemu/cpu-all.h Tue May 08 10:38:06 2007 +0100 +++ b/tools/ioemu/cpu-all.h Wed May 09 14:17:15 2007 +0100 @@ -727,6 +727,13 @@ void page_unprotect_range(target_ulong d #define cpu_gen_code cpu_ppc_gen_code #define cpu_signal_handler cpu_ppc_signal_handler +#elif defined(TARGET_M68K) +#define CPUState CPUM68KState +#define cpu_init cpu_m68k_init +#define cpu_exec cpu_m68k_exec +#define cpu_gen_code cpu_m68k_gen_code +#define cpu_signal_handler cpu_m68k_signal_handler + #elif defined(TARGET_MIPS) #define CPUState CPUMIPSState #define cpu_init cpu_mips_init @@ -770,6 +777,7 @@ extern int code_copy_enabled; #define CPU_INTERRUPT_TIMER 0x08 /* internal timer exception pending */ #define CPU_INTERRUPT_FIQ 0x10 /* Fast interrupt pending. */ #define CPU_INTERRUPT_HALT 0x20 /* CPU halt wanted */ +#define CPU_INTERRUPT_SMI 0x40 /* (x86 only) SMI interrupt pending */ void cpu_interrupt(CPUState *s, int mask); void cpu_reset_interrupt(CPUState *env, int mask); @@ -889,6 +897,7 @@ void cpu_register_physical_memory(target void cpu_register_physical_memory(target_phys_addr_t start_addr, unsigned long size, unsigned long phys_offset); +uint32_t cpu_get_physical_page_desc(target_phys_addr_t addr); int cpu_register_io_memory(int io_index, CPUReadMemoryFunc **mem_read, CPUWriteMemoryFunc **mem_write, @@ -1042,6 +1051,15 @@ static inline int64_t cpu_get_real_ticks return rval.i64; #endif } +#else +/* The host CPU doesn't have an easily accessible cycle counter. + Just return a monotonically increasing vlue. This will be totally wrong, + but hopefully better than nothing. */ +static inline int64_t cpu_get_real_ticks (void) +{ + static int64_t ticks = 0; + return ticks++; +} #endif /* profiling */ diff -r d2ef85c6bf84 -r 00618037d37d tools/ioemu/cpu-defs.h --- a/tools/ioemu/cpu-defs.h Tue May 08 10:38:06 2007 +0100 +++ b/tools/ioemu/cpu-defs.h Wed May 09 14:17:15 2007 +0100 @@ -80,6 +80,14 @@ typedef unsigned long ram_addr_t; #define TB_JMP_CACHE_BITS 12 #define TB_JMP_CACHE_SIZE (1 << TB_JMP_CACHE_BITS) +/* Only the bottom TB_JMP_PAGE_BITS of the jump cache hash bits vary for + addresses on the same page. The top bits are the same. This allows + TLB invalidation to quickly clear a subset of the hash table. */ +#define TB_JMP_PAGE_BITS (TB_JMP_CACHE_BITS / 2) +#define TB_JMP_PAGE_SIZE (1 << TB_JMP_PAGE_BITS) +#define TB_JMP_ADDR_MASK (TB_JMP_PAGE_SIZE - 1) +#define TB_JMP_PAGE_MASK (TB_JMP_CACHE_SIZE - TB_JMP_PAGE_SIZE) + #define CPU_TLB_BITS 8 #define CPU_TLB_SIZE (1 << CPU_TLB_BITS) diff -r d2ef85c6bf84 -r 00618037d37d tools/ioemu/cpu-exec.c --- a/tools/ioemu/cpu-exec.c Tue May 08 10:38:06 2007 +0100 +++ b/tools/ioemu/cpu-exec.c Wed May 09 14:17:15 2007 +0100 @@ -40,14 +40,14 @@ int tb_invalidated_flag; //#define DEBUG_EXEC //#define DEBUG_SIGNAL -#if defined(TARGET_ARM) || defined(TARGET_SPARC) +#if defined(TARGET_ARM) || defined(TARGET_SPARC) || defined(TARGET_M68K) /* XXX: unify with i386 target */ void cpu_loop_exit(void) { longjmp(env->jmp_env, 1); } #endif -#if !(defined(TARGET_SPARC) || defined(TARGET_SH4)) +#if !(defined(TARGET_SPARC) || defined(TARGET_SH4) || defined(TARGET_M68K)) #define reg_T2 #endif @@ -194,6 +194,10 @@ static inline TranslationBlock *tb_find_ flags = env->hflags & (MIPS_HFLAG_TMASK | MIPS_HFLAG_BMASK); cs_base = 0; pc = env->PC; +#elif defined(TARGET_M68K) + flags = env->fpcr & M68K_FPCR_PREC; + cs_base = 0; + pc = env->pc; #elif defined(TARGET_SH4) flags = env->sr & (SR_MD | SR_RB); cs_base = 0; /* XXXXX */ @@ -222,43 +226,16 @@ static inline TranslationBlock *tb_find_ int cpu_exec(CPUState *env1) { - int saved_T0, saved_T1; -#if defined(reg_T2) - int saved_T2; -#endif - CPUState *saved_env; -#if defined(TARGET_I386) -#ifdef reg_EAX - int saved_EAX; -#endif -#ifdef reg_ECX - int saved_ECX; -#endif -#ifdef reg_EDX - int saved_EDX; -#endif -#ifdef reg_EBX - int saved_EBX; -#endif -#ifdef reg_ESP - int saved_ESP; -#endif -#ifdef reg_EBP - int saved_EBP; -#endif -#ifdef reg_ESI - int saved_ESI; -#endif -#ifdef reg_EDI - int saved_EDI; -#endif -#elif defined(TARGET_SPARC) +#define DECLARE_HOST_REGS 1 +#include "hostregs_helper.h" +#if defined(TARGET_SPARC) #if defined(reg_REGWPTR) uint32_t *saved_regwptr; #endif #endif #if defined(__sparc__) && !defined(HOST_SOLARIS) - int saved_i7, tmp_T0; + int saved_i7; + target_ulong tmp_T0; #endif int ret, interrupt_request; void (*gen_func)(void); @@ -320,44 +297,15 @@ int cpu_exec(CPUState *env1) cpu_single_env = env1; /* first we save global registers */ - saved_env = env; +#define SAVE_HOST_REGS 1 +#include "hostregs_helper.h" env = env1; - saved_T0 = T0; - saved_T1 = T1; -#if defined(reg_T2) - saved_T2 = T2; -#endif #if defined(__sparc__) && !defined(HOST_SOLARIS) /* we also save i7 because longjmp may not restore it */ asm volatile ("mov %%i7, %0" : "=r" (saved_i7)); #endif #if defined(TARGET_I386) -#ifdef reg_EAX - saved_EAX = EAX; -#endif -#ifdef reg_ECX - saved_ECX = ECX; -#endif -#ifdef reg_EDX - saved_EDX = EDX; -#endif -#ifdef reg_EBX - saved_EBX = EBX; -#endif -#ifdef reg_ESP - saved_ESP = ESP; -#endif -#ifdef reg_EBP - saved_EBP = EBP; -#endif -#ifdef reg_ESI - saved_ESI = ESI; -#endif -#ifdef reg_EDI - saved_EDI = EDI; -#endif - env_to_regs(); /* put eflags in CPU temporary format */ CC_SRC = env->eflags & (CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C); @@ -370,6 +318,10 @@ int cpu_exec(CPUState *env1) saved_regwptr = REGWPTR; #endif #elif defined(TARGET_PPC) +#elif defined(TARGET_M68K) + env->cc_op = CC_OP_FLAGS; + env->cc_dest = env->sr & 0xf; + env->cc_x = (env->sr >> 4) & 1; #elif defined(TARGET_MIPS) #elif defined(TARGET_SH4) /* XXXXX */ @@ -390,7 +342,7 @@ int cpu_exec(CPUState *env1) break; } else if (env->user_mode_only) { /* if user mode only, we simulate a fake exception - which will be hanlded outside the cpu execution + which will be handled outside the cpu execution loop */ #if defined(TARGET_I386) do_interrupt_user(env->exception_index, @@ -458,8 +410,16 @@ int cpu_exec(CPUState *env1) interrupt_request = env->interrupt_request; if (__builtin_expect(interrupt_request, 0)) { #if defined(TARGET_I386) - /* if hardware interrupt pending, we execute it */ - if ((interrupt_request & CPU_INTERRUPT_HARD) && + if ((interrupt_request & CPU_INTERRUPT_SMI) && + !(env->hflags & HF_SMM_MASK)) { + env->interrupt_request &= ~CPU_INTERRUPT_SMI; + do_smm_enter(); +#if defined(__sparc__) && !defined(HOST_SOLARIS) + tmp_T0 = 0; +#else + T0 = 0; +#endif + } else if ((interrupt_request & CPU_INTERRUPT_HARD) && (env->eflags & IF_MASK) && !(env->hflags & HF_INHIBIT_IRQ_MASK)) { int intno; @@ -519,7 +479,6 @@ int cpu_exec(CPUState *env1) env->exception_index = EXCP_EXT_INTERRUPT; env->error_code = 0; do_interrupt(env); - env->interrupt_request &= ~CPU_INTERRUPT_HARD; #if defined(__sparc__) && !defined(HOST_SOLARIS) tmp_T0 = 0; #else @@ -548,8 +507,10 @@ int cpu_exec(CPUState *env1) //do_interrupt(0, 0, 0, 0, 0); env->interrupt_request &= ~CPU_INTERRUPT_TIMER; } else if (interrupt_request & CPU_INTERRUPT_HALT) { - env1->halted = 1; - return EXCP_HALTED; + env->interrupt_request &= ~CPU_INTERRUPT_HALT; + env->halted = 1; + env->exception_index = EXCP_HLT; + cpu_loop_exit(); } #elif defined(TARGET_ARM) if (interrupt_request & CPU_INTERRUPT_FIQ @@ -621,6 +582,12 @@ int cpu_exec(CPUState *env1) env->regwptr = REGWPTR; cpu_dump_state(env, logfile, fprintf, 0); #elif defined(TARGET_PPC) + cpu_dump_state(env, logfile, fprintf, 0); +#elif defined(TARGET_M68K) + cpu_m68k_flush_flags(env, env->cc_op); + env->cc_op = CC_OP_FLAGS; + env->sr = (env->sr & 0xffe0) + | env->cc_dest | (env->cc_x << 4); cpu_dump_state(env, logfile, fprintf, 0); #elif defined(TARGET_MIPS) cpu_dump_state(env, logfile, fprintf, 0); @@ -803,32 +770,6 @@ int cpu_exec(CPUState *env1) #endif /* restore flags in standard format */ env->eflags = env->eflags | cc_table[CC_OP].compute_all() | (DF & DF_MASK); - - /* restore global registers */ -#ifdef reg_EAX - EAX = saved_EAX; -#endif -#ifdef reg_ECX - ECX = saved_ECX; -#endif -#ifdef reg_EDX - EDX = saved_EDX; -#endif -#ifdef reg_EBX - EBX = saved_EBX; -#endif -#ifdef reg_ESP - ESP = saved_ESP; -#endif -#ifdef reg_EBP - EBP = saved_EBP; -#endif -#ifdef reg_ESI - ESI = saved_ESI; -#endif -#ifdef reg_EDI - EDI = saved_EDI; -#endif #elif defined(TARGET_ARM) /* XXX: Save/restore host fpu exception state?. */ #elif defined(TARGET_SPARC) @@ -836,21 +777,24 @@ int cpu_exec(CPUState *env1) REGWPTR = saved_regwptr; #endif #elif defined(TARGET_PPC) +#elif defined(TARGET_M68K) + cpu_m68k_flush_flags(env, env->cc_op); + env->cc_op = CC_OP_FLAGS; + env->sr = (env->sr & 0xffe0) + | env->cc_dest | (env->cc_x << 4); #elif defined(TARGET_MIPS) #elif defined(TARGET_SH4) /* XXXXX */ #else #error unsupported target CPU #endif + + /* restore global registers */ #if defined(__sparc__) && !defined(HOST_SOLARIS) asm volatile ("mov %0, %%i7" : : "r" (saved_i7)); #endif - T0 = saved_T0; - T1 = saved_T1; -#if defined(reg_T2) - T2 = saved_T2; -#endif - env = saved_env; +#include "hostregs_helper.h" + /* fail safe : never use cpu_single_env outside cpu_exec() */ cpu_single_env = NULL; return ret; @@ -1093,6 +1037,45 @@ static inline int handle_cpu_signal(unsi return 1; } +#elif defined(TARGET_M68K) +static inline int handle_cpu_signal(unsigned long pc, unsigned long address, + int is_write, sigset_t *old_set, + void *puc) +{ + TranslationBlock *tb; + int ret; + + if (cpu_single_env) + env = cpu_single_env; /* XXX: find a correct solution for multithread */ +#if defined(DEBUG_SIGNAL) + printf("qemu: SIGSEGV pc=0x%08lx address=%08lx w=%d oldset=0x%08lx\n", + pc, address, is_write, *(unsigned long *)old_set); +#endif + /* XXX: locking issue */ + if (is_write && page_unprotect(address, pc, puc)) { + return 1; + } + /* see if it is an MMU fault */ + ret = cpu_m68k_handle_mmu_fault(env, address, is_write, 1, 0); + if (ret < 0) + return 0; /* not an MMU fault */ + if (ret == 0) + return 1; /* the MMU fault was handled without causing real CPU fault */ + /* now we have a real cpu fault */ + tb = tb_find_pc(pc); + if (tb) { + /* the PC is inside the translated code. It means that we have + a virtual CPU fault */ + cpu_restore_state(tb, env, pc, puc); + } + /* we restore the process signal mask as the sigreturn should + do it (XXX: use sigsetjmp) */ + sigprocmask(SIG_SETMASK, old_set, NULL); + cpu_loop_exit(); + /* never comes here */ + return 1; +} + #elif defined (TARGET_MIPS) static inline int handle_cpu_signal(unsigned long pc, unsigned long address, int is_write, sigset_t *old_set, @@ -1193,6 +1176,18 @@ static inline int handle_cpu_signal(unsi #if defined(__i386__) +#if defined(__APPLE__) +# include <sys/ucontext.h> + +# define EIP_sig(context) (*((unsigned long*)&(context)->uc_mcontext->ss.eip)) +# define TRAP_sig(context) ((context)->uc_mcontext->es.trapno) +# define ERROR_sig(context) ((context)->uc_mcontext->es.err) +#else +# define EIP_sig(context) ((context)->uc_mcontext.gregs[REG_EIP]) +# define TRAP_sig(context) ((context)->uc_mcontext.gregs[REG_TRAPNO]) +# define ERROR_sig(context) ((context)->uc_mcontext.gregs[REG_ERR]) +#endif + #if defined(USE_CODE_COPY) static void cpu_send_trap(unsigned long pc, int trap, struct ucontext *uc) @@ -1213,9 +1208,10 @@ static void cpu_send_trap(unsigned long } #endif -int cpu_signal_handler(int host_signum, struct siginfo *info, +int cpu_signal_handler(int host_signum, void *pinfo, void *puc) { + siginfo_t *info = pinfo; struct ucontext *uc = puc; unsigned long pc; int trapno; @@ -1226,8 +1222,8 @@ int cpu_signal_handler(int host_signum, #define REG_ERR ERR #define REG_TRAPNO TRAPNO #endif - pc = uc->uc_mcontext.gregs[REG_EIP]; - trapno = uc->uc_mcontext.gregs[REG_TRAPNO]; + pc = EIP_sig(uc); + trapno = TRAP_sig(uc); #if defined(TARGET_I386) && defined(USE_CODE_COPY) if (trapno == 0x00 || trapno == 0x05) { /* send division by zero or bound exception */ @@ -1237,15 +1233,16 @@ int cpu_signal_handler(int host_signum, #endif return handle_cpu_signal(pc, (unsigned long)info->si_addr, trapno == 0xe ? - (uc->uc_mcontext.gregs[REG_ERR] >> 1) & 1 : 0, + (ERROR_sig(uc) >> 1) & 1 : 0, &uc->uc_sigmask, puc); } #elif defined(__x86_64__) -int cpu_signal_handler(int host_signum, struct siginfo *info, +int cpu_signal_handler(int host_signum, void *pinfo, void *puc) { + siginfo_t *info = pinfo; struct ucontext *uc = puc; unsigned long pc; @@ -1307,9 +1304,10 @@ typedef struct ucontext SIGCONTEXT; # define TRAP_sig(context) EXCEPREG_sig(exception, context) /* number of powerpc exception taken */ #endif /* __APPLE__ */ -int cpu_signal_handler(int host_signum, struct siginfo *info, +int cpu_signal_handler(int host_signum, void *pinfo, void *puc) { + siginfo_t *info = pinfo; struct ucontext *uc = puc; unsigned long pc; int is_write; @@ -1330,9 +1328,10 @@ int cpu_signal_handler(int host_signum, #elif defined(__alpha__) -int cpu_signal_handler(int host_signum, struct siginfo *info, +int cpu_signal_handler(int host_signum, void *pinfo, void *puc) { + siginfo_t *info = pinfo; struct ucontext *uc = puc; uint32_t *pc = uc->uc_mcontext.sc_pc; uint32_t insn = *pc; @@ -1359,9 +1358,10 @@ int cpu_signal_handler(int host_signum, } #elif defined(__sparc__) -int cpu_signal_handler(int host_signum, struct siginfo *info, +int cpu_signal_handler(int host_signum, void *pinfo, void *puc) { + siginfo_t *info = pinfo; uint32_t *regs = (uint32_t *)(info + 1); void *sigmask = (regs + 20); unsigned long pc; @@ -1392,9 +1392,10 @@ int cpu_signal_handler(int host_signum, #elif defined(__arm__) -int cpu_signal_handler(int host_signum, struct siginfo *info, +int cpu_signal_handler(int host_signum, void *pinfo, void *puc) { + siginfo_t *info = pinfo; struct ucontext *uc = puc; unsigned long pc; int is_write; @@ -1404,14 +1405,15 @@ int cpu_signal_handler(int host_signum, is_write = 0; return handle_cpu_signal(pc, (unsigned long)info->si_addr, is_write, - &uc->uc_sigmask); + &uc->uc_sigmask, puc); } #elif defined(__mc68000) -int cpu_signal_handler(int host_signum, struct siginfo *info, +int cpu_signal_handler(int host_signum, void *pinfo, void *puc) { + siginfo_t *info = pinfo; struct ucontext *uc = puc; unsigned long pc; int is_write; @@ -1431,8 +1433,9 @@ int cpu_signal_handler(int host_signum, # define __ISR_VALID 1 #endif -int cpu_signal_handler(int host_signum, struct siginfo *info, void *puc) -{ +int cpu_signal_handler(int host_signum, void *pinfo, void *puc) +{ + siginfo_t *info = pinfo; struct ucontext *uc = puc; unsigned long ip; int is_write = 0; @@ -1459,9 +1462,10 @@ int cpu_signal_handler(int host_signum, #elif defined(__s390__) -int cpu_signal_handler(int host_signum, struct siginfo *info, +int cpu_signal_handler(int host_signum, void *pinfo, void *puc) { + siginfo_t *info = pinfo; struct ucontext *uc = puc; unsigned long pc; int is_write; diff -r d2ef85c6bf84 -r 00618037d37d tools/ioemu/cutils.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/ioemu/cutils.c Wed May 09 14:17:15 2007 +0100 @@ -0,0 +1,83 @@ +/* + * Simple C functions to supplement the C library + * + * Copyright (c) 2006 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" + +void pstrcpy(char *buf, int buf_size, const char *str) +{ + int c; + char *q = buf; + + if (buf_size <= 0) + return; + + for(;;) { + c = *str++; + if (c == 0 || q >= buf + buf_size - 1) + break; + *q++ = c; + } + *q = '\0'; +} + +/* strcat and truncate. */ +char *pstrcat(char *buf, int buf_size, const char *s) +{ + int len; + len = strlen(buf); + if (len < buf_size) + pstrcpy(buf + len, buf_size - len, s); + return buf; +} + +int strstart(const char *str, const char *val, const char **ptr) +{ + const char *p, *q; + p = str; + q = val; + while (*q != '\0') { + if (*p != *q) + return 0; + p++; + q++; + } + if (ptr) + *ptr = p; + return 1; +} + +int stristart(const char *str, const char *val, const char **ptr) +{ + const char *p, *q; + p = str; + q = val; + while (*q != '\0') { + if (toupper(*p) != toupper(*q)) + return 0; + p++; + q++; + } + if (ptr) + *ptr = p; + return 1; +} diff -r d2ef85c6bf84 -r 00618037d37d tools/ioemu/disas.c --- a/tools/ioemu/disas.c Tue May 08 10:38:06 2007 +0100 +++ b/tools/ioemu/disas.c Wed May 09 14:17:15 2007 +0100 @@ -186,14 +186,14 @@ void target_disas(FILE *out, target_ulon disasm_info.mach = bfd_mach_ppc; #endif print_insn = print_insn_ppc; +#elif defined(TARGET_M68K) + print_insn = print_insn_m68k; #elif defined(TARGET_MIPS) #ifdef TARGET_WORDS_BIGENDIAN print_insn = print_insn_big_mips; #else print_insn = print_insn_little_mips; #endif -#elif defined(TARGET_M68K) - print_insn = print_insn_m68k; #elif defined(TARGET_SH4) disasm_info.mach = bfd_mach_sh4; print_insn = print_insn_sh; @@ -271,11 +271,9 @@ void disas(FILE *out, void *code, unsign for (pc = (unsigned long)code; pc < (unsigned long)code + size; pc += count) { fprintf(out, "0x%08lx: ", pc); #ifdef __arm__ - /* since data are included in the code, it is better to + /* since data is included in the code, it is better to display code data too */ - if (is_host) { - fprintf(out, "%08x ", (int)bfd_getl32((const bfd_byte *)pc)); - } + fprintf(out, "%08x ", (int)bfd_getl32((const bfd_byte *)pc)); #endif count = print_insn(pc, &disasm_info); fprintf(out, "\n"); @@ -387,14 +385,14 @@ void monitor_disas(CPUState *env, disasm_info.mach = bfd_mach_ppc; #endif print_insn = print_insn_ppc; +#elif defined(TARGET_M68K) + print_insn = print_insn_m68k; #elif defined(TARGET_MIPS) #ifdef TARGET_WORDS_BIGENDIAN print_insn = print_insn_big_mips; #else print_insn = print_insn_little_mips; #endif -#elif defined(TARGET_M68K) - print_insn = print_insn_m68k; #else term_printf("0x" TARGET_FMT_lx ": Asm output not supported on this arch\n", pc); diff -r d2ef85c6bf84 -r 00618037d37d tools/ioemu/dyngen-exec.h --- a/tools/ioemu/dyngen-exec.h Tue May 08 10:38:06 2007 +0100 +++ b/tools/ioemu/dyngen-exec.h Wed May 09 14:17:15 2007 +0100 @@ -61,6 +61,9 @@ typedef signed long long int64_t; typedef signed long long int64_t; #endif #endif + +/* XXX: This may be wrong for 64-bit ILP32 hosts. */ +typedef void * host_reg_t; #define INT8_MIN (-128) #define INT16_MIN (-32767-1) @@ -188,7 +191,7 @@ extern int printf(const char *, ...); #endif /* force GCC to generate only one epilog at the end of the function */ -#define FORCE_RET() asm volatile (""); +#define FORCE_RET() __asm__ __volatile__("" : : : "memory"); #ifndef OPPROTO #define OPPROTO diff -r d2ef85c6bf84 -r 00618037d37d tools/ioemu/dyngen.c --- a/tools/ioemu/dyngen.c Tue May 08 10:38:06 2007 +0100 +++ b/tools/ioemu/dyngen.c Wed May 09 14:17:15 2007 +0100 @@ -127,10 +127,12 @@ typedef int32_t host_long; typedef int32_t host_long; typedef uint32_t host_ulong; #define swabls(x) swab32s(x) +#define swablss(x) swab32ss(x) #else typedef int64_t host_long; typedef uint64_t host_ulong; #define swabls(x) swab64s(x) +#define swablss(x) swab64ss(x) #endif #ifdef ELF_USES_RELOCA @@ -284,7 +286,17 @@ void swab32s(uint32_t *p) *p = bswap32(*p); } +void swab32ss(int32_t *p) +{ + *p = bswap32(*p); +} + void swab64s(uint64_t *p) +{ + *p = bswap64(*p); +} + +void swab64ss(int64_t *p) { *p = bswap64(*p); } @@ -397,7 +409,7 @@ void elf_swap_rel(ELF_RELOC *rel) swabls(&rel->r_offset); swabls(&rel->r_info); #ifdef ELF_USES_RELOCA - swabls(&rel->r_addend); + swablss(&rel->r_addend); #endif } @@ -505,7 +517,7 @@ int load_object(const char *filename) } sec = &shdr[ehdr.e_shstrndx]; - shstr = sdata[ehdr.e_shstrndx]; + shstr = (char *)sdata[ehdr.e_shstrndx]; /* swap relocations */ for(i = 0; i < ehdr.e_shnum; i++) { @@ -541,7 +553,7 @@ int load_object(const char *filename) strtab_sec = &shdr[symtab_sec->sh_link]; symtab = (ElfW(Sym) *)sdata[symtab_sec - shdr]; - strtab = sdata[symtab_sec->sh_link]; + strtab = (char *)sdata[symtab_sec->sh_link]; nb_syms = symtab_sec->sh_size / sizeof(ElfW(Sym)); if (do_swap) { @@ -1255,90 +1267,149 @@ int arm_emit_ldr_info(const char *name, { uint8_t *p; uint32_t insn; - int offset, min_offset, pc_offset, data_size; + int offset, min_offset, pc_offset, data_size, spare, max_pool; uint8_t data_allocated[1024]; unsigned int data_index; + int type; memset(data_allocated, 0, sizeof(data_allocated)); p = p_start; min_offset = p_end - p_start; + spare = 0x7fffffff; while (p < p_start + min_offset) { insn = get32((uint32_t *)p); + /* TODO: Armv5e ldrd. */ + /* TODO: VFP load. */ if ((insn & 0x0d5f0000) == 0x051f0000) { /* ldr reg, [pc, #im] */ offset = insn & 0xfff; if (!(insn & 0x00800000)) - offset = -offset; + offset = -offset; + max_pool = 4096; + type = 0; + } else if ((insn & 0x0e5f0f00) == 0x0c1f0100) { + /* FPA ldf. */ + offset = (insn & 0xff) << 2; + if (!(insn & 0x00800000)) + offset = -offset; + max_pool = 1024; + type = 1; + } else if ((insn & 0x0fff0000) == 0x028f0000) { + /* Some gcc load a doubleword immediate with + add regN, pc, #imm + ldmia regN, {regN, regM} + Hope and pray the compiler never generates somethin like + add reg, pc, #imm1; ldr reg, [reg, #-imm2]; */ + int r; + + r = (insn & 0xf00) >> 7; + offset = ((insn & 0xff) >> r) | ((insn & 0xff) << (32 - r)); + max_pool = 1024; + type = 2; + } else { + max_pool = 0; + type = -1; + } + if (type >= 0) { + /* PC-relative load needs fixing up. */ + if (spare > max_pool - offset) + spare = max_pool - offset; if ((offset & 3) !=0) - error("%s:%04x: ldr pc offset must be 32 bit aligned", + error("%s:%04x: pc offset must be 32 bit aligned", + name, start_offset + p - p_start); + if (offset < 0) + error("%s:%04x: Embedded literal value", name, start_offset + p - p_start); pc_offset = p - p_start + offset + 8; if (pc_offset <= (p - p_start) || pc_offset >= (p_end - p_start)) - error("%s:%04x: ldr pc offset must point inside the function code", + error("%s:%04x: pc offset must point inside the function code", name, start_offset + p - p_start); if (pc_offset < min_offset) min_offset = pc_offset; if (outfile) { - /* ldr position */ + /* The intruction position */ fprintf(outfile, " arm_ldr_ptr->ptr = gen_code_ptr + %d;\n", p - p_start); - /* ldr data index */ - data_index = ((p_end - p_start) - pc_offset - 4) >> 2; - fprintf(outfile, " arm_ldr_ptr->data_ptr = arm_data_ptr + %d;\n", + /* The position of the constant pool data. */ + data_index = ((p_end - p_start) - pc_offset) >> 2; + fprintf(outfile, " arm_ldr_ptr->data_ptr = arm_data_ptr - %d;\n", data_index); + fprintf(outfile, " arm_ldr_ptr->type = %d;\n", type); fprintf(outfile, " arm_ldr_ptr++;\n"); - if (data_index >= sizeof(data_allocated)) - error("%s: too many data", name); - if (!data_allocated[data_index]) { - ELF_RELOC *rel; - int i, addend, type; - const char *sym_name, *p; - char relname[1024]; - - data_allocated[data_index] = 1; - - /* data value */ - addend = get32((uint32_t *)(p_start + pc_offset)); - relname[0] = '\0'; - for(i = 0, rel = relocs;i < nb_relocs; i++, rel++) { - if (rel->r_offset == (pc_offset + start_offset)) { - sym_name = get_rel_sym_name(rel); - /* the compiler leave some unnecessary references to the code */ - get_reloc_expr(relname, sizeof(relname), sym_name); - type = ELF32_R_TYPE(rel->r_info); - if (type != R_ARM_ABS32) - error("%s: unsupported data relocation", name); - break; - } - } - fprintf(outfile, " arm_data_ptr[%d] = 0x%x", - data_index, addend); - if (relname[0] != '\0') - fprintf(outfile, " + %s", relname); - fprintf(outfile, ";\n"); + } + } + p += 4; + } + + /* Copy and relocate the constant pool data. */ + data_size = (p_end - p_start) - min_offset; + if (data_size > 0 && outfile) { + spare += min_offset; + fprintf(outfile, " arm_data_ptr -= %d;\n", data_size >> 2); + fprintf(outfile, " arm_pool_ptr -= %d;\n", data_size); + fprintf(outfile, " if (arm_pool_ptr > gen_code_ptr + %d)\n" + " arm_pool_ptr = gen_code_ptr + %d;\n", + spare, spare); + + data_index = 0; + for (pc_offset = min_offset; + pc_offset < p_end - p_start; + pc_offset += 4) { + + ELF_RELOC *rel; + int i, addend, type; + const char *sym_name; + char relname[1024]; + + /* data value */ + addend = get32((uint32_t *)(p_start + pc_offset)); + relname[0] = '\0'; + for(i = 0, rel = relocs;i < nb_relocs; i++, rel++) { + if (rel->r_offset == (pc_offset + start_offset)) { + sym_name = get_rel_sym_name(rel); + /* the compiler leave some unnecessary references to the code */ + get_reloc_expr(relname, sizeof(relname), sym_name); + type = ELF32_R_TYPE(rel->r_info); + if (type != R_ARM_ABS32) + error("%s: unsupported data relocation", name); + break; } } - } - p += 4; - } - data_size = (p_end - p_start) - min_offset; - if (data_size > 0 && outfile) { - fprintf(outfile, " arm_data_ptr += %d;\n", data_size >> 2); - } - - /* the last instruction must be a mov pc, lr */ + fprintf(outfile, " arm_data_ptr[%d] = 0x%x", + data_index, addend); + if (relname[0] != '\0') + fprintf(outfile, " + %s", relname); + fprintf(outfile, ";\n"); + + data_index++; + } + } + if (p == p_start) goto arm_ret_error; p -= 4; insn = get32((uint32_t *)p); - if ((insn & 0xffff0000) != 0xe91b0000) { + /* The last instruction must be an ldm instruction. There are several + forms generated by gcc: + ldmib sp, {..., pc} (implies a sp adjustment of +4) + ldmia sp, {..., pc} + ldmea fp, {..., pc} */ + if ((insn & 0xffff8000) == 0xe99d8000) { + if (outfile) { + fprintf(outfile, + " *(uint32_t *)(gen_code_ptr + %d) = 0xe28dd004;\n", + p - p_start); + } + p += 4; + } else if ((insn & 0xffff8000) != 0xe89d8000 + && (insn & 0xffff8000) != 0xe91b8000) { arm_ret_error: if (!outfile) printf("%s: invalid epilog\n", name); } - return p - p_start; + return p - p_start; } #endif @@ -1537,6 +1608,8 @@ void gen_code(const char *name, host_ulo } #elif defined(HOST_ARM) { + uint32_t insn; + if ((p_end - p_start) <= 16) error("%s: function too small", name); if (get32((uint32_t *)p_start) != 0xe1a0c00d || @@ -1545,6 +1618,12 @@ void gen_code(const char *name, host_ulo error("%s: invalid prolog", name); p_start += 12; start_offset += 12; + insn = get32((uint32_t *)p_start); + if ((insn & 0xffffff00) == 0xe24dd000) { + /* Stack adjustment. Assume op uses the frame pointer. */ + p_start -= 4; + start_offset -= 4; + } copy_size = arm_emit_ldr_info(name, start_offset, NULL, p_start, p_end, relocs, nb_relocs); } @@ -2282,7 +2361,37 @@ void gen_code(const char *name, host_ulo int type; int addend; int reloc_offset; - + uint32_t insn; + + insn = get32((uint32_t *)(p_start + 4)); + /* If prologue ends in sub sp, sp, #const then assume + op has a stack frame and needs the frame pointer. */ + if ((insn & 0xffffff00) == 0xe24dd000) { + int i; + uint32_t opcode; + opcode = 0xe28db000; /* add fp, sp, #0. */ +#if 0 +/* ??? Need to undo the extra stack adjustment at the end of the op. + For now just leave the stack misaligned and hope it doesn't break anything + too important. */ + if ((insn & 4) != 0) { + /* Preserve doubleword stack alignment. */ + fprintf(outfile, + " *(uint32_t *)(gen_code_ptr + 4)= 0x%x;\n", + insn + 4); + opcode -= 4; + } +#endif + insn = get32((uint32_t *)(p_start - 4)); + /* Calculate the size of the saved registers, + excluding pc. */ + for (i = 0; i < 15; i++) { + if (insn & (1 << i)) + opcode += 4; + } + fprintf(outfile, + " *(uint32_t *)gen_code_ptr = 0x%x;\n", opcode); + } arm_emit_ldr_info(name, start_offset, outfile, p_start, p_end, relocs, nb_relocs); @@ -2303,6 +2412,8 @@ void gen_code(const char *name, host_ulo reloc_offset, name, addend); break; case R_ARM_PC24: + case R_ARM_JUMP24: + case R_ARM_CALL: fprintf(outfile, " arm_reloc_pc24((uint32_t *)(gen_code_ptr + %d), 0x%x, %s);\n", reloc_offset, addend, name); break; @@ -2407,6 +2518,28 @@ int gen_file(FILE *outfile, int out_type } else { /* generate big code generation switch */ + +#ifdef HOST_ARM + /* We need to know the size of all the ops so we can figure out when + to emit constant pools. This must be consistent with opc.h. */ +fprintf(outfile, +"static const uint32_t arm_opc_size[] = {\n" +" 0,\n" /* end */ +" 0,\n" /* nop */ +" 0,\n" /* nop1 */ +" 0,\n" /* nop2 */ +" 0,\n"); /* nop3 */ + for(i = 0, sym = symtab; i < nb_syms; i++, sym++) { + const char *name; + name = get_sym_name(sym); + if (strstart(name, OP_PREFIX, NULL)) { + fprintf(outfile, " %d,\n", sym->st_size); + } + } +fprintf(outfile, +"};\n"); +#endif + fprintf(outfile, "int dyngen_code(uint8_t *gen_code_buf,\n" " uint16_t *label_offsets, uint16_t *jmp_offsets,\n" @@ -2417,10 +2550,36 @@ fprintf(outfile, " const uint32_t *opparam_ptr;\n"); #ifdef HOST_ARM +/* Arm is tricky because it uses constant pools for loading immediate values. + We assume (and require) each function is code followed by a constant pool. + All the ops are small so this should be ok. For each op we figure + out how much "spare" range we have in the load instructions. This allows + us to insert subsequent ops in between the op and the constant pool, + eliminating the neeed to jump around the pool. + + We currently generate: + + [ For this example we assume merging would move op1_pool out of range. + In practice we should be able to combine many ops before the offset + limits are reached. ] + op1_code; + op2_code; + goto op3; + op2_pool; + op1_pool; +op3: + op3_code; + ret; + op3_pool; + + Ideally we'd put op1_pool before op2_pool, but that requires two passes. + */ fprintf(outfile, " uint8_t *last_gen_code_ptr = gen_code_buf;\n" " LDREntry *arm_ldr_ptr = arm_ldr_table;\n" -" uint32_t *arm_data_ptr = arm_data_table;\n"); +" uint32_t *arm_data_ptr = arm_data_table + ARM_LDR_TABLE_SIZE;\n" +/* Initialise the parmissible pool offset to an arbitary large value. */ +" uint8_t *arm_pool_ptr = gen_code_buf + 0x1000000;\n"); #endif #ifdef HOST_IA64 { @@ -2489,9 +2648,23 @@ fprintf(outfile, /* Generate prologue, if needed. */ fprintf(outfile, -" for(;;) {\n" -" switch(*opc_ptr++) {\n" -); +" for(;;) {\n"); + +#ifdef HOST_ARM +/* Generate constant pool if needed */ +fprintf(outfile, +" if (gen_code_ptr + arm_opc_size[*opc_ptr] >= arm_pool_ptr) {\n" +" gen_code_ptr = arm_flush_ldr(gen_code_ptr, arm_ldr_table, " +"arm_ldr_ptr, arm_data_ptr, arm_data_table + ARM_LDR_TABLE_SIZE, 1);\n" +" last_gen_code_ptr = gen_code_ptr;\n" +" arm_ldr_ptr = arm_ldr_table;\n" +" arm_data_ptr = arm_data_table + ARM_LDR_TABLE_SIZE;\n" +" arm_pool_ptr = gen_code_ptr + 0x1000000;\n" +" }\n"); +#endif + +fprintf(outfile, +" switch(*opc_ptr++) {\n"); for(i = 0, sym = symtab; i < nb_syms; i++, sym++) { const char *name; @@ -2525,17 +2698,6 @@ fprintf(outfile, " goto the_end;\n" " }\n"); -#ifdef HOST_ARM -/* generate constant table if needed */ -fprintf(outfile, -" if ((gen_code_ptr - last_gen_code_ptr) >= (MAX_FRAG_SIZE - MAX_OP_SIZE)) {\n" -" gen_code_ptr = arm_flush_ldr(gen_code_ptr, arm_ldr_table, arm_ldr_ptr, arm_data_table, arm_data_ptr, 1);\n" -" last_gen_code_ptr = gen_code_ptr;\n" -" arm_ldr_ptr = arm_ldr_table;\n" -" arm_data_ptr = arm_data_table;\n" -" }\n"); -#endif - fprintf(outfile, " }\n" @@ -2553,7 +2715,10 @@ fprintf(outfile, /* generate some code patching */ #ifdef HOST_ARM -fprintf(outfile, "gen_code_ptr = arm_flush_ldr(gen_code_ptr, arm_ldr_table, arm_ldr_ptr, arm_data_table, arm_data_ptr, 0);\n"); +fprintf(outfile, +"if (arm_data_ptr != arm_data_table + ARM_LDR_TABLE_SIZE)\n" +" gen_code_ptr = arm_flush_ldr(gen_code_ptr, arm_ldr_table, " +"arm_ldr_ptr, arm_data_ptr, arm_data_table + ARM_LDR_TABLE_SIZE, 0);\n"); _______________________________________________ Xen-changelog mailing list Xen-changelog@xxxxxxxxxxxxxxxxxxx http://lists.xensource.com/xen-changelog
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |