summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore5
-rw-r--r--CODING_STYLE3
-rw-r--r--HACKING125
-rw-r--r--MAINTAINERS8
-rw-r--r--Makefile81
-rw-r--r--Makefile.objs58
-rw-r--r--Makefile.target30
-rw-r--r--QMP/README73
-rw-r--r--VERSION2
-rw-r--r--arch_init.c24
-rw-r--r--audio/alsaaudio.c27
-rw-r--r--audio/audio.h8
-rw-r--r--audio/audio_int.h8
-rw-r--r--audio/audio_pt_int.c25
-rw-r--r--audio/audio_template.h4
-rw-r--r--audio/esdaudio.c58
-rw-r--r--audio/paaudio.c2
-rw-r--r--audio/sdlaudio.c22
-rw-r--r--balloon.c2
-rw-r--r--block-migration.c20
-rw-r--r--block.c107
-rw-r--r--block.h3
-rw-r--r--block/blkdebug.c4
-rw-r--r--block/blkverify.c382
-rw-r--r--block/nbd.c64
-rw-r--r--block/qcow2-cluster.c35
-rw-r--r--block/qcow2-refcount.c14
-rw-r--r--block/qcow2-snapshot.c2
-rw-r--r--block/qcow2.c115
-rw-r--r--block/qcow2.h4
-rw-r--r--block/raw-posix.c119
-rw-r--r--block/raw-win32.c6
-rw-r--r--block/raw.c6
-rw-r--r--block/sheepdog.c10
-rw-r--r--block/vvfat.c29
-rw-r--r--block_int.h12
-rw-r--r--blockdev.c5
-rw-r--r--blockdev.h23
-rw-r--r--bsd-user/main.c6
-rw-r--r--bsd-user/qemu.h2
-rw-r--r--buffered_file.c12
-rw-r--r--compatfd.c117
-rw-r--r--compatfd.h43
-rwxr-xr-xconfigure312
-rw-r--r--console.c6
-rw-r--r--cpu-all.h4
-rw-r--r--cpu-common.h5
-rw-r--r--cpu-exec.c17
-rw-r--r--cpus.c213
-rw-r--r--cpus.h2
-rw-r--r--cris-dis.c4
-rw-r--r--cutils.c50
-rw-r--r--darwin-user/qemu.h2
-rw-r--r--default-configs/ppc-softmmu.mak2
-rw-r--r--default-configs/ppc64-softmmu.mak2
-rw-r--r--default-configs/ppcemb-softmmu.mak2
-rw-r--r--disas.c3
-rw-r--r--disas.h3
-rw-r--r--docs/blkverify.txt69
-rw-r--r--docs/migration.txt303
-rw-r--r--docs/specs/ivshmem_device_spec.txt96
-rw-r--r--docs/tracing.txt184
-rw-r--r--dyngen-exec.h23
-rw-r--r--elf.h44
-rw-r--r--exec-all.h7
-rw-r--r--exec.c140
-rw-r--r--feature_to_c.sh1
-rw-r--r--fpu/softfloat.c79
-rw-r--r--fpu/softfloat.h3
-rw-r--r--fsdev/qemu-fsdev.c57
-rw-r--r--gdbstub.c1
-rw-r--r--gdbstub.h3
-rw-r--r--hmp-commands.hx1216
-rw-r--r--hw/acpi.c28
-rw-r--r--hw/acpi_piix4.c35
-rw-r--r--hw/apic.c48
-rw-r--r--hw/bonito.c1
-rw-r--r--hw/cirrus_vga.c30
-rw-r--r--hw/cirrus_vga_rop.h38
-rw-r--r--hw/cirrus_vga_rop2.h12
-rw-r--r--hw/device-hotplug.c1
-rw-r--r--hw/e1000.c38
-rw-r--r--hw/eepro100.c36
-rw-r--r--hw/elf_ops.h5
-rw-r--r--hw/escc.c56
-rw-r--r--hw/esp.c53
-rw-r--r--hw/esp.h3
-rw-r--r--hw/etraxfs.c1
-rw-r--r--hw/etraxfs_eth.c3
-rw-r--r--hw/fdc.c1
-rw-r--r--hw/fdc.h1
-rw-r--r--hw/file-op-9p.h40
-rw-r--r--hw/fmopl.c6
-rw-r--r--hw/gumstix.c1
-rw-r--r--hw/hw.h8
-rw-r--r--hw/ide/core.c99
-rw-r--r--hw/ide/pci.c61
-rw-r--r--hw/ide/qdev.c1
-rw-r--r--hw/ide/via.c1
-rw-r--r--hw/ivshmem.c829
-rw-r--r--hw/jazz_led.c21
-rw-r--r--hw/loader.c5
-rw-r--r--hw/lsi53c895a.c5
-rw-r--r--hw/mainstone.c1
-rw-r--r--hw/mips_fulong2e.c34
-rw-r--r--hw/mips_int.c32
-rw-r--r--hw/mips_jazz.c5
-rw-r--r--hw/mips_malta.c22
-rw-r--r--hw/mips_r4k.c4
-rw-r--r--hw/mipsnet.c2
-rw-r--r--hw/msix.c1
-rw-r--r--hw/multiboot.c2
-rw-r--r--hw/musicpal.c1
-rw-r--r--hw/omap.h6
-rw-r--r--hw/omap1.c18
-rw-r--r--hw/omap2.c6
-rw-r--r--hw/omap_clk.c1
-rw-r--r--hw/omap_i2c.c5
-rw-r--r--hw/omap_mmc.c5
-rw-r--r--hw/omap_sx1.c1
-rw-r--r--hw/omap_uart.c12
-rw-r--r--hw/pc.c7
-rw-r--r--hw/pc_piix.c8
-rw-r--r--hw/pci-hotplug.c3
-rw-r--r--hw/pci.c3
-rw-r--r--hw/pci.h29
-rw-r--r--hw/pckbd.c23
-rw-r--r--hw/pcmcia.h1
-rw-r--r--hw/petalogix_s3adsp1800_mmu.c14
-rw-r--r--hw/piix_pci.c1
-rw-r--r--hw/ppc.c27
-rw-r--r--hw/ppc.h7
-rw-r--r--hw/ppc405_boards.c29
-rw-r--r--hw/ppc405_uc.c28
-rw-r--r--hw/ppc440_bamboo.c41
-rw-r--r--hw/ppc4xx_devs.c5
-rw-r--r--hw/ppc_mac.h1
-rw-r--r--hw/ppc_newworld.c55
-rw-r--r--hw/ppc_oldworld.c121
-rw-r--r--hw/ppc_prep.c7
-rw-r--r--hw/ppce500_mpc8544ds.c13
-rw-r--r--hw/pxa2xx.c16
-rw-r--r--hw/qdev-properties.c3
-rw-r--r--hw/qdev.c3
-rw-r--r--hw/qdev.h1
-rw-r--r--hw/r2d.c1
-rw-r--r--hw/rc4030.c5
-rw-r--r--hw/realview.c1
-rw-r--r--hw/s390-virtio-bus.c8
-rw-r--r--hw/s390-virtio-bus.h1
-rw-r--r--hw/scsi-bus.c16
-rw-r--r--hw/scsi-disk.c146
-rw-r--r--hw/scsi-generic.c40
-rw-r--r--hw/scsi.h1
-rw-r--r--hw/sd.c3
-rw-r--r--hw/serial.c26
-rw-r--r--hw/sh_intc.c3
-rw-r--r--hw/slavio_timer.c4
-rw-r--r--hw/sm501.c5
-rw-r--r--hw/soc_dma.c5
-rw-r--r--hw/sparc32_dma.c23
-rw-r--r--hw/spitz.c1
-rw-r--r--hw/sun4m.c80
-rw-r--r--hw/sun4u.c1
-rw-r--r--hw/syborg_virtio.c8
-rw-r--r--hw/sysbus.c3
-rw-r--r--hw/sysbus.h5
-rw-r--r--hw/tc6393xb_template.h2
-rw-r--r--hw/tosa.c1
-rw-r--r--hw/usb-msd.c3
-rw-r--r--hw/usb-net.c2
-rw-r--r--hw/usb-wacom.c13
-rw-r--r--hw/versatilepb.c1
-rw-r--r--hw/vga.c7
-rw-r--r--hw/vhost.c58
-rw-r--r--hw/vhost_net.c27
-rw-r--r--hw/virtex_ml507.c276
-rw-r--r--hw/virtio-9p-debug.c195
-rw-r--r--hw/virtio-9p-debug.h1
-rw-r--r--hw/virtio-9p-local.c142
-rw-r--r--hw/virtio-9p-posix-acl.c140
-rw-r--r--hw/virtio-9p-xattr-user.c109
-rw-r--r--hw/virtio-9p-xattr.c156
-rw-r--r--hw/virtio-9p-xattr.h103
-rw-r--r--hw/virtio-9p.c1969
-rw-r--r--hw/virtio-9p.h257
-rw-r--r--hw/virtio-balloon.c12
-rw-r--r--hw/virtio-blk.c32
-rw-r--r--hw/virtio-net.c218
-rw-r--r--hw/virtio-net.h14
-rw-r--r--hw/virtio-pci.c54
-rw-r--r--hw/virtio-serial-bus.c31
-rw-r--r--hw/virtio.c59
-rw-r--r--hw/virtio.h11
-rw-r--r--hw/vmmouse.c31
-rw-r--r--hw/vmware_vga.c80
-rw-r--r--hw/vt82c686.c5
-rw-r--r--hw/watchdog.c2
-rw-r--r--hw/wm8750.c5
-rw-r--r--hw/xen_backend.h4
-rw-r--r--hw/xen_devconfig.c2
-rw-r--r--hw/xen_disk.c1
-rw-r--r--hw/xen_machine_pv.c1
-rw-r--r--ioport.c7
-rw-r--r--json-parser.c3
-rw-r--r--kvm-all.c83
-rw-r--r--kvm-stub.c10
-rw-r--r--kvm.h10
-rw-r--r--linux-user/elfload.c1799
-rw-r--r--linux-user/flatload.c3
-rw-r--r--linux-user/linuxload.c17
-rw-r--r--linux-user/m68k-sim.c6
-rw-r--r--linux-user/main.c39
-rw-r--r--linux-user/mmap.c16
-rw-r--r--linux-user/qemu.h9
-rw-r--r--linux-user/signal.c27
-rw-r--r--linux-user/syscall.c20
-rw-r--r--linux-user/syscall_defs.h14
-rw-r--r--migration-tcp.c5
-rw-r--r--migration.c10
-rw-r--r--monitor.c466
-rw-r--r--monitor.h8
-rw-r--r--nbd.c149
-rw-r--r--nbd.h7
-rw-r--r--net.c81
-rw-r--r--net.h1
-rw-r--r--net/tap-aix.c9
-rw-r--r--net/tap-bsd.c13
-rw-r--r--net/tap-haiku.c61
-rw-r--r--net/tap-linux.c29
-rw-r--r--net/tap-linux.h8
-rw-r--r--net/tap-solaris.c10
-rw-r--r--net/tap-win32.c14
-rw-r--r--net/tap.c49
-rw-r--r--net/tap.h4
-rw-r--r--os-posix.c2
-rw-r--r--os-win32.c21
-rw-r--r--osdep.c47
-rw-r--r--osdep.h37
-rw-r--r--pc-bios/README6
-rw-r--r--pc-bios/bios.binbin131072 -> 131072 bytes
-rw-r--r--pc-bios/openbios-ppcbin316128 -> 320328 bytes
-rw-r--r--pc-bios/openbios-sparc32bin217664 -> 217660 bytes
-rw-r--r--pc-bios/openbios-sparc64bin1065872 -> 1065864 bytes
-rw-r--r--pc-bios/video.xbin12192 -> 0 bytes
-rw-r--r--pflib.c213
-rw-r--r--pflib.h20
-rw-r--r--posix-aio-compat.c7
-rw-r--r--qemu-barrier.h3
-rw-r--r--qemu-char.c9
-rw-r--r--qemu-char.h6
-rw-r--r--qemu-common.h38
-rw-r--r--qemu-config.c91
-rw-r--r--qemu-config.h13
-rw-r--r--qemu-doc.texi64
-rw-r--r--qemu-error.h9
-rw-r--r--qemu-img-cmds.hx2
-rw-r--r--qemu-img.c7
-rw-r--r--qemu-io.c13
-rw-r--r--qemu-malloc.c12
-rw-r--r--qemu-nbd.c17
-rw-r--r--qemu-options.hx61
-rw-r--r--qemu-os-win32.h3
-rw-r--r--qemu-thread.c22
-rw-r--r--qemu-thread.h4
-rw-r--r--qemu-timer-common.c62
-rw-r--r--qemu-timer.c73
-rw-r--r--qemu-timer.h74
-rw-r--r--qemu-tool.c7
-rw-r--r--qemu_socket.h1
-rw-r--r--qerror.c10
-rw-r--r--qerror.h8
-rw-r--r--qjson.c55
-rw-r--r--qjson.h8
-rw-r--r--qmp-commands.hx (renamed from qemu-monitor.hx)1049
-rw-r--r--range.h29
m---------roms/seabios0
-rw-r--r--rules.mak9
-rw-r--r--savevm.c296
-rw-r--r--simpletrace.c255
-rw-r--r--simpletrace.h40
-rwxr-xr-xsimpletrace.py93
-rw-r--r--slirp/bootp.c2
-rw-r--r--slirp/cksum.c20
-rw-r--r--slirp/ip.h40
-rw-r--r--slirp/ip_icmp.h2
-rw-r--r--slirp/ip_input.c4
-rw-r--r--slirp/ip_output.c22
-rw-r--r--slirp/main.h2
-rw-r--r--slirp/misc.h20
-rw-r--r--slirp/slirp.c3
-rw-r--r--slirp/slirp.h45
-rw-r--r--slirp/slirp_config.h6
-rw-r--r--slirp/socket.c2
-rw-r--r--slirp/socket.h10
-rw-r--r--slirp/tcp.h14
-rw-r--r--slirp/tcp_input.c4
-rw-r--r--slirp/tcp_output.c10
-rw-r--r--slirp/tcp_subr.c12
-rw-r--r--slirp/tcp_var.h14
-rw-r--r--slirp/tftp.c8
-rw-r--r--slirp/tftp.h14
-rw-r--r--slirp/udp.c8
-rw-r--r--slirp/udp.h10
-rw-r--r--sysemu.h2
-rw-r--r--target-cris/cpu.h5
-rw-r--r--target-cris/helper.c10
-rw-r--r--target-cris/mmu.c4
-rw-r--r--target-cris/op_helper.c4
-rw-r--r--target-cris/translate.c2
-rw-r--r--target-cris/translate_v10.c5
-rw-r--r--target-i386/cpu.h32
-rw-r--r--target-i386/cpuid.c83
-rw-r--r--target-i386/helper.c6
-rw-r--r--target-i386/kvm.c415
-rw-r--r--target-i386/kvm_x86.h22
-rw-r--r--target-i386/op_helper.c6
-rw-r--r--target-i386/translate.c60
-rw-r--r--target-microblaze/cpu.h9
-rw-r--r--target-microblaze/helper.h16
-rw-r--r--target-microblaze/op_helper.c244
-rw-r--r--target-microblaze/translate.c200
-rw-r--r--target-mips/cpu.h25
-rw-r--r--target-mips/helper.c33
-rw-r--r--target-mips/helper.h1
-rw-r--r--target-mips/op_helper.c34
-rw-r--r--target-mips/translate.c44
-rw-r--r--target-mips/translate_init.c4
-rw-r--r--target-ppc/cpu.h8
-rw-r--r--target-ppc/helper.c132
-rw-r--r--target-ppc/helper.h1
-rw-r--r--target-ppc/kvm.c69
-rw-r--r--target-ppc/kvm_ppc.h14
-rw-r--r--target-ppc/op_helper.c140
-rw-r--r--target-ppc/translate.c6
-rw-r--r--target-ppc/translate_init.c10
-rw-r--r--tcg/ppc/tcg-target.h2
-rw-r--r--tcg/ppc64/tcg-target.c2
-rw-r--r--tcg/tcg.c2
-rw-r--r--tests/Makefile123
-rw-r--r--tests/cris/check_addo.c4
-rw-r--r--tests/cris/check_addoq.c2
-rw-r--r--tests/cris/check_settls1.c10
-rw-r--r--tests/cris/check_swap.c2
-rw-r--r--tests/runcom.c11
-rw-r--r--tests/sha1.c24
-rw-r--r--tests/test-i386.c5
-rw-r--r--tests/test_path.c13
-rw-r--r--trace-events83
-rwxr-xr-xtracetool377
-rw-r--r--ui/cocoa.m (renamed from cocoa.m)0
-rw-r--r--ui/curses.c (renamed from curses.c)9
-rw-r--r--ui/curses_keys.h (renamed from curses_keys.h)1
-rw-r--r--ui/d3des.c (renamed from d3des.c)0
-rw-r--r--ui/d3des.h (renamed from d3des.h)0
-rw-r--r--ui/keymaps.c (renamed from keymaps.c)0
-rw-r--r--ui/keymaps.h (renamed from keymaps.h)0
-rw-r--r--ui/qemu-spice.h41
-rw-r--r--ui/sdl.c (renamed from sdl.c)4
-rw-r--r--ui/sdl_keysym.h (renamed from sdl_keysym.h)0
-rw-r--r--ui/sdl_zoom.c (renamed from sdl_zoom.c)0
-rw-r--r--ui/sdl_zoom.h (renamed from sdl_zoom.h)0
-rw-r--r--ui/sdl_zoom_template.h (renamed from sdl_zoom_template.h)0
-rw-r--r--ui/spice-core.c189
-rw-r--r--ui/spice-display.c411
-rw-r--r--ui/spice-display.h69
-rw-r--r--ui/spice-input.c217
-rw-r--r--ui/vnc-auth-sasl.c (renamed from vnc-auth-sasl.c)0
-rw-r--r--ui/vnc-auth-sasl.h (renamed from vnc-auth-sasl.h)0
-rw-r--r--ui/vnc-auth-vencrypt.c (renamed from vnc-auth-vencrypt.c)0
-rw-r--r--ui/vnc-auth-vencrypt.h (renamed from vnc-auth-vencrypt.h)0
-rw-r--r--ui/vnc-enc-hextile-template.h (renamed from vnchextile.h)0
-rw-r--r--ui/vnc-enc-hextile.c (renamed from vnc-encoding-hextile.c)26
-rw-r--r--ui/vnc-enc-tight.c1717
-rw-r--r--ui/vnc-enc-tight.h (renamed from vnc-encoding-tight.h)21
-rw-r--r--ui/vnc-enc-zlib.c (renamed from vnc-encoding-zlib.c)34
-rw-r--r--ui/vnc-jobs-async.c331
-rw-r--r--ui/vnc-jobs-sync.c73
-rw-r--r--ui/vnc-jobs.h87
-rw-r--r--ui/vnc-palette.c136
-rw-r--r--ui/vnc-palette.h63
-rw-r--r--ui/vnc-tls.c (renamed from vnc-tls.c)0
-rw-r--r--ui/vnc-tls.h (renamed from vnc-tls.h)0
-rw-r--r--ui/vnc.c (renamed from vnc.c)175
-rw-r--r--ui/vnc.h (renamed from vnc.h)115
-rw-r--r--ui/vnc_keysym.h (renamed from vnc_keysym.h)0
-rw-r--r--ui/x_keymap.c (renamed from x_keymap.c)0
-rw-r--r--ui/x_keymap.h (renamed from x_keymap.h)0
-rw-r--r--version.rc28
-rw-r--r--vl.c200
-rw-r--r--vnc-encoding-tight.c959
391 files changed, 18296 insertions, 6010 deletions
diff --git a/.gitignore b/.gitignore
index a32b7c4bc0..a43e4d1d98 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,6 +2,9 @@ config-devices.*
config-all-devices.*
config-host.*
config-target.*
+trace.h
+trace.c
+*-timestamp
*-softmmu
*-darwin-user
*-linux-user
@@ -40,6 +43,7 @@ QMP/qmp-commands.txt
*.log
*.pdf
*.pg
+*.pyc
*.toc
*.tp
*.vr
@@ -53,3 +57,4 @@ pc-bios/optionrom/linuxboot.bin
pc-bios/optionrom/multiboot.bin
pc-bios/optionrom/multiboot.raw
.stgit-*
+cscope.*
diff --git a/CODING_STYLE b/CODING_STYLE
index 92036f34bc..2c8268ddc2 100644
--- a/CODING_STYLE
+++ b/CODING_STYLE
@@ -46,9 +46,6 @@ names are lower_case_with_underscores_ending_with_a_t, like the POSIX
uint64_t and family. Note that this last convention contradicts POSIX
and is therefore likely to be changed.
-Typedefs are used to eliminate the redundant 'struct' keyword. It is the
-QEMU coding style.
-
When wrapping standard library functions, use the prefix qemu_ to alert
readers that they are seeing a wrapped version; otherwise avoid this prefix.
diff --git a/HACKING b/HACKING
new file mode 100644
index 0000000000..6ba9d7e740
--- /dev/null
+++ b/HACKING
@@ -0,0 +1,125 @@
+1. Preprocessor
+
+For variadic macros, stick with this C99-like syntax:
+
+#define DPRINTF(fmt, ...) \
+ do { printf("IRQ: " fmt, ## __VA_ARGS__); } while (0)
+
+2. C types
+
+It should be common sense to use the right type, but we have collected
+a few useful guidelines here.
+
+2.1. Scalars
+
+If you're using "int" or "long", odds are good that there's a better type.
+If a variable is counting something, it should be declared with an
+unsigned type.
+
+If it's host memory-size related, size_t should be a good choice (use
+ssize_t only if required). Guest RAM memory offsets must use ram_addr_t,
+but only for RAM, it may not cover whole guest address space.
+
+If it's file-size related, use off_t.
+If it's file-offset related (i.e., signed), use off_t.
+If it's just counting small numbers use "unsigned int";
+(on all but oddball embedded systems, you can assume that that
+type is at least four bytes wide).
+
+In the event that you require a specific width, use a standard type
+like int32_t, uint32_t, uint64_t, etc. The specific types are
+mandatory for VMState fields.
+
+Don't use Linux kernel internal types like u32, __u32 or __le32.
+
+Use target_phys_addr_t for guest physical addresses except pcibus_t
+for PCI addresses. In addition, ram_addr_t is a QEMU internal address
+space that maps guest RAM physical addresses into an intermediate
+address space that can map to host virtual address spaces. Generally
+speaking, the size of guest memory can always fit into ram_addr_t but
+it would not be correct to store an actual guest physical address in a
+ram_addr_t.
+
+Use target_ulong (or abi_ulong) for CPU virtual addresses, however
+devices should not need to use target_ulong.
+
+Of course, take all of the above with a grain of salt. If you're about
+to use some system interface that requires a type like size_t, pid_t or
+off_t, use matching types for any corresponding variables.
+
+Also, if you try to use e.g., "unsigned int" as a type, and that
+conflicts with the signedness of a related variable, sometimes
+it's best just to use the *wrong* type, if "pulling the thread"
+and fixing all related variables would be too invasive.
+
+Finally, while using descriptive types is important, be careful not to
+go overboard. If whatever you're doing causes warnings, or requires
+casts, then reconsider or ask for help.
+
+2.2. Pointers
+
+Ensure that all of your pointers are "const-correct".
+Unless a pointer is used to modify the pointed-to storage,
+give it the "const" attribute. That way, the reader knows
+up-front that this is a read-only pointer. Perhaps more
+importantly, if we're diligent about this, when you see a non-const
+pointer, you're guaranteed that it is used to modify the storage
+it points to, or it is aliased to another pointer that is.
+
+2.3. Typedefs
+Typedefs are used to eliminate the redundant 'struct' keyword.
+
+2.4. Reserved namespaces in C and POSIX
+Underscore capital, double underscore, and underscore 't' suffixes should be
+avoided.
+
+3. Low level memory management
+
+Use of the malloc/free/realloc/calloc/valloc/memalign/posix_memalign
+APIs is not allowed in the QEMU codebase. Instead of these routines,
+use the replacement qemu_malloc/qemu_mallocz/qemu_realloc/qemu_free or
+qemu_vmalloc/qemu_memalign/qemu_vfree APIs.
+
+Please note that NULL check for the qemu_malloc result is redundant and
+that qemu_malloc() call with zero size is not allowed.
+
+Memory allocated by qemu_vmalloc or qemu_memalign must be freed with
+qemu_vfree, since breaking this will cause problems on Win32 and user
+emulators.
+
+4. String manipulation
+
+Do not use the strncpy function. According to the man page, it does
+*not* guarantee a NULL-terminated buffer, which makes it extremely dangerous
+to use. Instead, use functionally equivalent function:
+void pstrcpy(char *buf, int buf_size, const char *str)
+
+Don't use strcat because it can't check for buffer overflows, but:
+char *pstrcat(char *buf, int buf_size, const char *s)
+
+The same limitation exists with sprintf and vsprintf, so use snprintf and
+vsnprintf.
+
+QEMU provides other useful string functions:
+int strstart(const char *str, const char *val, const char **ptr)
+int stristart(const char *str, const char *val, const char **ptr)
+int qemu_strnlen(const char *s, int max_len)
+
+There are also replacement character processing macros for isxyz and toxyz,
+so instead of e.g. isalnum you should use qemu_isalnum.
+
+Because of the memory management rules, you must use qemu_strdup/qemu_strndup
+instead of plain strdup/strndup.
+
+5. Printf-style functions
+
+Whenever you add a new printf-style function, i.e., one with a format
+string argument and following "..." in its prototype, be sure to use
+gcc's printf attribute directive in the prototype.
+
+This makes it so gcc's -Wformat and -Wformat-security options can do
+their jobs and cross-check format strings with the number and types
+of arguments.
+
+Currently many functions in QEMU are not following this rule but
+patches to add the attribute would be very much appreciated.
diff --git a/MAINTAINERS b/MAINTAINERS
index 1dca65e7f7..e5165fbae8 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -14,7 +14,7 @@ x86 Fabrice Bellard
ARM Paul Brook
SPARC Blue Swirl
MIPS ?
-PowerPC ?
+PowerPC Alexander Graf
M68K Paul Brook
SH4 ?
CRIS Edgar E. Iglesias
@@ -48,9 +48,9 @@ MIPS
mips_mipssim.c ?
PowerPC
ppc_prep.c ?
- ppc_oldworld.c Fabrice Bellard
- ppc_chrp.c Fabrice Bellard
- ppc405_boards.c ?
+ ppc_oldworld.c Alexander Graf
+ ppc_newworld.c Alexander Graf
+ ppc405_boards.c Alexander Graf
M86K
mcf5208.c Paul Brook
an5206.c Paul Brook
diff --git a/Makefile b/Makefile
index 6fc1b2c89b..cf8f48a476 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
# Makefile for QEMU.
-GENERATED_HEADERS = config-host.h
+GENERATED_HEADERS = config-host.h trace.h qemu-options.def
ifneq ($(wildcard config-host.mak),)
# Put the all: rule here so that config-host.mak can contain dependencies.
@@ -43,9 +43,11 @@ config-all-devices.mak: $(SUBDIR_DEVICES_MAK)
%/config-devices.mak: default-configs/%.mak
$(call quiet-command,cat $< > $@.tmp, " GEN $@")
@if test -f $@; then \
- if cmp -s $@.old $@ || cmp -s $@ $@.tmp; then \
- mv $@.tmp $@; \
- cp -p $@ $@.old; \
+ if cmp -s $@.old $@; then \
+ if ! cmp -s $@ $@.tmp; then \
+ mv $@.tmp $@; \
+ cp -p $@ $@.old; \
+ fi; \
else \
if test -f $@.old; then \
echo "WARNING: $@ (user modified) out of date.";\
@@ -69,6 +71,8 @@ build-all: $(DOCS) $(TOOLS) recurse-all
config-host.h: config-host.h-timestamp
config-host.h-timestamp: config-host.mak
+qemu-options.def: qemu-options.hx
+ $(call quiet-command,sh $(SRC_PATH)/hxtool -h < $< > $@," GEN $@")
SUBDIR_RULES=$(patsubst %,subdir-%, $(TARGET_DIRS))
@@ -80,9 +84,9 @@ include $(SRC_PATH)/Makefile.objs
endif
$(common-obj-y): $(GENERATED_HEADERS)
-$(filter %-softmmu,$(SUBDIR_RULES)): $(common-obj-y) subdir-libdis
+$(filter %-softmmu,$(SUBDIR_RULES)): $(trace-obj-y) $(common-obj-y) subdir-libdis
-$(filter %-user,$(SUBDIR_RULES)): $(GENERATED_HEADERS) subdir-libdis-user subdir-libuser
+$(filter %-user,$(SUBDIR_RULES)): $(GENERATED_HEADERS) $(trace-obj-y) subdir-libdis-user subdir-libuser
ROMSUBDIR_RULES=$(patsubst %,romsubdir-%, $(ROMS))
romsubdir-%:
@@ -96,52 +100,42 @@ audio/audio.o audio/fmodaudio.o: QEMU_CFLAGS += $(FMOD_CFLAGS)
QEMU_CFLAGS+=$(CURL_CFLAGS)
-cocoa.o: cocoa.m
-
-keymaps.o: keymaps.c keymaps.h
-
-sdl_zoom.o: sdl_zoom.c sdl_zoom.h sdl_zoom_template.h
-
-sdl.o: sdl.c keymaps.h sdl_keysym.h sdl_zoom.h
-
-sdl.o audio/sdlaudio.o sdl_zoom.o baum.o: QEMU_CFLAGS += $(SDL_CFLAGS)
-
-acl.o: acl.h acl.c
-
-vnc.h: vnc-tls.h vnc-auth-vencrypt.h vnc-auth-sasl.h keymaps.h
+ui/cocoa.o: ui/cocoa.m
-vnc.o: vnc.c vnc.h vnc_keysym.h vnchextile.h d3des.c d3des.h acl.h
+ui/sdl.o audio/sdlaudio.o ui/sdl_zoom.o baum.o: QEMU_CFLAGS += $(SDL_CFLAGS)
-vnc.o: QEMU_CFLAGS += $(VNC_TLS_CFLAGS)
+ui/vnc.o: QEMU_CFLAGS += $(VNC_TLS_CFLAGS)
-vnc-tls.o: vnc-tls.c vnc.h
-
-vnc-auth-vencrypt.o: vnc-auth-vencrypt.c vnc.h
-
-vnc-auth-sasl.o: vnc-auth-sasl.c vnc.h
+bt-host.o: QEMU_CFLAGS += $(BLUEZ_CFLAGS)
-vnc-encoding-zlib.o: vnc-encoding-zlib.c vnc.h
+trace.h: trace.h-timestamp
+trace.h-timestamp: $(SRC_PATH)/trace-events config-host.mak
+ $(call quiet-command,sh $(SRC_PATH)/tracetool --$(TRACE_BACKEND) -h < $< > $@," GEN trace.h")
+ @cmp -s $@ trace.h || cp $@ trace.h
-vnc-encoding-hextile.o: vnc-encoding-hextile.c vnc.h
+trace.c: trace.c-timestamp
+trace.c-timestamp: $(SRC_PATH)/trace-events config-host.mak
+ $(call quiet-command,sh $(SRC_PATH)/tracetool --$(TRACE_BACKEND) -c < $< > $@," GEN trace.c")
+ @cmp -s $@ trace.c || cp $@ trace.c
-vnc-encoding-tight.o: vnc-encoding-tight.c vnc.h vnc-encoding-tight.h
+trace.o: trace.c $(GENERATED_HEADERS)
-curses.o: curses.c keymaps.h curses_keys.h
+simpletrace.o: simpletrace.c $(GENERATED_HEADERS)
-bt-host.o: QEMU_CFLAGS += $(BLUEZ_CFLAGS)
-
-iov.o: iov.c iov.h
+version.o: $(SRC_PATH)/version.rc config-host.mak
+ $(call quiet-command,$(WINDRES) -I. -o $@ $<," RC $(TARGET_DIR)$@")
+version-obj-$(CONFIG_WIN32) += version.o
######################################################################
qemu-img.o: qemu-img-cmds.h
qemu-img.o qemu-tool.o qemu-nbd.o qemu-io.o: $(GENERATED_HEADERS)
-qemu-img$(EXESUF): qemu-img.o qemu-tool.o qemu-error.o $(block-obj-y) $(qobject-obj-y)
+qemu-img$(EXESUF): qemu-img.o qemu-tool.o qemu-error.o $(trace-obj-y) $(block-obj-y) $(qobject-obj-y) $(version-obj-y) qemu-timer-common.o
-qemu-nbd$(EXESUF): qemu-nbd.o qemu-tool.o qemu-error.o $(block-obj-y) $(qobject-obj-y)
+qemu-nbd$(EXESUF): qemu-nbd.o qemu-tool.o qemu-error.o $(trace-obj-y) $(block-obj-y) $(qobject-obj-y) $(version-obj-y) qemu-timer-common.o
-qemu-io$(EXESUF): qemu-io.o cmd.o qemu-tool.o qemu-error.o $(block-obj-y) $(qobject-obj-y)
+qemu-io$(EXESUF): qemu-io.o cmd.o qemu-tool.o qemu-error.o $(trace-obj-y) $(block-obj-y) $(qobject-obj-y) $(version-obj-y) qemu-timer-common.o
qemu-img-cmds.h: $(SRC_PATH)/qemu-img-cmds.hx
$(call quiet-command,sh $(SRC_PATH)/hxtool -h < $< > $@," GEN $@")
@@ -158,17 +152,19 @@ check-qjson: check-qjson.o qfloat.o qint.o qdict.o qstring.o qlist.o qbool.o qjs
clean:
# avoid old build problems by removing potentially incorrect old files
rm -f config.mak op-i386.h opc-i386.h gen-op-i386.h op-arm.h opc-arm.h gen-op-arm.h
+ rm -f qemu-options.def
rm -f *.o *.d *.a $(TOOLS) TAGS cscope.* *.pod *~ */*~
- rm -f slirp/*.o slirp/*.d audio/*.o audio/*.d block/*.o block/*.d net/*.o net/*.d fsdev/*.o fsdev/*.d
+ rm -f slirp/*.o slirp/*.d audio/*.o audio/*.d block/*.o block/*.d net/*.o net/*.d fsdev/*.o fsdev/*.d ui/*.o ui/*.d
rm -f qemu-img-cmds.h
+ rm -f trace.c trace.h trace.c-timestamp trace.h-timestamp
$(MAKE) -C tests clean
for d in $(ALL_SUBDIRS) libhw32 libhw64 libuser libdis libdis-user; do \
if test -d $$d; then $(MAKE) -C $$d $@ || exit 1; fi; \
+ rm -f $$d/qemu-options.def; \
done
distclean: clean
rm -f config-host.mak config-host.h* config-host.ld $(DOCS) qemu-options.texi qemu-img-cmds.texi qemu-monitor.texi
- rm -f qemu-options.def
rm -f config-all-devices.mak
rm -f roms/seabios/config.mak roms/vgabios/config.mak
rm -f qemu-doc.info qemu-doc.aux qemu-doc.cp qemu-doc.dvi qemu-doc.fn qemu-doc.info qemu-doc.ky qemu-doc.log qemu-doc.pdf qemu-doc.pg qemu-doc.toc qemu-doc.tp qemu-doc.vr
@@ -183,7 +179,7 @@ common de-ch es fo fr-ca hu ja mk nl-be pt sl tr
ifdef INSTALL_BLOBS
BLOBS=bios.bin vgabios.bin vgabios-cirrus.bin ppc_rom.bin \
-video.x openbios-sparc32 openbios-sparc64 openbios-ppc \
+openbios-sparc32 openbios-sparc64 openbios-ppc \
gpxe-eepro100-80861209.rom \
gpxe-eepro100-80861229.rom \
pxe-e1000.bin \
@@ -262,10 +258,10 @@ TEXIFLAG=$(if $(V),,--quiet)
qemu-options.texi: $(SRC_PATH)/qemu-options.hx
$(call quiet-command,sh $(SRC_PATH)/hxtool -t < $< > $@," GEN $@")
-qemu-monitor.texi: $(SRC_PATH)/qemu-monitor.hx
+qemu-monitor.texi: $(SRC_PATH)/hmp-commands.hx
$(call quiet-command,sh $(SRC_PATH)/hxtool -t < $< > $@," GEN $@")
-QMP/qmp-commands.txt: $(SRC_PATH)/qemu-monitor.hx
+QMP/qmp-commands.txt: $(SRC_PATH)/qmp-commands.hx
$(call quiet-command,sh $(SRC_PATH)/hxtool -q < $< > $@," GEN $@")
qemu-img-cmds.texi: $(SRC_PATH)/qemu-img-cmds.hx
@@ -330,7 +326,6 @@ tarbin:
$(datadir)/vgabios.bin \
$(datadir)/vgabios-cirrus.bin \
$(datadir)/ppc_rom.bin \
- $(datadir)/video.x \
$(datadir)/openbios-sparc32 \
$(datadir)/openbios-sparc64 \
$(datadir)/openbios-ppc \
@@ -345,4 +340,4 @@ tarbin:
$(mandir)/man8/qemu-nbd.8
# Include automatically generated dependency files
--include $(wildcard *.d audio/*.d slirp/*.d block/*.d net/*.d)
+-include $(wildcard *.d audio/*.d slirp/*.d block/*.d net/*.d ui/*.d)
diff --git a/Makefile.objs b/Makefile.objs
index 138e545a09..702569f673 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -14,7 +14,7 @@ block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o
block-nested-y += raw.o cow.o qcow.o vdi.o vmdk.o cloop.o dmg.o bochs.o vpc.o vvfat.o
block-nested-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o
-block-nested-y += parallels.o nbd.o blkdebug.o sheepdog.o
+block-nested-y += parallels.o nbd.o blkdebug.o sheepdog.o blkverify.o
block-nested-$(CONFIG_WIN32) += raw-win32.o
block-nested-$(CONFIG_POSIX) += raw-posix.o
block-nested-$(CONFIG_CURL) += curl.o
@@ -31,6 +31,7 @@ net-nested-$(CONFIG_WIN32) += tap-win32.o
net-nested-$(CONFIG_BSD) += tap-bsd.o
net-nested-$(CONFIG_SOLARIS) += tap-solaris.o
net-nested-$(CONFIG_AIX) += tap-aix.o
+net-nested-$(CONFIG_HAIKU) += tap-haiku.o
net-nested-$(CONFIG_SLIRP) += slirp.o
net-nested-$(CONFIG_VDE) += vde.o
net-obj-y += $(addprefix net/, $(net-nested-y))
@@ -83,9 +84,13 @@ common-obj-y += qemu-char.o savevm.o #aio.o
common-obj-y += msmouse.o ps2.o
common-obj-y += qdev.o qdev-properties.o
common-obj-y += block-migration.o
+common-obj-y += pflib.o
common-obj-$(CONFIG_BRLAPI) += baum.o
common-obj-$(CONFIG_POSIX) += migration-exec.o migration-unix.o migration-fd.o
+common-obj-$(CONFIG_WIN32) += version.o
+
+common-obj-$(CONFIG_SPICE) += ui/spice-core.o ui/spice-input.o ui/spice-display.o
audio-obj-y = audio.o noaudio.o wavaudio.o mixeng.o
audio-obj-$(CONFIG_SDL) += sdlaudio.o
@@ -102,19 +107,27 @@ audio-obj-$(CONFIG_AUDIO_WIN_INT) += audio_win_int.o
audio-obj-y += wavcapture.o
common-obj-y += $(addprefix audio/, $(audio-obj-y))
-common-obj-y += keymaps.o
-common-obj-$(CONFIG_SDL) += sdl.o sdl_zoom.o x_keymap.o
-common-obj-$(CONFIG_CURSES) += curses.o
-common-obj-y += vnc.o acl.o d3des.o
-common-obj-y += vnc-encoding-zlib.o vnc-encoding-hextile.o
-common-obj-y += vnc-encoding-tight.o
-common-obj-y += iov.o
-common-obj-$(CONFIG_VNC_TLS) += vnc-tls.o vnc-auth-vencrypt.o
-common-obj-$(CONFIG_VNC_SASL) += vnc-auth-sasl.o
-common-obj-$(CONFIG_COCOA) += cocoa.o
-common-obj-$(CONFIG_IOTHREAD) += qemu-thread.o
+ui-obj-y += keymaps.o
+ui-obj-$(CONFIG_SDL) += sdl.o sdl_zoom.o x_keymap.o
+ui-obj-$(CONFIG_CURSES) += curses.o
+ui-obj-y += vnc.o d3des.o
+ui-obj-y += vnc-enc-zlib.o vnc-enc-hextile.o
+ui-obj-y += vnc-enc-tight.o vnc-palette.o
+ui-obj-$(CONFIG_VNC_TLS) += vnc-tls.o vnc-auth-vencrypt.o
+ui-obj-$(CONFIG_VNC_SASL) += vnc-auth-sasl.o
+ui-obj-$(CONFIG_COCOA) += cocoa.o
+ifdef CONFIG_VNC_THREAD
+ui-obj-y += vnc-jobs-async.o
+else
+ui-obj-y += vnc-jobs-sync.o
+endif
+common-obj-y += $(addprefix ui/, $(ui-obj-y))
+
+common-obj-y += iov.o acl.o
+common-obj-$(CONFIG_THREAD) += qemu-thread.o
+common-obj-$(CONFIG_IOTHREAD) += compatfd.o
common-obj-y += notify.o event_notifier.o
-common-obj-y += qemu-timer.o
+common-obj-y += qemu-timer.o qemu-timer-common.o
slirp-obj-y = cksum.o if.o ip_icmp.o ip_input.o ip_output.o
slirp-obj-y += slirp.o mbuf.o misc.o sbuf.o socket.o tcp_input.o tcp_output.o
@@ -239,7 +252,8 @@ sound-obj-$(CONFIG_CS4231A) += cs4231a.o
adlib.o fmopl.o: QEMU_CFLAGS += -DBUILD_Y8950=0
hw-obj-$(CONFIG_SOUND) += $(sound-obj-y)
-hw-obj-$(CONFIG_VIRTFS) += virtio-9p-debug.o virtio-9p-local.o
+hw-obj-$(CONFIG_VIRTFS) += virtio-9p-debug.o virtio-9p-local.o virtio-9p-xattr.o
+hw-obj-$(CONFIG_VIRTFS) += virtio-9p-xattr-user.o virtio-9p-posix-acl.o
######################################################################
# libdis
@@ -260,14 +274,16 @@ libdis-$(CONFIG_S390_DIS) += s390-dis.o
libdis-$(CONFIG_SH4_DIS) += sh4-dis.o
libdis-$(CONFIG_SPARC_DIS) += sparc-dis.o
-vl.o: QEMU_CFLAGS+=$(GPROF_CFLAGS)
+######################################################################
+# trace
-vl.o: QEMU_CFLAGS+=$(SDL_CFLAGS)
+trace-obj-y = trace.o
+ifeq ($(TRACE_BACKEND),simple)
+trace-obj-y += simpletrace.o
+user-obj-y += qemu-timer-common.o
+endif
-vl.o: qemu-options.def
-os-posix.o: qemu-options.def
-os-win32.o: qemu-options.def
+vl.o: QEMU_CFLAGS+=$(GPROF_CFLAGS)
-qemu-options.def: $(SRC_PATH)/qemu-options.hx
- $(call quiet-command,sh $(SRC_PATH)/hxtool -h < $< > $@," GEN $(TARGET_DIR)$@")
+vl.o: QEMU_CFLAGS+=$(SDL_CFLAGS)
diff --git a/Makefile.target b/Makefile.target
index 3ef4666d2c..c48cbcc7f3 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -31,7 +31,9 @@ endif
PROGS=$(QEMU_PROG)
+ifndef CONFIG_HAIKU
LIBS+=-lm
+endif
kvm.o kvm-all.o vhost.o vhost_net.o: QEMU_CFLAGS+=$(KVM_CFLAGS)
@@ -177,6 +179,8 @@ LIBS+=-lz
QEMU_CFLAGS += $(VNC_TLS_CFLAGS)
QEMU_CFLAGS += $(VNC_SASL_CFLAGS)
+QEMU_CFLAGS += $(VNC_JPEG_CFLAGS)
+QEMU_CFLAGS += $(VNC_PNG_CFLAGS)
# xen backend driver support
obj-$(CONFIG_XEN) += xen_machine_pv.o xen_domainbuild.o
@@ -188,6 +192,9 @@ obj-$(CONFIG_USB_OHCI) += usb-ohci.o
obj-y += rtl8139.o
obj-y += e1000.o
+# Inter-VM PCI shared memory
+obj-$(CONFIG_KVM) += ivshmem.o
+
# Hardware support
obj-i386-y += vga.o
obj-i386-y += mc146818rtc.o i8259.o pc.o
@@ -212,9 +219,17 @@ obj-ppc-y += ppc4xx_devs.o ppc4xx_pci.o ppc405_uc.o ppc405_boards.o
obj-ppc-y += ppc440.o ppc440_bamboo.o
# PowerPC E500 boards
obj-ppc-y += ppce500_mpc8544ds.o
+# PowerPC 440 Xilinx ML507 reference board.
+obj-ppc-y += virtex_ml507.o
obj-ppc-$(CONFIG_KVM) += kvm_ppc.o
obj-ppc-$(CONFIG_FDT) += device_tree.o
+# Xilinx PPC peripherals
+obj-ppc-y += xilinx_intc.o
+obj-ppc-y += xilinx_timer.o
+obj-ppc-y += xilinx_uartlite.o
+obj-ppc-y += xilinx_ethlite.o
+
obj-mips-y = mips_r4k.o mips_jazz.o mips_malta.o mips_mipssim.o
obj-mips-y += mips_addr.o mips_timer.o mips_int.o
obj-mips-y += vga.o i8259.o
@@ -294,7 +309,7 @@ obj-alpha-y = alpha_palcode.o
main.o: QEMU_CFLAGS+=$(GPROF_CFLAGS)
-monitor.o: qemu-monitor.h
+monitor.o: hmp-commands.h qmp-commands.h
$(obj-y) $(obj-$(TARGET_BASE_ARCH)-y): $(GENERATED_HEADERS)
@@ -305,6 +320,7 @@ obj-y += $(addprefix $(HWDIR)/, $(hw-obj-y))
endif # CONFIG_SOFTMMU
+obj-y += $(addprefix ../, $(trace-obj-y))
obj-$(CONFIG_GDBSTUB_XML) += gdbstub-xml.o
$(QEMU_PROG): $(obj-y) $(obj-$(TARGET_BASE_ARCH)-y)
@@ -314,17 +330,23 @@ $(QEMU_PROG): $(obj-y) $(obj-$(TARGET_BASE_ARCH)-y)
gdbstub-xml.c: $(TARGET_XML_FILES) $(SRC_PATH)/feature_to_c.sh
$(call quiet-command,rm -f $@ && $(SHELL) $(SRC_PATH)/feature_to_c.sh $@ $(TARGET_XML_FILES)," GEN $(TARGET_DIR)$@")
-qemu-monitor.h: $(SRC_PATH)/qemu-monitor.hx
+hmp-commands.h: $(SRC_PATH)/hmp-commands.hx
+ $(call quiet-command,sh $(SRC_PATH)/hxtool -h < $< > $@," GEN $(TARGET_DIR)$@")
+
+qmp-commands.h: $(SRC_PATH)/qmp-commands.hx
$(call quiet-command,sh $(SRC_PATH)/hxtool -h < $< > $@," GEN $(TARGET_DIR)$@")
clean:
rm -f *.o *.a *~ $(PROGS) nwfpe/*.o fpu/*.o
rm -f *.d */*.d tcg/*.o ide/*.o
- rm -f qemu-monitor.h gdbstub-xml.c
+ rm -f hmp-commands.h qmp-commands.h gdbstub-xml.c
install: all
ifneq ($(PROGS),)
- $(INSTALL) -m 755 $(STRIP_OPT) $(PROGS) "$(DESTDIR)$(bindir)"
+ $(INSTALL) -m 755 $(PROGS) "$(DESTDIR)$(bindir)"
+ifneq ($(STRIP),)
+ $(STRIP) $(patsubst %,"$(DESTDIR)$(bindir)/%",$(PROGS))
+endif
endif
# Include automatically generated dependency files
diff --git a/QMP/README b/QMP/README
index 35a80c73e4..80503f2d7a 100644
--- a/QMP/README
+++ b/QMP/README
@@ -7,60 +7,85 @@ Introduction
The QEMU Monitor Protocol (QMP) allows applications to communicate with
QEMU's Monitor.
-QMP is JSON[1] based and has the following features:
+QMP is JSON[1] based and currently has the following features:
- Lightweight, text-based, easy to parse data format
-- Asynchronous events support
-- Stability
+- Asynchronous messages support (ie. events)
+- Capabilities Negotiation
-For more information, please, refer to the following files:
+For detailed information on QMP's usage, please, refer to the following files:
o qmp-spec.txt QEMU Monitor Protocol current specification
-o qmp-commands.txt QMP supported commands
+o qmp-commands.txt QMP supported commands (auto-generated at build-time)
o qmp-events.txt List of available asynchronous events
There are also two simple Python scripts available:
-o qmp-shell A shell
-o vm-info Show some information about the Virtual Machine
+o qmp-shell A shell
+o vm-info Show some information about the Virtual Machine
+
+IMPORTANT: It's strongly recommended to read the 'Stability Considerations'
+section in the qmp-commands.txt file before making any serious use of QMP.
+
[1] http://www.json.org
Usage
-----
-To enable QMP, QEMU has to be started in "control mode". There are
-two ways of doing this, the simplest one is using the the '-qmp'
-command-line option.
+To enable QMP, you need a QEMU monitor instance in "control mode". There are
+two ways of doing this.
+
+The simplest one is using the '-qmp' command-line option. The following
+example makes QMP available on localhost port 4444:
-For example:
+ $ qemu [...] -qmp tcp:localhost:4444,server
-$ qemu [...] -qmp tcp:localhost:4444,server
+However, in order to have more complex combinations, like multiple monitors,
+the '-mon' command-line option should be used along with the '-chardev' one.
+For instance, the following example creates one user monitor on stdio and one
+QMP monitor on localhost port 4444.
-Will start QEMU in control mode, waiting for a client TCP connection
-on localhost port 4444.
+ $ qemu [...] -chardev stdio,id=mon0 -mon chardev=mon0,mode=readline \
+ -chardev socket,id=mon1,host=localhost,port=4444,server \
+ -mon chardev=mon1,mode=control
-It is also possible to use the '-mon' command-line option to have
-more complex combinations. Please, refer to the QEMU's manpage for
-more information.
+Please, refer to QEMU's manpage for more information.
Simple Testing
--------------
-To manually test QMP one can connect with telnet and issue commands:
+To manually test QMP one can connect with telnet and issue commands by hand:
$ telnet localhost 4444
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
-{"QMP": {"version": {"qemu": "0.12.50", "package": ""}, "capabilities": []}}
+{"QMP": {"version": {"qemu": {"micro": 50, "minor": 13, "major": 0}, "package": ""}, "capabilities": []}}
{ "execute": "qmp_capabilities" }
{"return": {}}
{ "execute": "query-version" }
-{"return": {"qemu": "0.12.50", "package": ""}}
+{"return": {"qemu": {"micro": 50, "minor": 13, "major": 0}, "package": ""}}
+
+Development Process
+-------------------
+
+When changing QMP's interface (by adding new commands, events or modifying
+existing ones) it's mandatory to update the relevant documentation, which is
+one (or more) of the files listed in the 'Introduction' section*.
+
+Also, it's strongly recommended to send the documentation patch first, before
+doing any code change. This is so because:
+
+ 1. Avoids the code dictating the interface
+
+ 2. Review can improve your interface. Letting that happen before
+ you implement it can save you work.
+
+* The qmp-commands.txt file is generated from the qmp-commands.hx one, which
+ is the file that should be edited.
-Contact
--------
+Homepage
+--------
-http://www.linux-kvm.org/page/MonitorProtocol
-Luiz Fernando N. Capitulino <lcapitulino@redhat.com>
+http://wiki.qemu.org/QMP
diff --git a/VERSION b/VERSION
index d9e30b8d37..d82f77b7c9 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-0.12.50
+0.13.50
diff --git a/arch_init.c b/arch_init.c
index 47bb4b2d8f..4caadd0c0a 100644
--- a/arch_init.c
+++ b/arch_init.c
@@ -82,12 +82,12 @@ const uint32_t arch_type = QEMU_ARCH;
/***********************************************************/
/* ram save/restore */
-#define RAM_SAVE_FLAG_FULL 0x01 /* Obsolete, not used anymore */
-#define RAM_SAVE_FLAG_COMPRESS 0x02
-#define RAM_SAVE_FLAG_MEM_SIZE 0x04
-#define RAM_SAVE_FLAG_PAGE 0x08
-#define RAM_SAVE_FLAG_EOS 0x10
-#define RAM_SAVE_FLAG_CONTINUE 0x20
+#define RAM_SAVE_FLAG_FULL 0x01 /* Obsolete, not used anymore */
+#define RAM_SAVE_FLAG_COMPRESS 0x02
+#define RAM_SAVE_FLAG_MEM_SIZE 0x04
+#define RAM_SAVE_FLAG_PAGE 0x08
+#define RAM_SAVE_FLAG_EOS 0x10
+#define RAM_SAVE_FLAG_CONTINUE 0x20
static int is_dup_page(uint8_t *page, uint8_t ch)
{
@@ -104,10 +104,11 @@ static int is_dup_page(uint8_t *page, uint8_t ch)
return 1;
}
+static RAMBlock *last_block;
+static ram_addr_t last_offset;
+
static int ram_save_block(QEMUFile *f)
{
- static RAMBlock *last_block = NULL;
- static ram_addr_t last_offset = 0;
RAMBlock *block = last_block;
ram_addr_t offset = last_offset;
ram_addr_t current_addr;
@@ -231,6 +232,8 @@ int ram_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque)
if (stage == 1) {
RAMBlock *block;
bytes_transferred = 0;
+ last_block = NULL;
+ last_offset = 0;
/* Make sure all dirty bits are set */
QLIST_FOREACH(block, &ram_list.blocks, next) {
@@ -387,13 +390,16 @@ int ram_load(QEMUFile *f, void *opaque, int version_id)
host = qemu_get_ram_ptr(addr);
else
host = host_from_stream_offset(f, addr, flags);
+ if (!host) {
+ return -EINVAL;
+ }
ch = qemu_get_byte(f);
memset(host, ch, TARGET_PAGE_SIZE);
#ifndef _WIN32
if (ch == 0 &&
(!kvm_enabled() || kvm_has_sync_mmu())) {
- madvise(host, TARGET_PAGE_SIZE, MADV_DONTNEED);
+ qemu_madvise(host, TARGET_PAGE_SIZE, QEMU_MADV_DONTNEED);
}
#endif
} else if (flags & RAM_SAVE_FLAG_PAGE) {
diff --git a/audio/alsaaudio.c b/audio/alsaaudio.c
index f0171f9842..07412030c2 100644
--- a/audio/alsaaudio.c
+++ b/audio/alsaaudio.c
@@ -843,11 +843,15 @@ static int alsa_init_out (HWVoiceOut *hw, struct audsettings *as)
return 0;
}
-static int alsa_voice_ctl (snd_pcm_t *handle, const char *typ, int pause)
+#define VOICE_CTL_PAUSE 0
+#define VOICE_CTL_PREPARE 1
+#define VOICE_CTL_START 2
+
+static int alsa_voice_ctl (snd_pcm_t *handle, const char *typ, int ctl)
{
int err;
- if (pause) {
+ if (ctl == VOICE_CTL_PAUSE) {
err = snd_pcm_drop (handle);
if (err < 0) {
alsa_logerr (err, "Could not stop %s\n", typ);
@@ -860,6 +864,13 @@ static int alsa_voice_ctl (snd_pcm_t *handle, const char *typ, int pause)
alsa_logerr (err, "Could not prepare handle for %s\n", typ);
return -1;
}
+ if (ctl == VOICE_CTL_START) {
+ err = snd_pcm_start(handle);
+ if (err < 0) {
+ alsa_logerr (err, "Could not start handle for %s\n", typ);
+ return -1;
+ }
+ }
}
return 0;
@@ -884,12 +895,16 @@ static int alsa_ctl_out (HWVoiceOut *hw, int cmd, ...)
poll_mode = 0;
}
hw->poll_mode = poll_mode;
- return alsa_voice_ctl (alsa->handle, "playback", 0);
+ return alsa_voice_ctl (alsa->handle, "playback", VOICE_CTL_PREPARE);
}
case VOICE_DISABLE:
ldebug ("disabling voice\n");
- return alsa_voice_ctl (alsa->handle, "playback", 1);
+ if (hw->poll_mode) {
+ hw->poll_mode = 0;
+ alsa_fini_poll (&alsa->pollhlp);
+ }
+ return alsa_voice_ctl (alsa->handle, "playback", VOICE_CTL_PAUSE);
}
return -1;
@@ -1102,7 +1117,7 @@ static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...)
}
hw->poll_mode = poll_mode;
- return alsa_voice_ctl (alsa->handle, "capture", 0);
+ return alsa_voice_ctl (alsa->handle, "capture", VOICE_CTL_START);
}
case VOICE_DISABLE:
@@ -1111,7 +1126,7 @@ static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...)
hw->poll_mode = 0;
alsa_fini_poll (&alsa->pollhlp);
}
- return alsa_voice_ctl (alsa->handle, "capture", 1);
+ return alsa_voice_ctl (alsa->handle, "capture", VOICE_CTL_PAUSE);
}
return -1;
diff --git a/audio/audio.h b/audio/audio.h
index 454ade260e..a70fda97e3 100644
--- a/audio/audio.h
+++ b/audio/audio.h
@@ -86,12 +86,8 @@ typedef struct QEMUAudioTimeStamp {
uint64_t old_ts;
} QEMUAudioTimeStamp;
-void AUD_vlog (const char *cap, const char *fmt, va_list ap);
-void AUD_log (const char *cap, const char *fmt, ...)
-#ifdef __GNUC__
- __attribute__ ((__format__ (__printf__, 2, 3)))
-#endif
- ;
+void AUD_vlog (const char *cap, const char *fmt, va_list ap) GCC_FMT_ATTR(2, 0);
+void AUD_log (const char *cap, const char *fmt, ...) GCC_FMT_ATTR(2, 3);
void AUD_help (void);
void AUD_register_card (const char *name, QEMUSoundCard *card);
diff --git a/audio/audio_int.h b/audio/audio_int.h
index 06e313f836..d8560b662b 100644
--- a/audio/audio_int.h
+++ b/audio/audio_int.h
@@ -236,14 +236,6 @@ static inline int audio_ring_dist (int dst, int src, int len)
return (dst >= src) ? (dst - src) : (len - src + dst);
}
-#if defined __GNUC__
-#define GCC_ATTR __attribute__ ((__unused__, __format__ (__printf__, 1, 2)))
-#define GCC_FMT_ATTR(n, m) __attribute__ ((__format__ (__printf__, n, m)))
-#else
-#define GCC_ATTR /**/
-#define GCC_FMT_ATTR(n, m)
-#endif
-
static void GCC_ATTR dolog (const char *fmt, ...)
{
va_list ap;
diff --git a/audio/audio_pt_int.c b/audio/audio_pt_int.c
index e889a983b1..f15cc7015b 100644
--- a/audio/audio_pt_int.c
+++ b/audio/audio_pt_int.c
@@ -6,6 +6,8 @@
#include "audio_int.h"
#include "audio_pt_int.h"
+#include <signal.h>
+
static void logerr (struct audio_pt *pt, int err, const char *fmt, ...)
{
va_list ap;
@@ -23,9 +25,16 @@ int audio_pt_init (struct audio_pt *p, void *(*func) (void *),
{
int err, err2;
const char *efunc;
+ sigset_t set, old_set;
p->drv = drv;
+ err = sigfillset (&set);
+ if (err) {
+ logerr (p, errno, "%s(%s): sigfillset failed", cap, AUDIO_FUNC);
+ return -1;
+ }
+
err = pthread_mutex_init (&p->mutex, NULL);
if (err) {
efunc = "pthread_mutex_init";
@@ -38,7 +47,23 @@ int audio_pt_init (struct audio_pt *p, void *(*func) (void *),
goto err1;
}
+ err = pthread_sigmask (SIG_BLOCK, &set, &old_set);
+ if (err) {
+ efunc = "pthread_sigmask";
+ goto err2;
+ }
+
err = pthread_create (&p->thread, NULL, func, opaque);
+
+ err2 = pthread_sigmask (SIG_SETMASK, &old_set, NULL);
+ if (err2) {
+ logerr (p, err2, "%s(%s): pthread_sigmask (restore) failed",
+ cap, AUDIO_FUNC);
+ /* We have failed to restore original signal mask, all bets are off,
+ so terminate the process */
+ exit (EXIT_FAILURE);
+ }
+
if (err) {
efunc = "pthread_create";
goto err2;
diff --git a/audio/audio_template.h b/audio/audio_template.h
index 2f5224ba29..fd4469e638 100644
--- a/audio/audio_template.h
+++ b/audio/audio_template.h
@@ -108,11 +108,7 @@ static int glue (audio_pcm_sw_alloc_resources_, TYPE) (SW *sw)
{
int samples;
-#ifdef DAC
- samples = sw->hw->samples;
-#else
samples = ((int64_t) sw->hw->samples << 32) / sw->ratio;
-#endif
sw->buf = audio_calloc (AUDIO_FUNC, samples, sizeof (struct st_sample));
if (!sw->buf) {
diff --git a/audio/esdaudio.c b/audio/esdaudio.c
index 79142d1706..9a1f2f8617 100644
--- a/audio/esdaudio.c
+++ b/audio/esdaudio.c
@@ -24,7 +24,6 @@
#include <esd.h>
#include "qemu-common.h"
#include "audio.h"
-#include <signal.h>
#define AUDIO_CAP "esd"
#include "audio_int.h"
@@ -190,10 +189,6 @@ static int qesd_init_out (HWVoiceOut *hw, struct audsettings *as)
ESDVoiceOut *esd = (ESDVoiceOut *) hw;
struct audsettings obt_as = *as;
int esdfmt = ESD_STREAM | ESD_PLAY;
- int err;
- sigset_t set, old_set;
-
- sigfillset (&set);
esdfmt |= (as->nchannels == 2) ? ESD_STEREO : ESD_MONO;
switch (as->fmt) {
@@ -231,43 +226,25 @@ static int qesd_init_out (HWVoiceOut *hw, struct audsettings *as)
return -1;
}
- esd->fd = -1;
- err = pthread_sigmask (SIG_BLOCK, &set, &old_set);
- if (err) {
- qesd_logerr (err, "pthread_sigmask failed\n");
- goto fail1;
- }
-
esd->fd = esd_play_stream (esdfmt, as->freq, conf.dac_host, NULL);
if (esd->fd < 0) {
qesd_logerr (errno, "esd_play_stream failed\n");
- goto fail2;
+ goto fail1;
}
if (audio_pt_init (&esd->pt, qesd_thread_out, esd, AUDIO_CAP, AUDIO_FUNC)) {
- goto fail3;
- }
-
- err = pthread_sigmask (SIG_SETMASK, &old_set, NULL);
- if (err) {
- qesd_logerr (err, "pthread_sigmask(restore) failed\n");
+ goto fail2;
}
return 0;
- fail3:
+ fail2:
if (close (esd->fd)) {
qesd_logerr (errno, "%s: close on esd socket(%d) failed\n",
AUDIO_FUNC, esd->fd);
}
esd->fd = -1;
- fail2:
- err = pthread_sigmask (SIG_SETMASK, &old_set, NULL);
- if (err) {
- qesd_logerr (err, "pthread_sigmask(restore) failed\n");
- }
-
fail1:
qemu_free (esd->pcm_buf);
esd->pcm_buf = NULL;
@@ -423,10 +400,6 @@ static int qesd_init_in (HWVoiceIn *hw, struct audsettings *as)
ESDVoiceIn *esd = (ESDVoiceIn *) hw;
struct audsettings obt_as = *as;
int esdfmt = ESD_STREAM | ESD_RECORD;
- int err;
- sigset_t set, old_set;
-
- sigfillset (&set);
esdfmt |= (as->nchannels == 2) ? ESD_STEREO : ESD_MONO;
switch (as->fmt) {
@@ -461,44 +434,25 @@ static int qesd_init_in (HWVoiceIn *hw, struct audsettings *as)
return -1;
}
- esd->fd = -1;
-
- err = pthread_sigmask (SIG_BLOCK, &set, &old_set);
- if (err) {
- qesd_logerr (err, "pthread_sigmask failed\n");
- goto fail1;
- }
-
esd->fd = esd_record_stream (esdfmt, as->freq, conf.adc_host, NULL);
if (esd->fd < 0) {
qesd_logerr (errno, "esd_record_stream failed\n");
- goto fail2;
+ goto fail1;
}
if (audio_pt_init (&esd->pt, qesd_thread_in, esd, AUDIO_CAP, AUDIO_FUNC)) {
- goto fail3;
- }
-
- err = pthread_sigmask (SIG_SETMASK, &old_set, NULL);
- if (err) {
- qesd_logerr (err, "pthread_sigmask(restore) failed\n");
+ goto fail2;
}
return 0;
- fail3:
+ fail2:
if (close (esd->fd)) {
qesd_logerr (errno, "%s: close on esd socket(%d) failed\n",
AUDIO_FUNC, esd->fd);
}
esd->fd = -1;
- fail2:
- err = pthread_sigmask (SIG_SETMASK, &old_set, NULL);
- if (err) {
- qesd_logerr (err, "pthread_sigmask(restore) failed\n");
- }
-
fail1:
qemu_free (esd->pcm_buf);
esd->pcm_buf = NULL;
diff --git a/audio/paaudio.c b/audio/paaudio.c
index 9118ece40c..ff71dac2a5 100644
--- a/audio/paaudio.c
+++ b/audio/paaudio.c
@@ -110,8 +110,8 @@ static void *qpa_thread_out (void *arg)
return NULL;
}
+ pa->live = 0;
pa->rpos = rpos;
- pa->live -= decr;
pa->decr += decr;
}
diff --git a/audio/sdlaudio.c b/audio/sdlaudio.c
index c353016c1f..b74dcfa734 100644
--- a/audio/sdlaudio.c
+++ b/audio/sdlaudio.c
@@ -184,11 +184,20 @@ static int sdl_open (SDL_AudioSpec *req, SDL_AudioSpec *obt)
{
int status;
#ifndef _WIN32
+ int err;
sigset_t new, old;
/* Make sure potential threads created by SDL don't hog signals. */
- sigfillset (&new);
- pthread_sigmask (SIG_BLOCK, &new, &old);
+ err = sigfillset (&new);
+ if (err) {
+ dolog ("sdl_open: sigfillset failed: %s\n", strerror (errno));
+ return -1;
+ }
+ err = pthread_sigmask (SIG_BLOCK, &new, &old);
+ if (err) {
+ dolog ("sdl_open: pthread_sigmask failed: %s\n", strerror (err));
+ return -1;
+ }
#endif
status = SDL_OpenAudio (req, obt);
@@ -197,7 +206,14 @@ static int sdl_open (SDL_AudioSpec *req, SDL_AudioSpec *obt)
}
#ifndef _WIN32
- pthread_sigmask (SIG_SETMASK, &old, NULL);
+ err = pthread_sigmask (SIG_SETMASK, &old, NULL);
+ if (err) {
+ dolog ("sdl_open: pthread_sigmask (restore) failed: %s\n",
+ strerror (errno));
+ /* We have failed to restore original signal mask, all bets are off,
+ so exit the process */
+ exit (EXIT_FAILURE);
+ }
#endif
return status;
}
diff --git a/balloon.c b/balloon.c
index 8e0b7f18de..0021fef4b8 100644
--- a/balloon.c
+++ b/balloon.c
@@ -29,6 +29,7 @@
#include "cpu-common.h"
#include "kvm.h"
#include "balloon.h"
+#include "trace.h"
static QEMUBalloonEvent *qemu_balloon_event;
@@ -43,6 +44,7 @@ void qemu_add_balloon_handler(QEMUBalloonEvent *func, void *opaque)
int qemu_balloon(ram_addr_t target, MonitorCompletion cb, void *opaque)
{
if (qemu_balloon_event) {
+ trace_balloon_event(qemu_balloon_event_opaque, target);
qemu_balloon_event(qemu_balloon_event_opaque, target, cb, opaque);
return 1;
} else {
diff --git a/block-migration.c b/block-migration.c
index 7db6f02b96..0bfdb73c8b 100644
--- a/block-migration.c
+++ b/block-migration.c
@@ -238,7 +238,7 @@ static void init_blk_migration_it(void *opaque, BlockDriverState *bs)
if (!bdrv_is_read_only(bs)) {
sectors = bdrv_getlength(bs) >> BDRV_SECTOR_BITS;
- if (sectors == 0) {
+ if (sectors <= 0) {
return;
}
@@ -346,7 +346,7 @@ static int mig_save_device_dirty(Monitor *mon, QEMUFile *f,
blk->iov.iov_len = nr_sectors * BDRV_SECTOR_SIZE;
qemu_iovec_init_external(&blk->qiov, &blk->iov, 1);
- blk->time = qemu_get_clock_ns(rt_clock);
+ blk->time = qemu_get_clock_ns(rt_clock);
blk->aiocb = bdrv_aio_readv(bmds->bs, sector, &blk->qiov,
nr_sectors, blk_mig_read_cb, blk);
@@ -449,13 +449,13 @@ static int is_stage2_completed(void)
if (block_mig_state.bulk_completed == 1) {
remaining_dirty = get_remaining_dirty();
- if (remaining_dirty == 0) {
- return 1;
- }
+ if (remaining_dirty == 0) {
+ return 1;
+ }
- bwidth = compute_read_bwidth();
+ bwidth = compute_read_bwidth();
- if ((remaining_dirty / bwidth) <=
+ if ((remaining_dirty / bwidth) <=
migrate_max_downtime()) {
/* finish stage2 because we think that we can finish remaing work
below max_downtime */
@@ -586,6 +586,7 @@ static int block_load(QEMUFile *f, void *opaque, int version_id)
addr >>= BDRV_SECTOR_BITS;
if (flags & BLK_MIG_FLAG_DEVICE_BLOCK) {
+ int ret;
/* get device name */
len = qemu_get_byte(f);
qemu_get_buffer(f, (uint8_t *)device_name, len);
@@ -601,9 +602,12 @@ static int block_load(QEMUFile *f, void *opaque, int version_id)
buf = qemu_malloc(BLOCK_SIZE);
qemu_get_buffer(f, buf, BLOCK_SIZE);
- bdrv_write(bs, addr, buf, BDRV_SECTORS_PER_DIRTY_CHUNK);
+ ret = bdrv_write(bs, addr, buf, BDRV_SECTORS_PER_DIRTY_CHUNK);
qemu_free(buf);
+ if (ret < 0) {
+ return ret;
+ }
} else if (flags & BLK_MIG_FLAG_PROGRESS) {
if (!banner_printed) {
printf("Receiving block device images\n");
diff --git a/block.c b/block.c
index 65cf4dc9a4..a19374dfcc 100644
--- a/block.c
+++ b/block.c
@@ -23,6 +23,7 @@
*/
#include "config-host.h"
#include "qemu-common.h"
+#include "trace.h"
#include "monitor.h"
#include "block_int.h"
#include "module.h"
@@ -330,7 +331,7 @@ BlockDriver *bdrv_find_protocol(const char *filename)
return NULL;
}
-static BlockDriver *find_image_format(const char *filename)
+static int find_image_format(const char *filename, BlockDriver **pdrv)
{
int ret, score, score_max;
BlockDriver *drv1, *drv;
@@ -338,19 +339,27 @@ static BlockDriver *find_image_format(const char *filename)
BlockDriverState *bs;
ret = bdrv_file_open(&bs, filename, 0);
- if (ret < 0)
- return NULL;
+ if (ret < 0) {
+ *pdrv = NULL;
+ return ret;
+ }
/* Return the raw BlockDriver * to scsi-generic devices or empty drives */
if (bs->sg || !bdrv_is_inserted(bs)) {
bdrv_delete(bs);
- return bdrv_find_format("raw");
+ drv = bdrv_find_format("raw");
+ if (!drv) {
+ ret = -ENOENT;
+ }
+ *pdrv = drv;
+ return ret;
}
ret = bdrv_pread(bs, 0, buf, sizeof(buf));
bdrv_delete(bs);
if (ret < 0) {
- return NULL;
+ *pdrv = NULL;
+ return ret;
}
score_max = 0;
@@ -364,7 +373,11 @@ static BlockDriver *find_image_format(const char *filename)
}
}
}
- return drv;
+ if (!drv) {
+ ret = -ENOENT;
+ }
+ *pdrv = drv;
+ return ret;
}
/**
@@ -570,11 +583,10 @@ int bdrv_open(BlockDriverState *bs, const char *filename, int flags,
/* Find the right image format driver */
if (!drv) {
- drv = find_image_format(filename);
+ ret = find_image_format(filename, &drv);
}
if (!drv) {
- ret = -ENOENT;
goto unlink_and_fail;
}
@@ -724,14 +736,17 @@ int bdrv_check(BlockDriverState *bs, BdrvCheckResult *res)
return bs->drv->bdrv_check(bs, res);
}
+#define COMMIT_BUF_SECTORS 2048
+
/* commit COW file into the raw image */
int bdrv_commit(BlockDriverState *bs)
{
BlockDriver *drv = bs->drv;
- int64_t i, total_sectors;
- int n, j, ro, open_flags;
+ BlockDriver *backing_drv;
+ int64_t sector, total_sectors;
+ int n, ro, open_flags;
int ret = 0, rw_ret = 0;
- unsigned char sector[BDRV_SECTOR_SIZE];
+ uint8_t *buf;
char filename[1024];
BlockDriverState *bs_rw, *bs_ro;
@@ -745,7 +760,8 @@ int bdrv_commit(BlockDriverState *bs)
if (bs->backing_hd->keep_read_only) {
return -EACCES;
}
-
+
+ backing_drv = bs->backing_hd->drv;
ro = bs->backing_hd->read_only;
strncpy(filename, bs->backing_hd->filename, sizeof(filename));
open_flags = bs->backing_hd->open_flags;
@@ -755,12 +771,14 @@ int bdrv_commit(BlockDriverState *bs)
bdrv_delete(bs->backing_hd);
bs->backing_hd = NULL;
bs_rw = bdrv_new("");
- rw_ret = bdrv_open(bs_rw, filename, open_flags | BDRV_O_RDWR, drv);
+ rw_ret = bdrv_open(bs_rw, filename, open_flags | BDRV_O_RDWR,
+ backing_drv);
if (rw_ret < 0) {
bdrv_delete(bs_rw);
/* try to re-open read-only */
bs_ro = bdrv_new("");
- ret = bdrv_open(bs_ro, filename, open_flags & ~BDRV_O_RDWR, drv);
+ ret = bdrv_open(bs_ro, filename, open_flags & ~BDRV_O_RDWR,
+ backing_drv);
if (ret < 0) {
bdrv_delete(bs_ro);
/* drive not functional anymore */
@@ -774,22 +792,20 @@ int bdrv_commit(BlockDriverState *bs)
}
total_sectors = bdrv_getlength(bs) >> BDRV_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) {
- ret = -EIO;
- goto ro_cleanup;
- }
+ buf = qemu_malloc(COMMIT_BUF_SECTORS * BDRV_SECTOR_SIZE);
- if (bdrv_write(bs->backing_hd, i, sector, 1) != 0) {
- ret = -EIO;
- goto ro_cleanup;
- }
- i++;
- }
- } else {
- i += n;
+ for (sector = 0; sector < total_sectors; sector += n) {
+ if (drv->bdrv_is_allocated(bs, sector, COMMIT_BUF_SECTORS, &n)) {
+
+ if (bdrv_read(bs, sector, buf, n) != 0) {
+ ret = -EIO;
+ goto ro_cleanup;
+ }
+
+ if (bdrv_write(bs->backing_hd, sector, buf, n) != 0) {
+ ret = -EIO;
+ goto ro_cleanup;
+ }
}
}
@@ -806,13 +822,15 @@ int bdrv_commit(BlockDriverState *bs)
bdrv_flush(bs->backing_hd);
ro_cleanup:
+ qemu_free(buf);
if (ro) {
/* re-open as RO */
bdrv_delete(bs->backing_hd);
bs->backing_hd = NULL;
bs_ro = bdrv_new("");
- ret = bdrv_open(bs_ro, filename, open_flags & ~BDRV_O_RDWR, drv);
+ ret = bdrv_open(bs_ro, filename, open_flags & ~BDRV_O_RDWR,
+ backing_drv);
if (ret < 0) {
bdrv_delete(bs_ro);
/* drive not functional anymore */
@@ -1461,10 +1479,8 @@ int bdrv_has_zero_init(BlockDriverState *bs)
{
assert(bs->drv);
- if (bs->drv->no_zero_init) {
- return 0;
- } else if (bs->file) {
- return bdrv_has_zero_init(bs->file);
+ if (bs->drv->bdrv_has_zero_init) {
+ return bs->drv->bdrv_has_zero_init(bs);
}
return 1;
@@ -1796,6 +1812,11 @@ int bdrv_can_snapshot(BlockDriverState *bs)
return 1;
}
+int bdrv_is_snapshot(BlockDriverState *bs)
+{
+ return !!(bs->open_flags & BDRV_O_SNAPSHOT);
+}
+
BlockDriverState *bdrv_snapshots(void)
{
BlockDriverState *bs;
@@ -1962,6 +1983,8 @@ BlockDriverAIOCB *bdrv_aio_readv(BlockDriverState *bs, int64_t sector_num,
BlockDriver *drv = bs->drv;
BlockDriverAIOCB *ret;
+ trace_bdrv_aio_readv(bs, sector_num, nb_sectors, opaque);
+
if (!drv)
return NULL;
if (bdrv_check_request(bs, sector_num, nb_sectors))
@@ -1986,6 +2009,8 @@ BlockDriverAIOCB *bdrv_aio_writev(BlockDriverState *bs, int64_t sector_num,
BlockDriver *drv = bs->drv;
BlockDriverAIOCB *ret;
+ trace_bdrv_aio_writev(bs, sector_num, nb_sectors, opaque);
+
if (!drv)
return NULL;
if (bs->read_only)
@@ -2043,6 +2068,8 @@ static void multiwrite_cb(void *opaque, int ret)
{
MultiwriteCB *mcb = opaque;
+ trace_multiwrite_cb(mcb, ret);
+
if (ret < 0 && !mcb->error) {
mcb->error = ret;
}
@@ -2183,6 +2210,8 @@ int bdrv_aio_multiwrite(BlockDriverState *bs, BlockRequest *reqs, int num_reqs)
// Check for mergable requests
num_reqs = multiwrite_merge(bs, reqs, num_reqs, mcb);
+ trace_bdrv_aio_multiwrite(mcb, mcb->num_callbacks, num_reqs);
+
/*
* Run the aio requests. As soon as one request can't be submitted
* successfully, fail all requests that are not yet submitted (we must
@@ -2204,6 +2233,7 @@ int bdrv_aio_multiwrite(BlockDriverState *bs, BlockRequest *reqs, int num_reqs)
*/
mcb->num_requests = 1;
+ // Run the aio requests
for (i = 0; i < num_reqs; i++) {
mcb->num_requests++;
acb = bdrv_aio_writev(bs, reqs[i].sector, reqs[i].qiov,
@@ -2214,8 +2244,10 @@ int bdrv_aio_multiwrite(BlockDriverState *bs, BlockRequest *reqs, int num_reqs)
// submitted yet. Otherwise we'll wait for the submitted AIOs to
// complete and report the error in the callback.
if (i == 0) {
+ trace_bdrv_aio_multiwrite_earlyfail(mcb);
goto fail;
} else {
+ trace_bdrv_aio_multiwrite_latefail(mcb, i);
multiwrite_cb(mcb, -EIO);
break;
}
@@ -2498,7 +2530,7 @@ int bdrv_is_inserted(BlockDriverState *bs)
if (!drv)
return 0;
if (!drv->bdrv_is_inserted)
- return 1;
+ return !bs->tray_open;
ret = drv->bdrv_is_inserted(bs);
return ret;
}
@@ -2540,10 +2572,11 @@ int bdrv_eject(BlockDriverState *bs, int eject_flag)
ret = drv->bdrv_eject(bs, eject_flag);
}
if (ret == -ENOTSUP) {
- if (eject_flag)
- bdrv_close(bs);
ret = 0;
}
+ if (ret >= 0) {
+ bs->tray_open = eject_flag;
+ }
return ret;
}
diff --git a/block.h b/block.h
index c2a7e4c762..5f6438010f 100644
--- a/block.h
+++ b/block.h
@@ -35,7 +35,7 @@ typedef struct QEMUSnapshotInfo {
#define BDRV_O_NO_BACKING 0x0100 /* don't open the backing file */
#define BDRV_O_NO_FLUSH 0x0200 /* disable flushing on this disk */
-#define BDRV_O_CACHE_MASK (BDRV_O_NOCACHE | BDRV_O_CACHE_WB)
+#define BDRV_O_CACHE_MASK (BDRV_O_NOCACHE | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH)
#define BDRV_SECTOR_BITS 9
#define BDRV_SECTOR_SIZE (1ULL << BDRV_SECTOR_BITS)
@@ -202,6 +202,7 @@ const char *bdrv_get_encrypted_filename(BlockDriverState *bs);
void bdrv_get_backing_filename(BlockDriverState *bs,
char *filename, int filename_size);
int bdrv_can_snapshot(BlockDriverState *bs);
+int bdrv_is_snapshot(BlockDriverState *bs);
BlockDriverState *bdrv_snapshots(void);
int bdrv_snapshot_create(BlockDriverState *bs,
QEMUSnapshotInfo *sn_info);
diff --git a/block/blkdebug.c b/block/blkdebug.c
index 2a63df9323..4d6ff0a368 100644
--- a/block/blkdebug.c
+++ b/block/blkdebug.c
@@ -439,9 +439,7 @@ static void blkdebug_debug_event(BlockDriverState *bs, BlkDebugEvent event)
struct BlkdebugRule *rule;
BlkdebugVars old_vars = s->vars;
- if (event < 0 || event >= BLKDBG_EVENT_MAX) {
- return;
- }
+ assert((int)event >= 0 && event < BLKDBG_EVENT_MAX);
QLIST_FOREACH(rule, &s->rules[event], next) {
process_rule(bs, rule, &old_vars);
diff --git a/block/blkverify.c b/block/blkverify.c
new file mode 100644
index 0000000000..8083464751
--- /dev/null
+++ b/block/blkverify.c
@@ -0,0 +1,382 @@
+/*
+ * Block protocol for block driver correctness testing
+ *
+ * Copyright (C) 2010 IBM, Corp.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include <stdarg.h>
+#include "qemu_socket.h" /* for EINPROGRESS on Windows */
+#include "block_int.h"
+
+typedef struct {
+ BlockDriverState *test_file;
+} BDRVBlkverifyState;
+
+typedef struct BlkverifyAIOCB BlkverifyAIOCB;
+struct BlkverifyAIOCB {
+ BlockDriverAIOCB common;
+ QEMUBH *bh;
+
+ /* Request metadata */
+ bool is_write;
+ int64_t sector_num;
+ int nb_sectors;
+
+ int ret; /* first completed request's result */
+ unsigned int done; /* completion counter */
+ bool *finished; /* completion signal for cancel */
+
+ QEMUIOVector *qiov; /* user I/O vector */
+ QEMUIOVector raw_qiov; /* cloned I/O vector for raw file */
+ void *buf; /* buffer for raw file I/O */
+
+ void (*verify)(BlkverifyAIOCB *acb);
+};
+
+static void blkverify_aio_cancel(BlockDriverAIOCB *blockacb)
+{
+ BlkverifyAIOCB *acb = (BlkverifyAIOCB *)blockacb;
+ bool finished = false;
+
+ /* Wait until request completes, invokes its callback, and frees itself */
+ acb->finished = &finished;
+ while (!finished) {
+ qemu_aio_wait();
+ }
+}
+
+static AIOPool blkverify_aio_pool = {
+ .aiocb_size = sizeof(BlkverifyAIOCB),
+ .cancel = blkverify_aio_cancel,
+};
+
+static void blkverify_err(BlkverifyAIOCB *acb, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ fprintf(stderr, "blkverify: %s sector_num=%" PRId64 " nb_sectors=%d ",
+ acb->is_write ? "write" : "read", acb->sector_num,
+ acb->nb_sectors);
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+ va_end(ap);
+ exit(1);
+}
+
+/* Valid blkverify filenames look like blkverify:path/to/raw_image:path/to/image */
+static int blkverify_open(BlockDriverState *bs, const char *filename, int flags)
+{
+ BDRVBlkverifyState *s = bs->opaque;
+ int ret;
+ char *raw, *c;
+
+ /* Parse the blkverify: prefix */
+ if (strncmp(filename, "blkverify:", strlen("blkverify:"))) {
+ return -EINVAL;
+ }
+ filename += strlen("blkverify:");
+
+ /* Parse the raw image filename */
+ c = strchr(filename, ':');
+ if (c == NULL) {
+ return -EINVAL;
+ }
+
+ raw = strdup(filename);
+ raw[c - filename] = '\0';
+ ret = bdrv_file_open(&bs->file, raw, flags);
+ free(raw);
+ if (ret < 0) {
+ return ret;
+ }
+ filename = c + 1;
+
+ /* Open the test file */
+ s->test_file = bdrv_new("");
+ ret = bdrv_open(s->test_file, filename, flags, NULL);
+ if (ret < 0) {
+ bdrv_delete(s->test_file);
+ s->test_file = NULL;
+ return ret;
+ }
+
+ return 0;
+}
+
+static void blkverify_close(BlockDriverState *bs)
+{
+ BDRVBlkverifyState *s = bs->opaque;
+
+ bdrv_delete(s->test_file);
+ s->test_file = NULL;
+}
+
+static void blkverify_flush(BlockDriverState *bs)
+{
+ BDRVBlkverifyState *s = bs->opaque;
+
+ /* Only flush test file, the raw file is not important */
+ bdrv_flush(s->test_file);
+}
+
+static int64_t blkverify_getlength(BlockDriverState *bs)
+{
+ BDRVBlkverifyState *s = bs->opaque;
+
+ return bdrv_getlength(s->test_file);
+}
+
+/**
+ * Check that I/O vector contents are identical
+ *
+ * @a: I/O vector
+ * @b: I/O vector
+ * @ret: Offset to first mismatching byte or -1 if match
+ */
+static ssize_t blkverify_iovec_compare(QEMUIOVector *a, QEMUIOVector *b)
+{
+ int i;
+ ssize_t offset = 0;
+
+ assert(a->niov == b->niov);
+ for (i = 0; i < a->niov; i++) {
+ size_t len = 0;
+ uint8_t *p = (uint8_t *)a->iov[i].iov_base;
+ uint8_t *q = (uint8_t *)b->iov[i].iov_base;
+
+ assert(a->iov[i].iov_len == b->iov[i].iov_len);
+ while (len < a->iov[i].iov_len && *p++ == *q++) {
+ len++;
+ }
+
+ offset += len;
+
+ if (len != a->iov[i].iov_len) {
+ return offset;
+ }
+ }
+ return -1;
+}
+
+typedef struct {
+ int src_index;
+ struct iovec *src_iov;
+ void *dest_base;
+} IOVectorSortElem;
+
+static int sortelem_cmp_src_base(const void *a, const void *b)
+{
+ const IOVectorSortElem *elem_a = a;
+ const IOVectorSortElem *elem_b = b;
+
+ /* Don't overflow */
+ if (elem_a->src_iov->iov_base < elem_b->src_iov->iov_base) {
+ return -1;
+ } else if (elem_a->src_iov->iov_base > elem_b->src_iov->iov_base) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+static int sortelem_cmp_src_index(const void *a, const void *b)
+{
+ const IOVectorSortElem *elem_a = a;
+ const IOVectorSortElem *elem_b = b;
+
+ return elem_a->src_index - elem_b->src_index;
+}
+
+/**
+ * Copy contents of I/O vector
+ *
+ * The relative relationships of overlapping iovecs are preserved. This is
+ * necessary to ensure identical semantics in the cloned I/O vector.
+ */
+static void blkverify_iovec_clone(QEMUIOVector *dest, const QEMUIOVector *src,
+ void *buf)
+{
+ IOVectorSortElem sortelems[src->niov];
+ void *last_end;
+ int i;
+
+ /* Sort by source iovecs by base address */
+ for (i = 0; i < src->niov; i++) {
+ sortelems[i].src_index = i;
+ sortelems[i].src_iov = &src->iov[i];
+ }
+ qsort(sortelems, src->niov, sizeof(sortelems[0]), sortelem_cmp_src_base);
+
+ /* Allocate buffer space taking into account overlapping iovecs */
+ last_end = NULL;
+ for (i = 0; i < src->niov; i++) {
+ struct iovec *cur = sortelems[i].src_iov;
+ ptrdiff_t rewind = 0;
+
+ /* Detect overlap */
+ if (last_end && last_end > cur->iov_base) {
+ rewind = last_end - cur->iov_base;
+ }
+
+ sortelems[i].dest_base = buf - rewind;
+ buf += cur->iov_len - MIN(rewind, cur->iov_len);
+ last_end = MAX(cur->iov_base + cur->iov_len, last_end);
+ }
+
+ /* Sort by source iovec index and build destination iovec */
+ qsort(sortelems, src->niov, sizeof(sortelems[0]), sortelem_cmp_src_index);
+ for (i = 0; i < src->niov; i++) {
+ qemu_iovec_add(dest, sortelems[i].dest_base, src->iov[i].iov_len);
+ }
+}
+
+static BlkverifyAIOCB *blkverify_aio_get(BlockDriverState *bs, bool is_write,
+ int64_t sector_num, QEMUIOVector *qiov,
+ int nb_sectors,
+ BlockDriverCompletionFunc *cb,
+ void *opaque)
+{
+ BlkverifyAIOCB *acb = qemu_aio_get(&blkverify_aio_pool, bs, cb, opaque);
+
+ acb->bh = NULL;
+ acb->is_write = is_write;
+ acb->sector_num = sector_num;
+ acb->nb_sectors = nb_sectors;
+ acb->ret = -EINPROGRESS;
+ acb->done = 0;
+ acb->qiov = qiov;
+ acb->buf = NULL;
+ acb->verify = NULL;
+ acb->finished = NULL;
+ return acb;
+}
+
+static void blkverify_aio_bh(void *opaque)
+{
+ BlkverifyAIOCB *acb = opaque;
+
+ qemu_bh_delete(acb->bh);
+ if (acb->buf) {
+ qemu_iovec_destroy(&acb->raw_qiov);
+ qemu_vfree(acb->buf);
+ }
+ acb->common.cb(acb->common.opaque, acb->ret);
+ if (acb->finished) {
+ *acb->finished = true;
+ }
+ qemu_aio_release(acb);
+}
+
+static void blkverify_aio_cb(void *opaque, int ret)
+{
+ BlkverifyAIOCB *acb = opaque;
+
+ switch (++acb->done) {
+ case 1:
+ acb->ret = ret;
+ break;
+
+ case 2:
+ if (acb->ret != ret) {
+ blkverify_err(acb, "return value mismatch %d != %d", acb->ret, ret);
+ }
+
+ if (acb->verify) {
+ acb->verify(acb);
+ }
+
+ acb->bh = qemu_bh_new(blkverify_aio_bh, acb);
+ qemu_bh_schedule(acb->bh);
+ break;
+ }
+}
+
+static void blkverify_verify_readv(BlkverifyAIOCB *acb)
+{
+ ssize_t offset = blkverify_iovec_compare(acb->qiov, &acb->raw_qiov);
+ if (offset != -1) {
+ blkverify_err(acb, "contents mismatch in sector %ld",
+ acb->sector_num + (offset / BDRV_SECTOR_SIZE));
+ }
+}
+
+static BlockDriverAIOCB *blkverify_aio_readv(BlockDriverState *bs,
+ int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
+ BlockDriverCompletionFunc *cb, void *opaque)
+{
+ BDRVBlkverifyState *s = bs->opaque;
+ BlkverifyAIOCB *acb = blkverify_aio_get(bs, false, sector_num, qiov,
+ nb_sectors, cb, opaque);
+
+ acb->verify = blkverify_verify_readv;
+ acb->buf = qemu_blockalign(bs->file, qiov->size);
+ qemu_iovec_init(&acb->raw_qiov, acb->qiov->niov);
+ blkverify_iovec_clone(&acb->raw_qiov, qiov, acb->buf);
+
+ if (!bdrv_aio_readv(s->test_file, sector_num, qiov, nb_sectors,
+ blkverify_aio_cb, acb)) {
+ blkverify_aio_cb(acb, -EIO);
+ }
+ if (!bdrv_aio_readv(bs->file, sector_num, &acb->raw_qiov, nb_sectors,
+ blkverify_aio_cb, acb)) {
+ blkverify_aio_cb(acb, -EIO);
+ }
+ return &acb->common;
+}
+
+static BlockDriverAIOCB *blkverify_aio_writev(BlockDriverState *bs,
+ int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
+ BlockDriverCompletionFunc *cb, void *opaque)
+{
+ BDRVBlkverifyState *s = bs->opaque;
+ BlkverifyAIOCB *acb = blkverify_aio_get(bs, true, sector_num, qiov,
+ nb_sectors, cb, opaque);
+
+ if (!bdrv_aio_writev(s->test_file, sector_num, qiov, nb_sectors,
+ blkverify_aio_cb, acb)) {
+ blkverify_aio_cb(acb, -EIO);
+ }
+ if (!bdrv_aio_writev(bs->file, sector_num, qiov, nb_sectors,
+ blkverify_aio_cb, acb)) {
+ blkverify_aio_cb(acb, -EIO);
+ }
+ return &acb->common;
+}
+
+static BlockDriverAIOCB *blkverify_aio_flush(BlockDriverState *bs,
+ BlockDriverCompletionFunc *cb,
+ void *opaque)
+{
+ BDRVBlkverifyState *s = bs->opaque;
+
+ /* Only flush test file, the raw file is not important */
+ return bdrv_aio_flush(s->test_file, cb, opaque);
+}
+
+static BlockDriver bdrv_blkverify = {
+ .format_name = "blkverify",
+ .protocol_name = "blkverify",
+
+ .instance_size = sizeof(BDRVBlkverifyState),
+
+ .bdrv_getlength = blkverify_getlength,
+
+ .bdrv_file_open = blkverify_open,
+ .bdrv_close = blkverify_close,
+ .bdrv_flush = blkverify_flush,
+
+ .bdrv_aio_readv = blkverify_aio_readv,
+ .bdrv_aio_writev = blkverify_aio_writev,
+ .bdrv_aio_flush = blkverify_aio_flush,
+};
+
+static void bdrv_blkverify_init(void)
+{
+ bdrv_register(&bdrv_blkverify);
+}
+
+block_init(bdrv_blkverify_init);
diff --git a/block/nbd.c b/block/nbd.c
index a1ec123a63..c8dc763c6b 100644
--- a/block/nbd.c
+++ b/block/nbd.c
@@ -33,6 +33,8 @@
#include <sys/types.h>
#include <unistd.h>
+#define EN_OPTSTR ":exportname="
+
typedef struct BDRVNBDState {
int sock;
off_t size;
@@ -42,55 +44,81 @@ typedef struct BDRVNBDState {
static int nbd_open(BlockDriverState *bs, const char* filename, int flags)
{
BDRVNBDState *s = bs->opaque;
+ uint32_t nbdflags;
+
+ char *file;
+ char *name;
const char *host;
const char *unixpath;
int sock;
off_t size;
size_t blocksize;
int ret;
+ int err = -EINVAL;
+
+ file = qemu_strdup(filename);
- if (!strstart(filename, "nbd:", &host))
- return -EINVAL;
+ name = strstr(file, EN_OPTSTR);
+ if (name) {
+ if (name[strlen(EN_OPTSTR)] == 0) {
+ goto out;
+ }
+ name[0] = 0;
+ name += strlen(EN_OPTSTR);
+ }
+
+ if (!strstart(file, "nbd:", &host)) {
+ goto out;
+ }
if (strstart(host, "unix:", &unixpath)) {
- if (unixpath[0] != '/')
- return -EINVAL;
+ if (unixpath[0] != '/') {
+ goto out;
+ }
sock = unix_socket_outgoing(unixpath);
} else {
- uint16_t port;
+ uint16_t port = NBD_DEFAULT_PORT;
char *p, *r;
char hostname[128];
pstrcpy(hostname, 128, host);
p = strchr(hostname, ':');
- if (p == NULL)
- return -EINVAL;
+ if (p != NULL) {
+ *p = '\0';
+ p++;
- *p = '\0';
- p++;
+ port = strtol(p, &r, 0);
+ if (r == p) {
+ goto out;
+ }
+ }
- port = strtol(p, &r, 0);
- if (r == p)
- return -EINVAL;
sock = tcp_socket_outgoing(hostname, port);
}
- if (sock == -1)
- return -errno;
+ if (sock == -1) {
+ err = -errno;
+ goto out;
+ }
- ret = nbd_receive_negotiate(sock, &size, &blocksize);
- if (ret == -1)
- return -errno;
+ ret = nbd_receive_negotiate(sock, name, &nbdflags, &size, &blocksize);
+ if (ret == -1) {
+ err = -errno;
+ goto out;
+ }
s->sock = sock;
s->size = size;
s->blocksize = blocksize;
+ err = 0;
- return 0;
+out:
+ qemu_free(file);
+ return err;
}
static int nbd_read(BlockDriverState *bs, int64_t sector_num,
diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c
index 166922f8be..fb4224a669 100644
--- a/block/qcow2-cluster.c
+++ b/block/qcow2-cluster.c
@@ -60,6 +60,7 @@ int qcow2_grow_l1_table(BlockDriverState *bs, int min_size)
qemu_free(new_l1_table);
return new_l1_table_offset;
}
+ bdrv_flush(bs->file);
BLKDBG_EVENT(bs->file, BLKDBG_L1_GROW_WRITE_TABLE);
for(i = 0; i < s->l1_size; i++)
@@ -243,6 +244,7 @@ static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table)
if (l2_offset < 0) {
return l2_offset;
}
+ bdrv_flush(bs->file);
/* allocate a new entry in the l2 cache */
@@ -348,6 +350,8 @@ static int qcow_read(BlockDriverState *bs, int64_t sector_num,
BDRVQcowState *s = bs->opaque;
int ret, index_in_cluster, n, n1;
uint64_t cluster_offset;
+ struct iovec iov;
+ QEMUIOVector qiov;
while (nb_sectors > 0) {
n = nb_sectors;
@@ -362,7 +366,11 @@ static int qcow_read(BlockDriverState *bs, int64_t sector_num,
if (!cluster_offset) {
if (bs->backing_hd) {
/* read from the base image */
- n1 = qcow2_backing_read1(bs->backing_hd, sector_num, buf, n);
+ iov.iov_base = buf;
+ iov.iov_len = n * 512;
+ qemu_iovec_init_external(&qiov, &iov, 1);
+
+ n1 = qcow2_backing_read1(bs->backing_hd, &qiov, sector_num, n);
if (n1 > 0) {
BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING);
ret = bdrv_read(bs->backing_hd, sector_num, buf, n1);
@@ -413,7 +421,7 @@ static int copy_sectors(BlockDriverState *bs, uint64_t start_sect,
&s->aes_encrypt_key);
}
BLKDBG_EVENT(bs->file, BLKDBG_COW_WRITE);
- ret = bdrv_write_sync(bs->file, (cluster_offset >> 9) + n_start,
+ ret = bdrv_write(bs->file, (cluster_offset >> 9) + n_start,
s->cluster_data, n);
if (ret < 0)
return ret;
@@ -655,7 +663,7 @@ static int write_l2_entries(BlockDriverState *bs, uint64_t *l2_table,
int ret;
BLKDBG_EVENT(bs->file, BLKDBG_L2_UPDATE);
- ret = bdrv_pwrite_sync(bs->file, l2_offset + start_offset,
+ ret = bdrv_pwrite(bs->file, l2_offset + start_offset,
&l2_table[l2_start_index], len);
if (ret < 0) {
return ret;
@@ -712,15 +720,30 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m)
(i << s->cluster_bits)) | QCOW_OFLAG_COPIED);
}
+ /*
+ * Before we update the L2 table to actually point to the new cluster, we
+ * need to be sure that the refcounts have been increased and COW was
+ * handled.
+ */
+ bdrv_flush(bs->file);
+
ret = write_l2_entries(bs, l2_table, l2_offset, l2_index, m->nb_clusters);
if (ret < 0) {
qcow2_l2_cache_reset(bs);
goto err;
}
- for (i = 0; i < j; i++)
- qcow2_free_any_clusters(bs,
- be64_to_cpu(old_cluster[i]) & ~QCOW_OFLAG_COPIED, 1);
+ /*
+ * If this was a COW, we need to decrease the refcount of the old cluster.
+ * Also flush bs->file to get the right order for L2 and refcount update.
+ */
+ if (j != 0) {
+ bdrv_flush(bs->file);
+ for (i = 0; i < j; i++) {
+ qcow2_free_any_clusters(bs,
+ be64_to_cpu(old_cluster[i]) & ~QCOW_OFLAG_COPIED, 1);
+ }
+ }
ret = 0;
err:
diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
index 4c19e7ebd8..0efb6760cb 100644
--- a/block/qcow2-refcount.c
+++ b/block/qcow2-refcount.c
@@ -261,6 +261,8 @@ static int64_t alloc_refcount_block(BlockDriverState *bs, int64_t cluster_index)
goto fail_block;
}
+ bdrv_flush(bs->file);
+
/* Initialize the new refcount block only after updating its refcount,
* update_refcount uses the refcount cache itself */
memset(s->refcount_block_cache, 0, s->cluster_size);
@@ -444,7 +446,7 @@ static int write_refcount_block_entries(BlockDriverState *bs,
size = (last_index - first_index) << REFCOUNT_SHIFT;
BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_UPDATE_PART);
- ret = bdrv_pwrite_sync(bs->file,
+ ret = bdrv_pwrite(bs->file,
refcount_block_offset + (first_index << REFCOUNT_SHIFT),
&s->refcount_block_cache[first_index], size);
if (ret < 0) {
@@ -549,6 +551,7 @@ fail:
if (ret < 0) {
int dummy;
dummy = update_refcount(bs, offset, cluster_offset - offset, -addend);
+ (void)dummy;
}
return ret;
@@ -573,6 +576,8 @@ static int update_cluster_refcount(BlockDriverState *bs,
return ret;
}
+ bdrv_flush(bs->file);
+
return get_refcount(bs, cluster_index);
}
@@ -624,6 +629,7 @@ int64_t qcow2_alloc_clusters(BlockDriverState *bs, int64_t size)
if (ret < 0) {
return ret;
}
+
return offset;
}
@@ -671,6 +677,8 @@ int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size)
goto redo;
}
}
+
+ bdrv_flush(bs->file);
return offset;
}
@@ -801,6 +809,10 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
if (ret < 0) {
goto fail;
}
+
+ /* TODO Flushing once for the whole function should
+ * be enough */
+ bdrv_flush(bs->file);
}
/* compressed clusters are never modified */
refcount = 2;
diff --git a/block/qcow2-snapshot.c b/block/qcow2-snapshot.c
index 6228612f84..bbfcaaae22 100644
--- a/block/qcow2-snapshot.c
+++ b/block/qcow2-snapshot.c
@@ -138,6 +138,7 @@ static int qcow_write_snapshots(BlockDriverState *bs)
snapshots_size = offset;
snapshots_offset = qcow2_alloc_clusters(bs, snapshots_size);
+ bdrv_flush(bs->file);
offset = snapshots_offset;
if (offset < 0) {
return offset;
@@ -271,6 +272,7 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
if (l1_table_offset < 0) {
goto fail;
}
+ bdrv_flush(bs->file);
sn->l1_table_offset = l1_table_offset;
sn->l1_size = s->l1_size;
diff --git a/block/qcow2.c b/block/qcow2.c
index a53014dbda..ee3481b786 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -311,8 +311,8 @@ static int qcow_is_allocated(BlockDriverState *bs, int64_t sector_num,
}
/* handle reading after the end of the backing file */
-int qcow2_backing_read1(BlockDriverState *bs,
- int64_t sector_num, uint8_t *buf, int nb_sectors)
+int qcow2_backing_read1(BlockDriverState *bs, QEMUIOVector *qiov,
+ int64_t sector_num, int nb_sectors)
{
int n1;
if ((sector_num + nb_sectors) <= bs->total_sectors)
@@ -321,7 +321,9 @@ int qcow2_backing_read1(BlockDriverState *bs,
n1 = 0;
else
n1 = bs->total_sectors - sector_num;
- memset(buf + n1 * 512, 0, 512 * (nb_sectors - n1));
+
+ qemu_iovec_memset(qiov, 0, 512 * (nb_sectors - n1));
+
return n1;
}
@@ -329,14 +331,12 @@ typedef struct QCowAIOCB {
BlockDriverAIOCB common;
int64_t sector_num;
QEMUIOVector *qiov;
- uint8_t *buf;
- void *orig_buf;
int remaining_sectors;
int cur_nr_sectors; /* number of sectors in current iteration */
+ uint64_t bytes_done;
uint64_t cluster_offset;
uint8_t *cluster_data;
BlockDriverAIOCB *hd_aiocb;
- struct iovec hd_iov;
QEMUIOVector hd_qiov;
QEMUBH *bh;
QCowL2Meta l2meta;
@@ -397,15 +397,19 @@ static void qcow_aio_read_cb(void *opaque, int ret)
/* nothing to do */
} else {
if (s->crypt_method) {
- qcow2_encrypt_sectors(s, acb->sector_num, acb->buf, acb->buf,
- acb->cur_nr_sectors, 0,
- &s->aes_decrypt_key);
+ qcow2_encrypt_sectors(s, acb->sector_num, acb->cluster_data,
+ acb->cluster_data, acb->cur_nr_sectors, 0, &s->aes_decrypt_key);
+ qemu_iovec_reset(&acb->hd_qiov);
+ qemu_iovec_copy(&acb->hd_qiov, acb->qiov, acb->bytes_done,
+ acb->cur_nr_sectors * 512);
+ qemu_iovec_from_buffer(&acb->hd_qiov, acb->cluster_data,
+ 512 * acb->cur_nr_sectors);
}
}
acb->remaining_sectors -= acb->cur_nr_sectors;
acb->sector_num += acb->cur_nr_sectors;
- acb->buf += acb->cur_nr_sectors * 512;
+ acb->bytes_done += acb->cur_nr_sectors * 512;
if (acb->remaining_sectors == 0) {
/* request completed */
@@ -415,6 +419,11 @@ static void qcow_aio_read_cb(void *opaque, int ret)
/* prepare next AIO request */
acb->cur_nr_sectors = acb->remaining_sectors;
+ if (s->crypt_method) {
+ acb->cur_nr_sectors = MIN(acb->cur_nr_sectors,
+ QCOW_MAX_CRYPT_CLUSTERS * s->cluster_sectors);
+ }
+
ret = qcow2_get_cluster_offset(bs, acb->sector_num << 9,
&acb->cur_nr_sectors, &acb->cluster_offset);
if (ret < 0) {
@@ -423,15 +432,17 @@ static void qcow_aio_read_cb(void *opaque, int ret)
index_in_cluster = acb->sector_num & (s->cluster_sectors - 1);
+ qemu_iovec_reset(&acb->hd_qiov);
+ qemu_iovec_copy(&acb->hd_qiov, acb->qiov, acb->bytes_done,
+ acb->cur_nr_sectors * 512);
+
if (!acb->cluster_offset) {
+
if (bs->backing_hd) {
/* read from the base image */
- n1 = qcow2_backing_read1(bs->backing_hd, acb->sector_num,
- acb->buf, acb->cur_nr_sectors);
+ n1 = qcow2_backing_read1(bs->backing_hd, &acb->hd_qiov,
+ acb->sector_num, acb->cur_nr_sectors);
if (n1 > 0) {
- acb->hd_iov.iov_base = (void *)acb->buf;
- acb->hd_iov.iov_len = acb->cur_nr_sectors * 512;
- qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1);
BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING_AIO);
acb->hd_aiocb = bdrv_aio_readv(bs->backing_hd, acb->sector_num,
&acb->hd_qiov, acb->cur_nr_sectors,
@@ -445,7 +456,7 @@ static void qcow_aio_read_cb(void *opaque, int ret)
}
} else {
/* Note: in this case, no need to wait */
- memset(acb->buf, 0, 512 * acb->cur_nr_sectors);
+ qemu_iovec_memset(&acb->hd_qiov, 0, 512 * acb->cur_nr_sectors);
ret = qcow_schedule_bh(qcow_aio_read_bh, acb);
if (ret < 0)
goto done;
@@ -454,8 +465,11 @@ static void qcow_aio_read_cb(void *opaque, int ret)
/* add AIO support for compressed blocks ? */
if (qcow2_decompress_cluster(bs, acb->cluster_offset) < 0)
goto done;
- memcpy(acb->buf, s->cluster_cache + index_in_cluster * 512,
- 512 * acb->cur_nr_sectors);
+
+ qemu_iovec_from_buffer(&acb->hd_qiov,
+ s->cluster_cache + index_in_cluster * 512,
+ 512 * acb->cur_nr_sectors);
+
ret = qcow_schedule_bh(qcow_aio_read_bh, acb);
if (ret < 0)
goto done;
@@ -465,9 +479,23 @@ static void qcow_aio_read_cb(void *opaque, int ret)
goto done;
}
- acb->hd_iov.iov_base = (void *)acb->buf;
- acb->hd_iov.iov_len = acb->cur_nr_sectors * 512;
- qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1);
+ if (s->crypt_method) {
+ /*
+ * For encrypted images, read everything into a temporary
+ * contiguous buffer on which the AES functions can work.
+ */
+ if (!acb->cluster_data) {
+ acb->cluster_data =
+ qemu_mallocz(QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size);
+ }
+
+ assert(acb->cur_nr_sectors <=
+ QCOW_MAX_CRYPT_CLUSTERS * s->cluster_sectors);
+ qemu_iovec_reset(&acb->hd_qiov);
+ qemu_iovec_add(&acb->hd_qiov, acb->cluster_data,
+ 512 * acb->cur_nr_sectors);
+ }
+
BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO);
acb->hd_aiocb = bdrv_aio_readv(bs->file,
(acb->cluster_offset >> 9) + index_in_cluster,
@@ -481,11 +509,8 @@ static void qcow_aio_read_cb(void *opaque, int ret)
return;
done:
- if (acb->qiov->niov > 1) {
- qemu_iovec_from_buffer(acb->qiov, acb->orig_buf, acb->qiov->size);
- qemu_vfree(acb->orig_buf);
- }
acb->common.cb(acb->common.opaque, ret);
+ qemu_iovec_destroy(&acb->hd_qiov);
qemu_aio_release(acb);
}
@@ -501,13 +526,10 @@ static QCowAIOCB *qcow_aio_setup(BlockDriverState *bs,
acb->hd_aiocb = NULL;
acb->sector_num = sector_num;
acb->qiov = qiov;
- if (qiov->niov > 1) {
- acb->buf = acb->orig_buf = qemu_blockalign(bs, qiov->size);
- if (is_write)
- qemu_iovec_to_buffer(qiov, acb->buf);
- } else {
- acb->buf = (uint8_t *)qiov->iov->iov_base;
- }
+
+ qemu_iovec_init(&acb->hd_qiov, qiov->niov);
+
+ acb->bytes_done = 0;
acb->remaining_sectors = nb_sectors;
acb->cur_nr_sectors = 0;
acb->cluster_offset = 0;
@@ -557,7 +579,6 @@ static void qcow_aio_write_cb(void *opaque, int ret)
BlockDriverState *bs = acb->common.bs;
BDRVQcowState *s = bs->opaque;
int index_in_cluster;
- const uint8_t *src_buf;
int n_end;
acb->hd_aiocb = NULL;
@@ -573,7 +594,7 @@ static void qcow_aio_write_cb(void *opaque, int ret)
acb->remaining_sectors -= acb->cur_nr_sectors;
acb->sector_num += acb->cur_nr_sectors;
- acb->buf += acb->cur_nr_sectors * 512;
+ acb->bytes_done += acb->cur_nr_sectors * 512;
if (acb->remaining_sectors == 0) {
/* request completed */
@@ -604,20 +625,27 @@ static void qcow_aio_write_cb(void *opaque, int ret)
assert((acb->cluster_offset & 511) == 0);
+ qemu_iovec_reset(&acb->hd_qiov);
+ qemu_iovec_copy(&acb->hd_qiov, acb->qiov, acb->bytes_done,
+ acb->cur_nr_sectors * 512);
+
if (s->crypt_method) {
if (!acb->cluster_data) {
acb->cluster_data = qemu_mallocz(QCOW_MAX_CRYPT_CLUSTERS *
s->cluster_size);
}
- qcow2_encrypt_sectors(s, acb->sector_num, acb->cluster_data, acb->buf,
- acb->cur_nr_sectors, 1, &s->aes_encrypt_key);
- src_buf = acb->cluster_data;
- } else {
- src_buf = acb->buf;
+
+ assert(acb->hd_qiov.size <= QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size);
+ qemu_iovec_to_buffer(&acb->hd_qiov, acb->cluster_data);
+
+ qcow2_encrypt_sectors(s, acb->sector_num, acb->cluster_data,
+ acb->cluster_data, acb->cur_nr_sectors, 1, &s->aes_encrypt_key);
+
+ qemu_iovec_reset(&acb->hd_qiov);
+ qemu_iovec_add(&acb->hd_qiov, acb->cluster_data,
+ acb->cur_nr_sectors * 512);
}
- acb->hd_iov.iov_base = (void *)src_buf;
- acb->hd_iov.iov_len = acb->cur_nr_sectors * 512;
- qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1);
+
BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO);
acb->hd_aiocb = bdrv_aio_writev(bs->file,
(acb->cluster_offset >> 9) + index_in_cluster,
@@ -635,9 +663,8 @@ fail:
QLIST_REMOVE(&acb->l2meta, next_in_flight);
}
done:
- if (acb->qiov->niov > 1)
- qemu_vfree(acb->orig_buf);
acb->common.cb(acb->common.opaque, ret);
+ qemu_iovec_destroy(&acb->hd_qiov);
qemu_aio_release(acb);
}
diff --git a/block/qcow2.h b/block/qcow2.h
index 3ff162efcd..356a34af43 100644
--- a/block/qcow2.h
+++ b/block/qcow2.h
@@ -166,8 +166,8 @@ static inline int64_t align_offset(int64_t offset, int n)
// FIXME Need qcow2_ prefix to global functions
/* qcow2.c functions */
-int qcow2_backing_read1(BlockDriverState *bs,
- int64_t sector_num, uint8_t *buf, int nb_sectors);
+int qcow2_backing_read1(BlockDriverState *bs, QEMUIOVector *qiov,
+ int64_t sector_num, int nb_sectors);
/* qcow2-refcount.c functions */
int qcow2_refcount_init(BlockDriverState *bs);
diff --git a/block/raw-posix.c b/block/raw-posix.c
index 291699fbc3..d0393e0c44 100644
--- a/block/raw-posix.c
+++ b/block/raw-posix.c
@@ -48,6 +48,7 @@
#endif
#ifdef __linux__
#include <sys/ioctl.h>
+#include <sys/param.h>
#include <linux/cdrom.h>
#include <linux/fd.h>
#endif
@@ -96,11 +97,11 @@
#define FTYPE_CD 1
#define FTYPE_FD 2
-#define ALIGNED_BUFFER_SIZE (32 * 512)
-
-/* if the FD is not accessed during that time (in ms), we try to
+/* if the FD is not accessed during that time (in ns), we try to
reopen it to see if the disk has been changed */
-#define FD_OPEN_TIMEOUT 1000
+#define FD_OPEN_TIMEOUT (1000000000)
+
+#define MAX_BLOCKSIZE 4096
typedef struct BDRVRawState {
int fd;
@@ -117,7 +118,8 @@ typedef struct BDRVRawState {
int use_aio;
void *aio_ctx;
#endif
- uint8_t* aligned_buf;
+ uint8_t *aligned_buf;
+ unsigned aligned_buf_size;
} BDRVRawState;
static int fd_open(BlockDriverState *bs);
@@ -160,7 +162,12 @@ static int raw_open_common(BlockDriverState *bs, const char *filename,
s->aligned_buf = NULL;
if ((bdrv_flags & BDRV_O_NOCACHE)) {
- s->aligned_buf = qemu_blockalign(bs, ALIGNED_BUFFER_SIZE);
+ /*
+ * Allocate a buffer for read/modify/write cycles. Chose the size
+ * pessimistically as we don't know the block size yet.
+ */
+ s->aligned_buf_size = 32 * MAX_BLOCKSIZE;
+ s->aligned_buf = qemu_memalign(MAX_BLOCKSIZE, s->aligned_buf_size);
if (s->aligned_buf == NULL) {
goto out_close;
}
@@ -216,7 +223,7 @@ static int raw_open(BlockDriverState *bs, const char *filename, int flags)
}
#endif
#ifdef CONFIG_COCOA
- u_int32_t blockSize = 512;
+ uint32_t blockSize = 512;
if ( !ioctl( fd, DKIOCGETBLOCKSIZE, &blockSize ) && blockSize > bufsize) {
bufsize = blockSize;
}
@@ -277,8 +284,9 @@ static int raw_pread_aligned(BlockDriverState *bs, int64_t offset,
}
/*
- * offset and count are in bytes, but must be multiples of 512 for files
- * opened with O_DIRECT. buf must be aligned to 512 bytes then.
+ * offset and count are in bytes, but must be multiples of the sector size
+ * for files opened with O_DIRECT. buf must be aligned to sector size bytes
+ * then.
*
* This function may be called without alignment if the caller ensures
* that O_DIRECT is not in effect.
@@ -315,24 +323,25 @@ static int raw_pread(BlockDriverState *bs, int64_t offset,
uint8_t *buf, int count)
{
BDRVRawState *s = bs->opaque;
+ unsigned sector_mask = bs->buffer_alignment - 1;
int size, ret, shift, sum;
sum = 0;
if (s->aligned_buf != NULL) {
- if (offset & 0x1ff) {
- /* align offset on a 512 bytes boundary */
+ if (offset & sector_mask) {
+ /* align offset on a sector size bytes boundary */
- shift = offset & 0x1ff;
- size = (shift + count + 0x1ff) & ~0x1ff;
- if (size > ALIGNED_BUFFER_SIZE)
- size = ALIGNED_BUFFER_SIZE;
+ shift = offset & sector_mask;
+ size = (shift + count + sector_mask) & ~sector_mask;
+ if (size > s->aligned_buf_size)
+ size = s->aligned_buf_size;
ret = raw_pread_aligned(bs, offset - shift, s->aligned_buf, size);
if (ret < 0)
return ret;
- size = 512 - shift;
+ size = bs->buffer_alignment - shift;
if (size > count)
size = count;
memcpy(buf, s->aligned_buf + shift, size);
@@ -345,15 +354,15 @@ static int raw_pread(BlockDriverState *bs, int64_t offset,
if (count == 0)
return sum;
}
- if (count & 0x1ff || (uintptr_t) buf & 0x1ff) {
+ if (count & sector_mask || (uintptr_t) buf & sector_mask) {
/* read on aligned buffer */
while (count) {
- size = (count + 0x1ff) & ~0x1ff;
- if (size > ALIGNED_BUFFER_SIZE)
- size = ALIGNED_BUFFER_SIZE;
+ size = (count + sector_mask) & ~sector_mask;
+ if (size > s->aligned_buf_size)
+ size = s->aligned_buf_size;
ret = raw_pread_aligned(bs, offset, s->aligned_buf, size);
if (ret < 0) {
@@ -403,25 +412,28 @@ static int raw_pwrite(BlockDriverState *bs, int64_t offset,
const uint8_t *buf, int count)
{
BDRVRawState *s = bs->opaque;
+ unsigned sector_mask = bs->buffer_alignment - 1;
int size, ret, shift, sum;
sum = 0;
if (s->aligned_buf != NULL) {
- if (offset & 0x1ff) {
- /* align offset on a 512 bytes boundary */
- shift = offset & 0x1ff;
- ret = raw_pread_aligned(bs, offset - shift, s->aligned_buf, 512);
+ if (offset & sector_mask) {
+ /* align offset on a sector size bytes boundary */
+ shift = offset & sector_mask;
+ ret = raw_pread_aligned(bs, offset - shift, s->aligned_buf,
+ bs->buffer_alignment);
if (ret < 0)
return ret;
- size = 512 - shift;
+ size = bs->buffer_alignment - shift;
if (size > count)
size = count;
memcpy(s->aligned_buf + shift, buf, size);
- ret = raw_pwrite_aligned(bs, offset - shift, s->aligned_buf, 512);
+ ret = raw_pwrite_aligned(bs, offset - shift, s->aligned_buf,
+ bs->buffer_alignment);
if (ret < 0)
return ret;
@@ -433,12 +445,12 @@ static int raw_pwrite(BlockDriverState *bs, int64_t offset,
if (count == 0)
return sum;
}
- if (count & 0x1ff || (uintptr_t) buf & 0x1ff) {
+ if (count & sector_mask || (uintptr_t) buf & sector_mask) {
- while ((size = (count & ~0x1ff)) != 0) {
+ while ((size = (count & ~sector_mask)) != 0) {
- if (size > ALIGNED_BUFFER_SIZE)
- size = ALIGNED_BUFFER_SIZE;
+ if (size > s->aligned_buf_size)
+ size = s->aligned_buf_size;
memcpy(s->aligned_buf, buf, size);
@@ -451,14 +463,16 @@ static int raw_pwrite(BlockDriverState *bs, int64_t offset,
count -= ret;
sum += ret;
}
- /* here, count < 512 because (count & ~0x1ff) == 0 */
+ /* here, count < 512 because (count & ~sector_mask) == 0 */
if (count) {
- ret = raw_pread_aligned(bs, offset, s->aligned_buf, 512);
+ ret = raw_pread_aligned(bs, offset, s->aligned_buf,
+ bs->buffer_alignment);
if (ret < 0)
return ret;
memcpy(s->aligned_buf, buf, count);
- ret = raw_pwrite_aligned(bs, offset, s->aligned_buf, 512);
+ ret = raw_pwrite_aligned(bs, offset, s->aligned_buf,
+ bs->buffer_alignment);
if (ret < 0)
return ret;
if (count < ret)
@@ -486,12 +500,12 @@ static int raw_write(BlockDriverState *bs, int64_t sector_num,
/*
* Check if all memory in this vector is sector aligned.
*/
-static int qiov_is_aligned(QEMUIOVector *qiov)
+static int qiov_is_aligned(BlockDriverState *bs, QEMUIOVector *qiov)
{
int i;
for (i = 0; i < qiov->niov; i++) {
- if ((uintptr_t) qiov->iov[i].iov_base % BDRV_SECTOR_SIZE) {
+ if ((uintptr_t) qiov->iov[i].iov_base % bs->buffer_alignment) {
return 0;
}
}
@@ -514,7 +528,7 @@ static BlockDriverAIOCB *raw_aio_submit(BlockDriverState *bs,
* driver that it needs to copy the buffer.
*/
if (s->aligned_buf) {
- if (!qiov_is_aligned(qiov)) {
+ if (!qiov_is_aligned(bs, qiov)) {
type |= QEMU_AIO_MISALIGNED;
#ifdef CONFIG_LINUX_AIO
} else if (s->use_aio) {
@@ -868,8 +882,13 @@ static int hdev_open(BlockDriverState *bs, const char *filename, int flags)
s->type = FTYPE_FILE;
#if defined(__linux__)
- if (strstart(filename, "/dev/sg", NULL)) {
- bs->sg = 1;
+ {
+ char resolved_path[ MAXPATHLEN ], *temp;
+
+ temp = realpath(filename, resolved_path);
+ if (temp && strstart(temp, "/dev/sg", NULL)) {
+ bs->sg = 1;
+ }
}
#endif
@@ -889,7 +908,7 @@ static int fd_open(BlockDriverState *bs)
return 0;
last_media_present = (s->fd >= 0);
if (s->fd >= 0 &&
- (qemu_get_clock(rt_clock) - s->fd_open_time) >= FD_OPEN_TIMEOUT) {
+ (get_clock() - s->fd_open_time) >= FD_OPEN_TIMEOUT) {
close(s->fd);
s->fd = -1;
#ifdef DEBUG_FLOPPY
@@ -898,7 +917,7 @@ static int fd_open(BlockDriverState *bs)
}
if (s->fd < 0) {
if (s->fd_got_error &&
- (qemu_get_clock(rt_clock) - s->fd_error_time) < FD_OPEN_TIMEOUT) {
+ (get_clock() - s->fd_error_time) < FD_OPEN_TIMEOUT) {
#ifdef DEBUG_FLOPPY
printf("No floppy (open delayed)\n");
#endif
@@ -906,7 +925,7 @@ static int fd_open(BlockDriverState *bs)
}
s->fd = open(bs->filename, s->open_flags & ~O_NONBLOCK);
if (s->fd < 0) {
- s->fd_error_time = qemu_get_clock(rt_clock);
+ s->fd_error_time = get_clock();
s->fd_got_error = 1;
if (last_media_present)
s->fd_media_changed = 1;
@@ -921,7 +940,7 @@ static int fd_open(BlockDriverState *bs)
}
if (!last_media_present)
s->fd_media_changed = 1;
- s->fd_open_time = qemu_get_clock(rt_clock);
+ s->fd_open_time = get_clock();
s->fd_got_error = 0;
return 0;
}
@@ -993,6 +1012,11 @@ static int hdev_create(const char *filename, QEMUOptionParameter *options)
return ret;
}
+static int hdev_has_zero_init(BlockDriverState *bs)
+{
+ return 0;
+}
+
static BlockDriver bdrv_host_device = {
.format_name = "host_device",
.protocol_name = "host_device",
@@ -1002,7 +1026,7 @@ static BlockDriver bdrv_host_device = {
.bdrv_close = raw_close,
.bdrv_create = hdev_create,
.create_options = raw_create_options,
- .no_zero_init = 1,
+ .bdrv_has_zero_init = hdev_has_zero_init,
.bdrv_flush = raw_flush,
.bdrv_aio_readv = raw_aio_readv,
@@ -1117,7 +1141,7 @@ static BlockDriver bdrv_host_floppy = {
.bdrv_close = raw_close,
.bdrv_create = hdev_create,
.create_options = raw_create_options,
- .no_zero_init = 1,
+ .bdrv_has_zero_init = hdev_has_zero_init,
.bdrv_flush = raw_flush,
.bdrv_aio_readv = raw_aio_readv,
@@ -1149,9 +1173,6 @@ static int cdrom_probe_device(const char *filename)
int fd, ret;
int prio = 0;
- if (strstart(filename, "/dev/cd", NULL))
- prio = 50;
-
fd = open(filename, O_RDONLY | O_NONBLOCK);
if (fd < 0) {
goto out;
@@ -1217,7 +1238,7 @@ static BlockDriver bdrv_host_cdrom = {
.bdrv_close = raw_close,
.bdrv_create = hdev_create,
.create_options = raw_create_options,
- .no_zero_init = 1,
+ .bdrv_has_zero_init = hdev_has_zero_init,
.bdrv_flush = raw_flush,
.bdrv_aio_readv = raw_aio_readv,
@@ -1340,7 +1361,7 @@ static BlockDriver bdrv_host_cdrom = {
.bdrv_close = raw_close,
.bdrv_create = hdev_create,
.create_options = raw_create_options,
- .no_zero_init = 1,
+ .bdrv_has_zero_init = hdev_has_zero_init,
.bdrv_flush = raw_flush,
.bdrv_aio_readv = raw_aio_readv,
diff --git a/block/raw-win32.c b/block/raw-win32.c
index 745bbde673..503ed3959a 100644
--- a/block/raw-win32.c
+++ b/block/raw-win32.c
@@ -394,6 +394,11 @@ static int raw_set_locked(BlockDriverState *bs, int locked)
}
#endif
+static int hdev_has_zero_init(BlockDriverState *bs)
+{
+ return 0;
+}
+
static BlockDriver bdrv_host_device = {
.format_name = "host_device",
.protocol_name = "host_device",
@@ -402,6 +407,7 @@ static BlockDriver bdrv_host_device = {
.bdrv_file_open = hdev_open,
.bdrv_close = raw_close,
.bdrv_flush = raw_flush,
+ .bdrv_has_zero_init = hdev_has_zero_init,
.bdrv_read = raw_read,
.bdrv_write = raw_write,
diff --git a/block/raw.c b/block/raw.c
index 4406b8c06b..91087792fc 100644
--- a/block/raw.c
+++ b/block/raw.c
@@ -107,6 +107,11 @@ static QEMUOptionParameter raw_create_options[] = {
{ NULL }
};
+static int raw_has_zero_init(BlockDriverState *bs)
+{
+ return bdrv_has_zero_init(bs->file);
+}
+
static BlockDriver bdrv_raw = {
.format_name = "raw",
@@ -134,6 +139,7 @@ static BlockDriver bdrv_raw = {
.bdrv_create = raw_create,
.create_options = raw_create_options,
+ .bdrv_has_zero_init = raw_has_zero_init,
};
static void bdrv_raw_init(void)
diff --git a/block/sheepdog.c b/block/sheepdog.c
index 81aa564f26..e62820a804 100644
--- a/block/sheepdog.c
+++ b/block/sheepdog.c
@@ -8,16 +8,6 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifdef _WIN32
-#include <windows.h>
-#include <winsock2.h>
-#include <ws2tcpip.h>
-#else
-#include <netdb.h>
-#include <netinet/tcp.h>
-
-#define closesocket(s) close(s)
-#endif
#include "qemu-common.h"
#include "qemu-error.h"
diff --git a/block/vvfat.c b/block/vvfat.c
index 6d61c2e6c3..26dd474bb5 100644
--- a/block/vvfat.c
+++ b/block/vvfat.c
@@ -512,7 +512,7 @@ static inline uint8_t fat_chksum(const direntry_t* entry)
for(i=0;i<11;i++) {
unsigned char c;
- c = (i <= 8) ? entry->name[i] : entry->extension[i-8];
+ c = (i < 8) ? entry->name[i] : entry->extension[i-8];
chksum=(((chksum&0xfe)>>1)|((chksum&0x01)?0x80:0)) + c;
}
@@ -2282,7 +2282,6 @@ static void check1(BDRVVVFATState* s)
fprintf(stderr, "deleted\n");
continue;
}
- assert(mapping->dir_index >= 0);
assert(mapping->dir_index < s->directory.next);
direntry_t* direntry = array_get(&(s->directory), mapping->dir_index);
assert(mapping->begin == begin_of_direntry(direntry) || mapping->first_mapping_index >= 0);
@@ -2665,6 +2664,11 @@ static int vvfat_write(BlockDriverState *bs, int64_t sector_num,
DLOG(checkpoint());
+ /* Check if we're operating in read-only mode */
+ if (s->qcow == NULL) {
+ return -EACCES;
+ }
+
vvfat_close_current_file(s);
/*
@@ -2763,12 +2767,12 @@ static int vvfat_is_allocated(BlockDriverState *bs,
static int write_target_commit(BlockDriverState *bs, int64_t sector_num,
const uint8_t* buffer, int nb_sectors) {
- BDRVVVFATState* s = bs->opaque;
+ BDRVVVFATState* s = *((BDRVVVFATState**) bs->opaque);
return try_commit(s);
}
static void write_target_close(BlockDriverState *bs) {
- BDRVVVFATState* s = bs->opaque;
+ BDRVVVFATState* s = *((BDRVVVFATState**) bs->opaque);
bdrv_delete(s->qcow);
free(s->qcow_filename);
}
@@ -2783,6 +2787,7 @@ static int enable_write_target(BDRVVVFATState *s)
{
BlockDriver *bdrv_qcow;
QEMUOptionParameter *options;
+ int ret;
int size = sector2cluster(s, s->sector_count);
s->used_clusters = calloc(size, 1);
@@ -2798,11 +2803,16 @@ static int enable_write_target(BDRVVVFATState *s)
if (bdrv_create(bdrv_qcow, s->qcow_filename, options) < 0)
return -1;
+
s->qcow = bdrv_new("");
- if (s->qcow == NULL ||
- bdrv_open(s->qcow, s->qcow_filename, BDRV_O_RDWR, bdrv_qcow) < 0)
- {
- return -1;
+ if (s->qcow == NULL) {
+ return -1;
+ }
+
+ ret = bdrv_open(s->qcow, s->qcow_filename,
+ BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH, bdrv_qcow);
+ if (ret < 0) {
+ return ret;
}
#ifndef _WIN32
@@ -2811,7 +2821,8 @@ static int enable_write_target(BDRVVVFATState *s)
s->bs->backing_hd = calloc(sizeof(BlockDriverState), 1);
s->bs->backing_hd->drv = &vvfat_write_target;
- s->bs->backing_hd->opaque = s;
+ s->bs->backing_hd->opaque = qemu_malloc(sizeof(void*));
+ *(void**)s->bs->backing_hd->opaque = s;
return 0;
}
diff --git a/block_int.h b/block_int.h
index 877e1e5943..e8e7156c92 100644
--- a/block_int.h
+++ b/block_int.h
@@ -127,8 +127,11 @@ struct BlockDriver {
void (*bdrv_debug_event)(BlockDriverState *bs, BlkDebugEvent event);
- /* Set if newly created images are not guaranteed to contain only zeros */
- int no_zero_init;
+ /*
+ * Returns 1 if newly created images are guaranteed to contain only
+ * zeros, 0 otherwise.
+ */
+ int (*bdrv_has_zero_init)(BlockDriverState *bs);
QLIST_ENTRY(BlockDriver) list;
};
@@ -141,6 +144,7 @@ struct BlockDriverState {
int open_flags; /* flags used to open the file, re-used for re-open */
int removable; /* if true, the media can be removed */
int locked; /* if true, the media cannot temporarily be ejected */
+ int tray_open; /* if true, the virtual tray is open */
int encrypted; /* if true, the media is encrypted */
int valid_key; /* if true, a valid encryption key has been set */
int sg; /* if true, the device is a /dev/sg* */
@@ -242,7 +246,7 @@ static inline unsigned int get_physical_block_exp(BlockConf *conf)
_conf.logical_block_size, 512), \
DEFINE_PROP_UINT16("physical_block_size", _state, \
_conf.physical_block_size, 512), \
- DEFINE_PROP_UINT16("min_io_size", _state, _conf.min_io_size, 512), \
- DEFINE_PROP_UINT32("opt_io_size", _state, _conf.opt_io_size, 512)
+ DEFINE_PROP_UINT16("min_io_size", _state, _conf.min_io_size, 0), \
+ DEFINE_PROP_UINT32("opt_io_size", _state, _conf.opt_io_size, 0)
#endif /* BLOCK_INT_H */
diff --git a/blockdev.c b/blockdev.c
index 0a9dec364e..ff7602be2c 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -50,7 +50,7 @@ QemuOpts *drive_add(const char *file, const char *fmt, ...)
vsnprintf(optstr, sizeof(optstr), fmt, ap);
va_end(ap);
- opts = qemu_opts_parse(&qemu_drive_opts, optstr, 0);
+ opts = qemu_opts_parse(qemu_find_opts("drive"), optstr, 0);
if (!opts) {
return NULL;
}
@@ -451,7 +451,7 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi, int *fatal_error)
break;
case IF_VIRTIO:
/* add virtio block device */
- opts = qemu_opts_create(&qemu_device_opts, NULL, 0);
+ opts = qemu_opts_create(qemu_find_opts("device"), NULL, 0);
qemu_opt_set(opts, "driver", "virtio-blk-pci");
qemu_opt_set(opts, "drive", dinfo->id);
if (devaddr)
@@ -590,6 +590,7 @@ int do_change_block(Monitor *mon, const char *device,
return -1;
}
bdrv_flags = bdrv_is_read_only(bs) ? 0 : BDRV_O_RDWR;
+ bdrv_flags |= bdrv_is_snapshot(bs) ? BDRV_O_SNAPSHOT : 0;
if (bdrv_open(bs, filename, bdrv_flags, drv) < 0) {
qerror_report(QERR_OPEN_FILE_FAILED, filename);
return -1;
diff --git a/blockdev.h b/blockdev.h
index 37f3a017ea..653affcc9b 100644
--- a/blockdev.h
+++ b/blockdev.h
@@ -16,15 +16,9 @@
void blockdev_mark_auto_del(BlockDriverState *bs);
void blockdev_auto_del(BlockDriverState *bs);
-typedef enum {
- IF_NONE,
- IF_IDE, IF_SCSI, IF_FLOPPY, IF_PFLASH, IF_MTD, IF_SD, IF_VIRTIO, IF_XEN,
- IF_COUNT
-} BlockInterfaceType;
-
#define BLOCK_SERIAL_STRLEN 20
-typedef struct DriveInfo {
+struct DriveInfo {
BlockDriverState *bdrv;
char *id;
const char *devaddr;
@@ -35,19 +29,18 @@ typedef struct DriveInfo {
QemuOpts *opts;
char serial[BLOCK_SERIAL_STRLEN + 1];
QTAILQ_ENTRY(DriveInfo) next;
-} DriveInfo;
+};
#define MAX_IDE_DEVS 2
#define MAX_SCSI_DEVS 7
-extern DriveInfo *drive_get(BlockInterfaceType type, int bus, int unit);
-extern int drive_get_max_bus(BlockInterfaceType type);
-extern void drive_uninit(DriveInfo *dinfo);
-extern DriveInfo *drive_get_by_blockdev(BlockDriverState *bs);
+DriveInfo *drive_get(BlockInterfaceType type, int bus, int unit);
+int drive_get_max_bus(BlockInterfaceType type);
+void drive_uninit(DriveInfo *dinfo);
+DriveInfo *drive_get_by_blockdev(BlockDriverState *bs);
-extern QemuOpts *drive_add(const char *file, const char *fmt, ...);
-extern DriveInfo *drive_init(QemuOpts *arg, int default_to_scsi,
- int *fatal_error);
+QemuOpts *drive_add(const char *file, const char *fmt, ...) GCC_FMT_ATTR(2, 3);
+DriveInfo *drive_init(QemuOpts *arg, int default_to_scsi, int *fatal_error);
/* device-hotplug */
diff --git a/bsd-user/main.c b/bsd-user/main.c
index aff9f13b18..6b12f8bba1 100644
--- a/bsd-user/main.c
+++ b/bsd-user/main.c
@@ -795,6 +795,12 @@ int main(int argc, char **argv)
r = argv[optind++];
if (envlist_setenv(envlist, r) != 0)
usage();
+ } else if (!strcmp(r, "ignore-environment")) {
+ envlist_free(envlist);
+ if ((envlist = envlist_create()) == NULL) {
+ (void) fprintf(stderr, "Unable to allocate envlist\n");
+ exit(1);
+ }
} else if (!strcmp(r, "U")) {
r = argv[optind++];
if (envlist_unsetenv(envlist, r) != 0)
diff --git a/bsd-user/qemu.h b/bsd-user/qemu.h
index 554ff8b0ee..976361622d 100644
--- a/bsd-user/qemu.h
+++ b/bsd-user/qemu.h
@@ -139,7 +139,7 @@ abi_long do_netbsd_syscall(void *cpu_env, int num, abi_long arg1,
abi_long do_openbsd_syscall(void *cpu_env, int num, abi_long arg1,
abi_long arg2, abi_long arg3, abi_long arg4,
abi_long arg5, abi_long arg6);
-void gemu_log(const char *fmt, ...) __attribute__((format(printf,1,2)));
+void gemu_log(const char *fmt, ...) GCC_FMT_ATTR(1, 2);
extern THREAD CPUState *thread_env;
void cpu_loop(CPUState *env);
char *target_strerror(int err);
diff --git a/buffered_file.c b/buffered_file.c
index 54dc6c29ba..1836e7e242 100644
--- a/buffered_file.c
+++ b/buffered_file.c
@@ -156,6 +156,14 @@ static int buffered_put_buffer(void *opaque, const uint8_t *buf, int64_t pos, in
offset = size;
}
+ if (pos == 0 && size == 0) {
+ DPRINTF("file is ready\n");
+ if (s->bytes_xfer <= s->xfer_limit) {
+ DPRINTF("notifying client\n");
+ s->put_ready(s->opaque);
+ }
+ }
+
return offset;
}
@@ -222,8 +230,10 @@ static void buffered_rate_tick(void *opaque)
{
QEMUFileBuffered *s = opaque;
- if (s->has_error)
+ if (s->has_error) {
+ buffered_close(s);
return;
+ }
qemu_mod_timer(s->timer, qemu_get_clock(rt_clock) + 100);
diff --git a/compatfd.c b/compatfd.c
new file mode 100644
index 0000000000..a7cebc4867
--- /dev/null
+++ b/compatfd.c
@@ -0,0 +1,117 @@
+/*
+ * signalfd/eventfd compatibility
+ *
+ * Copyright IBM, Corp. 2008
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#include "qemu-common.h"
+#include "compatfd.h"
+
+#include <sys/syscall.h>
+#include <pthread.h>
+
+struct sigfd_compat_info
+{
+ sigset_t mask;
+ int fd;
+};
+
+static void *sigwait_compat(void *opaque)
+{
+ struct sigfd_compat_info *info = opaque;
+ int err;
+ sigset_t all;
+
+ sigfillset(&all);
+ sigprocmask(SIG_BLOCK, &all, NULL);
+
+ do {
+ siginfo_t siginfo;
+
+ err = sigwaitinfo(&info->mask, &siginfo);
+ if (err == -1 && errno == EINTR) {
+ err = 0;
+ continue;
+ }
+
+ if (err > 0) {
+ char buffer[128];
+ size_t offset = 0;
+
+ memcpy(buffer, &err, sizeof(err));
+ while (offset < sizeof(buffer)) {
+ ssize_t len;
+
+ len = write(info->fd, buffer + offset,
+ sizeof(buffer) - offset);
+ if (len == -1 && errno == EINTR)
+ continue;
+
+ if (len <= 0) {
+ err = -1;
+ break;
+ }
+
+ offset += len;
+ }
+ }
+ } while (err >= 0);
+
+ return NULL;
+}
+
+static int qemu_signalfd_compat(const sigset_t *mask)
+{
+ pthread_attr_t attr;
+ pthread_t tid;
+ struct sigfd_compat_info *info;
+ int fds[2];
+
+ info = malloc(sizeof(*info));
+ if (info == NULL) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ if (pipe(fds) == -1) {
+ free(info);
+ return -1;
+ }
+
+ qemu_set_cloexec(fds[0]);
+ qemu_set_cloexec(fds[1]);
+
+ memcpy(&info->mask, mask, sizeof(*mask));
+ info->fd = fds[1];
+
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+
+ pthread_create(&tid, &attr, sigwait_compat, info);
+
+ pthread_attr_destroy(&attr);
+
+ return fds[0];
+}
+
+int qemu_signalfd(const sigset_t *mask)
+{
+#if defined(CONFIG_SIGNALFD)
+ int ret;
+
+ ret = syscall(SYS_signalfd, -1, mask, _NSIG / 8);
+ if (ret != -1) {
+ qemu_set_cloexec(ret);
+ return ret;
+ }
+#endif
+
+ return qemu_signalfd_compat(mask);
+}
diff --git a/compatfd.h b/compatfd.h
new file mode 100644
index 0000000000..fc3791520f
--- /dev/null
+++ b/compatfd.h
@@ -0,0 +1,43 @@
+/*
+ * signalfd/eventfd compatibility
+ *
+ * Copyright IBM, Corp. 2008
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#ifndef QEMU_COMPATFD_H
+#define QEMU_COMPATFD_H
+
+#include <signal.h>
+
+struct qemu_signalfd_siginfo {
+ uint32_t ssi_signo; /* Signal number */
+ int32_t ssi_errno; /* Error number (unused) */
+ int32_t ssi_code; /* Signal code */
+ uint32_t ssi_pid; /* PID of sender */
+ uint32_t ssi_uid; /* Real UID of sender */
+ int32_t ssi_fd; /* File descriptor (SIGIO) */
+ uint32_t ssi_tid; /* Kernel timer ID (POSIX timers) */
+ uint32_t ssi_band; /* Band event (SIGIO) */
+ uint32_t ssi_overrun; /* POSIX timer overrun count */
+ uint32_t ssi_trapno; /* Trap number that caused signal */
+ int32_t ssi_status; /* Exit status or signal (SIGCHLD) */
+ int32_t ssi_int; /* Integer sent by sigqueue(2) */
+ uint64_t ssi_ptr; /* Pointer sent by sigqueue(2) */
+ uint64_t ssi_utime; /* User CPU time consumed (SIGCHLD) */
+ uint64_t ssi_stime; /* System CPU time consumed (SIGCHLD) */
+ uint64_t ssi_addr; /* Address that generated signal
+ (for hardware-generated signals) */
+ uint8_t pad[48]; /* Pad size to 128 bytes (allow for
+ additional fields in the future) */
+};
+
+int qemu_signalfd(const sigset_t *mask);
+
+#endif
diff --git a/configure b/configure
index b68f01a665..f62c1feb88 100755
--- a/configure
+++ b/configure
@@ -15,16 +15,21 @@ TMPC="${TMPDIR1}/qemu-conf-${RANDOM}-$$-${RANDOM}.c"
TMPO="${TMPDIR1}/qemu-conf-${RANDOM}-$$-${RANDOM}.o"
TMPE="${TMPDIR1}/qemu-conf-${RANDOM}-$$-${RANDOM}.exe"
-trap "rm -f $TMPC $TMPO $TMPE ; exit" EXIT INT QUIT TERM
+# NB: do not call "exit" in the trap handler; this is buggy with some shells;
+# see <1285349658-3122-1-git-send-email-loic.minier@linaro.org>
+trap "rm -f $TMPC $TMPO $TMPE" EXIT INT QUIT TERM
+rm -f config.log
compile_object() {
- $cc $QEMU_CFLAGS -c -o $TMPO $TMPC > /dev/null 2> /dev/null
+ echo $cc $QEMU_CFLAGS -c -o $TMPO $TMPC >> config.log
+ $cc $QEMU_CFLAGS -c -o $TMPO $TMPC >> config.log 2>&1
}
compile_prog() {
local_cflags="$1"
local_ldflags="$2"
- $cc $QEMU_CFLAGS $local_cflags -o $TMPE $TMPC $LDFLAGS $local_ldflags > /dev/null 2> /dev/null
+ echo $cc $QEMU_CFLAGS $local_cflags -o $TMPE $TMPC $LDFLAGS $local_ldflags >> config.log
+ $cc $QEMU_CFLAGS $local_cflags -o $TMPE $TMPC $LDFLAGS $local_ldflags >> config.log 2>&1
}
# check whether a command is available to this shell (may be either an
@@ -80,11 +85,14 @@ make="make"
install="install"
objcopy="objcopy"
ld="ld"
+strip="strip"
+windres="windres"
helper_cflags=""
libs_softmmu=""
libs_tools=""
audio_pt_int=""
audio_win_int=""
+cc_i386=i386-pc-linux-gnu-gcc
# parse CC options first
for opt do
@@ -125,6 +133,8 @@ cc="${cross_prefix}${cc}"
ar="${cross_prefix}${ar}"
objcopy="${cross_prefix}${objcopy}"
ld="${cross_prefix}${ld}"
+strip="${cross_prefix}${strip}"
+windres="${cross_prefix}${windres}"
# default flags for all hosts
QEMU_CFLAGS="-fno-strict-aliasing $QEMU_CFLAGS"
@@ -136,13 +146,16 @@ QEMU_CFLAGS="-D_FORTIFY_SOURCE=2 $QEMU_CFLAGS"
QEMU_CFLAGS="-I. -I\$(SRC_PATH) $QEMU_CFLAGS"
LDFLAGS="-g $LDFLAGS"
-gcc_flags="-Wold-style-declaration -Wold-style-definition -fstack-protector-all"
+gcc_flags="-Wold-style-declaration -Wold-style-definition -Wtype-limits"
+gcc_flags="-Wformat-security -Wformat-y2k -Winit-self -Wignored-qualifiers $gcc_flags"
+gcc_flags="-Wmissing-include-dirs -Wempty-body -Wnested-externs $gcc_flags"
+gcc_flags="-fstack-protector-all $gcc_flags"
cat > $TMPC << EOF
int main(void) { return 0; }
EOF
for flag in $gcc_flags; do
if compile_prog "-Werror $QEMU_CFLAGS" "-Werror $flag" ; then
- QEMU_CFLAGS="$flag $QEMU_CFLAGS"
+ QEMU_CFLAGS="$QEMU_CFLAGS $flag"
fi
done
@@ -268,6 +281,9 @@ uuid=""
vde=""
vnc_tls=""
vnc_sasl=""
+vnc_jpeg=""
+vnc_png=""
+vnc_thread="no"
xen=""
linux_aio=""
attr=""
@@ -307,11 +323,15 @@ io_thread="no"
mixemu="no"
kerneldir=""
aix="no"
+haiku="no"
blobs="yes"
pkgversion=""
check_utests="no"
user_pie="no"
zero_malloc=""
+trace_backend="nop"
+trace_file="trace"
+spice=""
# OS specific
if check_define __linux__ ; then
@@ -322,6 +342,8 @@ elif check_define __OpenBSD__ ; then
targetos='OpenBSD'
elif check_define __sun__ ; then
targetos='SunOS'
+elif check_define __HAIKU__ ; then
+ targetos='Haiku'
else
targetos=`uname -s`
fi
@@ -437,6 +459,11 @@ AIX)
aix="yes"
make="gmake"
;;
+Haiku)
+ haiku="yes"
+ QEMU_CFLAGS="-DB_USE_POSITIVE_POSIX_ERRORS $QEMU_CFLAGS"
+ LIBS="-lposix_error_mapper -lnetwork $LIBS"
+;;
*)
audio_drv_list="oss"
audio_possible_drivers="oss alsa sdl esd pa"
@@ -514,6 +541,10 @@ for opt do
;;
--target-list=*) target_list="$optarg"
;;
+ --trace-backend=*) trace_backend="$optarg"
+ ;;
+ --trace-file=*) trace_file="$optarg"
+ ;;
--enable-gprof) gprof="yes"
;;
--static)
@@ -575,6 +606,18 @@ for opt do
;;
--enable-vnc-sasl) vnc_sasl="yes"
;;
+ --disable-vnc-jpeg) vnc_jpeg="no"
+ ;;
+ --enable-vnc-jpeg) vnc_jpeg="yes"
+ ;;
+ --disable-vnc-png) vnc_png="no"
+ ;;
+ --enable-vnc-png) vnc_png="yes"
+ ;;
+ --disable-vnc-thread) vnc_thread="no"
+ ;;
+ --enable-vnc-thread) vnc_thread="yes"
+ ;;
--disable-slirp) slirp="no"
;;
--disable-uuid) uuid="no"
@@ -601,6 +644,10 @@ for opt do
;;
--enable-kvm) kvm="yes"
;;
+ --disable-spice) spice="no"
+ ;;
+ --enable-spice) spice="yes"
+ ;;
--enable-profiler) profiler="yes"
;;
--enable-cocoa)
@@ -743,12 +790,14 @@ case "$cpu" in
i386)
QEMU_CFLAGS="-m32 $QEMU_CFLAGS"
LDFLAGS="-m32 $LDFLAGS"
+ cc_i386='$(CC) -m32'
helper_cflags="-fomit-frame-pointer"
host_guest_base="yes"
;;
x86_64)
QEMU_CFLAGS="-m64 $QEMU_CFLAGS"
LDFLAGS="-m64 $LDFLAGS"
+ cc_i386='$(CC) -m32'
host_guest_base="yes"
;;
arm*)
@@ -825,6 +874,12 @@ echo " --disable-vnc-tls disable TLS encryption for VNC server"
echo " --enable-vnc-tls enable TLS encryption for VNC server"
echo " --disable-vnc-sasl disable SASL encryption for VNC server"
echo " --enable-vnc-sasl enable SASL encryption for VNC server"
+echo " --disable-vnc-jpeg disable JPEG lossy compression for VNC server"
+echo " --enable-vnc-jpeg enable JPEG lossy compression for VNC server"
+echo " --disable-vnc-png disable PNG compression for VNC server (default)"
+echo " --enable-vnc-png enable PNG compression for VNC server"
+echo " --disable-vnc-thread disable threaded VNC server"
+echo " --enable-vnc-thread enable threaded VNC server"
echo " --disable-curses disable curses output"
echo " --enable-curses enable curses output"
echo " --disable-curl disable curl connectivity"
@@ -874,6 +929,11 @@ echo " --enable-docs enable documentation build"
echo " --disable-docs disable documentation build"
echo " --disable-vhost-net disable vhost-net acceleration support"
echo " --enable-vhost-net enable vhost-net acceleration support"
+echo " --trace-backend=B Trace backend nop simple ust"
+echo " --trace-file=NAME Full PATH,NAME of file to store traces"
+echo " Default:trace-<pid>"
+echo " --disable-spice disable spice"
+echo " --enable-spice enable spice"
echo ""
echo "NOTE: The object files are built at the place where configure is launched"
exit 1
@@ -1246,6 +1306,53 @@ EOF
fi
##########################################
+# VNC JPEG detection
+if test "$vnc_jpeg" != "no" ; then
+cat > $TMPC <<EOF
+#include <stdio.h>
+#include <jpeglib.h>
+int main(void) { struct jpeg_compress_struct s; jpeg_create_compress(&s); return 0; }
+EOF
+ vnc_jpeg_cflags=""
+ vnc_jpeg_libs="-ljpeg"
+ if compile_prog "$vnc_jpeg_cflags" "$vnc_jpeg_libs" ; then
+ vnc_jpeg=yes
+ libs_softmmu="$vnc_jpeg_libs $libs_softmmu"
+ else
+ if test "$vnc_jpeg" = "yes" ; then
+ feature_not_found "vnc-jpeg"
+ fi
+ vnc_jpeg=no
+ fi
+fi
+
+##########################################
+# VNC PNG detection
+if test "$vnc_png" != "no" ; then
+cat > $TMPC <<EOF
+//#include <stdio.h>
+#include <png.h>
+#include <stddef.h>
+int main(void) {
+ png_structp png_ptr;
+ png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ return 0;
+}
+EOF
+ vnc_png_cflags=""
+ vnc_png_libs="-lpng"
+ if compile_prog "$vnc_png_cflags" "$vnc_png_libs" ; then
+ vnc_png=yes
+ libs_softmmu="$vnc_png_libs $libs_softmmu"
+ else
+ if test "$vnc_png" = "yes" ; then
+ feature_not_found "vnc-png"
+ fi
+ vnc_png=no
+ fi
+fi
+
+##########################################
# fnmatch() probe, used for ACL routines
fnmatch="no"
cat > $TMPC << EOF
@@ -1410,6 +1517,7 @@ if test "$brlapi" != "no" ; then
brlapi_libs="-lbrlapi"
cat > $TMPC << EOF
#include <brlapi.h>
+#include <stddef.h>
int main( void ) { return brlapi__openConnection (NULL, NULL, NULL); }
EOF
if compile_prog "" "$brlapi_libs" ; then
@@ -1617,13 +1725,17 @@ cat > $TMPC << EOF
#include <pthread.h>
int main(void) { pthread_create(0,0,0,0); return 0; }
EOF
-for pthread_lib in $PTHREADLIBS_LIST; do
- if compile_prog "" "$pthread_lib" ; then
- pthread=yes
- LIBS="$pthread_lib $LIBS"
- break
- fi
-done
+if compile_prog "" "" ; then
+ pthread=yes
+else
+ for pthread_lib in $PTHREADLIBS_LIST; do
+ if compile_prog "" "$pthread_lib" ; then
+ pthread=yes
+ LIBS="$pthread_lib $LIBS"
+ break
+ fi
+ done
+fi
if test "$mingw32" != yes -a "$pthread" = no; then
echo
@@ -1640,6 +1752,7 @@ if test "$linux_aio" != "no" ; then
cat > $TMPC <<EOF
#include <libaio.h>
#include <sys/eventfd.h>
+#include <stddef.h>
int main(void) { io_setup(0, NULL); io_set_eventfd(NULL, 0); eventfd(0, 0); return 0; }
EOF
if compile_prog "" "-laio" ; then
@@ -1781,7 +1894,6 @@ fi
utimens=no
cat > $TMPC << EOF
#define _ATFILE_SOURCE
-#define _GNU_SOURCE
#include <stddef.h>
#include <fcntl.h>
@@ -1799,7 +1911,6 @@ fi
# check if pipe2 is there
pipe2=no
cat > $TMPC << EOF
-#define _GNU_SOURCE
#include <unistd.h>
#include <fcntl.h>
@@ -1817,7 +1928,6 @@ fi
# check if accept4 is there
accept4=no
cat > $TMPC << EOF
-#define _GNU_SOURCE
#include <sys/socket.h>
#include <stddef.h>
@@ -1834,7 +1944,6 @@ fi
# check if tee/splice is there. vmsplice was added same time.
splice=no
cat > $TMPC << EOF
-#define _GNU_SOURCE
#include <unistd.h>
#include <fcntl.h>
#include <limits.h>
@@ -1851,6 +1960,21 @@ if compile_prog "" "" ; then
splice=yes
fi
+##########################################
+# signalfd probe
+signalfd="no"
+cat > $TMPC << EOF
+#define _GNU_SOURCE
+#include <unistd.h>
+#include <sys/syscall.h>
+#include <signal.h>
+int main(void) { return syscall(SYS_signalfd, -1, NULL, _NSIG / 8); }
+EOF
+
+if compile_prog "" "" ; then
+ signalfd=yes
+fi
+
# check if eventfd is supported
eventfd=no
cat > $TMPC << EOF
@@ -1945,7 +2069,7 @@ elif compile_prog "" "-lrt" ; then
fi
if test "$darwin" != "yes" -a "$mingw32" != "yes" -a "$solaris" != yes -a \
- "$aix" != "yes" ; then
+ "$aix" != "yes" -a "$haiku" != "yes" ; then
libs_softmmu="-lutil $libs_softmmu"
fi
@@ -1978,6 +2102,29 @@ if compile_prog "" ""; then
gcc_attribute_warn_unused_result=yes
fi
+# spice probe
+if test "$spice" != "no" ; then
+ cat > $TMPC << EOF
+#include <spice.h>
+int main(void) { spice_server_new(); return 0; }
+EOF
+ spice_cflags=$($pkgconfig --cflags spice-protocol spice-server 2>/dev/null)
+ spice_libs=$($pkgconfig --libs spice-protocol spice-server 2>/dev/null)
+ if $pkgconfig --atleast-version=0.5.3 spice-server >/dev/null 2>&1 && \
+ compile_prog "$spice_cflags" "$spice_libs" ; then
+ spice="yes"
+ libs_softmmu="$libs_softmmu $spice_libs"
+ QEMU_CFLAGS="$QEMU_CFLAGS $spice_cflags"
+ else
+ if test "$spice" = "yes" ; then
+ feature_not_found "spice"
+ fi
+ spice="no"
+ fi
+fi
+
+##########################################
+
##########################################
# check if we have fdatasync
@@ -1990,6 +2137,63 @@ if compile_prog "" "" ; then
fdatasync=yes
fi
+##########################################
+# check if we have madvise
+
+madvise=no
+cat > $TMPC << EOF
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <stddef.h>
+int main(void) { return madvise(NULL, 0, MADV_DONTNEED); }
+EOF
+if compile_prog "" "" ; then
+ madvise=yes
+fi
+
+##########################################
+# check if we have posix_madvise
+
+posix_madvise=no
+cat > $TMPC << EOF
+#include <sys/mman.h>
+#include <stddef.h>
+int main(void) { return posix_madvise(NULL, 0, POSIX_MADV_DONTNEED); }
+EOF
+if compile_prog "" "" ; then
+ posix_madvise=yes
+fi
+
+##########################################
+# check if trace backend exists
+
+sh "$source_path/tracetool" "--$trace_backend" --check-backend > /dev/null 2> /dev/null
+if test "$?" -ne 0 ; then
+ echo
+ echo "Error: invalid trace backend"
+ echo "Please choose a supported trace backend."
+ echo
+ exit 1
+fi
+
+##########################################
+# For 'ust' backend, test if ust headers are present
+if test "$trace_backend" = "ust"; then
+ cat > $TMPC << EOF
+#include <ust/tracepoint.h>
+#include <ust/marker.h>
+int main(void) { return 0; }
+EOF
+ if compile_prog "" "" ; then
+ LIBS="-lust $LIBS"
+ else
+ echo
+ echo "Error: Trace backend 'ust' missing libust header files"
+ echo
+ exit 1
+ fi
+fi
+##########################################
# End of CC checks
# After here, no more $cc or $ld runs
@@ -2030,6 +2234,15 @@ if test "$solaris" = "no" ; then
fi
fi
+# Use ASLR, no-SEH and DEP if available
+if test "$mingw32" = "yes" ; then
+ for flag in --dynamicbase --no-seh --nxcompat; do
+ if $ld --help 2>/dev/null | grep ".$flag" >/dev/null 2>/dev/null ; then
+ LDFLAGS="-Wl,$flag $LDFLAGS"
+ fi
+ done
+fi
+
confdir=$sysconfdir$confsuffix
tools=
@@ -2094,6 +2307,9 @@ echo "Block whitelist $block_drv_whitelist"
echo "Mixer emulation $mixemu"
echo "VNC TLS support $vnc_tls"
echo "VNC SASL support $vnc_sasl"
+echo "VNC JPEG support $vnc_jpeg"
+echo "VNC PNG support $vnc_png"
+echo "VNC thread $vnc_thread"
if test -n "$sparc_cpu"; then
echo "Target Sparc Arch $sparc_cpu"
fi
@@ -2115,8 +2331,13 @@ echo "KVM support $kvm"
echo "fdt support $fdt"
echo "preadv support $preadv"
echo "fdatasync $fdatasync"
+echo "madvise $madvise"
+echo "posix_madvise $posix_madvise"
echo "uuid support $uuid"
echo "vhost-net support $vhost_net"
+echo "Trace backend $trace_backend"
+echo "Trace output file $trace_file-<pid>"
+echo "spice support $spice"
if test $sdl_too_old = "yes"; then
echo "-> Your SDL version is too old - please upgrade to have SDL support"
@@ -2130,6 +2351,7 @@ printf "# Configured with:" >> $config_host_mak
printf " '%s'" "$0" "$@" >> $config_host_mak
echo >> $config_host_mak
+echo all: >> $config_host_mak
echo "prefix=$prefix" >> $config_host_mak
echo "bindir=$bindir" >> $config_host_mak
echo "mandir=$mandir" >> $config_host_mak
@@ -2157,7 +2379,7 @@ if test "$debug" = "yes" ; then
echo "CONFIG_DEBUG_EXEC=y" >> $config_host_mak
fi
if test "$strip_opt" = "yes" ; then
- echo "STRIP_OPT=-s" >> $config_host_mak
+ echo "STRIP=${strip}" >> $config_host_mak
fi
if test "$bigendian" = "yes" ; then
echo "HOST_WORDS_BIGENDIAN=y" >> $config_host_mak
@@ -2165,6 +2387,15 @@ fi
echo "HOST_LONG_BITS=$hostlongbits" >> $config_host_mak
if test "$mingw32" = "yes" ; then
echo "CONFIG_WIN32=y" >> $config_host_mak
+ rc_version=`cat $source_path/VERSION`
+ version_major=${rc_version%%.*}
+ rc_version=${rc_version#*.}
+ version_minor=${rc_version%%.*}
+ rc_version=${rc_version#*.}
+ version_subminor=${rc_version%%.*}
+ version_micro=0
+ echo "CONFIG_FILEVERSION=$version_major,$version_minor,$version_subminor,$version_micro" >> $config_host_mak
+ echo "CONFIG_PRODUCTVERSION=$version_major,$version_minor,$version_subminor,$version_micro" >> $config_host_mak
else
echo "CONFIG_POSIX=y" >> $config_host_mak
fi
@@ -2188,6 +2419,9 @@ if test "$solaris" = "yes" ; then
echo "CONFIG_NEEDS_LIBSUNMATH=y" >> $config_host_mak
fi
fi
+if test "$haiku" = "yes" ; then
+ echo "CONFIG_HAIKU=y" >> $config_host_mak
+fi
if test "$static" = "yes" ; then
echo "CONFIG_STATIC=y" >> $config_host_mak
fi
@@ -2231,6 +2465,18 @@ if test "$vnc_sasl" = "yes" ; then
echo "CONFIG_VNC_SASL=y" >> $config_host_mak
echo "VNC_SASL_CFLAGS=$vnc_sasl_cflags" >> $config_host_mak
fi
+if test "$vnc_jpeg" != "no" ; then
+ echo "CONFIG_VNC_JPEG=y" >> $config_host_mak
+ echo "VNC_JPEG_CFLAGS=$vnc_jpeg_cflags" >> $config_host_mak
+fi
+if test "$vnc_png" != "no" ; then
+ echo "CONFIG_VNC_PNG=y" >> $config_host_mak
+ echo "VNC_PNG_CFLAGS=$vnc_png_cflags" >> $config_host_mak
+fi
+if test "$vnc_thread" != "no" ; then
+ echo "CONFIG_VNC_THREAD=y" >> $config_host_mak
+ echo "CONFIG_THREAD=y" >> $config_host_mak
+fi
if test "$fnmatch" = "yes" ; then
echo "CONFIG_FNMATCH=y" >> $config_host_mak
fi
@@ -2307,6 +2553,7 @@ if test "$xen" = "yes" ; then
fi
if test "$io_thread" = "yes" ; then
echo "CONFIG_IOTHREAD=y" >> $config_host_mak
+ echo "CONFIG_THREAD=y" >> $config_host_mak
fi
if test "$linux_aio" = "yes" ; then
echo "CONFIG_LINUX_AIO=y" >> $config_host_mak
@@ -2331,6 +2578,9 @@ fi
if test "$fdt" = "yes" ; then
echo "CONFIG_FDT=y" >> $config_host_mak
fi
+if test "$signalfd" = "yes" ; then
+ echo "CONFIG_SIGNALFD=y" >> $config_host_mak
+fi
if test "$need_offsetof" = "yes" ; then
echo "CONFIG_NEED_OFFSETOF=y" >> $config_host_mak
fi
@@ -2340,6 +2590,16 @@ fi
if test "$fdatasync" = "yes" ; then
echo "CONFIG_FDATASYNC=y" >> $config_host_mak
fi
+if test "$madvise" = "yes" ; then
+ echo "CONFIG_MADVISE=y" >> $config_host_mak
+fi
+if test "$posix_madvise" = "yes" ; then
+ echo "CONFIG_POSIX_MADVISE=y" >> $config_host_mak
+fi
+
+if test "$spice" = "yes" ; then
+ echo "CONFIG_SPICE=y" >> $config_host_mak
+fi
# XXX: suppress that
if [ "$bsd" = "yes" ] ; then
@@ -2365,6 +2625,16 @@ bsd)
;;
esac
+echo "TRACE_BACKEND=$trace_backend" >> $config_host_mak
+if test "$trace_backend" = "simple"; then
+ echo "CONFIG_SIMPLE_TRACE=y" >> $config_host_mak
+fi
+# Set the appropriate trace file.
+if test "$trace_backend" = "simple"; then
+ trace_file="\"$trace_file-%u\""
+fi
+echo "CONFIG_TRACE_FILE=$trace_file" >> $config_host_mak
+
echo "TOOLS=$tools" >> $config_host_mak
echo "ROMS=$roms" >> $config_host_mak
echo "MAKE=$make" >> $config_host_mak
@@ -2373,6 +2643,7 @@ echo "INSTALL_DIR=$install -d -m0755 -p" >> $config_host_mak
echo "INSTALL_DATA=$install -m0644 -p" >> $config_host_mak
echo "INSTALL_PROG=$install -m0755 -p" >> $config_host_mak
echo "CC=$cc" >> $config_host_mak
+echo "CC_I386=$cc_i386" >> $config_host_mak
echo "HOST_CC=$host_cc" >> $config_host_mak
if test "$sparse" = "yes" ; then
echo "CC := REAL_CC=\"\$(CC)\" cgcc" >> $config_host_mak
@@ -2382,6 +2653,7 @@ fi
echo "AR=$ar" >> $config_host_mak
echo "OBJCOPY=$objcopy" >> $config_host_mak
echo "LD=$ld" >> $config_host_mak
+echo "WINDRES=$windres" >> $config_host_mak
echo "CFLAGS=$CFLAGS" >> $config_host_mak
echo "QEMU_CFLAGS=$QEMU_CFLAGS" >> $config_host_mak
echo "HELPER_CFLAGS=$helper_cflags" >> $config_host_mak
@@ -2831,11 +3103,11 @@ done # for target in $targets
if test "$source_path_used" = "yes" ; then
DIRS="tests tests/cris slirp audio block net pc-bios/optionrom"
DIRS="$DIRS roms/seabios roms/vgabios"
- DIRS="$DIRS fsdev"
+ DIRS="$DIRS fsdev ui"
FILES="Makefile tests/Makefile"
FILES="$FILES tests/cris/Makefile tests/cris/.gdbinit"
FILES="$FILES tests/test-mmap.c"
- FILES="$FILES pc-bios/optionrom/Makefile pc-bios/keymaps pc-bios/video.x"
+ FILES="$FILES pc-bios/optionrom/Makefile pc-bios/keymaps"
FILES="$FILES roms/seabios/Makefile roms/vgabios/Makefile"
for bios_file in $source_path/pc-bios/*.bin $source_path/pc-bios/*.dtb $source_path/pc-bios/openbios-*; do
FILES="$FILES pc-bios/`basename $bios_file`"
diff --git a/console.c b/console.c
index 698bc10a60..c1728b1e04 100644
--- a/console.c
+++ b/console.c
@@ -1060,8 +1060,10 @@ void console_select(unsigned int index)
if (index >= MAX_CONSOLES)
return;
- active_console->g_width = ds_get_width(active_console->ds);
- active_console->g_height = ds_get_height(active_console->ds);
+ if (active_console) {
+ active_console->g_width = ds_get_width(active_console->ds);
+ active_console->g_height = ds_get_height(active_console->ds);
+ }
s = consoles[index];
if (s) {
DisplayState *ds = s->ds;
diff --git a/cpu-all.h b/cpu-all.h
index 224ca40c1d..11edddc76c 100644
--- a/cpu-all.h
+++ b/cpu-all.h
@@ -629,8 +629,10 @@ extern unsigned long guest_base;
extern int have_guest_base;
extern unsigned long reserved_va;
#define GUEST_BASE guest_base
+#define RESERVED_VA reserved_va
#else
#define GUEST_BASE 0ul
+#define RESERVED_VA 0ul
#endif
/* All direct uses of g2h and h2g need to go away for usermode softmmu. */
@@ -771,7 +773,7 @@ void cpu_dump_statistics (CPUState *env, FILE *f,
int flags);
void QEMU_NORETURN cpu_abort(CPUState *env, const char *fmt, ...)
- __attribute__ ((__format__ (__printf__, 2, 3)));
+ GCC_FMT_ATTR(2, 3);
extern CPUState *first_cpu;
extern CPUState *cpu_single_env;
diff --git a/cpu-common.h b/cpu-common.h
index 71e7933c51..a543b5d7cf 100644
--- a/cpu-common.h
+++ b/cpu-common.h
@@ -40,12 +40,15 @@ static inline void cpu_register_physical_memory(target_phys_addr_t start_addr,
}
ram_addr_t cpu_get_physical_page_desc(target_phys_addr_t addr);
+ram_addr_t qemu_ram_alloc_from_ptr(DeviceState *dev, const char *name,
+ ram_addr_t size, void *host);
ram_addr_t qemu_ram_alloc(DeviceState *dev, const char *name, ram_addr_t size);
void qemu_ram_free(ram_addr_t addr);
/* This should only be used for ram local to a device. */
void *qemu_get_ram_ptr(ram_addr_t addr);
/* This should not be used by devices. */
-ram_addr_t qemu_ram_addr_from_host(void *ptr);
+int qemu_ram_addr_from_host(void *ptr, ram_addr_t *ram_addr);
+ram_addr_t qemu_ram_addr_from_host_nofail(void *ptr);
int cpu_register_io_memory(CPUReadMemoryFunc * const *mem_read,
CPUWriteMemoryFunc * const *mem_write,
diff --git a/cpu-exec.c b/cpu-exec.c
index 026980a552..dbdfdccd8c 100644
--- a/cpu-exec.c
+++ b/cpu-exec.c
@@ -21,6 +21,7 @@
#include "disas.h"
#include "tcg.h"
#include "kvm.h"
+#include "qemu-barrier.h"
#if !defined(CONFIG_SOFTMMU)
#undef EAX
@@ -233,12 +234,11 @@ int cpu_exec(CPUState *env1)
use it. */
QEMU_BUILD_BUG_ON (sizeof (saved_env_reg) != sizeof (env));
saved_env_reg = (host_reg_t) env;
- asm("");
+ barrier();
env = env1;
- if (exit_request) {
+ if (unlikely(exit_request)) {
env->exit_request = 1;
- exit_request = 0;
}
#if defined(TARGET_I386)
@@ -448,7 +448,7 @@ int cpu_exec(CPUState *env1)
}
#elif defined(TARGET_MIPS)
if ((interrupt_request & CPU_INTERRUPT_HARD) &&
- (env->CP0_Status & env->CP0_Cause & CP0Ca_IP_mask) &&
+ cpu_mips_hw_interrupts_pending(env) &&
(env->CP0_Status & (1 << CP0St_IE)) &&
!(env->CP0_Status & (1 << CP0St_EXL)) &&
!(env->CP0_Status & (1 << CP0St_ERL)) &&
@@ -599,8 +599,9 @@ int cpu_exec(CPUState *env1)
TB, but before it is linked into a potentially
infinite loop and becomes env->current_tb. Avoid
starting execution if there is a pending interrupt. */
- if (!unlikely (env->exit_request)) {
- env->current_tb = tb;
+ env->current_tb = tb;
+ barrier();
+ if (likely(!env->exit_request)) {
tc_ptr = tb->tc_ptr;
/* execute the generated code */
#if defined(__sparc__) && !defined(CONFIG_SOLARIS)
@@ -609,7 +610,6 @@ int cpu_exec(CPUState *env1)
#define env cpu_single_env
#endif
next_tb = tcg_qemu_tb_exec(tc_ptr);
- env->current_tb = NULL;
if ((next_tb & 3) == 2) {
/* Instruction counter expired. */
int insns_left;
@@ -638,6 +638,7 @@ int cpu_exec(CPUState *env1)
}
}
}
+ env->current_tb = NULL;
/* reset soft MMU for next block (it can currently
only be set by a memory fault) */
} /* for(;;) */
@@ -669,7 +670,7 @@ int cpu_exec(CPUState *env1)
#endif
/* restore global registers */
- asm("");
+ barrier();
env = (void *) saved_env_reg;
/* fail safe : never use cpu_single_env outside cpu_exec() */
diff --git a/cpus.c b/cpus.c
index eef0bb8a0a..36a6d1f350 100644
--- a/cpus.c
+++ b/cpus.c
@@ -33,6 +33,10 @@
#include "exec-all.h"
#include "cpus.h"
+#include "compatfd.h"
+#ifdef CONFIG_LINUX
+#include <sys/prctl.h>
+#endif
#ifdef SIGRTMIN
#define SIG_IPI (SIGRTMIN+4)
@@ -40,7 +44,10 @@
#define SIG_IPI SIGUSR1
#endif
-static CPUState *cur_cpu;
+#ifndef PR_MCE_KILL
+#define PR_MCE_KILL 33
+#endif
+
static CPUState *next_cpu;
/***********************************************************/
@@ -132,7 +139,7 @@ static int cpu_has_work(CPUState *env)
return 0;
}
-static int tcg_has_work(void)
+static int any_cpu_has_work(void)
{
CPUState *env;
@@ -142,6 +149,13 @@ static int tcg_has_work(void)
return 0;
}
+static void cpu_debug_handler(CPUState *env)
+{
+ gdb_set_stop_cpu(env);
+ debug_requested = EXCP_DEBUG;
+ vm_stop(EXCP_DEBUG);
+}
+
#ifndef _WIN32
static int io_thread_fd = -1;
@@ -237,6 +251,8 @@ static void qemu_event_increment(void)
#ifndef CONFIG_IOTHREAD
int qemu_init_main_loop(void)
{
+ cpu_set_debug_excp_handler(cpu_debug_handler);
+
return qemu_event_init();
}
@@ -321,22 +337,85 @@ static QemuCond qemu_work_cond;
static void tcg_init_ipi(void);
static void kvm_init_ipi(CPUState *env);
-static void unblock_io_signals(void);
+static sigset_t block_io_signals(void);
+
+/* If we have signalfd, we mask out the signals we want to handle and then
+ * use signalfd to listen for them. We rely on whatever the current signal
+ * handler is to dispatch the signals when we receive them.
+ */
+static void sigfd_handler(void *opaque)
+{
+ int fd = (unsigned long) opaque;
+ struct qemu_signalfd_siginfo info;
+ struct sigaction action;
+ ssize_t len;
+
+ while (1) {
+ do {
+ len = read(fd, &info, sizeof(info));
+ } while (len == -1 && errno == EINTR);
+
+ if (len == -1 && errno == EAGAIN) {
+ break;
+ }
+
+ if (len != sizeof(info)) {
+ printf("read from sigfd returned %zd: %m\n", len);
+ return;
+ }
+
+ sigaction(info.ssi_signo, NULL, &action);
+ if ((action.sa_flags & SA_SIGINFO) && action.sa_sigaction) {
+ action.sa_sigaction(info.ssi_signo,
+ (siginfo_t *)&info, NULL);
+ } else if (action.sa_handler) {
+ action.sa_handler(info.ssi_signo);
+ }
+ }
+}
+
+static int qemu_signalfd_init(sigset_t mask)
+{
+ int sigfd;
+
+ sigfd = qemu_signalfd(&mask);
+ if (sigfd == -1) {
+ fprintf(stderr, "failed to create signalfd\n");
+ return -errno;
+ }
+
+ fcntl_setfl(sigfd, O_NONBLOCK);
+
+ qemu_set_fd_handler2(sigfd, NULL, sigfd_handler, NULL,
+ (void *)(unsigned long) sigfd);
+
+ return 0;
+}
int qemu_init_main_loop(void)
{
int ret;
+ sigset_t blocked_signals;
+
+ cpu_set_debug_excp_handler(cpu_debug_handler);
+
+ blocked_signals = block_io_signals();
+
+ ret = qemu_signalfd_init(blocked_signals);
+ if (ret)
+ return ret;
+ /* Note eventfd must be drained before signalfd handlers run */
ret = qemu_event_init();
if (ret)
return ret;
qemu_cond_init(&qemu_pause_cond);
+ qemu_cond_init(&qemu_system_cond);
qemu_mutex_init(&qemu_fair_mutex);
qemu_mutex_init(&qemu_global_mutex);
qemu_mutex_lock(&qemu_global_mutex);
- unblock_io_signals();
qemu_thread_self(&io_thread);
return 0;
@@ -402,10 +481,12 @@ static void qemu_wait_io_event_common(CPUState *env)
flush_queued_work(env);
}
-static void qemu_wait_io_event(CPUState *env)
+static void qemu_tcg_wait_io_event(void)
{
- while (!tcg_has_work())
- qemu_cond_timedwait(env->halt_cond, &qemu_global_mutex, 1000);
+ CPUState *env;
+
+ while (!any_cpu_has_work())
+ qemu_cond_timedwait(tcg_halt_cond, &qemu_global_mutex, 1000);
qemu_mutex_unlock(&qemu_global_mutex);
@@ -418,7 +499,36 @@ static void qemu_wait_io_event(CPUState *env)
qemu_mutex_unlock(&qemu_fair_mutex);
qemu_mutex_lock(&qemu_global_mutex);
- qemu_wait_io_event_common(env);
+
+ for (env = first_cpu; env != NULL; env = env->next_cpu) {
+ qemu_wait_io_event_common(env);
+ }
+}
+
+static void sigbus_reraise(void)
+{
+ sigset_t set;
+ struct sigaction action;
+
+ memset(&action, 0, sizeof(action));
+ action.sa_handler = SIG_DFL;
+ if (!sigaction(SIGBUS, &action, NULL)) {
+ raise(SIGBUS);
+ sigemptyset(&set);
+ sigaddset(&set, SIGBUS);
+ sigprocmask(SIG_UNBLOCK, &set, NULL);
+ }
+ perror("Failed to re-raise SIGBUS!\n");
+ abort();
+}
+
+static void sigbus_handler(int n, struct qemu_signalfd_siginfo *siginfo,
+ void *ctx)
+{
+#if defined(TARGET_I386)
+ if (kvm_on_sigbus(siginfo->ssi_code, (void *)(intptr_t)siginfo->ssi_addr))
+#endif
+ sigbus_reraise();
}
static void qemu_kvm_eat_signal(CPUState *env, int timeout)
@@ -427,22 +537,45 @@ static void qemu_kvm_eat_signal(CPUState *env, int timeout)
int r, e;
siginfo_t siginfo;
sigset_t waitset;
+ sigset_t chkset;
ts.tv_sec = timeout / 1000;
ts.tv_nsec = (timeout % 1000) * 1000000;
sigemptyset(&waitset);
sigaddset(&waitset, SIG_IPI);
+ sigaddset(&waitset, SIGBUS);
- qemu_mutex_unlock(&qemu_global_mutex);
- r = sigtimedwait(&waitset, &siginfo, &ts);
- e = errno;
- qemu_mutex_lock(&qemu_global_mutex);
+ do {
+ qemu_mutex_unlock(&qemu_global_mutex);
- if (r == -1 && !(e == EAGAIN || e == EINTR)) {
- fprintf(stderr, "sigtimedwait: %s\n", strerror(e));
- exit(1);
- }
+ r = sigtimedwait(&waitset, &siginfo, &ts);
+ e = errno;
+
+ qemu_mutex_lock(&qemu_global_mutex);
+
+ if (r == -1 && !(e == EAGAIN || e == EINTR)) {
+ fprintf(stderr, "sigtimedwait: %s\n", strerror(e));
+ exit(1);
+ }
+
+ switch (r) {
+ case SIGBUS:
+#ifdef TARGET_I386
+ if (kvm_on_sigbus_vcpu(env, siginfo.si_code, siginfo.si_addr))
+#endif
+ sigbus_reraise();
+ break;
+ default:
+ break;
+ }
+
+ r = sigpending(&chkset);
+ if (r == -1) {
+ fprintf(stderr, "sigpending: %s\n", strerror(e));
+ exit(1);
+ }
+ } while (sigismember(&chkset, SIG_IPI) || sigismember(&chkset, SIGBUS));
}
static void qemu_kvm_wait_io_event(CPUState *env)
@@ -502,8 +635,8 @@ static void *tcg_cpu_thread_fn(void *arg)
qemu_cond_timedwait(&qemu_system_cond, &qemu_global_mutex, 100);
while (1) {
- tcg_cpu_exec();
- qemu_wait_io_event(cur_cpu);
+ cpu_exec_all();
+ qemu_tcg_wait_io_event();
}
return NULL;
@@ -563,6 +696,7 @@ static void kvm_init_ipi(CPUState *env)
pthread_sigmask(SIG_BLOCK, NULL, &set);
sigdelset(&set, SIG_IPI);
+ sigdelset(&set, SIGBUS);
r = kvm_set_signal_mask(env, &set);
if (r) {
fprintf(stderr, "kvm_set_signal_mask: %s\n", strerror(r));
@@ -570,19 +704,30 @@ static void kvm_init_ipi(CPUState *env)
}
}
-static void unblock_io_signals(void)
+static sigset_t block_io_signals(void)
{
sigset_t set;
+ struct sigaction action;
+ /* SIGUSR2 used by posix-aio-compat.c */
sigemptyset(&set);
sigaddset(&set, SIGUSR2);
- sigaddset(&set, SIGIO);
- sigaddset(&set, SIGALRM);
pthread_sigmask(SIG_UNBLOCK, &set, NULL);
sigemptyset(&set);
+ sigaddset(&set, SIGIO);
+ sigaddset(&set, SIGALRM);
sigaddset(&set, SIG_IPI);
+ sigaddset(&set, SIGBUS);
pthread_sigmask(SIG_BLOCK, &set, NULL);
+
+ memset(&action, 0, sizeof(action));
+ action.sa_flags = SA_SIGINFO;
+ action.sa_sigaction = (void (*)(int, siginfo_t*, void*))sigbus_handler;
+ sigaction(SIGBUS, &action, NULL);
+ prctl(PR_MCE_KILL, 1, 1, 0, 0);
+
+ return set;
}
void qemu_mutex_lock_iothread(void)
@@ -763,32 +908,28 @@ static int qemu_cpu_exec(CPUState *env)
return ret;
}
-bool tcg_cpu_exec(void)
+bool cpu_exec_all(void)
{
- int ret = 0;
-
if (next_cpu == NULL)
next_cpu = first_cpu;
- for (; next_cpu != NULL; next_cpu = next_cpu->next_cpu) {
- CPUState *env = cur_cpu = next_cpu;
+ for (; next_cpu != NULL && !exit_request; next_cpu = next_cpu->next_cpu) {
+ CPUState *env = next_cpu;
qemu_clock_enable(vm_clock,
- (cur_cpu->singlestep_enabled & SSTEP_NOTIMER) == 0);
+ (env->singlestep_enabled & SSTEP_NOTIMER) == 0);
if (qemu_alarm_pending())
break;
- if (cpu_can_run(env))
- ret = qemu_cpu_exec(env);
- else if (env->stop)
- break;
-
- if (ret == EXCP_DEBUG) {
- gdb_set_stop_cpu(env);
- debug_requested = EXCP_DEBUG;
+ if (cpu_can_run(env)) {
+ if (qemu_cpu_exec(env) == EXCP_DEBUG) {
+ break;
+ }
+ } else if (env->stop) {
break;
}
}
- return tcg_has_work();
+ exit_request = 0;
+ return any_cpu_has_work();
}
void set_numa_modes(void)
diff --git a/cpus.h b/cpus.h
index 774150a763..af267ea07f 100644
--- a/cpus.h
+++ b/cpus.h
@@ -13,7 +13,7 @@ extern int smp_threads;
extern int debug_requested;
extern int vmstop_requested;
void vm_state_notify(int running, int reason);
-bool tcg_cpu_exec(void);
+bool cpu_exec_all(void);
void set_numa_modes(void);
void set_cpu_log(const char *optarg);
void list_cpus(FILE *f, int (*cpu_fprintf)(FILE *f, const char *fmt, ...),
diff --git a/cris-dis.c b/cris-dis.c
index 455ba8af3f..afd775c29b 100644
--- a/cris-dis.c
+++ b/cris-dis.c
@@ -18,13 +18,11 @@
You should have received a copy of the GNU General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>. */
+#include "qemu-common.h"
#include "dis-asm.h"
//#include "sysdep.h"
#include "target-cris/opcode-cris.h"
//#include "libiberty.h"
-
-
-void *qemu_malloc(size_t len); /* can't include qemu-common.h here */
#define CONST_STRNEQ(STR1,STR2) (strncmp ((STR1), (STR2), sizeof (STR2) - 1) == 0)
diff --git a/cutils.c b/cutils.c
index 036ae3ce31..588373774a 100644
--- a/cutils.c
+++ b/cutils.c
@@ -168,30 +168,50 @@ void qemu_iovec_add(QEMUIOVector *qiov, void *base, size_t len)
}
/*
- * Copies iovecs from src to the end dst until src is completely copied or the
- * total size of the copied iovec reaches size. The size of the last copied
- * iovec is changed in order to fit the specified total size if it isn't a
- * perfect fit already.
+ * Copies iovecs from src to the end of dst. It starts copying after skipping
+ * the given number of bytes in src and copies until src is completely copied
+ * or the total size of the copied iovec reaches size.The size of the last
+ * copied iovec is changed in order to fit the specified total size if it isn't
+ * a perfect fit already.
*/
-void qemu_iovec_concat(QEMUIOVector *dst, QEMUIOVector *src, size_t size)
+void qemu_iovec_copy(QEMUIOVector *dst, QEMUIOVector *src, uint64_t skip,
+ size_t size)
{
int i;
size_t done;
+ void *iov_base;
+ uint64_t iov_len;
assert(dst->nalloc != -1);
done = 0;
for (i = 0; (i < src->niov) && (done != size); i++) {
- if (done + src->iov[i].iov_len > size) {
- qemu_iovec_add(dst, src->iov[i].iov_base, size - done);
+ if (skip >= src->iov[i].iov_len) {
+ /* Skip the whole iov */
+ skip -= src->iov[i].iov_len;
+ continue;
+ } else {
+ /* Skip only part (or nothing) of the iov */
+ iov_base = (uint8_t*) src->iov[i].iov_base + skip;
+ iov_len = src->iov[i].iov_len - skip;
+ skip = 0;
+ }
+
+ if (done + iov_len > size) {
+ qemu_iovec_add(dst, iov_base, size - done);
break;
} else {
- qemu_iovec_add(dst, src->iov[i].iov_base, src->iov[i].iov_len);
+ qemu_iovec_add(dst, iov_base, iov_len);
}
- done += src->iov[i].iov_len;
+ done += iov_len;
}
}
+void qemu_iovec_concat(QEMUIOVector *dst, QEMUIOVector *src, size_t size)
+{
+ qemu_iovec_copy(dst, src, 0, size);
+}
+
void qemu_iovec_destroy(QEMUIOVector *qiov)
{
assert(qiov->nalloc != -1);
@@ -234,6 +254,18 @@ void qemu_iovec_from_buffer(QEMUIOVector *qiov, const void *buf, size_t count)
}
}
+void qemu_iovec_memset(QEMUIOVector *qiov, int c, size_t count)
+{
+ size_t n;
+ int i;
+
+ for (i = 0; i < qiov->niov && count; ++i) {
+ n = MIN(count, qiov->iov[i].iov_len);
+ memset(qiov->iov[i].iov_base, c, n);
+ count -= n;
+ }
+}
+
#ifndef _WIN32
/* Sets a specific flag */
int fcntl_setfl(int fd, int flag)
diff --git a/darwin-user/qemu.h b/darwin-user/qemu.h
index 462bbdac22..0c5081ba32 100644
--- a/darwin-user/qemu.h
+++ b/darwin-user/qemu.h
@@ -99,7 +99,7 @@ int do_sigaction(int sig, const struct sigaction *act,
struct sigaction *oact);
int do_sigaltstack(const struct sigaltstack *ss, struct sigaltstack *oss);
-void gemu_log(const char *fmt, ...) __attribute__((format(printf,1,2)));
+void gemu_log(const char *fmt, ...) GCC_FMT_ATTR(1, 2);
void qerror(const char *fmt, ...);
void write_dt(void *ptr, unsigned long addr, unsigned long limit, int flags);
diff --git a/default-configs/ppc-softmmu.mak b/default-configs/ppc-softmmu.mak
index c026bbb00e..940f4bf375 100644
--- a/default-configs/ppc-softmmu.mak
+++ b/default-configs/ppc-softmmu.mak
@@ -32,4 +32,6 @@ CONFIG_IDE_MACIO=y
CONFIG_NE2000_ISA=y
CONFIG_SOUND=y
CONFIG_VIRTIO_PCI=y
+CONFIG_PFLASH_CFI01=y
CONFIG_PFLASH_CFI02=y
+CONFIG_PTIMER=y
diff --git a/default-configs/ppc64-softmmu.mak b/default-configs/ppc64-softmmu.mak
index 0101a283e3..e1bc6b8f80 100644
--- a/default-configs/ppc64-softmmu.mak
+++ b/default-configs/ppc64-softmmu.mak
@@ -32,4 +32,6 @@ CONFIG_IDE_MACIO=y
CONFIG_NE2000_ISA=y
CONFIG_SOUND=y
CONFIG_VIRTIO_PCI=y
+CONFIG_PFLASH_CFI01=y
CONFIG_PFLASH_CFI02=y
+CONFIG_PTIMER=y
diff --git a/default-configs/ppcemb-softmmu.mak b/default-configs/ppcemb-softmmu.mak
index 8ba9ac179f..8f1cc09add 100644
--- a/default-configs/ppcemb-softmmu.mak
+++ b/default-configs/ppcemb-softmmu.mak
@@ -32,4 +32,6 @@ CONFIG_IDE_MACIO=y
CONFIG_NE2000_ISA=y
CONFIG_SOUND=y
CONFIG_VIRTIO_PCI=y
+CONFIG_PFLASH_CFI01=y
CONFIG_PFLASH_CFI02=y
+CONFIG_PTIMER=y
diff --git a/disas.c b/disas.c
index 79a98ded8b..afe331fdb9 100644
--- a/disas.c
+++ b/disas.c
@@ -349,7 +349,8 @@ monitor_read_memory (bfd_vma memaddr, bfd_byte *myaddr, int length,
return 0;
}
-static int monitor_fprintf(FILE *stream, const char *fmt, ...)
+static int GCC_FMT_ATTR(2, 3)
+monitor_fprintf(FILE *stream, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
diff --git a/disas.h b/disas.h
index 6a9332d6cc..f9287f73e1 100644
--- a/disas.h
+++ b/disas.h
@@ -8,11 +8,8 @@
void disas(FILE *out, void *code, unsigned long size);
void target_disas(FILE *out, target_ulong code, target_ulong size, int flags);
-/* The usual mess... FIXME: Remove this condition once dyngen-exec.h is gone */
-#ifndef __DYNGEN_EXEC_H__
void monitor_disas(Monitor *mon, CPUState *env,
target_ulong pc, int nb_insn, int is_physical, int flags);
-#endif
/* Look up symbol for debugging purpose. Returns "" if unknown. */
const char *lookup_symbol(target_ulong orig_addr);
diff --git a/docs/blkverify.txt b/docs/blkverify.txt
new file mode 100644
index 0000000000..d556dc4e6d
--- /dev/null
+++ b/docs/blkverify.txt
@@ -0,0 +1,69 @@
+= Block driver correctness testing with blkverify =
+
+== Introduction ==
+
+This document describes how to use the blkverify protocol to test that a block
+driver is operating correctly.
+
+It is difficult to test and debug block drivers against real guests. Often
+processes inside the guest will crash because corrupt sectors were read as part
+of the executable. Other times obscure errors are raised by a program inside
+the guest. These issues are extremely hard to trace back to bugs in the block
+driver.
+
+Blkverify solves this problem by catching data corruption inside QEMU the first
+time bad data is read and reporting the disk sector that is corrupted.
+
+== How it works ==
+
+The blkverify protocol has two child block devices, the "test" device and the
+"raw" device. Read/write operations are mirrored to both devices so their
+state should always be in sync.
+
+The "raw" device is a raw image, a flat file, that has identical starting
+contents to the "test" image. The idea is that the "raw" device will handle
+read/write operations correctly and not corrupt data. It can be used as a
+reference for comparison against the "test" device.
+
+After a mirrored read operation completes, blkverify will compare the data and
+raise an error if it is not identical. This makes it possible to catch the
+first instance where corrupt data is read.
+
+== Example ==
+
+Imagine raw.img has 0xcd repeated throughout its first sector:
+
+ $ ./qemu-io -c 'read -v 0 512' raw.img
+ 00000000: cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd ................
+ 00000010: cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd ................
+ [...]
+ 000001e0: cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd ................
+ 000001f0: cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd ................
+ read 512/512 bytes at offset 0
+ 512.000000 bytes, 1 ops; 0.0000 sec (97.656 MiB/sec and 200000.0000 ops/sec)
+
+And test.img is corrupt, its first sector is zeroed when it shouldn't be:
+
+ $ ./qemu-io -c 'read -v 0 512' test.img
+ 00000000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+ 00000010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+ [...]
+ 000001e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+ 000001f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+ read 512/512 bytes at offset 0
+ 512.000000 bytes, 1 ops; 0.0000 sec (81.380 MiB/sec and 166666.6667 ops/sec)
+
+This error is caught by blkverify:
+
+ $ ./qemu-io -c 'read 0 512' blkverify:a.img:b.img
+ blkverify: read sector_num=0 nb_sectors=4 contents mismatch in sector 0
+
+A more realistic scenario is verifying the installation of a guest OS:
+
+ $ ./qemu-img create raw.img 16G
+ $ ./qemu-img create -f qcow2 test.qcow2 16G
+ $ x86_64-softmmu/qemu-system-x86_64 -cdrom debian.iso \
+ -drive file=blkverify:raw.img:test.qcow2
+
+If the installation is aborted when blkverify detects corruption, use qemu-io
+to explore the contents of the disk image at the sector in question.
diff --git a/docs/migration.txt b/docs/migration.txt
new file mode 100644
index 0000000000..4848c1e52d
--- /dev/null
+++ b/docs/migration.txt
@@ -0,0 +1,303 @@
+= Migration =
+
+QEMU has code to load/save the state of the guest that it is running.
+These are two complementary operations. Saving the state just does
+that, saves the state for each device that the guest is running.
+Restoring a guest is just the opposite operation: we need to load the
+state of each device.
+
+For this to work, QEMU has to be launched with the same arguments the
+two times. I.e. it can only restore the state in one guest that has
+the same devices that the one it was saved (this last requirement can
+be relaxed a bit, but for now we can consider that configuration has
+to be exactly the same).
+
+Once that we are able to save/restore a guest, a new functionality is
+requested: migration. This means that QEMU is able to start in one
+machine and being "migrated" to another machine. I.e. being moved to
+another machine.
+
+Next was the "live migration" functionality. This is important
+because some guests run with a lot of state (specially RAM), and it
+can take a while to move all state from one machine to another. Live
+migration allows the guest to continue running while the state is
+transferred. Only while the last part of the state is transferred has
+the guest to be stopped. Typically the time that the guest is
+unresponsive during live migration is the low hundred of milliseconds
+(notice that this depends on a lot of things).
+
+=== Types of migration ===
+
+Now that we have talked about live migration, there are several ways
+to do migration:
+
+- tcp migration: do the migration using tcp sockets
+- unix migration: do the migration using unix sockets
+- exec migration: do the migration using the stdin/stdout through a process.
+- fd migration: do the migration using an file descriptor that is
+ passed to QEMU. QEMU doesn't care how this file descriptor is opened.
+
+All these four migration protocols use the same infrastructure to
+save/restore state devices. This infrastructure is shared with the
+savevm/loadvm functionality.
+
+=== State Live Migration ==
+
+This is used for RAM and block devices. It is not yet ported to vmstate.
+<Fill more information here>
+
+=== What is the common infrastructure ===
+
+QEMU uses a QEMUFile abstraction to be able to do migration. Any type
+of migration that wants to use QEMU infrastructure has to create a
+QEMUFile with:
+
+QEMUFile *qemu_fopen_ops(void *opaque,
+ QEMUFilePutBufferFunc *put_buffer,
+ QEMUFileGetBufferFunc *get_buffer,
+ QEMUFileCloseFunc *close,
+ QEMUFileRateLimit *rate_limit,
+ QEMUFileSetRateLimit *set_rate_limit,
+ QEMUFileGetRateLimit *get_rate_limit);
+
+The functions have the following functionality:
+
+This function writes a chunk of data to a file at the given position.
+The pos argument can be ignored if the file is only used for
+streaming. The handler should try to write all of the data it can.
+
+typedef int (QEMUFilePutBufferFunc)(void *opaque, const uint8_t *buf,
+ int64_t pos, int size);
+
+Read a chunk of data from a file at the given position. The pos argument
+can be ignored if the file is only be used for streaming. The number of
+bytes actually read should be returned.
+
+typedef int (QEMUFileGetBufferFunc)(void *opaque, uint8_t *buf,
+ int64_t pos, int size);
+
+Close a file and return an error code.
+
+typedef int (QEMUFileCloseFunc)(void *opaque);
+
+Called to determine if the file has exceeded its bandwidth allocation. The
+bandwidth capping is a soft limit, not a hard limit.
+
+typedef int (QEMUFileRateLimit)(void *opaque);
+
+Called to change the current bandwidth allocation. This function must return
+the new actual bandwidth. It should be new_rate if everything goes OK, and
+the old rate otherwise.
+
+typedef size_t (QEMUFileSetRateLimit)(void *opaque, size_t new_rate);
+typedef size_t (QEMUFileGetRateLimit)(void *opaque);
+
+You can use any internal state that you need using the opaque void *
+pointer that is passed to all functions.
+
+The rate limiting functions are used to limit the bandwidth used by
+QEMU migration.
+
+The important functions for us are put_buffer()/get_buffer() that
+allow to write/read a buffer into the QEMUFile.
+
+=== How to save the state of one device ==
+
+The state of a device is saved using intermediate buffers. There are
+some helper functions to assist this saving.
+
+There is a new concept that we have to explain here: device state
+version. When we migrate a device, we save/load the state as a series
+of fields. Some times, due to bugs or new functionality, we need to
+change the state to store more/different information. We use the
+version to identify each time that we do a change. Each version is
+associated with a series of fields saved. The save_state always saves
+the state as the newer version. But load_state sometimes is able to
+load state from an older version.
+
+ === Legacy way ===
+
+This way is going to disappear as soon as all current users are ported to VMSTATE.
+
+Each device has to register two functions, one to save the state and
+another to load the state back.
+
+int register_savevm(DeviceState *dev,
+ const char *idstr,
+ int instance_id,
+ int version_id,
+ SaveStateHandler *save_state,
+ LoadStateHandler *load_state,
+ void *opaque);
+
+typedef void SaveStateHandler(QEMUFile *f, void *opaque);
+typedef int LoadStateHandler(QEMUFile *f, void *opaque, int version_id);
+
+The important functions for the device state format are the save_state
+and load_state. Notice that load_state receives a version_id
+parameter to know what state format is receiving. save_state doesn't
+have a version_id parameter because it always uses the latest version.
+
+=== VMState ===
+
+The legacy way of saving/loading state of the device had the problem
+that we have to maintain two functions in sync. If we did one change
+in one of them and not in the other, we would get a failed migration.
+
+VMState changed the way that state is saved/loaded. Instead of using
+a function to save the state and another to load it, it was changed to
+a declarative way of what the state consisted of. Now VMState is able
+to interpret that definition to be able to load/save the state. As
+the state is declared only once, it can't go out of sync in the
+save/load functions.
+
+An example (from hw/pckbd.c)
+
+static const VMStateDescription vmstate_kbd = {
+ .name = "pckbd",
+ .version_id = 3,
+ .minimum_version_id = 3,
+ .minimum_version_id_old = 3,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT8(write_cmd, KBDState),
+ VMSTATE_UINT8(status, KBDState),
+ VMSTATE_UINT8(mode, KBDState),
+ VMSTATE_UINT8(pending, KBDState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+We are declaring the state with name "pckbd".
+The version_id is 3, and the fields are 4 uint8_t in a KBDState structure.
+We registered this with:
+
+ vmstate_register(NULL, 0, &vmstate_kbd, s);
+
+Note: talk about how vmstate <-> qdev interact, and what the instance ids mean.
+
+You can search for VMSTATE_* macros for lots of types used in QEMU in
+hw/hw.h.
+
+=== More about versions ==
+
+You can see that there are several version fields:
+
+- version_id: the maximum version_id supported by VMState for that device.
+- minimum_version_id: the minimum version_id that VMState is able to understand
+ for that device.
+- minimum_version_id_old: For devices that were not able to port to vmstate, we can
+ assign a function that knows how to read this old state.
+
+So, VMState is able to read versions from minimum_version_id to
+version_id. And the function load_state_old() is able to load state
+from minimum_version_id_old to minimum_version_id. This function is
+deprecated and will be removed when no more users are left.
+
+=== Massaging functions ===
+
+Sometimes, it is not enough to be able to save the state directly
+from one structure, we need to fill the correct values there. One
+example is when we are using kvm. Before saving the cpu state, we
+need to ask kvm to copy to QEMU the state that it is using. And the
+opposite when we are loading the state, we need a way to tell kvm to
+load the state for the cpu that we have just loaded from the QEMUFile.
+
+The functions to do that are inside a vmstate definition, and are called:
+
+- int (*pre_load)(void *opaque);
+
+ This function is called before we load the state of one device.
+
+- int (*post_load)(void *opaque, int version_id);
+
+ This function is called after we load the state of one device.
+
+- void (*pre_save)(void *opaque);
+
+ This function is called before we save the state of one device.
+
+Example: You can look at hpet.c, that uses the three function to
+ massage the state that is transferred.
+
+=== Subsections ===
+
+The use of version_id allows to be able to migrate from older versions
+to newer versions of a device. But not the other way around. This
+makes very complicated to fix bugs in stable branches. If we need to
+add anything to the state to fix a bug, we have to disable migration
+to older versions that don't have that bug-fix (i.e. a new field).
+
+But sometimes, that bug-fix is only needed sometimes, not always. For
+instance, if the device is in the middle of a DMA operation, it is
+using a specific functionality, ....
+
+It is impossible to create a way to make migration from any version to
+any other version to work. But we can do better than only allowing
+migration from older versions no newer ones. For that fields that are
+only needed sometimes, we add the idea of subsections. A subsection
+is "like" a device vmstate, but with a particularity, it has a Boolean
+function that tells if that values are needed to be sent or not. If
+this functions returns false, the subsection is not sent.
+
+On the receiving side, if we found a subsection for a device that we
+don't understand, we just fail the migration. If we understand all
+the subsections, then we load the state with success.
+
+One important note is that the post_load() function is called "after"
+loading all subsections, because a newer subsection could change same
+value that it uses.
+
+Example:
+
+static bool ide_drive_pio_state_needed(void *opaque)
+{
+ IDEState *s = opaque;
+
+ return (s->status & DRQ_STAT) != 0;
+}
+
+const VMStateDescription vmstate_ide_drive_pio_state = {
+ .name = "ide_drive/pio_state",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .pre_save = ide_drive_pio_pre_save,
+ .post_load = ide_drive_pio_post_load,
+ .fields = (VMStateField []) {
+ VMSTATE_INT32(req_nb_sectors, IDEState),
+ VMSTATE_VARRAY_INT32(io_buffer, IDEState, io_buffer_total_len, 1,
+ vmstate_info_uint8, uint8_t),
+ VMSTATE_INT32(cur_io_buffer_offset, IDEState),
+ VMSTATE_INT32(cur_io_buffer_len, IDEState),
+ VMSTATE_UINT8(end_transfer_fn_idx, IDEState),
+ VMSTATE_INT32(elementary_transfer_size, IDEState),
+ VMSTATE_INT32(packet_transfer_size, IDEState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+const VMStateDescription vmstate_ide_drive = {
+ .name = "ide_drive",
+ .version_id = 3,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .post_load = ide_drive_post_load,
+ .fields = (VMStateField []) {
+ .... several fields ....
+ VMSTATE_END_OF_LIST()
+ },
+ .subsections = (VMStateSubsection []) {
+ {
+ .vmsd = &vmstate_ide_drive_pio_state,
+ .needed = ide_drive_pio_state_needed,
+ }, {
+ /* empty */
+ }
+ }
+};
+
+Here we have a subsection for the pio state. We only need to
+save/send this state when we are in the middle of a pio operation
+(that is what ide_drive_pio_state_needed() checks). If DRQ_STAT is
+not enabled, the values on that fields are garbage and don't need to
+be sent.
diff --git a/docs/specs/ivshmem_device_spec.txt b/docs/specs/ivshmem_device_spec.txt
new file mode 100644
index 0000000000..23dd2ba89f
--- /dev/null
+++ b/docs/specs/ivshmem_device_spec.txt
@@ -0,0 +1,96 @@
+
+Device Specification for Inter-VM shared memory device
+------------------------------------------------------
+
+The Inter-VM shared memory device is designed to share a region of memory to
+userspace in multiple virtual guests. The memory region does not belong to any
+guest, but is a POSIX memory object on the host. Optionally, the device may
+support sending interrupts to other guests sharing the same memory region.
+
+
+The Inter-VM PCI device
+-----------------------
+
+*BARs*
+
+The device supports three BARs. BAR0 is a 1 Kbyte MMIO region to support
+registers. BAR1 is used for MSI-X when it is enabled in the device. BAR2 is
+used to map the shared memory object from the host. The size of BAR2 is
+specified when the guest is started and must be a power of 2 in size.
+
+*Registers*
+
+The device currently supports 4 registers of 32-bits each. Registers
+are used for synchronization between guests sharing the same memory object when
+interrupts are supported (this requires using the shared memory server).
+
+The server assigns each VM an ID number and sends this ID number to the Qemu
+process when the guest starts.
+
+enum ivshmem_registers {
+ IntrMask = 0,
+ IntrStatus = 4,
+ IVPosition = 8,
+ Doorbell = 12
+};
+
+The first two registers are the interrupt mask and status registers. Mask and
+status are only used with pin-based interrupts. They are unused with MSI
+interrupts.
+
+Status Register: The status register is set to 1 when an interrupt occurs.
+
+Mask Register: The mask register is bitwise ANDed with the interrupt status
+and the result will raise an interrupt if it is non-zero. However, since 1 is
+the only value the status will be set to, it is only the first bit of the mask
+that has any effect. Therefore interrupts can be masked by setting the first
+bit to 0 and unmasked by setting the first bit to 1.
+
+IVPosition Register: The IVPosition register is read-only and reports the
+guest's ID number. The guest IDs are non-negative integers. When using the
+server, since the server is a separate process, the VM ID will only be set when
+the device is ready (shared memory is received from the server and accessible via
+the device). If the device is not ready, the IVPosition will return -1.
+Applications should ensure that they have a valid VM ID before accessing the
+shared memory.
+
+Doorbell Register: To interrupt another guest, a guest must write to the
+Doorbell register. The doorbell register is 32-bits, logically divided into
+two 16-bit fields. The high 16-bits are the guest ID to interrupt and the low
+16-bits are the interrupt vector to trigger. The semantics of the value
+written to the doorbell depends on whether the device is using MSI or a regular
+pin-based interrupt. In short, MSI uses vectors while regular interrupts set the
+status register.
+
+Regular Interrupts
+
+If regular interrupts are used (due to either a guest not supporting MSI or the
+user specifying not to use them on startup) then the value written to the lower
+16-bits of the Doorbell register results is arbitrary and will trigger an
+interrupt in the destination guest.
+
+Message Signalled Interrupts
+
+A ivshmem device may support multiple MSI vectors. If so, the lower 16-bits
+written to the Doorbell register must be between 0 and the maximum number of
+vectors the guest supports. The lower 16 bits written to the doorbell is the
+MSI vector that will be raised in the destination guest. The number of MSI
+vectors is configurable but it is set when the VM is started.
+
+The important thing to remember with MSI is that it is only a signal, no status
+is set (since MSI interrupts are not shared). All information other than the
+interrupt itself should be communicated via the shared memory region. Devices
+supporting multiple MSI vectors can use different vectors to indicate different
+events have occurred. The semantics of interrupt vectors are left to the
+user's discretion.
+
+
+Usage in the Guest
+------------------
+
+The shared memory device is intended to be used with the provided UIO driver.
+Very little configuration is needed. The guest should map BAR0 to access the
+registers (an array of 32-bit ints allows simple writing) and map BAR2 to
+access the shared memory region itself. The size of the shared memory region
+is specified when the guest (or shared memory server) is started. A guest may
+map the whole shared memory region or only part of it.
diff --git a/docs/tracing.txt b/docs/tracing.txt
new file mode 100644
index 0000000000..963c5047fe
--- /dev/null
+++ b/docs/tracing.txt
@@ -0,0 +1,184 @@
+= Tracing =
+
+== Introduction ==
+
+This document describes the tracing infrastructure in QEMU and how to use it
+for debugging, profiling, and observing execution.
+
+== Quickstart ==
+
+1. Build with the 'simple' trace backend:
+
+ ./configure --trace-backend=simple
+ make
+
+2. Enable trace events you are interested in:
+
+ $EDITOR trace-events # remove "disable" from events you want
+
+3. Run the virtual machine to produce a trace file:
+
+ qemu ... # your normal QEMU invocation
+
+4. Pretty-print the binary trace file:
+
+ ./simpletrace.py trace-events trace-*
+
+== Trace events ==
+
+There is a set of static trace events declared in the trace-events source
+file. Each trace event declaration names the event, its arguments, and the
+format string which can be used for pretty-printing:
+
+ qemu_malloc(size_t size, void *ptr) "size %zu ptr %p"
+ qemu_free(void *ptr) "ptr %p"
+
+The trace-events file is processed by the tracetool script during build to
+generate code for the trace events. Trace events are invoked directly from
+source code like this:
+
+ #include "trace.h" /* needed for trace event prototype */
+
+ void *qemu_malloc(size_t size)
+ {
+ void *ptr;
+ if (!size && !allow_zero_malloc()) {
+ abort();
+ }
+ ptr = oom_check(malloc(size ? size : 1));
+ trace_qemu_malloc(size, ptr); /* <-- trace event */
+ return ptr;
+ }
+
+=== Declaring trace events ===
+
+The tracetool script produces the trace.h header file which is included by
+every source file that uses trace events. Since many source files include
+trace.h, it uses a minimum of types and other header files included to keep
+the namespace clean and compile times and dependencies down.
+
+Trace events should use types as follows:
+
+ * Use stdint.h types for fixed-size types. Most offsets and guest memory
+ addresses are best represented with uint32_t or uint64_t. Use fixed-size
+ types over primitive types whose size may change depending on the host
+ (32-bit versus 64-bit) so trace events don't truncate values or break
+ the build.
+
+ * Use void * for pointers to structs or for arrays. The trace.h header
+ cannot include all user-defined struct declarations and it is therefore
+ necessary to use void * for pointers to structs.
+
+ * For everything else, use primitive scalar types (char, int, long) with the
+ appropriate signedness.
+
+Format strings should reflect the types defined in the trace event. Take
+special care to use PRId64 and PRIu64 for int64_t and uint64_t types,
+respectively. This ensures portability between 32- and 64-bit platforms. Note
+that format strings must begin and end with double quotes. When using
+portability macros, ensure they are preceded and followed by double quotes:
+"value %"PRIx64"".
+
+=== Hints for adding new trace events ===
+
+1. Trace state changes in the code. Interesting points in the code usually
+ involve a state change like starting, stopping, allocating, freeing. State
+ changes are good trace events because they can be used to understand the
+ execution of the system.
+
+2. Trace guest operations. Guest I/O accesses like reading device registers
+ are good trace events because they can be used to understand guest
+ interactions.
+
+3. Use correlator fields so the context of an individual line of trace output
+ can be understood. For example, trace the pointer returned by malloc and
+ used as an argument to free. This way mallocs and frees can be matched up.
+ Trace events with no context are not very useful.
+
+4. Name trace events after their function. If there are multiple trace events
+ in one function, append a unique distinguisher at the end of the name.
+
+5. Declare trace events with the "disable" keyword. Some trace events can
+ produce a lot of output and users are typically only interested in a subset
+ of trace events. Marking trace events disabled by default saves the user
+ from having to manually disable noisy trace events.
+
+== Trace backends ==
+
+The tracetool script automates tedious trace event code generation and also
+keeps the trace event declarations independent of the trace backend. The trace
+events are not tightly coupled to a specific trace backend, such as LTTng or
+SystemTap. Support for trace backends can be added by extending the tracetool
+script.
+
+The trace backend is chosen at configure time and only one trace backend can
+be built into the binary:
+
+ ./configure --trace-backend=simple
+
+For a list of supported trace backends, try ./configure --help or see below.
+
+The following subsections describe the supported trace backends.
+
+=== Nop ===
+
+The "nop" backend generates empty trace event functions so that the compiler
+can optimize out trace events completely. This is the default and imposes no
+performance penalty.
+
+=== Simpletrace ===
+
+The "simple" backend supports common use cases and comes as part of the QEMU
+source tree. It may not be as powerful as platform-specific or third-party
+trace backends but it is portable. This is the recommended trace backend
+unless you have specific needs for more advanced backends.
+
+==== Monitor commands ====
+
+* info trace
+ Display the contents of trace buffer. This command dumps the trace buffer
+ with simple formatting. For full pretty-printing, use the simpletrace.py
+ script on a binary trace file.
+
+ The trace buffer is written into until full. The full trace buffer is
+ flushed and emptied. This means the 'info trace' will display few or no
+ entries if the buffer has just been flushed.
+
+* info trace-events
+ View available trace events and their state. State 1 means enabled, state 0
+ means disabled.
+
+* trace-event NAME on|off
+ Enable/disable a given trace event.
+
+* trace-file on|off|flush|set <path>
+ Enable/disable/flush the trace file or set the trace file name.
+
+==== Enabling/disabling trace events programmatically ====
+
+The st_change_trace_event_state() function can be used to enable or disable trace
+events at runtime inside QEMU:
+
+ #include "trace.h"
+
+ st_change_trace_event_state("virtio_irq", true); /* enable */
+ [...]
+ st_change_trace_event_state("virtio_irq", false); /* disable */
+
+==== Analyzing trace files ====
+
+The "simple" backend produces binary trace files that can be formatted with the
+simpletrace.py script. The script takes the trace-events file and the binary
+trace:
+
+ ./simpletrace.py trace-events trace-12345
+
+You must ensure that the same trace-events file was used to build QEMU,
+otherwise trace event declarations may have changed and output will not be
+consistent.
+
+=== LTTng Userspace Tracer ===
+
+The "ust" backend uses the LTTng Userspace Tracer library. There are no
+monitor commands built into QEMU, instead UST utilities should be used to list,
+enable/disable, and dump traces.
diff --git a/dyngen-exec.h b/dyngen-exec.h
index 5bfef3f6fe..db00fbae04 100644
--- a/dyngen-exec.h
+++ b/dyngen-exec.h
@@ -19,19 +19,7 @@
#if !defined(__DYNGEN_EXEC_H__)
#define __DYNGEN_EXEC_H__
-/* prevent Solaris from trying to typedef FILE in gcc's
- include/floatingpoint.h which will conflict with the
- definition down below */
-#ifdef __sun__
-#define _FILEDEFED
-#endif
-
-/* NOTE: standard headers should be used with special care at this
- point because host CPU registers are used as global variables. Some
- host headers do not allow that. */
-#include <stddef.h>
-#include <stdint.h>
-#include <stdbool.h>
+#include "qemu-common.h"
#ifdef __OpenBSD__
#include <sys/types.h>
@@ -40,15 +28,6 @@
/* XXX: This may be wrong for 64-bit ILP32 hosts. */
typedef void * host_reg_t;
-#ifdef CONFIG_BSD
-typedef struct __sFILE FILE;
-#else
-typedef struct FILE FILE;
-#endif
-extern int fprintf(FILE *, const char *, ...);
-extern int fputs(const char *, FILE *);
-extern int printf(const char *, ...);
-
#if defined(__i386__)
#define AREG0 "ebp"
#elif defined(__x86_64__)
diff --git a/elf.h b/elf.h
index eb9e3bece7..7067c90fb0 100644
--- a/elf.h
+++ b/elf.h
@@ -147,8 +147,37 @@ typedef int64_t Elf64_Sxword;
#define DT_DEBUG 21
#define DT_TEXTREL 22
#define DT_JMPREL 23
+#define DT_BINDNOW 24
+#define DT_INIT_ARRAY 25
+#define DT_FINI_ARRAY 26
+#define DT_INIT_ARRAYSZ 27
+#define DT_FINI_ARRAYSZ 28
+#define DT_RUNPATH 29
+#define DT_FLAGS 30
+#define DT_LOOS 0x6000000d
+#define DT_HIOS 0x6ffff000
#define DT_LOPROC 0x70000000
#define DT_HIPROC 0x7fffffff
+
+/* DT_ entries which fall between DT_VALRNGLO and DT_VALRNDHI use
+ the d_val field of the Elf*_Dyn structure. I.e. they contain scalars. */
+#define DT_VALRNGLO 0x6ffffd00
+#define DT_VALRNGHI 0x6ffffdff
+
+/* DT_ entries which fall between DT_ADDRRNGLO and DT_ADDRRNGHI use
+ the d_ptr field of the Elf*_Dyn structure. I.e. they contain pointers. */
+#define DT_ADDRRNGLO 0x6ffffe00
+#define DT_ADDRRNGHI 0x6ffffeff
+
+#define DT_VERSYM 0x6ffffff0
+#define DT_RELACOUNT 0x6ffffff9
+#define DT_RELCOUNT 0x6ffffffa
+#define DT_FLAGS_1 0x6ffffffb
+#define DT_VERDEF 0x6ffffffc
+#define DT_VERDEFNUM 0x6ffffffd
+#define DT_VERNEED 0x6ffffffe
+#define DT_VERNEEDNUM 0x6fffffff
+
#define DT_MIPS_RLD_VERSION 0x70000001
#define DT_MIPS_TIME_STAMP 0x70000002
#define DT_MIPS_ICHECKSUM 0x70000003
@@ -207,6 +236,21 @@ typedef int64_t Elf64_Sxword;
#define AT_PLATFORM 15 /* string identifying CPU for optimizations */
#define AT_HWCAP 16 /* arch dependent hints at CPU capabilities */
#define AT_CLKTCK 17 /* frequency at which times() increments */
+#define AT_FPUCW 18 /* info about fpu initialization by kernel */
+#define AT_DCACHEBSIZE 19 /* data cache block size */
+#define AT_ICACHEBSIZE 20 /* instruction cache block size */
+#define AT_UCACHEBSIZE 21 /* unified cache block size */
+#define AT_IGNOREPPC 22 /* ppc only; entry should be ignored */
+#define AT_SECURE 23 /* boolean, was exec suid-like? */
+#define AT_BASE_PLATFORM 24 /* string identifying real platforms */
+#define AT_RANDOM 25 /* address of 16 random bytes */
+#define AT_EXECFN 31 /* filename of the executable */
+#define AT_SYSINFO 32 /* address of kernel entry point */
+#define AT_SYSINFO_EHDR 33 /* address of kernel vdso */
+#define AT_L1I_CACHESHAPE 34 /* shapes of the caches: */
+#define AT_L1D_CACHESHAPE 35 /* bits 0-3: cache associativity. */
+#define AT_L2_CACHESHAPE 36 /* bits 4-7: log2 of line size. */
+#define AT_L3_CACHESHAPE 37 /* val&~255: cache size. */
typedef struct dynamic{
Elf32_Sword d_tag;
diff --git a/exec-all.h b/exec-all.h
index a775582be7..c4570587d7 100644
--- a/exec-all.h
+++ b/exec-all.h
@@ -86,9 +86,6 @@ int cpu_gen_code(CPUState *env, struct TranslationBlock *tb,
int cpu_restore_state(struct TranslationBlock *tb,
CPUState *env, unsigned long searched_pc,
void *puc);
-int cpu_restore_state_copy(struct TranslationBlock *tb,
- CPUState *env, unsigned long searched_pc,
- void *puc);
void cpu_resume_from_signal(CPUState *env1, void *puc);
void cpu_io_recompile(CPUState *env, void *retaddr);
TranslationBlock *tb_gen_code(CPUState *env,
@@ -191,8 +188,6 @@ void tb_link_page(TranslationBlock *tb,
void tb_phys_invalidate(TranslationBlock *tb, tb_page_addr_t page_addr);
extern TranslationBlock *tb_phys_hash[CODE_GEN_PHYS_HASH_SIZE];
-extern uint8_t *code_gen_ptr;
-extern int code_gen_max_blocks;
#if defined(USE_DIRECT_JUMP)
@@ -339,7 +334,7 @@ static inline tb_page_addr_t get_page_addr_code(CPUState *env1, target_ulong add
}
p = (void *)(unsigned long)addr
+ env1->tlb_table[mmu_idx][page_index].addend;
- return qemu_ram_addr_from_host(p);
+ return qemu_ram_addr_from_host_nofail(p);
}
#endif
diff --git a/exec.c b/exec.c
index 4641b3eb6e..631d8c564e 100644
--- a/exec.c
+++ b/exec.c
@@ -80,7 +80,7 @@
#define SMC_BITMAP_USE_THRESHOLD 10
static TranslationBlock *tbs;
-int code_gen_max_blocks;
+static int code_gen_max_blocks;
TranslationBlock *tb_phys_hash[CODE_GEN_PHYS_HASH_SIZE];
static int nb_tbs;
/* any access to the tbs or the page table must use this lock */
@@ -107,7 +107,7 @@ static uint8_t *code_gen_buffer;
static unsigned long code_gen_buffer_size;
/* threshold to flush the translated code buffer */
static unsigned long code_gen_buffer_max_size;
-uint8_t *code_gen_ptr;
+static uint8_t *code_gen_ptr;
#if !defined(CONFIG_USER_ONLY)
int phys_ram_fd;
@@ -1706,8 +1706,8 @@ static QLIST_HEAD(memory_client_list, CPUPhysMemoryClient) memory_client_list
= QLIST_HEAD_INITIALIZER(memory_client_list);
static void cpu_notify_set_memory(target_phys_addr_t start_addr,
- ram_addr_t size,
- ram_addr_t phys_offset)
+ ram_addr_t size,
+ ram_addr_t phys_offset)
{
CPUPhysMemoryClient *client;
QLIST_FOREACH(client, &memory_client_list, list) {
@@ -1716,7 +1716,7 @@ static void cpu_notify_set_memory(target_phys_addr_t start_addr,
}
static int cpu_notify_sync_dirty_bitmap(target_phys_addr_t start,
- target_phys_addr_t end)
+ target_phys_addr_t end)
{
CPUPhysMemoryClient *client;
QLIST_FOREACH(client, &memory_client_list, list) {
@@ -1803,17 +1803,17 @@ int cpu_str_to_log_mask(const char *str)
p1 = strchr(p, ',');
if (!p1)
p1 = p + strlen(p);
- if(cmp1(p,p1-p,"all")) {
- for(item = cpu_log_items; item->mask != 0; item++) {
- mask |= item->mask;
- }
- } else {
- for(item = cpu_log_items; item->mask != 0; item++) {
- if (cmp1(p, p1 - p, item->name))
- goto found;
+ if(cmp1(p,p1-p,"all")) {
+ for(item = cpu_log_items; item->mask != 0; item++) {
+ mask |= item->mask;
+ }
+ } else {
+ for(item = cpu_log_items; item->mask != 0; item++) {
+ if (cmp1(p, p1 - p, item->name))
+ goto found;
+ }
+ return 0;
}
- return 0;
- }
found:
mask |= item->mask;
if (*p1 != ',')
@@ -1907,11 +1907,11 @@ static inline void tlb_flush_jmp_cache(CPUState *env, target_ulong addr)
overlap the flushed page. */
i = tb_jmp_cache_hash_page(addr - TARGET_PAGE_SIZE);
memset (&env->tb_jmp_cache[i], 0,
- TB_JMP_PAGE_SIZE * sizeof(TranslationBlock *));
+ TB_JMP_PAGE_SIZE * sizeof(TranslationBlock *));
i = tb_jmp_cache_hash_page(addr);
memset (&env->tb_jmp_cache[i], 0,
- TB_JMP_PAGE_SIZE * sizeof(TranslationBlock *));
+ TB_JMP_PAGE_SIZE * sizeof(TranslationBlock *));
}
static CPUTLBEntry s_cputlb_empty_entry = {
@@ -2085,7 +2085,7 @@ static inline void tlb_update_dirty(CPUTLBEntry *tlb_entry)
if ((tlb_entry->addr_write & ~TARGET_PAGE_MASK) == IO_MEM_RAM) {
p = (void *)(unsigned long)((tlb_entry->addr_write & TARGET_PAGE_MASK)
+ tlb_entry->addend);
- ram_addr = qemu_ram_addr_from_host(p);
+ ram_addr = qemu_ram_addr_from_host_nofail(p);
if (!cpu_physical_memory_is_dirty(ram_addr)) {
tlb_entry->addr_write |= TLB_NOTDIRTY;
}
@@ -2173,8 +2173,9 @@ void tlb_set_page(CPUState *env, target_ulong vaddr,
pd = p->phys_offset;
}
#if defined(DEBUG_TLB)
- printf("tlb_set_page: vaddr=" TARGET_FMT_lx " paddr=0x%08x prot=%x idx=%d smmu=%d pd=0x%08lx\n",
- vaddr, (int)paddr, prot, mmu_idx, is_softmmu, pd);
+ printf("tlb_set_page: vaddr=" TARGET_FMT_lx " paddr=0x" TARGET_FMT_plx
+ " prot=%x idx=%d pd=0x%08lx\n",
+ vaddr, paddr, prot, mmu_idx, pd);
#endif
address = vaddr;
@@ -2687,16 +2688,16 @@ static long gethugepagesize(const char *path)
int ret;
do {
- ret = statfs(path, &fs);
+ ret = statfs(path, &fs);
} while (ret != 0 && errno == EINTR);
if (ret != 0) {
- perror(path);
- return 0;
+ perror(path);
+ return 0;
}
if (fs.f_type != HUGETLBFS_MAGIC)
- fprintf(stderr, "Warning: path not on HugeTLBFS: %s\n", path);
+ fprintf(stderr, "Warning: path not on HugeTLBFS: %s\n", path);
return fs.f_bsize;
}
@@ -2715,7 +2716,7 @@ static void *file_ram_alloc(RAMBlock *block,
hpagesize = gethugepagesize(path);
if (!hpagesize) {
- return NULL;
+ return NULL;
}
if (memory < hpagesize) {
@@ -2728,14 +2729,14 @@ static void *file_ram_alloc(RAMBlock *block,
}
if (asprintf(&filename, "%s/qemu_back_mem.XXXXXX", path) == -1) {
- return NULL;
+ return NULL;
}
fd = mkstemp(filename);
if (fd < 0) {
- perror("unable to create backing store for hugepages");
- free(filename);
- return NULL;
+ perror("unable to create backing store for hugepages");
+ free(filename);
+ return NULL;
}
unlink(filename);
free(filename);
@@ -2749,7 +2750,7 @@ static void *file_ram_alloc(RAMBlock *block,
* mmap will fail.
*/
if (ftruncate(fd, memory))
- perror("ftruncate");
+ perror("ftruncate");
#ifdef MAP_POPULATE
/* NB: MAP_POPULATE won't exhaustively alloc all phys pages in the case
@@ -2762,9 +2763,9 @@ static void *file_ram_alloc(RAMBlock *block,
area = mmap(0, memory, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
#endif
if (area == MAP_FAILED) {
- perror("file_ram_alloc: can't mmap RAM pages");
- close(fd);
- return (NULL);
+ perror("file_ram_alloc: can't mmap RAM pages");
+ close(fd);
+ return (NULL);
}
block->fd = fd;
return area;
@@ -2808,7 +2809,8 @@ static ram_addr_t last_ram_offset(void)
return last;
}
-ram_addr_t qemu_ram_alloc(DeviceState *dev, const char *name, ram_addr_t size)
+ram_addr_t qemu_ram_alloc_from_ptr(DeviceState *dev, const char *name,
+ ram_addr_t size, void *host)
{
RAMBlock *new_block, *block;
@@ -2832,32 +2834,33 @@ ram_addr_t qemu_ram_alloc(DeviceState *dev, const char *name, ram_addr_t size)
}
}
- if (mem_path) {
+ if (host) {
+ new_block->host = host;
+ } else {
+ if (mem_path) {
#if defined (__linux__) && !defined(TARGET_S390X)
- new_block->host = file_ram_alloc(new_block, size, mem_path);
- if (!new_block->host) {
- new_block->host = qemu_vmalloc(size);
-#ifdef MADV_MERGEABLE
- madvise(new_block->host, size, MADV_MERGEABLE);
-#endif
- }
+ new_block->host = file_ram_alloc(new_block, size, mem_path);
+ if (!new_block->host) {
+ new_block->host = qemu_vmalloc(size);
+ qemu_madvise(new_block->host, size, QEMU_MADV_MERGEABLE);
+ }
#else
- fprintf(stderr, "-mem-path option unsupported\n");
- exit(1);
+ fprintf(stderr, "-mem-path option unsupported\n");
+ exit(1);
#endif
- } else {
+ } else {
#if defined(TARGET_S390X) && defined(CONFIG_KVM)
- /* XXX S390 KVM requires the topmost vma of the RAM to be < 256GB */
- new_block->host = mmap((void*)0x1000000, size,
- PROT_EXEC|PROT_READ|PROT_WRITE,
- MAP_SHARED | MAP_ANONYMOUS, -1, 0);
+ /* XXX S390 KVM requires the topmost vma of the RAM to be < 256GB */
+ new_block->host = mmap((void*)0x1000000, size,
+ PROT_EXEC|PROT_READ|PROT_WRITE,
+ MAP_SHARED | MAP_ANONYMOUS, -1, 0);
#else
- new_block->host = qemu_vmalloc(size);
-#endif
-#ifdef MADV_MERGEABLE
- madvise(new_block->host, size, MADV_MERGEABLE);
+ new_block->host = qemu_vmalloc(size);
#endif
+ qemu_madvise(new_block->host, size, QEMU_MADV_MERGEABLE);
+ }
}
+
new_block->offset = find_ram_offset(size);
new_block->length = size;
@@ -2874,6 +2877,11 @@ ram_addr_t qemu_ram_alloc(DeviceState *dev, const char *name, ram_addr_t size)
return new_block->offset;
}
+ram_addr_t qemu_ram_alloc(DeviceState *dev, const char *name, ram_addr_t size)
+{
+ return qemu_ram_alloc_from_ptr(dev, name, size, NULL);
+}
+
void qemu_ram_free(ram_addr_t addr)
{
RAMBlock *block;
@@ -2930,23 +2938,31 @@ void *qemu_get_ram_ptr(ram_addr_t addr)
return NULL;
}
-/* Some of the softmmu routines need to translate from a host pointer
- (typically a TLB entry) back to a ram offset. */
-ram_addr_t qemu_ram_addr_from_host(void *ptr)
+int qemu_ram_addr_from_host(void *ptr, ram_addr_t *ram_addr)
{
RAMBlock *block;
uint8_t *host = ptr;
QLIST_FOREACH(block, &ram_list.blocks, next) {
if (host - block->host < block->length) {
- return block->offset + (host - block->host);
+ *ram_addr = block->offset + (host - block->host);
+ return 0;
}
}
+ return -1;
+}
- fprintf(stderr, "Bad ram pointer %p\n", ptr);
- abort();
+/* Some of the softmmu routines need to translate from a host pointer
+ (typically a TLB entry) back to a ram offset. */
+ram_addr_t qemu_ram_addr_from_host_nofail(void *ptr)
+{
+ ram_addr_t ram_addr;
- return 0;
+ if (qemu_ram_addr_from_host(ptr, &ram_addr)) {
+ fprintf(stderr, "Bad ram pointer %p\n", ptr);
+ abort();
+ }
+ return ram_addr;
}
static uint32_t unassigned_mem_readb(void *opaque, target_phys_addr_t addr)
@@ -3282,6 +3298,8 @@ static int subpage_register (subpage_t *mmio, uint32_t start, uint32_t end,
printf("%s: %p start %08x end %08x idx %08x eidx %08x mem %ld\n", __func__,
mmio, start, end, idx, eidx, memory);
#endif
+ if ((memory & ~TARGET_PAGE_MASK) == IO_MEM_RAM)
+ memory = IO_MEM_UNASSIGNED;
memory = (memory >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1);
for (; idx <= eidx; idx++) {
mmio->sub_io_index[idx] = memory;
@@ -3693,7 +3711,7 @@ void cpu_physical_memory_unmap(void *buffer, target_phys_addr_t len,
{
if (buffer != bounce.buffer) {
if (is_write) {
- ram_addr_t addr1 = qemu_ram_addr_from_host(buffer);
+ ram_addr_t addr1 = qemu_ram_addr_from_host_nofail(buffer);
while (access_len) {
unsigned l;
l = TARGET_PAGE_SIZE;
diff --git a/feature_to_c.sh b/feature_to_c.sh
index dbf9f19c91..0994d9546e 100644
--- a/feature_to_c.sh
+++ b/feature_to_c.sh
@@ -63,7 +63,6 @@ for input; do
done
echo >> $output
-echo "extern const char *const xml_builtin[][2];" >> $output
echo "const char *const xml_builtin[][2] = {" >> $output
for input; do
diff --git a/fpu/softfloat.c b/fpu/softfloat.c
index e6065b4bc7..0b8279798c 100644
--- a/fpu/softfloat.c
+++ b/fpu/softfloat.c
@@ -2056,6 +2056,85 @@ float32 float32_sqrt( float32 a STATUS_PARAM )
}
/*----------------------------------------------------------------------------
+| Returns the binary exponential of the single-precision floating-point value
+| `a'. The operation is performed according to the IEC/IEEE Standard for
+| Binary Floating-Point Arithmetic.
+|
+| Uses the following identities:
+|
+| 1. -------------------------------------------------------------------------
+| x x*ln(2)
+| 2 = e
+|
+| 2. -------------------------------------------------------------------------
+| 2 3 4 5 n
+| x x x x x x x
+| e = 1 + --- + --- + --- + --- + --- + ... + --- + ...
+| 1! 2! 3! 4! 5! n!
+*----------------------------------------------------------------------------*/
+
+static const float64 float32_exp2_coefficients[15] =
+{
+ make_float64( 0x3ff0000000000000ll ), /* 1 */
+ make_float64( 0x3fe0000000000000ll ), /* 2 */
+ make_float64( 0x3fc5555555555555ll ), /* 3 */
+ make_float64( 0x3fa5555555555555ll ), /* 4 */
+ make_float64( 0x3f81111111111111ll ), /* 5 */
+ make_float64( 0x3f56c16c16c16c17ll ), /* 6 */
+ make_float64( 0x3f2a01a01a01a01all ), /* 7 */
+ make_float64( 0x3efa01a01a01a01all ), /* 8 */
+ make_float64( 0x3ec71de3a556c734ll ), /* 9 */
+ make_float64( 0x3e927e4fb7789f5cll ), /* 10 */
+ make_float64( 0x3e5ae64567f544e4ll ), /* 11 */
+ make_float64( 0x3e21eed8eff8d898ll ), /* 12 */
+ make_float64( 0x3de6124613a86d09ll ), /* 13 */
+ make_float64( 0x3da93974a8c07c9dll ), /* 14 */
+ make_float64( 0x3d6ae7f3e733b81fll ), /* 15 */
+};
+
+float32 float32_exp2( float32 a STATUS_PARAM )
+{
+ flag aSign;
+ int16 aExp;
+ bits32 aSig;
+ float64 r, x, xn;
+ int i;
+
+ aSig = extractFloat32Frac( a );
+ aExp = extractFloat32Exp( a );
+ aSign = extractFloat32Sign( a );
+
+ if ( aExp == 0xFF) {
+ if ( aSig ) return propagateFloat32NaN( a, float32_zero STATUS_VAR );
+ return (aSign) ? float32_zero : a;
+ }
+ if (aExp == 0) {
+ if (aSig == 0) return float32_one;
+ }
+
+ float_raise( float_flag_inexact STATUS_VAR);
+
+ /* ******************************* */
+ /* using float64 for approximation */
+ /* ******************************* */
+ x = float32_to_float64(a STATUS_VAR);
+ x = float64_mul(x, float64_ln2 STATUS_VAR);
+
+ xn = x;
+ r = float64_one;
+ for (i = 0 ; i < 15 ; i++) {
+ float64 f;
+
+ f = float64_mul(xn, float32_exp2_coefficients[i] STATUS_VAR);
+ r = float64_add(r, f STATUS_VAR);
+
+ xn = float64_mul(xn, x STATUS_VAR);
+ }
+
+ return float64_to_float32(r, status);
+}
+
+/*----------------------------------------------------------------------------
| Returns the binary log of the single-precision floating-point value `a'.
| The operation is performed according to the IEC/IEEE Standard for Binary
| Floating-Point Arithmetic.
diff --git a/fpu/softfloat.h b/fpu/softfloat.h
index 636591b04c..9528825522 100644
--- a/fpu/softfloat.h
+++ b/fpu/softfloat.h
@@ -275,6 +275,7 @@ float32 float32_mul( float32, float32 STATUS_PARAM );
float32 float32_div( float32, float32 STATUS_PARAM );
float32 float32_rem( float32, float32 STATUS_PARAM );
float32 float32_sqrt( float32 STATUS_PARAM );
+float32 float32_exp2( float32 STATUS_PARAM );
float32 float32_log2( float32 STATUS_PARAM );
int float32_eq( float32, float32 STATUS_PARAM );
int float32_le( float32, float32 STATUS_PARAM );
@@ -315,6 +316,7 @@ INLINE int float32_is_zero(float32 a)
#define float32_zero make_float32(0)
#define float32_one make_float32(0x3f800000)
+#define float32_ln2 make_float32(0x3f317218)
/*----------------------------------------------------------------------------
| Software IEC/IEEE double-precision conversion routines.
@@ -386,6 +388,7 @@ INLINE int float64_is_zero(float64 a)
#define float64_zero make_float64(0)
#define float64_one make_float64(0x3ff0000000000000LL)
+#define float64_ln2 make_float64(0x3fe62e42fefa39efLL)
#ifdef FLOATX80
diff --git a/fsdev/qemu-fsdev.c b/fsdev/qemu-fsdev.c
index ad69b0ea4e..0b332907bd 100644
--- a/fsdev/qemu-fsdev.c
+++ b/fsdev/qemu-fsdev.c
@@ -16,6 +16,7 @@
#include "qemu-queue.h"
#include "osdep.h"
#include "qemu-common.h"
+#include "qemu-config.h"
static QTAILQ_HEAD(FsTypeEntry_head, FsTypeListEntry) fstype_entries =
QTAILQ_HEAD_INITIALIZER(fstype_entries);
@@ -28,35 +29,47 @@ int qemu_fsdev_add(QemuOpts *opts)
{
struct FsTypeListEntry *fsle;
int i;
+ const char *fsdev_id = qemu_opts_id(opts);
+ const char *fstype = qemu_opt_get(opts, "fstype");
+ const char *path = qemu_opt_get(opts, "path");
+ const char *sec_model = qemu_opt_get(opts, "security_model");
- if (qemu_opts_id(opts) == NULL) {
+ if (!fsdev_id) {
fprintf(stderr, "fsdev: No id specified\n");
return -1;
}
- for (i = 0; i < ARRAY_SIZE(FsTypes); i++) {
- if (strcmp(FsTypes[i].name, qemu_opt_get(opts, "fstype")) == 0) {
- break;
+ if (fstype) {
+ for (i = 0; i < ARRAY_SIZE(FsTypes); i++) {
+ if (strcmp(FsTypes[i].name, fstype) == 0) {
+ break;
+ }
}
- }
- if (i == ARRAY_SIZE(FsTypes)) {
- fprintf(stderr, "fsdev: fstype %s not found\n",
- qemu_opt_get(opts, "fstype"));
+ if (i == ARRAY_SIZE(FsTypes)) {
+ fprintf(stderr, "fsdev: fstype %s not found\n", fstype);
+ return -1;
+ }
+ } else {
+ fprintf(stderr, "fsdev: No fstype specified\n");
return -1;
}
- if (qemu_opt_get(opts, "security_model") == NULL) {
+ if (!sec_model) {
fprintf(stderr, "fsdev: No security_model specified.\n");
return -1;
}
+ if (!path) {
+ fprintf(stderr, "fsdev: No path specified.\n");
+ return -1;
+ }
+
fsle = qemu_malloc(sizeof(*fsle));
- fsle->fse.fsdev_id = qemu_strdup(qemu_opts_id(opts));
- fsle->fse.path = qemu_strdup(qemu_opt_get(opts, "path"));
- fsle->fse.security_model = qemu_strdup(qemu_opt_get(opts,
- "security_model"));
+ fsle->fse.fsdev_id = qemu_strdup(fsdev_id);
+ fsle->fse.path = qemu_strdup(path);
+ fsle->fse.security_model = qemu_strdup(sec_model);
fsle->fse.ops = FsTypes[i].ops;
QTAILQ_INSERT_TAIL(&fstype_entries, fsle, next);
@@ -66,12 +79,22 @@ int qemu_fsdev_add(QemuOpts *opts)
FsTypeEntry *get_fsdev_fsentry(char *id)
{
- struct FsTypeListEntry *fsle;
+ if (id) {
+ struct FsTypeListEntry *fsle;
- QTAILQ_FOREACH(fsle, &fstype_entries, next) {
- if (strcmp(fsle->fse.fsdev_id, id) == 0) {
- return &fsle->fse;
+ QTAILQ_FOREACH(fsle, &fstype_entries, next) {
+ if (strcmp(fsle->fse.fsdev_id, id) == 0) {
+ return &fsle->fse;
+ }
}
}
return NULL;
}
+
+static void fsdev_register_config(void)
+{
+ qemu_add_opts(&qemu_fsdev_opts);
+ qemu_add_opts(&qemu_virtfs_opts);
+}
+machine_init(fsdev_register_config);
+
diff --git a/gdbstub.c b/gdbstub.c
index 2b03ef2aa0..0aa081b13b 100644
--- a/gdbstub.c
+++ b/gdbstub.c
@@ -1504,7 +1504,6 @@ static int memtox(char *buf, const char *mem, int len)
static const char *get_feature_xml(const char *p, const char **newp)
{
- extern const char *const xml_builtin[][2];
size_t len;
int i;
const char *name;
diff --git a/gdbstub.h b/gdbstub.h
index 219abdab0e..ce5fdcc223 100644
--- a/gdbstub.h
+++ b/gdbstub.h
@@ -38,4 +38,7 @@ int gdbserver_start(int);
int gdbserver_start(const char *port);
#endif
+/* in gdbstub-xml.c, generated by feature_to_c.sh */
+extern const char *const xml_builtin[][2];
+
#endif
diff --git a/hmp-commands.hx b/hmp-commands.hx
new file mode 100644
index 0000000000..81999aa1a9
--- /dev/null
+++ b/hmp-commands.hx
@@ -0,0 +1,1216 @@
+HXCOMM Use DEFHEADING() to define headings in both help text and texi
+HXCOMM Text between STEXI and ETEXI are copied to texi version and
+HXCOMM discarded from C version
+HXCOMM DEF(command, args, callback, arg_string, help) is used to construct
+HXCOMM monitor commands
+HXCOMM HXCOMM can be used for comments, discarded from both texi and C
+
+STEXI
+@table @option
+ETEXI
+
+ {
+ .name = "help|?",
+ .args_type = "name:s?",
+ .params = "[cmd]",
+ .help = "show the help",
+ .mhandler.cmd = do_help_cmd,
+ },
+
+STEXI
+@item help or ? [@var{cmd}]
+@findex help
+Show the help for all commands or just for command @var{cmd}.
+ETEXI
+
+ {
+ .name = "commit",
+ .args_type = "device:B",
+ .params = "device|all",
+ .help = "commit changes to the disk images (if -snapshot is used) or backing files",
+ .mhandler.cmd = do_commit,
+ },
+
+STEXI
+@item commit
+@findex commit
+Commit changes to the disk images (if -snapshot is used) or backing files.
+ETEXI
+
+ {
+ .name = "q|quit",
+ .args_type = "",
+ .params = "",
+ .help = "quit the emulator",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_quit,
+ },
+
+STEXI
+@item q or quit
+@findex quit
+Quit the emulator.
+ETEXI
+
+ {
+ .name = "eject",
+ .args_type = "force:-f,device:B",
+ .params = "[-f] device",
+ .help = "eject a removable medium (use -f to force it)",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_eject,
+ },
+
+STEXI
+@item eject [-f] @var{device}
+@findex eject
+Eject a removable medium (use -f to force it).
+ETEXI
+
+ {
+ .name = "change",
+ .args_type = "device:B,target:F,arg:s?",
+ .params = "device filename [format]",
+ .help = "change a removable medium, optional format",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_change,
+ },
+
+STEXI
+@item change @var{device} @var{setting}
+@findex change
+
+Change the configuration of a device.
+
+@table @option
+@item change @var{diskdevice} @var{filename} [@var{format}]
+Change the medium for a removable disk device to point to @var{filename}. eg
+
+@example
+(qemu) change ide1-cd0 /path/to/some.iso
+@end example
+
+@var{format} is optional.
+
+@item change vnc @var{display},@var{options}
+Change the configuration of the VNC server. The valid syntax for @var{display}
+and @var{options} are described at @ref{sec_invocation}. eg
+
+@example
+(qemu) change vnc localhost:1
+@end example
+
+@item change vnc password [@var{password}]
+
+Change the password associated with the VNC server. If the new password is not
+supplied, the monitor will prompt for it to be entered. VNC passwords are only
+significant up to 8 letters. eg
+
+@example
+(qemu) change vnc password
+Password: ********
+@end example
+
+@end table
+ETEXI
+
+ {
+ .name = "screendump",
+ .args_type = "filename:F",
+ .params = "filename",
+ .help = "save screen into PPM image 'filename'",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_screen_dump,
+ },
+
+STEXI
+@item screendump @var{filename}
+@findex screendump
+Save screen into PPM image @var{filename}.
+ETEXI
+
+ {
+ .name = "logfile",
+ .args_type = "filename:F",
+ .params = "filename",
+ .help = "output logs to 'filename'",
+ .mhandler.cmd = do_logfile,
+ },
+
+STEXI
+@item logfile @var{filename}
+@findex logfile
+Output logs to @var{filename}.
+ETEXI
+
+#ifdef CONFIG_SIMPLE_TRACE
+ {
+ .name = "trace-event",
+ .args_type = "name:s,option:b",
+ .params = "name on|off",
+ .help = "changes status of a specific trace event",
+ .mhandler.cmd = do_change_trace_event_state,
+ },
+
+STEXI
+@item trace-event
+@findex trace-event
+changes status of a trace event
+ETEXI
+
+ {
+ .name = "trace-file",
+ .args_type = "op:s?,arg:F?",
+ .params = "on|off|flush|set [arg]",
+ .help = "open, close, or flush trace file, or set a new file name",
+ .mhandler.cmd = do_trace_file,
+ },
+
+STEXI
+@item trace-file on|off|flush
+@findex trace-file
+Open, close, or flush the trace file. If no argument is given, the status of the trace file is displayed.
+ETEXI
+#endif
+
+ {
+ .name = "log",
+ .args_type = "items:s",
+ .params = "item1[,...]",
+ .help = "activate logging of the specified items to '/tmp/qemu.log'",
+ .mhandler.cmd = do_log,
+ },
+
+STEXI
+@item log @var{item1}[,...]
+@findex log
+Activate logging of the specified items to @file{/tmp/qemu.log}.
+ETEXI
+
+ {
+ .name = "savevm",
+ .args_type = "name:s?",
+ .params = "[tag|id]",
+ .help = "save a VM snapshot. If no tag or id are provided, a new snapshot is created",
+ .mhandler.cmd = do_savevm,
+ },
+
+STEXI
+@item savevm [@var{tag}|@var{id}]
+@findex savevm
+Create a snapshot of the whole virtual machine. If @var{tag} is
+provided, it is used as human readable identifier. If there is already
+a snapshot with the same tag or ID, it is replaced. More info at
+@ref{vm_snapshots}.
+ETEXI
+
+ {
+ .name = "loadvm",
+ .args_type = "name:s",
+ .params = "tag|id",
+ .help = "restore a VM snapshot from its tag or id",
+ .mhandler.cmd = do_loadvm,
+ },
+
+STEXI
+@item loadvm @var{tag}|@var{id}
+@findex loadvm
+Set the whole virtual machine to the snapshot identified by the tag
+@var{tag} or the unique snapshot ID @var{id}.
+ETEXI
+
+ {
+ .name = "delvm",
+ .args_type = "name:s",
+ .params = "tag|id",
+ .help = "delete a VM snapshot from its tag or id",
+ .mhandler.cmd = do_delvm,
+ },
+
+STEXI
+@item delvm @var{tag}|@var{id}
+@findex delvm
+Delete the snapshot identified by @var{tag} or @var{id}.
+ETEXI
+
+ {
+ .name = "singlestep",
+ .args_type = "option:s?",
+ .params = "[on|off]",
+ .help = "run emulation in singlestep mode or switch to normal mode",
+ .mhandler.cmd = do_singlestep,
+ },
+
+STEXI
+@item singlestep [off]
+@findex singlestep
+Run the emulation in single step mode.
+If called with option off, the emulation returns to normal mode.
+ETEXI
+
+ {
+ .name = "stop",
+ .args_type = "",
+ .params = "",
+ .help = "stop emulation",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_stop,
+ },
+
+STEXI
+@item stop
+@findex stop
+Stop emulation.
+ETEXI
+
+ {
+ .name = "c|cont",
+ .args_type = "",
+ .params = "",
+ .help = "resume emulation",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_cont,
+ },
+
+STEXI
+@item c or cont
+@findex cont
+Resume emulation.
+ETEXI
+
+ {
+ .name = "gdbserver",
+ .args_type = "device:s?",
+ .params = "[device]",
+ .help = "start gdbserver on given device (default 'tcp::1234'), stop with 'none'",
+ .mhandler.cmd = do_gdbserver,
+ },
+
+STEXI
+@item gdbserver [@var{port}]
+@findex gdbserver
+Start gdbserver session (default @var{port}=1234)
+ETEXI
+
+ {
+ .name = "x",
+ .args_type = "fmt:/,addr:l",
+ .params = "/fmt addr",
+ .help = "virtual memory dump starting at 'addr'",
+ .mhandler.cmd = do_memory_dump,
+ },
+
+STEXI
+@item x/fmt @var{addr}
+@findex x
+Virtual memory dump starting at @var{addr}.
+ETEXI
+
+ {
+ .name = "xp",
+ .args_type = "fmt:/,addr:l",
+ .params = "/fmt addr",
+ .help = "physical memory dump starting at 'addr'",
+ .mhandler.cmd = do_physical_memory_dump,
+ },
+
+STEXI
+@item xp /@var{fmt} @var{addr}
+@findex xp
+Physical memory dump starting at @var{addr}.
+
+@var{fmt} is a format which tells the command how to format the
+data. Its syntax is: @option{/@{count@}@{format@}@{size@}}
+
+@table @var
+@item count
+is the number of items to be dumped.
+
+@item format
+can be x (hex), d (signed decimal), u (unsigned decimal), o (octal),
+c (char) or i (asm instruction).
+
+@item size
+can be b (8 bits), h (16 bits), w (32 bits) or g (64 bits). On x86,
+@code{h} or @code{w} can be specified with the @code{i} format to
+respectively select 16 or 32 bit code instruction size.
+
+@end table
+
+Examples:
+@itemize
+@item
+Dump 10 instructions at the current instruction pointer:
+@example
+(qemu) x/10i $eip
+0x90107063: ret
+0x90107064: sti
+0x90107065: lea 0x0(%esi,1),%esi
+0x90107069: lea 0x0(%edi,1),%edi
+0x90107070: ret
+0x90107071: jmp 0x90107080
+0x90107073: nop
+0x90107074: nop
+0x90107075: nop
+0x90107076: nop
+@end example
+
+@item
+Dump 80 16 bit values at the start of the video memory.
+@smallexample
+(qemu) xp/80hx 0xb8000
+0x000b8000: 0x0b50 0x0b6c 0x0b65 0x0b78 0x0b38 0x0b36 0x0b2f 0x0b42
+0x000b8010: 0x0b6f 0x0b63 0x0b68 0x0b73 0x0b20 0x0b56 0x0b47 0x0b41
+0x000b8020: 0x0b42 0x0b69 0x0b6f 0x0b73 0x0b20 0x0b63 0x0b75 0x0b72
+0x000b8030: 0x0b72 0x0b65 0x0b6e 0x0b74 0x0b2d 0x0b63 0x0b76 0x0b73
+0x000b8040: 0x0b20 0x0b30 0x0b35 0x0b20 0x0b4e 0x0b6f 0x0b76 0x0b20
+0x000b8050: 0x0b32 0x0b30 0x0b30 0x0b33 0x0720 0x0720 0x0720 0x0720
+0x000b8060: 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720
+0x000b8070: 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720
+0x000b8080: 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720
+0x000b8090: 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720
+@end smallexample
+@end itemize
+ETEXI
+
+ {
+ .name = "p|print",
+ .args_type = "fmt:/,val:l",
+ .params = "/fmt expr",
+ .help = "print expression value (use $reg for CPU register access)",
+ .mhandler.cmd = do_print,
+ },
+
+STEXI
+@item p or print/@var{fmt} @var{expr}
+@findex print
+
+Print expression value. Only the @var{format} part of @var{fmt} is
+used.
+ETEXI
+
+ {
+ .name = "i",
+ .args_type = "fmt:/,addr:i,index:i.",
+ .params = "/fmt addr",
+ .help = "I/O port read",
+ .mhandler.cmd = do_ioport_read,
+ },
+
+STEXI
+Read I/O port.
+ETEXI
+
+ {
+ .name = "o",
+ .args_type = "fmt:/,addr:i,val:i",
+ .params = "/fmt addr value",
+ .help = "I/O port write",
+ .mhandler.cmd = do_ioport_write,
+ },
+
+STEXI
+Write to I/O port.
+ETEXI
+
+ {
+ .name = "sendkey",
+ .args_type = "string:s,hold_time:i?",
+ .params = "keys [hold_ms]",
+ .help = "send keys to the VM (e.g. 'sendkey ctrl-alt-f1', default hold time=100 ms)",
+ .mhandler.cmd = do_sendkey,
+ },
+
+STEXI
+@item sendkey @var{keys}
+@findex sendkey
+
+Send @var{keys} to the emulator. @var{keys} could be the name of the
+key or @code{#} followed by the raw value in either decimal or hexadecimal
+format. Use @code{-} to press several keys simultaneously. Example:
+@example
+sendkey ctrl-alt-f1
+@end example
+
+This command is useful to send keys that your graphical user interface
+intercepts at low level, such as @code{ctrl-alt-f1} in X Window.
+ETEXI
+
+ {
+ .name = "system_reset",
+ .args_type = "",
+ .params = "",
+ .help = "reset the system",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_system_reset,
+ },
+
+STEXI
+@item system_reset
+@findex system_reset
+
+Reset the system.
+ETEXI
+
+ {
+ .name = "system_powerdown",
+ .args_type = "",
+ .params = "",
+ .help = "send system power down event",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_system_powerdown,
+ },
+
+STEXI
+@item system_powerdown
+@findex system_powerdown
+
+Power down the system (if supported).
+ETEXI
+
+ {
+ .name = "sum",
+ .args_type = "start:i,size:i",
+ .params = "addr size",
+ .help = "compute the checksum of a memory region",
+ .mhandler.cmd = do_sum,
+ },
+
+STEXI
+@item sum @var{addr} @var{size}
+@findex sum
+
+Compute the checksum of a memory region.
+ETEXI
+
+ {
+ .name = "usb_add",
+ .args_type = "devname:s",
+ .params = "device",
+ .help = "add USB device (e.g. 'host:bus.addr' or 'host:vendor_id:product_id')",
+ .mhandler.cmd = do_usb_add,
+ },
+
+STEXI
+@item usb_add @var{devname}
+@findex usb_add
+
+Add the USB device @var{devname}. For details of available devices see
+@ref{usb_devices}
+ETEXI
+
+ {
+ .name = "usb_del",
+ .args_type = "devname:s",
+ .params = "device",
+ .help = "remove USB device 'bus.addr'",
+ .mhandler.cmd = do_usb_del,
+ },
+
+STEXI
+@item usb_del @var{devname}
+@findex usb_del
+
+Remove the USB device @var{devname} from the QEMU virtual USB
+hub. @var{devname} has the syntax @code{bus.addr}. Use the monitor
+command @code{info usb} to see the devices you can remove.
+ETEXI
+
+ {
+ .name = "device_add",
+ .args_type = "device:O",
+ .params = "driver[,prop=value][,...]",
+ .help = "add device, like -device on the command line",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_device_add,
+ },
+
+STEXI
+@item device_add @var{config}
+@findex device_add
+
+Add device.
+ETEXI
+
+ {
+ .name = "device_del",
+ .args_type = "id:s",
+ .params = "device",
+ .help = "remove device",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_device_del,
+ },
+
+STEXI
+@item device_del @var{id}
+@findex device_del
+
+Remove device @var{id}.
+ETEXI
+
+ {
+ .name = "cpu",
+ .args_type = "index:i",
+ .params = "index",
+ .help = "set the default CPU",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_cpu_set,
+ },
+
+STEXI
+@item cpu @var{index}
+@findex cpu
+Set the default CPU.
+ETEXI
+
+ {
+ .name = "mouse_move",
+ .args_type = "dx_str:s,dy_str:s,dz_str:s?",
+ .params = "dx dy [dz]",
+ .help = "send mouse move events",
+ .mhandler.cmd = do_mouse_move,
+ },
+
+STEXI
+@item mouse_move @var{dx} @var{dy} [@var{dz}]
+@findex mouse_move
+Move the active mouse to the specified coordinates @var{dx} @var{dy}
+with optional scroll axis @var{dz}.
+ETEXI
+
+ {
+ .name = "mouse_button",
+ .args_type = "button_state:i",
+ .params = "state",
+ .help = "change mouse button state (1=L, 2=M, 4=R)",
+ .mhandler.cmd = do_mouse_button,
+ },
+
+STEXI
+@item mouse_button @var{val}
+@findex mouse_button
+Change the active mouse button state @var{val} (1=L, 2=M, 4=R).
+ETEXI
+
+ {
+ .name = "mouse_set",
+ .args_type = "index:i",
+ .params = "index",
+ .help = "set which mouse device receives events",
+ .mhandler.cmd = do_mouse_set,
+ },
+
+STEXI
+@item mouse_set @var{index}
+@findex mouse_set
+Set which mouse device receives events at given @var{index}, index
+can be obtained with
+@example
+info mice
+@end example
+ETEXI
+
+#ifdef HAS_AUDIO
+ {
+ .name = "wavcapture",
+ .args_type = "path:F,freq:i?,bits:i?,nchannels:i?",
+ .params = "path [frequency [bits [channels]]]",
+ .help = "capture audio to a wave file (default frequency=44100 bits=16 channels=2)",
+ .mhandler.cmd = do_wav_capture,
+ },
+#endif
+STEXI
+@item wavcapture @var{filename} [@var{frequency} [@var{bits} [@var{channels}]]]
+@findex wavcapture
+Capture audio into @var{filename}. Using sample rate @var{frequency}
+bits per sample @var{bits} and number of channels @var{channels}.
+
+Defaults:
+@itemize @minus
+@item Sample rate = 44100 Hz - CD quality
+@item Bits = 16
+@item Number of channels = 2 - Stereo
+@end itemize
+ETEXI
+
+#ifdef HAS_AUDIO
+ {
+ .name = "stopcapture",
+ .args_type = "n:i",
+ .params = "capture index",
+ .help = "stop capture",
+ .mhandler.cmd = do_stop_capture,
+ },
+#endif
+STEXI
+@item stopcapture @var{index}
+@findex stopcapture
+Stop capture with a given @var{index}, index can be obtained with
+@example
+info capture
+@end example
+ETEXI
+
+ {
+ .name = "memsave",
+ .args_type = "val:l,size:i,filename:s",
+ .params = "addr size file",
+ .help = "save to disk virtual memory dump starting at 'addr' of size 'size'",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_memory_save,
+ },
+
+STEXI
+@item memsave @var{addr} @var{size} @var{file}
+@findex memsave
+save to disk virtual memory dump starting at @var{addr} of size @var{size}.
+ETEXI
+
+ {
+ .name = "pmemsave",
+ .args_type = "val:l,size:i,filename:s",
+ .params = "addr size file",
+ .help = "save to disk physical memory dump starting at 'addr' of size 'size'",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_physical_memory_save,
+ },
+
+STEXI
+@item pmemsave @var{addr} @var{size} @var{file}
+@findex pmemsave
+save to disk physical memory dump starting at @var{addr} of size @var{size}.
+ETEXI
+
+ {
+ .name = "boot_set",
+ .args_type = "bootdevice:s",
+ .params = "bootdevice",
+ .help = "define new values for the boot device list",
+ .mhandler.cmd = do_boot_set,
+ },
+
+STEXI
+@item boot_set @var{bootdevicelist}
+@findex boot_set
+
+Define new values for the boot device list. Those values will override
+the values specified on the command line through the @code{-boot} option.
+
+The values that can be specified here depend on the machine type, but are
+the same that can be specified in the @code{-boot} command line option.
+ETEXI
+
+#if defined(TARGET_I386)
+ {
+ .name = "nmi",
+ .args_type = "cpu_index:i",
+ .params = "cpu",
+ .help = "inject an NMI on the given CPU",
+ .mhandler.cmd = do_inject_nmi,
+ },
+#endif
+STEXI
+@item nmi @var{cpu}
+@findex nmi
+Inject an NMI on the given CPU (x86 only).
+ETEXI
+
+ {
+ .name = "migrate",
+ .args_type = "detach:-d,blk:-b,inc:-i,uri:s",
+ .params = "[-d] [-b] [-i] uri",
+ .help = "migrate to URI (using -d to not wait for completion)"
+ "\n\t\t\t -b for migration without shared storage with"
+ " full copy of disk\n\t\t\t -i for migration without "
+ "shared storage with incremental copy of disk "
+ "(base image shared between src and destination)",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_migrate,
+ },
+
+
+STEXI
+@item migrate [-d] [-b] [-i] @var{uri}
+@findex migrate
+Migrate to @var{uri} (using -d to not wait for completion).
+ -b for migration with full copy of disk
+ -i for migration with incremental copy of disk (base image is shared)
+ETEXI
+
+ {
+ .name = "migrate_cancel",
+ .args_type = "",
+ .params = "",
+ .help = "cancel the current VM migration",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_migrate_cancel,
+ },
+
+STEXI
+@item migrate_cancel
+@findex migrate_cancel
+Cancel the current VM migration.
+ETEXI
+
+ {
+ .name = "migrate_set_speed",
+ .args_type = "value:f",
+ .params = "value",
+ .help = "set maximum speed (in bytes) for migrations",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_migrate_set_speed,
+ },
+
+STEXI
+@item migrate_set_speed @var{value}
+@findex migrate_set_speed
+Set maximum speed to @var{value} (in bytes) for migrations.
+ETEXI
+
+ {
+ .name = "migrate_set_downtime",
+ .args_type = "value:T",
+ .params = "value",
+ .help = "set maximum tolerated downtime (in seconds) for migrations",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_migrate_set_downtime,
+ },
+
+STEXI
+@item migrate_set_downtime @var{second}
+@findex migrate_set_downtime
+Set maximum tolerated downtime (in seconds) for migration.
+ETEXI
+
+#if defined(TARGET_I386)
+ {
+ .name = "drive_add",
+ .args_type = "pci_addr:s,opts:s",
+ .params = "[[<domain>:]<bus>:]<slot>\n"
+ "[file=file][,if=type][,bus=n]\n"
+ "[,unit=m][,media=d][index=i]\n"
+ "[,cyls=c,heads=h,secs=s[,trans=t]]\n"
+ "[snapshot=on|off][,cache=on|off]",
+ .help = "add drive to PCI storage controller",
+ .mhandler.cmd = drive_hot_add,
+ },
+#endif
+
+STEXI
+@item drive_add
+@findex drive_add
+Add drive to PCI storage controller.
+ETEXI
+
+#if defined(TARGET_I386)
+ {
+ .name = "pci_add",
+ .args_type = "pci_addr:s,type:s,opts:s?",
+ .params = "auto|[[<domain>:]<bus>:]<slot> nic|storage [[vlan=n][,macaddr=addr][,model=type]] [file=file][,if=type][,bus=nr]...",
+ .help = "hot-add PCI device",
+ .mhandler.cmd = pci_device_hot_add,
+ },
+#endif
+
+STEXI
+@item pci_add
+@findex pci_add
+Hot-add PCI device.
+ETEXI
+
+#if defined(TARGET_I386)
+ {
+ .name = "pci_del",
+ .args_type = "pci_addr:s",
+ .params = "[[<domain>:]<bus>:]<slot>",
+ .help = "hot remove PCI device",
+ .mhandler.cmd = do_pci_device_hot_remove,
+ },
+#endif
+
+STEXI
+@item pci_del
+@findex pci_del
+Hot remove PCI device.
+ETEXI
+
+ {
+ .name = "host_net_add",
+ .args_type = "device:s,opts:s?",
+ .params = "tap|user|socket|vde|dump [options]",
+ .help = "add host VLAN client",
+ .mhandler.cmd = net_host_device_add,
+ },
+
+STEXI
+@item host_net_add
+@findex host_net_add
+Add host VLAN client.
+ETEXI
+
+ {
+ .name = "host_net_remove",
+ .args_type = "vlan_id:i,device:s",
+ .params = "vlan_id name",
+ .help = "remove host VLAN client",
+ .mhandler.cmd = net_host_device_remove,
+ },
+
+STEXI
+@item host_net_remove
+@findex host_net_remove
+Remove host VLAN client.
+ETEXI
+
+ {
+ .name = "netdev_add",
+ .args_type = "netdev:O",
+ .params = "[user|tap|socket],id=str[,prop=value][,...]",
+ .help = "add host network device",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_netdev_add,
+ },
+
+STEXI
+@item netdev_add
+@findex netdev_add
+Add host network device.
+ETEXI
+
+ {
+ .name = "netdev_del",
+ .args_type = "id:s",
+ .params = "id",
+ .help = "remove host network device",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_netdev_del,
+ },
+
+STEXI
+@item netdev_del
+@findex netdev_del
+Remove host network device.
+ETEXI
+
+#ifdef CONFIG_SLIRP
+ {
+ .name = "hostfwd_add",
+ .args_type = "arg1:s,arg2:s?,arg3:s?",
+ .params = "[vlan_id name] [tcp|udp]:[hostaddr]:hostport-[guestaddr]:guestport",
+ .help = "redirect TCP or UDP connections from host to guest (requires -net user)",
+ .mhandler.cmd = net_slirp_hostfwd_add,
+ },
+#endif
+STEXI
+@item hostfwd_add
+@findex hostfwd_add
+Redirect TCP or UDP connections from host to guest (requires -net user).
+ETEXI
+
+#ifdef CONFIG_SLIRP
+ {
+ .name = "hostfwd_remove",
+ .args_type = "arg1:s,arg2:s?,arg3:s?",
+ .params = "[vlan_id name] [tcp|udp]:[hostaddr]:hostport",
+ .help = "remove host-to-guest TCP or UDP redirection",
+ .mhandler.cmd = net_slirp_hostfwd_remove,
+ },
+
+#endif
+STEXI
+@item hostfwd_remove
+@findex hostfwd_remove
+Remove host-to-guest TCP or UDP redirection.
+ETEXI
+
+ {
+ .name = "balloon",
+ .args_type = "value:M",
+ .params = "target",
+ .help = "request VM to change its memory allocation (in MB)",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_async = do_balloon,
+ .flags = MONITOR_CMD_ASYNC,
+ },
+
+STEXI
+@item balloon @var{value}
+@findex balloon
+Request VM to change its memory allocation to @var{value} (in MB).
+ETEXI
+
+ {
+ .name = "set_link",
+ .args_type = "name:s,up:b",
+ .params = "name on|off",
+ .help = "change the link status of a network adapter",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_set_link,
+ },
+
+STEXI
+@item set_link @var{name} [on|off]
+@findex set_link
+Switch link @var{name} on (i.e. up) or off (i.e. down).
+ETEXI
+
+ {
+ .name = "watchdog_action",
+ .args_type = "action:s",
+ .params = "[reset|shutdown|poweroff|pause|debug|none]",
+ .help = "change watchdog action",
+ .mhandler.cmd = do_watchdog_action,
+ },
+
+STEXI
+@item watchdog_action
+@findex watchdog_action
+Change watchdog action.
+ETEXI
+
+ {
+ .name = "acl_show",
+ .args_type = "aclname:s",
+ .params = "aclname",
+ .help = "list rules in the access control list",
+ .mhandler.cmd = do_acl_show,
+ },
+
+STEXI
+@item acl_show @var{aclname}
+@findex acl_show
+List all the matching rules in the access control list, and the default
+policy. There are currently two named access control lists,
+@var{vnc.x509dname} and @var{vnc.username} matching on the x509 client
+certificate distinguished name, and SASL username respectively.
+ETEXI
+
+ {
+ .name = "acl_policy",
+ .args_type = "aclname:s,policy:s",
+ .params = "aclname allow|deny",
+ .help = "set default access control list policy",
+ .mhandler.cmd = do_acl_policy,
+ },
+
+STEXI
+@item acl_policy @var{aclname} @code{allow|deny}
+@findex acl_policy
+Set the default access control list policy, used in the event that
+none of the explicit rules match. The default policy at startup is
+always @code{deny}.
+ETEXI
+
+ {
+ .name = "acl_add",
+ .args_type = "aclname:s,match:s,policy:s,index:i?",
+ .params = "aclname match allow|deny [index]",
+ .help = "add a match rule to the access control list",
+ .mhandler.cmd = do_acl_add,
+ },
+
+STEXI
+@item acl_add @var{aclname} @var{match} @code{allow|deny} [@var{index}]
+@findex acl_add
+Add a match rule to the access control list, allowing or denying access.
+The match will normally be an exact username or x509 distinguished name,
+but can optionally include wildcard globs. eg @code{*@@EXAMPLE.COM} to
+allow all users in the @code{EXAMPLE.COM} kerberos realm. The match will
+normally be appended to the end of the ACL, but can be inserted
+earlier in the list if the optional @var{index} parameter is supplied.
+ETEXI
+
+ {
+ .name = "acl_remove",
+ .args_type = "aclname:s,match:s",
+ .params = "aclname match",
+ .help = "remove a match rule from the access control list",
+ .mhandler.cmd = do_acl_remove,
+ },
+
+STEXI
+@item acl_remove @var{aclname} @var{match}
+@findex acl_remove
+Remove the specified match rule from the access control list.
+ETEXI
+
+ {
+ .name = "acl_reset",
+ .args_type = "aclname:s",
+ .params = "aclname",
+ .help = "reset the access control list",
+ .mhandler.cmd = do_acl_reset,
+ },
+
+STEXI
+@item acl_reset @var{aclname}
+@findex acl_reset
+Remove all matches from the access control list, and set the default
+policy back to @code{deny}.
+ETEXI
+
+#if defined(TARGET_I386)
+
+ {
+ .name = "mce",
+ .args_type = "cpu_index:i,bank:i,status:l,mcg_status:l,addr:l,misc:l",
+ .params = "cpu bank status mcgstatus addr misc",
+ .help = "inject a MCE on the given CPU",
+ .mhandler.cmd = do_inject_mce,
+ },
+
+#endif
+STEXI
+@item mce @var{cpu} @var{bank} @var{status} @var{mcgstatus} @var{addr} @var{misc}
+@findex mce (x86)
+Inject an MCE on the given CPU (x86 only).
+ETEXI
+
+ {
+ .name = "getfd",
+ .args_type = "fdname:s",
+ .params = "getfd name",
+ .help = "receive a file descriptor via SCM rights and assign it a name",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_getfd,
+ },
+
+STEXI
+@item getfd @var{fdname}
+@findex getfd
+If a file descriptor is passed alongside this command using the SCM_RIGHTS
+mechanism on unix sockets, it is stored using the name @var{fdname} for
+later use by other monitor commands.
+ETEXI
+
+ {
+ .name = "closefd",
+ .args_type = "fdname:s",
+ .params = "closefd name",
+ .help = "close a file descriptor previously passed via SCM rights",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_closefd,
+ },
+
+STEXI
+@item closefd @var{fdname}
+@findex closefd
+Close the file descriptor previously assigned to @var{fdname} using the
+@code{getfd} command. This is only needed if the file descriptor was never
+used by another monitor command.
+ETEXI
+
+ {
+ .name = "block_passwd",
+ .args_type = "device:B,password:s",
+ .params = "block_passwd device password",
+ .help = "set the password of encrypted block devices",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_block_set_passwd,
+ },
+
+STEXI
+@item block_passwd @var{device} @var{password}
+@findex block_passwd
+Set the encrypted device @var{device} password to @var{password}
+ETEXI
+
+ {
+ .name = "info",
+ .args_type = "item:s?",
+ .params = "[subcommand]",
+ .help = "show various information about the system state",
+ .mhandler.cmd = do_info,
+ },
+
+STEXI
+@item info @var{subcommand}
+@findex info
+Show various information about the system state.
+
+@table @option
+@item info version
+show the version of QEMU
+@item info network
+show the various VLANs and the associated devices
+@item info chardev
+show the character devices
+@item info block
+show the block devices
+@item info blockstats
+show block device statistics
+@item info registers
+show the cpu registers
+@item info cpus
+show infos for each CPU
+@item info history
+show the command line history
+@item info irq
+show the interrupts statistics (if available)
+@item info pic
+show i8259 (PIC) state
+@item info pci
+show emulated PCI device info
+@item info tlb
+show virtual to physical memory mappings (i386 only)
+@item info mem
+show the active virtual memory mappings (i386 only)
+@item info jit
+show dynamic compiler info
+@item info kvm
+show KVM information
+@item info numa
+show NUMA information
+@item info kvm
+show KVM information
+@item info usb
+show USB devices plugged on the virtual USB hub
+@item info usbhost
+show all USB host devices
+@item info profile
+show profiling information
+@item info capture
+show information about active capturing
+@item info snapshots
+show list of VM snapshots
+@item info status
+show the current VM status (running|paused)
+@item info pcmcia
+show guest PCMCIA status
+@item info mice
+show which guest mouse is receiving events
+@item info vnc
+show the vnc server status
+@item info name
+show the current VM name
+@item info uuid
+show the current VM UUID
+@item info cpustats
+show CPU statistics
+@item info usernet
+show user network stack connection states
+@item info migrate
+show migration status
+@item info balloon
+show balloon information
+@item info qtree
+show device tree
+@item info qdm
+show qdev device model list
+@item info roms
+show roms
+@end table
+ETEXI
+
+#ifdef CONFIG_SIMPLE_TRACE
+STEXI
+@item info trace
+show contents of trace buffer
+@item info trace-events
+show available trace events and their state
+ETEXI
+#endif
+
+STEXI
+@end table
+ETEXI
diff --git a/hw/acpi.c b/hw/acpi.c
index c7044b1fae..8071e7beba 100644
--- a/hw/acpi.c
+++ b/hw/acpi.c
@@ -50,6 +50,8 @@ int acpi_table_add(const char *t)
char buf[1024], *p, *f;
struct acpi_table_header acpi_hdr;
unsigned long val;
+ uint32_t length;
+ struct acpi_table_header *acpi_hdr_p;
size_t off;
memset(&acpi_hdr, 0, sizeof(acpi_hdr));
@@ -108,7 +110,7 @@ int acpi_table_add(const char *t)
buf[0] = '\0';
}
- acpi_hdr.length = sizeof(acpi_hdr);
+ length = sizeof(acpi_hdr);
f = buf;
while (buf[0]) {
@@ -120,7 +122,7 @@ int acpi_table_add(const char *t)
fprintf(stderr, "Can't stat file '%s': %s\n", f, strerror(errno));
goto out;
}
- acpi_hdr.length += s.st_size;
+ length += s.st_size;
if (!n)
break;
*n = ':';
@@ -131,12 +133,12 @@ int acpi_table_add(const char *t)
acpi_tables_len = sizeof(uint16_t);
acpi_tables = qemu_mallocz(acpi_tables_len);
}
+ acpi_tables = qemu_realloc(acpi_tables,
+ acpi_tables_len + sizeof(uint16_t) + length);
p = acpi_tables + acpi_tables_len;
- acpi_tables_len += sizeof(uint16_t) + acpi_hdr.length;
- acpi_tables = qemu_realloc(acpi_tables, acpi_tables_len);
+ acpi_tables_len += sizeof(uint16_t) + length;
- acpi_hdr.length = cpu_to_le32(acpi_hdr.length);
- *(uint16_t*)p = acpi_hdr.length;
+ *(uint16_t*)p = cpu_to_le32(length);
p += sizeof(uint16_t);
memcpy(p, &acpi_hdr, sizeof(acpi_hdr));
off = sizeof(acpi_hdr);
@@ -157,7 +159,9 @@ int acpi_table_add(const char *t)
goto out;
}
- do {
+ /* off < length is necessary because file size can be changed
+ under our foot */
+ while(s.st_size && off < length) {
int r;
r = read(fd, p + off, s.st_size);
if (r > 0) {
@@ -167,15 +171,21 @@ int acpi_table_add(const char *t)
close(fd);
goto out;
}
- } while(s.st_size);
+ }
close(fd);
if (!n)
break;
f = n + 1;
}
+ if (off < length) {
+ /* don't pass random value in process to guest */
+ memset(p + off, 0, length - off);
+ }
- ((struct acpi_table_header*)p)->checksum = acpi_checksum((uint8_t*)p, off);
+ acpi_hdr_p = (struct acpi_table_header*)p;
+ acpi_hdr_p->length = cpu_to_le32(length);
+ acpi_hdr_p->checksum = acpi_checksum((uint8_t*)p, length);
/* increase number of tables */
(*(uint16_t*)acpi_tables) =
cpu_to_le32(le32_to_cpu(*(uint16_t*)acpi_tables) + 1);
diff --git a/hw/acpi_piix4.c b/hw/acpi_piix4.c
index 24dfcf2039..66c7885d62 100644
--- a/hw/acpi_piix4.c
+++ b/hw/acpi_piix4.c
@@ -22,6 +22,7 @@
#include "pci.h"
#include "acpi.h"
#include "sysemu.h"
+#include "range.h"
//#define DEBUG
@@ -37,6 +38,8 @@
#define PCI_BASE 0xae00
#define PCI_EJ_BASE 0xae08
+#define PIIX4_PCI_HOTPLUG_STATUS 2
+
struct gpe_regs {
uint16_t sts; /* status */
uint16_t en; /* enabled */
@@ -104,7 +107,9 @@ static void pm_update_sci(PIIX4PMState *s)
(ACPI_BITMASK_RT_CLOCK_ENABLE |
ACPI_BITMASK_POWER_BUTTON_ENABLE |
ACPI_BITMASK_GLOBAL_LOCK_ENABLE |
- ACPI_BITMASK_TIMER_ENABLE)) != 0);
+ ACPI_BITMASK_TIMER_ENABLE)) != 0) ||
+ (((s->gpe.sts & s->gpe.en) & PIIX4_PCI_HOTPLUG_STATUS) != 0);
+
qemu_set_irq(s->irq, sci_level);
/* schedule a timer interruption if needed */
if ((s->pmen & ACPI_BITMASK_TIMER_ENABLE) &&
@@ -459,7 +464,9 @@ static uint32_t gpe_read_val(uint16_t val, uint32_t addr)
static uint32_t gpe_readb(void *opaque, uint32_t addr)
{
uint32_t val = 0;
- struct gpe_regs *g = opaque;
+ PIIX4PMState *s = opaque;
+ struct gpe_regs *g = &s->gpe;
+
switch (addr) {
case GPE_BASE:
case GPE_BASE + 1:
@@ -499,7 +506,9 @@ static void gpe_reset_val(uint16_t *cur, int addr, uint32_t val)
static void gpe_writeb(void *opaque, uint32_t addr, uint32_t val)
{
- struct gpe_regs *g = opaque;
+ PIIX4PMState *s = opaque;
+ struct gpe_regs *g = &s->gpe;
+
switch (addr) {
case GPE_BASE:
case GPE_BASE + 1:
@@ -511,7 +520,9 @@ static void gpe_writeb(void *opaque, uint32_t addr, uint32_t val)
break;
default:
break;
- }
+ }
+
+ pm_update_sci(s);
PIIX4_DPRINTF("gpe write %x <== %d\n", addr, val);
}
@@ -578,11 +589,10 @@ static int piix4_device_hotplug(DeviceState *qdev, PCIDevice *dev, int state);
static void piix4_acpi_system_hot_add_init(PCIBus *bus, PIIX4PMState *s)
{
- struct gpe_regs *gpe = &s->gpe;
struct pci_status *pci0_status = &s->pci0_status;
- register_ioport_write(GPE_BASE, 4, 1, gpe_writeb, gpe);
- register_ioport_read(GPE_BASE, 4, 1, gpe_readb, gpe);
+ register_ioport_write(GPE_BASE, 4, 1, gpe_writeb, s);
+ register_ioport_read(GPE_BASE, 4, 1, gpe_readb, s);
register_ioport_write(PCI_BASE, 8, 4, pcihotplug_write, pci0_status);
register_ioport_read(PCI_BASE, 8, 4, pcihotplug_read, pci0_status);
@@ -595,13 +605,13 @@ static void piix4_acpi_system_hot_add_init(PCIBus *bus, PIIX4PMState *s)
static void enable_device(PIIX4PMState *s, int slot)
{
- s->gpe.sts |= 2;
+ s->gpe.sts |= PIIX4_PCI_HOTPLUG_STATUS;
s->pci0_status.up |= (1 << slot);
}
static void disable_device(PIIX4PMState *s, int slot)
{
- s->gpe.sts |= 2;
+ s->gpe.sts |= PIIX4_PCI_HOTPLUG_STATUS;
s->pci0_status.down |= (1 << slot);
}
@@ -621,9 +631,8 @@ static int piix4_device_hotplug(DeviceState *qdev, PCIDevice *dev, int state)
} else {
disable_device(s, slot);
}
- if (s->gpe.en & 2) {
- qemu_set_irq(s->irq, 1);
- qemu_set_irq(s->irq, 0);
- }
+
+ pm_update_sci(s);
+
return 0;
}
diff --git a/hw/apic.c b/hw/apic.c
index d686b510b0..63d62c7553 100644
--- a/hw/apic.c
+++ b/hw/apic.c
@@ -21,23 +21,7 @@
#include "qemu-timer.h"
#include "host-utils.h"
#include "sysbus.h"
-
-//#define DEBUG_APIC
-//#define DEBUG_COALESCING
-
-#ifdef DEBUG_APIC
-#define DPRINTF(fmt, ...) \
- do { printf("apic: " fmt , ## __VA_ARGS__); } while (0)
-#else
-#define DPRINTF(fmt, ...)
-#endif
-
-#ifdef DEBUG_COALESCING
-#define DPRINTF_C(fmt, ...) \
- do { printf("apic: " fmt , ## __VA_ARGS__); } while (0)
-#else
-#define DPRINTF_C(fmt, ...)
-#endif
+#include "trace.h"
/* APIC Local Vector Table */
#define APIC_LVT_TIMER 0
@@ -168,8 +152,8 @@ static void apic_local_deliver(APICState *s, int vector)
uint32_t lvt = s->lvt[vector];
int trigger_mode;
- DPRINTF("%s: vector %d delivery mode %d\n", __func__, vector,
- (lvt >> 8) & 7);
+ trace_apic_local_deliver(vector, (lvt >> 8) & 7);
+
if (lvt & APIC_LVT_MASKED)
return;
@@ -300,9 +284,9 @@ void apic_deliver_irq(uint8_t dest, uint8_t dest_mode,
{
uint32_t deliver_bitmask[MAX_APIC_WORDS];
- DPRINTF("%s: dest %d dest_mode %d delivery_mode %d vector %d"
- " polarity %d trigger_mode %d\n", __func__, dest, dest_mode,
- delivery_mode, vector_num, polarity, trigger_mode);
+ trace_apic_deliver_irq(dest, dest_mode, delivery_mode, vector_num,
+ polarity, trigger_mode);
+
apic_get_delivery_bitmask(deliver_bitmask, dest, dest_mode);
apic_bus_deliver(deliver_bitmask, delivery_mode, vector_num, polarity,
trigger_mode);
@@ -312,7 +296,8 @@ void cpu_set_apic_base(DeviceState *d, uint64_t val)
{
APICState *s = DO_UPCAST(APICState, busdev.qdev, d);
- DPRINTF("cpu_set_apic_base: %016" PRIx64 "\n", val);
+ trace_cpu_set_apic_base(val);
+
if (!s)
return;
s->apicbase = (val & 0xfffff000) |
@@ -329,8 +314,8 @@ uint64_t cpu_get_apic_base(DeviceState *d)
{
APICState *s = DO_UPCAST(APICState, busdev.qdev, d);
- DPRINTF("cpu_get_apic_base: %016" PRIx64 "\n",
- s ? (uint64_t)s->apicbase: 0);
+ trace_cpu_get_apic_base(s ? (uint64_t)s->apicbase: 0);
+
return s ? s->apicbase : 0;
}
@@ -402,20 +387,23 @@ static void apic_update_irq(APICState *s)
void apic_reset_irq_delivered(void)
{
- DPRINTF_C("%s: old coalescing %d\n", __func__, apic_irq_delivered);
+ trace_apic_reset_irq_delivered(apic_irq_delivered);
+
apic_irq_delivered = 0;
}
int apic_get_irq_delivered(void)
{
- DPRINTF_C("%s: returning coalescing %d\n", __func__, apic_irq_delivered);
+ trace_apic_get_irq_delivered(apic_irq_delivered);
+
return apic_irq_delivered;
}
static void apic_set_irq(APICState *s, int vector_num, int trigger_mode)
{
apic_irq_delivered += !get_bit(s->irr, vector_num);
- DPRINTF_C("%s: coalescing %d\n", __func__, apic_irq_delivered);
+
+ trace_apic_set_irq(apic_irq_delivered);
set_bit(s->irr, vector_num);
if (trigger_mode)
@@ -769,7 +757,7 @@ static uint32_t apic_mem_readl(void *opaque, target_phys_addr_t addr)
val = 0;
break;
}
- DPRINTF("read: " TARGET_FMT_plx " = %08x\n", addr, val);
+ trace_apic_mem_readl(addr, val);
return val;
}
@@ -805,7 +793,7 @@ static void apic_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
}
s = DO_UPCAST(APICState, busdev.qdev, d);
- DPRINTF("write: " TARGET_FMT_plx " = %08x\n", addr, val);
+ trace_apic_mem_writel(addr, val);
switch(index) {
case 0x02:
diff --git a/hw/bonito.c b/hw/bonito.c
index 8b810321ad..dcf031134e 100644
--- a/hw/bonito.c
+++ b/hw/bonito.c
@@ -775,7 +775,6 @@ PCIBus *bonito_init(qemu_irq *pic)
pci_bonito_map_irq, pic, 0x28, 32);
pcihost->bus = b;
qdev_init_nofail(dev);
- pci_bus_set_mem_base(pcihost->bus, 0x10000000);
d = pci_create_simple(b, PCI_DEVFN(0, 0), "Bonito");
s = DO_UPCAST(PCIBonitoState, dev, d);
diff --git a/hw/cirrus_vga.c b/hw/cirrus_vga.c
index bbd4b082d2..aadc56f692 100644
--- a/hw/cirrus_vga.c
+++ b/hw/cirrus_vga.c
@@ -280,63 +280,63 @@ static void cirrus_bitblt_fill_nop(CirrusVGAState *s,
}
#define ROP_NAME 0
-#define ROP_OP(d, s) d = 0
+#define ROP_FN(d, s) 0
#include "cirrus_vga_rop.h"
#define ROP_NAME src_and_dst
-#define ROP_OP(d, s) d = (s) & (d)
+#define ROP_FN(d, s) (s) & (d)
#include "cirrus_vga_rop.h"
#define ROP_NAME src_and_notdst
-#define ROP_OP(d, s) d = (s) & (~(d))
+#define ROP_FN(d, s) (s) & (~(d))
#include "cirrus_vga_rop.h"
#define ROP_NAME notdst
-#define ROP_OP(d, s) d = ~(d)
+#define ROP_FN(d, s) ~(d)
#include "cirrus_vga_rop.h"
#define ROP_NAME src
-#define ROP_OP(d, s) d = s
+#define ROP_FN(d, s) s
#include "cirrus_vga_rop.h"
#define ROP_NAME 1
-#define ROP_OP(d, s) d = ~0
+#define ROP_FN(d, s) ~0
#include "cirrus_vga_rop.h"
#define ROP_NAME notsrc_and_dst
-#define ROP_OP(d, s) d = (~(s)) & (d)
+#define ROP_FN(d, s) (~(s)) & (d)
#include "cirrus_vga_rop.h"
#define ROP_NAME src_xor_dst
-#define ROP_OP(d, s) d = (s) ^ (d)
+#define ROP_FN(d, s) (s) ^ (d)
#include "cirrus_vga_rop.h"
#define ROP_NAME src_or_dst
-#define ROP_OP(d, s) d = (s) | (d)
+#define ROP_FN(d, s) (s) | (d)
#include "cirrus_vga_rop.h"
#define ROP_NAME notsrc_or_notdst
-#define ROP_OP(d, s) d = (~(s)) | (~(d))
+#define ROP_FN(d, s) (~(s)) | (~(d))
#include "cirrus_vga_rop.h"
#define ROP_NAME src_notxor_dst
-#define ROP_OP(d, s) d = ~((s) ^ (d))
+#define ROP_FN(d, s) ~((s) ^ (d))
#include "cirrus_vga_rop.h"
#define ROP_NAME src_or_notdst
-#define ROP_OP(d, s) d = (s) | (~(d))
+#define ROP_FN(d, s) (s) | (~(d))
#include "cirrus_vga_rop.h"
#define ROP_NAME notsrc
-#define ROP_OP(d, s) d = (~(s))
+#define ROP_FN(d, s) (~(s))
#include "cirrus_vga_rop.h"
#define ROP_NAME notsrc_or_dst
-#define ROP_OP(d, s) d = (~(s)) | (d)
+#define ROP_FN(d, s) (~(s)) | (d)
#include "cirrus_vga_rop.h"
#define ROP_NAME notsrc_and_notdst
-#define ROP_OP(d, s) d = (~(s)) & (~(d))
+#define ROP_FN(d, s) (~(s)) & (~(d))
#include "cirrus_vga_rop.h"
static const cirrus_bitblt_rop_t cirrus_fwd_rop[16] = {
diff --git a/hw/cirrus_vga_rop.h b/hw/cirrus_vga_rop.h
index 39a7b7285b..9c7bb09286 100644
--- a/hw/cirrus_vga_rop.h
+++ b/hw/cirrus_vga_rop.h
@@ -22,6 +22,26 @@
* THE SOFTWARE.
*/
+static inline void glue(rop_8_,ROP_NAME)(uint8_t *dst, uint8_t src)
+{
+ *dst = ROP_FN(*dst, src);
+}
+
+static inline void glue(rop_16_,ROP_NAME)(uint16_t *dst, uint16_t src)
+{
+ *dst = ROP_FN(*dst, src);
+}
+
+static inline void glue(rop_32_,ROP_NAME)(uint32_t *dst, uint32_t src)
+{
+ *dst = ROP_FN(*dst, src);
+}
+
+#define ROP_OP(d, s) glue(rop_8_,ROP_NAME)(d, s)
+#define ROP_OP_16(d, s) glue(rop_16_,ROP_NAME)(d, s)
+#define ROP_OP_32(d, s) glue(rop_32_,ROP_NAME)(d, s)
+#undef ROP_FN
+
static void
glue(cirrus_bitblt_rop_fwd_, ROP_NAME)(CirrusVGAState *s,
uint8_t *dst,const uint8_t *src,
@@ -39,7 +59,7 @@ glue(cirrus_bitblt_rop_fwd_, ROP_NAME)(CirrusVGAState *s,
for (y = 0; y < bltheight; y++) {
for (x = 0; x < bltwidth; x++) {
- ROP_OP(*dst, *src);
+ ROP_OP(dst, *src);
dst++;
src++;
}
@@ -59,7 +79,7 @@ glue(cirrus_bitblt_rop_bkwd_, ROP_NAME)(CirrusVGAState *s,
srcpitch += bltwidth;
for (y = 0; y < bltheight; y++) {
for (x = 0; x < bltwidth; x++) {
- ROP_OP(*dst, *src);
+ ROP_OP(dst, *src);
dst--;
src--;
}
@@ -81,7 +101,7 @@ glue(glue(cirrus_bitblt_rop_fwd_transp_, ROP_NAME),_8)(CirrusVGAState *s,
for (y = 0; y < bltheight; y++) {
for (x = 0; x < bltwidth; x++) {
p = *dst;
- ROP_OP(p, *src);
+ ROP_OP(&p, *src);
if (p != s->vga.gr[0x34]) *dst = p;
dst++;
src++;
@@ -104,7 +124,7 @@ glue(glue(cirrus_bitblt_rop_bkwd_transp_, ROP_NAME),_8)(CirrusVGAState *s,
for (y = 0; y < bltheight; y++) {
for (x = 0; x < bltwidth; x++) {
p = *dst;
- ROP_OP(p, *src);
+ ROP_OP(&p, *src);
if (p != s->vga.gr[0x34]) *dst = p;
dst--;
src--;
@@ -128,8 +148,8 @@ glue(glue(cirrus_bitblt_rop_fwd_transp_, ROP_NAME),_16)(CirrusVGAState *s,
for (x = 0; x < bltwidth; x+=2) {
p1 = *dst;
p2 = *(dst+1);
- ROP_OP(p1, *src);
- ROP_OP(p2, *(src+1));
+ ROP_OP(&p1, *src);
+ ROP_OP(&p2, *(src + 1));
if ((p1 != s->vga.gr[0x34]) || (p2 != s->vga.gr[0x35])) {
*dst = p1;
*(dst+1) = p2;
@@ -156,8 +176,8 @@ glue(glue(cirrus_bitblt_rop_bkwd_transp_, ROP_NAME),_16)(CirrusVGAState *s,
for (x = 0; x < bltwidth; x+=2) {
p1 = *(dst-1);
p2 = *dst;
- ROP_OP(p1, *(src-1));
- ROP_OP(p2, *src);
+ ROP_OP(&p1, *(src - 1));
+ ROP_OP(&p2, *src);
if ((p1 != s->vga.gr[0x34]) || (p2 != s->vga.gr[0x35])) {
*(dst-1) = p1;
*dst = p2;
@@ -184,3 +204,5 @@ glue(glue(cirrus_bitblt_rop_bkwd_transp_, ROP_NAME),_16)(CirrusVGAState *s,
#undef ROP_NAME
#undef ROP_OP
+#undef ROP_OP_16
+#undef ROP_OP_32
diff --git a/hw/cirrus_vga_rop2.h b/hw/cirrus_vga_rop2.h
index 81a5b398e0..d28bcc6f25 100644
--- a/hw/cirrus_vga_rop2.h
+++ b/hw/cirrus_vga_rop2.h
@@ -23,15 +23,15 @@
*/
#if DEPTH == 8
-#define PUTPIXEL() ROP_OP(d[0], col)
+#define PUTPIXEL() ROP_OP(&d[0], col)
#elif DEPTH == 16
-#define PUTPIXEL() ROP_OP(((uint16_t *)d)[0], col);
+#define PUTPIXEL() ROP_OP_16((uint16_t *)&d[0], col)
#elif DEPTH == 24
-#define PUTPIXEL() ROP_OP(d[0], col); \
- ROP_OP(d[1], (col >> 8)); \
- ROP_OP(d[2], (col >> 16))
+#define PUTPIXEL() ROP_OP(&d[0], col); \
+ ROP_OP(&d[1], (col >> 8)); \
+ ROP_OP(&d[2], (col >> 16))
#elif DEPTH == 32
-#define PUTPIXEL() ROP_OP(((uint32_t *)d)[0], col)
+#define PUTPIXEL() ROP_OP_32(((uint32_t *)&d[0]), col)
#else
#error unsupported DEPTH
#endif
diff --git a/hw/device-hotplug.c b/hw/device-hotplug.c
index c1a9a561d7..9704e2feb2 100644
--- a/hw/device-hotplug.c
+++ b/hw/device-hotplug.c
@@ -25,6 +25,7 @@
#include "hw.h"
#include "boards.h"
#include "net.h"
+#include "blockdev.h"
DriveInfo *add_init_drive(const char *optstr)
{
diff --git a/hw/e1000.c b/hw/e1000.c
index 8d87492e0b..532efdc27d 100644
--- a/hw/e1000.c
+++ b/hw/e1000.c
@@ -55,6 +55,7 @@ static int debugflags = DBGBIT(TXERR) | DBGBIT(GENERAL);
#define IOPORT_SIZE 0x40
#define PNPMMIO_SIZE 0x20000
+#define MIN_BUF_SIZE 60 /* Min. octets in an ethernet frame sans FCS */
/*
* HW models:
@@ -262,21 +263,20 @@ set_eecd(E1000State *s, int index, uint32_t val)
s->eecd_state.old_eecd = val & (E1000_EECD_SK | E1000_EECD_CS |
E1000_EECD_DI|E1000_EECD_FWE_MASK|E1000_EECD_REQ);
+ if (!(E1000_EECD_CS & val)) // CS inactive; nothing to do
+ return;
+ if (E1000_EECD_CS & (val ^ oldval)) { // CS rise edge; reset state
+ s->eecd_state.val_in = 0;
+ s->eecd_state.bitnum_in = 0;
+ s->eecd_state.bitnum_out = 0;
+ s->eecd_state.reading = 0;
+ }
if (!(E1000_EECD_SK & (val ^ oldval))) // no clock edge
return;
if (!(E1000_EECD_SK & val)) { // falling edge
s->eecd_state.bitnum_out++;
return;
}
- if (!(val & E1000_EECD_CS)) { // rising, no CS (EEPROM reset)
- memset(&s->eecd_state, 0, sizeof s->eecd_state);
- /*
- * restore old_eecd's E1000_EECD_SK (known to be on)
- * to avoid false detection of a clock edge
- */
- s->eecd_state.old_eecd = E1000_EECD_SK;
- return;
- }
s->eecd_state.val_in <<= 1;
if (val & E1000_EECD_DI)
s->eecd_state.val_in |= 1;
@@ -346,7 +346,7 @@ is_vlan_txd(uint32_t txd_lower)
/* FCS aka Ethernet CRC-32. We don't get it from backends and can't
* fill it in, just pad descriptor length by 4 bytes unless guest
- * told us to trip it off the packet. */
+ * told us to strip it off the packet. */
static inline int
fcs_len(E1000State *s)
{
@@ -636,10 +636,19 @@ e1000_receive(VLANClientState *nc, const uint8_t *buf, size_t size)
uint32_t rdh_start;
uint16_t vlan_special = 0;
uint8_t vlan_status = 0, vlan_offset = 0;
+ uint8_t min_buf[MIN_BUF_SIZE];
if (!(s->mac_reg[RCTL] & E1000_RCTL_EN))
return -1;
+ /* Pad to minimum Ethernet frame length */
+ if (size < sizeof(min_buf)) {
+ memcpy(min_buf, buf, size);
+ memset(&min_buf[size], 0, sizeof(min_buf) - size);
+ buf = min_buf;
+ size = sizeof(min_buf);
+ }
+
if (size > s->rxbuf_size) {
DBGOUT(RX, "packet too large for buffers (%lu > %d)\n",
(unsigned long)size, s->rxbuf_size);
@@ -691,9 +700,14 @@ e1000_receive(VLANClientState *nc, const uint8_t *buf, size_t size)
s->mac_reg[GPRC]++;
s->mac_reg[TPR]++;
- n = s->mac_reg[TORL];
- if ((s->mac_reg[TORL] += size) < n)
+ /* TOR - Total Octets Received:
+ * This register includes bytes received in a packet from the <Destination
+ * Address> field through the <CRC> field, inclusively.
+ */
+ n = s->mac_reg[TORL] + size + /* Always include FCS length. */ 4;
+ if (n < s->mac_reg[TORL])
s->mac_reg[TORH]++;
+ s->mac_reg[TORL] = n;
n = E1000_ICS_RXT0;
if ((rdt = s->mac_reg[RDT]) < s->mac_reg[RDH])
diff --git a/hw/eepro100.c b/hw/eepro100.c
index 8cbc3aa7a2..41d792ad24 100644
--- a/hw/eepro100.c
+++ b/hw/eepro100.c
@@ -219,7 +219,8 @@ typedef enum {
typedef struct {
PCIDevice dev;
- uint8_t mult[8]; /* multicast mask array */
+ /* Hash register (multicast mask array, multiple individual addresses). */
+ uint8_t mult[8];
int mmio_index;
NICState *nic;
NICConf conf;
@@ -599,7 +600,7 @@ static void nic_reset(void *opaque)
{
EEPRO100State *s = opaque;
TRACE(OTHER, logout("%p\n", s));
- /* TODO: Clearing of multicast table for selective reset, too? */
+ /* TODO: Clearing of hash register for selective reset, too? */
memset(&s->mult[0], 0, sizeof(s->mult));
nic_selective_reset(s);
}
@@ -851,7 +852,14 @@ static void action_command(EEPRO100State *s)
case CmdConfigure:
cpu_physical_memory_read(s->cb_address + 8, &s->configuration[0],
sizeof(s->configuration));
- TRACE(OTHER, logout("configuration: %s\n", nic_dump(&s->configuration[0], 16)));
+ TRACE(OTHER, logout("configuration: %s\n",
+ nic_dump(&s->configuration[0], 16)));
+ TRACE(OTHER, logout("configuration: %s\n",
+ nic_dump(&s->configuration[16],
+ ARRAY_SIZE(s->configuration) - 16)));
+ if (s->configuration[20] & BIT(6)) {
+ TRACE(OTHER, logout("Multiple IA bit\n"));
+ }
break;
case CmdMulticastList:
set_multicast_list(s);
@@ -1282,7 +1290,7 @@ static void eepro100_write_port(EEPRO100State * s, uint32_t val)
static uint8_t eepro100_read1(EEPRO100State * s, uint32_t addr)
{
- uint8_t val;
+ uint8_t val = 0;
if (addr <= sizeof(s->mem) - sizeof(val)) {
memcpy(&val, &s->mem[addr], sizeof(val));
}
@@ -1325,7 +1333,7 @@ static uint8_t eepro100_read1(EEPRO100State * s, uint32_t addr)
static uint16_t eepro100_read2(EEPRO100State * s, uint32_t addr)
{
- uint16_t val;
+ uint16_t val = 0;
if (addr <= sizeof(s->mem) - sizeof(val)) {
memcpy(&val, &s->mem[addr], sizeof(val));
}
@@ -1348,7 +1356,7 @@ static uint16_t eepro100_read2(EEPRO100State * s, uint32_t addr)
static uint32_t eepro100_read4(EEPRO100State * s, uint32_t addr)
{
- uint32_t val;
+ uint32_t val = 0;
if (addr <= sizeof(s->mem) - sizeof(val)) {
memcpy(&val, &s->mem[addr], sizeof(val));
}
@@ -1647,12 +1655,6 @@ static ssize_t nic_receive(VLANClientState *nc, const uint8_t * buf, size_t size
static const uint8_t broadcast_macaddr[6] =
{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
- /* TODO: check multiple IA bit. */
- if (s->configuration[20] & BIT(6)) {
- missing("Multiple IA bit");
- return -1;
- }
-
if (s->configuration[8] & 0x80) {
/* CSMA is disabled. */
logout("%p received while CSMA is disabled\n", s);
@@ -1702,6 +1704,16 @@ static ssize_t nic_receive(VLANClientState *nc, const uint8_t * buf, size_t size
/* Promiscuous: receive all. */
TRACE(RXTX, logout("%p received frame in promiscuous mode, len=%zu\n", s, size));
rfd_status |= 0x0004;
+ } else if (s->configuration[20] & BIT(6)) {
+ /* Multiple IA bit set. */
+ unsigned mcast_idx = compute_mcast_idx(buf);
+ assert(mcast_idx < 64);
+ if (s->mult[mcast_idx >> 3] & (1 << (mcast_idx & 7))) {
+ TRACE(RXTX, logout("%p accepted, multiple IA bit set\n", s));
+ } else {
+ TRACE(RXTX, logout("%p frame ignored, multiple IA bit set\n", s));
+ return -1;
+ }
} else {
TRACE(RXTX, logout("%p received frame, ignored, len=%zu,%s\n", s, size,
nic_dump(buf, size)));
diff --git a/hw/elf_ops.h b/hw/elf_ops.h
index 27d1ab9bc2..0bd72350b4 100644
--- a/hw/elf_ops.h
+++ b/hw/elf_ops.h
@@ -153,6 +153,11 @@ static int glue(load_symbols, SZ)(struct elfhdr *ehdr, int fd, int must_swab,
syms = qemu_realloc(syms, nsyms * sizeof(*syms));
qsort(syms, nsyms, sizeof(*syms), glue(symcmp, SZ));
+ for (i = 0; i < nsyms - 1; i++) {
+ if (syms[i].st_size == 0) {
+ syms[i].st_size = syms[i + 1].st_value - syms[i].st_value;
+ }
+ }
} else {
qemu_free(syms);
syms = NULL;
diff --git a/hw/escc.c b/hw/escc.c
index 6d2fd36b11..8714239780 100644
--- a/hw/escc.c
+++ b/hw/escc.c
@@ -65,6 +65,8 @@
* 2006-Aug-10 Igor Kovalenko : Renamed KBDQueue to SERIOQueue, implemented
* serial mouse queue.
* Implemented serial mouse protocol.
+ *
+ * 2010-May-23 Artyom Tarasenko: Reworked IUS logic
*/
#ifdef DEBUG_SERIAL
@@ -279,7 +281,7 @@ static uint32_t get_queue(void *opaque)
static int escc_update_irq_chn(ChannelState *s)
{
- if ((((s->wregs[W_INTR] & INTR_TXINT) && s->txint == 1) ||
+ if ((((s->wregs[W_INTR] & INTR_TXINT) && (s->txint == 1)) ||
// tx ints enabled, pending
((((s->wregs[W_INTR] & INTR_RXMODEMSK) == INTR_RXINT1ST) ||
((s->wregs[W_INTR] & INTR_RXMODEMSK) == INTR_RXINTALL)) &&
@@ -342,24 +344,22 @@ static void escc_reset(DeviceState *d)
static inline void set_rxint(ChannelState *s)
{
s->rxint = 1;
- if (!s->txint_under_svc) {
- s->rxint_under_svc = 1;
- if (s->chn == chn_a) {
- if (s->wregs[W_MINTR] & MINTR_STATUSHI)
- s->otherchn->rregs[R_IVEC] = IVEC_HIRXINTA;
- else
- s->otherchn->rregs[R_IVEC] = IVEC_LORXINTA;
- } else {
- if (s->wregs[W_MINTR] & MINTR_STATUSHI)
- s->rregs[R_IVEC] = IVEC_HIRXINTB;
- else
- s->rregs[R_IVEC] = IVEC_LORXINTB;
- }
- }
- if (s->chn == chn_a)
+ /* XXX: missing daisy chainnig: chn_b rx should have a lower priority
+ than chn_a rx/tx/special_condition service*/
+ s->rxint_under_svc = 1;
+ if (s->chn == chn_a) {
s->rregs[R_INTR] |= INTR_RXINTA;
- else
+ if (s->wregs[W_MINTR] & MINTR_STATUSHI)
+ s->otherchn->rregs[R_IVEC] = IVEC_HIRXINTA;
+ else
+ s->otherchn->rregs[R_IVEC] = IVEC_LORXINTA;
+ } else {
s->otherchn->rregs[R_INTR] |= INTR_RXINTB;
+ if (s->wregs[W_MINTR] & MINTR_STATUSHI)
+ s->rregs[R_IVEC] = IVEC_HIRXINTB;
+ else
+ s->rregs[R_IVEC] = IVEC_LORXINTB;
+ }
escc_update_irq(s);
}
@@ -369,19 +369,17 @@ static inline void set_txint(ChannelState *s)
if (!s->rxint_under_svc) {
s->txint_under_svc = 1;
if (s->chn == chn_a) {
+ s->rregs[R_INTR] |= INTR_TXINTA;
if (s->wregs[W_MINTR] & MINTR_STATUSHI)
s->otherchn->rregs[R_IVEC] = IVEC_HITXINTA;
else
s->otherchn->rregs[R_IVEC] = IVEC_LOTXINTA;
} else {
s->rregs[R_IVEC] = IVEC_TXINTB;
+ s->otherchn->rregs[R_INTR] |= INTR_TXINTB;
}
- }
- if (s->chn == chn_a)
- s->rregs[R_INTR] |= INTR_TXINTA;
- else
- s->otherchn->rregs[R_INTR] |= INTR_TXINTB;
escc_update_irq(s);
+ }
}
static inline void clr_rxint(ChannelState *s)
@@ -417,6 +415,7 @@ static inline void clr_txint(ChannelState *s)
s->otherchn->rregs[R_IVEC] = IVEC_LONOINT;
s->rregs[R_INTR] &= ~INTR_TXINTA;
} else {
+ s->otherchn->rregs[R_INTR] &= ~INTR_TXINTB;
if (s->wregs[W_MINTR] & MINTR_STATUSHI)
s->rregs[R_IVEC] = IVEC_HINOINT;
else
@@ -515,10 +514,15 @@ static void escc_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
clr_txint(s);
break;
case CMD_CLR_IUS:
- if (s->rxint_under_svc)
- clr_rxint(s);
- else if (s->txint_under_svc)
- clr_txint(s);
+ if (s->rxint_under_svc) {
+ s->rxint_under_svc = 0;
+ if (s->txint) {
+ set_txint(s);
+ }
+ } else if (s->txint_under_svc) {
+ s->txint_under_svc = 0;
+ }
+ escc_update_irq(s);
break;
default:
break;
diff --git a/hw/esp.c b/hw/esp.c
index 349052a024..910fd31665 100644
--- a/hw/esp.c
+++ b/hw/esp.c
@@ -80,6 +80,8 @@ struct ESPState {
ESPDMAMemoryReadWriteFunc dma_memory_read;
ESPDMAMemoryReadWriteFunc dma_memory_write;
void *dma_opaque;
+ int dma_enabled;
+ void (*dma_cb)(ESPState *s);
};
#define ESP_TCLO 0x0
@@ -167,6 +169,24 @@ static void esp_lower_irq(ESPState *s)
}
}
+static void esp_dma_enable(void *opaque, int irq, int level)
+{
+ DeviceState *d = opaque;
+ ESPState *s = container_of(d, ESPState, busdev.qdev);
+
+ if (level) {
+ s->dma_enabled = 1;
+ DPRINTF("Raise enable\n");
+ if (s->dma_cb) {
+ s->dma_cb(s);
+ s->dma_cb = NULL;
+ }
+ } else {
+ DPRINTF("Lower enable\n");
+ s->dma_enabled = 0;
+ }
+}
+
static uint32_t get_cmd(ESPState *s, uint8_t *buf)
{
uint32_t dmalen;
@@ -243,6 +263,10 @@ static void handle_satn(ESPState *s)
uint8_t buf[32];
int len;
+ if (!s->dma_enabled) {
+ s->dma_cb = handle_satn;
+ return;
+ }
len = get_cmd(s, buf);
if (len)
do_cmd(s, buf);
@@ -253,6 +277,10 @@ static void handle_s_without_atn(ESPState *s)
uint8_t buf[32];
int len;
+ if (!s->dma_enabled) {
+ s->dma_cb = handle_s_without_atn;
+ return;
+ }
len = get_cmd(s, buf);
if (len) {
do_busid_cmd(s, buf, 0);
@@ -261,6 +289,10 @@ static void handle_s_without_atn(ESPState *s)
static void handle_satn_stop(ESPState *s)
{
+ if (!s->dma_enabled) {
+ s->dma_cb = handle_satn_stop;
+ return;
+ }
s->cmdlen = get_cmd(s, s->cmdbuf);
if (s->cmdlen) {
DPRINTF("Set ATN & Stop: cmdlen %d\n", s->cmdlen);
@@ -431,6 +463,7 @@ static void esp_hard_reset(DeviceState *d)
s->ti_wptr = 0;
s->dma = 0;
s->do_cmd = 0;
+ s->dma_cb = NULL;
s->rregs[ESP_CFG1] = 7;
}
@@ -450,6 +483,18 @@ static void parent_esp_reset(void *opaque, int irq, int level)
}
}
+static void esp_gpio_demux(void *opaque, int irq, int level)
+{
+ switch (irq) {
+ case 0:
+ parent_esp_reset(opaque, irq, level);
+ break;
+ case 1:
+ esp_dma_enable(opaque, irq, level);
+ break;
+ }
+}
+
static uint32_t esp_mem_readb(void *opaque, target_phys_addr_t addr)
{
ESPState *s = opaque;
@@ -646,7 +691,8 @@ static const VMStateDescription vmstate_esp = {
void esp_init(target_phys_addr_t espaddr, int it_shift,
ESPDMAMemoryReadWriteFunc dma_memory_read,
ESPDMAMemoryReadWriteFunc dma_memory_write,
- void *dma_opaque, qemu_irq irq, qemu_irq *reset)
+ void *dma_opaque, qemu_irq irq, qemu_irq *reset,
+ qemu_irq *dma_enable)
{
DeviceState *dev;
SysBusDevice *s;
@@ -658,11 +704,14 @@ void esp_init(target_phys_addr_t espaddr, int it_shift,
esp->dma_memory_write = dma_memory_write;
esp->dma_opaque = dma_opaque;
esp->it_shift = it_shift;
+ /* XXX for now until rc4030 has been changed to use DMA enable signal */
+ esp->dma_enabled = 1;
qdev_init_nofail(dev);
s = sysbus_from_qdev(dev);
sysbus_connect_irq(s, 0, irq);
sysbus_mmio_map(s, 0, espaddr);
*reset = qdev_get_gpio_in(dev, 0);
+ *dma_enable = qdev_get_gpio_in(dev, 1);
}
static int esp_init1(SysBusDevice *dev)
@@ -676,7 +725,7 @@ static int esp_init1(SysBusDevice *dev)
esp_io_memory = cpu_register_io_memory(esp_mem_read, esp_mem_write, s);
sysbus_init_mmio(dev, ESP_REGS << s->it_shift, esp_io_memory);
- qdev_init_gpio_in(&dev->qdev, parent_esp_reset, 1);
+ qdev_init_gpio_in(&dev->qdev, esp_gpio_demux, 2);
scsi_bus_new(&s->bus, &dev->qdev, 0, ESP_MAX_DEVS, esp_command_complete);
return scsi_bus_legacy_handle_cmdline(&s->bus);
diff --git a/hw/esp.h b/hw/esp.h
index 605f953754..62bfd4d129 100644
--- a/hw/esp.h
+++ b/hw/esp.h
@@ -7,6 +7,7 @@ typedef void (*ESPDMAMemoryReadWriteFunc)(void *opaque, uint8_t *buf, int len);
void esp_init(target_phys_addr_t espaddr, int it_shift,
ESPDMAMemoryReadWriteFunc dma_memory_read,
ESPDMAMemoryReadWriteFunc dma_memory_write,
- void *dma_opaque, qemu_irq irq, qemu_irq *reset);
+ void *dma_opaque, qemu_irq irq, qemu_irq *reset,
+ qemu_irq *dma_enable);
#endif
diff --git a/hw/etraxfs.c b/hw/etraxfs.c
index 46e2920c23..5ee5f979aa 100644
--- a/hw/etraxfs.c
+++ b/hw/etraxfs.c
@@ -31,6 +31,7 @@
#include "loader.h"
#include "elf.h"
#include "cris-boot.h"
+#include "blockdev.h"
#define FLASH_SIZE 0x2000000
#define INTMEM_SIZE (128 * 1024)
diff --git a/hw/etraxfs_eth.c b/hw/etraxfs_eth.c
index 187ece19ea..ade96f14ac 100644
--- a/hw/etraxfs_eth.c
+++ b/hw/etraxfs_eth.c
@@ -437,6 +437,7 @@ eth_writel (void *opaque, target_phys_addr_t addr, uint32_t value)
eth_validate_duplex(eth);
}
eth->mdio_bus.mdc = !!(value & 4);
+ eth->regs[addr] = value;
break;
case RW_REC_CTRL:
@@ -463,7 +464,7 @@ static int eth_match_groupaddr(struct fs_eth *eth, const unsigned char *sa)
/* First bit on the wire of a MAC address signals multicast or
physical address. */
- if (!m_individual && !sa[0] & 1)
+ if (!m_individual && !(sa[0] & 1))
return 0;
/* Calculate the hash index for the GA registers. */
diff --git a/hw/fdc.c b/hw/fdc.c
index 2d50bd6a39..c159dcb630 100644
--- a/hw/fdc.c
+++ b/hw/fdc.c
@@ -34,6 +34,7 @@
#include "isa.h"
#include "sysbus.h"
#include "qdev-addr.h"
+#include "blockdev.h"
/********************************************************/
/* debug Floppy devices */
diff --git a/hw/fdc.h b/hw/fdc.h
index b6b3772592..242730af8c 100644
--- a/hw/fdc.h
+++ b/hw/fdc.h
@@ -2,7 +2,6 @@
#define HW_FDC_H
/* fdc.c */
-#include "blockdev.h"
#define MAX_FD 2
typedef struct FDCtrl FDCtrl;
diff --git a/hw/file-op-9p.h b/hw/file-op-9p.h
index a741c93527..21d60b5855 100644
--- a/hw/file-op-9p.h
+++ b/hw/file-op-9p.h
@@ -24,8 +24,19 @@
typedef enum
{
- SM_PASSTHROUGH = 1, /* uid/gid set on fileserver files */
- SM_MAPPED, /* uid/gid part of xattr */
+ /*
+ * Server will try to set uid/gid.
+ * On failure ignore the error.
+ */
+ SM_NONE = 0,
+ /*
+ * uid/gid set on fileserver files
+ */
+ SM_PASSTHROUGH = 1,
+ /*
+ * uid/gid part of xattr
+ */
+ SM_MAPPED,
} SecModel;
typedef struct FsCred
@@ -36,11 +47,14 @@ typedef struct FsCred
dev_t fc_rdev;
} FsCred;
+struct xattr_operations;
+
typedef struct FsContext
{
char *fs_root;
SecModel fs_sm;
uid_t uid;
+ struct xattr_operations **xops;
} FsContext;
extern void cred_init(FsCred *);
@@ -52,7 +66,7 @@ typedef struct FileOperations
int (*chmod)(FsContext *, const char *, FsCred *);
int (*chown)(FsContext *, const char *, FsCred *);
int (*mknod)(FsContext *, const char *, FsCred *);
- int (*utime)(FsContext *, const char *, const struct utimbuf *);
+ int (*utimensat)(FsContext *, const char *, const struct timespec *);
int (*remove)(FsContext *, const char *);
int (*symlink)(FsContext *, const char *, const char *, FsCred *);
int (*link)(FsContext *, const char *, const char *);
@@ -66,14 +80,28 @@ typedef struct FileOperations
off_t (*telldir)(FsContext *, DIR *);
struct dirent *(*readdir)(FsContext *, DIR *);
void (*seekdir)(FsContext *, DIR *, off_t);
- ssize_t (*readv)(FsContext *, int, const struct iovec *, int);
- ssize_t (*writev)(FsContext *, int, const struct iovec *, int);
- off_t (*lseek)(FsContext *, int, off_t, int);
+ ssize_t (*preadv)(FsContext *, int, const struct iovec *, int, off_t);
+ ssize_t (*pwritev)(FsContext *, int, const struct iovec *, int, off_t);
int (*mkdir)(FsContext *, const char *, FsCred *);
int (*fstat)(FsContext *, int, struct stat *);
int (*rename)(FsContext *, const char *, const char *);
int (*truncate)(FsContext *, const char *, off_t);
int (*fsync)(FsContext *, int);
+ int (*statfs)(FsContext *s, const char *path, struct statfs *stbuf);
+ ssize_t (*lgetxattr)(FsContext *, const char *,
+ const char *, void *, size_t);
+ ssize_t (*llistxattr)(FsContext *, const char *, void *, size_t);
+ int (*lsetxattr)(FsContext *, const char *,
+ const char *, void *, size_t, int);
+ int (*lremovexattr)(FsContext *, const char *, const char *);
void *opaque;
} FileOperations;
+
+static inline const char *rpath(FsContext *ctx, const char *path)
+{
+ /* FIXME: so wrong... */
+ static char buffer[4096];
+ snprintf(buffer, sizeof(buffer), "%s/%s", ctx->fs_root, path);
+ return buffer;
+}
#endif
diff --git a/hw/fmopl.c b/hw/fmopl.c
index d1161f848f..3df1806a91 100644
--- a/hw/fmopl.c
+++ b/hw/fmopl.c
@@ -1342,8 +1342,9 @@ unsigned char OPLRead(FM_OPL *OPL,int a)
{
if(OPL->keyboardhandler_r)
return OPL->keyboardhandler_r(OPL->keyboard_param);
- else
+ else {
LOG(LOG_WAR,("OPL:read unmapped KEYBOARD port\n"));
+ }
}
return 0;
#if 0
@@ -1355,8 +1356,9 @@ unsigned char OPLRead(FM_OPL *OPL,int a)
{
if(OPL->porthandler_r)
return OPL->porthandler_r(OPL->port_param);
- else
+ else {
LOG(LOG_WAR,("OPL:read unmapped I/O port\n"));
+ }
}
return 0;
case 0x1a: /* PCM-DATA */
diff --git a/hw/gumstix.c b/hw/gumstix.c
index c343a166e8..af8b464b88 100644
--- a/hw/gumstix.c
+++ b/hw/gumstix.c
@@ -38,6 +38,7 @@
#include "sysemu.h"
#include "devices.h"
#include "boards.h"
+#include "blockdev.h"
static const int sector_len = 128 * 1024;
diff --git a/hw/hw.h b/hw/hw.h
index c2de6fe8c4..4405092b54 100644
--- a/hw/hw.h
+++ b/hw/hw.h
@@ -264,6 +264,8 @@ int register_savevm_live(DeviceState *dev,
void *opaque);
void unregister_savevm(DeviceState *dev, const char *idstr, void *opaque);
+void register_device_unmigratable(DeviceState *dev, const char *idstr,
+ void *opaque);
typedef void QEMUResetHandler(void *opaque);
@@ -313,6 +315,11 @@ typedef struct {
bool (*field_exists)(void *opaque, int version_id);
} VMStateField;
+typedef struct VMStateSubsection {
+ const VMStateDescription *vmsd;
+ bool (*needed)(void *opaque);
+} VMStateSubsection;
+
struct VMStateDescription {
const char *name;
int version_id;
@@ -323,6 +330,7 @@ struct VMStateDescription {
int (*post_load)(void *opaque, int version_id);
void (*pre_save)(void *opaque);
VMStateField *fields;
+ const VMStateSubsection *subsections;
};
extern const VMStateInfo vmstate_info_int8;
diff --git a/hw/ide/core.c b/hw/ide/core.c
index af52c2cb2d..06b6e14e56 100644
--- a/hw/ide/core.c
+++ b/hw/ide/core.c
@@ -30,6 +30,7 @@
#include "qemu-timer.h"
#include "sysemu.h"
#include "dma.h"
+#include "blockdev.h"
#include <hw/ide/internal.h>
@@ -138,6 +139,7 @@ static void ide_identify(IDEState *s)
put_le16(p + 61, s->nb_sectors >> 16);
put_le16(p + 62, 0x07); /* single word dma0-2 supported */
put_le16(p + 63, 0x07); /* mdma0-2 supported */
+ put_le16(p + 64, 0x03); /* pio3-4 supported */
put_le16(p + 65, 120);
put_le16(p + 66, 120);
put_le16(p + 67, 120);
@@ -198,13 +200,12 @@ static void ide_atapi_identify(IDEState *s)
put_le16(p + 53, 7); /* words 64-70, 54-58, 88 valid */
put_le16(p + 62, 7); /* single word dma0-2 supported */
put_le16(p + 63, 7); /* mdma0-2 supported */
- put_le16(p + 64, 0x3f); /* PIO modes supported */
#else
put_le16(p + 49, 1 << 9); /* LBA supported, no DMA */
put_le16(p + 53, 3); /* words 64-70, 54-58 valid */
put_le16(p + 63, 0x103); /* DMA modes XXX: may be incorrect */
- put_le16(p + 64, 1); /* PIO modes */
#endif
+ put_le16(p + 64, 3); /* pio3-4 supported */
put_le16(p + 65, 0xb4); /* minimum DMA multiword tx cycle time */
put_le16(p + 66, 0xb4); /* recommended DMA multiword tx cycle time */
put_le16(p + 67, 0x12c); /* minimum PIO cycle time without flow control */
@@ -1643,6 +1644,21 @@ static void ide_atapi_cmd(IDEState *s)
ide_atapi_cmd_reply(s, len, max_len);
break;
}
+ case GPCMD_GET_EVENT_STATUS_NOTIFICATION:
+ max_len = ube16_to_cpu(packet + 7);
+
+ if (packet[1] & 0x01) { /* polling */
+ /* We don't support any event class (yet). */
+ cpu_to_ube16(buf, 0x00); /* No event descriptor returned */
+ buf[2] = 0x80; /* No Event Available (NEA) */
+ buf[3] = 0x00; /* Empty supported event classes */
+ ide_atapi_cmd_reply(s, 4, max_len);
+ } else { /* asynchronous mode */
+ /* Only polling is supported, asynchronous mode is not. */
+ ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
+ ASC_INV_FIELD_IN_CMD_PACKET);
+ }
+ break;
default:
ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
ASC_ILLEGAL_OPCODE);
@@ -2629,7 +2645,12 @@ int ide_init_drive(IDEState *s, BlockDriverState *bs,
if (bdrv_get_type_hint(bs) == BDRV_TYPE_CDROM) {
s->drive_kind = IDE_CD;
bdrv_set_change_cb(bs, cdrom_change_cb, s);
+ bs->buffer_alignment = 2048;
} else {
+ if (!bdrv_is_inserted(s->bs)) {
+ error_report("Device needs media, but drive is empty");
+ return -1;
+ }
if (bdrv_is_read_only(bs)) {
error_report("Can't use a read-only drive");
return -1;
@@ -2659,7 +2680,8 @@ static void ide_init1(IDEBus *bus, int unit)
s->bus = bus;
s->unit = unit;
s->drive_serial = drive_serial++;
- s->io_buffer = qemu_blockalign(s->bs, IDE_DMA_BUF_SECTORS*512 + 4);
+ /* we need at least 2k alignment for accessing CDROMs using O_DIRECT */
+ s->io_buffer = qemu_memalign(2048, IDE_DMA_BUF_SECTORS*512 + 4);
s->io_buffer_total_len = IDE_DMA_BUF_SECTORS*512 + 4;
s->smart_selftest_data = qemu_blockalign(s->bs, 512);
s->sector_write_timer = qemu_new_timer(vm_clock,
@@ -2729,6 +2751,7 @@ static EndTransferFunc* transfer_end_table[] = {
ide_transfer_stop,
ide_atapi_cmd_reply_end,
ide_atapi_cmd,
+ ide_dummy_transfer_stop,
};
static int transfer_end_table_idx(EndTransferFunc *fn)
@@ -2752,26 +2775,28 @@ static int ide_drive_post_load(void *opaque, int version_id)
s->cdrom_changed = 1;
}
}
+ return 0;
+}
+
+static int ide_drive_pio_post_load(void *opaque, int version_id)
+{
+ IDEState *s = opaque;
- if (s->cur_io_buffer_len) {
- s->end_transfer_func = transfer_end_table[s->end_transfer_fn_idx];
- s->data_ptr = s->io_buffer + s->cur_io_buffer_offset;
- s->data_end = s->data_ptr + s->cur_io_buffer_len;
+ if (s->end_transfer_fn_idx > ARRAY_SIZE(transfer_end_table)) {
+ return -EINVAL;
}
-
+ s->end_transfer_func = transfer_end_table[s->end_transfer_fn_idx];
+ s->data_ptr = s->io_buffer + s->cur_io_buffer_offset;
+ s->data_end = s->data_ptr + s->cur_io_buffer_len;
+
return 0;
}
-static void ide_drive_pre_save(void *opaque)
+static void ide_drive_pio_pre_save(void *opaque)
{
IDEState *s = opaque;
int idx;
- s->cur_io_buffer_len = 0;
-
- if (!(s->status & DRQ_STAT))
- return;
-
s->cur_io_buffer_offset = s->data_ptr - s->io_buffer;
s->cur_io_buffer_len = s->data_end - s->data_ptr;
@@ -2785,12 +2810,38 @@ static void ide_drive_pre_save(void *opaque)
}
}
+static bool ide_drive_pio_state_needed(void *opaque)
+{
+ IDEState *s = opaque;
+
+ return (s->status & DRQ_STAT) != 0;
+}
+
+const VMStateDescription vmstate_ide_drive_pio_state = {
+ .name = "ide_drive/pio_state",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .pre_save = ide_drive_pio_pre_save,
+ .post_load = ide_drive_pio_post_load,
+ .fields = (VMStateField []) {
+ VMSTATE_INT32(req_nb_sectors, IDEState),
+ VMSTATE_VARRAY_INT32(io_buffer, IDEState, io_buffer_total_len, 1,
+ vmstate_info_uint8, uint8_t),
+ VMSTATE_INT32(cur_io_buffer_offset, IDEState),
+ VMSTATE_INT32(cur_io_buffer_len, IDEState),
+ VMSTATE_UINT8(end_transfer_fn_idx, IDEState),
+ VMSTATE_INT32(elementary_transfer_size, IDEState),
+ VMSTATE_INT32(packet_transfer_size, IDEState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
const VMStateDescription vmstate_ide_drive = {
.name = "ide_drive",
- .version_id = 4,
+ .version_id = 3,
.minimum_version_id = 0,
.minimum_version_id_old = 0,
- .pre_save = ide_drive_pre_save,
.post_load = ide_drive_post_load,
.fields = (VMStateField []) {
VMSTATE_INT32(mult_sectors, IDEState),
@@ -2813,15 +2864,15 @@ const VMStateDescription vmstate_ide_drive = {
VMSTATE_UINT8(sense_key, IDEState),
VMSTATE_UINT8(asc, IDEState),
VMSTATE_UINT8_V(cdrom_changed, IDEState, 3),
- VMSTATE_INT32_V(req_nb_sectors, IDEState, 4),
- VMSTATE_VARRAY_INT32(io_buffer, IDEState, io_buffer_total_len, 4,
- vmstate_info_uint8, uint8_t),
- VMSTATE_INT32_V(cur_io_buffer_offset, IDEState, 4),
- VMSTATE_INT32_V(cur_io_buffer_len, IDEState, 4),
- VMSTATE_UINT8_V(end_transfer_fn_idx, IDEState, 4),
- VMSTATE_INT32_V(elementary_transfer_size, IDEState, 4),
- VMSTATE_INT32_V(packet_transfer_size, IDEState, 4),
VMSTATE_END_OF_LIST()
+ },
+ .subsections = (VMStateSubsection []) {
+ {
+ .vmsd = &vmstate_ide_drive_pio_state,
+ .needed = ide_drive_pio_state_needed,
+ }, {
+ /* empty */
+ }
}
};
diff --git a/hw/ide/pci.c b/hw/ide/pci.c
index 4d95cc5e22..ec90f266e9 100644
--- a/hw/ide/pci.c
+++ b/hw/ide/pci.c
@@ -40,8 +40,27 @@ void bmdma_cmd_writeb(void *opaque, uint32_t addr, uint32_t val)
printf("%s: 0x%08x\n", __func__, val);
#endif
if (!(val & BM_CMD_START)) {
- /* XXX: do it better */
- ide_dma_cancel(bm);
+ /*
+ * We can't cancel Scatter Gather DMA in the middle of the
+ * operation or a partial (not full) DMA transfer would reach
+ * the storage so we wait for completion instead (we beahve
+ * like if the DMA was completed by the time the guest trying
+ * to cancel dma with bmdma_cmd_writeb with BM_CMD_START not
+ * set).
+ *
+ * In the future we'll be able to safely cancel the I/O if the
+ * whole DMA operation will be submitted to disk with a single
+ * aio operation with preadv/pwritev.
+ */
+ if (bm->aiocb) {
+ qemu_aio_flush();
+#ifdef DEBUG_IDE
+ if (bm->aiocb)
+ printf("ide_dma_cancel: aiocb still pending");
+ if (bm->status & BM_STATUS_DMAING)
+ printf("ide_dma_cancel: BM_STATUS_DMAING still pending");
+#endif
+ }
bm->cmd = val & 0x09;
} else {
if (!(bm->status & BM_STATUS_DMAING)) {
@@ -121,9 +140,31 @@ void bmdma_addr_writel(void *opaque, uint32_t addr, uint32_t val)
bm->cur_addr = bm->addr;
}
+static bool ide_bmdma_current_needed(void *opaque)
+{
+ BMDMAState *bm = opaque;
+
+ return (bm->cur_prd_len != 0);
+}
+
+static const VMStateDescription vmstate_bmdma_current = {
+ .name = "ide bmdma_current",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT32(cur_addr, BMDMAState),
+ VMSTATE_UINT32(cur_prd_last, BMDMAState),
+ VMSTATE_UINT32(cur_prd_addr, BMDMAState),
+ VMSTATE_UINT32(cur_prd_len, BMDMAState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+
static const VMStateDescription vmstate_bmdma = {
.name = "ide bmdma",
- .version_id = 4,
+ .version_id = 3,
.minimum_version_id = 0,
.minimum_version_id_old = 0,
.fields = (VMStateField []) {
@@ -133,11 +174,15 @@ static const VMStateDescription vmstate_bmdma = {
VMSTATE_INT64(sector_num, BMDMAState),
VMSTATE_UINT32(nsector, BMDMAState),
VMSTATE_UINT8(unit, BMDMAState),
- VMSTATE_UINT32_V(cur_addr, BMDMAState, 4),
- VMSTATE_UINT32_V(cur_prd_last, BMDMAState, 4),
- VMSTATE_UINT32_V(cur_prd_addr, BMDMAState, 4),
- VMSTATE_UINT32_V(cur_prd_len, BMDMAState, 4),
VMSTATE_END_OF_LIST()
+ },
+ .subsections = (VMStateSubsection []) {
+ {
+ .vmsd = &vmstate_bmdma_current,
+ .needed = ide_bmdma_current_needed,
+ }, {
+ /* empty */
+ }
}
};
@@ -156,7 +201,7 @@ static int ide_pci_post_load(void *opaque, int version_id)
const VMStateDescription vmstate_ide_pci = {
.name = "ide",
- .version_id = 4,
+ .version_id = 3,
.minimum_version_id = 0,
.minimum_version_id_old = 0,
.post_load = ide_pci_post_load,
diff --git a/hw/ide/qdev.c b/hw/ide/qdev.c
index 53468edcbc..080876035f 100644
--- a/hw/ide/qdev.c
+++ b/hw/ide/qdev.c
@@ -20,6 +20,7 @@
#include "dma.h"
#include "qemu-error.h"
#include <hw/ide/internal.h>
+#include "blockdev.h"
/* --------------------------------- */
diff --git a/hw/ide/via.c b/hw/ide/via.c
index a403e8cd98..b2c7cad622 100644
--- a/hw/ide/via.c
+++ b/hw/ide/via.c
@@ -150,7 +150,6 @@ static int vt82c686b_ide_initfn(PCIDevice *dev)
pci_config_set_class(pci_conf, PCI_CLASS_STORAGE_IDE);
pci_config_set_prog_interface(pci_conf, 0x8a); /* legacy ATA mode */
pci_config_set_revision(pci_conf,0x06); /* Revision 0.6 */
- pci_conf[PCI_HEADER_TYPE] = PCI_HEADER_TYPE_NORMAL; /* header_type */
pci_set_long(pci_conf + PCI_CAPABILITY_LIST, 0x000000c0);
qemu_register_reset(via_reset, d);
diff --git a/hw/ivshmem.c b/hw/ivshmem.c
new file mode 100644
index 0000000000..06dce70e78
--- /dev/null
+++ b/hw/ivshmem.c
@@ -0,0 +1,829 @@
+/*
+ * Inter-VM Shared Memory PCI device.
+ *
+ * Author:
+ * Cam Macdonell <cam@cs.ualberta.ca>
+ *
+ * Based On: cirrus_vga.c
+ * Copyright (c) 2004 Fabrice Bellard
+ * Copyright (c) 2004 Makoto Suzuki (suzu)
+ *
+ * and rtl8139.c
+ * Copyright (c) 2006 Igor Kovalenko
+ *
+ * This code is licensed under the GNU GPL v2.
+ */
+#include "hw.h"
+#include "pc.h"
+#include "pci.h"
+#include "msix.h"
+#include "kvm.h"
+
+#include <sys/mman.h>
+#include <sys/types.h>
+
+#define IVSHMEM_IOEVENTFD 0
+#define IVSHMEM_MSI 1
+
+#define IVSHMEM_PEER 0
+#define IVSHMEM_MASTER 1
+
+#define IVSHMEM_REG_BAR_SIZE 0x100
+
+//#define DEBUG_IVSHMEM
+#ifdef DEBUG_IVSHMEM
+#define IVSHMEM_DPRINTF(fmt, ...) \
+ do {printf("IVSHMEM: " fmt, ## __VA_ARGS__); } while (0)
+#else
+#define IVSHMEM_DPRINTF(fmt, ...)
+#endif
+
+typedef struct Peer {
+ int nb_eventfds;
+ int *eventfds;
+} Peer;
+
+typedef struct EventfdEntry {
+ PCIDevice *pdev;
+ int vector;
+} EventfdEntry;
+
+typedef struct IVShmemState {
+ PCIDevice dev;
+ uint32_t intrmask;
+ uint32_t intrstatus;
+ uint32_t doorbell;
+
+ CharDriverState **eventfd_chr;
+ CharDriverState *server_chr;
+ int ivshmem_mmio_io_addr;
+
+ pcibus_t mmio_addr;
+ pcibus_t shm_pci_addr;
+ uint64_t ivshmem_offset;
+ uint64_t ivshmem_size; /* size of shared memory region */
+ int shm_fd; /* shared memory file descriptor */
+
+ Peer *peers;
+ int nb_peers; /* how many guests we have space for */
+ int max_peer; /* maximum numbered peer */
+
+ int vm_id;
+ uint32_t vectors;
+ uint32_t features;
+ EventfdEntry *eventfd_table;
+
+ char * shmobj;
+ char * sizearg;
+ char * role;
+ int role_val; /* scalar to avoid multiple string comparisons */
+} IVShmemState;
+
+/* registers for the Inter-VM shared memory device */
+enum ivshmem_registers {
+ INTRMASK = 0,
+ INTRSTATUS = 4,
+ IVPOSITION = 8,
+ DOORBELL = 12,
+};
+
+static inline uint32_t ivshmem_has_feature(IVShmemState *ivs,
+ unsigned int feature) {
+ return (ivs->features & (1 << feature));
+}
+
+static inline bool is_power_of_two(uint64_t x) {
+ return (x & (x - 1)) == 0;
+}
+
+static void ivshmem_map(PCIDevice *pci_dev, int region_num,
+ pcibus_t addr, pcibus_t size, int type)
+{
+ IVShmemState *s = DO_UPCAST(IVShmemState, dev, pci_dev);
+
+ s->shm_pci_addr = addr;
+
+ if (s->ivshmem_offset > 0) {
+ cpu_register_physical_memory(s->shm_pci_addr, s->ivshmem_size,
+ s->ivshmem_offset);
+ }
+
+ IVSHMEM_DPRINTF("guest pci addr = %" FMT_PCIBUS ", guest h/w addr = %"
+ PRIu64 ", size = %" FMT_PCIBUS "\n", addr, s->ivshmem_offset, size);
+
+}
+
+/* accessing registers - based on rtl8139 */
+static void ivshmem_update_irq(IVShmemState *s, int val)
+{
+ int isr;
+ isr = (s->intrstatus & s->intrmask) & 0xffffffff;
+
+ /* don't print ISR resets */
+ if (isr) {
+ IVSHMEM_DPRINTF("Set IRQ to %d (%04x %04x)\n",
+ isr ? 1 : 0, s->intrstatus, s->intrmask);
+ }
+
+ qemu_set_irq(s->dev.irq[0], (isr != 0));
+}
+
+static void ivshmem_IntrMask_write(IVShmemState *s, uint32_t val)
+{
+ IVSHMEM_DPRINTF("IntrMask write(w) val = 0x%04x\n", val);
+
+ s->intrmask = val;
+
+ ivshmem_update_irq(s, val);
+}
+
+static uint32_t ivshmem_IntrMask_read(IVShmemState *s)
+{
+ uint32_t ret = s->intrmask;
+
+ IVSHMEM_DPRINTF("intrmask read(w) val = 0x%04x\n", ret);
+
+ return ret;
+}
+
+static void ivshmem_IntrStatus_write(IVShmemState *s, uint32_t val)
+{
+ IVSHMEM_DPRINTF("IntrStatus write(w) val = 0x%04x\n", val);
+
+ s->intrstatus = val;
+
+ ivshmem_update_irq(s, val);
+ return;
+}
+
+static uint32_t ivshmem_IntrStatus_read(IVShmemState *s)
+{
+ uint32_t ret = s->intrstatus;
+
+ /* reading ISR clears all interrupts */
+ s->intrstatus = 0;
+
+ ivshmem_update_irq(s, 0);
+
+ return ret;
+}
+
+static void ivshmem_io_writew(void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+
+ IVSHMEM_DPRINTF("We shouldn't be writing words\n");
+}
+
+static void ivshmem_io_writel(void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ IVShmemState *s = opaque;
+
+ uint64_t write_one = 1;
+ uint16_t dest = val >> 16;
+ uint16_t vector = val & 0xff;
+
+ addr &= 0xfc;
+
+ IVSHMEM_DPRINTF("writing to addr " TARGET_FMT_plx "\n", addr);
+ switch (addr)
+ {
+ case INTRMASK:
+ ivshmem_IntrMask_write(s, val);
+ break;
+
+ case INTRSTATUS:
+ ivshmem_IntrStatus_write(s, val);
+ break;
+
+ case DOORBELL:
+ /* check that dest VM ID is reasonable */
+ if (dest > s->max_peer) {
+ IVSHMEM_DPRINTF("Invalid destination VM ID (%d)\n", dest);
+ break;
+ }
+
+ /* check doorbell range */
+ if (vector < s->peers[dest].nb_eventfds) {
+ IVSHMEM_DPRINTF("Writing %" PRId64 " to VM %d on vector %d\n",
+ write_one, dest, vector);
+ if (write(s->peers[dest].eventfds[vector],
+ &(write_one), 8) != 8) {
+ IVSHMEM_DPRINTF("error writing to eventfd\n");
+ }
+ }
+ break;
+ default:
+ IVSHMEM_DPRINTF("Invalid VM Doorbell VM %d\n", dest);
+ }
+}
+
+static void ivshmem_io_writeb(void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ IVSHMEM_DPRINTF("We shouldn't be writing bytes\n");
+}
+
+static uint32_t ivshmem_io_readw(void *opaque, target_phys_addr_t addr)
+{
+
+ IVSHMEM_DPRINTF("We shouldn't be reading words\n");
+ return 0;
+}
+
+static uint32_t ivshmem_io_readl(void *opaque, target_phys_addr_t addr)
+{
+
+ IVShmemState *s = opaque;
+ uint32_t ret;
+
+ switch (addr)
+ {
+ case INTRMASK:
+ ret = ivshmem_IntrMask_read(s);
+ break;
+
+ case INTRSTATUS:
+ ret = ivshmem_IntrStatus_read(s);
+ break;
+
+ case IVPOSITION:
+ /* return my VM ID if the memory is mapped */
+ if (s->shm_fd > 0) {
+ ret = s->vm_id;
+ } else {
+ ret = -1;
+ }
+ break;
+
+ default:
+ IVSHMEM_DPRINTF("why are we reading " TARGET_FMT_plx "\n", addr);
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static uint32_t ivshmem_io_readb(void *opaque, target_phys_addr_t addr)
+{
+ IVSHMEM_DPRINTF("We shouldn't be reading bytes\n");
+
+ return 0;
+}
+
+static CPUReadMemoryFunc * const ivshmem_mmio_read[3] = {
+ ivshmem_io_readb,
+ ivshmem_io_readw,
+ ivshmem_io_readl,
+};
+
+static CPUWriteMemoryFunc * const ivshmem_mmio_write[3] = {
+ ivshmem_io_writeb,
+ ivshmem_io_writew,
+ ivshmem_io_writel,
+};
+
+static void ivshmem_receive(void *opaque, const uint8_t *buf, int size)
+{
+ IVShmemState *s = opaque;
+
+ ivshmem_IntrStatus_write(s, *buf);
+
+ IVSHMEM_DPRINTF("ivshmem_receive 0x%02x\n", *buf);
+}
+
+static int ivshmem_can_receive(void * opaque)
+{
+ return 8;
+}
+
+static void ivshmem_event(void *opaque, int event)
+{
+ IVSHMEM_DPRINTF("ivshmem_event %d\n", event);
+}
+
+static void fake_irqfd(void *opaque, const uint8_t *buf, int size) {
+
+ EventfdEntry *entry = opaque;
+ PCIDevice *pdev = entry->pdev;
+
+ IVSHMEM_DPRINTF("interrupt on vector %p %d\n", pdev, entry->vector);
+ msix_notify(pdev, entry->vector);
+}
+
+static CharDriverState* create_eventfd_chr_device(void * opaque, int eventfd,
+ int vector)
+{
+ /* create a event character device based on the passed eventfd */
+ IVShmemState *s = opaque;
+ CharDriverState * chr;
+
+ chr = qemu_chr_open_eventfd(eventfd);
+
+ if (chr == NULL) {
+ fprintf(stderr, "creating eventfd for eventfd %d failed\n", eventfd);
+ exit(-1);
+ }
+
+ /* if MSI is supported we need multiple interrupts */
+ if (ivshmem_has_feature(s, IVSHMEM_MSI)) {
+ s->eventfd_table[vector].pdev = &s->dev;
+ s->eventfd_table[vector].vector = vector;
+
+ qemu_chr_add_handlers(chr, ivshmem_can_receive, fake_irqfd,
+ ivshmem_event, &s->eventfd_table[vector]);
+ } else {
+ qemu_chr_add_handlers(chr, ivshmem_can_receive, ivshmem_receive,
+ ivshmem_event, s);
+ }
+
+ return chr;
+
+}
+
+static int check_shm_size(IVShmemState *s, int fd) {
+ /* check that the guest isn't going to try and map more memory than the
+ * the object has allocated return -1 to indicate error */
+
+ struct stat buf;
+
+ fstat(fd, &buf);
+
+ if (s->ivshmem_size > buf.st_size) {
+ fprintf(stderr,
+ "IVSHMEM ERROR: Requested memory size greater"
+ " than shared object size (%" PRIu64 " > %" PRIu64")\n",
+ s->ivshmem_size, (uint64_t)buf.st_size);
+ return -1;
+ } else {
+ return 0;
+ }
+}
+
+/* create the shared memory BAR when we are not using the server, so we can
+ * create the BAR and map the memory immediately */
+static void create_shared_memory_BAR(IVShmemState *s, int fd) {
+
+ void * ptr;
+
+ s->shm_fd = fd;
+
+ ptr = mmap(0, s->ivshmem_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
+
+ s->ivshmem_offset = qemu_ram_alloc_from_ptr(&s->dev.qdev, "ivshmem.bar2",
+ s->ivshmem_size, ptr);
+
+ /* region for shared memory */
+ pci_register_bar(&s->dev, 2, s->ivshmem_size,
+ PCI_BASE_ADDRESS_SPACE_MEMORY, ivshmem_map);
+}
+
+static void close_guest_eventfds(IVShmemState *s, int posn)
+{
+ int i, guest_curr_max;
+
+ guest_curr_max = s->peers[posn].nb_eventfds;
+
+ for (i = 0; i < guest_curr_max; i++) {
+ kvm_set_ioeventfd_mmio_long(s->peers[posn].eventfds[i],
+ s->mmio_addr + DOORBELL, (posn << 16) | i, 0);
+ close(s->peers[posn].eventfds[i]);
+ }
+
+ qemu_free(s->peers[posn].eventfds);
+ s->peers[posn].nb_eventfds = 0;
+}
+
+static void setup_ioeventfds(IVShmemState *s) {
+
+ int i, j;
+
+ for (i = 0; i <= s->max_peer; i++) {
+ for (j = 0; j < s->peers[i].nb_eventfds; j++) {
+ kvm_set_ioeventfd_mmio_long(s->peers[i].eventfds[j],
+ s->mmio_addr + DOORBELL, (i << 16) | j, 1);
+ }
+ }
+}
+
+/* this function increase the dynamic storage need to store data about other
+ * guests */
+static void increase_dynamic_storage(IVShmemState *s, int new_min_size) {
+
+ int j, old_nb_alloc;
+
+ old_nb_alloc = s->nb_peers;
+
+ while (new_min_size >= s->nb_peers)
+ s->nb_peers = s->nb_peers * 2;
+
+ IVSHMEM_DPRINTF("bumping storage to %d guests\n", s->nb_peers);
+ s->peers = qemu_realloc(s->peers, s->nb_peers * sizeof(Peer));
+
+ /* zero out new pointers */
+ for (j = old_nb_alloc; j < s->nb_peers; j++) {
+ s->peers[j].eventfds = NULL;
+ s->peers[j].nb_eventfds = 0;
+ }
+}
+
+static void ivshmem_read(void *opaque, const uint8_t * buf, int flags)
+{
+ IVShmemState *s = opaque;
+ int incoming_fd, tmp_fd;
+ int guest_max_eventfd;
+ long incoming_posn;
+
+ memcpy(&incoming_posn, buf, sizeof(long));
+ /* pick off s->server_chr->msgfd and store it, posn should accompany msg */
+ tmp_fd = qemu_chr_get_msgfd(s->server_chr);
+ IVSHMEM_DPRINTF("posn is %ld, fd is %d\n", incoming_posn, tmp_fd);
+
+ /* make sure we have enough space for this guest */
+ if (incoming_posn >= s->nb_peers) {
+ increase_dynamic_storage(s, incoming_posn);
+ }
+
+ if (tmp_fd == -1) {
+ /* if posn is positive and unseen before then this is our posn*/
+ if ((incoming_posn >= 0) &&
+ (s->peers[incoming_posn].eventfds == NULL)) {
+ /* receive our posn */
+ s->vm_id = incoming_posn;
+ return;
+ } else {
+ /* otherwise an fd == -1 means an existing guest has gone away */
+ IVSHMEM_DPRINTF("posn %ld has gone away\n", incoming_posn);
+ close_guest_eventfds(s, incoming_posn);
+ return;
+ }
+ }
+
+ /* because of the implementation of get_msgfd, we need a dup */
+ incoming_fd = dup(tmp_fd);
+
+ if (incoming_fd == -1) {
+ fprintf(stderr, "could not allocate file descriptor %s\n",
+ strerror(errno));
+ return;
+ }
+
+ /* if the position is -1, then it's shared memory region fd */
+ if (incoming_posn == -1) {
+
+ void * map_ptr;
+
+ s->max_peer = 0;
+
+ if (check_shm_size(s, incoming_fd) == -1) {
+ exit(-1);
+ }
+
+ /* mmap the region and map into the BAR2 */
+ map_ptr = mmap(0, s->ivshmem_size, PROT_READ|PROT_WRITE, MAP_SHARED,
+ incoming_fd, 0);
+ s->ivshmem_offset = qemu_ram_alloc_from_ptr(&s->dev.qdev,
+ "ivshmem.bar2", s->ivshmem_size, map_ptr);
+
+ IVSHMEM_DPRINTF("guest pci addr = %" FMT_PCIBUS ", guest h/w addr = %"
+ PRIu64 ", size = %" PRIu64 "\n", s->shm_pci_addr,
+ s->ivshmem_offset, s->ivshmem_size);
+
+ if (s->shm_pci_addr > 0) {
+ /* map memory into BAR2 */
+ cpu_register_physical_memory(s->shm_pci_addr, s->ivshmem_size,
+ s->ivshmem_offset);
+ }
+
+ /* only store the fd if it is successfully mapped */
+ s->shm_fd = incoming_fd;
+
+ return;
+ }
+
+ /* each guest has an array of eventfds, and we keep track of how many
+ * guests for each VM */
+ guest_max_eventfd = s->peers[incoming_posn].nb_eventfds;
+
+ if (guest_max_eventfd == 0) {
+ /* one eventfd per MSI vector */
+ s->peers[incoming_posn].eventfds = (int *) qemu_malloc(s->vectors *
+ sizeof(int));
+ }
+
+ /* this is an eventfd for a particular guest VM */
+ IVSHMEM_DPRINTF("eventfds[%ld][%d] = %d\n", incoming_posn,
+ guest_max_eventfd, incoming_fd);
+ s->peers[incoming_posn].eventfds[guest_max_eventfd] = incoming_fd;
+
+ /* increment count for particular guest */
+ s->peers[incoming_posn].nb_eventfds++;
+
+ /* keep track of the maximum VM ID */
+ if (incoming_posn > s->max_peer) {
+ s->max_peer = incoming_posn;
+ }
+
+ if (incoming_posn == s->vm_id) {
+ s->eventfd_chr[guest_max_eventfd] = create_eventfd_chr_device(s,
+ s->peers[s->vm_id].eventfds[guest_max_eventfd],
+ guest_max_eventfd);
+ }
+
+ if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD)) {
+ if (kvm_set_ioeventfd_mmio_long(incoming_fd, s->mmio_addr + DOORBELL,
+ (incoming_posn << 16) | guest_max_eventfd, 1) < 0) {
+ fprintf(stderr, "ivshmem: ioeventfd not available\n");
+ }
+ }
+
+ return;
+}
+
+static void ivshmem_reset(DeviceState *d)
+{
+ IVShmemState *s = DO_UPCAST(IVShmemState, dev.qdev, d);
+
+ s->intrstatus = 0;
+ return;
+}
+
+static void ivshmem_mmio_map(PCIDevice *pci_dev, int region_num,
+ pcibus_t addr, pcibus_t size, int type)
+{
+ IVShmemState *s = DO_UPCAST(IVShmemState, dev, pci_dev);
+
+ s->mmio_addr = addr;
+ cpu_register_physical_memory(addr + 0, IVSHMEM_REG_BAR_SIZE,
+ s->ivshmem_mmio_io_addr);
+
+ if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD)) {
+ setup_ioeventfds(s);
+ }
+}
+
+static uint64_t ivshmem_get_size(IVShmemState * s) {
+
+ uint64_t value;
+ char *ptr;
+
+ value = strtoull(s->sizearg, &ptr, 10);
+ switch (*ptr) {
+ case 0: case 'M': case 'm':
+ value <<= 20;
+ break;
+ case 'G': case 'g':
+ value <<= 30;
+ break;
+ default:
+ fprintf(stderr, "qemu: invalid ram size: %s\n", s->sizearg);
+ exit(1);
+ }
+
+ /* BARs must be a power of 2 */
+ if (!is_power_of_two(value)) {
+ fprintf(stderr, "ivshmem: size must be power of 2\n");
+ exit(1);
+ }
+
+ return value;
+}
+
+static void ivshmem_setup_msi(IVShmemState * s) {
+
+ int i;
+
+ /* allocate the MSI-X vectors */
+
+ if (!msix_init(&s->dev, s->vectors, 1, 0)) {
+ pci_register_bar(&s->dev, 1,
+ msix_bar_size(&s->dev),
+ PCI_BASE_ADDRESS_SPACE_MEMORY,
+ msix_mmio_map);
+ IVSHMEM_DPRINTF("msix initialized (%d vectors)\n", s->vectors);
+ } else {
+ IVSHMEM_DPRINTF("msix initialization failed\n");
+ exit(1);
+ }
+
+ /* 'activate' the vectors */
+ for (i = 0; i < s->vectors; i++) {
+ msix_vector_use(&s->dev, i);
+ }
+
+ /* allocate Qemu char devices for receiving interrupts */
+ s->eventfd_table = qemu_mallocz(s->vectors * sizeof(EventfdEntry));
+}
+
+static void ivshmem_save(QEMUFile* f, void *opaque)
+{
+ IVShmemState *proxy = opaque;
+
+ IVSHMEM_DPRINTF("ivshmem_save\n");
+ pci_device_save(&proxy->dev, f);
+
+ if (ivshmem_has_feature(proxy, IVSHMEM_MSI)) {
+ msix_save(&proxy->dev, f);
+ } else {
+ qemu_put_be32(f, proxy->intrstatus);
+ qemu_put_be32(f, proxy->intrmask);
+ }
+
+}
+
+static int ivshmem_load(QEMUFile* f, void *opaque, int version_id)
+{
+ IVSHMEM_DPRINTF("ivshmem_load\n");
+
+ IVShmemState *proxy = opaque;
+ int ret, i;
+
+ if (version_id > 0) {
+ return -EINVAL;
+ }
+
+ if (proxy->role_val == IVSHMEM_PEER) {
+ fprintf(stderr, "ivshmem: 'peer' devices are not migratable\n");
+ return -EINVAL;
+ }
+
+ ret = pci_device_load(&proxy->dev, f);
+ if (ret) {
+ return ret;
+ }
+
+ if (ivshmem_has_feature(proxy, IVSHMEM_MSI)) {
+ msix_load(&proxy->dev, f);
+ for (i = 0; i < proxy->vectors; i++) {
+ msix_vector_use(&proxy->dev, i);
+ }
+ } else {
+ proxy->intrstatus = qemu_get_be32(f);
+ proxy->intrmask = qemu_get_be32(f);
+ }
+
+ return 0;
+}
+
+static int pci_ivshmem_init(PCIDevice *dev)
+{
+ IVShmemState *s = DO_UPCAST(IVShmemState, dev, dev);
+ uint8_t *pci_conf;
+
+ if (s->sizearg == NULL)
+ s->ivshmem_size = 4 << 20; /* 4 MB default */
+ else {
+ s->ivshmem_size = ivshmem_get_size(s);
+ }
+
+ register_savevm(&s->dev.qdev, "ivshmem", 0, 0, ivshmem_save, ivshmem_load,
+ dev);
+
+ /* IRQFD requires MSI */
+ if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD) &&
+ !ivshmem_has_feature(s, IVSHMEM_MSI)) {
+ fprintf(stderr, "ivshmem: ioeventfd/irqfd requires MSI\n");
+ exit(1);
+ }
+
+ /* check that role is reasonable */
+ if (s->role) {
+ if (strncmp(s->role, "peer", 5) == 0) {
+ s->role_val = IVSHMEM_PEER;
+ } else if (strncmp(s->role, "master", 7) == 0) {
+ s->role_val = IVSHMEM_MASTER;
+ } else {
+ fprintf(stderr, "ivshmem: 'role' must be 'peer' or 'master'\n");
+ exit(1);
+ }
+ } else {
+ s->role_val = IVSHMEM_MASTER; /* default */
+ }
+
+ if (s->role_val == IVSHMEM_PEER) {
+ register_device_unmigratable(&s->dev.qdev, "ivshmem", s);
+ }
+
+ pci_conf = s->dev.config;
+ pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_REDHAT_QUMRANET);
+ pci_conf[0x02] = 0x10;
+ pci_conf[0x03] = 0x11;
+ pci_conf[PCI_COMMAND] = PCI_COMMAND_IO | PCI_COMMAND_MEMORY;
+ pci_config_set_class(pci_conf, PCI_CLASS_MEMORY_RAM);
+ pci_conf[PCI_HEADER_TYPE] = PCI_HEADER_TYPE_NORMAL;
+
+ pci_config_set_interrupt_pin(pci_conf, 1);
+
+ s->shm_pci_addr = 0;
+ s->ivshmem_offset = 0;
+ s->shm_fd = 0;
+
+ s->ivshmem_mmio_io_addr = cpu_register_io_memory(ivshmem_mmio_read,
+ ivshmem_mmio_write, s);
+ /* region for registers*/
+ pci_register_bar(&s->dev, 0, IVSHMEM_REG_BAR_SIZE,
+ PCI_BASE_ADDRESS_SPACE_MEMORY, ivshmem_mmio_map);
+
+ if ((s->server_chr != NULL) &&
+ (strncmp(s->server_chr->filename, "unix:", 5) == 0)) {
+ /* if we get a UNIX socket as the parameter we will talk
+ * to the ivshmem server to receive the memory region */
+
+ if (s->shmobj != NULL) {
+ fprintf(stderr, "WARNING: do not specify both 'chardev' "
+ "and 'shm' with ivshmem\n");
+ }
+
+ IVSHMEM_DPRINTF("using shared memory server (socket = %s)\n",
+ s->server_chr->filename);
+
+ if (ivshmem_has_feature(s, IVSHMEM_MSI)) {
+ ivshmem_setup_msi(s);
+ }
+
+ /* we allocate enough space for 16 guests and grow as needed */
+ s->nb_peers = 16;
+ s->vm_id = -1;
+
+ /* allocate/initialize space for interrupt handling */
+ s->peers = qemu_mallocz(s->nb_peers * sizeof(Peer));
+
+ pci_register_bar(&s->dev, 2, s->ivshmem_size,
+ PCI_BASE_ADDRESS_SPACE_MEMORY, ivshmem_map);
+
+ s->eventfd_chr = qemu_mallocz(s->vectors * sizeof(CharDriverState *));
+
+ qemu_chr_add_handlers(s->server_chr, ivshmem_can_receive, ivshmem_read,
+ ivshmem_event, s);
+ } else {
+ /* just map the file immediately, we're not using a server */
+ int fd;
+
+ if (s->shmobj == NULL) {
+ fprintf(stderr, "Must specify 'chardev' or 'shm' to ivshmem\n");
+ }
+
+ IVSHMEM_DPRINTF("using shm_open (shm object = %s)\n", s->shmobj);
+
+ /* try opening with O_EXCL and if it succeeds zero the memory
+ * by truncating to 0 */
+ if ((fd = shm_open(s->shmobj, O_CREAT|O_RDWR|O_EXCL,
+ S_IRWXU|S_IRWXG|S_IRWXO)) > 0) {
+ /* truncate file to length PCI device's memory */
+ if (ftruncate(fd, s->ivshmem_size) != 0) {
+ fprintf(stderr, "ivshmem: could not truncate shared file\n");
+ }
+
+ } else if ((fd = shm_open(s->shmobj, O_CREAT|O_RDWR,
+ S_IRWXU|S_IRWXG|S_IRWXO)) < 0) {
+ fprintf(stderr, "ivshmem: could not open shared file\n");
+ exit(-1);
+
+ }
+
+ if (check_shm_size(s, fd) == -1) {
+ exit(-1);
+ }
+
+ create_shared_memory_BAR(s, fd);
+
+ }
+
+ return 0;
+}
+
+static int pci_ivshmem_uninit(PCIDevice *dev)
+{
+ IVShmemState *s = DO_UPCAST(IVShmemState, dev, dev);
+
+ cpu_unregister_io_memory(s->ivshmem_mmio_io_addr);
+ unregister_savevm(&dev->qdev, "ivshmem", s);
+
+ return 0;
+}
+
+static PCIDeviceInfo ivshmem_info = {
+ .qdev.name = "ivshmem",
+ .qdev.size = sizeof(IVShmemState),
+ .qdev.reset = ivshmem_reset,
+ .init = pci_ivshmem_init,
+ .exit = pci_ivshmem_uninit,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_CHR("chardev", IVShmemState, server_chr),
+ DEFINE_PROP_STRING("size", IVShmemState, sizearg),
+ DEFINE_PROP_UINT32("vectors", IVShmemState, vectors, 1),
+ DEFINE_PROP_BIT("ioeventfd", IVShmemState, features, IVSHMEM_IOEVENTFD, false),
+ DEFINE_PROP_BIT("msi", IVShmemState, features, IVSHMEM_MSI, true),
+ DEFINE_PROP_STRING("shm", IVShmemState, shmobj),
+ DEFINE_PROP_STRING("role", IVShmemState, role),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static void ivshmem_register_devices(void)
+{
+ pci_qdev_register(&ivshmem_info);
+}
+
+device_init(ivshmem_register_devices)
diff --git a/hw/jazz_led.c b/hw/jazz_led.c
index 18780e9371..4cb680c3e4 100644
--- a/hw/jazz_led.c
+++ b/hw/jazz_led.c
@@ -29,6 +29,15 @@
//#define DEBUG_LED
+#ifdef DEBUG_LED
+#define DPRINTF(fmt, ...) \
+do { printf("jazz led: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while (0)
+#endif
+#define BADF(fmt, ...) \
+do { fprintf(stderr, "jazz led ERROR: " fmt , ## __VA_ARGS__);} while (0)
+
typedef enum {
REDRAW_NONE = 0, REDRAW_SEGMENTS = 1, REDRAW_BACKGROUND = 2,
} screen_state_t;
@@ -49,12 +58,12 @@ static uint32_t led_readb(void *opaque, target_phys_addr_t addr)
val = s->segments;
break;
default:
-#ifdef DEBUG_LED
- printf("jazz led: invalid read [0x%x]\n", relative_addr);
-#endif
+ BADF("invalid read at [" TARGET_FMT_plx "]\n", addr);
val = 0;
}
+ DPRINTF("read addr=" TARGET_FMT_plx " val=0x%02x\n", addr, val);
+
return val;
}
@@ -92,15 +101,15 @@ static void led_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
{
LedState *s = opaque;
+ DPRINTF("write addr=" TARGET_FMT_plx " val=0x%02x\n", addr, val);
+
switch (addr) {
case 0:
s->segments = val;
s->state |= REDRAW_SEGMENTS;
break;
default:
-#ifdef DEBUG_LED
- printf("jazz led: invalid write of 0x%02x at [0x%x]\n", val, relative_addr);
-#endif
+ BADF("invalid write of 0x%08x at [" TARGET_FMT_plx "]\n", val, addr);
break;
}
}
diff --git a/hw/loader.c b/hw/loader.c
index 79a6f95186..49ac1fa1cc 100644
--- a/hw/loader.c
+++ b/hw/loader.c
@@ -733,11 +733,6 @@ int rom_copy(uint8_t *dest, target_phys_addr_t addr, size_t size)
s = rom->data;
l = rom->romsize;
- if (rom->addr < addr) {
- d = dest;
- s += (addr - rom->addr);
- l -= (addr - rom->addr);
- }
if ((d + l) > (dest + size)) {
l = dest - d;
}
diff --git a/hw/lsi53c895a.c b/hw/lsi53c895a.c
index bd7b661426..f97335eaa9 100644
--- a/hw/lsi53c895a.c
+++ b/hw/lsi53c895a.c
@@ -600,7 +600,7 @@ static void lsi_queue_command(LSIState *s)
{
lsi_request *p = s->current;
- DPRINTF("Queueing tag=0x%x\n", s->current_tag);
+ DPRINTF("Queueing tag=0x%x\n", p->tag);
assert(s->current != NULL);
assert(s->current->dma_len == 0);
QTAILQ_INSERT_TAIL(&s->queue, s->current, next);
@@ -864,6 +864,7 @@ static void lsi_do_msgout(LSIState *s)
case 0x01:
len = lsi_get_msgbyte(s);
msg = lsi_get_msgbyte(s);
+ (void)len; /* avoid a warning about unused variable*/
DPRINTF("Extended message 0x%x (len %d)\n", msg, len);
switch (msg) {
case 1:
@@ -880,7 +881,7 @@ static void lsi_do_msgout(LSIState *s)
break;
case 0x20: /* SIMPLE queue */
s->select_tag |= lsi_get_msgbyte(s) | LSI_TAG_VALID;
- DPRINTF("SIMPLE queue tag=0x%x\n", s->current_tag & 0xff);
+ DPRINTF("SIMPLE queue tag=0x%x\n", s->select_tag & 0xff);
break;
case 0x21: /* HEAD of queue */
BADF("HEAD queue not implemented\n");
diff --git a/hw/mainstone.c b/hw/mainstone.c
index cba7e63b3c..efa2959c72 100644
--- a/hw/mainstone.c
+++ b/hw/mainstone.c
@@ -17,6 +17,7 @@
#include "mainstone.h"
#include "sysemu.h"
#include "flash.h"
+#include "blockdev.h"
static struct keymap map[0xE0] = {
[0 ... 0xDF] = { -1, -1 },
diff --git a/hw/mips_fulong2e.c b/hw/mips_fulong2e.c
index a9bbff64b3..07eb9eeba1 100644
--- a/hw/mips_fulong2e.c
+++ b/hw/mips_fulong2e.c
@@ -37,6 +37,7 @@
#include "elf.h"
#include "vt82c686.h"
#include "mc146818rtc.h"
+#include "blockdev.h"
#define DEBUG_FULONG2E_INIT
@@ -75,7 +76,8 @@ static struct _loaderparams {
const char *initrd_filename;
} loaderparams;
-static void prom_set(uint32_t* prom_buf, int index, const char *string, ...)
+static void GCC_FMT_ATTR(3, 4) prom_set(uint32_t* prom_buf, int index,
+ const char *string, ...)
{
va_list ap;
int32_t table_addr;
@@ -140,13 +142,13 @@ static int64_t load_kernel (CPUState *env)
prom_size = ENVP_NB_ENTRIES * (sizeof(int32_t) + ENVP_ENTRY_SIZE);
prom_buf = qemu_malloc(prom_size);
- prom_set(prom_buf, index++, loaderparams.kernel_filename);
+ prom_set(prom_buf, index++, "%s", loaderparams.kernel_filename);
if (initrd_size > 0) {
- prom_set(prom_buf, index++, "rd_start=0x" PRIx64 " rd_size=%li %s",
+ prom_set(prom_buf, index++, "rd_start=0x%" PRIx64 " rd_size=%li %s",
cpu_mips_phys_to_kseg0(NULL, initrd_offset), initrd_size,
loaderparams.kernel_cmdline);
} else {
- prom_set(prom_buf, index++, loaderparams.kernel_cmdline);
+ prom_set(prom_buf, index++, "%s", loaderparams.kernel_cmdline);
}
/* Setup minimum environment variables */
@@ -219,8 +221,8 @@ uint8_t eeprom_spd[0x80] = {
#ifdef HAS_AUDIO
static void audio_init (PCIBus *pci_bus)
{
- vt82c686b_ac97_init(pci_bus, (FULONG2E_VIA_SLOT << 3) + 5);
- vt82c686b_mc97_init(pci_bus, (FULONG2E_VIA_SLOT << 3) + 6);
+ vt82c686b_ac97_init(pci_bus, PCI_DEVFN(FULONG2E_VIA_SLOT, 5));
+ vt82c686b_mc97_init(pci_bus, PCI_DEVFN(FULONG2E_VIA_SLOT, 6));
}
#endif
@@ -257,19 +259,17 @@ static void mips_fulong2e_init(ram_addr_t ram_size, const char *boot_device,
{
char *filename;
unsigned long ram_offset, bios_offset;
- unsigned long bios_size;
+ long bios_size;
int64_t kernel_entry;
qemu_irq *i8259;
qemu_irq *cpu_exit_irq;
int via_devfn;
PCIBus *pci_bus;
- ISADevice *isa_dev;
uint8_t *eeprom_buf;
i2c_bus *smbus;
int i;
DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS];
DeviceState *eeprom;
- ISADevice *rtc_state;
CPUState *env;
/* init CPUs */
@@ -295,7 +295,7 @@ static void mips_fulong2e_init(ram_addr_t ram_size, const char *boot_device,
ram_offset = qemu_ram_alloc(NULL, "fulong2e.ram", ram_size);
bios_offset = qemu_ram_alloc(NULL, "fulong2e.bios", bios_size);
- cpu_register_physical_memory(0, ram_size, IO_MEM_RAM);
+ cpu_register_physical_memory(0, ram_size, ram_offset);
cpu_register_physical_memory(0x1fc00000LL,
bios_size, bios_offset | IO_MEM_ROM);
@@ -349,18 +349,18 @@ static void mips_fulong2e_init(ram_addr_t ram_size, const char *boot_device,
hd[i] = drive_get(IF_IDE, i / MAX_IDE_DEVS, i % MAX_IDE_DEVS);
}
- via_devfn = vt82c686b_init(pci_bus, FULONG2E_VIA_SLOT << 3);
+ via_devfn = vt82c686b_init(pci_bus, PCI_DEVFN(FULONG2E_VIA_SLOT, 0));
if (via_devfn < 0) {
fprintf(stderr, "vt82c686b_init error \n");
exit(1);
}
isa_bus_irqs(i8259);
- vt82c686b_ide_init(pci_bus, hd, (FULONG2E_VIA_SLOT << 3) + 1);
- usb_uhci_vt82c686b_init(pci_bus, (FULONG2E_VIA_SLOT << 3) + 2);
- usb_uhci_vt82c686b_init(pci_bus, (FULONG2E_VIA_SLOT << 3) + 3);
+ vt82c686b_ide_init(pci_bus, hd, PCI_DEVFN(FULONG2E_VIA_SLOT, 1));
+ usb_uhci_vt82c686b_init(pci_bus, PCI_DEVFN(FULONG2E_VIA_SLOT, 2));
+ usb_uhci_vt82c686b_init(pci_bus, PCI_DEVFN(FULONG2E_VIA_SLOT, 3));
- smbus = vt82c686b_pm_init(pci_bus, (FULONG2E_VIA_SLOT << 3) + 4,
+ smbus = vt82c686b_pm_init(pci_bus, PCI_DEVFN(FULONG2E_VIA_SLOT, 4),
0xeee1, NULL);
eeprom_buf = qemu_mallocz(8 * 256); /* XXX: make this persistent */
memcpy(eeprom_buf, eeprom_spd, sizeof(eeprom_spd));
@@ -376,9 +376,9 @@ static void mips_fulong2e_init(ram_addr_t ram_size, const char *boot_device,
DMA_init(0, cpu_exit_irq);
/* Super I/O */
- isa_dev = isa_create_simple("i8042");
+ isa_create_simple("i8042");
- rtc_state = rtc_init(2000, NULL);
+ rtc_init(2000, NULL);
for(i = 0; i < MAX_SERIAL_PORTS; i++) {
if (serial_hds[i]) {
diff --git a/hw/mips_int.c b/hw/mips_int.c
index c30954caaf..477f6abf95 100644
--- a/hw/mips_int.c
+++ b/hw/mips_int.c
@@ -24,22 +24,6 @@
#include "mips_cpudevs.h"
#include "cpu.h"
-/* Raise IRQ to CPU if necessary. It must be called every time the active
- IRQ may change */
-void cpu_mips_update_irq(CPUState *env)
-{
- if ((env->CP0_Status & (1 << CP0St_IE)) &&
- !(env->CP0_Status & (1 << CP0St_EXL)) &&
- !(env->CP0_Status & (1 << CP0St_ERL)) &&
- !(env->hflags & MIPS_HFLAG_DM)) {
- if ((env->CP0_Status & env->CP0_Cause & CP0Ca_IP_mask) &&
- !(env->interrupt_request & CPU_INTERRUPT_HARD)) {
- cpu_interrupt(env, CPU_INTERRUPT_HARD);
- }
- } else
- cpu_reset_interrupt(env, CPU_INTERRUPT_HARD);
-}
-
static void cpu_mips_irq_request(void *opaque, int irq, int level)
{
CPUState *env = (CPUState *)opaque;
@@ -52,7 +36,12 @@ static void cpu_mips_irq_request(void *opaque, int irq, int level)
} else {
env->CP0_Cause &= ~(1 << (irq + CP0Ca_IP));
}
- cpu_mips_update_irq(env);
+
+ if (env->CP0_Cause & CP0Ca_IP_mask) {
+ cpu_interrupt(env, CPU_INTERRUPT_HARD);
+ } else {
+ cpu_reset_interrupt(env, CPU_INTERRUPT_HARD);
+ }
}
void cpu_mips_irq_init_cpu(CPUState *env)
@@ -65,3 +54,12 @@ void cpu_mips_irq_init_cpu(CPUState *env)
env->irq[i] = qi[i];
}
}
+
+void cpu_mips_soft_irq(CPUState *env, int irq, int level)
+{
+ if (irq < 0 || irq > 2) {
+ return;
+ }
+
+ qemu_set_irq(env->irq[irq], level);
+}
diff --git a/hw/mips_jazz.c b/hw/mips_jazz.c
index 71b05a203c..66397c0c9a 100644
--- a/hw/mips_jazz.c
+++ b/hw/mips_jazz.c
@@ -36,6 +36,7 @@
#include "mips-bios.h"
#include "loader.h"
#include "mc146818rtc.h"
+#include "blockdev.h"
enum jazz_model_e
{
@@ -136,7 +137,7 @@ void mips_jazz_init (ram_addr_t ram_size,
NICInfo *nd;
PITState *pit;
DriveInfo *fds[MAX_FD];
- qemu_irq esp_reset;
+ qemu_irq esp_reset, dma_enable;
qemu_irq *cpu_exit_irq;
ram_addr_t ram_offset;
ram_addr_t bios_offset;
@@ -244,7 +245,7 @@ void mips_jazz_init (ram_addr_t ram_size,
/* SCSI adapter */
esp_init(0x80002000, 0,
rc4030_dma_read, rc4030_dma_write, dmas[0],
- rc4030[5], &esp_reset);
+ rc4030[5], &esp_reset, &dma_enable);
/* Floppy */
if (drive_get_max_bus(IF_FLOPPY) >= MAX_FD) {
diff --git a/hw/mips_malta.c b/hw/mips_malta.c
index 11e220a944..80260714ec 100644
--- a/hw/mips_malta.c
+++ b/hw/mips_malta.c
@@ -45,6 +45,7 @@
#include "loader.h"
#include "elf.h"
#include "mc146818rtc.h"
+#include "blockdev.h"
//#define DEBUG_BOARD_INIT
@@ -653,7 +654,8 @@ static void write_bootloader (CPUState *env, uint8_t *base,
}
-static void prom_set(uint32_t* prom_buf, int index, const char *string, ...)
+static void GCC_FMT_ATTR(3, 4) prom_set(uint32_t* prom_buf, int index,
+ const char *string, ...)
{
va_list ap;
int32_t table_addr;
@@ -727,13 +729,13 @@ static int64_t load_kernel (void)
prom_size = ENVP_NB_ENTRIES * (sizeof(int32_t) + ENVP_ENTRY_SIZE);
prom_buf = qemu_malloc(prom_size);
- prom_set(prom_buf, prom_index++, loaderparams.kernel_filename);
+ prom_set(prom_buf, prom_index++, "%s", loaderparams.kernel_filename);
if (initrd_size > 0) {
prom_set(prom_buf, prom_index++, "rd_start=0x%" PRIx64 " rd_size=%li %s",
cpu_mips_phys_to_kseg0(NULL, initrd_offset), initrd_size,
loaderparams.kernel_cmdline);
} else {
- prom_set(prom_buf, prom_index++, loaderparams.kernel_cmdline);
+ prom_set(prom_buf, prom_index++, "%s", loaderparams.kernel_cmdline);
}
prom_set(prom_buf, prom_index++, "memsize");
@@ -782,11 +784,7 @@ void mips_malta_init (ram_addr_t ram_size,
target_long bios_size;
int64_t kernel_entry;
PCIBus *pci_bus;
- ISADevice *isa_dev;
CPUState *env;
- ISADevice *rtc_state;
- FDCtrl *floppy_controller;
- MaltaFPGAState *malta_fpga;
qemu_irq *i8259;
qemu_irq *cpu_exit_irq;
int piix4_devfn;
@@ -849,7 +847,7 @@ void mips_malta_init (ram_addr_t ram_size,
be = 0;
#endif
/* FPGA */
- malta_fpga = malta_fpga_init(0x1f000000LL, env->irq[2], serial_hds[2]);
+ malta_fpga_init(0x1f000000LL, env->irq[2], serial_hds[2]);
/* Load firmware in flash / BIOS unless we boot directly into a kernel. */
if (kernel_filename) {
@@ -955,9 +953,9 @@ void mips_malta_init (ram_addr_t ram_size,
DMA_init(0, cpu_exit_irq);
/* Super I/O */
- isa_dev = isa_create_simple("i8042");
-
- rtc_state = rtc_init(2000, NULL);
+ isa_create_simple("i8042");
+
+ rtc_init(2000, NULL);
serial_isa_init(0, serial_hds[0]);
serial_isa_init(1, serial_hds[1]);
if (parallel_hds[0])
@@ -965,7 +963,7 @@ void mips_malta_init (ram_addr_t ram_size,
for(i = 0; i < MAX_FD; i++) {
fd[i] = drive_get(IF_FLOPPY, 0, i);
}
- floppy_controller = fdctrl_init_isa(fd);
+ fdctrl_init_isa(fd);
/* Sound card */
audio_init(pci_bus);
diff --git a/hw/mips_r4k.c b/hw/mips_r4k.c
index 61cd33a93c..aa348904ad 100644
--- a/hw/mips_r4k.c
+++ b/hw/mips_r4k.c
@@ -22,6 +22,7 @@
#include "loader.h"
#include "elf.h"
#include "mc146818rtc.h"
+#include "blockdev.h"
#define MAX_IDE_BUS 2
@@ -166,7 +167,6 @@ void mips_r4k_init (ram_addr_t ram_size,
int bios_size;
CPUState *env;
ResetData *reset_info;
- ISADevice *rtc_state;
int i;
qemu_irq *i8259;
DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS];
@@ -267,7 +267,7 @@ void mips_r4k_init (ram_addr_t ram_size,
isa_bus_new(NULL);
isa_bus_irqs(i8259);
- rtc_state = rtc_init(2000, NULL);
+ rtc_init(2000, NULL);
/* Register 64 KB of ISA IO space at 0x14000000 */
#ifdef TARGET_WORDS_BIGENDIAN
diff --git a/hw/mipsnet.c b/hw/mipsnet.c
index a95b3ce07b..c5e54ffc35 100644
--- a/hw/mipsnet.c
+++ b/hw/mipsnet.c
@@ -81,7 +81,7 @@ static ssize_t mipsnet_receive(VLANClientState *nc, const uint8_t *buf, size_t s
MIPSnetState *s = DO_UPCAST(NICState, nc, nc)->opaque;
#ifdef DEBUG_MIPSNET_RECEIVE
- printf("mipsnet: receiving len=%d\n", size);
+ printf("mipsnet: receiving len=%zu\n", size);
#endif
if (!mipsnet_can_receive(nc))
return -1;
diff --git a/hw/msix.c b/hw/msix.c
index b202ff7d85..f66d2550a7 100644
--- a/hw/msix.c
+++ b/hw/msix.c
@@ -14,6 +14,7 @@
#include "hw.h"
#include "msix.h"
#include "pci.h"
+#include "range.h"
/* MSI-X capability structure */
#define MSIX_TABLE_OFFSET 4
diff --git a/hw/multiboot.c b/hw/multiboot.c
index dc980e6498..f9097a2f60 100644
--- a/hw/multiboot.c
+++ b/hw/multiboot.c
@@ -252,7 +252,7 @@ int load_multiboot(void *fw_cfg,
do {
char *next_space;
- uint32_t mb_mod_length;
+ int mb_mod_length;
uint32_t offs = mbs.mb_buf_size;
next_initrd = strchr(initrd_filename, ',');
diff --git a/hw/musicpal.c b/hw/musicpal.c
index 33180a2656..56f27669d2 100644
--- a/hw/musicpal.c
+++ b/hw/musicpal.c
@@ -18,6 +18,7 @@
#include "flash.h"
#include "console.h"
#include "i2c.h"
+#include "blockdev.h"
#define MP_MISC_BASE 0x80002000
#define MP_MISC_SIZE 0x00001000
diff --git a/hw/omap.h b/hw/omap.h
index 18eb72b82f..fe32ca5c4c 100644
--- a/hw/omap.h
+++ b/hw/omap.h
@@ -664,10 +664,12 @@ void omap_synctimer_reset(struct omap_synctimer_s *s);
struct omap_uart_s;
struct omap_uart_s *omap_uart_init(target_phys_addr_t base,
qemu_irq irq, omap_clk fclk, omap_clk iclk,
- qemu_irq txdma, qemu_irq rxdma, CharDriverState *chr);
+ qemu_irq txdma, qemu_irq rxdma,
+ const char *label, CharDriverState *chr);
struct omap_uart_s *omap2_uart_init(struct omap_target_agent_s *ta,
qemu_irq irq, omap_clk fclk, omap_clk iclk,
- qemu_irq txdma, qemu_irq rxdma, CharDriverState *chr);
+ qemu_irq txdma, qemu_irq rxdma,
+ const char *label, CharDriverState *chr);
void omap_uart_reset(struct omap_uart_s *s);
void omap_uart_attach(struct omap_uart_s *s, CharDriverState *chr);
diff --git a/hw/omap1.c b/hw/omap1.c
index cf0d428692..f4966f74b6 100644
--- a/hw/omap1.c
+++ b/hw/omap1.c
@@ -25,6 +25,8 @@
#include "soc_dma.h"
/* We use pc-style serial ports. */
#include "pc.h"
+#include "blockdev.h"
+#include "range.h"
/* Should signal the TCMI/GPMC */
uint32_t omap_badwidth_read8(void *opaque, target_phys_addr_t addr)
@@ -3668,37 +3670,38 @@ static const struct dma_irq_map omap1_dma_irq_map[] = {
static int omap_validate_emiff_addr(struct omap_mpu_state_s *s,
target_phys_addr_t addr)
{
- return addr >= OMAP_EMIFF_BASE && addr < OMAP_EMIFF_BASE + s->sdram_size;
+ return range_covers_byte(OMAP_EMIFF_BASE, s->sdram_size, addr);
}
static int omap_validate_emifs_addr(struct omap_mpu_state_s *s,
target_phys_addr_t addr)
{
- return addr >= OMAP_EMIFS_BASE && addr < OMAP_EMIFF_BASE;
+ return range_covers_byte(OMAP_EMIFS_BASE, OMAP_EMIFF_BASE - OMAP_EMIFS_BASE,
+ addr);
}
static int omap_validate_imif_addr(struct omap_mpu_state_s *s,
target_phys_addr_t addr)
{
- return addr >= OMAP_IMIF_BASE && addr < OMAP_IMIF_BASE + s->sram_size;
+ return range_covers_byte(OMAP_IMIF_BASE, s->sram_size, addr);
}
static int omap_validate_tipb_addr(struct omap_mpu_state_s *s,
target_phys_addr_t addr)
{
- return addr >= 0xfffb0000 && addr < 0xffff0000;
+ return range_covers_byte(0xfffb0000, 0xffff0000 - 0xfffb0000, addr);
}
static int omap_validate_local_addr(struct omap_mpu_state_s *s,
target_phys_addr_t addr)
{
- return addr >= OMAP_LOCALBUS_BASE && addr < OMAP_LOCALBUS_BASE + 0x1000000;
+ return range_covers_byte(OMAP_LOCALBUS_BASE, 0x1000000, addr);
}
static int omap_validate_tipb_mpui_addr(struct omap_mpu_state_s *s,
target_phys_addr_t addr)
{
- return addr >= 0xe1010000 && addr < 0xe1020004;
+ return range_covers_byte(0xe1010000, 0xe1020004 - 0xe1010000, addr);
}
struct omap_mpu_state_s *omap310_mpu_init(unsigned long sdram_size,
@@ -3808,16 +3811,19 @@ struct omap_mpu_state_s *omap310_mpu_init(unsigned long sdram_size,
omap_findclk(s, "uart1_ck"),
omap_findclk(s, "uart1_ck"),
s->drq[OMAP_DMA_UART1_TX], s->drq[OMAP_DMA_UART1_RX],
+ "uart1",
serial_hds[0]);
s->uart[1] = omap_uart_init(0xfffb0800, s->irq[1][OMAP_INT_UART2],
omap_findclk(s, "uart2_ck"),
omap_findclk(s, "uart2_ck"),
s->drq[OMAP_DMA_UART2_TX], s->drq[OMAP_DMA_UART2_RX],
+ "uart2",
serial_hds[0] ? serial_hds[1] : NULL);
s->uart[2] = omap_uart_init(0xfffb9800, s->irq[0][OMAP_INT_UART3],
omap_findclk(s, "uart3_ck"),
omap_findclk(s, "uart3_ck"),
s->drq[OMAP_DMA_UART3_TX], s->drq[OMAP_DMA_UART3_RX],
+ "uart3",
serial_hds[0] && serial_hds[1] ? serial_hds[2] : NULL);
omap_dpll_init(&s->dpll[0], 0xfffecf00, omap_findclk(s, "dpll1"));
diff --git a/hw/omap2.c b/hw/omap2.c
index 179075e996..e35a56e043 100644
--- a/hw/omap2.c
+++ b/hw/omap2.c
@@ -2291,13 +2291,16 @@ struct omap_mpu_state_s *omap2420_mpu_init(unsigned long sdram_size,
omap_findclk(s, "uart1_fclk"),
omap_findclk(s, "uart1_iclk"),
s->drq[OMAP24XX_DMA_UART1_TX],
- s->drq[OMAP24XX_DMA_UART1_RX], serial_hds[0]);
+ s->drq[OMAP24XX_DMA_UART1_RX],
+ "uart1",
+ serial_hds[0]);
s->uart[1] = omap2_uart_init(omap_l4ta(s->l4, 20),
s->irq[0][OMAP_INT_24XX_UART2_IRQ],
omap_findclk(s, "uart2_fclk"),
omap_findclk(s, "uart2_iclk"),
s->drq[OMAP24XX_DMA_UART2_TX],
s->drq[OMAP24XX_DMA_UART2_RX],
+ "uart2",
serial_hds[0] ? serial_hds[1] : NULL);
s->uart[2] = omap2_uart_init(omap_l4ta(s->l4, 21),
s->irq[0][OMAP_INT_24XX_UART3_IRQ],
@@ -2305,6 +2308,7 @@ struct omap_mpu_state_s *omap2420_mpu_init(unsigned long sdram_size,
omap_findclk(s, "uart3_iclk"),
s->drq[OMAP24XX_DMA_UART3_TX],
s->drq[OMAP24XX_DMA_UART3_RX],
+ "uart3",
serial_hds[0] && serial_hds[1] ? serial_hds[2] : NULL);
s->gptimer[0] = omap_gp_timer_init(omap_l4ta(s->l4, 7),
diff --git a/hw/omap_clk.c b/hw/omap_clk.c
index 6bcabef8ac..10c9c4308c 100644
--- a/hw/omap_clk.c
+++ b/hw/omap_clk.c
@@ -20,6 +20,7 @@
*/
#include "hw.h"
#include "omap.h"
+#include "qemu-timer.h" /* for muldiv64() */
struct clk {
const char *name;
diff --git a/hw/omap_i2c.c b/hw/omap_i2c.c
index d7c18882da..d133977e7f 100644
--- a/hw/omap_i2c.c
+++ b/hw/omap_i2c.c
@@ -190,8 +190,9 @@ static uint32_t omap_i2c_read(void *opaque, target_phys_addr_t addr)
if (s->rxlen > 2)
s->fifo >>= 16;
s->rxlen -= 2;
- } else
- /* XXX: remote access (qualifier) error - what's that? */;
+ } else {
+ /* XXX: remote access (qualifier) error - what's that? */
+ }
if (!s->rxlen) {
s->stat &= ~(1 << 3); /* RRDY */
if (((s->control >> 10) & 1) && /* MST */
diff --git a/hw/omap_mmc.c b/hw/omap_mmc.c
index 15cbf06c87..9d167ff535 100644
--- a/hw/omap_mmc.c
+++ b/hw/omap_mmc.c
@@ -559,8 +559,9 @@ static void omap_mmc_cover_cb(void *opaque, int line, int level)
if (!host->cdet_state && level) {
host->status |= 0x0002;
omap_mmc_interrupts_update(host);
- if (host->cdet_wakeup)
- /* TODO: Assert wake-up */;
+ if (host->cdet_wakeup) {
+ /* TODO: Assert wake-up */
+ }
}
if (host->cdet_state != level) {
diff --git a/hw/omap_sx1.c b/hw/omap_sx1.c
index c3f197393d..44dc514f3f 100644
--- a/hw/omap_sx1.c
+++ b/hw/omap_sx1.c
@@ -32,6 +32,7 @@
#include "boards.h"
#include "arm-misc.h"
#include "flash.h"
+#include "blockdev.h"
/*****************************************************************************/
/* Siemens SX1 Cellphone V1 */
diff --git a/hw/omap_uart.c b/hw/omap_uart.c
index 395bf0ccbb..cc66cd9d94 100644
--- a/hw/omap_uart.c
+++ b/hw/omap_uart.c
@@ -51,7 +51,8 @@ void omap_uart_reset(struct omap_uart_s *s)
struct omap_uart_s *omap_uart_init(target_phys_addr_t base,
qemu_irq irq, omap_clk fclk, omap_clk iclk,
- qemu_irq txdma, qemu_irq rxdma, CharDriverState *chr)
+ qemu_irq txdma, qemu_irq rxdma,
+ const char *label, CharDriverState *chr)
{
struct omap_uart_s *s = (struct omap_uart_s *)
qemu_mallocz(sizeof(struct omap_uart_s));
@@ -61,11 +62,11 @@ struct omap_uart_s *omap_uart_init(target_phys_addr_t base,
s->irq = irq;
#ifdef TARGET_WORDS_BIGENDIAN
s->serial = serial_mm_init(base, 2, irq, omap_clk_getrate(fclk)/16,
- chr ?: qemu_chr_open("null", "null", NULL), 1,
+ chr ?: qemu_chr_open(label, "null", NULL), 1,
1);
#else
s->serial = serial_mm_init(base, 2, irq, omap_clk_getrate(fclk)/16,
- chr ?: qemu_chr_open("null", "null", NULL), 1,
+ chr ?: qemu_chr_open(label, "null", NULL), 1,
0);
#endif
return s;
@@ -162,11 +163,12 @@ static CPUWriteMemoryFunc * const omap_uart_writefn[] = {
struct omap_uart_s *omap2_uart_init(struct omap_target_agent_s *ta,
qemu_irq irq, omap_clk fclk, omap_clk iclk,
- qemu_irq txdma, qemu_irq rxdma, CharDriverState *chr)
+ qemu_irq txdma, qemu_irq rxdma,
+ const char *label, CharDriverState *chr)
{
target_phys_addr_t base = omap_l4_attach(ta, 0, 0);
struct omap_uart_s *s = omap_uart_init(base, irq,
- fclk, iclk, txdma, rxdma, chr);
+ fclk, iclk, txdma, rxdma, label, chr);
int iomemtype = cpu_register_io_memory(omap_uart_readfn,
omap_uart_writefn, s);
diff --git a/hw/pc.c b/hw/pc.c
index a96187f5b5..69b13bf62c 100644
--- a/hw/pc.c
+++ b/hw/pc.c
@@ -39,6 +39,7 @@
#include "msix.h"
#include "sysbus.h"
#include "sysemu.h"
+#include "blockdev.h"
/* output Bochs bios info messages */
//#define DEBUG_BIOS
@@ -916,8 +917,10 @@ void pc_memory_init(ram_addr_t ram_size,
below_4g_mem_size - 0x100000,
ram_addr + 0x100000);
#if TARGET_PHYS_ADDR_BITS > 32
- cpu_register_physical_memory(0x100000000ULL, above_4g_mem_size,
- ram_addr + below_4g_mem_size);
+ if (above_4g_mem_size > 0) {
+ cpu_register_physical_memory(0x100000000ULL, above_4g_mem_size,
+ ram_addr + below_4g_mem_size);
+ }
#endif
/* BIOS load */
diff --git a/hw/pc_piix.c b/hw/pc_piix.c
index 519e8a5ccb..12359a75c9 100644
--- a/hw/pc_piix.c
+++ b/hw/pc_piix.c
@@ -34,6 +34,7 @@
#include "kvm.h"
#include "sysemu.h"
#include "sysbus.h"
+#include "blockdev.h"
#define MAX_IDE_BUS 2
@@ -103,6 +104,7 @@ static void pc_init1(ram_addr_t ram_size,
pci_bus = i440fx_init(&i440fx_state, &piix3_devfn, isa_irq, ram_size);
} else {
pci_bus = NULL;
+ i440fx_state = NULL;
isa_bus_new(NULL);
}
isa_bus_irqs(isa_irq);
@@ -226,7 +228,7 @@ static QEMUMachine pc_machine_v0_12 = {
.compat_props = (GlobalProperty[]) {
{
.driver = "virtio-serial-pci",
- .property = "max_nr_ports",
+ .property = "max_ports",
.value = stringify(1),
},{
.driver = "virtio-serial-pci",
@@ -249,7 +251,7 @@ static QEMUMachine pc_machine_v0_11 = {
.value = stringify(0),
},{
.driver = "virtio-serial-pci",
- .property = "max_nr_ports",
+ .property = "max_ports",
.value = stringify(1),
},{
.driver = "virtio-serial-pci",
@@ -288,7 +290,7 @@ static QEMUMachine pc_machine_v0_10 = {
.value = stringify(PCI_CLASS_DISPLAY_OTHER),
},{
.driver = "virtio-serial-pci",
- .property = "max_nr_ports",
+ .property = "max_ports",
.value = stringify(1),
},{
.driver = "virtio-serial-pci",
diff --git a/hw/pci-hotplug.c b/hw/pci-hotplug.c
index c38f47fbf1..716133c376 100644
--- a/hw/pci-hotplug.c
+++ b/hw/pci-hotplug.c
@@ -31,6 +31,7 @@
#include "scsi.h"
#include "virtio-blk.h"
#include "qemu-config.h"
+#include "blockdev.h"
#if defined(TARGET_I386)
static PCIDevice *qemu_pci_hot_add_nic(Monitor *mon,
@@ -51,7 +52,7 @@ static PCIDevice *qemu_pci_hot_add_nic(Monitor *mon,
return NULL;
}
- opts = qemu_opts_parse(&qemu_net_opts, opts_str ? opts_str : "", 0);
+ opts = qemu_opts_parse(qemu_find_opts("net"), opts_str ? opts_str : "", 0);
if (!opts) {
return NULL;
}
diff --git a/hw/pci.c b/hw/pci.c
index 5386f5a083..962886e767 100644
--- a/hw/pci.c
+++ b/hw/pci.c
@@ -32,6 +32,7 @@
#include "sysemu.h"
#include "loader.h"
#include "qemu-objects.h"
+#include "range.h"
//#define DEBUG_PCI
#ifdef DEBUG_PCI
@@ -585,7 +586,7 @@ static int pci_init_multifunction(PCIBus *bus, PCIDevice *dev)
}
/*
- * multifuction bit is interpreted in two ways as follows.
+ * multifunction bit is interpreted in two ways as follows.
* - all functions must set the bit to 1.
* Example: Intel X53
* - function 0 must set the bit, but the rest function (> 0)
diff --git a/hw/pci.h b/hw/pci.h
index d6c522b8dd..7100804e7c 100644
--- a/hw/pci.h
+++ b/hw/pci.h
@@ -448,33 +448,4 @@ static inline uint32_t pci_config_size(const PCIDevice *d)
return pci_is_express(d) ? PCIE_CONFIG_SPACE_SIZE : PCI_CONFIG_SPACE_SIZE;
}
-/* These are not pci specific. Should move into a separate header.
- * Only pci.c uses them, so keep them here for now.
- */
-
-/* Get last byte of a range from offset + length.
- * Undefined for ranges that wrap around 0. */
-static inline uint64_t range_get_last(uint64_t offset, uint64_t len)
-{
- return offset + len - 1;
-}
-
-/* Check whether a given range covers a given byte. */
-static inline int range_covers_byte(uint64_t offset, uint64_t len,
- uint64_t byte)
-{
- return offset <= byte && byte <= range_get_last(offset, len);
-}
-
-/* Check whether 2 given ranges overlap.
- * Undefined if ranges that wrap around 0. */
-static inline int ranges_overlap(uint64_t first1, uint64_t len1,
- uint64_t first2, uint64_t len2)
-{
- uint64_t last1 = range_get_last(first1, len1);
- uint64_t last2 = range_get_last(first2, len2);
-
- return !(last2 < first1 || last1 < first2);
-}
-
#endif
diff --git a/hw/pckbd.c b/hw/pckbd.c
index 0533b1d9e3..6e4e4062ad 100644
--- a/hw/pckbd.c
+++ b/hw/pckbd.c
@@ -56,7 +56,9 @@
#define KBD_CCMD_WRITE_MOUSE 0xD4 /* Write the following byte to the mouse */
#define KBD_CCMD_DISABLE_A20 0xDD /* HP vectra only ? */
#define KBD_CCMD_ENABLE_A20 0xDF /* HP vectra only ? */
-#define KBD_CCMD_RESET 0xFE
+#define KBD_CCMD_PULSE_BITS_3_0 0xF0 /* Pulse bits 3-0 of the output port P2. */
+#define KBD_CCMD_RESET 0xFE /* Pulse bit 0 of the output port P2 = CPU reset. */
+#define KBD_CCMD_NO_OP 0xFF /* Pulse no bits of the output port P2. */
/* Keyboard Commands */
#define KBD_CMD_SET_LEDS 0xED /* Set keyboard leds */
@@ -238,6 +240,21 @@ static void kbd_write_command(void *opaque, uint32_t addr, uint32_t val)
KBDState *s = opaque;
DPRINTF("kbd: write cmd=0x%02x\n", val);
+
+ /* Bits 3-0 of the output port P2 of the keyboard controller may be pulsed
+ * low for approximately 6 micro seconds. Bits 3-0 of the KBD_CCMD_PULSE
+ * command specify the output port bits to be pulsed.
+ * 0: Bit should be pulsed. 1: Bit should not be modified.
+ * The only useful version of this command is pulsing bit 0,
+ * which does a CPU reset.
+ */
+ if((val & KBD_CCMD_PULSE_BITS_3_0) == KBD_CCMD_PULSE_BITS_3_0) {
+ if(!(val & 1))
+ val = KBD_CCMD_RESET;
+ else
+ val = KBD_CCMD_NO_OP;
+ }
+
switch(val) {
case KBD_CCMD_READ_MODE:
kbd_queue(s, s->mode, 0);
@@ -294,8 +311,8 @@ static void kbd_write_command(void *opaque, uint32_t addr, uint32_t val)
case KBD_CCMD_RESET:
qemu_system_reset_request();
break;
- case 0xff:
- /* ignore that - I don't know what is its use */
+ case KBD_CCMD_NO_OP:
+ /* ignore that */
break;
default:
fprintf(stderr, "qemu: unsupported keyboard cmd=0x%02x\n", val);
diff --git a/hw/pcmcia.h b/hw/pcmcia.h
index 360292395b..50648c973f 100644
--- a/hw/pcmcia.h
+++ b/hw/pcmcia.h
@@ -1,7 +1,6 @@
/* PCMCIA/Cardbus */
#include "qemu-common.h"
-#include "blockdev.h"
typedef struct {
qemu_irq irq;
diff --git a/hw/petalogix_s3adsp1800_mmu.c b/hw/petalogix_s3adsp1800_mmu.c
index 70b6a36e1e..42de45963b 100644
--- a/hw/petalogix_s3adsp1800_mmu.c
+++ b/hw/petalogix_s3adsp1800_mmu.c
@@ -34,6 +34,7 @@
#include "xilinx.h"
#include "loader.h"
#include "elf.h"
+#include "blockdev.h"
#define LMB_BRAM_SIZE (128 * 1024)
#define FLASH_SIZE (16 * 1024 * 1024)
@@ -179,11 +180,22 @@ petalogix_s3adsp1800_init(ram_addr_t ram_size,
}
/* Always boot into physical ram. */
boot_info.bootstrap_pc = ddr_base + (entry & 0x0fffffff);
+
+ /* If it wasn't an ELF image, try an u-boot image. */
+ if (kernel_size < 0) {
+ target_phys_addr_t uentry, loadaddr;
+
+ kernel_size = load_uimage(kernel_filename, &uentry, &loadaddr, 0);
+ boot_info.bootstrap_pc = uentry;
+ high = (loadaddr + kernel_size + 3) & ~3;
+ }
+
+ /* Not an ELF image nor an u-boot image, try a RAW image. */
if (kernel_size < 0) {
- /* If we failed loading ELF's try a raw image. */
kernel_size = load_image_targphys(kernel_filename, ddr_base,
ram_size);
boot_info.bootstrap_pc = ddr_base;
+ high = (ddr_base + kernel_size + 3) & ~3;
}
boot_info.cmdline = high + 4096;
diff --git a/hw/piix_pci.c b/hw/piix_pci.c
index f152a0ff06..b5589b9035 100644
--- a/hw/piix_pci.c
+++ b/hw/piix_pci.c
@@ -28,6 +28,7 @@
#include "pci_host.h"
#include "isa.h"
#include "sysbus.h"
+#include "range.h"
/*
* I440FX chipset data sheet.
diff --git a/hw/ppc.c b/hw/ppc.c
index 2a77eb9bff..968aec1b16 100644
--- a/hw/ppc.c
+++ b/hw/ppc.c
@@ -28,6 +28,8 @@
#include "nvram.h"
#include "qemu-log.h"
#include "loader.h"
+#include "kvm.h"
+#include "kvm_ppc.h"
//#define PPC_DEBUG_IRQ
//#define PPC_DEBUG_TB
@@ -50,6 +52,8 @@ static void cpu_ppc_tb_start (CPUState *env);
static void ppc_set_irq (CPUState *env, int n_IRQ, int level)
{
+ unsigned int old_pending = env->pending_interrupts;
+
if (level) {
env->pending_interrupts |= 1 << n_IRQ;
cpu_interrupt(env, CPU_INTERRUPT_HARD);
@@ -58,6 +62,13 @@ static void ppc_set_irq (CPUState *env, int n_IRQ, int level)
if (env->pending_interrupts == 0)
cpu_reset_interrupt(env, CPU_INTERRUPT_HARD);
}
+
+ if (old_pending != env->pending_interrupts) {
+#ifdef CONFIG_KVM
+ kvmppc_set_interrupt(env, n_IRQ, level);
+#endif
+ }
+
LOG_IRQ("%s: %p n_IRQ %d level %d => pending %08" PRIx32
"req %08x\n", __func__, env, n_IRQ, level,
env->pending_interrupts, env->interrupt_request);
@@ -758,6 +769,9 @@ struct ppcemb_timer_t {
struct QEMUTimer *fit_timer;
uint64_t wdt_next; /* Tick for next WDT interrupt */
struct QEMUTimer *wdt_timer;
+
+ /* 405 have the PIT, 440 have a DECR. */
+ unsigned int decr_excp;
};
/* Fixed interval timer */
@@ -840,7 +854,7 @@ static void cpu_4xx_pit_cb (void *opaque)
ppcemb_timer = tb_env->opaque;
env->spr[SPR_40x_TSR] |= 1 << 27;
if ((env->spr[SPR_40x_TCR] >> 26) & 0x1)
- ppc_set_irq(env, PPC_INTERRUPT_PIT, 1);
+ ppc_set_irq(env, ppcemb_timer->decr_excp, 1);
start_stop_pit(env, tb_env, 1);
LOG_TB("%s: ar %d ir %d TCR " TARGET_FMT_lx " TSR " TARGET_FMT_lx " "
"%016" PRIx64 "\n", __func__,
@@ -937,10 +951,15 @@ target_ulong load_40x_pit (CPUState *env)
void store_booke_tsr (CPUState *env, target_ulong val)
{
+ ppc_tb_t *tb_env = env->tb_env;
+ ppcemb_timer_t *ppcemb_timer;
+
+ ppcemb_timer = tb_env->opaque;
+
LOG_TB("%s: val " TARGET_FMT_lx "\n", __func__, val);
env->spr[SPR_40x_TSR] &= ~(val & 0xFC000000);
if (val & 0x80000000)
- ppc_set_irq(env, PPC_INTERRUPT_PIT, 0);
+ ppc_set_irq(env, ppcemb_timer->decr_excp, 0);
}
void store_booke_tcr (CPUState *env, target_ulong val)
@@ -966,7 +985,8 @@ static void ppc_emb_set_tb_clk (void *opaque, uint32_t freq)
/* XXX: we should also update all timers */
}
-clk_setup_cb ppc_emb_timers_init (CPUState *env, uint32_t freq)
+clk_setup_cb ppc_emb_timers_init (CPUState *env, uint32_t freq,
+ unsigned int decr_excp)
{
ppc_tb_t *tb_env;
ppcemb_timer_t *ppcemb_timer;
@@ -985,6 +1005,7 @@ clk_setup_cb ppc_emb_timers_init (CPUState *env, uint32_t freq)
qemu_new_timer(vm_clock, &cpu_4xx_fit_cb, env);
ppcemb_timer->wdt_timer =
qemu_new_timer(vm_clock, &cpu_4xx_wdt_cb, env);
+ ppcemb_timer->decr_excp = decr_excp;
}
return &ppc_emb_set_tb_clk;
diff --git a/hw/ppc.h b/hw/ppc.h
index de13092ae4..34f54cf5da 100644
--- a/hw/ppc.h
+++ b/hw/ppc.h
@@ -19,7 +19,9 @@ int ppc_dcr_init (CPUState *env, int (*dcr_read_error)(int dcrn),
int (*dcr_write_error)(int dcrn));
int ppc_dcr_register (CPUState *env, int dcrn, void *opaque,
dcr_read_cb drc_read, dcr_write_cb dcr_write);
-clk_setup_cb ppc_emb_timers_init (CPUState *env, uint32_t freq);
+clk_setup_cb ppc_emb_timers_init (CPUState *env, uint32_t freq,
+ unsigned int decr_excp);
+
/* Embedded PowerPC reset */
void ppc40x_core_reset (CPUState *env);
void ppc40x_chip_reset (CPUState *env);
@@ -47,5 +49,8 @@ enum {
#define FW_CFG_PPC_HEIGHT (FW_CFG_ARCH_LOCAL + 0x01)
#define FW_CFG_PPC_DEPTH (FW_CFG_ARCH_LOCAL + 0x02)
#define FW_CFG_PPC_TBFREQ (FW_CFG_ARCH_LOCAL + 0x03)
+#define FW_CFG_PPC_IS_KVM (FW_CFG_ARCH_LOCAL + 0x05)
+#define FW_CFG_PPC_KVM_HC (FW_CFG_ARCH_LOCAL + 0x06)
+#define FW_CFG_PPC_KVM_PID (FW_CFG_ARCH_LOCAL + 0x07)
#define PPC_SERIAL_MM_BAUDBASE 399193
diff --git a/hw/ppc405_boards.c b/hw/ppc405_boards.c
index 40ff1b34ea..c5897a9d4d 100644
--- a/hw/ppc405_boards.c
+++ b/hw/ppc405_boards.c
@@ -31,6 +31,7 @@
#include "boards.h"
#include "qemu-log.h"
#include "loader.h"
+#include "blockdev.h"
#define BIOS_FILENAME "ppc405_rom.bin"
#define BIOS_SIZE (2048 * 1024)
@@ -181,10 +182,12 @@ static void ref405ep_init (ram_addr_t ram_size,
qemu_irq *pic;
ram_addr_t sram_offset, bios_offset, bdloc;
target_phys_addr_t ram_bases[2], ram_sizes[2];
- target_ulong sram_size, bios_size;
+ target_ulong sram_size;
+ long bios_size;
//int phy_addr = 0;
//static int phy_addr = 1;
- target_ulong kernel_base, kernel_size, initrd_base, initrd_size;
+ target_ulong kernel_base, initrd_base;
+ long kernel_size, initrd_size;
int linux_boot;
int fl_idx, fl_sectors, len;
DriveInfo *dinfo;
@@ -220,8 +223,8 @@ static void ref405ep_init (ram_addr_t ram_size,
bios_offset = qemu_ram_alloc(NULL, "ef405ep.bios", bios_size);
fl_sectors = (bios_size + 65535) >> 16;
#ifdef DEBUG_BOARD_INIT
- printf("Register parallel flash %d size " TARGET_FMT_lx
- " at offset %08lx addr " TARGET_FMT_lx " '%s' %d\n",
+ printf("Register parallel flash %d size %lx"
+ " at offset %08lx addr %lx '%s' %d\n",
fl_idx, bios_size, bios_offset, -bios_size,
bdrv_get_device_name(dinfo->bdrv), fl_sectors);
#endif
@@ -307,7 +310,7 @@ static void ref405ep_init (ram_addr_t ram_size,
kernel_filename);
exit(1);
}
- printf("Load kernel size " TARGET_FMT_ld " at " TARGET_FMT_lx,
+ printf("Load kernel size %ld at " TARGET_FMT_lx,
kernel_size, kernel_base);
/* load initrd */
if (initrd_filename) {
@@ -498,12 +501,12 @@ static void taihu_405ep_init(ram_addr_t ram_size,
const char *cpu_model)
{
char *filename;
- CPUPPCState *env;
qemu_irq *pic;
ram_addr_t bios_offset;
target_phys_addr_t ram_bases[2], ram_sizes[2];
- target_ulong bios_size;
- target_ulong kernel_base, kernel_size, initrd_base, initrd_size;
+ long bios_size;
+ target_ulong kernel_base, initrd_base;
+ long kernel_size, initrd_size;
int linux_boot;
int fl_idx, fl_sectors;
DriveInfo *dinfo;
@@ -517,8 +520,8 @@ static void taihu_405ep_init(ram_addr_t ram_size,
#ifdef DEBUG_BOARD_INIT
printf("%s: register cpu\n", __func__);
#endif
- env = ppc405ep_init(ram_bases, ram_sizes, 33333333, &pic,
- kernel_filename == NULL ? 0 : 1);
+ ppc405ep_init(ram_bases, ram_sizes, 33333333, &pic,
+ kernel_filename == NULL ? 0 : 1);
/* allocate and load BIOS */
#ifdef DEBUG_BOARD_INIT
printf("%s: register BIOS\n", __func__);
@@ -533,8 +536,8 @@ static void taihu_405ep_init(ram_addr_t ram_size,
fl_sectors = (bios_size + 65535) >> 16;
bios_offset = qemu_ram_alloc(NULL, "taihu_405ep.bios", bios_size);
#ifdef DEBUG_BOARD_INIT
- printf("Register parallel flash %d size " TARGET_FMT_lx
- " at offset %08lx addr " TARGET_FMT_lx " '%s' %d\n",
+ printf("Register parallel flash %d size %lx"
+ " at offset %08lx addr %lx '%s' %d\n",
fl_idx, bios_size, bios_offset, -bios_size,
bdrv_get_device_name(dinfo->bdrv), fl_sectors);
#endif
@@ -575,7 +578,7 @@ static void taihu_405ep_init(ram_addr_t ram_size,
bios_size = 32 * 1024 * 1024;
fl_sectors = (bios_size + 65535) >> 16;
#ifdef DEBUG_BOARD_INIT
- printf("Register parallel flash %d size " TARGET_FMT_lx
+ printf("Register parallel flash %d size %lx"
" at offset %08lx addr " TARGET_FMT_lx " '%s'\n",
fl_idx, bios_size, bios_offset, (target_ulong)0xfc000000,
bdrv_get_device_name(dinfo->bdrv));
diff --git a/hw/ppc405_uc.c b/hw/ppc405_uc.c
index b884ea5fbd..3600737412 100644
--- a/hw/ppc405_uc.c
+++ b/hw/ppc405_uc.c
@@ -630,18 +630,11 @@ struct ppc405_dma_t {
static uint32_t dcr_read_dma (void *opaque, int dcrn)
{
- ppc405_dma_t *dma;
-
- dma = opaque;
-
return 0;
}
static void dcr_write_dma (void *opaque, int dcrn, uint32_t val)
{
- ppc405_dma_t *dma;
-
- dma = opaque;
}
static void ppc405_dma_reset (void *opaque)
@@ -739,9 +732,6 @@ struct ppc405_gpio_t {
static uint32_t ppc405_gpio_readb (void *opaque, target_phys_addr_t addr)
{
- ppc405_gpio_t *gpio;
-
- gpio = opaque;
#ifdef DEBUG_GPIO
printf("%s: addr " TARGET_FMT_plx "\n", __func__, addr);
#endif
@@ -752,9 +742,6 @@ static uint32_t ppc405_gpio_readb (void *opaque, target_phys_addr_t addr)
static void ppc405_gpio_writeb (void *opaque,
target_phys_addr_t addr, uint32_t value)
{
- ppc405_gpio_t *gpio;
-
- gpio = opaque;
#ifdef DEBUG_GPIO
printf("%s: addr " TARGET_FMT_plx " val %08" PRIx32 "\n", __func__, addr,
value);
@@ -763,9 +750,6 @@ static void ppc405_gpio_writeb (void *opaque,
static uint32_t ppc405_gpio_readw (void *opaque, target_phys_addr_t addr)
{
- ppc405_gpio_t *gpio;
-
- gpio = opaque;
#ifdef DEBUG_GPIO
printf("%s: addr " TARGET_FMT_plx "\n", __func__, addr);
#endif
@@ -776,9 +760,6 @@ static uint32_t ppc405_gpio_readw (void *opaque, target_phys_addr_t addr)
static void ppc405_gpio_writew (void *opaque,
target_phys_addr_t addr, uint32_t value)
{
- ppc405_gpio_t *gpio;
-
- gpio = opaque;
#ifdef DEBUG_GPIO
printf("%s: addr " TARGET_FMT_plx " val %08" PRIx32 "\n", __func__, addr,
value);
@@ -787,9 +768,6 @@ static void ppc405_gpio_writew (void *opaque,
static uint32_t ppc405_gpio_readl (void *opaque, target_phys_addr_t addr)
{
- ppc405_gpio_t *gpio;
-
- gpio = opaque;
#ifdef DEBUG_GPIO
printf("%s: addr " TARGET_FMT_plx "\n", __func__, addr);
#endif
@@ -800,9 +778,6 @@ static uint32_t ppc405_gpio_readl (void *opaque, target_phys_addr_t addr)
static void ppc405_gpio_writel (void *opaque,
target_phys_addr_t addr, uint32_t value)
{
- ppc405_gpio_t *gpio;
-
- gpio = opaque;
#ifdef DEBUG_GPIO
printf("%s: addr " TARGET_FMT_plx " val %08" PRIx32 "\n", __func__, addr,
value);
@@ -823,9 +798,6 @@ static CPUWriteMemoryFunc * const ppc405_gpio_write[] = {
static void ppc405_gpio_reset (void *opaque)
{
- ppc405_gpio_t *gpio;
-
- gpio = opaque;
}
static void ppc405_gpio_init(target_phys_addr_t base)
diff --git a/hw/ppc440_bamboo.c b/hw/ppc440_bamboo.c
index 6ca873ee7e..34ddf45477 100644
--- a/hw/ppc440_bamboo.c
+++ b/hw/ppc440_bamboo.c
@@ -27,6 +27,11 @@
#define BINARY_DEVICE_TREE_FILE "bamboo.dtb"
+/* from u-boot */
+#define KERNEL_ADDR 0x1000000
+#define FDT_ADDR 0x1800000
+#define RAMDISK_ADDR 0x1900000
+
static int bamboo_load_device_tree(target_phys_addr_t addr,
uint32_t ramsize,
target_phys_addr_t initrd_base,
@@ -98,10 +103,8 @@ static void bamboo_init(ram_addr_t ram_size,
uint64_t elf_lowaddr;
target_phys_addr_t entry = 0;
target_phys_addr_t loadaddr = 0;
- target_long kernel_size = 0;
- target_ulong initrd_base = 0;
target_long initrd_size = 0;
- target_ulong dt_base = 0;
+ int success;
int i;
/* Setup CPU. */
@@ -118,15 +121,15 @@ static void bamboo_init(ram_addr_t ram_size,
/* Load kernel. */
if (kernel_filename) {
- kernel_size = load_uimage(kernel_filename, &entry, &loadaddr, NULL);
- if (kernel_size < 0) {
- kernel_size = load_elf(kernel_filename, NULL, NULL, &elf_entry,
- &elf_lowaddr, NULL, 1, ELF_MACHINE, 0);
+ success = load_uimage(kernel_filename, &entry, &loadaddr, NULL);
+ if (success < 0) {
+ success = load_elf(kernel_filename, NULL, NULL, &elf_entry,
+ &elf_lowaddr, NULL, 1, ELF_MACHINE, 0);
entry = elf_entry;
loadaddr = elf_lowaddr;
}
/* XXX try again as binary */
- if (kernel_size < 0) {
+ if (success < 0) {
fprintf(stderr, "qemu: could not load kernel '%s'\n",
kernel_filename);
exit(1);
@@ -135,26 +138,20 @@ static void bamboo_init(ram_addr_t ram_size,
/* Load initrd. */
if (initrd_filename) {
- initrd_base = kernel_size + loadaddr;
- initrd_size = load_image_targphys(initrd_filename, initrd_base,
- ram_size - initrd_base);
+ initrd_size = load_image_targphys(initrd_filename, RAMDISK_ADDR,
+ ram_size - RAMDISK_ADDR);
if (initrd_size < 0) {
- fprintf(stderr, "qemu: could not load initial ram disk '%s'\n",
- initrd_filename);
+ fprintf(stderr, "qemu: could not load ram disk '%s' at %x\n",
+ initrd_filename, RAMDISK_ADDR);
exit(1);
}
}
/* If we're loading a kernel directly, we must load the device tree too. */
if (kernel_filename) {
- if (initrd_base)
- dt_base = initrd_base + initrd_size;
- else
- dt_base = kernel_size + loadaddr;
-
- if (bamboo_load_device_tree(dt_base, ram_size,
- initrd_base, initrd_size, kernel_cmdline) < 0) {
+ if (bamboo_load_device_tree(FDT_ADDR, ram_size, RAMDISK_ADDR,
+ initrd_size, kernel_cmdline) < 0) {
fprintf(stderr, "couldn't load device tree\n");
exit(1);
}
@@ -163,7 +160,7 @@ static void bamboo_init(ram_addr_t ram_size,
/* Set initial guest state. */
env->gpr[1] = (16<<20) - 8;
- env->gpr[3] = dt_base;
+ env->gpr[3] = FDT_ADDR;
env->nip = entry;
/* XXX we currently depend on KVM to create some initial TLB entries. */
}
@@ -186,7 +183,7 @@ static QEMUMachine bamboo_machine_v0_12 = {
.compat_props = (GlobalProperty[]) {
{
.driver = "virtio-serial-pci",
- .property = "max_nr_ports",
+ .property = "max_ports",
.value = stringify(1),
},{
.driver = "virtio-serial-pci",
diff --git a/hw/ppc4xx_devs.c b/hw/ppc4xx_devs.c
index b15db81b6a..5f581fe2c4 100644
--- a/hw/ppc4xx_devs.c
+++ b/hw/ppc4xx_devs.c
@@ -56,7 +56,7 @@ CPUState *ppc4xx_init (const char *cpu_model,
cpu_clk->cb = NULL; /* We don't care about CPU clock frequency changes */
cpu_clk->opaque = env;
/* Set time-base frequency to sysclk */
- tb_clk->cb = ppc_emb_timers_init(env, sysclk);
+ tb_clk->cb = ppc_emb_timers_init(env, sysclk, PPC_INTERRUPT_PIT);
tb_clk->opaque = env;
ppc_dcr_init(env, NULL, NULL);
/* Register qemu callbacks */
@@ -619,7 +619,6 @@ static void sdram_reset (void *opaque)
/* We pre-initialize RAM banks */
sdram->status = 0x00000000;
sdram->cfg = 0x00800000;
- sdram_unmap_bcr(sdram);
}
void ppc4xx_sdram_init (CPUState *env, qemu_irq irq, int nbanks,
@@ -684,7 +683,7 @@ ram_addr_t ppc4xx_sdram_adjust(ram_addr_t ram_size, int nr_banks,
}
ram_size -= size_left;
- if (ram_size)
+ if (size_left)
printf("Truncating memory to %d MiB to fit SDRAM controller limits.\n",
(int)(ram_size >> 20));
diff --git a/hw/ppc_mac.h b/hw/ppc_mac.h
index 89f96bbc34..ea8759324c 100644
--- a/hw/ppc_mac.h
+++ b/hw/ppc_mac.h
@@ -30,7 +30,6 @@
#define BIOS_SIZE (1024 * 1024)
#define BIOS_FILENAME "ppc_rom.bin"
-#define VGABIOS_FILENAME "video.x"
#define NVRAM_SIZE 0x2000
#define PROM_FILENAME "openbios-ppc"
#define PROM_ADDR 0xfff00000
diff --git a/hw/ppc_newworld.c b/hw/ppc_newworld.c
index fbba9b6fb2..4369337b21 100644
--- a/hw/ppc_newworld.c
+++ b/hw/ppc_newworld.c
@@ -66,9 +66,9 @@
#include "kvm.h"
#include "kvm_ppc.h"
#include "hw/usb.h"
+#include "blockdev.h"
#define MAX_IDE_BUS 2
-#define VGA_BIOS_SIZE 65536
#define CFG_ADDR 0xf0000510
/* debug UniNorth */
@@ -128,24 +128,24 @@ static void ppc_core99_init (ram_addr_t ram_size,
const char *initrd_filename,
const char *cpu_model)
{
- CPUState *env = NULL, *envs[MAX_CPUS];
+ CPUState *env = NULL;
char *filename;
qemu_irq *pic, **openpic_irqs;
int unin_memory;
int linux_boot, i;
- ram_addr_t ram_offset, bios_offset, vga_bios_offset;
- uint32_t kernel_base, kernel_size, initrd_base, initrd_size;
+ ram_addr_t ram_offset, bios_offset;
+ uint32_t kernel_base, initrd_base;
+ long kernel_size, initrd_size;
PCIBus *pci_bus;
MacIONVRAMState *nvr;
int nvram_mem_index;
- int vga_bios_size, bios_size;
+ int bios_size;
int pic_mem_index, dbdma_mem_index, cuda_mem_index, escc_mem_index;
int ide_mem_index[3];
int ppc_boot_device;
DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS];
void *fw_cfg;
void *dbdma;
- uint8_t *vga_bios_ptr;
int machine_arch;
linux_boot = (kernel_filename != NULL);
@@ -165,11 +165,7 @@ static void ppc_core99_init (ram_addr_t ram_size,
}
/* Set time-base frequency to 100 Mhz */
cpu_ppc_tb_init(env, 100UL * 1000UL * 1000UL);
-#if 0
- env->osi_call = vga_osi_call;
-#endif
qemu_register_reset((QEMUResetHandler*)&cpu_reset, env);
- envs[i] = env;
}
/* allocate RAM */
@@ -197,36 +193,6 @@ static void ppc_core99_init (ram_addr_t ram_size,
exit(1);
}
- /* allocate and load VGA BIOS */
- vga_bios_offset = qemu_ram_alloc(NULL, "ppc_core99.vbios", VGA_BIOS_SIZE);
- vga_bios_ptr = qemu_get_ram_ptr(vga_bios_offset);
- filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, VGABIOS_FILENAME);
- if (filename) {
- vga_bios_size = load_image(filename, vga_bios_ptr + 8);
- qemu_free(filename);
- } else {
- vga_bios_size = -1;
- }
- if (vga_bios_size < 0) {
- /* if no bios is present, we can still work */
- fprintf(stderr, "qemu: warning: could not load VGA bios '%s'\n",
- VGABIOS_FILENAME);
- vga_bios_size = 0;
- } else {
- /* set a specific header (XXX: find real Apple format for NDRV
- drivers) */
- vga_bios_ptr[0] = 'N';
- vga_bios_ptr[1] = 'D';
- vga_bios_ptr[2] = 'R';
- vga_bios_ptr[3] = 'V';
- cpu_to_be32w((uint32_t *)(vga_bios_ptr + 4), vga_bios_size);
- vga_bios_size += 8;
-
- /* Round to page boundary */
- vga_bios_size = (vga_bios_size + TARGET_PAGE_SIZE - 1) &
- TARGET_PAGE_MASK;
- }
-
if (linux_boot) {
uint64_t lowaddr = 0;
int bswap_needed;
@@ -350,7 +316,7 @@ static void ppc_core99_init (ram_addr_t ram_size,
machine_arch = ARCH_MAC99;
}
/* init basic PC hardware */
- pci_vga_init(pci_bus, vga_bios_offset, vga_bios_size);
+ pci_vga_init(pci_bus, 0, 0);
escc_mem_index = escc_init(0x80013000, pic[0x25], pic[0x24],
serial_hds[0], serial_hds[1], ESCC_CLOCK, 4);
@@ -426,9 +392,16 @@ static void ppc_core99_init (ram_addr_t ram_size,
fw_cfg_add_i16(fw_cfg, FW_CFG_PPC_HEIGHT, graphic_height);
fw_cfg_add_i16(fw_cfg, FW_CFG_PPC_DEPTH, graphic_depth);
+ fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_IS_KVM, kvm_enabled());
if (kvm_enabled()) {
#ifdef CONFIG_KVM
+ uint8_t *hypercall;
+
fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_TBFREQ, kvmppc_get_tbfreq());
+ hypercall = qemu_malloc(16);
+ kvmppc_get_hypercall(env, hypercall, 16);
+ fw_cfg_add_bytes(fw_cfg, FW_CFG_PPC_KVM_HC, hypercall, 16);
+ fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_KVM_PID, getpid());
#endif
} else {
fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_TBFREQ, get_ticks_per_sec());
diff --git a/hw/ppc_oldworld.c b/hw/ppc_oldworld.c
index 6b3ab89611..a2f9ddf738 100644
--- a/hw/ppc_oldworld.c
+++ b/hw/ppc_oldworld.c
@@ -1,3 +1,4 @@
+
/*
* QEMU OldWorld PowerMac (currently ~G3 Beige) hardware System Emulator
*
@@ -41,81 +42,11 @@
#include "elf.h"
#include "kvm.h"
#include "kvm_ppc.h"
+#include "blockdev.h"
#define MAX_IDE_BUS 2
-#define VGA_BIOS_SIZE 65536
#define CFG_ADDR 0xf0000510
-/* temporary frame buffer OSI calls for the video.x driver. The right
- solution is to modify the driver to use VGA PCI I/Os */
-/* XXX: to be removed. This is no way related to emulation */
-static int vga_osi_call (CPUState *env)
-{
- static int vga_vbl_enabled;
- int linesize;
-
-#if 0
- printf("osi_call R5=%016" PRIx64 "\n", ppc_dump_gpr(env, 5));
-#endif
-
- /* same handler as PearPC, coming from the original MOL video
- driver. */
- switch(env->gpr[5]) {
- case 4:
- break;
- case 28: /* set_vmode */
- if (env->gpr[6] != 1 || env->gpr[7] != 0)
- env->gpr[3] = 1;
- else
- env->gpr[3] = 0;
- break;
- case 29: /* get_vmode_info */
- if (env->gpr[6] != 0) {
- if (env->gpr[6] != 1 || env->gpr[7] != 0) {
- env->gpr[3] = 1;
- break;
- }
- }
- env->gpr[3] = 0;
- env->gpr[4] = (1 << 16) | 1; /* num_vmodes, cur_vmode */
- env->gpr[5] = (1 << 16) | 0; /* num_depths, cur_depth_mode */
- env->gpr[6] = (graphic_width << 16) | graphic_height; /* w, h */
- env->gpr[7] = 85 << 16; /* refresh rate */
- env->gpr[8] = (graphic_depth + 7) & ~7; /* depth (round to byte) */
- linesize = ((graphic_depth + 7) >> 3) * graphic_width;
- linesize = (linesize + 3) & ~3;
- env->gpr[9] = (linesize << 16) | 0; /* row_bytes, offset */
- break;
- case 31: /* set_video power */
- env->gpr[3] = 0;
- break;
- case 39: /* video_ctrl */
- if (env->gpr[6] == 0 || env->gpr[6] == 1)
- vga_vbl_enabled = env->gpr[6];
- env->gpr[3] = 0;
- break;
- case 47:
- break;
- case 59: /* set_color */
- /* R6 = index, R7 = RGB */
- env->gpr[3] = 0;
- break;
- case 64: /* get color */
- /* R6 = index */
- env->gpr[3] = 0;
- break;
- case 116: /* set hwcursor */
- /* R6 = x, R7 = y, R8 = visible, R9 = data */
- break;
- default:
- fprintf(stderr, "unsupported OSI call R5=%016" PRIx64 "\n",
- ppc_dump_gpr(env, 5));
- break;
- }
-
- return 1; /* osi_call handled */
-}
-
static int fw_cfg_boot_set(void *opaque, const char *boot_device)
{
fw_cfg_add_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]);
@@ -135,23 +66,22 @@ static void ppc_heathrow_init (ram_addr_t ram_size,
const char *initrd_filename,
const char *cpu_model)
{
- CPUState *env = NULL, *envs[MAX_CPUS];
+ CPUState *env = NULL;
char *filename;
qemu_irq *pic, **heathrow_irqs;
int linux_boot, i;
- ram_addr_t ram_offset, bios_offset, vga_bios_offset;
+ ram_addr_t ram_offset, bios_offset;
uint32_t kernel_base, initrd_base;
int32_t kernel_size, initrd_size;
PCIBus *pci_bus;
MacIONVRAMState *nvr;
- int vga_bios_size, bios_size;
+ int bios_size;
int pic_mem_index, nvram_mem_index, dbdma_mem_index, cuda_mem_index;
int escc_mem_index, ide_mem_index[2];
uint16_t ppc_boot_device;
DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS];
void *fw_cfg;
void *dbdma;
- uint8_t *vga_bios_ptr;
linux_boot = (kernel_filename != NULL);
@@ -166,9 +96,7 @@ static void ppc_heathrow_init (ram_addr_t ram_size,
}
/* Set time-base frequency to 16.6 Mhz */
cpu_ppc_tb_init(env, 16600000UL);
- env->osi_call = vga_osi_call;
qemu_register_reset((QEMUResetHandler*)&cpu_reset, env);
- envs[i] = env;
}
/* allocate RAM */
@@ -202,36 +130,6 @@ static void ppc_heathrow_init (ram_addr_t ram_size,
exit(1);
}
- /* allocate and load VGA BIOS */
- vga_bios_offset = qemu_ram_alloc(NULL, "ppc_heathrow.vbios", VGA_BIOS_SIZE);
- vga_bios_ptr = qemu_get_ram_ptr(vga_bios_offset);
- filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, VGABIOS_FILENAME);
- if (filename) {
- vga_bios_size = load_image(filename, vga_bios_ptr + 8);
- qemu_free(filename);
- } else {
- vga_bios_size = -1;
- }
- if (vga_bios_size < 0) {
- /* if no bios is present, we can still work */
- fprintf(stderr, "qemu: warning: could not load VGA bios '%s'\n",
- VGABIOS_FILENAME);
- vga_bios_size = 0;
- } else {
- /* set a specific header (XXX: find real Apple format for NDRV
- drivers) */
- vga_bios_ptr[0] = 'N';
- vga_bios_ptr[1] = 'D';
- vga_bios_ptr[2] = 'R';
- vga_bios_ptr[3] = 'V';
- cpu_to_be32w((uint32_t *)(vga_bios_ptr + 4), vga_bios_size);
- vga_bios_size += 8;
-
- /* Round to page boundary */
- vga_bios_size = (vga_bios_size + TARGET_PAGE_SIZE - 1) &
- TARGET_PAGE_MASK;
- }
-
if (linux_boot) {
uint64_t lowaddr = 0;
int bswap_needed;
@@ -329,7 +227,7 @@ static void ppc_heathrow_init (ram_addr_t ram_size,
}
pic = heathrow_pic_init(&pic_mem_index, 1, heathrow_irqs);
pci_bus = pci_grackle_init(0xfec00000, pic);
- pci_vga_init(pci_bus, vga_bios_offset, vga_bios_size);
+ pci_vga_init(pci_bus, 0, 0);
escc_mem_index = escc_init(0x80013000, pic[0x0f], pic[0x10], serial_hds[0],
serial_hds[1], ESCC_CLOCK, 4);
@@ -398,9 +296,16 @@ static void ppc_heathrow_init (ram_addr_t ram_size,
fw_cfg_add_i16(fw_cfg, FW_CFG_PPC_HEIGHT, graphic_height);
fw_cfg_add_i16(fw_cfg, FW_CFG_PPC_DEPTH, graphic_depth);
+ fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_IS_KVM, kvm_enabled());
if (kvm_enabled()) {
#ifdef CONFIG_KVM
+ uint8_t *hypercall;
+
fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_TBFREQ, kvmppc_get_tbfreq());
+ hypercall = qemu_malloc(16);
+ kvmppc_get_hypercall(env, hypercall, 16);
+ fw_cfg_add_bytes(fw_cfg, FW_CFG_PPC_KVM_HC, hypercall, 16);
+ fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_KVM_PID, getpid());
#endif
} else {
fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_TBFREQ, get_ticks_per_sec());
diff --git a/hw/ppc_prep.c b/hw/ppc_prep.c
index fc3e17a0c3..a6915f7e68 100644
--- a/hw/ppc_prep.c
+++ b/hw/ppc_prep.c
@@ -37,6 +37,7 @@
#include "ide.h"
#include "loader.h"
#include "mc146818rtc.h"
+#include "blockdev.h"
//#define HARD_DEBUG_PPC_IO
//#define DEBUG_PPC_IO
@@ -564,14 +565,15 @@ static void ppc_prep_init (ram_addr_t ram_size,
const char *initrd_filename,
const char *cpu_model)
{
- CPUState *env = NULL, *envs[MAX_CPUS];
+ CPUState *env = NULL;
char *filename;
nvram_t nvram;
M48t59State *m48t59;
int PPC_io_memory;
int linux_boot, i, nb_nics1, bios_size;
ram_addr_t ram_offset, bios_offset;
- uint32_t kernel_base, kernel_size, initrd_base, initrd_size;
+ uint32_t kernel_base, initrd_base;
+ long kernel_size, initrd_size;
PCIBus *pci_bus;
qemu_irq *i8259;
qemu_irq *cpu_exit_irq;
@@ -600,7 +602,6 @@ static void ppc_prep_init (ram_addr_t ram_size,
cpu_ppc_tb_init(env, 100UL * 1000UL * 1000UL);
}
qemu_register_reset((QEMUResetHandler*)&cpu_reset, env);
- envs[i] = env;
}
/* allocate RAM */
diff --git a/hw/ppce500_mpc8544ds.c b/hw/ppce500_mpc8544ds.c
index 1422fad072..59d20d30ae 100644
--- a/hw/ppce500_mpc8544ds.c
+++ b/hw/ppce500_mpc8544ds.c
@@ -176,7 +176,6 @@ static void mpc8544ds_init(ram_addr_t ram_size,
int i=0;
unsigned int pci_irq_nrs[4] = {1, 2, 3, 4};
qemu_irq *irqs, *mpic, *pci_irqs;
- SerialState * serial[2];
/* Setup CPU */
env = cpu_ppc_init("e500v2_v30");
@@ -200,15 +199,15 @@ static void mpc8544ds_init(ram_addr_t ram_size,
/* Serial */
if (serial_hds[0]) {
- serial[0] = serial_mm_init(MPC8544_SERIAL0_REGS_BASE,
- 0, mpic[12+26], 399193,
- serial_hds[0], 1, 1);
+ serial_mm_init(MPC8544_SERIAL0_REGS_BASE,
+ 0, mpic[12+26], 399193,
+ serial_hds[0], 1, 1);
}
if (serial_hds[1]) {
- serial[0] = serial_mm_init(MPC8544_SERIAL1_REGS_BASE,
- 0, mpic[12+26], 399193,
- serial_hds[0], 1, 1);
+ serial_mm_init(MPC8544_SERIAL1_REGS_BASE,
+ 0, mpic[12+26], 399193,
+ serial_hds[0], 1, 1);
}
/* PCI */
diff --git a/hw/pxa2xx.c b/hw/pxa2xx.c
index 953e9ee1d1..6e046450df 100644
--- a/hw/pxa2xx.c
+++ b/hw/pxa2xx.c
@@ -15,6 +15,7 @@
#include "ssi.h"
#include "qemu-timer.h"
#include "qemu-char.h"
+#include "blockdev.h"
static struct {
target_phys_addr_t io_base;
@@ -124,7 +125,7 @@ static void pxa2xx_pm_write(void *opaque, target_phys_addr_t addr,
break;
default: /* Read-write registers */
- if (addr >= PMCR && addr <= PCMD31 && !(addr & 3)) {
+ if (!(addr & 3)) {
s->pm_regs[addr >> 2] = value;
break;
}
@@ -635,6 +636,7 @@ static void pxa2xx_ssp_fifo_update(PXA2xxSSPState *s)
{
s->sssr &= ~(0xf << 12); /* Clear RFL */
s->sssr &= ~(0xf << 8); /* Clear TFL */
+ s->sssr &= ~SSSR_TFS;
s->sssr &= ~SSSR_TNF;
if (s->enable) {
s->sssr |= ((s->rx_level - 1) & 0xf) << 12;
@@ -642,14 +644,13 @@ static void pxa2xx_ssp_fifo_update(PXA2xxSSPState *s)
s->sssr |= SSSR_RFS;
else
s->sssr &= ~SSSR_RFS;
- if (0 <= SSCR1_TFT(s->sscr[1]))
- s->sssr |= SSSR_TFS;
- else
- s->sssr &= ~SSSR_TFS;
if (s->rx_level)
s->sssr |= SSSR_RNE;
else
s->sssr &= ~SSSR_RNE;
+ /* TX FIFO is never filled, so it is always in underrun
+ condition if SSP is enabled */
+ s->sssr |= SSSR_TFS;
s->sssr |= SSSR_TNF;
}
@@ -1876,8 +1877,9 @@ static void pxa2xx_fir_write(void *opaque, target_phys_addr_t addr,
s->control[0] = value;
if (!(value & (1 << 4))) /* RXE */
s->rx_len = s->rx_start = 0;
- if (!(value & (1 << 3))) /* TXE */
- /* Nop */;
+ if (!(value & (1 << 3))) { /* TXE */
+ /* Nop */
+ }
s->enable = value & 1; /* ITR */
if (!s->enable)
s->status[0] = 0;
diff --git a/hw/qdev-properties.c b/hw/qdev-properties.c
index 9219cd7a60..a493087a52 100644
--- a/hw/qdev-properties.c
+++ b/hw/qdev-properties.c
@@ -1,6 +1,7 @@
#include "net.h"
#include "qdev.h"
#include "qerror.h"
+#include "blockdev.h"
void *qdev_get_prop_ptr(DeviceState *dev, Property *prop)
{
@@ -772,5 +773,5 @@ static int qdev_add_one_global(QemuOpts *opts, void *opaque)
void qemu_add_globals(void)
{
- qemu_opts_foreach(&qemu_global_opts, qdev_add_one_global, NULL, 0);
+ qemu_opts_foreach(qemu_find_opts("global"), qdev_add_one_global, NULL, 0);
}
diff --git a/hw/qdev.c b/hw/qdev.c
index e99c73f0d9..35858cb81b 100644
--- a/hw/qdev.c
+++ b/hw/qdev.c
@@ -29,6 +29,7 @@
#include "qdev.h"
#include "sysemu.h"
#include "monitor.h"
+#include "blockdev.h"
static int qdev_hotplug = 0;
@@ -792,7 +793,7 @@ int do_device_add(Monitor *mon, const QDict *qdict, QObject **ret_data)
{
QemuOpts *opts;
- opts = qemu_opts_from_qdict(&qemu_device_opts, qdict);
+ opts = qemu_opts_from_qdict(qemu_find_opts("device"), qdict);
if (!opts) {
return -1;
}
diff --git a/hw/qdev.h b/hw/qdev.h
index 678f8b7d1d..579328afc2 100644
--- a/hw/qdev.h
+++ b/hw/qdev.h
@@ -2,7 +2,6 @@
#define QDEV_H
#include "hw.h"
-#include "blockdev.h"
#include "qemu-queue.h"
#include "qemu-char.h"
#include "qemu-option.h"
diff --git a/hw/r2d.c b/hw/r2d.c
index 8c1fbad4ba..a58f653e52 100644
--- a/hw/r2d.c
+++ b/hw/r2d.c
@@ -36,6 +36,7 @@
#include "loader.h"
#include "usb.h"
#include "flash.h"
+#include "blockdev.h"
#define FLASH_BASE 0x00000000
#define FLASH_SIZE 0x02000000
diff --git a/hw/rc4030.c b/hw/rc4030.c
index 223137323b..abbc3eb4e2 100644
--- a/hw/rc4030.c
+++ b/hw/rc4030.c
@@ -749,7 +749,10 @@ static void rc4030_do_dma(void *opaque, int n, uint8_t *buf, int len, int is_wri
printf("rc4030 dma: Copying %d bytes %s host %p\n",
len, is_write ? "from" : "to", buf);
for (i = 0; i < len; i += 16) {
- int n = min(16, len - i);
+ int n = 16;
+ if (n > len - i) {
+ n = len - i;
+ }
for (j = 0; j < n; j++)
printf("%02x ", buf[i + j]);
while (j++ < 16)
diff --git a/hw/realview.c b/hw/realview.c
index 70bcdb846d..e9fcbc9a6d 100644
--- a/hw/realview.c
+++ b/hw/realview.c
@@ -18,6 +18,7 @@
#include "boards.h"
#include "bitbang_i2c.h"
#include "sysbus.h"
+#include "blockdev.h"
#define SMP_BOOT_ADDR 0xe0000000
diff --git a/hw/s390-virtio-bus.c b/hw/s390-virtio-bus.c
index fe6884d47d..784dc01b97 100644
--- a/hw/s390-virtio-bus.c
+++ b/hw/s390-virtio-bus.c
@@ -27,6 +27,7 @@
#include "elf.h"
#include "hw/virtio.h"
#include "hw/virtio-serial.h"
+#include "hw/virtio-net.h"
#include "hw/sysbus.h"
#include "kvm.h"
@@ -110,7 +111,7 @@ static int s390_virtio_net_init(VirtIOS390Device *dev)
{
VirtIODevice *vdev;
- vdev = virtio_net_init((DeviceState *)dev, &dev->nic);
+ vdev = virtio_net_init((DeviceState *)dev, &dev->nic, &dev->net);
if (!vdev) {
return -1;
}
@@ -327,6 +328,11 @@ static VirtIOS390DeviceInfo s390_virtio_net = {
.qdev.size = sizeof(VirtIOS390Device),
.qdev.props = (Property[]) {
DEFINE_NIC_PROPERTIES(VirtIOS390Device, nic),
+ DEFINE_PROP_UINT32("x-txtimer", VirtIOS390Device,
+ net.txtimer, TX_TIMER_INTERVAL),
+ DEFINE_PROP_INT32("x-txburst", VirtIOS390Device,
+ net.txburst, TX_BURST),
+ DEFINE_PROP_STRING("tx", VirtIOS390Device, net.tx),
DEFINE_PROP_END_OF_LIST(),
},
};
diff --git a/hw/s390-virtio-bus.h b/hw/s390-virtio-bus.h
index 333fea8963..41558c9c67 100644
--- a/hw/s390-virtio-bus.h
+++ b/hw/s390-virtio-bus.h
@@ -43,6 +43,7 @@ typedef struct VirtIOS390Device {
uint32_t host_features;
/* Max. number of ports we can have for a the virtio-serial device */
uint32_t max_virtserial_ports;
+ virtio_net_conf net;
} VirtIOS390Device;
typedef struct VirtIOS390Bus {
diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c
index d69c74c4ef..5a3fd4b7ac 100644
--- a/hw/scsi-bus.c
+++ b/hw/scsi-bus.c
@@ -3,6 +3,7 @@
#include "scsi.h"
#include "scsi-defs.h"
#include "qdev.h"
+#include "blockdev.h"
static struct BusInfo scsi_bus_info = {
.name = "SCSI",
@@ -142,6 +143,7 @@ SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, uint32_t tag, uint32_t l
req->tag = tag;
req->lun = lun;
req->status = -1;
+ req->enqueued = true;
QTAILQ_INSERT_TAIL(&d->requests, req, next);
return req;
}
@@ -158,9 +160,17 @@ SCSIRequest *scsi_req_find(SCSIDevice *d, uint32_t tag)
return NULL;
}
+static void scsi_req_dequeue(SCSIRequest *req)
+{
+ if (req->enqueued) {
+ QTAILQ_REMOVE(&req->dev->requests, req, next);
+ req->enqueued = false;
+ }
+}
+
void scsi_req_free(SCSIRequest *req)
{
- QTAILQ_REMOVE(&req->dev->requests, req, next);
+ scsi_req_dequeue(req);
qemu_free(req);
}
@@ -198,6 +208,8 @@ static int scsi_req_length(SCSIRequest *req, uint8_t *cmd)
case SEEK_6:
case WRITE_FILEMARKS:
case SPACE:
+ case RESERVE:
+ case RELEASE:
case ERASE:
case ALLOW_MEDIUM_REMOVAL:
case VERIFY:
@@ -309,7 +321,6 @@ static void scsi_req_xfer_mode(SCSIRequest *req)
case WRITE_BUFFER:
case FORMAT_UNIT:
case REASSIGN_BLOCKS:
- case RESERVE:
case SEARCH_EQUAL:
case SEARCH_HIGH:
case SEARCH_LOW:
@@ -512,6 +523,7 @@ void scsi_req_print(SCSIRequest *req)
void scsi_req_complete(SCSIRequest *req)
{
assert(req->status != -1);
+ scsi_req_dequeue(req);
req->bus->complete(req->bus, SCSI_REASON_DONE,
req->tag,
req->status);
diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c
index c30709c550..9628b39a21 100644
--- a/hw/scsi-disk.c
+++ b/hw/scsi-disk.c
@@ -36,6 +36,7 @@ do { fprintf(stderr, "scsi-disk: " fmt , ## __VA_ARGS__); } while (0)
#include "scsi.h"
#include "scsi-defs.h"
#include "sysemu.h"
+#include "blockdev.h"
#define SCSI_DMA_BUF_SIZE 131072
#define SCSI_MAX_INQUIRY_LEN 256
@@ -69,14 +70,15 @@ struct SCSIDiskState
char *serial;
};
-static SCSIDiskReq *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun)
+static SCSIDiskReq *scsi_new_request(SCSIDiskState *s, uint32_t tag,
+ uint32_t lun)
{
SCSIRequest *req;
SCSIDiskReq *r;
- req = scsi_req_alloc(sizeof(SCSIDiskReq), d, tag, lun);
+ req = scsi_req_alloc(sizeof(SCSIDiskReq), &s->qdev, tag, lun);
r = DO_UPCAST(SCSIDiskReq, req, req);
- r->iov.iov_base = qemu_memalign(512, SCSI_DMA_BUF_SIZE);
+ r->iov.iov_base = qemu_blockalign(s->bs, SCSI_DMA_BUF_SIZE);
return r;
}
@@ -134,7 +136,7 @@ static void scsi_read_complete(void * opaque, int ret)
scsi_command_complete(r, CHECK_CONDITION, NO_SENSE);
return;
}
- DPRINTF("Data ready tag=0x%x len=%" PRId64 "\n", r->req.tag, r->iov.iov_len);
+ DPRINTF("Data ready tag=0x%x len=%zd\n", r->req.tag, r->iov.iov_len);
r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, r->iov.iov_len);
}
@@ -154,7 +156,7 @@ static void scsi_read_data(SCSIDevice *d, uint32_t tag)
return;
}
if (r->sector_count == (uint32_t)-1) {
- DPRINTF("Read buf_len=%" PRId64 "\n", r->iov.iov_len);
+ DPRINTF("Read buf_len=%zd\n", r->iov.iov_len);
r->sector_count = 0;
r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, r->iov.iov_len);
return;
@@ -485,16 +487,26 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
return buflen;
}
-static int mode_sense_page(SCSIRequest *req, int page, uint8_t *p)
+static int mode_sense_page(SCSIRequest *req, int page, uint8_t *p,
+ int page_control)
{
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev);
BlockDriverState *bdrv = s->bs;
int cylinders, heads, secs;
+ /*
+ * If Changeable Values are requested, a mask denoting those mode parameters
+ * that are changeable shall be returned. As we currently don't support
+ * parameter changes via MODE_SELECT all bits are returned set to zero.
+ * The buffer was already menset to zero by the caller of this function.
+ */
switch (page) {
case 4: /* Rigid disk device geometry page. */
p[0] = 4;
p[1] = 0x16;
+ if (page_control == 1) { /* Changeable Values */
+ return p[1] + 2;
+ }
/* if a geometry hint is available, use it */
bdrv_get_geometry_hint(bdrv, &cylinders, &heads, &secs);
p[2] = (cylinders >> 16) & 0xff;
@@ -519,11 +531,14 @@ static int mode_sense_page(SCSIRequest *req, int page, uint8_t *p)
/* Medium rotation rate [rpm], 5400 rpm */
p[20] = (5400 >> 8) & 0xff;
p[21] = 5400 & 0xff;
- return 0x16;
+ return p[1] + 2;
case 5: /* Flexible disk device geometry page. */
p[0] = 5;
p[1] = 0x1e;
+ if (page_control == 1) { /* Changeable Values */
+ return p[1] + 2;
+ }
/* Transfer rate [kbit/s], 5Mbit/s */
p[2] = 5000 >> 8;
p[3] = 5000 & 0xff;
@@ -555,21 +570,27 @@ static int mode_sense_page(SCSIRequest *req, int page, uint8_t *p)
/* Medium rotation rate [rpm], 5400 rpm */
p[28] = (5400 >> 8) & 0xff;
p[29] = 5400 & 0xff;
- return 0x1e;
+ return p[1] + 2;
case 8: /* Caching page. */
p[0] = 8;
p[1] = 0x12;
+ if (page_control == 1) { /* Changeable Values */
+ return p[1] + 2;
+ }
if (bdrv_enable_write_cache(s->bs)) {
p[2] = 4; /* WCE */
}
- return 20;
+ return p[1] + 2;
case 0x2a: /* CD Capabilities and Mechanical Status page. */
if (bdrv_get_type_hint(bdrv) != BDRV_TYPE_CDROM)
return 0;
p[0] = 0x2a;
p[1] = 0x14;
+ if (page_control == 1) { /* Changeable Values */
+ return p[1] + 2;
+ }
p[2] = 3; // CD-R & CD-RW read
p[3] = 0; // Writing not supported
p[4] = 0x7f; /* Audio, composite, digital out,
@@ -593,7 +614,7 @@ static int mode_sense_page(SCSIRequest *req, int page, uint8_t *p)
p[19] = (16 * 176) & 0xff;
p[20] = (16 * 176) >> 8; // 16x write speed current
p[21] = (16 * 176) & 0xff;
- return 22;
+ return p[1] + 2;
default:
return 0;
@@ -604,29 +625,46 @@ static int scsi_disk_emulate_mode_sense(SCSIRequest *req, uint8_t *outbuf)
{
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev);
uint64_t nb_sectors;
- int page, dbd, buflen;
+ int page, dbd, buflen, page_control;
uint8_t *p;
+ uint8_t dev_specific_param;
dbd = req->cmd.buf[1] & 0x8;
page = req->cmd.buf[2] & 0x3f;
- DPRINTF("Mode Sense (page %d, len %zd)\n", page, req->cmd.xfer);
+ page_control = (req->cmd.buf[2] & 0xc0) >> 6;
+ DPRINTF("Mode Sense(%d) (page %d, xfer %zd, page_control %d)\n",
+ (req->cmd.buf[0] == MODE_SENSE) ? 6 : 10, page, req->cmd.xfer, page_control);
memset(outbuf, 0, req->cmd.xfer);
p = outbuf;
- p[1] = 0; /* Default media type. */
- p[3] = 0; /* Block descriptor length. */
if (bdrv_is_read_only(s->bs)) {
- p[2] = 0x80; /* Readonly. */
+ dev_specific_param = 0x80; /* Readonly. */
+ } else {
+ dev_specific_param = 0x00;
+ }
+
+ if (req->cmd.buf[0] == MODE_SENSE) {
+ p[1] = 0; /* Default media type. */
+ p[2] = dev_specific_param;
+ p[3] = 0; /* Block descriptor length. */
+ p += 4;
+ } else { /* MODE_SENSE_10 */
+ p[2] = 0; /* Default media type. */
+ p[3] = dev_specific_param;
+ p[6] = p[7] = 0; /* Block descriptor length. */
+ p += 8;
}
- p += 4;
bdrv_get_geometry(s->bs, &nb_sectors);
- if ((~dbd) & nb_sectors) {
- outbuf[3] = 8; /* Block descriptor length */
+ if (!dbd && nb_sectors) {
+ if (req->cmd.buf[0] == MODE_SENSE) {
+ outbuf[3] = 8; /* Block descriptor length */
+ } else { /* MODE_SENSE_10 */
+ outbuf[7] = 8; /* Block descriptor length */
+ }
nb_sectors /= s->cluster_size;
- nb_sectors--;
if (nb_sectors > 0xffffff)
- nb_sectors = 0xffffff;
+ nb_sectors = 0;
p[0] = 0; /* media density code */
p[1] = (nb_sectors >> 16) & 0xff;
p[2] = (nb_sectors >> 8) & 0xff;
@@ -638,21 +676,37 @@ static int scsi_disk_emulate_mode_sense(SCSIRequest *req, uint8_t *outbuf)
p += 8;
}
+ if (page_control == 3) { /* Saved Values */
+ return -1; /* ILLEGAL_REQUEST */
+ }
+
switch (page) {
case 0x04:
case 0x05:
case 0x08:
case 0x2a:
- p += mode_sense_page(req, page, p);
+ p += mode_sense_page(req, page, p, page_control);
break;
case 0x3f:
- p += mode_sense_page(req, 0x08, p);
- p += mode_sense_page(req, 0x2a, p);
+ p += mode_sense_page(req, 0x08, p, page_control);
+ p += mode_sense_page(req, 0x2a, p, page_control);
break;
+ default:
+ return -1; /* ILLEGAL_REQUEST */
}
buflen = p - outbuf;
- outbuf[0] = buflen - 4;
+ /*
+ * The mode data length field specifies the length in bytes of the
+ * following data that is available to be transferred. The mode data
+ * length does not include itself.
+ */
+ if (req->cmd.buf[0] == MODE_SENSE) {
+ outbuf[0] = buflen - 1;
+ } else { /* MODE_SENSE_10 */
+ outbuf[0] = ((buflen - 2) >> 8) & 0xff;
+ outbuf[1] = (buflen - 2) & 0xff;
+ }
if (buflen > req->cmd.xfer)
buflen = req->cmd.xfer;
return buflen;
@@ -839,6 +893,12 @@ static int scsi_disk_emulate_command(SCSIRequest *req, uint8_t *outbuf)
break;
case VERIFY:
break;
+ case REZERO_UNIT:
+ DPRINTF("Rezero Unit\n");
+ if (!bdrv_is_inserted(s->bs)) {
+ goto not_ready;
+ }
+ break;
default:
goto illegal_request;
}
@@ -880,7 +940,7 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
}
/* ??? Tags are not unique for different luns. We only implement a
single lun, so this should not matter. */
- r = scsi_new_request(d, tag, lun);
+ r = scsi_new_request(s, tag, lun);
outbuf = (uint8_t *)r->iov.iov_base;
is_write = 0;
DPRINTF("Command: lun=%d tag=0x%x data=0x%02x", lun, tag, buf[0]);
@@ -958,6 +1018,7 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
case SERVICE_ACTION_IN:
case REPORT_LUNS:
case VERIFY:
+ case REZERO_UNIT:
rc = scsi_disk_emulate_command(&r->req, outbuf);
if (rc > 0) {
r->iov.iov_len = rc;
@@ -981,13 +1042,40 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
case WRITE_10:
case WRITE_12:
case WRITE_16:
- DPRINTF("Write (sector %" PRId64 ", count %d)\n", lba, len);
+ case WRITE_VERIFY:
+ case WRITE_VERIFY_12:
+ case WRITE_VERIFY_16:
+ DPRINTF("Write %s(sector %" PRId64 ", count %d)\n",
+ (command & 0xe) == 0xe ? "And Verify " : "", lba, len);
if (lba > s->max_lba)
goto illegal_lba;
r->sector = lba * s->cluster_size;
r->sector_count = len * s->cluster_size;
is_write = 1;
break;
+ case MODE_SELECT:
+ DPRINTF("Mode Select(6) (len %d)\n", len);
+ /* We don't support mode parameter changes.
+ Allow the mode parameter header + block descriptors only. */
+ if (len > 12) {
+ goto fail;
+ }
+ break;
+ case MODE_SELECT_10:
+ DPRINTF("Mode Select(10) (len %d)\n", len);
+ /* We don't support mode parameter changes.
+ Allow the mode parameter header + block descriptors only. */
+ if (len > 16) {
+ goto fail;
+ }
+ break;
+ case SEEK_6:
+ case SEEK_10:
+ DPRINTF("Seek(%d) (sector %" PRId64 ")\n", command == SEEK_6 ? 6 : 10, lba);
+ if (lba > s->max_lba) {
+ goto illegal_lba;
+ }
+ break;
default:
DPRINTF("Unknown SCSI command (%2.2x)\n", buf[0]);
fail:
@@ -1059,6 +1147,11 @@ static int scsi_disk_initfn(SCSIDevice *dev)
s->bs = s->qdev.conf.bs;
is_cd = bdrv_get_type_hint(s->bs) == BDRV_TYPE_CDROM;
+ if (!is_cd && !bdrv_is_inserted(s->bs)) {
+ error_report("Device needs media, but drive is empty");
+ return -1;
+ }
+
if (bdrv_get_on_error(s->bs, 1) != BLOCK_ERR_REPORT) {
error_report("Device doesn't support drive option rerror");
return -1;
@@ -1085,6 +1178,7 @@ static int scsi_disk_initfn(SCSIDevice *dev)
s->qdev.blocksize = s->qdev.conf.logical_block_size;
}
s->cluster_size = s->qdev.blocksize / 512;
+ s->bs->buffer_alignment = s->qdev.blocksize;
s->qdev.type = TYPE_DISK;
qemu_add_vm_change_state_handler(scsi_dma_restart_cb, s);
diff --git a/hw/scsi-generic.c b/hw/scsi-generic.c
index a8b4176d80..7212091695 100644
--- a/hw/scsi-generic.c
+++ b/hw/scsi-generic.c
@@ -14,6 +14,7 @@
#include "qemu-common.h"
#include "qemu-error.h"
#include "scsi.h"
+#include "blockdev.h"
#ifdef __linux__
@@ -163,7 +164,7 @@ static void scsi_read_complete(void * opaque, int ret)
int len;
if (ret) {
- DPRINTF("IO error\n");
+ DPRINTF("IO error ret %d\n", ret);
scsi_command_complete(r, ret);
return;
}
@@ -235,7 +236,7 @@ static void scsi_write_complete(void * opaque, int ret)
if (r->req.cmd.buf[0] == MODE_SELECT && r->req.cmd.buf[4] == 12 &&
s->qdev.type == TYPE_TAPE) {
s->qdev.blocksize = (r->buf[9] << 16) | (r->buf[10] << 8) | r->buf[11];
- DPRINTF("block size %d\n", s->blocksize);
+ DPRINTF("block size %d\n", s->qdev.blocksize);
}
scsi_command_complete(r, ret);
@@ -350,8 +351,18 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
}
scsi_req_fixup(&r->req);
- DPRINTF("Command: lun=%d tag=0x%x data=0x%02x len %d\n", lun, tag,
- cmd[0], r->req.cmd.xfer);
+ DPRINTF("Command: lun=%d tag=0x%x len %zd data=0x%02x", lun, tag,
+ r->req.cmd.xfer, cmd[0]);
+
+#ifdef DEBUG_SCSI
+ {
+ int i;
+ for (i = 1; i < r->req.cmd.len; i++) {
+ printf(" 0x%02x", cmd[i]);
+ }
+ printf("\n");
+ }
+#endif
if (r->req.cmd.xfer == 0) {
if (r->buf != NULL)
@@ -444,15 +455,31 @@ static int get_stream_blocksize(BlockDriverState *bdrv)
return (buf[9] << 16) | (buf[10] << 8) | buf[11];
}
-static void scsi_destroy(SCSIDevice *d)
+static void scsi_generic_purge_requests(SCSIGenericState *s)
{
- SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d);
SCSIGenericReq *r;
while (!QTAILQ_EMPTY(&s->qdev.requests)) {
r = DO_UPCAST(SCSIGenericReq, req, QTAILQ_FIRST(&s->qdev.requests));
+ if (r->req.aiocb) {
+ bdrv_aio_cancel(r->req.aiocb);
+ }
scsi_remove_request(r);
}
+}
+
+static void scsi_generic_reset(DeviceState *dev)
+{
+ SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev.qdev, dev);
+
+ scsi_generic_purge_requests(s);
+}
+
+static void scsi_destroy(SCSIDevice *d)
+{
+ SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d);
+
+ scsi_generic_purge_requests(s);
blockdev_mark_auto_del(s->qdev.conf.bs);
}
@@ -526,6 +553,7 @@ static SCSIDeviceInfo scsi_generic_info = {
.qdev.name = "scsi-generic",
.qdev.desc = "pass through generic scsi device (/dev/sg*)",
.qdev.size = sizeof(SCSIGenericState),
+ .qdev.reset = scsi_generic_reset,
.init = scsi_generic_initfn,
.destroy = scsi_destroy,
.send_command = scsi_send_command,
diff --git a/hw/scsi.h b/hw/scsi.h
index 4fbf1d5dfd..cb06d6d824 100644
--- a/hw/scsi.h
+++ b/hw/scsi.h
@@ -43,6 +43,7 @@ typedef struct SCSIRequest {
enum SCSIXferMode mode;
} cmd;
BlockDriverAIOCB *aiocb;
+ bool enqueued;
QTAILQ_ENTRY(SCSIRequest) next;
} SCSIRequest;
diff --git a/hw/sd.c b/hw/sd.c
index c928120abd..601545b2d9 100644
--- a/hw/sd.c
+++ b/hw/sd.c
@@ -31,6 +31,7 @@
#include "hw.h"
#include "block.h"
+#include "block_int.h"
#include "sd.h"
//#define DEBUG_SD 1
@@ -440,7 +441,7 @@ SDState *sd_init(BlockDriverState *bs, int is_spi)
SDState *sd;
sd = (SDState *) qemu_mallocz(sizeof(SDState));
- sd->buf = qemu_memalign(512, 512);
+ sd->buf = qemu_blockalign(bs, 512);
sd->spi = is_spi;
sd->enable = 1;
sd_reset(sd, bs);
diff --git a/hw/serial.c b/hw/serial.c
index b66d13ad41..9ebc452aea 100644
--- a/hw/serial.c
+++ b/hw/serial.c
@@ -99,6 +99,14 @@
#define RECV_FIFO 1
#define MAX_XMIT_RETRY 4
+#ifdef DEBUG_SERIAL
+#define DPRINTF(fmt, ...) \
+do { fprintf(stderr, "serial: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) \
+do {} while (0)
+#endif
+
typedef struct SerialFIFO {
uint8_t data[UART_FIFO_LENGTH];
uint8_t count;
@@ -267,10 +275,9 @@ static void serial_update_parameters(SerialState *s)
ssp.stop_bits = stop_bits;
s->char_transmit_time = (get_ticks_per_sec() / speed) * frame_size;
qemu_chr_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp);
-#if 0
- printf("speed=%d parity=%c data=%d stop=%d\n",
+
+ DPRINTF("speed=%d parity=%c data=%d stop=%d\n",
speed, parity, data_bits, stop_bits);
-#endif
}
static void serial_update_msl(SerialState *s)
@@ -360,9 +367,7 @@ static void serial_ioport_write(void *opaque, uint32_t addr, uint32_t val)
SerialState *s = opaque;
addr &= 7;
-#ifdef DEBUG_SERIAL
- printf("serial: write addr=0x%02x val=0x%02x\n", addr, val);
-#endif
+ DPRINTF("write addr=0x%02x val=0x%02x\n", addr, val);
switch(addr) {
default:
case 0:
@@ -583,9 +588,7 @@ static uint32_t serial_ioport_read(void *opaque, uint32_t addr)
ret = s->scr;
break;
}
-#ifdef DEBUG_SERIAL
- printf("serial: read addr=0x%02x val=0x%02x\n", addr, ret);
-#endif
+ DPRINTF("read addr=0x%02x val=0x%02x\n", addr, ret);
return ret;
}
@@ -651,9 +654,7 @@ static void serial_receive1(void *opaque, const uint8_t *buf, int size)
static void serial_event(void *opaque, int event)
{
SerialState *s = opaque;
-#ifdef DEBUG_SERIAL
- printf("serial: event %x\n", event);
-#endif
+ DPRINTF("event %x\n", event);
if (event == CHR_EVENT_BREAK)
serial_receive_break(s);
}
@@ -673,6 +674,7 @@ static int serial_post_load(void *opaque, int version_id)
}
/* Initialize fcr via setter to perform essential side-effects */
serial_ioport_write(s, 0x02, s->fcr_vmstate);
+ serial_update_parameters(s);
return 0;
}
diff --git a/hw/sh_intc.c b/hw/sh_intc.c
index da36d32b1d..d3f5ea57d5 100644
--- a/hw/sh_intc.c
+++ b/hw/sh_intc.c
@@ -431,9 +431,8 @@ int sh_intc_init(struct intc_desc *desc,
desc->nr_prio_regs = nr_prio_regs;
i = sizeof(struct intc_source) * nr_sources;
- desc->sources = qemu_malloc(i);
+ desc->sources = qemu_mallocz(i);
- memset(desc->sources, 0, i);
for (i = 0; i < desc->nr_sources; i++) {
struct intc_source *source = desc->sources + i;
diff --git a/hw/slavio_timer.c b/hw/slavio_timer.c
index d7875536b6..c125de4b62 100644
--- a/hw/slavio_timer.c
+++ b/hw/slavio_timer.c
@@ -377,12 +377,12 @@ static void slavio_timer_reset(DeviceState *d)
curr_timer->limit = 0;
curr_timer->count = 0;
curr_timer->reached = 0;
- if (i < s->num_cpus) {
+ if (i <= s->num_cpus) {
ptimer_set_limit(curr_timer->timer,
LIMIT_TO_PERIODS(TIMER_MAX_COUNT32), 1);
ptimer_run(curr_timer->timer, 0);
+ curr_timer->running = 1;
}
- curr_timer->running = 1;
}
s->cputimer_mode = 0;
}
diff --git a/hw/sm501.c b/hw/sm501.c
index 8e6932d747..705e0a5c76 100644
--- a/hw/sm501.c
+++ b/hw/sm501.c
@@ -29,6 +29,7 @@
#include "devices.h"
#include "sysbus.h"
#include "qdev-addr.h"
+#include "range.h"
/*
* Status: 2010/05/07
@@ -814,7 +815,7 @@ static uint32_t sm501_palette_read(void *opaque, target_phys_addr_t addr)
/* TODO : consider BYTE/WORD access */
/* TODO : consider endian */
- assert(0 <= addr && addr < 0x400 * 3);
+ assert(range_covers_byte(0, 0x400 * 3, addr));
return *(uint32_t*)&s->dc_palette[addr];
}
@@ -828,7 +829,7 @@ static void sm501_palette_write(void *opaque,
/* TODO : consider BYTE/WORD access */
/* TODO : consider endian */
- assert(0 <= addr && addr < 0x400 * 3);
+ assert(range_covers_byte(0, 0x400 * 3, addr));
*(uint32_t*)&s->dc_palette[addr] = value;
}
diff --git a/hw/soc_dma.c b/hw/soc_dma.c
index e116e6373a..23ec51695a 100644
--- a/hw/soc_dma.c
+++ b/hw/soc_dma.c
@@ -192,12 +192,13 @@ static void soc_dma_ch_freq_update(struct dma_s *s)
if (s->enabled_count)
/* We completely ignore channel priorities and stuff */
s->channel_freq = s->soc.freq / s->enabled_count;
- else
+ else {
/* TODO: Signal that we want to disable the functional clock and let
* the platform code decide what to do with it, i.e. check that
* auto-idle is enabled in the clock controller and if we are stopping
* the clock, do the same with any parent clocks that had only one
- * user keeping them on and auto-idle enabled. */;
+ * user keeping them on and auto-idle enabled. */
+ }
}
void soc_dma_set_request(struct soc_dma_ch_s *ch, int level)
diff --git a/hw/sparc32_dma.c b/hw/sparc32_dma.c
index b52170787b..984ffc3e53 100644
--- a/hw/sparc32_dma.c
+++ b/hw/sparc32_dma.c
@@ -58,6 +58,7 @@
#define DMA_INTR 1
#define DMA_INTREN 0x10
#define DMA_WRITE_MEM 0x100
+#define DMA_EN 0x200
#define DMA_LOADED 0x04000000
#define DMA_DRAIN_FIFO 0x40
#define DMA_RESET 0x80
@@ -72,7 +73,12 @@ struct DMAState {
uint32_t dmaregs[DMA_REGS];
qemu_irq irq;
void *iommu;
- qemu_irq dev_reset;
+ qemu_irq gpio[2];
+};
+
+enum {
+ GPIO_RESET = 0,
+ GPIO_DMA,
};
/* Note: on sparc, the lance 16 bit bus is swapped */
@@ -201,12 +207,21 @@ static void dma_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
}
}
if (val & DMA_RESET) {
- qemu_irq_raise(s->dev_reset);
- qemu_irq_lower(s->dev_reset);
+ qemu_irq_raise(s->gpio[GPIO_RESET]);
+ qemu_irq_lower(s->gpio[GPIO_RESET]);
} else if (val & DMA_DRAIN_FIFO) {
val &= ~DMA_DRAIN_FIFO;
} else if (val == 0)
val = DMA_DRAIN_FIFO;
+
+ if (val & DMA_EN && !(s->dmaregs[0] & DMA_EN)) {
+ DPRINTF("Raise DMA enable\n");
+ qemu_irq_raise(s->gpio[GPIO_DMA]);
+ } else if (!(val & DMA_EN) && !!(s->dmaregs[0] & DMA_EN)) {
+ DPRINTF("Lower DMA enable\n");
+ qemu_irq_lower(s->gpio[GPIO_DMA]);
+ }
+
val &= ~DMA_CSR_RO_MASK;
val |= DMA_VER;
s->dmaregs[0] = (s->dmaregs[0] & DMA_CSR_RO_MASK) | val;
@@ -262,7 +277,7 @@ static int sparc32_dma_init1(SysBusDevice *dev)
sysbus_init_mmio(dev, DMA_SIZE, dma_io_memory);
qdev_init_gpio_in(&dev->qdev, dma_set_irq, 1);
- qdev_init_gpio_out(&dev->qdev, &s->dev_reset, 1);
+ qdev_init_gpio_out(&dev->qdev, s->gpio, 2);
return 0;
}
diff --git a/hw/spitz.c b/hw/spitz.c
index ccf2a091fb..a064460936 100644
--- a/hw/spitz.c
+++ b/hw/spitz.c
@@ -22,6 +22,7 @@
#include "block.h"
#include "audio/audio.h"
#include "boards.h"
+#include "blockdev.h"
#undef REG_FMT
#define REG_FMT "0x%02lx"
diff --git a/hw/sun4m.c b/hw/sun4m.c
index 208c8a86df..0392109230 100644
--- a/hw/sun4m.c
+++ b/hw/sun4m.c
@@ -40,6 +40,7 @@
#include "qdev-addr.h"
#include "loader.h"
#include "elf.h"
+#include "blockdev.h"
//#define DEBUG_IRQ
@@ -89,6 +90,7 @@
#define MAX_CPUS 16
#define MAX_PILS 16
+#define MAX_VSIMMS 4
#define ESCC_CLOCK 4915200
@@ -98,6 +100,10 @@ struct sun4m_hwdef {
target_phys_addr_t serial_base, fd_base;
target_phys_addr_t afx_base, idreg_base, dma_base, esp_base, le_base;
target_phys_addr_t tcx_base, cs_base, apc_base, aux1_base, aux2_base;
+ target_phys_addr_t bpp_base, dbri_base, sx_base;
+ struct {
+ target_phys_addr_t reg_base, vram_base;
+ } vsimm[MAX_VSIMMS];
target_phys_addr_t ecc_base;
uint32_t ecc_version;
uint8_t nvram_machine_id;
@@ -804,12 +810,13 @@ static void sun4m_hw_init(const struct sun4m_hwdef *hwdef, ram_addr_t RAM_size,
void *iommu, *espdma, *ledma, *nvram;
qemu_irq *cpu_irqs[MAX_CPUS], slavio_irq[32], slavio_cpu_irq[MAX_CPUS],
espdma_irq, ledma_irq;
- qemu_irq esp_reset;
+ qemu_irq esp_reset, dma_enable;
qemu_irq fdc_tc;
qemu_irq *cpu_halt;
unsigned long kernel_size;
DriveInfo *fd[MAX_FD];
void *fw_cfg;
+ unsigned int num_vsimms;
/* init CPUs */
if (!cpu_model)
@@ -872,8 +879,22 @@ static void sun4m_hw_init(const struct sun4m_hwdef *hwdef, ram_addr_t RAM_size,
fprintf(stderr, "qemu: Unsupported depth: %d\n", graphic_depth);
exit (1);
}
- tcx_init(hwdef->tcx_base, 0x00100000, graphic_width, graphic_height,
- graphic_depth);
+ num_vsimms = 0;
+ if (num_vsimms == 0) {
+ tcx_init(hwdef->tcx_base, 0x00100000, graphic_width, graphic_height,
+ graphic_depth);
+ }
+
+ for (i = num_vsimms; i < MAX_VSIMMS; i++) {
+ /* vsimm registers probed by OBP */
+ if (hwdef->vsimm[i].reg_base) {
+ empty_slot_init(hwdef->vsimm[i].reg_base, 0x2000);
+ }
+ }
+
+ if (hwdef->sx_base) {
+ empty_slot_init(hwdef->sx_base, 0x2000);
+ }
lance_init(&nd_table[0], hwdef->le_base, ledma, ledma_irq);
@@ -909,17 +930,31 @@ static void sun4m_hw_init(const struct sun4m_hwdef *hwdef, ram_addr_t RAM_size,
exit(1);
}
- esp_reset = qdev_get_gpio_in(espdma, 0);
esp_init(hwdef->esp_base, 2,
espdma_memory_read, espdma_memory_write,
- espdma, espdma_irq, &esp_reset);
+ espdma, espdma_irq, &esp_reset, &dma_enable);
+ qdev_connect_gpio_out(espdma, 0, esp_reset);
+ qdev_connect_gpio_out(espdma, 1, dma_enable);
if (hwdef->cs_base) {
sysbus_create_simple("SUNW,CS4231", hwdef->cs_base,
slavio_irq[5]);
}
+ if (hwdef->dbri_base) {
+ /* ISDN chip with attached CS4215 audio codec */
+ /* prom space */
+ empty_slot_init(hwdef->dbri_base+0x1000, 0x30);
+ /* reg space */
+ empty_slot_init(hwdef->dbri_base+0x10000, 0x100);
+ }
+
+ if (hwdef->bpp_base) {
+ /* parallel port */
+ empty_slot_init(hwdef->bpp_base, 0x20);
+ }
+
kernel_size = sun4m_load_kernel(kernel_filename, initrd_filename,
RAM_size);
@@ -945,8 +980,11 @@ static void sun4m_hw_init(const struct sun4m_hwdef *hwdef, ram_addr_t RAM_size,
fw_cfg_add_bytes(fw_cfg, FW_CFG_CMDLINE_DATA,
(uint8_t*)strdup(kernel_cmdline),
strlen(kernel_cmdline) + 1);
+ fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE,
+ strlen(kernel_cmdline) + 1);
} else {
fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_CMDLINE, 0);
+ fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE, 0);
}
fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_ADDR, INITRD_LOAD_ADDR);
fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_SIZE, 0); // not used
@@ -1063,9 +1101,25 @@ static const struct sun4m_hwdef sun4m_hwdefs[] = {
.dma_base = 0xef0400000ULL,
.esp_base = 0xef0800000ULL,
.le_base = 0xef0c00000ULL,
+ .bpp_base = 0xef4800000ULL,
.apc_base = 0xefa000000ULL, // XXX should not exist
.aux1_base = 0xff1800000ULL,
.aux2_base = 0xff1a01000ULL,
+ .dbri_base = 0xee0000000ULL,
+ .sx_base = 0xf80000000ULL,
+ .vsimm = {
+ {
+ .reg_base = 0x9c000000ULL,
+ .vram_base = 0xfc000000ULL
+ }, {
+ .reg_base = 0x90000000ULL,
+ .vram_base = 0xf0000000ULL
+ }, {
+ .reg_base = 0x94000000ULL
+ }, {
+ .reg_base = 0x98000000ULL
+ }
+ },
.ecc_base = 0xf00000000ULL,
.ecc_version = 0x20000000, // version 0, implementation 2
.nvram_machine_id = 0x72,
@@ -1441,7 +1495,7 @@ static void sun4d_hw_init(const struct sun4d_hwdef *hwdef, ram_addr_t RAM_size,
void *iounits[MAX_IOUNITS], *espdma, *ledma, *nvram;
qemu_irq *cpu_irqs[MAX_CPUS], sbi_irq[32], sbi_cpu_irq[MAX_CPUS],
espdma_irq, ledma_irq;
- qemu_irq esp_reset;
+ qemu_irq esp_reset, dma_enable;
unsigned long kernel_size;
void *fw_cfg;
DeviceState *dev;
@@ -1508,10 +1562,12 @@ static void sun4d_hw_init(const struct sun4d_hwdef *hwdef, ram_addr_t RAM_size,
exit(1);
}
- esp_reset = qdev_get_gpio_in(espdma, 0);
esp_init(hwdef->esp_base, 2,
espdma_memory_read, espdma_memory_write,
- espdma, espdma_irq, &esp_reset);
+ espdma, espdma_irq, &esp_reset, &dma_enable);
+
+ qdev_connect_gpio_out(espdma, 0, esp_reset);
+ qdev_connect_gpio_out(espdma, 1, dma_enable);
kernel_size = sun4m_load_kernel(kernel_filename, initrd_filename,
RAM_size);
@@ -1630,7 +1686,7 @@ static void sun4c_hw_init(const struct sun4c_hwdef *hwdef, ram_addr_t RAM_size,
{
void *iommu, *espdma, *ledma, *nvram;
qemu_irq *cpu_irqs, slavio_irq[8], espdma_irq, ledma_irq;
- qemu_irq esp_reset;
+ qemu_irq esp_reset, dma_enable;
qemu_irq fdc_tc;
unsigned long kernel_size;
DriveInfo *fd[MAX_FD];
@@ -1698,10 +1754,12 @@ static void sun4c_hw_init(const struct sun4c_hwdef *hwdef, ram_addr_t RAM_size,
exit(1);
}
- esp_reset = qdev_get_gpio_in(espdma, 0);
esp_init(hwdef->esp_base, 2,
espdma_memory_read, espdma_memory_write,
- espdma, espdma_irq, &esp_reset);
+ espdma, espdma_irq, &esp_reset, &dma_enable);
+
+ qdev_connect_gpio_out(espdma, 0, esp_reset);
+ qdev_connect_gpio_out(espdma, 1, dma_enable);
kernel_size = sun4m_load_kernel(kernel_filename, initrd_filename,
RAM_size);
diff --git a/hw/sun4u.c b/hw/sun4u.c
index 31c0c4c482..45a46d673c 100644
--- a/hw/sun4u.c
+++ b/hw/sun4u.c
@@ -37,6 +37,7 @@
#include "ide.h"
#include "loader.h"
#include "elf.h"
+#include "blockdev.h"
//#define DEBUG_IRQ
//#define DEBUG_EBUS
diff --git a/hw/syborg_virtio.c b/hw/syborg_virtio.c
index abf0370107..4dfd1a87b9 100644
--- a/hw/syborg_virtio.c
+++ b/hw/syborg_virtio.c
@@ -68,6 +68,7 @@ typedef struct {
uint32_t id;
NICConf nic;
uint32_t host_features;
+ virtio_net_conf net;
} SyborgVirtIOProxy;
static uint32_t syborg_virtio_readl(void *opaque, target_phys_addr_t offset)
@@ -284,7 +285,7 @@ static int syborg_virtio_net_init(SysBusDevice *dev)
VirtIODevice *vdev;
SyborgVirtIOProxy *proxy = FROM_SYSBUS(SyborgVirtIOProxy, dev);
- vdev = virtio_net_init(&dev->qdev, &proxy->nic);
+ vdev = virtio_net_init(&dev->qdev, &proxy->nic, &proxy->net);
return syborg_virtio_init(proxy, vdev);
}
@@ -295,6 +296,11 @@ static SysBusDeviceInfo syborg_virtio_net_info = {
.qdev.props = (Property[]) {
DEFINE_NIC_PROPERTIES(SyborgVirtIOProxy, nic),
DEFINE_VIRTIO_NET_FEATURES(SyborgVirtIOProxy, host_features),
+ DEFINE_PROP_UINT32("x-txtimer", SyborgVirtIOProxy,
+ net.txtimer, TX_TIMER_INTERVAL),
+ DEFINE_PROP_INT32("x-txburst", SyborgVirtIOProxy,
+ net.txburst, TX_BURST),
+ DEFINE_PROP_STRING("tx", SyborgVirtIOProxy, net.tx),
DEFINE_PROP_END_OF_LIST(),
}
};
diff --git a/hw/sysbus.c b/hw/sysbus.c
index 1f7f138416..d817721420 100644
--- a/hw/sysbus.c
+++ b/hw/sysbus.c
@@ -82,7 +82,8 @@ void sysbus_pass_irq(SysBusDevice *dev, SysBusDevice *target)
}
}
-void sysbus_init_mmio(SysBusDevice *dev, target_phys_addr_t size, int iofunc)
+void sysbus_init_mmio(SysBusDevice *dev, target_phys_addr_t size,
+ ram_addr_t iofunc)
{
int n;
diff --git a/hw/sysbus.h b/hw/sysbus.h
index 1a8f289c75..5980901845 100644
--- a/hw/sysbus.h
+++ b/hw/sysbus.h
@@ -21,7 +21,7 @@ struct SysBusDevice {
target_phys_addr_t addr;
target_phys_addr_t size;
mmio_mapfunc cb;
- int iofunc;
+ ram_addr_t iofunc;
} mmio[QDEV_MAX_MMIO];
};
@@ -39,7 +39,8 @@ typedef struct {
void sysbus_register_dev(const char *name, size_t size, sysbus_initfn init);
void sysbus_register_withprop(SysBusDeviceInfo *info);
void *sysbus_new(void);
-void sysbus_init_mmio(SysBusDevice *dev, target_phys_addr_t size, int iofunc);
+void sysbus_init_mmio(SysBusDevice *dev, target_phys_addr_t size,
+ ram_addr_t iofunc);
void sysbus_init_mmio_cb(SysBusDevice *dev, target_phys_addr_t size,
mmio_mapfunc cb);
void sysbus_init_irq(SysBusDevice *dev, qemu_irq *p);
diff --git a/hw/tc6393xb_template.h b/hw/tc6393xb_template.h
index 37bf8336be..1ccf6e8dfe 100644
--- a/hw/tc6393xb_template.h
+++ b/hw/tc6393xb_template.h
@@ -38,12 +38,10 @@
static void glue(tc6393xb_draw_graphic, BITS)(TC6393xbState *s)
{
int i;
- int w_display;
uint16_t *data_buffer;
uint8_t *data_display;
data_buffer = s->vram_ptr;
- w_display = s->scr_width * BITS / 8;
data_display = ds_get_data(s->ds);
for(i = 0; i < s->scr_height; i++) {
#if (BITS == 16)
diff --git a/hw/tosa.c b/hw/tosa.c
index ba6d9e73bf..cc8ce6d641 100644
--- a/hw/tosa.c
+++ b/hw/tosa.c
@@ -19,6 +19,7 @@
#include "boards.h"
#include "i2c.h"
#include "ssi.h"
+#include "blockdev.h"
#define TOSA_RAM 0x04000000
#define TOSA_ROM 0x00800000
diff --git a/hw/usb-msd.c b/hw/usb-msd.c
index 65e9624e54..0a95d8d506 100644
--- a/hw/usb-msd.c
+++ b/hw/usb-msd.c
@@ -15,6 +15,7 @@
#include "console.h"
#include "monitor.h"
#include "sysemu.h"
+#include "blockdev.h"
//#define DEBUG_MSD
@@ -575,7 +576,7 @@ static USBDevice *usb_msd_init(const char *filename)
/* parse -usbdevice disk: syntax into drive opts */
snprintf(id, sizeof(id), "usb%d", nr++);
- opts = qemu_opts_create(&qemu_drive_opts, id, 0);
+ opts = qemu_opts_create(qemu_find_opts("drive"), id, 0);
p1 = strchr(filename, ':');
if (p1++) {
diff --git a/hw/usb-net.c b/hw/usb-net.c
index a43bd17636..70f9263291 100644
--- a/hw/usb-net.c
+++ b/hw/usb-net.c
@@ -1472,7 +1472,7 @@ static USBDevice *usb_net_init(const char *cmdline)
QemuOpts *opts;
int idx;
- opts = qemu_opts_parse(&qemu_net_opts, cmdline, 0);
+ opts = qemu_opts_parse(qemu_find_opts("net"), cmdline, 0);
if (!opts) {
return NULL;
}
diff --git a/hw/usb-wacom.c b/hw/usb-wacom.c
index fe052eb756..47f26cd0a3 100644
--- a/hw/usb-wacom.c
+++ b/hw/usb-wacom.c
@@ -160,6 +160,7 @@ static int usb_mouse_poll(USBWacomState *s, uint8_t *buf, int len)
if (!s->mouse_grabbed) {
s->eh_entry = qemu_add_mouse_event_handler(usb_mouse_event, s, 0,
"QEMU PenPartner tablet");
+ qemu_activate_mouse_event_handler(s->eh_entry);
s->mouse_grabbed = 1;
}
@@ -197,6 +198,7 @@ static int usb_wacom_poll(USBWacomState *s, uint8_t *buf, int len)
if (!s->mouse_grabbed) {
s->eh_entry = qemu_add_mouse_event_handler(usb_wacom_event, s, 1,
"QEMU PenPartner tablet");
+ qemu_activate_mouse_event_handler(s->eh_entry);
s->mouse_grabbed = 1;
}
@@ -334,8 +336,10 @@ static int usb_wacom_handle_control(USBDevice *dev, int request, int value,
ret = 0;
break;
case WACOM_SET_REPORT:
- qemu_remove_mouse_event_handler(s->eh_entry);
- s->mouse_grabbed = 0;
+ if (s->mouse_grabbed) {
+ qemu_remove_mouse_event_handler(s->eh_entry);
+ s->mouse_grabbed = 0;
+ }
s->mode = data[0];
ret = 0;
break;
@@ -397,7 +401,10 @@ static void usb_wacom_handle_destroy(USBDevice *dev)
{
USBWacomState *s = (USBWacomState *) dev;
- qemu_remove_mouse_event_handler(s->eh_entry);
+ if (s->mouse_grabbed) {
+ qemu_remove_mouse_event_handler(s->eh_entry);
+ s->mouse_grabbed = 0;
+ }
}
static int usb_wacom_initfn(USBDevice *dev)
diff --git a/hw/versatilepb.c b/hw/versatilepb.c
index 1d049f2342..c51ee02c4d 100644
--- a/hw/versatilepb.c
+++ b/hw/versatilepb.c
@@ -16,6 +16,7 @@
#include "pci.h"
#include "usb-ohci.h"
#include "boards.h"
+#include "blockdev.h"
/* Primary interrupt controller. */
diff --git a/hw/vga.c b/hw/vga.c
index b5c7ee7fe3..966185e03b 100644
--- a/hw/vga.c
+++ b/hw/vga.c
@@ -2313,13 +2313,6 @@ void vga_init(VGACommonState *s)
register_ioport_write(0x1ce, 1, 2, vbe_ioport_write_index, s);
register_ioport_write(0x1cf, 1, 2, vbe_ioport_write_data, s);
-
- /* old Bochs IO ports */
- register_ioport_read(0xff80, 1, 2, vbe_ioport_read_index, s);
- register_ioport_read(0xff81, 1, 2, vbe_ioport_read_data, s);
-
- register_ioport_write(0xff80, 1, 2, vbe_ioport_write_index, s);
- register_ioport_write(0xff81, 1, 2, vbe_ioport_write_data, s);
#else
register_ioport_read(0x1ce, 1, 2, vbe_ioport_read_index, s);
register_ioport_read(0x1d0, 1, 2, vbe_ioport_read_data, s);
diff --git a/hw/vhost.c b/hw/vhost.c
index 65709d005d..8586f66bac 100644
--- a/hw/vhost.c
+++ b/hw/vhost.c
@@ -11,11 +11,9 @@
*/
#include <sys/ioctl.h>
-#include <sys/eventfd.h>
#include "vhost.h"
#include "hw/hw.h"
-/* For range_get_last */
-#include "pci.h"
+#include "range.h"
#include <linux/vhost.h>
static void vhost_dev_sync_region(struct vhost_dev *dev,
@@ -456,11 +454,6 @@ static int vhost_virtqueue_init(struct vhost_dev *dev,
};
struct VirtQueue *vvq = virtio_get_queue(vdev, idx);
- if (!vdev->binding->set_guest_notifier) {
- fprintf(stderr, "binding does not support guest notifiers\n");
- return -ENOSYS;
- }
-
if (!vdev->binding->set_host_notifier) {
fprintf(stderr, "binding does not support host notifiers\n");
return -ENOSYS;
@@ -513,12 +506,6 @@ static int vhost_virtqueue_init(struct vhost_dev *dev,
r = -errno;
goto fail_alloc;
}
- r = vdev->binding->set_guest_notifier(vdev->binding_opaque, idx, true);
- if (r < 0) {
- fprintf(stderr, "Error binding guest notifier: %d\n", -r);
- goto fail_guest_notifier;
- }
-
r = vdev->binding->set_host_notifier(vdev->binding_opaque, idx, true);
if (r < 0) {
fprintf(stderr, "Error binding host notifier: %d\n", -r);
@@ -528,12 +515,14 @@ static int vhost_virtqueue_init(struct vhost_dev *dev,
file.fd = event_notifier_get_fd(virtio_queue_get_host_notifier(vvq));
r = ioctl(dev->control, VHOST_SET_VRING_KICK, &file);
if (r) {
+ r = -errno;
goto fail_kick;
}
file.fd = event_notifier_get_fd(virtio_queue_get_guest_notifier(vvq));
r = ioctl(dev->control, VHOST_SET_VRING_CALL, &file);
if (r) {
+ r = -errno;
goto fail_call;
}
@@ -543,8 +532,6 @@ fail_call:
fail_kick:
vdev->binding->set_host_notifier(vdev->binding_opaque, idx, false);
fail_host_notifier:
- vdev->binding->set_guest_notifier(vdev->binding_opaque, idx, false);
-fail_guest_notifier:
fail_alloc:
cpu_physical_memory_unmap(vq->ring, virtio_queue_get_ring_size(vdev, idx),
0, 0);
@@ -570,13 +557,6 @@ static void vhost_virtqueue_cleanup(struct vhost_dev *dev,
.index = idx,
};
int r;
- r = vdev->binding->set_guest_notifier(vdev->binding_opaque, idx, false);
- if (r < 0) {
- fprintf(stderr, "vhost VQ %d guest cleanup failed: %d\n", idx, r);
- fflush(stderr);
- }
- assert (r >= 0);
-
r = vdev->binding->set_host_notifier(vdev->binding_opaque, idx, false);
if (r < 0) {
fprintf(stderr, "vhost VQ %d host cleanup failed: %d\n", idx, r);
@@ -649,15 +629,26 @@ void vhost_dev_cleanup(struct vhost_dev *hdev)
int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev)
{
int i, r;
+ if (!vdev->binding->set_guest_notifiers) {
+ fprintf(stderr, "binding does not support guest notifiers\n");
+ r = -ENOSYS;
+ goto fail;
+ }
+
+ r = vdev->binding->set_guest_notifiers(vdev->binding_opaque, true);
+ if (r < 0) {
+ fprintf(stderr, "Error binding guest notifier: %d\n", -r);
+ goto fail_notifiers;
+ }
r = vhost_dev_set_features(hdev, hdev->log_enabled);
if (r < 0) {
- goto fail;
+ goto fail_features;
}
r = ioctl(hdev->control, VHOST_SET_MEM_TABLE, hdev->mem);
if (r < 0) {
r = -errno;
- goto fail;
+ goto fail_mem;
}
for (i = 0; i < hdev->nvqs; ++i) {
r = vhost_virtqueue_init(hdev,
@@ -677,13 +668,14 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev)
(uint64_t)(unsigned long)hdev->log);
if (r < 0) {
r = -errno;
- goto fail_vq;
+ goto fail_log;
}
}
hdev->started = true;
return 0;
+fail_log:
fail_vq:
while (--i >= 0) {
vhost_virtqueue_cleanup(hdev,
@@ -691,13 +683,18 @@ fail_vq:
hdev->vqs + i,
i);
}
+fail_mem:
+fail_features:
+ vdev->binding->set_guest_notifiers(vdev->binding_opaque, false);
+fail_notifiers:
fail:
return r;
}
void vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev)
{
- int i;
+ int i, r;
+
for (i = 0; i < hdev->nvqs; ++i) {
vhost_virtqueue_cleanup(hdev,
vdev,
@@ -706,6 +703,13 @@ void vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev)
}
vhost_client_sync_dirty_bitmap(&hdev->client, 0,
(target_phys_addr_t)~0x0ull);
+ r = vdev->binding->set_guest_notifiers(vdev->binding_opaque, false);
+ if (r < 0) {
+ fprintf(stderr, "vhost guest notifier cleanup failed: %d\n", r);
+ fflush(stderr);
+ }
+ assert (r >= 0);
+
hdev->started = false;
qemu_free(hdev->log);
hdev->log_size = 0;
diff --git a/hw/vhost_net.c b/hw/vhost_net.c
index 606aa0c1c9..c068be1f54 100644
--- a/hw/vhost_net.c
+++ b/hw/vhost_net.c
@@ -20,7 +20,6 @@
#ifdef CONFIG_VHOST_NET
#include <linux/vhost.h>
-#include <sys/eventfd.h>
#include <sys/socket.h>
#include <linux/kvm.h>
#include <fcntl.h>
@@ -51,7 +50,9 @@ unsigned vhost_net_get_features(struct vhost_net *net, unsigned features)
if (!(net->dev.features & (1 << VIRTIO_RING_F_INDIRECT_DESC))) {
features &= ~(1 << VIRTIO_RING_F_INDIRECT_DESC);
}
- features &= ~(1 << VIRTIO_NET_F_MRG_RXBUF);
+ if (!(net->dev.features & (1 << VIRTIO_NET_F_MRG_RXBUF))) {
+ features &= ~(1 << VIRTIO_NET_F_MRG_RXBUF);
+ }
return features;
}
@@ -64,6 +65,9 @@ void vhost_net_ack_features(struct vhost_net *net, unsigned features)
if (features & (1 << VIRTIO_RING_F_INDIRECT_DESC)) {
net->dev.acked_features |= (1 << VIRTIO_RING_F_INDIRECT_DESC);
}
+ if (features & (1 << VIRTIO_NET_F_MRG_RXBUF)) {
+ net->dev.acked_features |= (1 << VIRTIO_NET_F_MRG_RXBUF);
+ }
}
static int vhost_net_get_fd(VLANClientState *backend)
@@ -98,6 +102,10 @@ struct vhost_net *vhost_net_init(VLANClientState *backend, int devfd)
if (r < 0) {
goto fail;
}
+ if (!tap_has_vnet_hdr_len(backend,
+ sizeof(struct virtio_net_hdr_mrg_rxbuf))) {
+ net->dev.features &= ~(1 << VIRTIO_NET_F_MRG_RXBUF);
+ }
if (~net->dev.features & net->dev.backend_features) {
fprintf(stderr, "vhost lacks feature mask %" PRIu64 " for backend\n",
(uint64_t)(~net->dev.features & net->dev.backend_features));
@@ -118,6 +126,10 @@ int vhost_net_start(struct vhost_net *net,
{
struct vhost_vring_file file = { };
int r;
+ if (net->dev.acked_features & (1 << VIRTIO_NET_F_MRG_RXBUF)) {
+ tap_set_vnet_hdr_len(net->vc,
+ sizeof(struct virtio_net_hdr_mrg_rxbuf));
+ }
net->dev.nvqs = 2;
net->dev.vqs = net->vqs;
@@ -139,12 +151,15 @@ int vhost_net_start(struct vhost_net *net,
return 0;
fail:
file.fd = -1;
- while (--file.index >= 0) {
+ while (file.index-- > 0) {
int r = ioctl(net->dev.control, VHOST_NET_SET_BACKEND, &file);
assert(r >= 0);
}
net->vc->info->poll(net->vc, true);
vhost_dev_stop(&net->dev, dev);
+ if (net->dev.acked_features & (1 << VIRTIO_NET_F_MRG_RXBUF)) {
+ tap_set_vnet_hdr_len(net->vc, sizeof(struct virtio_net_hdr));
+ }
return r;
}
@@ -159,11 +174,17 @@ void vhost_net_stop(struct vhost_net *net,
}
net->vc->info->poll(net->vc, true);
vhost_dev_stop(&net->dev, dev);
+ if (net->dev.acked_features & (1 << VIRTIO_NET_F_MRG_RXBUF)) {
+ tap_set_vnet_hdr_len(net->vc, sizeof(struct virtio_net_hdr));
+ }
}
void vhost_net_cleanup(struct vhost_net *net)
{
vhost_dev_cleanup(&net->dev);
+ if (net->dev.acked_features & (1 << VIRTIO_NET_F_MRG_RXBUF)) {
+ tap_set_vnet_hdr_len(net->vc, sizeof(struct virtio_net_hdr));
+ }
qemu_free(net);
}
#else
diff --git a/hw/virtex_ml507.c b/hw/virtex_ml507.c
new file mode 100644
index 0000000000..fa605158e7
--- /dev/null
+++ b/hw/virtex_ml507.c
@@ -0,0 +1,276 @@
+/*
+ * Model of Xilinx Virtex5 ML507 PPC-440 refdesign.
+ *
+ * Copyright (c) 2010 Edgar E. Iglesias.
+ *
+ * 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 "sysbus.h"
+#include "hw.h"
+#include "pc.h"
+#include "net.h"
+#include "flash.h"
+#include "sysemu.h"
+#include "devices.h"
+#include "boards.h"
+#include "device_tree.h"
+#include "loader.h"
+#include "elf.h"
+#include "qemu-log.h"
+
+#include "ppc.h"
+#include "ppc4xx.h"
+#include "ppc440.h"
+#include "ppc405.h"
+
+#include "blockdev.h"
+#include "xilinx.h"
+
+#define EPAPR_MAGIC (0x45504150)
+#define FLASH_SIZE (16 * 1024 * 1024)
+
+static struct boot_info
+{
+ uint32_t bootstrap_pc;
+ uint32_t cmdline;
+ uint32_t fdt;
+ uint32_t ima_size;
+ void *vfdt;
+} boot_info;
+
+/* Create reset TLB entries for BookE, spanning the 32bit addr space. */
+static void mmubooke_create_initial_mapping(CPUState *env,
+ target_ulong va,
+ target_phys_addr_t pa)
+{
+ ppcemb_tlb_t *tlb = &env->tlb[0].tlbe;
+
+ tlb->attr = 0;
+ tlb->prot = PAGE_VALID | ((PAGE_READ | PAGE_WRITE | PAGE_EXEC) << 4);
+ tlb->size = 1 << 31; /* up to 0x80000000 */
+ tlb->EPN = va & TARGET_PAGE_MASK;
+ tlb->RPN = pa & TARGET_PAGE_MASK;
+ tlb->PID = 0;
+
+ tlb = &env->tlb[1].tlbe;
+ tlb->attr = 0;
+ tlb->prot = PAGE_VALID | ((PAGE_READ | PAGE_WRITE | PAGE_EXEC) << 4);
+ tlb->size = 1 << 31; /* up to 0xffffffff */
+ tlb->EPN = 0x80000000 & TARGET_PAGE_MASK;
+ tlb->RPN = 0x80000000 & TARGET_PAGE_MASK;
+ tlb->PID = 0;
+}
+
+static CPUState *ppc440_init_xilinx(ram_addr_t *ram_size,
+ int do_init,
+ const char *cpu_model,
+ clk_setup_t *cpu_clk, clk_setup_t *tb_clk,
+ uint32_t sysclk)
+{
+ CPUState *env;
+ qemu_irq *irqs;
+
+ env = cpu_init(cpu_model);
+ if (!env) {
+ fprintf(stderr, "Unable to initialize CPU!\n");
+ exit(1);
+ }
+
+ cpu_clk->cb = NULL; /* We don't care about CPU clock frequency changes */
+ cpu_clk->opaque = env;
+ /* Set time-base frequency to sysclk */
+ tb_clk->cb = ppc_emb_timers_init(env, sysclk, PPC_INTERRUPT_DECR);
+ tb_clk->opaque = env;
+
+ ppc_dcr_init(env, NULL, NULL);
+
+ /* interrupt controller */
+ irqs = qemu_mallocz(sizeof(qemu_irq) * PPCUIC_OUTPUT_NB);
+ irqs[PPCUIC_OUTPUT_INT] = ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_INT];
+ irqs[PPCUIC_OUTPUT_CINT] = ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_CINT];
+ ppcuic_init(env, irqs, 0x0C0, 0, 1);
+ return env;
+}
+
+static void main_cpu_reset(void *opaque)
+{
+ CPUState *env = opaque;
+ struct boot_info *bi = env->load_info;
+
+ cpu_reset(env);
+ /* Linux Kernel Parameters (passing device tree):
+ * r3: pointer to the fdt
+ * r4: 0
+ * r5: 0
+ * r6: epapr magic
+ * r7: size of IMA in bytes
+ * r8: 0
+ * r9: 0
+ */
+ env->gpr[1] = (16<<20) - 8;
+ /* Provide a device-tree. */
+ env->gpr[3] = bi->fdt;
+ env->nip = bi->bootstrap_pc;
+
+ /* Create a mapping for the kernel. */
+ mmubooke_create_initial_mapping(env, 0, 0);
+ env->gpr[6] = tswap32(EPAPR_MAGIC);
+ env->gpr[7] = bi->ima_size;
+}
+
+#define BINARY_DEVICE_TREE_FILE "virtex-ml507.dtb"
+static int xilinx_load_device_tree(target_phys_addr_t addr,
+ uint32_t ramsize,
+ target_phys_addr_t initrd_base,
+ target_phys_addr_t initrd_size,
+ const char *kernel_cmdline)
+{
+ char *path;
+ int fdt_size;
+#ifdef CONFIG_FDT
+ void *fdt;
+ int r;
+
+ /* Try the local "ppc.dtb" override. */
+ fdt = load_device_tree("ppc.dtb", &fdt_size);
+ if (!fdt) {
+ path = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE);
+ if (path) {
+ fdt = load_device_tree(path, &fdt_size);
+ qemu_free(path);
+ }
+ if (!fdt) {
+ return 0;
+ }
+ }
+
+ r = qemu_devtree_setprop_string(fdt, "/chosen", "bootargs", kernel_cmdline);
+ if (r < 0)
+ fprintf(stderr, "couldn't set /chosen/bootargs\n");
+ cpu_physical_memory_write (addr, (void *)fdt, fdt_size);
+#else
+ /* We lack libfdt so we cannot manipulate the fdt. Just pass on the blob
+ to the kernel. */
+ fdt_size = load_image_targphys("ppc.dtb", addr, 0x10000);
+ if (fdt_size < 0) {
+ path = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE);
+ if (path) {
+ fdt_size = load_image_targphys(path, addr, 0x10000);
+ qemu_free(path);
+ }
+ }
+
+ if (kernel_cmdline) {
+ fprintf(stderr,
+ "Warning: missing libfdt, cannot pass cmdline to kernel!\n");
+ }
+#endif
+ return fdt_size;
+}
+
+static void virtex_init(ram_addr_t ram_size,
+ const char *boot_device,
+ const char *kernel_filename,
+ const char *kernel_cmdline,
+ const char *initrd_filename, const char *cpu_model)
+{
+ DeviceState *dev;
+ CPUState *env;
+ target_phys_addr_t ram_base = 0;
+ DriveInfo *dinfo;
+ ram_addr_t phys_ram;
+ ram_addr_t phys_flash;
+ qemu_irq irq[32], *cpu_irq;
+ clk_setup_t clk_setup[7];
+ int kernel_size;
+ int i;
+
+ /* init CPUs */
+ if (cpu_model == NULL) {
+ cpu_model = "440-Xilinx";
+ }
+
+ memset(clk_setup, 0, sizeof(clk_setup));
+ env = ppc440_init_xilinx(&ram_size, 1, cpu_model, &clk_setup[0],
+ &clk_setup[1], 400000000);
+ qemu_register_reset(main_cpu_reset, env);
+
+ phys_ram = qemu_ram_alloc(NULL, "ram", ram_size);
+ cpu_register_physical_memory(ram_base, ram_size, phys_ram | IO_MEM_RAM);
+
+ phys_flash = qemu_ram_alloc(NULL, "virtex.flash", FLASH_SIZE);
+ dinfo = drive_get(IF_PFLASH, 0, 0);
+ pflash_cfi01_register(0xfc000000, phys_flash,
+ dinfo ? dinfo->bdrv : NULL, (64 * 1024),
+ FLASH_SIZE >> 16,
+ 1, 0x89, 0x18, 0x0000, 0x0, 1);
+
+ cpu_irq = (qemu_irq *) &env->irq_inputs[PPC40x_INPUT_INT];
+ dev = xilinx_intc_create(0x81800000, cpu_irq[0], 0);
+ for (i = 0; i < 32; i++) {
+ irq[i] = qdev_get_gpio_in(dev, i);
+ }
+
+ serial_mm_init(0x83e01003ULL, 2, irq[9], 115200, serial_hds[0], 1, 0);
+
+ /* 2 timers at irq 2 @ 62 Mhz. */
+ xilinx_timer_create(0x83c00000, irq[3], 2, 62 * 1000000);
+
+ if (kernel_filename) {
+ uint64_t entry, low, high;
+ target_phys_addr_t boot_offset;
+
+ /* Boots a kernel elf binary. */
+ kernel_size = load_elf(kernel_filename, NULL, NULL,
+ &entry, &low, &high, 1, ELF_MACHINE, 0);
+ boot_info.bootstrap_pc = entry & 0x00ffffff;
+
+ if (kernel_size < 0) {
+ boot_offset = 0x1200000;
+ /* If we failed loading ELF's try a raw image. */
+ kernel_size = load_image_targphys(kernel_filename,
+ boot_offset,
+ ram_size);
+ boot_info.bootstrap_pc = boot_offset;
+ high = boot_info.bootstrap_pc + kernel_size + 8192;
+ }
+
+ boot_info.ima_size = kernel_size;
+
+ /* Provide a device-tree. */
+ boot_info.fdt = high + (8192 * 2);
+ boot_info.fdt &= ~8191;
+ xilinx_load_device_tree(boot_info.fdt, ram_size, 0, 0, kernel_cmdline);
+ }
+ env->load_info = &boot_info;
+}
+
+static QEMUMachine virtex_machine = {
+ .name = "virtex-ml507",
+ .desc = "Xilinx Virtex ML507 reference design",
+ .init = virtex_init,
+};
+
+static void virtex_machine_init(void)
+{
+ qemu_register_machine(&virtex_machine);
+}
+
+machine_init(virtex_machine_init);
diff --git a/hw/virtio-9p-debug.c b/hw/virtio-9p-debug.c
index e4ab4bca5f..cff5b07297 100644
--- a/hw/virtio-9p-debug.c
+++ b/hw/virtio-9p-debug.c
@@ -169,15 +169,37 @@ static void pprint_stat(V9fsPDU *pdu, int rx, size_t *offsetp, const char *name)
pprint_str(pdu, rx, offsetp, ", uid");
pprint_str(pdu, rx, offsetp, ", gid");
pprint_str(pdu, rx, offsetp, ", muid");
- if (dotu) {
- pprint_str(pdu, rx, offsetp, ", extension");
- pprint_int32(pdu, rx, offsetp, ", uid");
- pprint_int32(pdu, rx, offsetp, ", gid");
- pprint_int32(pdu, rx, offsetp, ", muid");
- }
+ pprint_str(pdu, rx, offsetp, ", extension");
+ pprint_int32(pdu, rx, offsetp, ", uid");
+ pprint_int32(pdu, rx, offsetp, ", gid");
+ pprint_int32(pdu, rx, offsetp, ", muid");
+ fprintf(llogfile, "}");
+}
+
+static void pprint_stat_dotl(V9fsPDU *pdu, int rx, size_t *offsetp,
+ const char *name)
+{
+ fprintf(llogfile, "%s={", name);
+ pprint_qid(pdu, rx, offsetp, "qid");
+ pprint_int32(pdu, rx, offsetp, ", st_mode");
+ pprint_int64(pdu, rx, offsetp, ", st_nlink");
+ pprint_int32(pdu, rx, offsetp, ", st_uid");
+ pprint_int32(pdu, rx, offsetp, ", st_gid");
+ pprint_int64(pdu, rx, offsetp, ", st_rdev");
+ pprint_int64(pdu, rx, offsetp, ", st_size");
+ pprint_int64(pdu, rx, offsetp, ", st_blksize");
+ pprint_int64(pdu, rx, offsetp, ", st_blocks");
+ pprint_int64(pdu, rx, offsetp, ", atime");
+ pprint_int64(pdu, rx, offsetp, ", atime_nsec");
+ pprint_int64(pdu, rx, offsetp, ", mtime");
+ pprint_int64(pdu, rx, offsetp, ", mtime_nsec");
+ pprint_int64(pdu, rx, offsetp, ", ctime");
+ pprint_int64(pdu, rx, offsetp, ", ctime_nsec");
fprintf(llogfile, "}");
}
+
+
static void pprint_strs(V9fsPDU *pdu, int rx, size_t *offsetp, const char *name)
{
int sg_count = get_sg_count(pdu, rx);
@@ -327,7 +349,33 @@ void pprint_pdu(V9fsPDU *pdu)
llogfile = fopen("/tmp/pdu.log", "w");
}
+ BUG_ON(!llogfile);
+
switch (pdu->id) {
+ case P9_TREADDIR:
+ fprintf(llogfile, "TREADDIR: (");
+ pprint_int32(pdu, 0, &offset, "fid");
+ pprint_int64(pdu, 0, &offset, ", initial offset");
+ pprint_int32(pdu, 0, &offset, ", max count");
+ break;
+ case P9_RREADDIR:
+ fprintf(llogfile, "RREADDIR: (");
+ pprint_int32(pdu, 1, &offset, "count");
+#ifdef DEBUG_DATA
+ pprint_data(pdu, 1, &offset, ", data");
+#endif
+ break;
+ case P9_TMKDIR:
+ fprintf(llogfile, "TMKDIR: (");
+ pprint_int32(pdu, 0, &offset, "fid");
+ pprint_str(pdu, 0, &offset, "name");
+ pprint_int32(pdu, 0, &offset, "mode");
+ pprint_int32(pdu, 0, &offset, "gid");
+ break;
+ case P9_RMKDIR:
+ fprintf(llogfile, "RMKDIR: (");
+ pprint_qid(pdu, 0, &offset, "qid");
+ break;
case P9_TVERSION:
fprintf(llogfile, "TVERSION: (");
pprint_int32(pdu, 0, &offset, "msize");
@@ -338,14 +386,20 @@ void pprint_pdu(V9fsPDU *pdu)
pprint_int32(pdu, 1, &offset, "msize");
pprint_str(pdu, 1, &offset, ", version");
break;
+ case P9_TGETATTR:
+ fprintf(llogfile, "TGETATTR: (");
+ pprint_int32(pdu, 0, &offset, "fid");
+ break;
+ case P9_RGETATTR:
+ fprintf(llogfile, "RGETATTR: (");
+ pprint_stat_dotl(pdu, 1, &offset, "getattr");
+ break;
case P9_TAUTH:
fprintf(llogfile, "TAUTH: (");
pprint_int32(pdu, 0, &offset, "afid");
pprint_str(pdu, 0, &offset, ", uname");
pprint_str(pdu, 0, &offset, ", aname");
- if (dotu) {
- pprint_int32(pdu, 0, &offset, ", n_uname");
- }
+ pprint_int32(pdu, 0, &offset, ", n_uname");
break;
case P9_RAUTH:
fprintf(llogfile, "RAUTH: (");
@@ -357,9 +411,7 @@ void pprint_pdu(V9fsPDU *pdu)
pprint_int32(pdu, 0, &offset, ", afid");
pprint_str(pdu, 0, &offset, ", uname");
pprint_str(pdu, 0, &offset, ", aname");
- if (dotu) {
- pprint_int32(pdu, 0, &offset, ", n_uname");
- }
+ pprint_int32(pdu, 0, &offset, ", n_uname");
break;
case P9_RATTACH:
fprintf(llogfile, "RATTACH: (");
@@ -371,9 +423,7 @@ void pprint_pdu(V9fsPDU *pdu)
case P9_RERROR:
fprintf(llogfile, "RERROR: (");
pprint_str(pdu, 1, &offset, "ename");
- if (dotu) {
- pprint_int32(pdu, 1, &offset, ", ecode");
- }
+ pprint_int32(pdu, 1, &offset, ", ecode");
break;
case P9_TFLUSH:
fprintf(llogfile, "TFLUSH: (");
@@ -408,15 +458,58 @@ void pprint_pdu(V9fsPDU *pdu)
pprint_str(pdu, 0, &offset, ", name");
pprint_int32(pdu, 0, &offset, ", perm");
pprint_int8(pdu, 0, &offset, ", mode");
- if (dotu) {
- pprint_str(pdu, 0, &offset, ", extension");
- }
+ pprint_str(pdu, 0, &offset, ", extension");
break;
case P9_RCREATE:
fprintf(llogfile, "RCREATE: (");
pprint_qid(pdu, 1, &offset, "qid");
pprint_int32(pdu, 1, &offset, ", iounit");
break;
+ case P9_TSYMLINK:
+ fprintf(llogfile, "TSYMLINK: (");
+ pprint_int32(pdu, 0, &offset, "fid");
+ pprint_str(pdu, 0, &offset, ", name");
+ pprint_str(pdu, 0, &offset, ", symname");
+ pprint_int32(pdu, 0, &offset, ", gid");
+ break;
+ case P9_RSYMLINK:
+ fprintf(llogfile, "RSYMLINK: (");
+ pprint_qid(pdu, 1, &offset, "qid");
+ break;
+ case P9_TLCREATE:
+ fprintf(llogfile, "TLCREATE: (");
+ pprint_int32(pdu, 0, &offset, "dfid");
+ pprint_str(pdu, 0, &offset, ", name");
+ pprint_int32(pdu, 0, &offset, ", flags");
+ pprint_int32(pdu, 0, &offset, ", mode");
+ pprint_int32(pdu, 0, &offset, ", gid");
+ break;
+ case P9_RLCREATE:
+ fprintf(llogfile, "RLCREATE: (");
+ pprint_qid(pdu, 1, &offset, "qid");
+ pprint_int32(pdu, 1, &offset, ", iounit");
+ break;
+ case P9_TMKNOD:
+ fprintf(llogfile, "TMKNOD: (");
+ pprint_int32(pdu, 0, &offset, "fid");
+ pprint_str(pdu, 0, &offset, "name");
+ pprint_int32(pdu, 0, &offset, "mode");
+ pprint_int32(pdu, 0, &offset, "major");
+ pprint_int32(pdu, 0, &offset, "minor");
+ pprint_int32(pdu, 0, &offset, "gid");
+ break;
+ case P9_RMKNOD:
+ fprintf(llogfile, "RMKNOD: )");
+ pprint_qid(pdu, 0, &offset, "qid");
+ break;
+ case P9_TREADLINK:
+ fprintf(llogfile, "TREADLINK: (");
+ pprint_int32(pdu, 0, &offset, "fid");
+ break;
+ case P9_RREADLINK:
+ fprintf(llogfile, "RREADLINK: (");
+ pprint_str(pdu, 0, &offset, "target");
+ break;
case P9_TREAD:
fprintf(llogfile, "TREAD: (");
pprint_int32(pdu, 0, &offset, "fid");
@@ -450,6 +543,22 @@ void pprint_pdu(V9fsPDU *pdu)
case P9_RCLUNK:
fprintf(llogfile, "RCLUNK: (");
break;
+ case P9_TFSYNC:
+ fprintf(llogfile, "TFSYNC: (");
+ pprint_int32(pdu, 0, &offset, "fid");
+ break;
+ case P9_RFSYNC:
+ fprintf(llogfile, "RFSYNC: (");
+ break;
+ case P9_TLINK:
+ fprintf(llogfile, "TLINK: (");
+ pprint_int32(pdu, 0, &offset, "fid");
+ pprint_str(pdu, 0, &offset, ", oldpath");
+ pprint_str(pdu, 0, &offset, ", newpath");
+ break;
+ case P9_RLINK:
+ fprintf(llogfile, "RLINK: (");
+ break;
case P9_TREMOVE:
fprintf(llogfile, "TREMOVE: (");
pprint_int32(pdu, 0, &offset, "fid");
@@ -475,6 +584,56 @@ void pprint_pdu(V9fsPDU *pdu)
case P9_RWSTAT:
fprintf(llogfile, "RWSTAT: (");
break;
+ case P9_TXATTRWALK:
+ fprintf(llogfile, "TXATTRWALK: (");
+ pprint_int32(pdu, 0, &offset, "fid");
+ pprint_int32(pdu, 0, &offset, ", newfid");
+ pprint_str(pdu, 0, &offset, ", xattr name");
+ break;
+ case P9_RXATTRWALK:
+ fprintf(llogfile, "RXATTRWALK: (");
+ pprint_int64(pdu, 1, &offset, "xattrsize");
+ case P9_TXATTRCREATE:
+ fprintf(llogfile, "TXATTRCREATE: (");
+ pprint_int32(pdu, 0, &offset, "fid");
+ pprint_str(pdu, 0, &offset, ", name");
+ pprint_int64(pdu, 0, &offset, ", xattrsize");
+ pprint_int32(pdu, 0, &offset, ", flags");
+ break;
+ case P9_RXATTRCREATE:
+ fprintf(llogfile, "RXATTRCREATE: (");
+ break;
+ case P9_TLOCK:
+ fprintf(llogfile, "TLOCK: (");
+ pprint_int32(pdu, 0, &offset, "fid");
+ pprint_int8(pdu, 0, &offset, ", type");
+ pprint_int32(pdu, 0, &offset, ", flags");
+ pprint_int64(pdu, 0, &offset, ", start");
+ pprint_int64(pdu, 0, &offset, ", length");
+ pprint_int32(pdu, 0, &offset, ", proc_id");
+ pprint_str(pdu, 0, &offset, ", client_id");
+ break;
+ case P9_RLOCK:
+ fprintf(llogfile, "RLOCK: (");
+ pprint_int8(pdu, 0, &offset, "status");
+ break;
+ case P9_TGETLOCK:
+ fprintf(llogfile, "TGETLOCK: (");
+ pprint_int32(pdu, 0, &offset, "fid");
+ pprint_int8(pdu, 0, &offset, ", type");
+ pprint_int64(pdu, 0, &offset, ", start");
+ pprint_int64(pdu, 0, &offset, ", length");
+ pprint_int32(pdu, 0, &offset, ", proc_id");
+ pprint_str(pdu, 0, &offset, ", client_id");
+ break;
+ case P9_RGETLOCK:
+ fprintf(llogfile, "RGETLOCK: (");
+ pprint_int8(pdu, 0, &offset, "type");
+ pprint_int64(pdu, 0, &offset, ", start");
+ pprint_int64(pdu, 0, &offset, ", length");
+ pprint_int32(pdu, 0, &offset, ", proc_id");
+ pprint_str(pdu, 0, &offset, ", client_id");
+ break;
default:
fprintf(llogfile, "unknown(%d): (", pdu->id);
break;
diff --git a/hw/virtio-9p-debug.h b/hw/virtio-9p-debug.h
index 0104be5eb3..d9a249118d 100644
--- a/hw/virtio-9p-debug.h
+++ b/hw/virtio-9p-debug.h
@@ -1,7 +1,6 @@
#ifndef _QEMU_VIRTIO_9P_DEBUG_H
#define _QEMU_VIRTIO_9P_DEBUG_H
-extern int dotu;
void pprint_pdu(V9fsPDU *pdu);
#endif
diff --git a/hw/virtio-9p-local.c b/hw/virtio-9p-local.c
index 04f7f6f501..0d520201b4 100644
--- a/hw/virtio-9p-local.c
+++ b/hw/virtio-9p-local.c
@@ -12,6 +12,7 @@
*/
#include "virtio.h"
#include "virtio-9p.h"
+#include "virtio-9p-xattr.h"
#include <arpa/inet.h>
#include <pwd.h>
#include <grp.h>
@@ -19,14 +20,6 @@
#include <sys/un.h>
#include <attr/xattr.h>
-static const char *rpath(FsContext *ctx, const char *path)
-{
- /* FIXME: so wrong... */
- static char buffer[4096];
- snprintf(buffer, sizeof(buffer), "%s/%s", ctx->fs_root, path);
- return buffer;
-}
-
static int local_lstat(FsContext *fs_ctx, const char *path, struct stat *stbuf)
{
@@ -101,8 +94,14 @@ static int local_post_create_passthrough(FsContext *fs_ctx, const char *path,
if (chmod(rpath(fs_ctx, path), credp->fc_mode & 07777) < 0) {
return -1;
}
- if (chown(rpath(fs_ctx, path), credp->fc_uid, credp->fc_gid) < 0) {
- return -1;
+ if (lchown(rpath(fs_ctx, path), credp->fc_uid, credp->fc_gid) < 0) {
+ /*
+ * If we fail to change ownership and if we are
+ * using security model none. Ignore the error
+ */
+ if (fs_ctx->fs_sm != SM_NONE) {
+ return -1;
+ }
}
return 0;
}
@@ -122,7 +121,8 @@ static ssize_t local_readlink(FsContext *fs_ctx, const char *path,
} while (tsize == -1 && errno == EINTR);
close(fd);
return tsize;
- } else if (fs_ctx->fs_sm == SM_PASSTHROUGH) {
+ } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) ||
+ (fs_ctx->fs_sm == SM_NONE)) {
tsize = readlink(rpath(fs_ctx, path), buf, bufsz);
}
return tsize;
@@ -168,28 +168,42 @@ static void local_seekdir(FsContext *ctx, DIR *dir, off_t off)
return seekdir(dir, off);
}
-static ssize_t local_readv(FsContext *ctx, int fd, const struct iovec *iov,
- int iovcnt)
+static ssize_t local_preadv(FsContext *ctx, int fd, const struct iovec *iov,
+ int iovcnt, off_t offset)
{
- return readv(fd, iov, iovcnt);
-}
-
-static off_t local_lseek(FsContext *ctx, int fd, off_t offset, int whence)
-{
- return lseek(fd, offset, whence);
+#ifdef CONFIG_PREADV
+ return preadv(fd, iov, iovcnt, offset);
+#else
+ int err = lseek(fd, offset, SEEK_SET);
+ if (err == -1) {
+ return err;
+ } else {
+ return readv(fd, iov, iovcnt);
+ }
+#endif
}
-static ssize_t local_writev(FsContext *ctx, int fd, const struct iovec *iov,
- int iovcnt)
+static ssize_t local_pwritev(FsContext *ctx, int fd, const struct iovec *iov,
+ int iovcnt, off_t offset)
{
- return writev(fd, iov, iovcnt);
+#ifdef CONFIG_PREADV
+ return pwritev(fd, iov, iovcnt, offset);
+#else
+ int err = lseek(fd, offset, SEEK_SET);
+ if (err == -1) {
+ return err;
+ } else {
+ return writev(fd, iov, iovcnt);
+ }
+#endif
}
static int local_chmod(FsContext *fs_ctx, const char *path, FsCred *credp)
{
if (fs_ctx->fs_sm == SM_MAPPED) {
return local_set_xattr(rpath(fs_ctx, path), credp);
- } else if (fs_ctx->fs_sm == SM_PASSTHROUGH) {
+ } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) ||
+ (fs_ctx->fs_sm == SM_NONE)) {
return chmod(rpath(fs_ctx, path), credp->fc_mode);
}
return -1;
@@ -211,7 +225,8 @@ static int local_mknod(FsContext *fs_ctx, const char *path, FsCred *credp)
serrno = errno;
goto err_end;
}
- } else if (fs_ctx->fs_sm == SM_PASSTHROUGH) {
+ } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) ||
+ (fs_ctx->fs_sm == SM_NONE)) {
err = mknod(rpath(fs_ctx, path), credp->fc_mode, credp->fc_rdev);
if (err == -1) {
return err;
@@ -247,7 +262,8 @@ static int local_mkdir(FsContext *fs_ctx, const char *path, FsCred *credp)
serrno = errno;
goto err_end;
}
- } else if (fs_ctx->fs_sm == SM_PASSTHROUGH) {
+ } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) ||
+ (fs_ctx->fs_sm == SM_NONE)) {
err = mkdir(rpath(fs_ctx, path), credp->fc_mode);
if (err == -1) {
return err;
@@ -316,7 +332,8 @@ static int local_open2(FsContext *fs_ctx, const char *path, int flags,
serrno = errno;
goto err_end;
}
- } else if (fs_ctx->fs_sm == SM_PASSTHROUGH) {
+ } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) ||
+ (fs_ctx->fs_sm == SM_NONE)) {
fd = open(rpath(fs_ctx, path), flags, credp->fc_mode);
if (fd == -1) {
return fd;
@@ -372,15 +389,23 @@ static int local_symlink(FsContext *fs_ctx, const char *oldpath,
serrno = errno;
goto err_end;
}
- } else if (fs_ctx->fs_sm == SM_PASSTHROUGH) {
+ } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) ||
+ (fs_ctx->fs_sm == SM_NONE)) {
err = symlink(oldpath, rpath(fs_ctx, newpath));
if (err) {
return err;
}
err = lchown(rpath(fs_ctx, newpath), credp->fc_uid, credp->fc_gid);
if (err == -1) {
- serrno = errno;
- goto err_end;
+ /*
+ * If we fail to change ownership and if we are
+ * using security model none. Ignore the error
+ */
+ if (fs_ctx->fs_sm != SM_NONE) {
+ serrno = errno;
+ goto err_end;
+ } else
+ err = 0;
}
}
return err;
@@ -426,9 +451,6 @@ static int local_rename(FsContext *ctx, const char *oldpath,
int err;
tmp = qemu_strdup(rpath(ctx, oldpath));
- if (tmp == NULL) {
- return -1;
- }
err = rename(tmp, rpath(ctx, newpath));
if (err == -1) {
@@ -445,18 +467,22 @@ static int local_rename(FsContext *ctx, const char *oldpath,
static int local_chown(FsContext *fs_ctx, const char *path, FsCred *credp)
{
- if (fs_ctx->fs_sm == SM_MAPPED) {
+ if ((credp->fc_uid == -1 && credp->fc_gid == -1) ||
+ (fs_ctx->fs_sm == SM_PASSTHROUGH)) {
+ return lchown(rpath(fs_ctx, path), credp->fc_uid, credp->fc_gid);
+ } else if (fs_ctx->fs_sm == SM_MAPPED) {
return local_set_xattr(rpath(fs_ctx, path), credp);
- } else if (fs_ctx->fs_sm == SM_PASSTHROUGH) {
+ } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) ||
+ (fs_ctx->fs_sm == SM_NONE)) {
return lchown(rpath(fs_ctx, path), credp->fc_uid, credp->fc_gid);
}
return -1;
}
-static int local_utime(FsContext *ctx, const char *path,
- const struct utimbuf *buf)
+static int local_utimensat(FsContext *s, const char *path,
+ const struct timespec *buf)
{
- return utime(rpath(ctx, path), buf);
+ return utimensat(AT_FDCWD, rpath(s, path), buf, AT_SYMLINK_NOFOLLOW);
}
static int local_remove(FsContext *ctx, const char *path)
@@ -469,6 +495,36 @@ static int local_fsync(FsContext *ctx, int fd)
return fsync(fd);
}
+static int local_statfs(FsContext *s, const char *path, struct statfs *stbuf)
+{
+ return statfs(rpath(s, path), stbuf);
+}
+
+static ssize_t local_lgetxattr(FsContext *ctx, const char *path,
+ const char *name, void *value, size_t size)
+{
+ return v9fs_get_xattr(ctx, path, name, value, size);
+}
+
+static ssize_t local_llistxattr(FsContext *ctx, const char *path,
+ void *value, size_t size)
+{
+ return v9fs_list_xattr(ctx, path, value, size);
+}
+
+static int local_lsetxattr(FsContext *ctx, const char *path, const char *name,
+ void *value, size_t size, int flags)
+{
+ return v9fs_set_xattr(ctx, path, name, value, size, flags);
+}
+
+static int local_lremovexattr(FsContext *ctx,
+ const char *path, const char *name)
+{
+ return v9fs_remove_xattr(ctx, path, name);
+}
+
+
FileOperations local_ops = {
.lstat = local_lstat,
.readlink = local_readlink,
@@ -480,9 +536,8 @@ FileOperations local_ops = {
.telldir = local_telldir,
.readdir = local_readdir,
.seekdir = local_seekdir,
- .readv = local_readv,
- .lseek = local_lseek,
- .writev = local_writev,
+ .preadv = local_preadv,
+ .pwritev = local_pwritev,
.chmod = local_chmod,
.mknod = local_mknod,
.mkdir = local_mkdir,
@@ -493,7 +548,12 @@ FileOperations local_ops = {
.truncate = local_truncate,
.rename = local_rename,
.chown = local_chown,
- .utime = local_utime,
+ .utimensat = local_utimensat,
.remove = local_remove,
.fsync = local_fsync,
+ .statfs = local_statfs,
+ .lgetxattr = local_lgetxattr,
+ .llistxattr = local_llistxattr,
+ .lsetxattr = local_lsetxattr,
+ .lremovexattr = local_lremovexattr,
};
diff --git a/hw/virtio-9p-posix-acl.c b/hw/virtio-9p-posix-acl.c
new file mode 100644
index 0000000000..3978d0cf71
--- /dev/null
+++ b/hw/virtio-9p-posix-acl.c
@@ -0,0 +1,140 @@
+/*
+ * Virtio 9p system.posix* xattr callback
+ *
+ * Copyright IBM, Corp. 2010
+ *
+ * Authors:
+ * Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#include <sys/types.h>
+#include <attr/xattr.h>
+#include "virtio.h"
+#include "virtio-9p.h"
+#include "file-op-9p.h"
+#include "virtio-9p-xattr.h"
+
+#define MAP_ACL_ACCESS "user.virtfs.system.posix_acl_access"
+#define MAP_ACL_DEFAULT "user.virtfs.system.posix_acl_default"
+#define ACL_ACCESS "system.posix_acl_access"
+#define ACL_DEFAULT "system.posix_acl_default"
+
+static ssize_t mp_pacl_getxattr(FsContext *ctx, const char *path,
+ const char *name, void *value, size_t size)
+{
+ return lgetxattr(rpath(ctx, path), MAP_ACL_ACCESS, value, size);
+}
+
+static ssize_t mp_pacl_listxattr(FsContext *ctx, const char *path,
+ char *name, void *value, size_t osize)
+{
+ ssize_t len = sizeof(ACL_ACCESS);
+
+ if (!value) {
+ return len;
+ }
+
+ if (osize < len) {
+ errno = ERANGE;
+ return -1;
+ }
+
+ strncpy(value, ACL_ACCESS, len);
+ return 0;
+}
+
+static int mp_pacl_setxattr(FsContext *ctx, const char *path, const char *name,
+ void *value, size_t size, int flags)
+{
+ return lsetxattr(rpath(ctx, path), MAP_ACL_ACCESS, value, size, flags);
+}
+
+static int mp_pacl_removexattr(FsContext *ctx,
+ const char *path, const char *name)
+{
+ int ret;
+ ret = lremovexattr(rpath(ctx, path), MAP_ACL_ACCESS);
+ if (ret == -1 && errno == ENODATA) {
+ /*
+ * We don't get ENODATA error when trying to remote a
+ * posix acl that is not present. So don't throw the error
+ * even in case of mapped security model
+ */
+ errno = 0;
+ ret = 0;
+ }
+ return ret;
+}
+
+static ssize_t mp_dacl_getxattr(FsContext *ctx, const char *path,
+ const char *name, void *value, size_t size)
+{
+ return lgetxattr(rpath(ctx, path), MAP_ACL_DEFAULT, value, size);
+}
+
+static ssize_t mp_dacl_listxattr(FsContext *ctx, const char *path,
+ char *name, void *value, size_t osize)
+{
+ ssize_t len = sizeof(ACL_DEFAULT);
+
+ if (!value) {
+ return len;
+ }
+
+ if (osize < len) {
+ errno = ERANGE;
+ return -1;
+ }
+
+ strncpy(value, ACL_DEFAULT, len);
+ return 0;
+}
+
+static int mp_dacl_setxattr(FsContext *ctx, const char *path, const char *name,
+ void *value, size_t size, int flags)
+{
+ return lsetxattr(rpath(ctx, path), MAP_ACL_DEFAULT, value, size, flags);
+}
+
+static int mp_dacl_removexattr(FsContext *ctx,
+ const char *path, const char *name)
+{
+ return lremovexattr(rpath(ctx, path), MAP_ACL_DEFAULT);
+}
+
+
+XattrOperations mapped_pacl_xattr = {
+ .name = "system.posix_acl_access",
+ .getxattr = mp_pacl_getxattr,
+ .setxattr = mp_pacl_setxattr,
+ .listxattr = mp_pacl_listxattr,
+ .removexattr = mp_pacl_removexattr,
+};
+
+XattrOperations mapped_dacl_xattr = {
+ .name = "system.posix_acl_default",
+ .getxattr = mp_dacl_getxattr,
+ .setxattr = mp_dacl_setxattr,
+ .listxattr = mp_dacl_listxattr,
+ .removexattr = mp_dacl_removexattr,
+};
+
+XattrOperations passthrough_acl_xattr = {
+ .name = "system.posix_acl_",
+ .getxattr = pt_getxattr,
+ .setxattr = pt_setxattr,
+ .listxattr = pt_listxattr,
+ .removexattr = pt_removexattr,
+};
+
+XattrOperations none_acl_xattr = {
+ .name = "system.posix_acl_",
+ .getxattr = notsup_getxattr,
+ .setxattr = notsup_setxattr,
+ .listxattr = notsup_listxattr,
+ .removexattr = notsup_removexattr,
+};
diff --git a/hw/virtio-9p-xattr-user.c b/hw/virtio-9p-xattr-user.c
new file mode 100644
index 0000000000..faa02a1911
--- /dev/null
+++ b/hw/virtio-9p-xattr-user.c
@@ -0,0 +1,109 @@
+/*
+ * Virtio 9p user. xattr callback
+ *
+ * Copyright IBM, Corp. 2010
+ *
+ * Authors:
+ * Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#include <sys/types.h>
+#include "virtio.h"
+#include "virtio-9p.h"
+#include "file-op-9p.h"
+#include "virtio-9p-xattr.h"
+
+
+static ssize_t mp_user_getxattr(FsContext *ctx, const char *path,
+ const char *name, void *value, size_t size)
+{
+ if (strncmp(name, "user.virtfs.", 12) == 0) {
+ /*
+ * Don't allow fetch of user.virtfs namesapce
+ * in case of mapped security
+ */
+ errno = ENOATTR;
+ return -1;
+ }
+ return lgetxattr(rpath(ctx, path), name, value, size);
+}
+
+static ssize_t mp_user_listxattr(FsContext *ctx, const char *path,
+ char *name, void *value, size_t size)
+{
+ int name_size = strlen(name) + 1;
+ if (strncmp(name, "user.virtfs.", 12) == 0) {
+
+ /* check if it is a mapped posix acl */
+ if (strncmp(name, "user.virtfs.system.posix_acl_", 29) == 0) {
+ /* adjust the name and size */
+ name += 12;
+ name_size -= 12;
+ } else {
+ /*
+ * Don't allow fetch of user.virtfs namesapce
+ * in case of mapped security
+ */
+ return 0;
+ }
+ }
+ if (!value) {
+ return name_size;
+ }
+
+ if (size < name_size) {
+ errno = ERANGE;
+ return -1;
+ }
+
+ strncpy(value, name, name_size);
+ return name_size;
+}
+
+static int mp_user_setxattr(FsContext *ctx, const char *path, const char *name,
+ void *value, size_t size, int flags)
+{
+ if (strncmp(name, "user.virtfs.", 12) == 0) {
+ /*
+ * Don't allow fetch of user.virtfs namesapce
+ * in case of mapped security
+ */
+ errno = EACCES;
+ return -1;
+ }
+ return lsetxattr(rpath(ctx, path), name, value, size, flags);
+}
+
+static int mp_user_removexattr(FsContext *ctx,
+ const char *path, const char *name)
+{
+ if (strncmp(name, "user.virtfs.", 12) == 0) {
+ /*
+ * Don't allow fetch of user.virtfs namesapce
+ * in case of mapped security
+ */
+ errno = EACCES;
+ return -1;
+ }
+ return lremovexattr(rpath(ctx, path), name);
+}
+
+XattrOperations mapped_user_xattr = {
+ .name = "user.",
+ .getxattr = mp_user_getxattr,
+ .setxattr = mp_user_setxattr,
+ .listxattr = mp_user_listxattr,
+ .removexattr = mp_user_removexattr,
+};
+
+XattrOperations passthrough_user_xattr = {
+ .name = "user.",
+ .getxattr = pt_getxattr,
+ .setxattr = pt_setxattr,
+ .listxattr = pt_listxattr,
+ .removexattr = pt_removexattr,
+};
diff --git a/hw/virtio-9p-xattr.c b/hw/virtio-9p-xattr.c
new file mode 100644
index 0000000000..175f372c39
--- /dev/null
+++ b/hw/virtio-9p-xattr.c
@@ -0,0 +1,156 @@
+/*
+ * Virtio 9p xattr callback
+ *
+ * Copyright IBM, Corp. 2010
+ *
+ * Authors:
+ * Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#include "virtio.h"
+#include "virtio-9p.h"
+#include "file-op-9p.h"
+#include "virtio-9p-xattr.h"
+
+
+static XattrOperations *get_xattr_operations(XattrOperations **h,
+ const char *name)
+{
+ XattrOperations *xops;
+ for (xops = *(h)++; xops != NULL; xops = *(h)++) {
+ if (!strncmp(name, xops->name, strlen(xops->name))) {
+ return xops;
+ }
+ }
+ return NULL;
+}
+
+ssize_t v9fs_get_xattr(FsContext *ctx, const char *path,
+ const char *name, void *value, size_t size)
+{
+ XattrOperations *xops = get_xattr_operations(ctx->xops, name);
+ if (xops) {
+ return xops->getxattr(ctx, path, name, value, size);
+ }
+ errno = -EOPNOTSUPP;
+ return -1;
+}
+
+ssize_t pt_listxattr(FsContext *ctx, const char *path,
+ char *name, void *value, size_t size)
+{
+ int name_size = strlen(name) + 1;
+ if (!value) {
+ return name_size;
+ }
+
+ if (size < name_size) {
+ errno = ERANGE;
+ return -1;
+ }
+
+ strncpy(value, name, name_size);
+ return name_size;
+}
+
+
+/*
+ * Get the list and pass to each layer to find out whether
+ * to send the data or not
+ */
+ssize_t v9fs_list_xattr(FsContext *ctx, const char *path,
+ void *value, size_t vsize)
+{
+ ssize_t size = 0;
+ void *ovalue = value;
+ XattrOperations *xops;
+ char *orig_value, *orig_value_start;
+ ssize_t xattr_len, parsed_len = 0, attr_len;
+
+ /* Get the actual len */
+ xattr_len = llistxattr(rpath(ctx, path), value, 0);
+
+ /* Now fetch the xattr and find the actual size */
+ orig_value = qemu_malloc(xattr_len);
+ xattr_len = llistxattr(rpath(ctx, path), orig_value, xattr_len);
+
+ /* store the orig pointer */
+ orig_value_start = orig_value;
+ while (xattr_len > parsed_len) {
+ xops = get_xattr_operations(ctx->xops, orig_value);
+ if (!xops) {
+ goto next_entry;
+ }
+
+ if (!value) {
+ size += xops->listxattr(ctx, path, orig_value, value, vsize);
+ } else {
+ size = xops->listxattr(ctx, path, orig_value, value, vsize);
+ if (size < 0) {
+ goto err_out;
+ }
+ value += size;
+ vsize -= size;
+ }
+next_entry:
+ /* Got the next entry */
+ attr_len = strlen(orig_value) + 1;
+ parsed_len += attr_len;
+ orig_value += attr_len;
+ }
+ if (value) {
+ size = value - ovalue;
+ }
+
+err_out:
+ qemu_free(orig_value_start);
+ return size;
+}
+
+int v9fs_set_xattr(FsContext *ctx, const char *path, const char *name,
+ void *value, size_t size, int flags)
+{
+ XattrOperations *xops = get_xattr_operations(ctx->xops, name);
+ if (xops) {
+ return xops->setxattr(ctx, path, name, value, size, flags);
+ }
+ errno = -EOPNOTSUPP;
+ return -1;
+
+}
+
+int v9fs_remove_xattr(FsContext *ctx,
+ const char *path, const char *name)
+{
+ XattrOperations *xops = get_xattr_operations(ctx->xops, name);
+ if (xops) {
+ return xops->removexattr(ctx, path, name);
+ }
+ errno = -EOPNOTSUPP;
+ return -1;
+
+}
+
+XattrOperations *mapped_xattr_ops[] = {
+ &mapped_user_xattr,
+ &mapped_pacl_xattr,
+ &mapped_dacl_xattr,
+ NULL,
+};
+
+XattrOperations *passthrough_xattr_ops[] = {
+ &passthrough_user_xattr,
+ &passthrough_acl_xattr,
+ NULL,
+};
+
+/* for .user none model should be same as passthrough */
+XattrOperations *none_xattr_ops[] = {
+ &passthrough_user_xattr,
+ &none_acl_xattr,
+ NULL,
+};
diff --git a/hw/virtio-9p-xattr.h b/hw/virtio-9p-xattr.h
new file mode 100644
index 0000000000..a6e31a152f
--- /dev/null
+++ b/hw/virtio-9p-xattr.h
@@ -0,0 +1,103 @@
+/*
+ * Virtio 9p
+ *
+ * Copyright IBM, Corp. 2010
+ *
+ * Authors:
+ * Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+#ifndef _QEMU_VIRTIO_9P_XATTR_H
+#define _QEMU_VIRTIO_9P_XATTR_H
+
+#include <attr/xattr.h>
+
+typedef struct xattr_operations
+{
+ const char *name;
+ ssize_t (*getxattr)(FsContext *ctx, const char *path,
+ const char *name, void *value, size_t size);
+ ssize_t (*listxattr)(FsContext *ctx, const char *path,
+ char *name, void *value, size_t size);
+ int (*setxattr)(FsContext *ctx, const char *path, const char *name,
+ void *value, size_t size, int flags);
+ int (*removexattr)(FsContext *ctx,
+ const char *path, const char *name);
+} XattrOperations;
+
+
+extern XattrOperations mapped_user_xattr;
+extern XattrOperations passthrough_user_xattr;
+
+extern XattrOperations mapped_pacl_xattr;
+extern XattrOperations mapped_dacl_xattr;
+extern XattrOperations passthrough_acl_xattr;
+extern XattrOperations none_acl_xattr;
+
+extern XattrOperations *mapped_xattr_ops[];
+extern XattrOperations *passthrough_xattr_ops[];
+extern XattrOperations *none_xattr_ops[];
+
+extern ssize_t v9fs_get_xattr(FsContext *ctx, const char *path,
+ const char *name, void *value, size_t size);
+extern ssize_t v9fs_list_xattr(FsContext *ctx, const char *path,
+ void *value, size_t vsize);
+extern int v9fs_set_xattr(FsContext *ctx, const char *path, const char *name,
+ void *value, size_t size, int flags);
+extern int v9fs_remove_xattr(FsContext *ctx,
+ const char *path, const char *name);
+extern ssize_t pt_listxattr(FsContext *ctx, const char *path,
+ char *name, void *value, size_t size);
+
+static inline ssize_t pt_getxattr(FsContext *ctx, const char *path,
+ const char *name, void *value, size_t size)
+{
+ return lgetxattr(rpath(ctx, path), name, value, size);
+}
+
+static inline int pt_setxattr(FsContext *ctx, const char *path,
+ const char *name, void *value,
+ size_t size, int flags)
+{
+ return lsetxattr(rpath(ctx, path), name, value, size, flags);
+}
+
+static inline int pt_removexattr(FsContext *ctx,
+ const char *path, const char *name)
+{
+ return lremovexattr(rpath(ctx, path), name);
+}
+
+static inline ssize_t notsup_getxattr(FsContext *ctx, const char *path,
+ const char *name, void *value,
+ size_t size)
+{
+ errno = ENOTSUP;
+ return -1;
+}
+
+static inline int notsup_setxattr(FsContext *ctx, const char *path,
+ const char *name, void *value,
+ size_t size, int flags)
+{
+ errno = ENOTSUP;
+ return -1;
+}
+
+static inline ssize_t notsup_listxattr(FsContext *ctx, const char *path,
+ char *name, void *value, size_t size)
+{
+ return 0;
+}
+
+static inline int notsup_removexattr(FsContext *ctx,
+ const char *path, const char *name)
+{
+ errno = ENOTSUP;
+ return -1;
+}
+
+#endif
diff --git a/hw/virtio-9p.c b/hw/virtio-9p.c
index f8c85c3d28..daade77ed9 100644
--- a/hw/virtio-9p.c
+++ b/hw/virtio-9p.c
@@ -17,8 +17,8 @@
#include "virtio-9p.h"
#include "fsdev/qemu-fsdev.h"
#include "virtio-9p-debug.h"
+#include "virtio-9p-xattr.h"
-int dotu = 1;
int debug_9p_pdu;
enum {
@@ -135,21 +135,16 @@ static void v9fs_do_seekdir(V9fsState *s, DIR *dir, off_t off)
return s->ops->seekdir(&s->ctx, dir, off);
}
-static int v9fs_do_readv(V9fsState *s, int fd, const struct iovec *iov,
- int iovcnt)
+static int v9fs_do_preadv(V9fsState *s, int fd, const struct iovec *iov,
+ int iovcnt, int64_t offset)
{
- return s->ops->readv(&s->ctx, fd, iov, iovcnt);
+ return s->ops->preadv(&s->ctx, fd, iov, iovcnt, offset);
}
-static off_t v9fs_do_lseek(V9fsState *s, int fd, off_t offset, int whence)
+static int v9fs_do_pwritev(V9fsState *s, int fd, const struct iovec *iov,
+ int iovcnt, int64_t offset)
{
- return s->ops->lseek(&s->ctx, fd, offset, whence);
-}
-
-static int v9fs_do_writev(V9fsState *s, int fd, const struct iovec *iov,
- int iovcnt)
-{
- return s->ops->writev(&s->ctx, fd, iov, iovcnt);
+ return s->ops->pwritev(&s->ctx, fd, iov, iovcnt, offset);
}
static int v9fs_do_chmod(V9fsState *s, V9fsString *path, mode_t mode)
@@ -160,26 +155,29 @@ static int v9fs_do_chmod(V9fsState *s, V9fsString *path, mode_t mode)
return s->ops->chmod(&s->ctx, path->data, &cred);
}
-static int v9fs_do_mknod(V9fsState *s, V9fsCreateState *vs, mode_t mode,
- dev_t dev)
+static int v9fs_do_mknod(V9fsState *s, char *name,
+ mode_t mode, dev_t dev, uid_t uid, gid_t gid)
{
FsCred cred;
cred_init(&cred);
- cred.fc_uid = vs->fidp->uid;
+ cred.fc_uid = uid;
+ cred.fc_gid = gid;
cred.fc_mode = mode;
cred.fc_rdev = dev;
- return s->ops->mknod(&s->ctx, vs->fullname.data, &cred);
+ return s->ops->mknod(&s->ctx, name, &cred);
}
-static int v9fs_do_mkdir(V9fsState *s, V9fsCreateState *vs)
+static int v9fs_do_mkdir(V9fsState *s, char *name, mode_t mode,
+ uid_t uid, gid_t gid)
{
FsCred cred;
cred_init(&cred);
- cred.fc_uid = vs->fidp->uid;
- cred.fc_mode = vs->perm & 0777;
+ cred.fc_uid = uid;
+ cred.fc_gid = gid;
+ cred.fc_mode = mode;
- return s->ops->mkdir(&s->ctx, vs->fullname.data, &cred);
+ return s->ops->mkdir(&s->ctx, name, &cred);
}
static int v9fs_do_fstat(V9fsState *s, int fd, struct stat *stbuf)
@@ -187,28 +185,30 @@ static int v9fs_do_fstat(V9fsState *s, int fd, struct stat *stbuf)
return s->ops->fstat(&s->ctx, fd, stbuf);
}
-static int v9fs_do_open2(V9fsState *s, V9fsCreateState *vs)
+static int v9fs_do_open2(V9fsState *s, char *fullname, uid_t uid, gid_t gid,
+ int flags, int mode)
{
FsCred cred;
- int flags;
cred_init(&cred);
- cred.fc_uid = vs->fidp->uid;
- cred.fc_mode = vs->perm & 0777;
- flags = omode_to_uflags(vs->mode) | O_CREAT;
+ cred.fc_uid = uid;
+ cred.fc_gid = gid;
+ cred.fc_mode = mode & 07777;
+ flags = flags;
- return s->ops->open2(&s->ctx, vs->fullname.data, flags, &cred);
+ return s->ops->open2(&s->ctx, fullname, flags, &cred);
}
-static int v9fs_do_symlink(V9fsState *s, V9fsCreateState *vs)
+static int v9fs_do_symlink(V9fsState *s, V9fsFidState *fidp,
+ const char *oldpath, const char *newpath, gid_t gid)
{
FsCred cred;
cred_init(&cred);
- cred.fc_uid = vs->fidp->uid;
- cred.fc_mode = vs->perm | 0777;
+ cred.fc_uid = fidp->uid;
+ cred.fc_gid = gid;
+ cred.fc_mode = 0777;
- return s->ops->symlink(&s->ctx, vs->extension.data, vs->fullname.data,
- &cred);
+ return s->ops->symlink(&s->ctx, oldpath, newpath, &cred);
}
static int v9fs_do_link(V9fsState *s, V9fsString *oldpath, V9fsString *newpath)
@@ -237,10 +237,10 @@ static int v9fs_do_chown(V9fsState *s, V9fsString *path, uid_t uid, gid_t gid)
return s->ops->chown(&s->ctx, path->data, &cred);
}
-static int v9fs_do_utime(V9fsState *s, V9fsString *path,
- const struct utimbuf *buf)
+static int v9fs_do_utimensat(V9fsState *s, V9fsString *path,
+ const struct timespec times[2])
{
- return s->ops->utime(&s->ctx, path->data, buf);
+ return s->ops->utimensat(&s->ctx, path->data, times);
}
static int v9fs_do_remove(V9fsState *s, V9fsString *path)
@@ -253,6 +253,42 @@ static int v9fs_do_fsync(V9fsState *s, int fd)
return s->ops->fsync(&s->ctx, fd);
}
+static int v9fs_do_statfs(V9fsState *s, V9fsString *path, struct statfs *stbuf)
+{
+ return s->ops->statfs(&s->ctx, path->data, stbuf);
+}
+
+static ssize_t v9fs_do_lgetxattr(V9fsState *s, V9fsString *path,
+ V9fsString *xattr_name,
+ void *value, size_t size)
+{
+ return s->ops->lgetxattr(&s->ctx, path->data,
+ xattr_name->data, value, size);
+}
+
+static ssize_t v9fs_do_llistxattr(V9fsState *s, V9fsString *path,
+ void *value, size_t size)
+{
+ return s->ops->llistxattr(&s->ctx, path->data,
+ value, size);
+}
+
+static int v9fs_do_lsetxattr(V9fsState *s, V9fsString *path,
+ V9fsString *xattr_name,
+ void *value, size_t size, int flags)
+{
+ return s->ops->lsetxattr(&s->ctx, path->data,
+ xattr_name->data, value, size, flags);
+}
+
+static int v9fs_do_lremovexattr(V9fsState *s, V9fsString *path,
+ V9fsString *xattr_name)
+{
+ return s->ops->lremovexattr(&s->ctx, path->data,
+ xattr_name->data);
+}
+
+
static void v9fs_string_init(V9fsString *str)
{
str->data = NULL;
@@ -285,6 +321,14 @@ static int number_to_string(void *arg, char type)
} while (num);
break;
}
+ case 'U': {
+ unsigned long num = *(unsigned long *)arg;
+ do {
+ ret++;
+ num = num/10;
+ } while (num);
+ break;
+ }
default:
printf("Number_to_string: Unknown number format\n");
return -1;
@@ -293,7 +337,8 @@ static int number_to_string(void *arg, char type)
return ret;
}
-static int v9fs_string_alloc_printf(char **strp, const char *fmt, va_list ap)
+static int GCC_FMT_ATTR(2, 0)
+v9fs_string_alloc_printf(char **strp, const char *fmt, va_list ap)
{
va_list ap2;
char *iter = (char *)fmt;
@@ -301,6 +346,7 @@ static int v9fs_string_alloc_printf(char **strp, const char *fmt, va_list ap)
int nr_args = 0;
char *arg_char_ptr;
unsigned int arg_uint;
+ unsigned long arg_ulong;
/* Find the number of %'s that denotes an argument */
for (iter = strstr(iter, "%"); iter; iter = strstr(iter, "%")) {
@@ -326,6 +372,14 @@ static int v9fs_string_alloc_printf(char **strp, const char *fmt, va_list ap)
arg_uint = va_arg(ap2, unsigned int);
len += number_to_string((void *)&arg_uint, 'u');
break;
+ case 'l':
+ if (*++iter == 'u') {
+ arg_ulong = va_arg(ap2, unsigned long);
+ len += number_to_string((void *)&arg_ulong, 'U');
+ } else {
+ return -1;
+ }
+ break;
case 's':
arg_char_ptr = va_arg(ap2, char *);
len += strlen(arg_char_ptr);
@@ -347,7 +401,8 @@ alloc_print:
return vsprintf(*strp, fmt, ap);
}
-static void v9fs_string_sprintf(V9fsString *str, const char *fmt, ...)
+static void GCC_FMT_ATTR(2, 3)
+v9fs_string_sprintf(V9fsString *str, const char *fmt, ...)
{
va_list ap;
int err;
@@ -398,8 +453,7 @@ static V9fsFidState *alloc_fid(V9fsState *s, int32_t fid)
f = qemu_mallocz(sizeof(V9fsFidState));
f->fid = fid;
- f->fd = -1;
- f->dir = NULL;
+ f->fid_type = P9_FID_NONE;
f->next = s->fid_list;
s->fid_list = f;
@@ -407,8 +461,43 @@ static V9fsFidState *alloc_fid(V9fsState *s, int32_t fid)
return f;
}
+static int v9fs_xattr_fid_clunk(V9fsState *s, V9fsFidState *fidp)
+{
+ int retval = 0;
+
+ if (fidp->fs.xattr.copied_len == -1) {
+ /* getxattr/listxattr fid */
+ goto free_value;
+ }
+ /*
+ * if this is fid for setxattr. clunk should
+ * result in setxattr localcall
+ */
+ if (fidp->fs.xattr.len != fidp->fs.xattr.copied_len) {
+ /* clunk after partial write */
+ retval = -EINVAL;
+ goto free_out;
+ }
+ if (fidp->fs.xattr.len) {
+ retval = v9fs_do_lsetxattr(s, &fidp->path, &fidp->fs.xattr.name,
+ fidp->fs.xattr.value,
+ fidp->fs.xattr.len,
+ fidp->fs.xattr.flags);
+ } else {
+ retval = v9fs_do_lremovexattr(s, &fidp->path, &fidp->fs.xattr.name);
+ }
+free_out:
+ v9fs_string_free(&fidp->fs.xattr.name);
+free_value:
+ if (fidp->fs.xattr.value) {
+ qemu_free(fidp->fs.xattr.value);
+ }
+ return retval;
+}
+
static int free_fid(V9fsState *s, int32_t fid)
{
+ int retval = 0;
V9fsFidState **fidpp, *fidp;
for (fidpp = &s->fid_list; *fidpp; fidpp = &(*fidpp)->next) {
@@ -424,16 +513,17 @@ static int free_fid(V9fsState *s, int32_t fid)
fidp = *fidpp;
*fidpp = fidp->next;
- if (fidp->fd != -1) {
- v9fs_do_close(s, fidp->fd);
- }
- if (fidp->dir) {
- v9fs_do_closedir(s, fidp->dir);
+ if (fidp->fid_type == P9_FID_FILE) {
+ v9fs_do_close(s, fidp->fs.fd);
+ } else if (fidp->fid_type == P9_FID_DIR) {
+ v9fs_do_closedir(s, fidp->fs.dir);
+ } else if (fidp->fid_type == P9_FID_XATTR) {
+ retval = v9fs_xattr_fid_clunk(s, fidp);
}
v9fs_string_free(&fidp->path);
qemu_free(fidp);
- return 0;
+ return retval;
}
#define P9_QID_TYPE_DIR 0x80
@@ -660,6 +750,15 @@ static size_t pdu_unmarshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...)
&statp->n_muid);
break;
}
+ case 'I': {
+ V9fsIattr *iattr = va_arg(ap, V9fsIattr *);
+ offset += pdu_unmarshal(pdu, offset, "ddddqqqqq",
+ &iattr->valid, &iattr->mode,
+ &iattr->uid, &iattr->gid, &iattr->size,
+ &iattr->atime_sec, &iattr->atime_nsec,
+ &iattr->mtime_sec, &iattr->mtime_nsec);
+ break;
+ }
default:
break;
}
@@ -731,6 +830,21 @@ static size_t pdu_marshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...)
statp->n_gid, statp->n_muid);
break;
}
+ case 'A': {
+ V9fsStatDotl *statp = va_arg(ap, V9fsStatDotl *);
+ offset += pdu_marshal(pdu, offset, "qQdddqqqqqqqqqqqqqqq",
+ statp->st_result_mask,
+ &statp->qid, statp->st_mode,
+ statp->st_uid, statp->st_gid,
+ statp->st_nlink, statp->st_rdev,
+ statp->st_size, statp->st_blksize, statp->st_blocks,
+ statp->st_atime_sec, statp->st_atime_nsec,
+ statp->st_mtime_sec, statp->st_mtime_nsec,
+ statp->st_ctime_sec, statp->st_ctime_nsec,
+ statp->st_btime_sec, statp->st_btime_nsec,
+ statp->st_gen, statp->st_data_version);
+ break;
+ }
default:
break;
}
@@ -745,19 +859,24 @@ static void complete_pdu(V9fsState *s, V9fsPDU *pdu, ssize_t len)
int8_t id = pdu->id + 1; /* Response */
if (len < 0) {
- V9fsString str;
int err = -len;
+ len = 7;
- str.data = strerror(err);
- str.size = strlen(str.data);
+ if (s->proto_version != V9FS_PROTO_2000L) {
+ V9fsString str;
- len = 7;
- len += pdu_marshal(pdu, len, "s", &str);
- if (dotu) {
- len += pdu_marshal(pdu, len, "d", err);
+ str.data = strerror(err);
+ str.size = strlen(str.data);
+
+ len += pdu_marshal(pdu, len, "s", &str);
+ id = P9_RERROR;
}
- id = P9_RERROR;
+ len += pdu_marshal(pdu, len, "d", err);
+
+ if (s->proto_version == V9FS_PROTO_2000L) {
+ id = P9_RLERROR;
+ }
}
/* fill out the header */
@@ -785,22 +904,20 @@ static mode_t v9mode_to_mode(uint32_t mode, V9fsString *extension)
ret |= S_IFDIR;
}
- if (dotu) {
- if (mode & P9_STAT_MODE_SYMLINK) {
- ret |= S_IFLNK;
- }
- if (mode & P9_STAT_MODE_SOCKET) {
- ret |= S_IFSOCK;
- }
- if (mode & P9_STAT_MODE_NAMED_PIPE) {
- ret |= S_IFIFO;
- }
- if (mode & P9_STAT_MODE_DEVICE) {
- if (extension && extension->data[0] == 'c') {
- ret |= S_IFCHR;
- } else {
- ret |= S_IFBLK;
- }
+ if (mode & P9_STAT_MODE_SYMLINK) {
+ ret |= S_IFLNK;
+ }
+ if (mode & P9_STAT_MODE_SOCKET) {
+ ret |= S_IFSOCK;
+ }
+ if (mode & P9_STAT_MODE_NAMED_PIPE) {
+ ret |= S_IFIFO;
+ }
+ if (mode & P9_STAT_MODE_DEVICE) {
+ if (extension && extension->data[0] == 'c') {
+ ret |= S_IFCHR;
+ } else {
+ ret |= S_IFBLK;
}
}
@@ -863,34 +980,32 @@ static uint32_t stat_to_v9mode(const struct stat *stbuf)
mode |= P9_STAT_MODE_DIR;
}
- if (dotu) {
- if (S_ISLNK(stbuf->st_mode)) {
- mode |= P9_STAT_MODE_SYMLINK;
- }
+ if (S_ISLNK(stbuf->st_mode)) {
+ mode |= P9_STAT_MODE_SYMLINK;
+ }
- if (S_ISSOCK(stbuf->st_mode)) {
- mode |= P9_STAT_MODE_SOCKET;
- }
+ if (S_ISSOCK(stbuf->st_mode)) {
+ mode |= P9_STAT_MODE_SOCKET;
+ }
- if (S_ISFIFO(stbuf->st_mode)) {
- mode |= P9_STAT_MODE_NAMED_PIPE;
- }
+ if (S_ISFIFO(stbuf->st_mode)) {
+ mode |= P9_STAT_MODE_NAMED_PIPE;
+ }
- if (S_ISBLK(stbuf->st_mode) || S_ISCHR(stbuf->st_mode)) {
- mode |= P9_STAT_MODE_DEVICE;
- }
+ if (S_ISBLK(stbuf->st_mode) || S_ISCHR(stbuf->st_mode)) {
+ mode |= P9_STAT_MODE_DEVICE;
+ }
- if (stbuf->st_mode & S_ISUID) {
- mode |= P9_STAT_MODE_SETUID;
- }
+ if (stbuf->st_mode & S_ISUID) {
+ mode |= P9_STAT_MODE_SETUID;
+ }
- if (stbuf->st_mode & S_ISGID) {
- mode |= P9_STAT_MODE_SETGID;
- }
+ if (stbuf->st_mode & S_ISGID) {
+ mode |= P9_STAT_MODE_SETGID;
+ }
- if (stbuf->st_mode & S_ISVTX) {
- mode |= P9_STAT_MODE_SETVTX;
- }
+ if (stbuf->st_mode & S_ISVTX) {
+ mode |= P9_STAT_MODE_SETVTX;
}
return mode;
@@ -915,29 +1030,27 @@ static int stat_to_v9stat(V9fsState *s, V9fsString *name,
v9fs_string_null(&v9stat->gid);
v9fs_string_null(&v9stat->muid);
- if (dotu) {
- v9stat->n_uid = stbuf->st_uid;
- v9stat->n_gid = stbuf->st_gid;
- v9stat->n_muid = 0;
+ v9stat->n_uid = stbuf->st_uid;
+ v9stat->n_gid = stbuf->st_gid;
+ v9stat->n_muid = 0;
- v9fs_string_null(&v9stat->extension);
+ v9fs_string_null(&v9stat->extension);
- if (v9stat->mode & P9_STAT_MODE_SYMLINK) {
- err = v9fs_do_readlink(s, name, &v9stat->extension);
- if (err == -1) {
- err = -errno;
- return err;
- }
- v9stat->extension.data[err] = 0;
- v9stat->extension.size = err;
- } else if (v9stat->mode & P9_STAT_MODE_DEVICE) {
- v9fs_string_sprintf(&v9stat->extension, "%c %u %u",
- S_ISCHR(stbuf->st_mode) ? 'c' : 'b',
- major(stbuf->st_rdev), minor(stbuf->st_rdev));
- } else if (S_ISDIR(stbuf->st_mode) || S_ISREG(stbuf->st_mode)) {
- v9fs_string_sprintf(&v9stat->extension, "%s %u",
- "HARDLINKCOUNT", stbuf->st_nlink);
+ if (v9stat->mode & P9_STAT_MODE_SYMLINK) {
+ err = v9fs_do_readlink(s, name, &v9stat->extension);
+ if (err == -1) {
+ err = -errno;
+ return err;
}
+ v9stat->extension.data[err] = 0;
+ v9stat->extension.size = err;
+ } else if (v9stat->mode & P9_STAT_MODE_DEVICE) {
+ v9fs_string_sprintf(&v9stat->extension, "%c %u %u",
+ S_ISCHR(stbuf->st_mode) ? 'c' : 'b',
+ major(stbuf->st_rdev), minor(stbuf->st_rdev));
+ } else if (S_ISDIR(stbuf->st_mode) || S_ISREG(stbuf->st_mode)) {
+ v9fs_string_sprintf(&v9stat->extension, "%s %lu",
+ "HARDLINKCOUNT", (unsigned long)stbuf->st_nlink);
}
str = strrchr(name->data, '/');
@@ -958,6 +1071,51 @@ static int stat_to_v9stat(V9fsState *s, V9fsString *name,
return 0;
}
+#define P9_STATS_MODE 0x00000001ULL
+#define P9_STATS_NLINK 0x00000002ULL
+#define P9_STATS_UID 0x00000004ULL
+#define P9_STATS_GID 0x00000008ULL
+#define P9_STATS_RDEV 0x00000010ULL
+#define P9_STATS_ATIME 0x00000020ULL
+#define P9_STATS_MTIME 0x00000040ULL
+#define P9_STATS_CTIME 0x00000080ULL
+#define P9_STATS_INO 0x00000100ULL
+#define P9_STATS_SIZE 0x00000200ULL
+#define P9_STATS_BLOCKS 0x00000400ULL
+
+#define P9_STATS_BTIME 0x00000800ULL
+#define P9_STATS_GEN 0x00001000ULL
+#define P9_STATS_DATA_VERSION 0x00002000ULL
+
+#define P9_STATS_BASIC 0x000007ffULL /* Mask for fields up to BLOCKS */
+#define P9_STATS_ALL 0x00003fffULL /* Mask for All fields above */
+
+
+static void stat_to_v9stat_dotl(V9fsState *s, const struct stat *stbuf,
+ V9fsStatDotl *v9lstat)
+{
+ memset(v9lstat, 0, sizeof(*v9lstat));
+
+ v9lstat->st_mode = stbuf->st_mode;
+ v9lstat->st_nlink = stbuf->st_nlink;
+ v9lstat->st_uid = stbuf->st_uid;
+ v9lstat->st_gid = stbuf->st_gid;
+ v9lstat->st_rdev = stbuf->st_rdev;
+ v9lstat->st_size = stbuf->st_size;
+ v9lstat->st_blksize = stbuf->st_blksize;
+ v9lstat->st_blocks = stbuf->st_blocks;
+ v9lstat->st_atime_sec = stbuf->st_atime;
+ v9lstat->st_atime_nsec = stbuf->st_atim.tv_nsec;
+ v9lstat->st_mtime_sec = stbuf->st_mtime;
+ v9lstat->st_mtime_nsec = stbuf->st_mtim.tv_nsec;
+ v9lstat->st_ctime_sec = stbuf->st_ctime;
+ v9lstat->st_ctime_nsec = stbuf->st_ctim.tv_nsec;
+ /* Currently we only support BASIC fields in stat */
+ v9lstat->st_result_mask = P9_STATS_BASIC;
+
+ stat_to_qid(stbuf, &v9lstat->qid);
+}
+
static struct iovec *adjust_sg(struct iovec *sg, int len, int *iovcnt)
{
while (len && *iovcnt) {
@@ -1019,17 +1177,20 @@ static void v9fs_fix_path(V9fsString *dst, V9fsString *src, int len)
static void v9fs_version(V9fsState *s, V9fsPDU *pdu)
{
- int32_t msize;
V9fsString version;
size_t offset = 7;
- pdu_unmarshal(pdu, offset, "ds", &msize, &version);
+ pdu_unmarshal(pdu, offset, "ds", &s->msize, &version);
- if (strcmp(version.data, "9P2000.u")) {
+ if (!strcmp(version.data, "9P2000.u")) {
+ s->proto_version = V9FS_PROTO_2000U;
+ } else if (!strcmp(version.data, "9P2000.L")) {
+ s->proto_version = V9FS_PROTO_2000L;
+ } else {
v9fs_string_sprintf(&version, "unknown");
}
- offset += pdu_marshal(pdu, offset, "ds", msize, &version);
+ offset += pdu_marshal(pdu, offset, "ds", s->msize, &version);
complete_pdu(s, pdu, offset);
v9fs_string_free(&version);
@@ -1121,6 +1282,202 @@ out:
qemu_free(vs);
}
+static void v9fs_getattr_post_lstat(V9fsState *s, V9fsStatStateDotl *vs,
+ int err)
+{
+ if (err == -1) {
+ err = -errno;
+ goto out;
+ }
+
+ stat_to_v9stat_dotl(s, &vs->stbuf, &vs->v9stat_dotl);
+ vs->offset += pdu_marshal(vs->pdu, vs->offset, "A", &vs->v9stat_dotl);
+ err = vs->offset;
+
+out:
+ complete_pdu(s, vs->pdu, err);
+ qemu_free(vs);
+}
+
+static void v9fs_getattr(V9fsState *s, V9fsPDU *pdu)
+{
+ int32_t fid;
+ V9fsStatStateDotl *vs;
+ ssize_t err = 0;
+ V9fsFidState *fidp;
+ uint64_t request_mask;
+
+ vs = qemu_malloc(sizeof(*vs));
+ vs->pdu = pdu;
+ vs->offset = 7;
+
+ memset(&vs->v9stat_dotl, 0, sizeof(vs->v9stat_dotl));
+
+ pdu_unmarshal(vs->pdu, vs->offset, "dq", &fid, &request_mask);
+
+ fidp = lookup_fid(s, fid);
+ if (fidp == NULL) {
+ err = -ENOENT;
+ goto out;
+ }
+
+ /* Currently we only support BASIC fields in stat, so there is no
+ * need to look at request_mask.
+ */
+ err = v9fs_do_lstat(s, &fidp->path, &vs->stbuf);
+ v9fs_getattr_post_lstat(s, vs, err);
+ return;
+
+out:
+ complete_pdu(s, vs->pdu, err);
+ qemu_free(vs);
+}
+
+/* From Linux kernel code */
+#define ATTR_MODE (1 << 0)
+#define ATTR_UID (1 << 1)
+#define ATTR_GID (1 << 2)
+#define ATTR_SIZE (1 << 3)
+#define ATTR_ATIME (1 << 4)
+#define ATTR_MTIME (1 << 5)
+#define ATTR_CTIME (1 << 6)
+#define ATTR_MASK 127
+#define ATTR_ATIME_SET (1 << 7)
+#define ATTR_MTIME_SET (1 << 8)
+
+static void v9fs_setattr_post_truncate(V9fsState *s, V9fsSetattrState *vs,
+ int err)
+{
+ if (err == -1) {
+ err = -errno;
+ goto out;
+ }
+ err = vs->offset;
+
+out:
+ complete_pdu(s, vs->pdu, err);
+ qemu_free(vs);
+}
+
+static void v9fs_setattr_post_chown(V9fsState *s, V9fsSetattrState *vs, int err)
+{
+ if (err == -1) {
+ err = -errno;
+ goto out;
+ }
+
+ if (vs->v9iattr.valid & (ATTR_SIZE)) {
+ err = v9fs_do_truncate(s, &vs->fidp->path, vs->v9iattr.size);
+ }
+ v9fs_setattr_post_truncate(s, vs, err);
+ return;
+
+out:
+ complete_pdu(s, vs->pdu, err);
+ qemu_free(vs);
+}
+
+static void v9fs_setattr_post_utimensat(V9fsState *s, V9fsSetattrState *vs,
+ int err)
+{
+ if (err == -1) {
+ err = -errno;
+ goto out;
+ }
+
+ /* If the only valid entry in iattr is ctime we can call
+ * chown(-1,-1) to update the ctime of the file
+ */
+ if ((vs->v9iattr.valid & (ATTR_UID | ATTR_GID)) ||
+ ((vs->v9iattr.valid & ATTR_CTIME)
+ && !((vs->v9iattr.valid & ATTR_MASK) & ~ATTR_CTIME))) {
+ if (!(vs->v9iattr.valid & ATTR_UID)) {
+ vs->v9iattr.uid = -1;
+ }
+ if (!(vs->v9iattr.valid & ATTR_GID)) {
+ vs->v9iattr.gid = -1;
+ }
+ err = v9fs_do_chown(s, &vs->fidp->path, vs->v9iattr.uid,
+ vs->v9iattr.gid);
+ }
+ v9fs_setattr_post_chown(s, vs, err);
+ return;
+
+out:
+ complete_pdu(s, vs->pdu, err);
+ qemu_free(vs);
+}
+
+static void v9fs_setattr_post_chmod(V9fsState *s, V9fsSetattrState *vs, int err)
+{
+ if (err == -1) {
+ err = -errno;
+ goto out;
+ }
+
+ if (vs->v9iattr.valid & (ATTR_ATIME | ATTR_MTIME)) {
+ struct timespec times[2];
+ if (vs->v9iattr.valid & ATTR_ATIME) {
+ if (vs->v9iattr.valid & ATTR_ATIME_SET) {
+ times[0].tv_sec = vs->v9iattr.atime_sec;
+ times[0].tv_nsec = vs->v9iattr.atime_nsec;
+ } else {
+ times[0].tv_nsec = UTIME_NOW;
+ }
+ } else {
+ times[0].tv_nsec = UTIME_OMIT;
+ }
+
+ if (vs->v9iattr.valid & ATTR_MTIME) {
+ if (vs->v9iattr.valid & ATTR_MTIME_SET) {
+ times[1].tv_sec = vs->v9iattr.mtime_sec;
+ times[1].tv_nsec = vs->v9iattr.mtime_nsec;
+ } else {
+ times[1].tv_nsec = UTIME_NOW;
+ }
+ } else {
+ times[1].tv_nsec = UTIME_OMIT;
+ }
+ err = v9fs_do_utimensat(s, &vs->fidp->path, times);
+ }
+ v9fs_setattr_post_utimensat(s, vs, err);
+ return;
+
+out:
+ complete_pdu(s, vs->pdu, err);
+ qemu_free(vs);
+}
+
+static void v9fs_setattr(V9fsState *s, V9fsPDU *pdu)
+{
+ int32_t fid;
+ V9fsSetattrState *vs;
+ int err = 0;
+
+ vs = qemu_malloc(sizeof(*vs));
+ vs->pdu = pdu;
+ vs->offset = 7;
+
+ pdu_unmarshal(pdu, vs->offset, "dI", &fid, &vs->v9iattr);
+
+ vs->fidp = lookup_fid(s, fid);
+ if (vs->fidp == NULL) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ if (vs->v9iattr.valid & ATTR_MODE) {
+ err = v9fs_do_chmod(s, &vs->fidp->path, vs->v9iattr.mode);
+ }
+
+ v9fs_setattr_post_chmod(s, vs, err);
+ return;
+
+out:
+ complete_pdu(s, vs->pdu, err);
+ qemu_free(vs);
+}
+
static void v9fs_walk_complete(V9fsState *s, V9fsWalkState *vs, int err)
{
complete_pdu(s, vs->pdu, err);
@@ -1241,8 +1598,7 @@ static void v9fs_walk(V9fsState *s, V9fsPDU *pdu)
/* FIXME: is this really valid? */
if (fid == newfid) {
- BUG_ON(vs->fidp->fd != -1);
- BUG_ON(vs->fidp->dir);
+ BUG_ON(vs->fidp->fid_type != P9_FID_NONE);
v9fs_string_init(&vs->path);
vs->name_idx = 0;
@@ -1284,13 +1640,33 @@ out:
v9fs_walk_complete(s, vs, err);
}
+static int32_t get_iounit(V9fsState *s, V9fsString *name)
+{
+ struct statfs stbuf;
+ int32_t iounit = 0;
+
+ /*
+ * iounit should be multiples of f_bsize (host filesystem block size
+ * and as well as less than (client msize - P9_IOHDRSZ))
+ */
+ if (!v9fs_do_statfs(s, name, &stbuf)) {
+ iounit = stbuf.f_bsize;
+ iounit *= (s->msize - P9_IOHDRSZ)/stbuf.f_bsize;
+ }
+
+ if (!iounit) {
+ iounit = s->msize - P9_IOHDRSZ;
+ }
+ return iounit;
+}
+
static void v9fs_open_post_opendir(V9fsState *s, V9fsOpenState *vs, int err)
{
- if (vs->fidp->dir == NULL) {
+ if (vs->fidp->fs.dir == NULL) {
err = -errno;
goto out;
}
-
+ vs->fidp->fid_type = P9_FID_DIR;
vs->offset += pdu_marshal(vs->pdu, vs->offset, "Qd", &vs->qid, 0);
err = vs->offset;
out:
@@ -1299,15 +1675,25 @@ out:
}
+static void v9fs_open_post_getiounit(V9fsState *s, V9fsOpenState *vs)
+{
+ int err;
+ vs->offset += pdu_marshal(vs->pdu, vs->offset, "Qd", &vs->qid, vs->iounit);
+ err = vs->offset;
+ complete_pdu(s, vs->pdu, err);
+ qemu_free(vs);
+}
+
static void v9fs_open_post_open(V9fsState *s, V9fsOpenState *vs, int err)
{
- if (vs->fidp->fd == -1) {
+ if (vs->fidp->fs.fd == -1) {
err = -errno;
goto out;
}
-
- vs->offset += pdu_marshal(vs->pdu, vs->offset, "Qd", &vs->qid, 0);
- err = vs->offset;
+ vs->fidp->fid_type = P9_FID_FILE;
+ vs->iounit = get_iounit(s, &vs->fidp->path);
+ v9fs_open_post_getiounit(s, vs);
+ return;
out:
complete_pdu(s, vs->pdu, err);
qemu_free(vs);
@@ -1315,6 +1701,8 @@ out:
static void v9fs_open_post_lstat(V9fsState *s, V9fsOpenState *vs, int err)
{
+ int flags;
+
if (err) {
err = -errno;
goto out;
@@ -1323,11 +1711,18 @@ static void v9fs_open_post_lstat(V9fsState *s, V9fsOpenState *vs, int err)
stat_to_qid(&vs->stbuf, &vs->qid);
if (S_ISDIR(vs->stbuf.st_mode)) {
- vs->fidp->dir = v9fs_do_opendir(s, &vs->fidp->path);
+ vs->fidp->fs.dir = v9fs_do_opendir(s, &vs->fidp->path);
v9fs_open_post_opendir(s, vs, err);
} else {
- vs->fidp->fd = v9fs_do_open(s, &vs->fidp->path,
- omode_to_uflags(vs->mode));
+ if (s->proto_version == V9FS_PROTO_2000L) {
+ flags = vs->mode;
+ flags &= ~(O_NOCTTY | O_ASYNC | O_CREAT);
+ /* Ignore direct disk access hint until the server supports it. */
+ flags &= ~O_DIRECT;
+ } else {
+ flags = omode_to_uflags(vs->mode);
+ }
+ vs->fidp->fs.fd = v9fs_do_open(s, &vs->fidp->path, flags);
v9fs_open_post_open(s, vs, err);
}
return;
@@ -1342,12 +1737,16 @@ static void v9fs_open(V9fsState *s, V9fsPDU *pdu)
V9fsOpenState *vs;
ssize_t err = 0;
-
vs = qemu_malloc(sizeof(*vs));
vs->pdu = pdu;
vs->offset = 7;
+ vs->mode = 0;
- pdu_unmarshal(vs->pdu, vs->offset, "db", &fid, &vs->mode);
+ if (s->proto_version == V9FS_PROTO_2000L) {
+ pdu_unmarshal(vs->pdu, vs->offset, "dd", &fid, &vs->mode);
+ } else {
+ pdu_unmarshal(vs->pdu, vs->offset, "db", &fid, &vs->mode);
+ }
vs->fidp = lookup_fid(s, fid);
if (vs->fidp == NULL) {
@@ -1355,8 +1754,7 @@ static void v9fs_open(V9fsState *s, V9fsPDU *pdu)
goto out;
}
- BUG_ON(vs->fidp->fd != -1);
- BUG_ON(vs->fidp->dir);
+ BUG_ON(vs->fidp->fid_type != P9_FID_NONE);
err = v9fs_do_lstat(s, &vs->fidp->path, &vs->stbuf);
@@ -1367,6 +1765,122 @@ out:
qemu_free(vs);
}
+static void v9fs_post_lcreate(V9fsState *s, V9fsLcreateState *vs, int err)
+{
+ if (err == 0) {
+ v9fs_string_copy(&vs->fidp->path, &vs->fullname);
+ stat_to_qid(&vs->stbuf, &vs->qid);
+ vs->offset += pdu_marshal(vs->pdu, vs->offset, "Qd", &vs->qid,
+ &vs->iounit);
+ err = vs->offset;
+ } else {
+ vs->fidp->fid_type = P9_FID_NONE;
+ err = -errno;
+ if (vs->fidp->fs.fd > 0) {
+ close(vs->fidp->fs.fd);
+ }
+ }
+
+ complete_pdu(s, vs->pdu, err);
+ v9fs_string_free(&vs->name);
+ v9fs_string_free(&vs->fullname);
+ qemu_free(vs);
+}
+
+static void v9fs_lcreate_post_get_iounit(V9fsState *s, V9fsLcreateState *vs,
+ int err)
+{
+ if (err) {
+ err = -errno;
+ goto out;
+ }
+ err = v9fs_do_lstat(s, &vs->fullname, &vs->stbuf);
+
+out:
+ v9fs_post_lcreate(s, vs, err);
+}
+
+static void v9fs_lcreate_post_do_open2(V9fsState *s, V9fsLcreateState *vs,
+ int err)
+{
+ if (vs->fidp->fs.fd == -1) {
+ err = -errno;
+ goto out;
+ }
+ vs->fidp->fid_type = P9_FID_FILE;
+ vs->iounit = get_iounit(s, &vs->fullname);
+ v9fs_lcreate_post_get_iounit(s, vs, err);
+ return;
+
+out:
+ v9fs_post_lcreate(s, vs, err);
+}
+
+static void v9fs_lcreate(V9fsState *s, V9fsPDU *pdu)
+{
+ int32_t dfid, flags, mode;
+ gid_t gid;
+ V9fsLcreateState *vs;
+ ssize_t err = 0;
+
+ vs = qemu_malloc(sizeof(*vs));
+ vs->pdu = pdu;
+ vs->offset = 7;
+
+ v9fs_string_init(&vs->fullname);
+
+ pdu_unmarshal(vs->pdu, vs->offset, "dsddd", &dfid, &vs->name, &flags,
+ &mode, &gid);
+
+ vs->fidp = lookup_fid(s, dfid);
+ if (vs->fidp == NULL) {
+ err = -ENOENT;
+ goto out;
+ }
+
+ v9fs_string_sprintf(&vs->fullname, "%s/%s", vs->fidp->path.data,
+ vs->name.data);
+
+ /* Ignore direct disk access hint until the server supports it. */
+ flags &= ~O_DIRECT;
+
+ vs->fidp->fs.fd = v9fs_do_open2(s, vs->fullname.data, vs->fidp->uid,
+ gid, flags, mode);
+ v9fs_lcreate_post_do_open2(s, vs, err);
+ return;
+
+out:
+ complete_pdu(s, vs->pdu, err);
+ v9fs_string_free(&vs->name);
+ qemu_free(vs);
+}
+
+static void v9fs_post_do_fsync(V9fsState *s, V9fsPDU *pdu, int err)
+{
+ if (err == -1) {
+ err = -errno;
+ }
+ complete_pdu(s, pdu, err);
+}
+
+static void v9fs_fsync(V9fsState *s, V9fsPDU *pdu)
+{
+ int32_t fid;
+ size_t offset = 7;
+ V9fsFidState *fidp;
+ int err;
+
+ pdu_unmarshal(pdu, offset, "d", &fid);
+ fidp = lookup_fid(s, fid);
+ if (fidp == NULL) {
+ err = -ENOENT;
+ v9fs_post_do_fsync(s, pdu, err);
+ return;
+ }
+ err = v9fs_do_fsync(s, fidp->fs.fd);
+ v9fs_post_do_fsync(s, pdu, err);
+}
+
static void v9fs_clunk(V9fsState *s, V9fsPDU *pdu)
{
int32_t fid;
@@ -1420,7 +1934,7 @@ static void v9fs_read_post_dir_lstat(V9fsState *s, V9fsReadState *vs,
&vs->v9stat);
if ((vs->len != (vs->v9stat.size + 2)) ||
((vs->count + vs->len) > vs->max_count)) {
- v9fs_do_seekdir(s, vs->fidp->dir, vs->dir_pos);
+ v9fs_do_seekdir(s, vs->fidp->fs.dir, vs->dir_pos);
v9fs_read_post_seekdir(s, vs, err);
return;
}
@@ -1428,11 +1942,11 @@ static void v9fs_read_post_dir_lstat(V9fsState *s, V9fsReadState *vs,
v9fs_stat_free(&vs->v9stat);
v9fs_string_free(&vs->name);
vs->dir_pos = vs->dent->d_off;
- vs->dent = v9fs_do_readdir(s, vs->fidp->dir);
+ vs->dent = v9fs_do_readdir(s, vs->fidp->fs.dir);
v9fs_read_post_readdir(s, vs, err);
return;
out:
- v9fs_do_seekdir(s, vs->fidp->dir, vs->dir_pos);
+ v9fs_do_seekdir(s, vs->fidp->fs.dir, vs->dir_pos);
v9fs_read_post_seekdir(s, vs, err);
return;
@@ -1460,7 +1974,7 @@ static void v9fs_read_post_readdir(V9fsState *s, V9fsReadState *vs, ssize_t err)
static void v9fs_read_post_telldir(V9fsState *s, V9fsReadState *vs, ssize_t err)
{
- vs->dent = v9fs_do_readdir(s, vs->fidp->dir);
+ vs->dent = v9fs_do_readdir(s, vs->fidp->fs.dir);
v9fs_read_post_readdir(s, vs, err);
return;
}
@@ -1468,12 +1982,12 @@ static void v9fs_read_post_telldir(V9fsState *s, V9fsReadState *vs, ssize_t err)
static void v9fs_read_post_rewinddir(V9fsState *s, V9fsReadState *vs,
ssize_t err)
{
- vs->dir_pos = v9fs_do_telldir(s, vs->fidp->dir);
+ vs->dir_pos = v9fs_do_telldir(s, vs->fidp->fs.dir);
v9fs_read_post_telldir(s, vs, err);
return;
}
-static void v9fs_read_post_readv(V9fsState *s, V9fsReadState *vs, ssize_t err)
+static void v9fs_read_post_preadv(V9fsState *s, V9fsReadState *vs, ssize_t err)
{
if (err < 0) {
/* IO error return the error */
@@ -1487,12 +2001,16 @@ static void v9fs_read_post_readv(V9fsState *s, V9fsReadState *vs, ssize_t err)
if (0) {
print_sg(vs->sg, vs->cnt);
}
- vs->len = v9fs_do_readv(s, vs->fidp->fd, vs->sg, vs->cnt);
+ vs->len = v9fs_do_preadv(s, vs->fidp->fs.fd, vs->sg, vs->cnt,
+ vs->off);
+ if (vs->len > 0) {
+ vs->off += vs->len;
+ }
} while (vs->len == -1 && errno == EINTR);
if (vs->len == -1) {
err = -errno;
}
- v9fs_read_post_readv(s, vs, err);
+ v9fs_read_post_preadv(s, vs, err);
return;
}
vs->offset += pdu_marshal(vs->pdu, vs->offset, "d", vs->total);
@@ -1504,28 +2022,27 @@ out:
qemu_free(vs);
}
-static void v9fs_read_post_lseek(V9fsState *s, V9fsReadState *vs, ssize_t err)
+static void v9fs_xattr_read(V9fsState *s, V9fsReadState *vs)
{
- if (err == -1) {
- err = -errno;
- goto out;
- }
- vs->sg = cap_sg(vs->sg, vs->count, &vs->cnt);
-
- if (vs->total < vs->count) {
- do {
- if (0) {
- print_sg(vs->sg, vs->cnt);
- }
- vs->len = v9fs_do_readv(s, vs->fidp->fd, vs->sg, vs->cnt);
- } while (vs->len == -1 && errno == EINTR);
- if (vs->len == -1) {
- err = -errno;
- }
- v9fs_read_post_readv(s, vs, err);
- return;
+ ssize_t err = 0;
+ int read_count;
+ int64_t xattr_len;
+
+ xattr_len = vs->fidp->fs.xattr.len;
+ read_count = xattr_len - vs->off;
+ if (read_count > vs->count) {
+ read_count = vs->count;
+ } else if (read_count < 0) {
+ /*
+ * read beyond XATTR value
+ */
+ read_count = 0;
}
-out:
+ vs->offset += pdu_marshal(vs->pdu, vs->offset, "d", read_count);
+ vs->offset += pdu_pack(vs->pdu, vs->offset,
+ ((char *)vs->fidp->fs.xattr.value) + vs->off,
+ read_count);
+ err = vs->offset;
complete_pdu(s, vs->pdu, err);
qemu_free(vs);
}
@@ -1551,19 +2068,30 @@ static void v9fs_read(V9fsState *s, V9fsPDU *pdu)
goto out;
}
- if (vs->fidp->dir) {
+ if (vs->fidp->fid_type == P9_FID_DIR) {
vs->max_count = vs->count;
vs->count = 0;
if (vs->off == 0) {
- v9fs_do_rewinddir(s, vs->fidp->dir);
+ v9fs_do_rewinddir(s, vs->fidp->fs.dir);
}
v9fs_read_post_rewinddir(s, vs, err);
return;
- } else if (vs->fidp->fd != -1) {
+ } else if (vs->fidp->fid_type == P9_FID_FILE) {
vs->sg = vs->iov;
pdu_marshal(vs->pdu, vs->offset + 4, "v", vs->sg, &vs->cnt);
- err = v9fs_do_lseek(s, vs->fidp->fd, vs->off, SEEK_SET);
- v9fs_read_post_lseek(s, vs, err);
+ vs->sg = cap_sg(vs->sg, vs->count, &vs->cnt);
+ if (vs->total <= vs->count) {
+ vs->len = v9fs_do_preadv(s, vs->fidp->fs.fd, vs->sg, vs->cnt,
+ vs->off);
+ if (vs->len > 0) {
+ vs->off += vs->len;
+ }
+ err = vs->len;
+ v9fs_read_post_preadv(s, vs, err);
+ }
+ return;
+ } else if (vs->fidp->fid_type == P9_FID_XATTR) {
+ v9fs_xattr_read(s, vs);
return;
} else {
err = -EINVAL;
@@ -1573,7 +2101,128 @@ out:
qemu_free(vs);
}
-static void v9fs_write_post_writev(V9fsState *s, V9fsWriteState *vs,
+typedef struct V9fsReadDirState {
+ V9fsPDU *pdu;
+ V9fsFidState *fidp;
+ V9fsQID qid;
+ off_t saved_dir_pos;
+ struct dirent *dent;
+ int32_t count;
+ int32_t max_count;
+ size_t offset;
+ int64_t initial_offset;
+ V9fsString name;
+} V9fsReadDirState;
+
+static void v9fs_readdir_post_seekdir(V9fsState *s, V9fsReadDirState *vs)
+{
+ vs->offset += pdu_marshal(vs->pdu, vs->offset, "d", vs->count);
+ vs->offset += vs->count;
+ complete_pdu(s, vs->pdu, vs->offset);
+ qemu_free(vs);
+ return;
+}
+
+/* Size of each dirent on the wire: size of qid (13) + size of offset (8)
+ * size of type (1) + size of name.size (2) + strlen(name.data)
+ */
+#define V9_READDIR_DATA_SZ (24 + strlen(vs->name.data))
+
+static void v9fs_readdir_post_readdir(V9fsState *s, V9fsReadDirState *vs)
+{
+ int len;
+ size_t size;
+
+ if (vs->dent) {
+ v9fs_string_init(&vs->name);
+ v9fs_string_sprintf(&vs->name, "%s", vs->dent->d_name);
+
+ if ((vs->count + V9_READDIR_DATA_SZ) > vs->max_count) {
+ /* Ran out of buffer. Set dir back to old position and return */
+ v9fs_do_seekdir(s, vs->fidp->fs.dir, vs->saved_dir_pos);
+ v9fs_readdir_post_seekdir(s, vs);
+ return;
+ }
+
+ /* Fill up just the path field of qid because the client uses
+ * only that. To fill the entire qid structure we will have
+ * to stat each dirent found, which is expensive
+ */
+ size = MIN(sizeof(vs->dent->d_ino), sizeof(vs->qid.path));
+ memcpy(&vs->qid.path, &vs->dent->d_ino, size);
+ /* Fill the other fields with dummy values */
+ vs->qid.type = 0;
+ vs->qid.version = 0;
+
+ len = pdu_marshal(vs->pdu, vs->offset+4+vs->count, "Qqbs",
+ &vs->qid, vs->dent->d_off,
+ vs->dent->d_type, &vs->name);
+ vs->count += len;
+ v9fs_string_free(&vs->name);
+ vs->saved_dir_pos = vs->dent->d_off;
+ vs->dent = v9fs_do_readdir(s, vs->fidp->fs.dir);
+ v9fs_readdir_post_readdir(s, vs);
+ return;
+ }
+
+ vs->offset += pdu_marshal(vs->pdu, vs->offset, "d", vs->count);
+ vs->offset += vs->count;
+ complete_pdu(s, vs->pdu, vs->offset);
+ qemu_free(vs);
+ return;
+}
+
+static void v9fs_readdir_post_telldir(V9fsState *s, V9fsReadDirState *vs)
+{
+ vs->dent = v9fs_do_readdir(s, vs->fidp->fs.dir);
+ v9fs_readdir_post_readdir(s, vs);
+ return;
+}
+
+static void v9fs_readdir_post_setdir(V9fsState *s, V9fsReadDirState *vs)
+{
+ vs->saved_dir_pos = v9fs_do_telldir(s, vs->fidp->fs.dir);
+ v9fs_readdir_post_telldir(s, vs);
+ return;
+}
+
+static void v9fs_readdir(V9fsState *s, V9fsPDU *pdu)
+{
+ int32_t fid;
+ V9fsReadDirState *vs;
+ ssize_t err = 0;
+ size_t offset = 7;
+
+ vs = qemu_malloc(sizeof(*vs));
+ vs->pdu = pdu;
+ vs->offset = 7;
+ vs->count = 0;
+
+ pdu_unmarshal(vs->pdu, offset, "dqd", &fid, &vs->initial_offset,
+ &vs->max_count);
+
+ vs->fidp = lookup_fid(s, fid);
+ if (vs->fidp == NULL || !(vs->fidp->fs.dir)) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ if (vs->initial_offset == 0) {
+ v9fs_do_rewinddir(s, vs->fidp->fs.dir);
+ } else {
+ v9fs_do_seekdir(s, vs->fidp->fs.dir, vs->initial_offset);
+ }
+
+ v9fs_readdir_post_setdir(s, vs);
+ return;
+
+out:
+ complete_pdu(s, pdu, err);
+ qemu_free(vs);
+ return;
+}
+
+static void v9fs_write_post_pwritev(V9fsState *s, V9fsWriteState *vs,
ssize_t err)
{
if (err < 0) {
@@ -1588,44 +2237,62 @@ static void v9fs_write_post_writev(V9fsState *s, V9fsWriteState *vs,
if (0) {
print_sg(vs->sg, vs->cnt);
}
- vs->len = v9fs_do_writev(s, vs->fidp->fd, vs->sg, vs->cnt);
+ vs->len = v9fs_do_pwritev(s, vs->fidp->fs.fd, vs->sg, vs->cnt,
+ vs->off);
+ if (vs->len > 0) {
+ vs->off += vs->len;
+ }
} while (vs->len == -1 && errno == EINTR);
if (vs->len == -1) {
err = -errno;
}
- v9fs_write_post_writev(s, vs, err);
+ v9fs_write_post_pwritev(s, vs, err);
return;
}
vs->offset += pdu_marshal(vs->pdu, vs->offset, "d", vs->total);
-
err = vs->offset;
out:
complete_pdu(s, vs->pdu, err);
qemu_free(vs);
}
-static void v9fs_write_post_lseek(V9fsState *s, V9fsWriteState *vs, ssize_t err)
+static void v9fs_xattr_write(V9fsState *s, V9fsWriteState *vs)
{
- if (err == -1) {
- err = -errno;
+ int i, to_copy;
+ ssize_t err = 0;
+ int write_count;
+ int64_t xattr_len;
+
+ xattr_len = vs->fidp->fs.xattr.len;
+ write_count = xattr_len - vs->off;
+ if (write_count > vs->count) {
+ write_count = vs->count;
+ } else if (write_count < 0) {
+ /*
+ * write beyond XATTR value len specified in
+ * xattrcreate
+ */
+ err = -ENOSPC;
goto out;
}
- vs->sg = cap_sg(vs->sg, vs->count, &vs->cnt);
-
- if (vs->total < vs->count) {
- do {
- if (0) {
- print_sg(vs->sg, vs->cnt);
- }
- vs->len = v9fs_do_writev(s, vs->fidp->fd, vs->sg, vs->cnt);
- } while (vs->len == -1 && errno == EINTR);
- if (vs->len == -1) {
- err = -errno;
+ vs->offset += pdu_marshal(vs->pdu, vs->offset, "d", write_count);
+ err = vs->offset;
+ vs->fidp->fs.xattr.copied_len += write_count;
+ /*
+ * Now copy the content from sg list
+ */
+ for (i = 0; i < vs->cnt; i++) {
+ if (write_count > vs->sg[i].iov_len) {
+ to_copy = vs->sg[i].iov_len;
+ } else {
+ to_copy = write_count;
}
- v9fs_write_post_writev(s, vs, err);
- return;
+ memcpy((char *)vs->fidp->fs.xattr.value + vs->off,
+ vs->sg[i].iov_base, to_copy);
+ /* updating vs->off since we are not using below */
+ vs->off += to_copy;
+ write_count -= to_copy;
}
-
out:
complete_pdu(s, vs->pdu, err);
qemu_free(vs);
@@ -1646,7 +2313,7 @@ static void v9fs_write(V9fsState *s, V9fsPDU *pdu)
vs->len = 0;
pdu_unmarshal(vs->pdu, vs->offset, "dqdv", &fid, &vs->off, &vs->count,
- vs->sg, &vs->cnt);
+ vs->sg, &vs->cnt);
vs->fidp = lookup_fid(s, fid);
if (vs->fidp == NULL) {
@@ -1654,30 +2321,58 @@ static void v9fs_write(V9fsState *s, V9fsPDU *pdu)
goto out;
}
- if (vs->fidp->fd == -1) {
+ if (vs->fidp->fid_type == P9_FID_FILE) {
+ if (vs->fidp->fs.fd == -1) {
+ err = -EINVAL;
+ goto out;
+ }
+ } else if (vs->fidp->fid_type == P9_FID_XATTR) {
+ /*
+ * setxattr operation
+ */
+ v9fs_xattr_write(s, vs);
+ return;
+ } else {
err = -EINVAL;
goto out;
}
+ vs->sg = cap_sg(vs->sg, vs->count, &vs->cnt);
+ if (vs->total <= vs->count) {
+ vs->len = v9fs_do_pwritev(s, vs->fidp->fs.fd, vs->sg, vs->cnt, vs->off);
+ if (vs->len > 0) {
+ vs->off += vs->len;
+ }
+ err = vs->len;
+ v9fs_write_post_pwritev(s, vs, err);
+ }
+ return;
+out:
+ complete_pdu(s, vs->pdu, err);
+ qemu_free(vs);
+}
- err = v9fs_do_lseek(s, vs->fidp->fd, vs->off, SEEK_SET);
+static void v9fs_create_post_getiounit(V9fsState *s, V9fsCreateState *vs)
+{
+ int err;
+ v9fs_string_copy(&vs->fidp->path, &vs->fullname);
+ stat_to_qid(&vs->stbuf, &vs->qid);
- v9fs_write_post_lseek(s, vs, err);
- return;
+ vs->offset += pdu_marshal(vs->pdu, vs->offset, "Qd", &vs->qid, vs->iounit);
+ err = vs->offset;
-out:
complete_pdu(s, vs->pdu, err);
+ v9fs_string_free(&vs->name);
+ v9fs_string_free(&vs->extension);
+ v9fs_string_free(&vs->fullname);
qemu_free(vs);
}
static void v9fs_post_create(V9fsState *s, V9fsCreateState *vs, int err)
{
if (err == 0) {
- v9fs_string_copy(&vs->fidp->path, &vs->fullname);
- stat_to_qid(&vs->stbuf, &vs->qid);
-
- vs->offset += pdu_marshal(vs->pdu, vs->offset, "Qd", &vs->qid, 0);
-
- err = vs->offset;
+ vs->iounit = get_iounit(s, &vs->fidp->path);
+ v9fs_create_post_getiounit(s, vs);
+ return;
}
complete_pdu(s, vs->pdu, err);
@@ -1698,9 +2393,10 @@ static void v9fs_create_post_perms(V9fsState *s, V9fsCreateState *vs, int err)
static void v9fs_create_post_opendir(V9fsState *s, V9fsCreateState *vs,
int err)
{
- if (!vs->fidp->dir) {
+ if (!vs->fidp->fs.dir) {
err = -errno;
}
+ vs->fidp->fid_type = P9_FID_DIR;
v9fs_post_create(s, vs, err);
}
@@ -1712,7 +2408,7 @@ static void v9fs_create_post_dir_lstat(V9fsState *s, V9fsCreateState *vs,
goto out;
}
- vs->fidp->dir = v9fs_do_opendir(s, &vs->fullname);
+ vs->fidp->fs.dir = v9fs_do_opendir(s, &vs->fullname);
v9fs_create_post_opendir(s, vs, err);
return;
@@ -1738,22 +2434,22 @@ out:
static void v9fs_create_post_fstat(V9fsState *s, V9fsCreateState *vs, int err)
{
if (err) {
- vs->fidp->fd = -1;
+ vs->fidp->fid_type = P9_FID_NONE;
+ close(vs->fidp->fs.fd);
err = -errno;
}
-
v9fs_post_create(s, vs, err);
return;
}
static void v9fs_create_post_open2(V9fsState *s, V9fsCreateState *vs, int err)
{
- if (vs->fidp->fd == -1) {
+ if (vs->fidp->fs.fd == -1) {
err = -errno;
goto out;
}
-
- err = v9fs_do_fstat(s, vs->fidp->fd, &vs->stbuf);
+ vs->fidp->fid_type = P9_FID_FILE;
+ err = v9fs_do_fstat(s, vs->fidp->fs.fd, &vs->stbuf);
v9fs_create_post_fstat(s, vs, err);
return;
@@ -1772,10 +2468,12 @@ static void v9fs_create_post_lstat(V9fsState *s, V9fsCreateState *vs, int err)
}
if (vs->perm & P9_STAT_MODE_DIR) {
- err = v9fs_do_mkdir(s, vs);
+ err = v9fs_do_mkdir(s, vs->fullname.data, vs->perm & 0777,
+ vs->fidp->uid, -1);
v9fs_create_post_mkdir(s, vs, err);
} else if (vs->perm & P9_STAT_MODE_SYMLINK) {
- err = v9fs_do_symlink(s, vs);
+ err = v9fs_do_symlink(s, vs->fidp, vs->extension.data,
+ vs->fullname.data, -1);
v9fs_create_post_perms(s, vs, err);
} else if (vs->perm & P9_STAT_MODE_LINK) {
int32_t nfid = atoi(vs->extension.data);
@@ -1810,16 +2508,21 @@ static void v9fs_create_post_lstat(V9fsState *s, V9fsCreateState *vs, int err)
}
nmode |= vs->perm & 0777;
- err = v9fs_do_mknod(s, vs, nmode, makedev(major, minor));
+ err = v9fs_do_mknod(s, vs->fullname.data, nmode,
+ makedev(major, minor), vs->fidp->uid, -1);
v9fs_create_post_perms(s, vs, err);
} else if (vs->perm & P9_STAT_MODE_NAMED_PIPE) {
- err = v9fs_do_mknod(s, vs, S_IFIFO | (vs->perm & 0777), 0);
+ err = v9fs_do_mknod(s, vs->fullname.data, S_IFIFO | (vs->perm & 0777),
+ 0, vs->fidp->uid, -1);
v9fs_post_create(s, vs, err);
} else if (vs->perm & P9_STAT_MODE_SOCKET) {
- err = v9fs_do_mknod(s, vs, S_IFSOCK | (vs->perm & 0777), 0);
+ err = v9fs_do_mknod(s, vs->fullname.data, S_IFSOCK | (vs->perm & 0777),
+ 0, vs->fidp->uid, -1);
v9fs_post_create(s, vs, err);
} else {
- vs->fidp->fd = v9fs_do_open2(s, vs);
+ vs->fidp->fs.fd = v9fs_do_open2(s, vs->fullname.data, vs->fidp->uid,
+ -1, omode_to_uflags(vs->mode)|O_CREAT, vs->perm);
+
v9fs_create_post_open2(s, vs, err);
}
@@ -1864,23 +2567,124 @@ out:
qemu_free(vs);
}
+static void v9fs_post_symlink(V9fsState *s, V9fsSymlinkState *vs, int err)
+{
+ if (err == 0) {
+ stat_to_qid(&vs->stbuf, &vs->qid);
+ vs->offset += pdu_marshal(vs->pdu, vs->offset, "Q", &vs->qid);
+ err = vs->offset;
+ } else {
+ err = -errno;
+ }
+ complete_pdu(s, vs->pdu, err);
+ v9fs_string_free(&vs->name);
+ v9fs_string_free(&vs->symname);
+ v9fs_string_free(&vs->fullname);
+ qemu_free(vs);
+}
+
+static void v9fs_symlink_post_do_symlink(V9fsState *s, V9fsSymlinkState *vs,
+ int err)
+{
+ if (err) {
+ goto out;
+ }
+ err = v9fs_do_lstat(s, &vs->fullname, &vs->stbuf);
+out:
+ v9fs_post_symlink(s, vs, err);
+}
+
+static void v9fs_symlink(V9fsState *s, V9fsPDU *pdu)
+{
+ int32_t dfid;
+ V9fsSymlinkState *vs;
+ int err = 0;
+ gid_t gid;
+
+ vs = qemu_malloc(sizeof(*vs));
+ vs->pdu = pdu;
+ vs->offset = 7;
+
+ v9fs_string_init(&vs->fullname);
+
+ pdu_unmarshal(vs->pdu, vs->offset, "dssd", &dfid, &vs->name,
+ &vs->symname, &gid);
+
+ vs->dfidp = lookup_fid(s, dfid);
+ if (vs->dfidp == NULL) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ v9fs_string_sprintf(&vs->fullname, "%s/%s", vs->dfidp->path.data,
+ vs->name.data);
+ err = v9fs_do_symlink(s, vs->dfidp, vs->symname.data,
+ vs->fullname.data, gid);
+ v9fs_symlink_post_do_symlink(s, vs, err);
+ return;
+
+out:
+ complete_pdu(s, vs->pdu, err);
+ v9fs_string_free(&vs->name);
+ v9fs_string_free(&vs->symname);
+ qemu_free(vs);
+}
+
static void v9fs_flush(V9fsState *s, V9fsPDU *pdu)
{
/* A nop call with no return */
complete_pdu(s, pdu, 7);
}
+static void v9fs_link(V9fsState *s, V9fsPDU *pdu)
+{
+ int32_t dfid, oldfid;
+ V9fsFidState *dfidp, *oldfidp;
+ V9fsString name, fullname;
+ size_t offset = 7;
+ int err = 0;
+
+ v9fs_string_init(&fullname);
+
+ pdu_unmarshal(pdu, offset, "dds", &dfid, &oldfid, &name);
+
+ dfidp = lookup_fid(s, dfid);
+ if (dfidp == NULL) {
+ err = -errno;
+ goto out;
+ }
+
+ oldfidp = lookup_fid(s, oldfid);
+ if (oldfidp == NULL) {
+ err = -errno;
+ goto out;
+ }
+
+ v9fs_string_sprintf(&fullname, "%s/%s", dfidp->path.data, name.data);
+ err = offset;
+ err = v9fs_do_link(s, &oldfidp->path, &fullname);
+ if (err) {
+ err = -errno;
+ }
+ v9fs_string_free(&fullname);
+
+out:
+ v9fs_string_free(&name);
+ complete_pdu(s, pdu, err);
+}
+
static void v9fs_remove_post_remove(V9fsState *s, V9fsRemoveState *vs,
int err)
{
- /* For TREMOVE we need to clunk the fid even on failed remove */
- err = free_fid(s, vs->fidp->fid);
if (err < 0) {
- goto out;
+ err = -errno;
+ } else {
+ err = vs->offset;
}
- err = vs->offset;
-out:
+ /* For TREMOVE we need to clunk the fid even on failed remove */
+ free_fid(s, vs->fidp->fid);
+
complete_pdu(s, vs->pdu, err);
qemu_free(vs);
}
@@ -1931,11 +2735,6 @@ static void v9fs_wstat_post_rename(V9fsState *s, V9fsWstatState *vs, int err)
if (err < 0) {
goto out;
}
-
- if (vs->v9stat.name.size != 0) {
- v9fs_string_free(&vs->nname);
- }
-
if (vs->v9stat.length != -1) {
if (v9fs_do_truncate(s, &vs->fidp->path, vs->v9stat.length) < 0) {
err = -errno;
@@ -1950,17 +2749,29 @@ out:
qemu_free(vs);
}
-static void v9fs_wstat_post_chown(V9fsState *s, V9fsWstatState *vs, int err)
+static int v9fs_complete_rename(V9fsState *s, V9fsRenameState *vs)
{
- V9fsFidState *fidp;
- if (err < 0) {
- goto out;
- }
+ int err = 0;
+ char *old_name, *new_name;
+ char *end;
- if (vs->v9stat.name.size != 0) {
- char *old_name, *new_name;
- char *end;
+ if (vs->newdirfid != -1) {
+ V9fsFidState *dirfidp;
+ dirfidp = lookup_fid(s, vs->newdirfid);
+ if (dirfidp == NULL) {
+ err = -ENOENT;
+ goto out;
+ }
+
+ BUG_ON(dirfidp->fid_type != P9_FID_NONE);
+
+ new_name = qemu_mallocz(dirfidp->path.size + vs->name.size + 2);
+
+ strcpy(new_name, dirfidp->path.data);
+ strcat(new_name, "/");
+ strcat(new_name + dirfidp->path.size, vs->name.data);
+ } else {
old_name = vs->fidp->path.data;
end = strrchr(old_name, '/');
if (end) {
@@ -1968,44 +2779,74 @@ static void v9fs_wstat_post_chown(V9fsState *s, V9fsWstatState *vs, int err)
} else {
end = old_name;
}
+ new_name = qemu_mallocz(end - old_name + vs->name.size + 1);
- new_name = qemu_malloc(end - old_name + vs->v9stat.name.size + 1);
+ strncat(new_name, old_name, end - old_name);
+ strncat(new_name + (end - old_name), vs->name.data, vs->name.size);
+ }
- memset(new_name, 0, end - old_name + vs->v9stat.name.size + 1);
- memcpy(new_name, old_name, end - old_name);
- memcpy(new_name + (end - old_name), vs->v9stat.name.data,
- vs->v9stat.name.size);
- vs->nname.data = new_name;
- vs->nname.size = strlen(new_name);
+ v9fs_string_free(&vs->name);
+ vs->name.data = qemu_strdup(new_name);
+ vs->name.size = strlen(new_name);
- if (strcmp(new_name, vs->fidp->path.data) != 0) {
- if (v9fs_do_rename(s, &vs->fidp->path, &vs->nname)) {
- err = -errno;
- } else {
- /*
- * Fixup fid's pointing to the old name to
- * start pointing to the new name
- */
- for (fidp = s->fid_list; fidp; fidp = fidp->next) {
-
- if (vs->fidp == fidp) {
- /*
- * we replace name of this fid towards the end
- * so that our below strcmp will work
- */
- continue;
- }
- if (!strncmp(vs->fidp->path.data, fidp->path.data,
- strlen(vs->fidp->path.data))) {
- /* replace the name */
- v9fs_fix_path(&fidp->path, &vs->nname,
- strlen(vs->fidp->path.data));
- }
+ if (strcmp(new_name, vs->fidp->path.data) != 0) {
+ if (v9fs_do_rename(s, &vs->fidp->path, &vs->name)) {
+ err = -errno;
+ } else {
+ V9fsFidState *fidp;
+ /*
+ * Fixup fid's pointing to the old name to
+ * start pointing to the new name
+ */
+ for (fidp = s->fid_list; fidp; fidp = fidp->next) {
+ if (vs->fidp == fidp) {
+ /*
+ * we replace name of this fid towards the end
+ * so that our below strcmp will work
+ */
+ continue;
+ }
+ if (!strncmp(vs->fidp->path.data, fidp->path.data,
+ strlen(vs->fidp->path.data))) {
+ /* replace the name */
+ v9fs_fix_path(&fidp->path, &vs->name,
+ strlen(vs->fidp->path.data));
}
- v9fs_string_copy(&vs->fidp->path, &vs->nname);
}
+ v9fs_string_copy(&vs->fidp->path, &vs->name);
}
}
+out:
+ v9fs_string_free(&vs->name);
+ return err;
+}
+
+static void v9fs_rename_post_rename(V9fsState *s, V9fsRenameState *vs, int err)
+{
+ complete_pdu(s, vs->pdu, err);
+ qemu_free(vs);
+}
+
+static void v9fs_wstat_post_chown(V9fsState *s, V9fsWstatState *vs, int err)
+{
+ if (err < 0) {
+ goto out;
+ }
+
+ if (vs->v9stat.name.size != 0) {
+ V9fsRenameState *vr;
+
+ vr = qemu_mallocz(sizeof(V9fsRenameState));
+ vr->newdirfid = -1;
+ vr->pdu = vs->pdu;
+ vr->fidp = vs->fidp;
+ vr->offset = vs->offset;
+ vr->name.size = vs->v9stat.name.size;
+ vr->name.data = qemu_strdup(vs->v9stat.name.data);
+
+ err = v9fs_complete_rename(s, vr);
+ qemu_free(vr);
+ }
v9fs_wstat_post_rename(s, vs, err);
return;
@@ -2015,6 +2856,34 @@ out:
qemu_free(vs);
}
+static void v9fs_rename(V9fsState *s, V9fsPDU *pdu)
+{
+ int32_t fid;
+ V9fsRenameState *vs;
+ ssize_t err = 0;
+
+ vs = qemu_malloc(sizeof(*vs));
+ vs->pdu = pdu;
+ vs->offset = 7;
+
+ pdu_unmarshal(vs->pdu, vs->offset, "dds", &fid, &vs->newdirfid, &vs->name);
+
+ vs->fidp = lookup_fid(s, fid);
+ if (vs->fidp == NULL) {
+ err = -ENOENT;
+ goto out;
+ }
+
+ BUG_ON(vs->fidp->fid_type != P9_FID_NONE);
+
+ err = v9fs_complete_rename(s, vs);
+ v9fs_rename_post_rename(s, vs, err);
+ return;
+out:
+ complete_pdu(s, vs->pdu, err);
+ qemu_free(vs);
+}
+
static void v9fs_wstat_post_utime(V9fsState *s, V9fsWstatState *vs, int err)
{
if (err < 0) {
@@ -2042,11 +2911,22 @@ static void v9fs_wstat_post_chmod(V9fsState *s, V9fsWstatState *vs, int err)
goto out;
}
- if (vs->v9stat.mtime != -1) {
- struct utimbuf tb;
- tb.actime = 0;
- tb.modtime = vs->v9stat.mtime;
- if (v9fs_do_utime(s, &vs->fidp->path, &tb)) {
+ if (vs->v9stat.mtime != -1 || vs->v9stat.atime != -1) {
+ struct timespec times[2];
+ if (vs->v9stat.atime != -1) {
+ times[0].tv_sec = vs->v9stat.atime;
+ times[0].tv_nsec = 0;
+ } else {
+ times[0].tv_nsec = UTIME_OMIT;
+ }
+ if (vs->v9stat.mtime != -1) {
+ times[1].tv_sec = vs->v9stat.mtime;
+ times[1].tv_nsec = 0;
+ } else {
+ times[1].tv_nsec = UTIME_OMIT;
+ }
+
+ if (v9fs_do_utimensat(s, &vs->fidp->path, times)) {
err = -errno;
}
}
@@ -2121,7 +3001,7 @@ static void v9fs_wstat(V9fsState *s, V9fsPDU *pdu)
/* do we need to sync the file? */
if (donttouch_stat(&vs->v9stat)) {
- err = v9fs_do_fsync(s, vs->fidp->fd);
+ err = v9fs_do_fsync(s, vs->fidp->fs.fd);
v9fs_wstat_post_fsync(s, vs, err);
return;
}
@@ -2141,21 +3021,565 @@ out:
qemu_free(vs);
}
+static void v9fs_statfs_post_statfs(V9fsState *s, V9fsStatfsState *vs, int err)
+{
+ int32_t bsize_factor;
+
+ if (err) {
+ err = -errno;
+ goto out;
+ }
+
+ /*
+ * compute bsize factor based on host file system block size
+ * and client msize
+ */
+ bsize_factor = (s->msize - P9_IOHDRSZ)/vs->stbuf.f_bsize;
+ if (!bsize_factor) {
+ bsize_factor = 1;
+ }
+ vs->v9statfs.f_type = vs->stbuf.f_type;
+ vs->v9statfs.f_bsize = vs->stbuf.f_bsize;
+ vs->v9statfs.f_bsize *= bsize_factor;
+ /*
+ * f_bsize is adjusted(multiplied) by bsize factor, so we need to
+ * adjust(divide) the number of blocks, free blocks and available
+ * blocks by bsize factor
+ */
+ vs->v9statfs.f_blocks = vs->stbuf.f_blocks/bsize_factor;
+ vs->v9statfs.f_bfree = vs->stbuf.f_bfree/bsize_factor;
+ vs->v9statfs.f_bavail = vs->stbuf.f_bavail/bsize_factor;
+ vs->v9statfs.f_files = vs->stbuf.f_files;
+ vs->v9statfs.f_ffree = vs->stbuf.f_ffree;
+ vs->v9statfs.fsid_val = (unsigned int) vs->stbuf.f_fsid.__val[0] |
+ (unsigned long long)vs->stbuf.f_fsid.__val[1] << 32;
+ vs->v9statfs.f_namelen = vs->stbuf.f_namelen;
+
+ vs->offset += pdu_marshal(vs->pdu, vs->offset, "ddqqqqqqd",
+ vs->v9statfs.f_type, vs->v9statfs.f_bsize, vs->v9statfs.f_blocks,
+ vs->v9statfs.f_bfree, vs->v9statfs.f_bavail, vs->v9statfs.f_files,
+ vs->v9statfs.f_ffree, vs->v9statfs.fsid_val,
+ vs->v9statfs.f_namelen);
+
+out:
+ complete_pdu(s, vs->pdu, vs->offset);
+ qemu_free(vs);
+}
+
+static void v9fs_statfs(V9fsState *s, V9fsPDU *pdu)
+{
+ V9fsStatfsState *vs;
+ ssize_t err = 0;
+
+ vs = qemu_malloc(sizeof(*vs));
+ vs->pdu = pdu;
+ vs->offset = 7;
+
+ memset(&vs->v9statfs, 0, sizeof(vs->v9statfs));
+
+ pdu_unmarshal(vs->pdu, vs->offset, "d", &vs->fid);
+
+ vs->fidp = lookup_fid(s, vs->fid);
+ if (vs->fidp == NULL) {
+ err = -ENOENT;
+ goto out;
+ }
+
+ err = v9fs_do_statfs(s, &vs->fidp->path, &vs->stbuf);
+ v9fs_statfs_post_statfs(s, vs, err);
+ return;
+
+out:
+ complete_pdu(s, vs->pdu, err);
+ qemu_free(vs);
+}
+
+static void v9fs_mknod_post_lstat(V9fsState *s, V9fsMkState *vs, int err)
+{
+ if (err == -1) {
+ err = -errno;
+ goto out;
+ }
+
+ stat_to_qid(&vs->stbuf, &vs->qid);
+ vs->offset += pdu_marshal(vs->pdu, vs->offset, "Q", &vs->qid);
+ err = vs->offset;
+out:
+ complete_pdu(s, vs->pdu, err);
+ v9fs_string_free(&vs->fullname);
+ v9fs_string_free(&vs->name);
+ qemu_free(vs);
+}
+
+static void v9fs_mknod_post_mknod(V9fsState *s, V9fsMkState *vs, int err)
+{
+ if (err == -1) {
+ err = -errno;
+ goto out;
+ }
+
+ err = v9fs_do_lstat(s, &vs->fullname, &vs->stbuf);
+ v9fs_mknod_post_lstat(s, vs, err);
+ return;
+out:
+ complete_pdu(s, vs->pdu, err);
+ v9fs_string_free(&vs->fullname);
+ v9fs_string_free(&vs->name);
+ qemu_free(vs);
+}
+
+static void v9fs_mknod(V9fsState *s, V9fsPDU *pdu)
+{
+ int32_t fid;
+ V9fsMkState *vs;
+ int err = 0;
+ V9fsFidState *fidp;
+ gid_t gid;
+ int mode;
+ int major, minor;
+
+ vs = qemu_malloc(sizeof(*vs));
+ vs->pdu = pdu;
+ vs->offset = 7;
+
+ v9fs_string_init(&vs->fullname);
+ pdu_unmarshal(vs->pdu, vs->offset, "dsdddd", &fid, &vs->name, &mode,
+ &major, &minor, &gid);
+
+ fidp = lookup_fid(s, fid);
+ if (fidp == NULL) {
+ err = -ENOENT;
+ goto out;
+ }
+
+ v9fs_string_sprintf(&vs->fullname, "%s/%s", fidp->path.data, vs->name.data);
+ err = v9fs_do_mknod(s, vs->fullname.data, mode, makedev(major, minor),
+ fidp->uid, gid);
+ v9fs_mknod_post_mknod(s, vs, err);
+ return;
+
+out:
+ complete_pdu(s, vs->pdu, err);
+ v9fs_string_free(&vs->fullname);
+ v9fs_string_free(&vs->name);
+ qemu_free(vs);
+}
+
+/*
+ * Implement posix byte range locking code
+ * Server side handling of locking code is very simple, because 9p server in
+ * QEMU can handle only one client. And most of the lock handling
+ * (like conflict, merging) etc is done by the VFS layer itself, so no need to
+ * do any thing in * qemu 9p server side lock code path.
+ * So when a TLOCK request comes, always return success
+ */
+
+static void v9fs_lock(V9fsState *s, V9fsPDU *pdu)
+{
+ int32_t fid, err = 0;
+ V9fsLockState *vs;
+
+ vs = qemu_mallocz(sizeof(*vs));
+ vs->pdu = pdu;
+ vs->offset = 7;
+
+ vs->flock = qemu_malloc(sizeof(*vs->flock));
+ pdu_unmarshal(vs->pdu, vs->offset, "dbdqqds", &fid, &vs->flock->type,
+ &vs->flock->flags, &vs->flock->start, &vs->flock->length,
+ &vs->flock->proc_id, &vs->flock->client_id);
+
+ vs->status = P9_LOCK_ERROR;
+
+ /* We support only block flag now (that too ignored currently) */
+ if (vs->flock->flags & ~P9_LOCK_FLAGS_BLOCK) {
+ err = -EINVAL;
+ goto out;
+ }
+ vs->fidp = lookup_fid(s, fid);
+ if (vs->fidp == NULL) {
+ err = -ENOENT;
+ goto out;
+ }
+
+ err = v9fs_do_fstat(s, vs->fidp->fs.fd, &vs->stbuf);
+ if (err < 0) {
+ err = -errno;
+ goto out;
+ }
+ vs->status = P9_LOCK_SUCCESS;
+out:
+ vs->offset += pdu_marshal(vs->pdu, vs->offset, "b", vs->status);
+ complete_pdu(s, vs->pdu, err);
+ qemu_free(vs->flock);
+ qemu_free(vs);
+}
+
+/*
+ * When a TGETLOCK request comes, always return success because all lock
+ * handling is done by client's VFS layer.
+ */
+
+static void v9fs_getlock(V9fsState *s, V9fsPDU *pdu)
+{
+ int32_t fid, err = 0;
+ V9fsGetlockState *vs;
+
+ vs = qemu_mallocz(sizeof(*vs));
+ vs->pdu = pdu;
+ vs->offset = 7;
+
+ vs->glock = qemu_malloc(sizeof(*vs->glock));
+ pdu_unmarshal(vs->pdu, vs->offset, "dbqqds", &fid, &vs->glock->type,
+ &vs->glock->start, &vs->glock->length, &vs->glock->proc_id,
+ &vs->glock->client_id);
+
+ vs->fidp = lookup_fid(s, fid);
+ if (vs->fidp == NULL) {
+ err = -ENOENT;
+ goto out;
+ }
+
+ err = v9fs_do_fstat(s, vs->fidp->fs.fd, &vs->stbuf);
+ if (err < 0) {
+ err = -errno;
+ goto out;
+ }
+ vs->glock->type = F_UNLCK;
+ vs->offset += pdu_marshal(vs->pdu, vs->offset, "bqqds", vs->glock->type,
+ vs->glock->start, vs->glock->length, vs->glock->proc_id,
+ &vs->glock->client_id);
+out:
+ complete_pdu(s, vs->pdu, err);
+ qemu_free(vs->glock);
+ qemu_free(vs);
+}
+
+static void v9fs_mkdir_post_lstat(V9fsState *s, V9fsMkState *vs, int err)
+{
+ if (err == -1) {
+ err = -errno;
+ goto out;
+ }
+
+ stat_to_qid(&vs->stbuf, &vs->qid);
+ vs->offset += pdu_marshal(vs->pdu, vs->offset, "Q", &vs->qid);
+ err = vs->offset;
+out:
+ complete_pdu(s, vs->pdu, err);
+ v9fs_string_free(&vs->fullname);
+ v9fs_string_free(&vs->name);
+ qemu_free(vs);
+}
+
+static void v9fs_mkdir_post_mkdir(V9fsState *s, V9fsMkState *vs, int err)
+{
+ if (err == -1) {
+ err = -errno;
+ goto out;
+ }
+
+ err = v9fs_do_lstat(s, &vs->fullname, &vs->stbuf);
+ v9fs_mkdir_post_lstat(s, vs, err);
+ return;
+out:
+ complete_pdu(s, vs->pdu, err);
+ v9fs_string_free(&vs->fullname);
+ v9fs_string_free(&vs->name);
+ qemu_free(vs);
+}
+
+static void v9fs_mkdir(V9fsState *s, V9fsPDU *pdu)
+{
+ int32_t fid;
+ V9fsMkState *vs;
+ int err = 0;
+ V9fsFidState *fidp;
+ gid_t gid;
+ int mode;
+
+ vs = qemu_malloc(sizeof(*vs));
+ vs->pdu = pdu;
+ vs->offset = 7;
+
+ v9fs_string_init(&vs->fullname);
+ pdu_unmarshal(vs->pdu, vs->offset, "dsdd", &fid, &vs->name, &mode,
+ &gid);
+
+ fidp = lookup_fid(s, fid);
+ if (fidp == NULL) {
+ err = -ENOENT;
+ goto out;
+ }
+
+ v9fs_string_sprintf(&vs->fullname, "%s/%s", fidp->path.data, vs->name.data);
+ err = v9fs_do_mkdir(s, vs->fullname.data, mode, fidp->uid, gid);
+ v9fs_mkdir_post_mkdir(s, vs, err);
+ return;
+
+out:
+ complete_pdu(s, vs->pdu, err);
+ v9fs_string_free(&vs->fullname);
+ v9fs_string_free(&vs->name);
+ qemu_free(vs);
+}
+
+static void v9fs_post_xattr_getvalue(V9fsState *s, V9fsXattrState *vs, int err)
+{
+
+ if (err < 0) {
+ err = -errno;
+ free_fid(s, vs->xattr_fidp->fid);
+ goto out;
+ }
+ vs->offset += pdu_marshal(vs->pdu, vs->offset, "q", vs->size);
+ err = vs->offset;
+out:
+ complete_pdu(s, vs->pdu, err);
+ v9fs_string_free(&vs->name);
+ qemu_free(vs);
+ return;
+}
+
+static void v9fs_post_xattr_check(V9fsState *s, V9fsXattrState *vs, ssize_t err)
+{
+ if (err < 0) {
+ err = -errno;
+ free_fid(s, vs->xattr_fidp->fid);
+ goto out;
+ }
+ /*
+ * Read the xattr value
+ */
+ vs->xattr_fidp->fs.xattr.len = vs->size;
+ vs->xattr_fidp->fid_type = P9_FID_XATTR;
+ vs->xattr_fidp->fs.xattr.copied_len = -1;
+ if (vs->size) {
+ vs->xattr_fidp->fs.xattr.value = qemu_malloc(vs->size);
+ err = v9fs_do_lgetxattr(s, &vs->xattr_fidp->path,
+ &vs->name, vs->xattr_fidp->fs.xattr.value,
+ vs->xattr_fidp->fs.xattr.len);
+ }
+ v9fs_post_xattr_getvalue(s, vs, err);
+ return;
+out:
+ complete_pdu(s, vs->pdu, err);
+ v9fs_string_free(&vs->name);
+ qemu_free(vs);
+}
+
+static void v9fs_post_lxattr_getvalue(V9fsState *s,
+ V9fsXattrState *vs, int err)
+{
+ if (err < 0) {
+ err = -errno;
+ free_fid(s, vs->xattr_fidp->fid);
+ goto out;
+ }
+ vs->offset += pdu_marshal(vs->pdu, vs->offset, "q", vs->size);
+ err = vs->offset;
+out:
+ complete_pdu(s, vs->pdu, err);
+ v9fs_string_free(&vs->name);
+ qemu_free(vs);
+ return;
+}
+
+static void v9fs_post_lxattr_check(V9fsState *s,
+ V9fsXattrState *vs, ssize_t err)
+{
+ if (err < 0) {
+ err = -errno;
+ free_fid(s, vs->xattr_fidp->fid);
+ goto out;
+ }
+ /*
+ * Read the xattr value
+ */
+ vs->xattr_fidp->fs.xattr.len = vs->size;
+ vs->xattr_fidp->fid_type = P9_FID_XATTR;
+ vs->xattr_fidp->fs.xattr.copied_len = -1;
+ if (vs->size) {
+ vs->xattr_fidp->fs.xattr.value = qemu_malloc(vs->size);
+ err = v9fs_do_llistxattr(s, &vs->xattr_fidp->path,
+ vs->xattr_fidp->fs.xattr.value,
+ vs->xattr_fidp->fs.xattr.len);
+ }
+ v9fs_post_lxattr_getvalue(s, vs, err);
+ return;
+out:
+ complete_pdu(s, vs->pdu, err);
+ v9fs_string_free(&vs->name);
+ qemu_free(vs);
+}
+
+static void v9fs_xattrwalk(V9fsState *s, V9fsPDU *pdu)
+{
+ ssize_t err = 0;
+ V9fsXattrState *vs;
+ int32_t fid, newfid;
+
+ vs = qemu_malloc(sizeof(*vs));
+ vs->pdu = pdu;
+ vs->offset = 7;
+
+ pdu_unmarshal(vs->pdu, vs->offset, "dds", &fid, &newfid, &vs->name);
+ vs->file_fidp = lookup_fid(s, fid);
+ if (vs->file_fidp == NULL) {
+ err = -ENOENT;
+ goto out;
+ }
+
+ vs->xattr_fidp = alloc_fid(s, newfid);
+ if (vs->xattr_fidp == NULL) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ v9fs_string_copy(&vs->xattr_fidp->path, &vs->file_fidp->path);
+ if (vs->name.data[0] == 0) {
+ /*
+ * listxattr request. Get the size first
+ */
+ vs->size = v9fs_do_llistxattr(s, &vs->xattr_fidp->path,
+ NULL, 0);
+ if (vs->size < 0) {
+ err = vs->size;
+ }
+ v9fs_post_lxattr_check(s, vs, err);
+ return;
+ } else {
+ /*
+ * specific xattr fid. We check for xattr
+ * presence also collect the xattr size
+ */
+ vs->size = v9fs_do_lgetxattr(s, &vs->xattr_fidp->path,
+ &vs->name, NULL, 0);
+ if (vs->size < 0) {
+ err = vs->size;
+ }
+ v9fs_post_xattr_check(s, vs, err);
+ return;
+ }
+out:
+ complete_pdu(s, vs->pdu, err);
+ v9fs_string_free(&vs->name);
+ qemu_free(vs);
+}
+
+static void v9fs_xattrcreate(V9fsState *s, V9fsPDU *pdu)
+{
+ int flags;
+ int32_t fid;
+ ssize_t err = 0;
+ V9fsXattrState *vs;
+
+ vs = qemu_malloc(sizeof(*vs));
+ vs->pdu = pdu;
+ vs->offset = 7;
+
+ pdu_unmarshal(vs->pdu, vs->offset, "dsqd",
+ &fid, &vs->name, &vs->size, &flags);
+
+ vs->file_fidp = lookup_fid(s, fid);
+ if (vs->file_fidp == NULL) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ /* Make the file fid point to xattr */
+ vs->xattr_fidp = vs->file_fidp;
+ vs->xattr_fidp->fid_type = P9_FID_XATTR;
+ vs->xattr_fidp->fs.xattr.copied_len = 0;
+ vs->xattr_fidp->fs.xattr.len = vs->size;
+ vs->xattr_fidp->fs.xattr.flags = flags;
+ v9fs_string_init(&vs->xattr_fidp->fs.xattr.name);
+ v9fs_string_copy(&vs->xattr_fidp->fs.xattr.name, &vs->name);
+ if (vs->size)
+ vs->xattr_fidp->fs.xattr.value = qemu_malloc(vs->size);
+ else
+ vs->xattr_fidp->fs.xattr.value = NULL;
+
+out:
+ complete_pdu(s, vs->pdu, err);
+ v9fs_string_free(&vs->name);
+ qemu_free(vs);
+}
+
+static void v9fs_readlink_post_readlink(V9fsState *s, V9fsReadLinkState *vs,
+ int err)
+{
+ if (err < 0) {
+ err = -errno;
+ goto out;
+ }
+ vs->offset += pdu_marshal(vs->pdu, vs->offset, "s", &vs->target);
+ err = vs->offset;
+out:
+ complete_pdu(s, vs->pdu, err);
+ v9fs_string_free(&vs->target);
+ qemu_free(vs);
+}
+
+static void v9fs_readlink(V9fsState *s, V9fsPDU *pdu)
+{
+ int32_t fid;
+ V9fsReadLinkState *vs;
+ int err = 0;
+ V9fsFidState *fidp;
+
+ vs = qemu_malloc(sizeof(*vs));
+ vs->pdu = pdu;
+ vs->offset = 7;
+
+ pdu_unmarshal(vs->pdu, vs->offset, "d", &fid);
+
+ fidp = lookup_fid(s, fid);
+ if (fidp == NULL) {
+ err = -ENOENT;
+ goto out;
+ }
+
+ v9fs_string_init(&vs->target);
+ err = v9fs_do_readlink(s, &fidp->path, &vs->target);
+ v9fs_readlink_post_readlink(s, vs, err);
+ return;
+out:
+ complete_pdu(s, vs->pdu, err);
+ qemu_free(vs);
+}
+
typedef void (pdu_handler_t)(V9fsState *s, V9fsPDU *pdu);
static pdu_handler_t *pdu_handlers[] = {
+ [P9_TREADDIR] = v9fs_readdir,
+ [P9_TSTATFS] = v9fs_statfs,
+ [P9_TGETATTR] = v9fs_getattr,
+ [P9_TSETATTR] = v9fs_setattr,
+ [P9_TXATTRWALK] = v9fs_xattrwalk,
+ [P9_TXATTRCREATE] = v9fs_xattrcreate,
+ [P9_TMKNOD] = v9fs_mknod,
+ [P9_TRENAME] = v9fs_rename,
+ [P9_TLOCK] = v9fs_lock,
+ [P9_TGETLOCK] = v9fs_getlock,
+ [P9_TREADLINK] = v9fs_readlink,
+ [P9_TMKDIR] = v9fs_mkdir,
[P9_TVERSION] = v9fs_version,
+ [P9_TLOPEN] = v9fs_open,
[P9_TATTACH] = v9fs_attach,
[P9_TSTAT] = v9fs_stat,
[P9_TWALK] = v9fs_walk,
[P9_TCLUNK] = v9fs_clunk,
+ [P9_TFSYNC] = v9fs_fsync,
[P9_TOPEN] = v9fs_open,
[P9_TREAD] = v9fs_read,
#if 0
[P9_TAUTH] = v9fs_auth,
#endif
[P9_TFLUSH] = v9fs_flush,
+ [P9_TLINK] = v9fs_link,
+ [P9_TSYMLINK] = v9fs_symlink,
[P9_TCREATE] = v9fs_create,
+ [P9_TLCREATE] = v9fs_lcreate,
[P9_TWRITE] = v9fs_write,
[P9_TWSTAT] = v9fs_wstat,
[P9_TREMOVE] = v9fs_remove,
@@ -2252,8 +3676,8 @@ VirtIODevice *virtio_9p_init(DeviceState *dev, V9fsConf *conf)
if (!fse) {
/* We don't have a fsdev identified by fsdev_id */
- fprintf(stderr, "Virtio-9p device couldn't find fsdev "
- "with the id %s\n", conf->fsdev_id);
+ fprintf(stderr, "Virtio-9p device couldn't find fsdev with the "
+ "id = %s\n", conf->fsdev_id ? conf->fsdev_id : "NULL");
exit(1);
}
@@ -2268,17 +3692,26 @@ VirtIODevice *virtio_9p_init(DeviceState *dev, V9fsConf *conf)
if (!strcmp(fse->security_model, "passthrough")) {
/* Files on the Fileserver set to client user credentials */
s->ctx.fs_sm = SM_PASSTHROUGH;
+ s->ctx.xops = passthrough_xattr_ops;
} else if (!strcmp(fse->security_model, "mapped")) {
/* Files on the fileserver are set to QEMU credentials.
* Client user credentials are saved in extended attributes.
*/
s->ctx.fs_sm = SM_MAPPED;
+ s->ctx.xops = mapped_xattr_ops;
+ } else if (!strcmp(fse->security_model, "none")) {
+ /*
+ * Files on the fileserver are set to QEMU credentials.
+ */
+ s->ctx.fs_sm = SM_NONE;
+ s->ctx.xops = none_xattr_ops;
} else {
- /* user haven't specified a correct security option */
- fprintf(stderr, "one of the following must be specified as the"
+ fprintf(stderr, "Default to security_model=none. You may want"
+ " enable advanced security model using "
"security option:\n\t security_model=passthrough \n\t "
"security_model=mapped\n");
- return NULL;
+ s->ctx.fs_sm = SM_NONE;
+ s->ctx.xops = none_xattr_ops;
}
if (lstat(fse->path, &stat)) {
diff --git a/hw/virtio-9p.h b/hw/virtio-9p.h
index 67f808761f..6c233192e3 100644
--- a/hw/virtio-9p.h
+++ b/hw/virtio-9p.h
@@ -13,6 +13,42 @@
#define VIRTIO_9P_MOUNT_TAG 0
enum {
+ P9_TLERROR = 6,
+ P9_RLERROR,
+ P9_TSTATFS = 8,
+ P9_RSTATFS,
+ P9_TLOPEN = 12,
+ P9_RLOPEN,
+ P9_TLCREATE = 14,
+ P9_RLCREATE,
+ P9_TSYMLINK = 16,
+ P9_RSYMLINK,
+ P9_TMKNOD = 18,
+ P9_RMKNOD,
+ P9_TRENAME = 20,
+ P9_RRENAME,
+ P9_TREADLINK = 22,
+ P9_RREADLINK,
+ P9_TGETATTR = 24,
+ P9_RGETATTR,
+ P9_TSETATTR = 26,
+ P9_RSETATTR,
+ P9_TXATTRWALK = 30,
+ P9_RXATTRWALK,
+ P9_TXATTRCREATE = 32,
+ P9_RXATTRCREATE,
+ P9_TREADDIR = 40,
+ P9_RREADDIR,
+ P9_TFSYNC = 50,
+ P9_RFSYNC,
+ P9_TLOCK = 52,
+ P9_RLOCK,
+ P9_TGETLOCK = 54,
+ P9_RGETLOCK,
+ P9_TLINK = 70,
+ P9_RLINK,
+ P9_TMKDIR = 72,
+ P9_RMKDIR,
P9_TVERSION = 100,
P9_RVERSION,
P9_TAUTH = 102,
@@ -57,10 +93,21 @@ enum {
P9_QTFILE = 0x00,
};
+enum p9_proto_version {
+ V9FS_PROTO_2000U = 0x01,
+ V9FS_PROTO_2000L = 0x02,
+};
+
#define P9_NOTAG (u16)(~0)
#define P9_NOFID (u32)(~0)
#define P9_MAXWELEM 16
+/*
+ * ample room for Twrite/Rread header
+ * size[4] Tread/Twrite tag[2] fid[4] offset[8] count[4]
+ */
+#define P9_IOHDRSZ 24
+
typedef struct V9fsPDU V9fsPDU;
struct V9fsPDU
@@ -122,12 +169,32 @@ typedef struct V9fsStat
int32_t n_muid;
} V9fsStat;
+enum {
+ P9_FID_NONE = 0,
+ P9_FID_FILE,
+ P9_FID_DIR,
+ P9_FID_XATTR,
+};
+
+typedef struct V9fsXattr
+{
+ int64_t copied_len;
+ int64_t len;
+ void *value;
+ V9fsString name;
+ int flags;
+} V9fsXattr;
+
struct V9fsFidState
{
+ int fid_type;
int32_t fid;
V9fsString path;
- int fd;
- DIR *dir;
+ union {
+ int fd;
+ DIR *dir;
+ V9fsXattr xattr;
+ } fs;
uid_t uid;
V9fsFidState *next;
};
@@ -144,6 +211,8 @@ typedef struct V9fsState
uint16_t tag_len;
uint8_t *tag;
size_t config_size;
+ enum p9_proto_version proto_version;
+ int32_t msize;
} V9fsState;
typedef struct V9fsCreateState {
@@ -157,8 +226,20 @@ typedef struct V9fsCreateState {
V9fsString name;
V9fsString extension;
V9fsString fullname;
+ int iounit;
} V9fsCreateState;
+typedef struct V9fsLcreateState {
+ V9fsPDU *pdu;
+ size_t offset;
+ V9fsFidState *fidp;
+ V9fsQID qid;
+ int32_t iounit;
+ struct stat stbuf;
+ V9fsString name;
+ V9fsString fullname;
+} V9fsLcreateState;
+
typedef struct V9fsStatState {
V9fsPDU *pdu;
size_t offset;
@@ -167,6 +248,37 @@ typedef struct V9fsStatState {
struct stat stbuf;
} V9fsStatState;
+typedef struct V9fsStatDotl {
+ uint64_t st_result_mask;
+ V9fsQID qid;
+ uint32_t st_mode;
+ uint32_t st_uid;
+ uint32_t st_gid;
+ uint64_t st_nlink;
+ uint64_t st_rdev;
+ uint64_t st_size;
+ uint64_t st_blksize;
+ uint64_t st_blocks;
+ uint64_t st_atime_sec;
+ uint64_t st_atime_nsec;
+ uint64_t st_mtime_sec;
+ uint64_t st_mtime_nsec;
+ uint64_t st_ctime_sec;
+ uint64_t st_ctime_nsec;
+ uint64_t st_btime_sec;
+ uint64_t st_btime_nsec;
+ uint64_t st_gen;
+ uint64_t st_data_version;
+} V9fsStatDotl;
+
+typedef struct V9fsStatStateDotl {
+ V9fsPDU *pdu;
+ size_t offset;
+ V9fsStatDotl v9stat_dotl;
+ struct stat stbuf;
+} V9fsStatStateDotl;
+
+
typedef struct V9fsWalkState {
V9fsPDU *pdu;
size_t offset;
@@ -183,10 +295,11 @@ typedef struct V9fsWalkState {
typedef struct V9fsOpenState {
V9fsPDU *pdu;
size_t offset;
- int8_t mode;
+ int32_t mode;
V9fsFidState *fidp;
V9fsQID qid;
struct stat stbuf;
+ int iounit;
} V9fsOpenState;
typedef struct V9fsReadState {
@@ -235,9 +348,41 @@ typedef struct V9fsWstatState
V9fsStat v9stat;
V9fsFidState *fidp;
struct stat stbuf;
- V9fsString nname;
} V9fsWstatState;
+typedef struct V9fsSymlinkState
+{
+ V9fsPDU *pdu;
+ size_t offset;
+ V9fsString name;
+ V9fsString symname;
+ V9fsString fullname;
+ V9fsFidState *dfidp;
+ V9fsQID qid;
+ struct stat stbuf;
+} V9fsSymlinkState;
+
+typedef struct V9fsIattr
+{
+ int32_t valid;
+ int32_t mode;
+ int32_t uid;
+ int32_t gid;
+ int64_t size;
+ int64_t atime_sec;
+ int64_t atime_nsec;
+ int64_t mtime_sec;
+ int64_t mtime_nsec;
+} V9fsIattr;
+
+typedef struct V9fsSetattrState
+{
+ V9fsPDU *pdu;
+ size_t offset;
+ V9fsIattr v9iattr;
+ V9fsFidState *fidp;
+} V9fsSetattrState;
+
struct virtio_9p_config
{
/* number of characters in tag */
@@ -246,6 +391,110 @@ struct virtio_9p_config
uint8_t tag[0];
} __attribute__((packed));
+typedef struct V9fsStatfs
+{
+ uint32_t f_type;
+ uint32_t f_bsize;
+ uint64_t f_blocks;
+ uint64_t f_bfree;
+ uint64_t f_bavail;
+ uint64_t f_files;
+ uint64_t f_ffree;
+ uint64_t fsid_val;
+ uint32_t f_namelen;
+} V9fsStatfs;
+
+typedef struct V9fsStatfsState {
+ V9fsPDU *pdu;
+ size_t offset;
+ int32_t fid;
+ V9fsStatfs v9statfs;
+ V9fsFidState *fidp;
+ struct statfs stbuf;
+} V9fsStatfsState;
+
+typedef struct V9fsMkState {
+ V9fsPDU *pdu;
+ size_t offset;
+ V9fsQID qid;
+ struct stat stbuf;
+ V9fsString name;
+ V9fsString fullname;
+} V9fsMkState;
+
+typedef struct V9fsRenameState {
+ V9fsPDU *pdu;
+ V9fsFidState *fidp;
+ size_t offset;
+ int32_t newdirfid;
+ V9fsString name;
+} V9fsRenameState;
+
+typedef struct V9fsXattrState
+{
+ V9fsPDU *pdu;
+ size_t offset;
+ V9fsFidState *file_fidp;
+ V9fsFidState *xattr_fidp;
+ V9fsString name;
+ int64_t size;
+ int flags;
+ void *value;
+} V9fsXattrState;
+
+#define P9_LOCK_SUCCESS 0
+#define P9_LOCK_BLOCKED 1
+#define P9_LOCK_ERROR 2
+#define P9_LOCK_GRACE 3
+
+#define P9_LOCK_FLAGS_BLOCK 1
+#define P9_LOCK_FLAGS_RECLAIM 2
+
+typedef struct V9fsFlock
+{
+ uint8_t type;
+ uint32_t flags;
+ uint64_t start; /* absolute offset */
+ uint64_t length;
+ uint32_t proc_id;
+ V9fsString client_id;
+} V9fsFlock;
+
+typedef struct V9fsLockState
+{
+ V9fsPDU *pdu;
+ size_t offset;
+ int8_t status;
+ struct stat stbuf;
+ V9fsFidState *fidp;
+ V9fsFlock *flock;
+} V9fsLockState;
+
+typedef struct V9fsGetlock
+{
+ uint8_t type;
+ uint64_t start; /* absolute offset */
+ uint64_t length;
+ uint32_t proc_id;
+ V9fsString client_id;
+} V9fsGetlock;
+
+typedef struct V9fsGetlockState
+{
+ V9fsPDU *pdu;
+ size_t offset;
+ struct stat stbuf;
+ V9fsFidState *fidp;
+ V9fsGetlock *glock;
+} V9fsGetlockState;
+
+typedef struct V9fsReadLinkState
+{
+ V9fsPDU *pdu;
+ size_t offset;
+ V9fsString target;
+} V9fsReadLinkState;
+
extern size_t pdu_packunpack(void *addr, struct iovec *sg, int sg_count,
size_t offset, size_t size, int pack);
diff --git a/hw/virtio-balloon.c b/hw/virtio-balloon.c
index 9fe3886b0f..8adddeaa53 100644
--- a/hw/virtio-balloon.c
+++ b/hw/virtio-balloon.c
@@ -29,6 +29,10 @@
#include <sys/mman.h>
#endif
+/* Disable guest-provided stats by now (https://bugzilla.redhat.com/show_bug.cgi?id=623903) */
+#define ENABLE_GUEST_STATS 0
+
+
typedef struct VirtIOBalloon
{
VirtIODevice vdev;
@@ -51,8 +55,8 @@ static void balloon_page(void *addr, int deflate)
{
#if defined(__linux__)
if (!kvm_enabled() || kvm_has_sync_mmu())
- madvise(addr, TARGET_PAGE_SIZE,
- deflate ? MADV_WILLNEED : MADV_DONTNEED);
+ qemu_madvise(addr, TARGET_PAGE_SIZE,
+ deflate ? QEMU_MADV_WILLNEED : QEMU_MADV_DONTNEED);
#endif
}
@@ -83,12 +87,14 @@ static QObject *get_stats_qobject(VirtIOBalloon *dev)
VIRTIO_BALLOON_PFN_SHIFT);
stat_put(dict, "actual", actual);
+#if ENABLE_GUEST_STATS
stat_put(dict, "mem_swapped_in", dev->stats[VIRTIO_BALLOON_S_SWAP_IN]);
stat_put(dict, "mem_swapped_out", dev->stats[VIRTIO_BALLOON_S_SWAP_OUT]);
stat_put(dict, "major_page_faults", dev->stats[VIRTIO_BALLOON_S_MAJFLT]);
stat_put(dict, "minor_page_faults", dev->stats[VIRTIO_BALLOON_S_MINFLT]);
stat_put(dict, "free_mem", dev->stats[VIRTIO_BALLOON_S_MEMFREE]);
stat_put(dict, "total_mem", dev->stats[VIRTIO_BALLOON_S_MEMTOT]);
+#endif
return QOBJECT(dict);
}
@@ -214,7 +220,7 @@ static void virtio_balloon_to_target(void *opaque, ram_addr_t target,
}
dev->stats_callback = cb;
dev->stats_opaque_callback_data = cb_data;
- if (dev->vdev.guest_features & (1 << VIRTIO_BALLOON_F_STATS_VQ)) {
+ if (ENABLE_GUEST_STATS && (dev->vdev.guest_features & (1 << VIRTIO_BALLOON_F_STATS_VQ))) {
virtqueue_push(dev->svq, &dev->stats_vq_elem, dev->stats_vq_offset);
virtio_notify(&dev->vdev, dev->svq);
} else {
diff --git a/hw/virtio-blk.c b/hw/virtio-blk.c
index 8747634fbe..a1df26dbcf 100644
--- a/hw/virtio-blk.c
+++ b/hw/virtio-blk.c
@@ -12,6 +12,9 @@
*/
#include <qemu-common.h>
+#include "qemu-error.h"
+#include "trace.h"
+#include "blockdev.h"
#include "virtio-blk.h"
#ifdef __linux__
# include <scsi/sg.h>
@@ -27,6 +30,7 @@ typedef struct VirtIOBlock
BlockConf *conf;
unsigned short sector_mask;
char sn[BLOCK_SERIAL_STRLEN];
+ DeviceState *qdev;
} VirtIOBlock;
static VirtIOBlock *to_virtio_blk(VirtIODevice *vdev)
@@ -49,6 +53,8 @@ static void virtio_blk_req_complete(VirtIOBlockReq *req, int status)
{
VirtIOBlock *s = req->dev;
+ trace_virtio_blk_req_complete(req, status);
+
req->in->status = status;
virtqueue_push(s->vq, &req->elem, req->qiov.size + sizeof(*req->in));
virtio_notify(&s->vdev, s->vq);
@@ -85,6 +91,8 @@ static void virtio_blk_rw_complete(void *opaque, int ret)
{
VirtIOBlockReq *req = opaque;
+ trace_virtio_blk_rw_complete(req, ret);
+
if (ret) {
int is_read = !(req->out->type & VIRTIO_BLK_T_OUT);
if (virtio_blk_handle_rw_error(req, -ret, is_read))
@@ -267,6 +275,8 @@ static void virtio_blk_handle_write(VirtIOBlockReq *req, MultiReqBuffer *mrb)
{
BlockRequest *blkreq;
+ trace_virtio_blk_handle_write(req, req->out->sector, req->qiov.size / 512);
+
if (req->out->sector & req->dev->sector_mask) {
virtio_blk_rw_complete(req, -EIO);
return;
@@ -478,6 +488,11 @@ static int virtio_blk_load(QEMUFile *f, void *opaque, int version_id)
qemu_get_buffer(f, (unsigned char*)&req->elem, sizeof(req->elem));
req->next = s->rq;
s->rq = req;
+
+ virtqueue_map_sg(req->elem.in_sg, req->elem.in_addr,
+ req->elem.in_num, 1);
+ virtqueue_map_sg(req->elem.out_sg, req->elem.out_addr,
+ req->elem.out_num, 0);
}
return 0;
@@ -490,6 +505,15 @@ VirtIODevice *virtio_blk_init(DeviceState *dev, BlockConf *conf)
static int virtio_blk_id;
DriveInfo *dinfo;
+ if (!conf->bs) {
+ error_report("virtio-blk-pci: drive property not set");
+ return NULL;
+ }
+ if (!bdrv_is_inserted(conf->bs)) {
+ error_report("Device needs media, but drive is empty");
+ return NULL;
+ }
+
s = (VirtIOBlock *)virtio_common_init("virtio-blk", VIRTIO_ID_BLOCK,
sizeof(struct virtio_blk_config),
sizeof(VirtIOBlock));
@@ -512,9 +536,17 @@ VirtIODevice *virtio_blk_init(DeviceState *dev, BlockConf *conf)
s->vq = virtio_add_queue(&s->vdev, 128, virtio_blk_handle_output);
qemu_add_vm_change_state_handler(virtio_blk_dma_restart_cb, s);
+ s->qdev = dev;
register_savevm(dev, "virtio-blk", virtio_blk_id++, 2,
virtio_blk_save, virtio_blk_load, s);
bdrv_set_removable(s->bs, 0);
+ s->bs->buffer_alignment = conf->logical_block_size;
return &s->vdev;
}
+
+void virtio_blk_exit(VirtIODevice *vdev)
+{
+ VirtIOBlock *s = to_virtio_blk(vdev);
+ unregister_savevm(s->qdev, "virtio-blk", s);
+}
diff --git a/hw/virtio-net.c b/hw/virtio-net.c
index 075f72df2d..7e1688cf69 100644
--- a/hw/virtio-net.c
+++ b/hw/virtio-net.c
@@ -36,7 +36,10 @@ typedef struct VirtIONet
VirtQueue *ctrl_vq;
NICState *nic;
QEMUTimer *tx_timer;
- int tx_timer_active;
+ QEMUBH *tx_bh;
+ uint32_t tx_timeout;
+ int32_t tx_burst;
+ int tx_waiting;
uint32_t has_vnet_hdr;
uint8_t has_ufo;
struct {
@@ -51,6 +54,7 @@ typedef struct VirtIONet
uint8_t nouni;
uint8_t nobcast;
uint8_t vhost_started;
+ bool vm_running;
VMChangeStateEntry *vmstate;
struct {
int in_use;
@@ -95,6 +99,38 @@ static void virtio_net_set_config(VirtIODevice *vdev, const uint8_t *config)
}
}
+static void virtio_net_set_status(struct VirtIODevice *vdev, uint8_t status)
+{
+ VirtIONet *n = to_virtio_net(vdev);
+ if (!n->nic->nc.peer) {
+ return;
+ }
+ if (n->nic->nc.peer->info->type != NET_CLIENT_TYPE_TAP) {
+ return;
+ }
+
+ if (!tap_get_vhost_net(n->nic->nc.peer)) {
+ return;
+ }
+ if (!!n->vhost_started == ((status & VIRTIO_CONFIG_S_DRIVER_OK) &&
+ (n->status & VIRTIO_NET_S_LINK_UP) &&
+ n->vm_running)) {
+ return;
+ }
+ if (!n->vhost_started) {
+ int r = vhost_net_start(tap_get_vhost_net(n->nic->nc.peer), &n->vdev);
+ if (r < 0) {
+ fprintf(stderr, "unable to start vhost net: %d: "
+ "falling back on userspace virtio\n", -r);
+ } else {
+ n->vhost_started = 1;
+ }
+ } else {
+ vhost_net_stop(tap_get_vhost_net(n->nic->nc.peer), &n->vdev);
+ n->vhost_started = 0;
+ }
+}
+
static void virtio_net_set_link_status(VLANClientState *nc)
{
VirtIONet *n = DO_UPCAST(NICState, nc, nc)->opaque;
@@ -107,6 +143,8 @@ static void virtio_net_set_link_status(VLANClientState *nc)
if (n->status != old_status)
virtio_notify_config(&n->vdev);
+
+ virtio_net_set_status(&n->vdev, n->vdev.status);
}
static void virtio_net_reset(VirtIODevice *vdev)
@@ -120,10 +158,6 @@ static void virtio_net_reset(VirtIODevice *vdev)
n->nomulti = 0;
n->nouni = 0;
n->nobcast = 0;
- if (n->vhost_started) {
- vhost_net_stop(tap_get_vhost_net(n->nic->nc.peer), vdev);
- n->vhost_started = 0;
- }
/* Flush any MAC and VLAN filter table state */
n->mac_table.in_use = 0;
@@ -619,7 +653,7 @@ static ssize_t virtio_net_receive(VLANClientState *nc, const uint8_t *buf, size_
return size;
}
-static void virtio_net_flush_tx(VirtIONet *n, VirtQueue *vq);
+static int32_t virtio_net_flush_tx(VirtIONet *n, VirtQueue *vq);
static void virtio_net_tx_complete(VLANClientState *nc, ssize_t len)
{
@@ -635,16 +669,18 @@ static void virtio_net_tx_complete(VLANClientState *nc, ssize_t len)
}
/* TX */
-static void virtio_net_flush_tx(VirtIONet *n, VirtQueue *vq)
+static int32_t virtio_net_flush_tx(VirtIONet *n, VirtQueue *vq)
{
VirtQueueElement elem;
+ int32_t num_packets = 0;
- if (!(n->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK))
- return;
+ if (!(n->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK)) {
+ return num_packets;
+ }
if (n->async_tx.elem.out_num) {
virtio_queue_set_notification(n->tx_vq, 0);
- return;
+ return num_packets;
}
while (virtqueue_pop(vq, &elem)) {
@@ -681,38 +717,55 @@ static void virtio_net_flush_tx(VirtIONet *n, VirtQueue *vq)
virtio_queue_set_notification(n->tx_vq, 0);
n->async_tx.elem = elem;
n->async_tx.len = len;
- return;
+ return -EBUSY;
}
len += ret;
virtqueue_push(vq, &elem, len);
virtio_notify(&n->vdev, vq);
+
+ if (++num_packets >= n->tx_burst) {
+ break;
+ }
}
+ return num_packets;
}
-static void virtio_net_handle_tx(VirtIODevice *vdev, VirtQueue *vq)
+static void virtio_net_handle_tx_timer(VirtIODevice *vdev, VirtQueue *vq)
{
VirtIONet *n = to_virtio_net(vdev);
- if (n->tx_timer_active) {
+ if (n->tx_waiting) {
virtio_queue_set_notification(vq, 1);
qemu_del_timer(n->tx_timer);
- n->tx_timer_active = 0;
+ n->tx_waiting = 0;
virtio_net_flush_tx(n, vq);
} else {
qemu_mod_timer(n->tx_timer,
- qemu_get_clock(vm_clock) + TX_TIMER_INTERVAL);
- n->tx_timer_active = 1;
+ qemu_get_clock(vm_clock) + n->tx_timeout);
+ n->tx_waiting = 1;
virtio_queue_set_notification(vq, 0);
}
}
+static void virtio_net_handle_tx_bh(VirtIODevice *vdev, VirtQueue *vq)
+{
+ VirtIONet *n = to_virtio_net(vdev);
+
+ if (unlikely(n->tx_waiting)) {
+ return;
+ }
+ virtio_queue_set_notification(vq, 0);
+ qemu_bh_schedule(n->tx_bh);
+ n->tx_waiting = 1;
+}
+
static void virtio_net_tx_timer(void *opaque)
{
VirtIONet *n = opaque;
- n->tx_timer_active = 0;
+ n->tx_waiting = 0;
/* Just in case the driver is not ready on more */
if (!(n->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK))
@@ -722,20 +775,52 @@ static void virtio_net_tx_timer(void *opaque)
virtio_net_flush_tx(n, n->tx_vq);
}
-static void virtio_net_save(QEMUFile *f, void *opaque)
+static void virtio_net_tx_bh(void *opaque)
{
VirtIONet *n = opaque;
+ int32_t ret;
- if (n->vhost_started) {
- /* TODO: should we really stop the backend?
- * If we don't, it might keep writing to memory. */
- vhost_net_stop(tap_get_vhost_net(n->nic->nc.peer), &n->vdev);
- n->vhost_started = 0;
+ n->tx_waiting = 0;
+
+ /* Just in case the driver is not ready on more */
+ if (unlikely(!(n->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK)))
+ return;
+
+ ret = virtio_net_flush_tx(n, n->tx_vq);
+ if (ret == -EBUSY) {
+ return; /* Notification re-enable handled by tx_complete */
}
+
+ /* If we flush a full burst of packets, assume there are
+ * more coming and immediately reschedule */
+ if (ret >= n->tx_burst) {
+ qemu_bh_schedule(n->tx_bh);
+ n->tx_waiting = 1;
+ return;
+ }
+
+ /* If less than a full burst, re-enable notification and flush
+ * anything that may have come in while we weren't looking. If
+ * we find something, assume the guest is still active and reschedule */
+ virtio_queue_set_notification(n->tx_vq, 1);
+ if (virtio_net_flush_tx(n, n->tx_vq) > 0) {
+ virtio_queue_set_notification(n->tx_vq, 0);
+ qemu_bh_schedule(n->tx_bh);
+ n->tx_waiting = 1;
+ }
+}
+
+static void virtio_net_save(QEMUFile *f, void *opaque)
+{
+ VirtIONet *n = opaque;
+
+ /* At this point, backend must be stopped, otherwise
+ * it might keep writing to memory. */
+ assert(!n->vhost_started);
virtio_save(&n->vdev, f);
qemu_put_buffer(f, n->mac, ETH_ALEN);
- qemu_put_be32(f, n->tx_timer_active);
+ qemu_put_be32(f, n->tx_waiting);
qemu_put_be32(f, n->mergeable_rx_bufs);
qemu_put_be16(f, n->status);
qemu_put_byte(f, n->promisc);
@@ -764,7 +849,7 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id)
virtio_load(&n->vdev, f);
qemu_get_buffer(f, n->mac, ETH_ALEN);
- n->tx_timer_active = qemu_get_be32(f);
+ n->tx_waiting = qemu_get_be32(f);
n->mergeable_rx_bufs = qemu_get_be32(f);
if (version_id >= 3)
@@ -840,9 +925,13 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id)
}
n->mac_table.first_multi = i;
- if (n->tx_timer_active) {
- qemu_mod_timer(n->tx_timer,
- qemu_get_clock(vm_clock) + TX_TIMER_INTERVAL);
+ if (n->tx_waiting) {
+ if (n->tx_timer) {
+ qemu_mod_timer(n->tx_timer,
+ qemu_get_clock(vm_clock) + n->tx_timeout);
+ } else {
+ qemu_bh_schedule(n->tx_bh);
+ }
}
return 0;
}
@@ -863,47 +952,18 @@ static NetClientInfo net_virtio_info = {
.link_status_changed = virtio_net_set_link_status,
};
-static void virtio_net_set_status(struct VirtIODevice *vdev, uint8_t status)
-{
- VirtIONet *n = to_virtio_net(vdev);
- if (!n->nic->nc.peer) {
- return;
- }
- if (n->nic->nc.peer->info->type != NET_CLIENT_TYPE_TAP) {
- return;
- }
-
- if (!tap_get_vhost_net(n->nic->nc.peer)) {
- return;
- }
- if (!!n->vhost_started == !!(status & VIRTIO_CONFIG_S_DRIVER_OK)) {
- return;
- }
- if (status & VIRTIO_CONFIG_S_DRIVER_OK) {
- int r = vhost_net_start(tap_get_vhost_net(n->nic->nc.peer), vdev);
- if (r < 0) {
- fprintf(stderr, "unable to start vhost net: %d: "
- "falling back on userspace virtio\n", -r);
- } else {
- n->vhost_started = 1;
- }
- } else {
- vhost_net_stop(tap_get_vhost_net(n->nic->nc.peer), vdev);
- n->vhost_started = 0;
- }
-}
-
static void virtio_net_vmstate_change(void *opaque, int running, int reason)
{
VirtIONet *n = opaque;
- uint8_t status = running ? VIRTIO_CONFIG_S_DRIVER_OK : 0;
+ n->vm_running = running;
/* This is called when vm is started/stopped,
- * it will start/stop vhost backend if * appropriate
+ * it will start/stop vhost backend if appropriate
* e.g. after migration. */
- virtio_net_set_status(&n->vdev, n->vdev.status & status);
+ virtio_net_set_status(&n->vdev, n->vdev.status);
}
-VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf)
+VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf,
+ virtio_net_conf *net)
{
VirtIONet *n;
@@ -919,7 +979,22 @@ VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf)
n->vdev.reset = virtio_net_reset;
n->vdev.set_status = virtio_net_set_status;
n->rx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_rx);
- n->tx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_tx);
+
+ if (net->tx && strcmp(net->tx, "timer") && strcmp(net->tx, "bh")) {
+ fprintf(stderr, "virtio-net: "
+ "Unknown option tx=%s, valid options: \"timer\" \"bh\"\n",
+ net->tx);
+ fprintf(stderr, "Defaulting to \"bh\"\n");
+ }
+
+ if (net->tx && !strcmp(net->tx, "timer")) {
+ n->tx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_tx_timer);
+ n->tx_timer = qemu_new_timer(vm_clock, virtio_net_tx_timer, n);
+ n->tx_timeout = net->txtimer;
+ } else {
+ n->tx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_tx_bh);
+ n->tx_bh = qemu_bh_new(virtio_net_tx_bh, n);
+ }
n->ctrl_vq = virtio_add_queue(&n->vdev, 64, virtio_net_handle_ctrl);
qemu_macaddr_default_if_unset(&conf->macaddr);
memcpy(&n->mac[0], &conf->macaddr, sizeof(n->mac));
@@ -929,8 +1004,8 @@ VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf)
qemu_format_nic_info_str(&n->nic->nc, conf->macaddr.a);
- n->tx_timer = qemu_new_timer(vm_clock, virtio_net_tx_timer, n);
- n->tx_timer_active = 0;
+ n->tx_waiting = 0;
+ n->tx_burst = net->txburst;
n->mergeable_rx_bufs = 0;
n->promisc = 1; /* for compatibility */
@@ -951,9 +1026,8 @@ void virtio_net_exit(VirtIODevice *vdev)
VirtIONet *n = DO_UPCAST(VirtIONet, vdev, vdev);
qemu_del_vm_change_state_handler(n->vmstate);
- if (n->vhost_started) {
- vhost_net_stop(tap_get_vhost_net(n->nic->nc.peer), vdev);
- }
+ /* This will stop vhost backend if appropriate. */
+ virtio_net_set_status(vdev, 0);
qemu_purge_queued_packets(&n->nic->nc);
@@ -962,8 +1036,12 @@ void virtio_net_exit(VirtIODevice *vdev)
qemu_free(n->mac_table.macs);
qemu_free(n->vlans);
- qemu_del_timer(n->tx_timer);
- qemu_free_timer(n->tx_timer);
+ if (n->tx_timer) {
+ qemu_del_timer(n->tx_timer);
+ qemu_free_timer(n->tx_timer);
+ } else {
+ qemu_bh_delete(n->tx_bh);
+ }
virtio_cleanup(&n->vdev);
qemu_del_vlan_client(&n->nic->nc);
diff --git a/hw/virtio-net.h b/hw/virtio-net.h
index 235f1a9fa8..8af9a1ce55 100644
--- a/hw/virtio-net.h
+++ b/hw/virtio-net.h
@@ -49,6 +49,20 @@
#define TX_TIMER_INTERVAL 150000 /* 150 us */
+/* Limit the number of packets that can be sent via a single flush
+ * of the TX queue. This gives us a guaranteed exit condition and
+ * ensures fairness in the io path. 256 conveniently matches the
+ * length of the TX queue and shows a good balance of performance
+ * and latency. */
+#define TX_BURST 256
+
+typedef struct virtio_net_conf
+{
+ uint32_t txtimer;
+ int32_t txburst;
+ char *tx;
+} virtio_net_conf;
+
/* Maximum packet size we can receive from tap device: header + 64k */
#define VIRTIO_NET_MAX_BUFSIZE (sizeof(struct virtio_net_hdr) + (64 << 10))
diff --git a/hw/virtio-pci.c b/hw/virtio-pci.c
index c728fffd73..729917d891 100644
--- a/hw/virtio-pci.c
+++ b/hw/virtio-pci.c
@@ -24,6 +24,7 @@
#include "net.h"
#include "loader.h"
#include "kvm.h"
+#include "blockdev.h"
/* from Linux's linux/virtio_pci.h */
@@ -106,6 +107,7 @@ typedef struct {
#endif
/* Max. number of ports we can have for a the virtio-serial device */
uint32_t max_virtserial_ports;
+ virtio_net_conf net;
} VirtIOPCIProxy;
/* virtio device */
@@ -449,6 +451,33 @@ static int virtio_pci_set_guest_notifier(void *opaque, int n, bool assign)
return 0;
}
+static int virtio_pci_set_guest_notifiers(void *opaque, bool assign)
+{
+ VirtIOPCIProxy *proxy = opaque;
+ VirtIODevice *vdev = proxy->vdev;
+ int r, n;
+
+ for (n = 0; n < VIRTIO_PCI_QUEUE_MAX; n++) {
+ if (!virtio_queue_get_num(vdev, n)) {
+ break;
+ }
+
+ r = virtio_pci_set_guest_notifier(opaque, n, assign);
+ if (r < 0) {
+ goto assign_error;
+ }
+ }
+
+ return 0;
+
+assign_error:
+ /* We get here on assignment failure. Recover by undoing for VQs 0 .. n. */
+ while (--n >= 0) {
+ virtio_pci_set_guest_notifier(opaque, n, !assign);
+ }
+ return r;
+}
+
static int virtio_pci_set_host_notifier(void *opaque, int n, bool assign)
{
VirtIOPCIProxy *proxy = opaque;
@@ -486,7 +515,7 @@ static const VirtIOBindings virtio_pci_bindings = {
.load_queue = virtio_pci_load_queue,
.get_features = virtio_pci_get_features,
.set_host_notifier = virtio_pci_set_host_notifier,
- .set_guest_notifier = virtio_pci_set_guest_notifier,
+ .set_guest_notifiers = virtio_pci_set_guest_notifiers,
};
static void virtio_init_pci(VirtIOPCIProxy *proxy, VirtIODevice *vdev,
@@ -546,11 +575,10 @@ static int virtio_blk_init_pci(PCIDevice *pci_dev)
proxy->class_code != PCI_CLASS_STORAGE_OTHER)
proxy->class_code = PCI_CLASS_STORAGE_SCSI;
- if (!proxy->block.bs) {
- error_report("virtio-blk-pci: drive property not set");
+ vdev = virtio_blk_init(&pci_dev->qdev, &proxy->block);
+ if (!vdev) {
return -1;
}
- vdev = virtio_blk_init(&pci_dev->qdev, &proxy->block);
vdev->nvectors = proxy->nvectors;
virtio_init_pci(proxy, vdev,
PCI_VENDOR_ID_REDHAT_QUMRANET,
@@ -570,6 +598,7 @@ static int virtio_blk_exit_pci(PCIDevice *pci_dev)
{
VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev);
+ virtio_blk_exit(proxy->vdev);
blockdev_mark_auto_del(proxy->block.bs);
return virtio_exit_pci(pci_dev);
}
@@ -599,12 +628,20 @@ static int virtio_serial_init_pci(PCIDevice *pci_dev)
return 0;
}
+static int virtio_serial_exit_pci(PCIDevice *pci_dev)
+{
+ VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev);
+
+ virtio_serial_exit(proxy->vdev);
+ return virtio_exit_pci(pci_dev);
+}
+
static int virtio_net_init_pci(PCIDevice *pci_dev)
{
VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev);
VirtIODevice *vdev;
- vdev = virtio_net_init(&pci_dev->qdev, &proxy->nic);
+ vdev = virtio_net_init(&pci_dev->qdev, &proxy->nic, &proxy->net);
vdev->nvectors = proxy->nvectors;
virtio_init_pci(proxy, vdev,
@@ -681,6 +718,11 @@ static PCIDeviceInfo virtio_info[] = {
DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 3),
DEFINE_VIRTIO_NET_FEATURES(VirtIOPCIProxy, host_features),
DEFINE_NIC_PROPERTIES(VirtIOPCIProxy, nic),
+ DEFINE_PROP_UINT32("x-txtimer", VirtIOPCIProxy,
+ net.txtimer, TX_TIMER_INTERVAL),
+ DEFINE_PROP_INT32("x-txburst", VirtIOPCIProxy,
+ net.txburst, TX_BURST),
+ DEFINE_PROP_STRING("tx", VirtIOPCIProxy, net.tx),
DEFINE_PROP_END_OF_LIST(),
},
.qdev.reset = virtio_pci_reset,
@@ -689,7 +731,7 @@ static PCIDeviceInfo virtio_info[] = {
.qdev.alias = "virtio-serial",
.qdev.size = sizeof(VirtIOPCIProxy),
.init = virtio_serial_init_pci,
- .exit = virtio_exit_pci,
+ .exit = virtio_serial_exit_pci,
.qdev.props = (Property[]) {
DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors,
DEV_NVECTORS_UNSPECIFIED),
diff --git a/hw/virtio-serial-bus.c b/hw/virtio-serial-bus.c
index 26d5841154..74ba5ec3d3 100644
--- a/hw/virtio-serial-bus.c
+++ b/hw/virtio-serial-bus.c
@@ -41,6 +41,8 @@ struct VirtIOSerial {
VirtIOSerialBus *bus;
+ DeviceState *qdev;
+
QTAILQ_HEAD(, VirtIOSerialPort) ports;
/* bitmap for identifying active ports */
@@ -117,6 +119,7 @@ static void do_flush_queued_data(VirtIOSerialPort *port, VirtQueue *vq,
VirtQueueElement elem;
assert(port || discard);
+ assert(virtio_queue_ready(vq));
while ((discard || !port->throttled) && virtqueue_pop(vq, &elem)) {
uint8_t *buf;
@@ -139,6 +142,9 @@ static void flush_queued_data(VirtIOSerialPort *port, bool discard)
{
assert(port);
+ if (!virtio_queue_ready(port->ovq)) {
+ return;
+ }
do_flush_queued_data(port, port->ovq, &port->vser->vdev, discard);
}
@@ -730,11 +736,19 @@ VirtIODevice *virtio_serial_init(DeviceState *dev, uint32_t max_nr_ports)
{
VirtIOSerial *vser;
VirtIODevice *vdev;
- uint32_t i;
+ uint32_t i, max_supported_ports;
if (!max_nr_ports)
return NULL;
+ /* Each port takes 2 queues, and one pair is for the control queue */
+ max_supported_ports = VIRTIO_PCI_QUEUE_MAX / 2 - 1;
+
+ if (max_nr_ports > max_supported_ports) {
+ error_report("maximum ports supported: %u", max_supported_ports);
+ return NULL;
+ }
+
vdev = virtio_common_init("virtio-serial", VIRTIO_ID_CONSOLE,
sizeof(struct virtio_console_config),
sizeof(VirtIOSerial));
@@ -780,6 +794,8 @@ VirtIODevice *virtio_serial_init(DeviceState *dev, uint32_t max_nr_ports)
vser->vdev.get_config = get_config;
vser->vdev.set_config = set_config;
+ vser->qdev = dev;
+
/*
* Register for the savevm section with the virtio-console name
* to preserve backward compat
@@ -789,3 +805,16 @@ VirtIODevice *virtio_serial_init(DeviceState *dev, uint32_t max_nr_ports)
return vdev;
}
+
+void virtio_serial_exit(VirtIODevice *vdev)
+{
+ VirtIOSerial *vser = DO_UPCAST(VirtIOSerial, vdev, vdev);
+
+ unregister_savevm(vser->qdev, "virtio-console", vser);
+
+ qemu_free(vser->ivqs);
+ qemu_free(vser->ovqs);
+ qemu_free(vser->ports_map);
+
+ virtio_cleanup(vdev);
+}
diff --git a/hw/virtio.c b/hw/virtio.c
index 4475bb3e44..a2a657e132 100644
--- a/hw/virtio.c
+++ b/hw/virtio.c
@@ -13,6 +13,7 @@
#include <inttypes.h>
+#include "trace.h"
#include "virtio.h"
#include "sysemu.h"
@@ -205,6 +206,8 @@ void virtqueue_fill(VirtQueue *vq, const VirtQueueElement *elem,
unsigned int offset;
int i;
+ trace_virtqueue_fill(vq, elem, len, idx);
+
offset = 0;
for (i = 0; i < elem->in_num; i++) {
size_t size = MIN(len - offset, elem->in_sg[i].iov_len);
@@ -232,6 +235,7 @@ void virtqueue_flush(VirtQueue *vq, unsigned int count)
{
/* Make sure buffer is written before we update index. */
wmb();
+ trace_virtqueue_flush(vq, count);
vring_used_idx_increment(vq, count);
vq->inuse -= count;
}
@@ -360,11 +364,26 @@ int virtqueue_avail_bytes(VirtQueue *vq, int in_bytes, int out_bytes)
return 0;
}
+void virtqueue_map_sg(struct iovec *sg, target_phys_addr_t *addr,
+ size_t num_sg, int is_write)
+{
+ unsigned int i;
+ target_phys_addr_t len;
+
+ for (i = 0; i < num_sg; i++) {
+ len = sg[i].iov_len;
+ sg[i].iov_base = cpu_physical_memory_map(addr[i], &len, is_write);
+ if (sg[i].iov_base == NULL || len != sg[i].iov_len) {
+ fprintf(stderr, "virtio: trying to map MMIO memory\n");
+ exit(1);
+ }
+ }
+}
+
int virtqueue_pop(VirtQueue *vq, VirtQueueElement *elem)
{
unsigned int i, head, max;
target_phys_addr_t desc_pa = vq->vring.desc;
- target_phys_addr_t len;
if (!virtqueue_num_heads(vq, vq->last_avail_idx))
return 0;
@@ -388,28 +407,19 @@ int virtqueue_pop(VirtQueue *vq, VirtQueueElement *elem)
i = 0;
}
+ /* Collect all the descriptors */
do {
struct iovec *sg;
- int is_write = 0;
if (vring_desc_flags(desc_pa, i) & VRING_DESC_F_WRITE) {
elem->in_addr[elem->in_num] = vring_desc_addr(desc_pa, i);
sg = &elem->in_sg[elem->in_num++];
- is_write = 1;
- } else
+ } else {
+ elem->out_addr[elem->out_num] = vring_desc_addr(desc_pa, i);
sg = &elem->out_sg[elem->out_num++];
+ }
- /* Grab the first descriptor, and check it's OK. */
sg->iov_len = vring_desc_len(desc_pa, i);
- len = sg->iov_len;
-
- sg->iov_base = cpu_physical_memory_map(vring_desc_addr(desc_pa, i),
- &len, is_write);
-
- if (sg->iov_base == NULL || len != sg->iov_len) {
- fprintf(stderr, "virtio: trying to map MMIO memory\n");
- exit(1);
- }
/* If we've got too many, that implies a descriptor loop. */
if ((elem->in_num + elem->out_num) > max) {
@@ -418,10 +428,15 @@ int virtqueue_pop(VirtQueue *vq, VirtQueueElement *elem)
}
} while ((i = virtqueue_next_desc(desc_pa, i, max)) != max);
+ /* Now map what we have collected */
+ virtqueue_map_sg(elem->in_sg, elem->in_addr, elem->in_num, 1);
+ virtqueue_map_sg(elem->out_sg, elem->out_addr, elem->out_num, 0);
+
elem->index = head;
vq->inuse++;
+ trace_virtqueue_pop(vq, elem, elem->in_num, elem->out_num);
return elem->in_num + elem->out_num;
}
@@ -443,6 +458,8 @@ void virtio_reset(void *opaque)
VirtIODevice *vdev = opaque;
int i;
+ virtio_set_status(vdev, 0);
+
if (vdev->reset)
vdev->reset(vdev);
@@ -560,6 +577,7 @@ int virtio_queue_get_num(VirtIODevice *vdev, int n)
void virtio_queue_notify(VirtIODevice *vdev, int n)
{
if (n < VIRTIO_PCI_QUEUE_MAX && vdev->vq[n].vring.desc) {
+ trace_virtio_queue_notify(vdev, n, &vdev->vq[n]);
vdev->vq[n].handle_output(vdev, &vdev->vq[n]);
}
}
@@ -597,6 +615,7 @@ VirtQueue *virtio_add_queue(VirtIODevice *vdev, int queue_size,
void virtio_irq(VirtQueue *vq)
{
+ trace_virtio_irq(vq);
vq->vdev->isr |= 0x01;
virtio_notify_vector(vq->vdev, vq->vector);
}
@@ -609,6 +628,7 @@ void virtio_notify(VirtIODevice *vdev, VirtQueue *vq)
(vq->inuse || vring_avail_idx(vq) != vq->last_avail_idx)))
return;
+ trace_virtio_notify(vdev, vq);
vdev->isr |= 0x01;
virtio_notify_vector(vdev, vq->vector);
}
@@ -661,6 +681,7 @@ int virtio_load(VirtIODevice *vdev, QEMUFile *f)
uint32_t features;
uint32_t supported_features =
vdev->binding->get_features(vdev->binding_opaque);
+ uint16_t num_heads;
if (vdev->binding->load_config) {
ret = vdev->binding->load_config(vdev->binding_opaque, f);
@@ -693,6 +714,16 @@ int virtio_load(VirtIODevice *vdev, QEMUFile *f)
if (vdev->vq[i].pa) {
virtqueue_init(&vdev->vq[i]);
}
+ num_heads = vring_avail_idx(&vdev->vq[i]) - vdev->vq[i].last_avail_idx;
+ /* Check it isn't doing very strange things with descriptor numbers. */
+ if (num_heads > vdev->vq[i].vring.num) {
+ fprintf(stderr, "VQ %d size 0x%x Guest index 0x%x "
+ "inconsistent with Host index 0x%x: delta 0x%x\n",
+ i, vdev->vq[i].vring.num,
+ vring_avail_idx(&vdev->vq[i]),
+ vdev->vq[i].last_avail_idx, num_heads);
+ return -1;
+ }
if (vdev->binding->load_queue) {
ret = vdev->binding->load_queue(vdev->binding_opaque, i, f);
if (ret)
diff --git a/hw/virtio.h b/hw/virtio.h
index e4306cd750..02fa312d3e 100644
--- a/hw/virtio.h
+++ b/hw/virtio.h
@@ -81,6 +81,7 @@ typedef struct VirtQueueElement
unsigned int out_num;
unsigned int in_num;
target_phys_addr_t in_addr[VIRTQUEUE_MAX_SIZE];
+ target_phys_addr_t out_addr[VIRTQUEUE_MAX_SIZE];
struct iovec in_sg[VIRTQUEUE_MAX_SIZE];
struct iovec out_sg[VIRTQUEUE_MAX_SIZE];
} VirtQueueElement;
@@ -92,7 +93,7 @@ typedef struct {
int (*load_config)(void * opaque, QEMUFile *f);
int (*load_queue)(void * opaque, int n, QEMUFile *f);
unsigned (*get_features)(void * opaque);
- int (*set_guest_notifier)(void * opaque, int n, bool assigned);
+ int (*set_guest_notifiers)(void * opaque, bool assigned);
int (*set_host_notifier)(void * opaque, int n, bool assigned);
} VirtIOBindings;
@@ -142,6 +143,8 @@ void virtqueue_flush(VirtQueue *vq, unsigned int count);
void virtqueue_fill(VirtQueue *vq, const VirtQueueElement *elem,
unsigned int len, unsigned int idx);
+void virtqueue_map_sg(struct iovec *sg, target_phys_addr_t *addr,
+ size_t num_sg, int is_write);
int virtqueue_pop(VirtQueue *vq, VirtQueueElement *elem);
int virtqueue_avail_bytes(VirtQueue *vq, int in_bytes, int out_bytes);
@@ -185,7 +188,9 @@ void virtio_bind_device(VirtIODevice *vdev, const VirtIOBindings *binding,
/* Base devices. */
VirtIODevice *virtio_blk_init(DeviceState *dev, BlockConf *conf);
-VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf);
+struct virtio_net_conf;
+VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf,
+ struct virtio_net_conf *net);
VirtIODevice *virtio_serial_init(DeviceState *dev, uint32_t max_nr_ports);
VirtIODevice *virtio_balloon_init(DeviceState *dev);
#ifdef CONFIG_LINUX
@@ -194,6 +199,8 @@ VirtIODevice *virtio_9p_init(DeviceState *dev, V9fsConf *conf);
void virtio_net_exit(VirtIODevice *vdev);
+void virtio_blk_exit(VirtIODevice *vdev);
+void virtio_serial_exit(VirtIODevice *vdev);
#define DEFINE_VIRTIO_COMMON_FEATURES(_state, _field) \
DEFINE_PROP_BIT("indirect_desc", _state, _field, \
diff --git a/hw/vmmouse.c b/hw/vmmouse.c
index f3593047e7..209711942f 100644
--- a/hw/vmmouse.c
+++ b/hw/vmmouse.c
@@ -100,16 +100,29 @@ static void vmmouse_mouse_event(void *opaque, int x, int y, int dz, int buttons_
i8042_isa_mouse_fake_event(s->ps2_mouse);
}
-static void vmmouse_update_handler(VMMouseState *s)
+static void vmmouse_remove_handler(VMMouseState *s)
{
if (s->entry) {
qemu_remove_mouse_event_handler(s->entry);
s->entry = NULL;
}
- if (s->status == 0)
+}
+
+static void vmmouse_update_handler(VMMouseState *s, int absolute)
+{
+ if (s->status != 0) {
+ return;
+ }
+ if (s->absolute != absolute) {
+ s->absolute = absolute;
+ vmmouse_remove_handler(s);
+ }
+ if (s->entry == NULL) {
s->entry = qemu_add_mouse_event_handler(vmmouse_mouse_event,
s, s->absolute,
"vmmouse");
+ qemu_activate_mouse_event_handler(s->entry);
+ }
}
static void vmmouse_read_id(VMMouseState *s)
@@ -121,28 +134,25 @@ static void vmmouse_read_id(VMMouseState *s)
s->queue[s->nb_queue++] = VMMOUSE_VERSION;
s->status = 0;
- vmmouse_update_handler(s);
}
static void vmmouse_request_relative(VMMouseState *s)
{
DPRINTF("vmmouse_request_relative()\n");
- s->absolute = 0;
- vmmouse_update_handler(s);
+ vmmouse_update_handler(s, 0);
}
static void vmmouse_request_absolute(VMMouseState *s)
{
DPRINTF("vmmouse_request_absolute()\n");
- s->absolute = 1;
- vmmouse_update_handler(s);
+ vmmouse_update_handler(s, 1);
}
static void vmmouse_disable(VMMouseState *s)
{
DPRINTF("vmmouse_disable()\n");
s->status = 0xffff;
- vmmouse_update_handler(s);
+ vmmouse_remove_handler(s);
}
static void vmmouse_data(VMMouseState *s, uint32_t *data, uint32_t size)
@@ -154,7 +164,7 @@ static void vmmouse_data(VMMouseState *s, uint32_t *data, uint32_t size)
if (size == 0 || size > 6 || size > s->nb_queue) {
printf("vmmouse: driver requested too much data %d\n", size);
s->status = 0xffff;
- vmmouse_update_handler(s);
+ vmmouse_remove_handler(s);
return;
}
@@ -239,7 +249,8 @@ static int vmmouse_post_load(void *opaque, int version_id)
{
VMMouseState *s = opaque;
- vmmouse_update_handler(s);
+ vmmouse_remove_handler(s);
+ vmmouse_update_handler(s, s->absolute);
return 0;
}
diff --git a/hw/vmware_vga.c b/hw/vmware_vga.c
index 12bff480eb..3d25c14da9 100644
--- a/hw/vmware_vga.c
+++ b/hw/vmware_vga.c
@@ -519,11 +519,15 @@ static inline void vmsvga_cursor_define(struct vmsvga_state_s *s,
#define CMD(f) le32_to_cpu(s->cmd->f)
-static inline int vmsvga_fifo_empty(struct vmsvga_state_s *s)
+static inline int vmsvga_fifo_length(struct vmsvga_state_s *s)
{
+ int num;
if (!s->config || !s->enable)
- return 1;
- return (s->cmd->next_cmd == s->cmd->stop);
+ return 0;
+ num = CMD(next_cmd) - CMD(stop);
+ if (num < 0)
+ num += CMD(max) - CMD(min);
+ return num >> 2;
}
static inline uint32_t vmsvga_fifo_read_raw(struct vmsvga_state_s *s)
@@ -543,13 +547,23 @@ static inline uint32_t vmsvga_fifo_read(struct vmsvga_state_s *s)
static void vmsvga_fifo_run(struct vmsvga_state_s *s)
{
uint32_t cmd, colour;
- int args = 0;
+ int args, len;
int x, y, dx, dy, width, height;
struct vmsvga_cursor_definition_s cursor;
- while (!vmsvga_fifo_empty(s))
+ uint32_t cmd_start;
+
+ len = vmsvga_fifo_length(s);
+ while (len > 0) {
+ /* May need to go back to the start of the command if incomplete */
+ cmd_start = s->cmd->stop;
+
switch (cmd = vmsvga_fifo_read(s)) {
case SVGA_CMD_UPDATE:
case SVGA_CMD_UPDATE_VERBOSE:
+ len -= 5;
+ if (len < 0)
+ goto rewind;
+
x = vmsvga_fifo_read(s);
y = vmsvga_fifo_read(s);
width = vmsvga_fifo_read(s);
@@ -558,6 +572,10 @@ static void vmsvga_fifo_run(struct vmsvga_state_s *s)
break;
case SVGA_CMD_RECT_FILL:
+ len -= 6;
+ if (len < 0)
+ goto rewind;
+
colour = vmsvga_fifo_read(s);
x = vmsvga_fifo_read(s);
y = vmsvga_fifo_read(s);
@@ -567,10 +585,15 @@ static void vmsvga_fifo_run(struct vmsvga_state_s *s)
vmsvga_fill_rect(s, colour, x, y, width, height);
break;
#else
+ args = 0;
goto badcmd;
#endif
case SVGA_CMD_RECT_COPY:
+ len -= 7;
+ if (len < 0)
+ goto rewind;
+
x = vmsvga_fifo_read(s);
y = vmsvga_fifo_read(s);
dx = vmsvga_fifo_read(s);
@@ -581,10 +604,15 @@ static void vmsvga_fifo_run(struct vmsvga_state_s *s)
vmsvga_copy_rect(s, x, y, dx, dy, width, height);
break;
#else
+ args = 0;
goto badcmd;
#endif
case SVGA_CMD_DEFINE_CURSOR:
+ len -= 8;
+ if (len < 0)
+ goto rewind;
+
cursor.id = vmsvga_fifo_read(s);
cursor.hot_x = vmsvga_fifo_read(s);
cursor.hot_y = vmsvga_fifo_read(s);
@@ -593,11 +621,14 @@ static void vmsvga_fifo_run(struct vmsvga_state_s *s)
vmsvga_fifo_read(s);
cursor.bpp = vmsvga_fifo_read(s);
- if (SVGA_BITMAP_SIZE(x, y) > sizeof cursor.mask ||
- SVGA_PIXMAP_SIZE(x, y, cursor.bpp) > sizeof cursor.image) {
- args = SVGA_BITMAP_SIZE(x, y) + SVGA_PIXMAP_SIZE(x, y, cursor.bpp);
- goto badcmd;
- }
+ args = SVGA_BITMAP_SIZE(x, y) + SVGA_PIXMAP_SIZE(x, y, cursor.bpp);
+ if (SVGA_BITMAP_SIZE(x, y) > sizeof cursor.mask ||
+ SVGA_PIXMAP_SIZE(x, y, cursor.bpp) > sizeof cursor.image)
+ goto badcmd;
+
+ len -= args;
+ if (len < 0)
+ goto rewind;
for (args = 0; args < SVGA_BITMAP_SIZE(x, y); args ++)
cursor.mask[args] = vmsvga_fifo_read_raw(s);
@@ -616,6 +647,10 @@ static void vmsvga_fifo_run(struct vmsvga_state_s *s)
* for so we can avoid FIFO desync if driver uses them illegally.
*/
case SVGA_CMD_DEFINE_ALPHA_CURSOR:
+ len -= 6;
+ if (len < 0)
+ goto rewind;
+
vmsvga_fifo_read(s);
vmsvga_fifo_read(s);
vmsvga_fifo_read(s);
@@ -630,6 +665,10 @@ static void vmsvga_fifo_run(struct vmsvga_state_s *s)
args = 7;
goto badcmd;
case SVGA_CMD_DRAW_GLYPH_CLIPPED:
+ len -= 4;
+ if (len < 0)
+ goto rewind;
+
vmsvga_fifo_read(s);
vmsvga_fifo_read(s);
args = 7 + (vmsvga_fifo_read(s) >> 2);
@@ -650,13 +689,22 @@ static void vmsvga_fifo_run(struct vmsvga_state_s *s)
break; /* Nop */
default:
+ args = 0;
badcmd:
+ len -= args;
+ if (len < 0)
+ goto rewind;
while (args --)
vmsvga_fifo_read(s);
printf("%s: Unknown command 0x%02x in SVGA command FIFO\n",
__FUNCTION__, cmd);
break;
+
+ rewind:
+ s->cmd->stop = cmd_start;
+ break;
}
+ }
s->syncing = 0;
}
@@ -809,11 +857,11 @@ static void vmsvga_value_write(void *opaque, uint32_t address, uint32_t value)
s->invalidated = 1;
s->vga.invalidate(&s->vga);
if (s->enable) {
- s->fb_size = ((s->depth + 7) >> 3) * s->new_width * s->new_height;
- vga_dirty_log_stop(&s->vga);
- } else {
- vga_dirty_log_start(&s->vga);
- }
+ s->fb_size = ((s->depth + 7) >> 3) * s->new_width * s->new_height;
+ vga_dirty_log_stop(&s->vga);
+ } else {
+ vga_dirty_log_start(&s->vga);
+ }
break;
case SVGA_REG_WIDTH:
@@ -1255,7 +1303,7 @@ static int pci_vmsvga_initfn(PCIDevice *dev)
PCI_BASE_ADDRESS_MEM_PREFETCH, pci_vmsvga_map_mem);
pci_register_bar(&s->card, 2, SVGA_FIFO_SIZE,
- PCI_BASE_ADDRESS_MEM_PREFETCH, pci_vmsvga_map_fifo);
+ PCI_BASE_ADDRESS_MEM_PREFETCH, pci_vmsvga_map_fifo);
vmsvga_init(&s->chip, VGA_RAM_SIZE);
diff --git a/hw/vt82c686.c b/hw/vt82c686.c
index a0c5747b59..cacc21767b 100644
--- a/hw/vt82c686.c
+++ b/hw/vt82c686.c
@@ -468,7 +468,6 @@ static int vt82c686b_pm_initfn(PCIDevice *dev)
pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_VIA_ACPI);
pci_config_set_class(pci_conf, PCI_CLASS_BRIDGE_OTHER);
pci_config_set_revision(pci_conf, 0x40);
- pci_conf[PCI_HEADER_TYPE] = PCI_HEADER_TYPE_NORMAL; // header_type
pci_set_word(pci_conf + PCI_COMMAND, 0);
pci_set_word(pci_conf + PCI_STATUS, PCI_STATUS_FAST_BACK |
@@ -556,8 +555,6 @@ static int vt82c686b_initfn(PCIDevice *d)
pci_config_set_class(pci_conf, PCI_CLASS_BRIDGE_ISA);
pci_config_set_prog_interface(pci_conf, 0x0);
pci_config_set_revision(pci_conf,0x40); /* Revision 4.0 */
- pci_conf[PCI_HEADER_TYPE] =
- PCI_HEADER_TYPE_NORMAL | PCI_HEADER_TYPE_MULTI_FUNCTION;
wmask = d->wmask;
for (i = 0x00; i < 0xff; i++) {
@@ -575,7 +572,7 @@ int vt82c686b_init(PCIBus *bus, int devfn)
{
PCIDevice *d;
- d = pci_create_simple(bus, devfn, "VT82C686B");
+ d = pci_create_simple_multifunction(bus, devfn, true, "VT82C686B");
return d->devfn;
}
diff --git a/hw/watchdog.c b/hw/watchdog.c
index aebb08a0ee..e9dd56e229 100644
--- a/hw/watchdog.c
+++ b/hw/watchdog.c
@@ -66,7 +66,7 @@ int select_watchdog(const char *p)
QLIST_FOREACH(model, &watchdog_list, entry) {
if (strcasecmp(model->wdt_name, p) == 0) {
/* add the device */
- opts = qemu_opts_create(&qemu_device_opts, NULL, 0);
+ opts = qemu_opts_create(qemu_find_opts("device"), NULL, 0);
qemu_opt_set(opts, "driver", p);
return 0;
}
diff --git a/hw/wm8750.c b/hw/wm8750.c
index ce43c234ac..c9c674451b 100644
--- a/hw/wm8750.c
+++ b/hw/wm8750.c
@@ -171,7 +171,6 @@ static void wm8750_set_format(WM8750State *s)
int i;
struct audsettings in_fmt;
struct audsettings out_fmt;
- struct audsettings monoout_fmt;
wm8750_out_flush(s);
@@ -212,10 +211,6 @@ static void wm8750_set_format(WM8750State *s)
out_fmt.nchannels = 2;
out_fmt.freq = s->dac_hz;
out_fmt.fmt = AUD_FMT_S16;
- monoout_fmt.endianness = 0;
- monoout_fmt.nchannels = 1;
- monoout_fmt.freq = s->rate->dac_hz;
- monoout_fmt.fmt = AUD_FMT_S16;
s->dac_voice[0] = AUD_open_out(&s->card, s->dac_voice[0],
CODEC ".speaker", s, wm8750_audio_out_cb, &out_fmt);
diff --git a/hw/xen_backend.h b/hw/xen_backend.h
index cc25f9d7db..1b428e3bf4 100644
--- a/hw/xen_backend.h
+++ b/hw/xen_backend.h
@@ -4,8 +4,6 @@
#include "xen_common.h"
#include "sysemu.h"
#include "net.h"
-#include "block_int.h"
-#include "blockdev.h"
/* ------------------------------------------------------------- */
@@ -86,7 +84,7 @@ int xen_be_bind_evtchn(struct XenDevice *xendev);
void xen_be_unbind_evtchn(struct XenDevice *xendev);
int xen_be_send_notify(struct XenDevice *xendev);
void xen_be_printf(struct XenDevice *xendev, int msg_level, const char *fmt, ...)
- __attribute__ ((format(printf, 3, 4)));
+ GCC_FMT_ATTR(3, 4);
/* actual backend drivers */
extern struct XenDevOps xen_console_ops; /* xen_console.c */
diff --git a/hw/xen_devconfig.c b/hw/xen_devconfig.c
index ea8f8c4c2d..8d50216c04 100644
--- a/hw/xen_devconfig.c
+++ b/hw/xen_devconfig.c
@@ -1,4 +1,6 @@
#include "xen_backend.h"
+#include "blockdev.h"
+#include "block_int.h" /* XXX */
/* ------------------------------------------------------------- */
diff --git a/hw/xen_disk.c b/hw/xen_disk.c
index 9a466f3cc1..134ac3388e 100644
--- a/hw/xen_disk.c
+++ b/hw/xen_disk.c
@@ -41,6 +41,7 @@
#include "qemu-char.h"
#include "xen_blkif.h"
#include "xen_backend.h"
+#include "blockdev.h"
/* ------------------------------------------------------------- */
diff --git a/hw/xen_machine_pv.c b/hw/xen_machine_pv.c
index 586214d8ba..77a34bf111 100644
--- a/hw/xen_machine_pv.c
+++ b/hw/xen_machine_pv.c
@@ -28,6 +28,7 @@
#include "boards.h"
#include "xen_backend.h"
#include "xen_domainbuild.h"
+#include "blockdev.h"
static void xen_init_pv(ram_addr_t ram_size,
const char *boot_device,
diff --git a/ioport.c b/ioport.c
index 53dd87af33..ec3dc65cdd 100644
--- a/ioport.c
+++ b/ioport.c
@@ -26,6 +26,7 @@
*/
#include "ioport.h"
+#include "trace.h"
/***********************************************************/
/* IO Port */
@@ -195,18 +196,21 @@ void isa_unassign_ioport(pio_addr_t start, int length)
void cpu_outb(pio_addr_t addr, uint8_t val)
{
LOG_IOPORT("outb: %04"FMT_pioaddr" %02"PRIx8"\n", addr, val);
+ trace_cpu_out(addr, val);
ioport_write(0, addr, val);
}
void cpu_outw(pio_addr_t addr, uint16_t val)
{
LOG_IOPORT("outw: %04"FMT_pioaddr" %04"PRIx16"\n", addr, val);
+ trace_cpu_out(addr, val);
ioport_write(1, addr, val);
}
void cpu_outl(pio_addr_t addr, uint32_t val)
{
LOG_IOPORT("outl: %04"FMT_pioaddr" %08"PRIx32"\n", addr, val);
+ trace_cpu_out(addr, val);
ioport_write(2, addr, val);
}
@@ -214,6 +218,7 @@ uint8_t cpu_inb(pio_addr_t addr)
{
uint8_t val;
val = ioport_read(0, addr);
+ trace_cpu_in(addr, val);
LOG_IOPORT("inb : %04"FMT_pioaddr" %02"PRIx8"\n", addr, val);
return val;
}
@@ -222,6 +227,7 @@ uint16_t cpu_inw(pio_addr_t addr)
{
uint16_t val;
val = ioport_read(1, addr);
+ trace_cpu_in(addr, val);
LOG_IOPORT("inw : %04"FMT_pioaddr" %04"PRIx16"\n", addr, val);
return val;
}
@@ -230,6 +236,7 @@ uint32_t cpu_inl(pio_addr_t addr)
{
uint32_t val;
val = ioport_read(2, addr);
+ trace_cpu_in(addr, val);
LOG_IOPORT("inl : %04"FMT_pioaddr" %08"PRIx32"\n", addr, val);
return val;
}
diff --git a/json-parser.c b/json-parser.c
index 70b9b6f967..6c06ef91a6 100644
--- a/json-parser.c
+++ b/json-parser.c
@@ -91,7 +91,8 @@ static int token_is_escape(QObject *obj, const char *value)
/**
* Error handler
*/
-static void parse_error(JSONParserContext *ctxt, QObject *token, const char *msg, ...)
+static void GCC_FMT_ATTR(3, 4) parse_error(JSONParserContext *ctxt,
+ QObject *token, const char *msg, ...)
{
va_list ap;
va_start(ap, msg);
diff --git a/kvm-all.c b/kvm-all.c
index 9380302300..37b99c7510 100644
--- a/kvm-all.c
+++ b/kvm-all.c
@@ -137,6 +137,24 @@ static KVMSlot *kvm_lookup_overlapping_slot(KVMState *s,
return found;
}
+int kvm_physical_memory_addr_from_ram(KVMState *s, ram_addr_t ram_addr,
+ target_phys_addr_t *phys_addr)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(s->slots); i++) {
+ KVMSlot *mem = &s->slots[i];
+
+ if (ram_addr >= mem->phys_offset &&
+ ram_addr < mem->phys_offset + mem->memory_size) {
+ *phys_addr = mem->start_addr + (ram_addr - mem->phys_offset);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
static int kvm_set_user_memory_region(KVMState *s, KVMSlot *slot)
{
struct kvm_userspace_memory_region mem;
@@ -274,6 +292,9 @@ static int kvm_set_migration_log(int enable)
for (i = 0; i < ARRAY_SIZE(s->slots); i++) {
mem = &s->slots[i];
+ if (!mem->memory_size) {
+ continue;
+ }
if (!!(mem->flags & KVM_MEM_LOG_DIRTY_PAGES) == enable) {
continue;
}
@@ -429,18 +450,10 @@ static void kvm_set_phys_mem(target_phys_addr_t start_addr,
KVMSlot *mem, old;
int err;
- if (start_addr & ~TARGET_PAGE_MASK) {
- if (flags >= IO_MEM_UNASSIGNED) {
- if (!kvm_lookup_overlapping_slot(s, start_addr,
- start_addr + size)) {
- return;
- }
- fprintf(stderr, "Unaligned split of a KVM memory slot\n");
- } else {
- fprintf(stderr, "Only page-aligned memory slots supported\n");
- }
- abort();
- }
+ /* kvm works in page size chunks, but the function may be called
+ with sub-page size and unaligned start address. */
+ size = TARGET_PAGE_ALIGN(size);
+ start_addr = TARGET_PAGE_ALIGN(start_addr);
/* KVM does not support read-only slots */
phys_offset &= ~IO_MEM_ROM;
@@ -924,8 +937,6 @@ int kvm_cpu_exec(CPUState *env)
DPRINTF("kvm_exit_debug\n");
#ifdef KVM_CAP_SET_GUEST_DEBUG
if (kvm_arch_debug(&run->debug.arch)) {
- gdb_set_stop_cpu(env);
- vm_stop(EXCP_DEBUG);
env->exception_index = EXCP_DEBUG;
return 0;
}
@@ -1038,18 +1049,14 @@ int kvm_has_xcrs(void)
void kvm_setup_guest_memory(void *start, size_t size)
{
if (!kvm_has_sync_mmu()) {
-#ifdef MADV_DONTFORK
- int ret = madvise(start, size, MADV_DONTFORK);
+ int ret = qemu_madvise(start, size, QEMU_MADV_DONTFORK);
if (ret) {
- perror("madvice");
+ perror("qemu_madvise");
+ fprintf(stderr,
+ "Need MADV_DONTFORK in absence of synchronous KVM MMU\n");
exit(1);
}
-#else
- fprintf(stderr,
- "Need MADV_DONTFORK in absence of synchronous KVM MMU\n");
- exit(1);
-#endif
}
}
@@ -1243,6 +1250,38 @@ int kvm_set_signal_mask(CPUState *env, const sigset_t *sigset)
return r;
}
+int kvm_set_ioeventfd_mmio_long(int fd, uint32_t addr, uint32_t val, bool assign)
+{
+#ifdef KVM_IOEVENTFD
+ int ret;
+ struct kvm_ioeventfd iofd;
+
+ iofd.datamatch = val;
+ iofd.addr = addr;
+ iofd.len = 4;
+ iofd.flags = KVM_IOEVENTFD_FLAG_DATAMATCH;
+ iofd.fd = fd;
+
+ if (!kvm_enabled()) {
+ return -ENOSYS;
+ }
+
+ if (!assign) {
+ iofd.flags |= KVM_IOEVENTFD_FLAG_DEASSIGN;
+ }
+
+ ret = kvm_vm_ioctl(kvm_state, KVM_IOEVENTFD, &iofd);
+
+ if (ret < 0) {
+ return -errno;
+ }
+
+ return 0;
+#else
+ return -ENOSYS;
+#endif
+}
+
int kvm_set_ioeventfd_pio_word(int fd, uint16_t addr, uint16_t val, bool assign)
{
#ifdef KVM_IOEVENTFD
diff --git a/kvm-stub.c b/kvm-stub.c
index 3378bd3b21..5384a4b9a4 100644
--- a/kvm-stub.c
+++ b/kvm-stub.c
@@ -136,3 +136,13 @@ int kvm_set_ioeventfd_pio_word(int fd, uint16_t addr, uint16_t val, bool assign)
{
return -ENOSYS;
}
+
+int kvm_set_ioeventfd_mmio_long(int fd, uint32_t adr, uint32_t val, bool assign)
+{
+ return -ENOSYS;
+}
+
+int kvm_on_sigbus(int code, void *addr)
+{
+ return 1;
+}
diff --git a/kvm.h b/kvm.h
index 93f81871e9..60a9b425c8 100644
--- a/kvm.h
+++ b/kvm.h
@@ -110,6 +110,9 @@ int kvm_arch_init_vcpu(CPUState *env);
void kvm_arch_reset_vcpu(CPUState *env);
+int kvm_on_sigbus_vcpu(CPUState *env, int code, void *addr);
+int kvm_on_sigbus(int code, void *addr);
+
struct kvm_guest_debug;
struct kvm_debug_exit_arch;
@@ -174,7 +177,14 @@ static inline void cpu_synchronize_post_init(CPUState *env)
}
}
+
+#if !defined(CONFIG_USER_ONLY)
+int kvm_physical_memory_addr_from_ram(KVMState *s, ram_addr_t ram_addr,
+ target_phys_addr_t *phys_addr);
+#endif
+
#endif
+int kvm_set_ioeventfd_mmio_long(int fd, uint32_t adr, uint32_t val, bool assign);
int kvm_set_ioeventfd_pio_word(int fd, uint16_t adr, uint16_t val, bool assign);
#endif
diff --git a/linux-user/elfload.c b/linux-user/elfload.c
index accb44d9de..33d776de41 100644
--- a/linux-user/elfload.c
+++ b/linux-user/elfload.c
@@ -35,18 +35,17 @@
* These occupy the top three bytes.
*/
enum {
- ADDR_NO_RANDOMIZE = 0x0040000, /* disable randomization of VA space */
- FDPIC_FUNCPTRS = 0x0080000, /* userspace function ptrs point to descriptors
- * (signal handling)
- */
- MMAP_PAGE_ZERO = 0x0100000,
- ADDR_COMPAT_LAYOUT = 0x0200000,
- READ_IMPLIES_EXEC = 0x0400000,
- ADDR_LIMIT_32BIT = 0x0800000,
- SHORT_INODE = 0x1000000,
- WHOLE_SECONDS = 0x2000000,
- STICKY_TIMEOUTS = 0x4000000,
- ADDR_LIMIT_3GB = 0x8000000,
+ ADDR_NO_RANDOMIZE = 0x0040000, /* disable randomization of VA space */
+ FDPIC_FUNCPTRS = 0x0080000, /* userspace function ptrs point to
+ descriptors (signal handling) */
+ MMAP_PAGE_ZERO = 0x0100000,
+ ADDR_COMPAT_LAYOUT = 0x0200000,
+ READ_IMPLIES_EXEC = 0x0400000,
+ ADDR_LIMIT_32BIT = 0x0800000,
+ SHORT_INODE = 0x1000000,
+ WHOLE_SECONDS = 0x2000000,
+ STICKY_TIMEOUTS = 0x4000000,
+ ADDR_LIMIT_3GB = 0x8000000,
};
/*
@@ -56,36 +55,35 @@ enum {
* conflict with error returns.
*/
enum {
- PER_LINUX = 0x0000,
- PER_LINUX_32BIT = 0x0000 | ADDR_LIMIT_32BIT,
- PER_LINUX_FDPIC = 0x0000 | FDPIC_FUNCPTRS,
- PER_SVR4 = 0x0001 | STICKY_TIMEOUTS | MMAP_PAGE_ZERO,
- PER_SVR3 = 0x0002 | STICKY_TIMEOUTS | SHORT_INODE,
- PER_SCOSVR3 = 0x0003 | STICKY_TIMEOUTS |
- WHOLE_SECONDS | SHORT_INODE,
- PER_OSR5 = 0x0003 | STICKY_TIMEOUTS | WHOLE_SECONDS,
- PER_WYSEV386 = 0x0004 | STICKY_TIMEOUTS | SHORT_INODE,
- PER_ISCR4 = 0x0005 | STICKY_TIMEOUTS,
- PER_BSD = 0x0006,
- PER_SUNOS = 0x0006 | STICKY_TIMEOUTS,
- PER_XENIX = 0x0007 | STICKY_TIMEOUTS | SHORT_INODE,
- PER_LINUX32 = 0x0008,
- PER_LINUX32_3GB = 0x0008 | ADDR_LIMIT_3GB,
- PER_IRIX32 = 0x0009 | STICKY_TIMEOUTS,/* IRIX5 32-bit */
- PER_IRIXN32 = 0x000a | STICKY_TIMEOUTS,/* IRIX6 new 32-bit */
- PER_IRIX64 = 0x000b | STICKY_TIMEOUTS,/* IRIX6 64-bit */
- PER_RISCOS = 0x000c,
- PER_SOLARIS = 0x000d | STICKY_TIMEOUTS,
- PER_UW7 = 0x000e | STICKY_TIMEOUTS | MMAP_PAGE_ZERO,
- PER_OSF4 = 0x000f, /* OSF/1 v4 */
- PER_HPUX = 0x0010,
- PER_MASK = 0x00ff,
+ PER_LINUX = 0x0000,
+ PER_LINUX_32BIT = 0x0000 | ADDR_LIMIT_32BIT,
+ PER_LINUX_FDPIC = 0x0000 | FDPIC_FUNCPTRS,
+ PER_SVR4 = 0x0001 | STICKY_TIMEOUTS | MMAP_PAGE_ZERO,
+ PER_SVR3 = 0x0002 | STICKY_TIMEOUTS | SHORT_INODE,
+ PER_SCOSVR3 = 0x0003 | STICKY_TIMEOUTS | WHOLE_SECONDS | SHORT_INODE,
+ PER_OSR5 = 0x0003 | STICKY_TIMEOUTS | WHOLE_SECONDS,
+ PER_WYSEV386 = 0x0004 | STICKY_TIMEOUTS | SHORT_INODE,
+ PER_ISCR4 = 0x0005 | STICKY_TIMEOUTS,
+ PER_BSD = 0x0006,
+ PER_SUNOS = 0x0006 | STICKY_TIMEOUTS,
+ PER_XENIX = 0x0007 | STICKY_TIMEOUTS | SHORT_INODE,
+ PER_LINUX32 = 0x0008,
+ PER_LINUX32_3GB = 0x0008 | ADDR_LIMIT_3GB,
+ PER_IRIX32 = 0x0009 | STICKY_TIMEOUTS,/* IRIX5 32-bit */
+ PER_IRIXN32 = 0x000a | STICKY_TIMEOUTS,/* IRIX6 new 32-bit */
+ PER_IRIX64 = 0x000b | STICKY_TIMEOUTS,/* IRIX6 64-bit */
+ PER_RISCOS = 0x000c,
+ PER_SOLARIS = 0x000d | STICKY_TIMEOUTS,
+ PER_UW7 = 0x000e | STICKY_TIMEOUTS | MMAP_PAGE_ZERO,
+ PER_OSF4 = 0x000f, /* OSF/1 v4 */
+ PER_HPUX = 0x0010,
+ PER_MASK = 0x00ff,
};
/*
* Return the base personality without flags.
*/
-#define personality(pers) (pers & PER_MASK)
+#define personality(pers) (pers & PER_MASK)
/* this flag is uneffective under linux too, should be deleted */
#ifndef MAP_DENYWRITE
@@ -97,15 +95,21 @@ enum {
#define ELIBBAD 80
#endif
-typedef target_ulong target_elf_greg_t;
+#ifdef TARGET_WORDS_BIGENDIAN
+#define ELF_DATA ELFDATA2MSB
+#else
+#define ELF_DATA ELFDATA2LSB
+#endif
+
+typedef target_ulong target_elf_greg_t;
#ifdef USE_UID16
-typedef uint16_t target_uid_t;
-typedef uint16_t target_gid_t;
+typedef uint16_t target_uid_t;
+typedef uint16_t target_gid_t;
#else
-typedef uint32_t target_uid_t;
-typedef uint32_t target_gid_t;
+typedef uint32_t target_uid_t;
+typedef uint32_t target_gid_t;
#endif
-typedef int32_t target_pid_t;
+typedef int32_t target_pid_t;
#ifdef TARGET_I386
@@ -126,7 +130,7 @@ static const char *get_elf_platform(void)
static uint32_t get_elf_hwcap(void)
{
- return thread_env->cpuid_features;
+ return thread_env->cpuid_features;
}
#ifdef TARGET_X86_64
@@ -134,7 +138,6 @@ static uint32_t get_elf_hwcap(void)
#define elf_check_arch(x) ( ((x) == ELF_ARCH) )
#define ELF_CLASS ELFCLASS64
-#define ELF_DATA ELFDATA2LSB
#define ELF_ARCH EM_X86_64
static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop)
@@ -197,11 +200,11 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUState *env)
/*
* These are used to set parameters in the core dumps.
*/
-#define ELF_CLASS ELFCLASS32
-#define ELF_DATA ELFDATA2LSB
-#define ELF_ARCH EM_386
+#define ELF_CLASS ELFCLASS32
+#define ELF_ARCH EM_386
-static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop)
+static inline void init_thread(struct target_pt_regs *regs,
+ struct image_info *infop)
{
regs->esp = infop->start_stack;
regs->eip = infop->entry;
@@ -249,7 +252,7 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUState *env)
#endif
#define USE_ELF_CORE_DUMP
-#define ELF_EXEC_PAGESIZE 4096
+#define ELF_EXEC_PAGESIZE 4096
#endif
@@ -259,21 +262,17 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUState *env)
#define elf_check_arch(x) ( (x) == EM_ARM )
-#define ELF_CLASS ELFCLASS32
-#ifdef TARGET_WORDS_BIGENDIAN
-#define ELF_DATA ELFDATA2MSB
-#else
-#define ELF_DATA ELFDATA2LSB
-#endif
-#define ELF_ARCH EM_ARM
+#define ELF_CLASS ELFCLASS32
+#define ELF_ARCH EM_ARM
-static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop)
+static inline void init_thread(struct target_pt_regs *regs,
+ struct image_info *infop)
{
abi_long stack = infop->start_stack;
memset(regs, 0, sizeof(*regs));
regs->ARM_cpsr = 0x10;
if (infop->entry & 1)
- regs->ARM_cpsr |= CPSR_T;
+ regs->ARM_cpsr |= CPSR_T;
regs->ARM_pc = infop->entry & 0xfffffffe;
regs->ARM_sp = infop->start_stack;
/* FIXME - what to for failure of get_user()? */
@@ -313,30 +312,30 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUState *env)
}
#define USE_ELF_CORE_DUMP
-#define ELF_EXEC_PAGESIZE 4096
+#define ELF_EXEC_PAGESIZE 4096
enum
{
- ARM_HWCAP_ARM_SWP = 1 << 0,
- ARM_HWCAP_ARM_HALF = 1 << 1,
- ARM_HWCAP_ARM_THUMB = 1 << 2,
- ARM_HWCAP_ARM_26BIT = 1 << 3,
- ARM_HWCAP_ARM_FAST_MULT = 1 << 4,
- ARM_HWCAP_ARM_FPA = 1 << 5,
- ARM_HWCAP_ARM_VFP = 1 << 6,
- ARM_HWCAP_ARM_EDSP = 1 << 7,
- ARM_HWCAP_ARM_JAVA = 1 << 8,
- ARM_HWCAP_ARM_IWMMXT = 1 << 9,
- ARM_HWCAP_ARM_THUMBEE = 1 << 10,
- ARM_HWCAP_ARM_NEON = 1 << 11,
- ARM_HWCAP_ARM_VFPv3 = 1 << 12,
- ARM_HWCAP_ARM_VFPv3D16 = 1 << 13,
+ ARM_HWCAP_ARM_SWP = 1 << 0,
+ ARM_HWCAP_ARM_HALF = 1 << 1,
+ ARM_HWCAP_ARM_THUMB = 1 << 2,
+ ARM_HWCAP_ARM_26BIT = 1 << 3,
+ ARM_HWCAP_ARM_FAST_MULT = 1 << 4,
+ ARM_HWCAP_ARM_FPA = 1 << 5,
+ ARM_HWCAP_ARM_VFP = 1 << 6,
+ ARM_HWCAP_ARM_EDSP = 1 << 7,
+ ARM_HWCAP_ARM_JAVA = 1 << 8,
+ ARM_HWCAP_ARM_IWMMXT = 1 << 9,
+ ARM_HWCAP_ARM_THUMBEE = 1 << 10,
+ ARM_HWCAP_ARM_NEON = 1 << 11,
+ ARM_HWCAP_ARM_VFPv3 = 1 << 12,
+ ARM_HWCAP_ARM_VFPv3D16 = 1 << 13,
};
-#define ELF_HWCAP (ARM_HWCAP_ARM_SWP | ARM_HWCAP_ARM_HALF \
- | ARM_HWCAP_ARM_THUMB | ARM_HWCAP_ARM_FAST_MULT \
- | ARM_HWCAP_ARM_FPA | ARM_HWCAP_ARM_VFP \
- | ARM_HWCAP_ARM_NEON | ARM_HWCAP_ARM_VFPv3 )
+#define ELF_HWCAP (ARM_HWCAP_ARM_SWP | ARM_HWCAP_ARM_HALF \
+ | ARM_HWCAP_ARM_THUMB | ARM_HWCAP_ARM_FAST_MULT \
+ | ARM_HWCAP_ARM_FPA | ARM_HWCAP_ARM_VFP \
+ | ARM_HWCAP_ARM_NEON | ARM_HWCAP_ARM_VFPv3 )
#endif
@@ -352,12 +351,12 @@ enum
#endif
#define ELF_CLASS ELFCLASS64
-#define ELF_DATA ELFDATA2MSB
#define ELF_ARCH EM_SPARCV9
-#define STACK_BIAS 2047
+#define STACK_BIAS 2047
-static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop)
+static inline void init_thread(struct target_pt_regs *regs,
+ struct image_info *infop)
{
#ifndef TARGET_ABI32
regs->tstate = 0;
@@ -381,10 +380,10 @@ static inline void init_thread(struct target_pt_regs *regs, struct image_info *i
#define elf_check_arch(x) ( (x) == EM_SPARC )
#define ELF_CLASS ELFCLASS32
-#define ELF_DATA ELFDATA2MSB
#define ELF_ARCH EM_SPARC
-static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop)
+static inline void init_thread(struct target_pt_regs *regs,
+ struct image_info *infop)
{
regs->psr = 0;
regs->pc = infop->entry;
@@ -404,22 +403,17 @@ static inline void init_thread(struct target_pt_regs *regs, struct image_info *i
#define elf_check_arch(x) ( (x) == EM_PPC64 )
-#define ELF_CLASS ELFCLASS64
+#define ELF_CLASS ELFCLASS64
#else
#define elf_check_arch(x) ( (x) == EM_PPC )
-#define ELF_CLASS ELFCLASS32
+#define ELF_CLASS ELFCLASS32
#endif
-#ifdef TARGET_WORDS_BIGENDIAN
-#define ELF_DATA ELFDATA2MSB
-#else
-#define ELF_DATA ELFDATA2LSB
-#endif
-#define ELF_ARCH EM_PPC
+#define ELF_ARCH EM_PPC
/* Feature masks for the Aux Vector Hardware Capabilities (AT_HWCAP).
See arch/powerpc/include/asm/cputable.h. */
@@ -464,7 +458,7 @@ static uint32_t get_elf_hwcap(void)
/* We don't have to be terribly complete here; the high points are
Altivec/FP/SPE support. Anything else is just a bonus. */
-#define GET_FEATURE(flag, feature) \
+#define GET_FEATURE(flag, feature) \
do {if (e->insns_flags & flag) features |= feature; } while(0)
GET_FEATURE(PPC_64B, QEMU_PPC_FEATURE_64);
GET_FEATURE(PPC_FLOAT, QEMU_PPC_FEATURE_HAS_FPU);
@@ -480,15 +474,6 @@ static uint32_t get_elf_hwcap(void)
}
/*
- * We need to put in some extra aux table entries to tell glibc what
- * the cache block size is, so it can use the dcbz instruction safely.
- */
-#define AT_DCACHEBSIZE 19
-#define AT_ICACHEBSIZE 20
-#define AT_UCACHEBSIZE 21
-/* A special ignored type value for PPC, for glibc compatibility. */
-#define AT_IGNOREPPC 22
-/*
* The requirements here are:
* - keep the final alignment of sp (sp & 0xf)
* - make sure the 32-bit value at the first 16 byte aligned position of
@@ -498,17 +483,17 @@ static uint32_t get_elf_hwcap(void)
* even if DLINFO_ARCH_ITEMS goes to zero or is undefined.
*/
#define DLINFO_ARCH_ITEMS 5
-#define ARCH_DLINFO \
-do { \
- NEW_AUX_ENT(AT_DCACHEBSIZE, 0x20); \
- NEW_AUX_ENT(AT_ICACHEBSIZE, 0x20); \
- NEW_AUX_ENT(AT_UCACHEBSIZE, 0); \
- /* \
- * Now handle glibc compatibility. \
- */ \
- NEW_AUX_ENT(AT_IGNOREPPC, AT_IGNOREPPC); \
- NEW_AUX_ENT(AT_IGNOREPPC, AT_IGNOREPPC); \
- } while (0)
+#define ARCH_DLINFO \
+ do { \
+ NEW_AUX_ENT(AT_DCACHEBSIZE, 0x20); \
+ NEW_AUX_ENT(AT_ICACHEBSIZE, 0x20); \
+ NEW_AUX_ENT(AT_UCACHEBSIZE, 0); \
+ /* \
+ * Now handle glibc compatibility. \
+ */ \
+ NEW_AUX_ENT(AT_IGNOREPPC, AT_IGNOREPPC); \
+ NEW_AUX_ENT(AT_IGNOREPPC, AT_IGNOREPPC); \
+ } while (0)
static inline void init_thread(struct target_pt_regs *_regs, struct image_info *infop)
{
@@ -546,7 +531,7 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUState *env)
}
#define USE_ELF_CORE_DUMP
-#define ELF_EXEC_PAGESIZE 4096
+#define ELF_EXEC_PAGESIZE 4096
#endif
@@ -561,14 +546,10 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUState *env)
#else
#define ELF_CLASS ELFCLASS32
#endif
-#ifdef TARGET_WORDS_BIGENDIAN
-#define ELF_DATA ELFDATA2MSB
-#else
-#define ELF_DATA ELFDATA2LSB
-#endif
#define ELF_ARCH EM_MIPS
-static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop)
+static inline void init_thread(struct target_pt_regs *regs,
+ struct image_info *infop)
{
regs->cp0_status = 2 << CP0St_KSU;
regs->cp0_epc = infop->entry;
@@ -632,10 +613,10 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUState *env)
#define elf_check_arch(x) ( (x) == EM_MICROBLAZE || (x) == EM_MICROBLAZE_OLD)
#define ELF_CLASS ELFCLASS32
-#define ELF_DATA ELFDATA2MSB
#define ELF_ARCH EM_MICROBLAZE
-static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop)
+static inline void init_thread(struct target_pt_regs *regs,
+ struct image_info *infop)
{
regs->pc = infop->entry;
regs->r1 = infop->start_stack;
@@ -671,14 +652,14 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUState *env)
#define elf_check_arch(x) ( (x) == EM_SH )
#define ELF_CLASS ELFCLASS32
-#define ELF_DATA ELFDATA2LSB
#define ELF_ARCH EM_SH
-static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop)
+static inline void init_thread(struct target_pt_regs *regs,
+ struct image_info *infop)
{
- /* Check other registers XXXXX */
- regs->pc = infop->entry;
- regs->regs[15] = infop->start_stack;
+ /* Check other registers XXXXX */
+ regs->pc = infop->entry;
+ regs->regs[15] = infop->start_stack;
}
/* See linux kernel: arch/sh/include/asm/elf.h. */
@@ -696,7 +677,8 @@ enum {
TARGET_REG_SYSCALL = 22
};
-static inline void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUState *env)
+static inline void elf_core_copy_regs(target_elf_gregset_t *regs,
+ const CPUState *env)
{
int i;
@@ -725,12 +707,12 @@ static inline void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUState
#define elf_check_arch(x) ( (x) == EM_CRIS )
#define ELF_CLASS ELFCLASS32
-#define ELF_DATA ELFDATA2LSB
#define ELF_ARCH EM_CRIS
-static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop)
+static inline void init_thread(struct target_pt_regs *regs,
+ struct image_info *infop)
{
- regs->erp = infop->entry;
+ regs->erp = infop->entry;
}
#define ELF_EXEC_PAGESIZE 8192
@@ -743,14 +725,14 @@ static inline void init_thread(struct target_pt_regs *regs, struct image_info *i
#define elf_check_arch(x) ( (x) == EM_68K )
-#define ELF_CLASS ELFCLASS32
-#define ELF_DATA ELFDATA2MSB
-#define ELF_ARCH EM_68K
+#define ELF_CLASS ELFCLASS32
+#define ELF_ARCH EM_68K
/* ??? Does this need to do anything?
-#define ELF_PLAT_INIT(_r) */
+ #define ELF_PLAT_INIT(_r) */
-static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop)
+static inline void init_thread(struct target_pt_regs *regs,
+ struct image_info *infop)
{
regs->usp = infop->start_stack;
regs->sr = 0;
@@ -786,7 +768,7 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUState *env)
}
#define USE_ELF_CORE_DUMP
-#define ELF_EXEC_PAGESIZE 8192
+#define ELF_EXEC_PAGESIZE 8192
#endif
@@ -797,10 +779,10 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUState *env)
#define elf_check_arch(x) ( (x) == ELF_ARCH )
#define ELF_CLASS ELFCLASS64
-#define ELF_DATA ELFDATA2MSB
#define ELF_ARCH EM_ALPHA
-static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop)
+static inline void init_thread(struct target_pt_regs *regs,
+ struct image_info *infop)
{
regs->pc = infop->entry;
regs->ps = 8;
@@ -830,14 +812,14 @@ static inline void init_thread(struct target_pt_regs *regs, struct image_info *i
struct exec
{
- unsigned int a_info; /* Use macros N_MAGIC, etc for access */
- unsigned int a_text; /* length of text, in bytes */
- unsigned int a_data; /* length of data, in bytes */
- unsigned int a_bss; /* length of uninitialized data area, in bytes */
- unsigned int a_syms; /* length of symbol table data in file, in bytes */
- unsigned int a_entry; /* start address */
- unsigned int a_trsize; /* length of relocation info for text, in bytes */
- unsigned int a_drsize; /* length of relocation info for data, in bytes */
+ unsigned int a_info; /* Use macros N_MAGIC, etc for access */
+ unsigned int a_text; /* length of text, in bytes */
+ unsigned int a_data; /* length of data, in bytes */
+ unsigned int a_bss; /* length of uninitialized data area, in bytes */
+ unsigned int a_syms; /* length of symbol table data in file, in bytes */
+ unsigned int a_entry; /* start address */
+ unsigned int a_trsize; /* length of relocation info for text, in bytes */
+ unsigned int a_drsize; /* length of relocation info for data, in bytes */
};
@@ -847,72 +829,66 @@ struct exec
#define ZMAGIC 0413
#define QMAGIC 0314
-/* max code+data+bss space allocated to elf interpreter */
-#define INTERP_MAP_SIZE (32 * 1024 * 1024)
-
-/* max code+data+bss+brk space allocated to ET_DYN executables */
-#define ET_DYN_MAP_SIZE (128 * 1024 * 1024)
-
/* Necessary parameters */
#define TARGET_ELF_EXEC_PAGESIZE TARGET_PAGE_SIZE
#define TARGET_ELF_PAGESTART(_v) ((_v) & ~(unsigned long)(TARGET_ELF_EXEC_PAGESIZE-1))
#define TARGET_ELF_PAGEOFFSET(_v) ((_v) & (TARGET_ELF_EXEC_PAGESIZE-1))
-#define INTERPRETER_NONE 0
-#define INTERPRETER_AOUT 1
-#define INTERPRETER_ELF 2
-
#define DLINFO_ITEMS 12
static inline void memcpy_fromfs(void * to, const void * from, unsigned long n)
{
- memcpy(to, from, n);
+ memcpy(to, from, n);
}
-static int load_aout_interp(void * exptr, int interp_fd);
-
#ifdef BSWAP_NEEDED
static void bswap_ehdr(struct elfhdr *ehdr)
{
- bswap16s(&ehdr->e_type); /* Object file type */
- bswap16s(&ehdr->e_machine); /* Architecture */
- bswap32s(&ehdr->e_version); /* Object file version */
- bswaptls(&ehdr->e_entry); /* Entry point virtual address */
- bswaptls(&ehdr->e_phoff); /* Program header table file offset */
- bswaptls(&ehdr->e_shoff); /* Section header table file offset */
- bswap32s(&ehdr->e_flags); /* Processor-specific flags */
- bswap16s(&ehdr->e_ehsize); /* ELF header size in bytes */
- bswap16s(&ehdr->e_phentsize); /* Program header table entry size */
- bswap16s(&ehdr->e_phnum); /* Program header table entry count */
- bswap16s(&ehdr->e_shentsize); /* Section header table entry size */
- bswap16s(&ehdr->e_shnum); /* Section header table entry count */
- bswap16s(&ehdr->e_shstrndx); /* Section header string table index */
-}
-
-static void bswap_phdr(struct elf_phdr *phdr)
-{
- bswap32s(&phdr->p_type); /* Segment type */
- bswaptls(&phdr->p_offset); /* Segment file offset */
- bswaptls(&phdr->p_vaddr); /* Segment virtual address */
- bswaptls(&phdr->p_paddr); /* Segment physical address */
- bswaptls(&phdr->p_filesz); /* Segment size in file */
- bswaptls(&phdr->p_memsz); /* Segment size in memory */
- bswap32s(&phdr->p_flags); /* Segment flags */
- bswaptls(&phdr->p_align); /* Segment alignment */
-}
-
-static void bswap_shdr(struct elf_shdr *shdr)
-{
- bswap32s(&shdr->sh_name);
- bswap32s(&shdr->sh_type);
- bswaptls(&shdr->sh_flags);
- bswaptls(&shdr->sh_addr);
- bswaptls(&shdr->sh_offset);
- bswaptls(&shdr->sh_size);
- bswap32s(&shdr->sh_link);
- bswap32s(&shdr->sh_info);
- bswaptls(&shdr->sh_addralign);
- bswaptls(&shdr->sh_entsize);
+ bswap16s(&ehdr->e_type); /* Object file type */
+ bswap16s(&ehdr->e_machine); /* Architecture */
+ bswap32s(&ehdr->e_version); /* Object file version */
+ bswaptls(&ehdr->e_entry); /* Entry point virtual address */
+ bswaptls(&ehdr->e_phoff); /* Program header table file offset */
+ bswaptls(&ehdr->e_shoff); /* Section header table file offset */
+ bswap32s(&ehdr->e_flags); /* Processor-specific flags */
+ bswap16s(&ehdr->e_ehsize); /* ELF header size in bytes */
+ bswap16s(&ehdr->e_phentsize); /* Program header table entry size */
+ bswap16s(&ehdr->e_phnum); /* Program header table entry count */
+ bswap16s(&ehdr->e_shentsize); /* Section header table entry size */
+ bswap16s(&ehdr->e_shnum); /* Section header table entry count */
+ bswap16s(&ehdr->e_shstrndx); /* Section header string table index */
+}
+
+static void bswap_phdr(struct elf_phdr *phdr, int phnum)
+{
+ int i;
+ for (i = 0; i < phnum; ++i, ++phdr) {
+ bswap32s(&phdr->p_type); /* Segment type */
+ bswap32s(&phdr->p_flags); /* Segment flags */
+ bswaptls(&phdr->p_offset); /* Segment file offset */
+ bswaptls(&phdr->p_vaddr); /* Segment virtual address */
+ bswaptls(&phdr->p_paddr); /* Segment physical address */
+ bswaptls(&phdr->p_filesz); /* Segment size in file */
+ bswaptls(&phdr->p_memsz); /* Segment size in memory */
+ bswaptls(&phdr->p_align); /* Segment alignment */
+ }
+}
+
+static void bswap_shdr(struct elf_shdr *shdr, int shnum)
+{
+ int i;
+ for (i = 0; i < shnum; ++i, ++shdr) {
+ bswap32s(&shdr->sh_name);
+ bswap32s(&shdr->sh_type);
+ bswaptls(&shdr->sh_flags);
+ bswaptls(&shdr->sh_addr);
+ bswaptls(&shdr->sh_offset);
+ bswaptls(&shdr->sh_size);
+ bswap32s(&shdr->sh_link);
+ bswap32s(&shdr->sh_info);
+ bswaptls(&shdr->sh_addralign);
+ bswaptls(&shdr->sh_entsize);
+ }
}
static void bswap_sym(struct elf_sym *sym)
@@ -922,21 +898,41 @@ static void bswap_sym(struct elf_sym *sym)
bswaptls(&sym->st_size);
bswap16s(&sym->st_shndx);
}
+#else
+static inline void bswap_ehdr(struct elfhdr *ehdr) { }
+static inline void bswap_phdr(struct elf_phdr *phdr, int phnum) { }
+static inline void bswap_shdr(struct elf_shdr *shdr, int shnum) { }
+static inline void bswap_sym(struct elf_sym *sym) { }
#endif
#ifdef USE_ELF_CORE_DUMP
static int elf_core_dump(int, const CPUState *);
+#endif /* USE_ELF_CORE_DUMP */
+static void load_symbols(struct elfhdr *hdr, int fd, abi_ulong load_bias);
-#ifdef BSWAP_NEEDED
-static void bswap_note(struct elf_note *en)
+/* Verify the portions of EHDR within E_IDENT for the target.
+ This can be performed before bswapping the entire header. */
+static bool elf_check_ident(struct elfhdr *ehdr)
{
- bswap32s(&en->n_namesz);
- bswap32s(&en->n_descsz);
- bswap32s(&en->n_type);
+ return (ehdr->e_ident[EI_MAG0] == ELFMAG0
+ && ehdr->e_ident[EI_MAG1] == ELFMAG1
+ && ehdr->e_ident[EI_MAG2] == ELFMAG2
+ && ehdr->e_ident[EI_MAG3] == ELFMAG3
+ && ehdr->e_ident[EI_CLASS] == ELF_CLASS
+ && ehdr->e_ident[EI_DATA] == ELF_DATA
+ && ehdr->e_ident[EI_VERSION] == EV_CURRENT);
}
-#endif /* BSWAP_NEEDED */
-#endif /* USE_ELF_CORE_DUMP */
+/* Verify the portions of EHDR outside of E_IDENT for the target.
+ This has to wait until after bswapping the header. */
+static bool elf_check_ehdr(struct elfhdr *ehdr)
+{
+ return (elf_check_arch(ehdr->e_machine)
+ && ehdr->e_ehsize == sizeof(struct elfhdr)
+ && ehdr->e_phentsize == sizeof(struct elf_phdr)
+ && ehdr->e_shentsize == sizeof(struct elf_shdr)
+ && (ehdr->e_type == ET_EXEC || ehdr->e_type == ET_DYN));
+}
/*
* 'copy_elf_strings()' copies argument/envelope strings from user
@@ -951,24 +947,24 @@ static abi_ulong copy_elf_strings(int argc,char ** argv, void **page,
int len, offset = 0;
if (!p) {
- return 0; /* bullet-proofing */
+ return 0; /* bullet-proofing */
}
while (argc-- > 0) {
tmp = argv[argc];
if (!tmp) {
- fprintf(stderr, "VFS: argc is wrong");
- exit(-1);
- }
+ fprintf(stderr, "VFS: argc is wrong");
+ exit(-1);
+ }
tmp1 = tmp;
- while (*tmp++);
- len = tmp - tmp1;
- if (p < len) { /* this shouldn't happen - 128kB */
- return 0;
- }
- while (len) {
- --p; --tmp; --len;
- if (--offset < 0) {
- offset = p % TARGET_PAGE_SIZE;
+ while (*tmp++);
+ len = tmp - tmp1;
+ if (p < len) { /* this shouldn't happen - 128kB */
+ return 0;
+ }
+ while (len) {
+ --p; --tmp; --len;
+ if (--offset < 0) {
+ offset = p % TARGET_PAGE_SIZE;
pag = (char *)page[p/TARGET_PAGE_SIZE];
if (!pag) {
pag = (char *)malloc(TARGET_PAGE_SIZE);
@@ -976,20 +972,20 @@ static abi_ulong copy_elf_strings(int argc,char ** argv, void **page,
page[p/TARGET_PAGE_SIZE] = pag;
if (!pag)
return 0;
- }
- }
- if (len == 0 || offset == 0) {
- *(pag + offset) = *tmp;
- }
- else {
- int bytes_to_copy = (len > offset) ? offset : len;
- tmp -= bytes_to_copy;
- p -= bytes_to_copy;
- offset -= bytes_to_copy;
- len -= bytes_to_copy;
- memcpy_fromfs(pag + offset, tmp, bytes_to_copy + 1);
- }
- }
+ }
+ }
+ if (len == 0 || offset == 0) {
+ *(pag + offset) = *tmp;
+ }
+ else {
+ int bytes_to_copy = (len > offset) ? offset : len;
+ tmp -= bytes_to_copy;
+ p -= bytes_to_copy;
+ offset -= bytes_to_copy;
+ len -= bytes_to_copy;
+ memcpy_fromfs(pag + offset, tmp, bytes_to_copy + 1);
+ }
+ }
}
return p;
}
@@ -997,332 +993,440 @@ static abi_ulong copy_elf_strings(int argc,char ** argv, void **page,
static abi_ulong setup_arg_pages(abi_ulong p, struct linux_binprm *bprm,
struct image_info *info)
{
- abi_ulong stack_base, size, error;
+ abi_ulong stack_base, size, error, guard;
int i;
/* Create enough stack to hold everything. If we don't use
- * it for args, we'll use it for something else...
- */
+ it for args, we'll use it for something else. */
size = guest_stack_size;
- if (size < MAX_ARG_PAGES*TARGET_PAGE_SIZE)
+ if (size < MAX_ARG_PAGES*TARGET_PAGE_SIZE) {
size = MAX_ARG_PAGES*TARGET_PAGE_SIZE;
- error = target_mmap(0,
- size + qemu_host_page_size,
- PROT_READ | PROT_WRITE,
- MAP_PRIVATE | MAP_ANONYMOUS,
- -1, 0);
+ }
+ guard = TARGET_PAGE_SIZE;
+ if (guard < qemu_real_host_page_size) {
+ guard = qemu_real_host_page_size;
+ }
+
+ error = target_mmap(0, size + guard, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (error == -1) {
- perror("stk mmap");
+ perror("mmap stack");
exit(-1);
}
- /* we reserve one extra page at the top of the stack as guard */
- target_mprotect(error + size, qemu_host_page_size, PROT_NONE);
- info->stack_limit = error;
- stack_base = error + size - MAX_ARG_PAGES*TARGET_PAGE_SIZE;
+ /* We reserve one extra page at the top of the stack as guard. */
+ target_mprotect(error, guard, PROT_NONE);
+
+ info->stack_limit = error + guard;
+ stack_base = info->stack_limit + size - MAX_ARG_PAGES*TARGET_PAGE_SIZE;
p += stack_base;
for (i = 0 ; i < MAX_ARG_PAGES ; i++) {
- if (bprm->page[i]) {
- info->rss++;
+ if (bprm->page[i]) {
+ info->rss++;
/* FIXME - check return value of memcpy_to_target() for failure */
- memcpy_to_target(stack_base, bprm->page[i], TARGET_PAGE_SIZE);
- free(bprm->page[i]);
- }
+ memcpy_to_target(stack_base, bprm->page[i], TARGET_PAGE_SIZE);
+ free(bprm->page[i]);
+ }
stack_base += TARGET_PAGE_SIZE;
}
return p;
}
-static void set_brk(abi_ulong start, abi_ulong end)
-{
- /* page-align the start and end addresses... */
- start = HOST_PAGE_ALIGN(start);
- end = HOST_PAGE_ALIGN(end);
- if (end <= start)
- return;
- if(target_mmap(start, end - start,
- PROT_READ | PROT_WRITE | PROT_EXEC,
- MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0) == -1) {
- perror("cannot mmap brk");
- exit(-1);
- }
-}
-
-
-/* We need to explicitly zero any fractional pages after the data
- section (i.e. bss). This would contain the junk from the file that
- should not be in memory. */
-static void padzero(abi_ulong elf_bss, abi_ulong last_bss)
-{
- abi_ulong nbyte;
-
- if (elf_bss >= last_bss)
- return;
-
- /* XXX: this is really a hack : if the real host page size is
- smaller than the target page size, some pages after the end
- of the file may not be mapped. A better fix would be to
- patch target_mmap(), but it is more complicated as the file
- size must be known */
- if (qemu_real_host_page_size < qemu_host_page_size) {
- abi_ulong end_addr, end_addr1;
- end_addr1 = (elf_bss + qemu_real_host_page_size - 1) &
- ~(qemu_real_host_page_size - 1);
- end_addr = HOST_PAGE_ALIGN(elf_bss);
- if (end_addr1 < end_addr) {
- mmap((void *)g2h(end_addr1), end_addr - end_addr1,
- PROT_READ|PROT_WRITE|PROT_EXEC,
- MAP_FIXED|MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
- }
- }
+/* Map and zero the bss. We need to explicitly zero any fractional pages
+ after the data section (i.e. bss). */
+static void zero_bss(abi_ulong elf_bss, abi_ulong last_bss, int prot)
+{
+ uintptr_t host_start, host_map_start, host_end;
- nbyte = elf_bss & (qemu_host_page_size-1);
- if (nbyte) {
- nbyte = qemu_host_page_size - nbyte;
- do {
- /* FIXME - what to do if put_user() fails? */
- put_user_u8(0, elf_bss);
- elf_bss++;
- } while (--nbyte);
+ last_bss = TARGET_PAGE_ALIGN(last_bss);
+
+ /* ??? There is confusion between qemu_real_host_page_size and
+ qemu_host_page_size here and elsewhere in target_mmap, which
+ may lead to the end of the data section mapping from the file
+ not being mapped. At least there was an explicit test and
+ comment for that here, suggesting that "the file size must
+ be known". The comment probably pre-dates the introduction
+ of the fstat system call in target_mmap which does in fact
+ find out the size. What isn't clear is if the workaround
+ here is still actually needed. For now, continue with it,
+ but merge it with the "normal" mmap that would allocate the bss. */
+
+ host_start = (uintptr_t) g2h(elf_bss);
+ host_end = (uintptr_t) g2h(last_bss);
+ host_map_start = (host_start + qemu_real_host_page_size - 1);
+ host_map_start &= -qemu_real_host_page_size;
+
+ if (host_map_start < host_end) {
+ void *p = mmap((void *)host_map_start, host_end - host_map_start,
+ prot, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (p == MAP_FAILED) {
+ perror("cannot mmap brk");
+ exit(-1);
}
-}
+ /* Since we didn't use target_mmap, make sure to record
+ the validity of the pages with qemu. */
+ page_set_flags(elf_bss & TARGET_PAGE_MASK, last_bss, prot|PAGE_VALID);
+ }
+
+ if (host_start < host_map_start) {
+ memset((void *)host_start, 0, host_map_start - host_start);
+ }
+}
static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc,
- struct elfhdr * exec,
- abi_ulong load_addr,
- abi_ulong load_bias,
- abi_ulong interp_load_addr, int ibcs,
- struct image_info *info)
-{
- abi_ulong sp;
- int size;
- abi_ulong u_platform;
- const char *k_platform;
- const int n = sizeof(elf_addr_t);
-
- sp = p;
- u_platform = 0;
- k_platform = ELF_PLATFORM;
- if (k_platform) {
- size_t len = strlen(k_platform) + 1;
- sp -= (len + n - 1) & ~(n - 1);
- u_platform = sp;
- /* FIXME - check return value of memcpy_to_target() for failure */
- memcpy_to_target(sp, k_platform, len);
- }
- /*
- * Force 16 byte _final_ alignment here for generality.
- */
- sp = sp &~ (abi_ulong)15;
- size = (DLINFO_ITEMS + 1) * 2;
- if (k_platform)
- size += 2;
+ struct elfhdr *exec,
+ struct image_info *info,
+ struct image_info *interp_info)
+{
+ abi_ulong sp;
+ int size;
+ abi_ulong u_platform;
+ const char *k_platform;
+ const int n = sizeof(elf_addr_t);
+
+ sp = p;
+ u_platform = 0;
+ k_platform = ELF_PLATFORM;
+ if (k_platform) {
+ size_t len = strlen(k_platform) + 1;
+ sp -= (len + n - 1) & ~(n - 1);
+ u_platform = sp;
+ /* FIXME - check return value of memcpy_to_target() for failure */
+ memcpy_to_target(sp, k_platform, len);
+ }
+ /*
+ * Force 16 byte _final_ alignment here for generality.
+ */
+ sp = sp &~ (abi_ulong)15;
+ size = (DLINFO_ITEMS + 1) * 2;
+ if (k_platform)
+ size += 2;
#ifdef DLINFO_ARCH_ITEMS
- size += DLINFO_ARCH_ITEMS * 2;
+ size += DLINFO_ARCH_ITEMS * 2;
#endif
- size += envc + argc + 2;
- size += (!ibcs ? 3 : 1); /* argc itself */
- size *= n;
- if (size & 15)
- sp -= 16 - (size & 15);
-
- /* This is correct because Linux defines
- * elf_addr_t as Elf32_Off / Elf64_Off
- */
-#define NEW_AUX_ENT(id, val) do { \
- sp -= n; put_user_ual(val, sp); \
- sp -= n; put_user_ual(id, sp); \
- } while(0)
-
- NEW_AUX_ENT (AT_NULL, 0);
-
- /* There must be exactly DLINFO_ITEMS entries here. */
- NEW_AUX_ENT(AT_PHDR, (abi_ulong)(load_addr + exec->e_phoff));
- NEW_AUX_ENT(AT_PHENT, (abi_ulong)(sizeof (struct elf_phdr)));
- NEW_AUX_ENT(AT_PHNUM, (abi_ulong)(exec->e_phnum));
- NEW_AUX_ENT(AT_PAGESZ, (abi_ulong)(TARGET_PAGE_SIZE));
- NEW_AUX_ENT(AT_BASE, (abi_ulong)(interp_load_addr));
- NEW_AUX_ENT(AT_FLAGS, (abi_ulong)0);
- NEW_AUX_ENT(AT_ENTRY, load_bias + exec->e_entry);
- NEW_AUX_ENT(AT_UID, (abi_ulong) getuid());
- NEW_AUX_ENT(AT_EUID, (abi_ulong) geteuid());
- NEW_AUX_ENT(AT_GID, (abi_ulong) getgid());
- NEW_AUX_ENT(AT_EGID, (abi_ulong) getegid());
- NEW_AUX_ENT(AT_HWCAP, (abi_ulong) ELF_HWCAP);
- NEW_AUX_ENT(AT_CLKTCK, (abi_ulong) sysconf(_SC_CLK_TCK));
- if (k_platform)
- NEW_AUX_ENT(AT_PLATFORM, u_platform);
+ size += envc + argc + 2;
+ size += 1; /* argc itself */
+ size *= n;
+ if (size & 15)
+ sp -= 16 - (size & 15);
+
+ /* This is correct because Linux defines
+ * elf_addr_t as Elf32_Off / Elf64_Off
+ */
+#define NEW_AUX_ENT(id, val) do { \
+ sp -= n; put_user_ual(val, sp); \
+ sp -= n; put_user_ual(id, sp); \
+ } while(0)
+
+ NEW_AUX_ENT (AT_NULL, 0);
+
+ /* There must be exactly DLINFO_ITEMS entries here. */
+ NEW_AUX_ENT(AT_PHDR, (abi_ulong)(info->load_addr + exec->e_phoff));
+ NEW_AUX_ENT(AT_PHENT, (abi_ulong)(sizeof (struct elf_phdr)));
+ NEW_AUX_ENT(AT_PHNUM, (abi_ulong)(exec->e_phnum));
+ NEW_AUX_ENT(AT_PAGESZ, (abi_ulong)(TARGET_PAGE_SIZE));
+ NEW_AUX_ENT(AT_BASE, (abi_ulong)(interp_info ? interp_info->load_addr : 0));
+ NEW_AUX_ENT(AT_FLAGS, (abi_ulong)0);
+ NEW_AUX_ENT(AT_ENTRY, info->entry);
+ NEW_AUX_ENT(AT_UID, (abi_ulong) getuid());
+ NEW_AUX_ENT(AT_EUID, (abi_ulong) geteuid());
+ NEW_AUX_ENT(AT_GID, (abi_ulong) getgid());
+ NEW_AUX_ENT(AT_EGID, (abi_ulong) getegid());
+ NEW_AUX_ENT(AT_HWCAP, (abi_ulong) ELF_HWCAP);
+ NEW_AUX_ENT(AT_CLKTCK, (abi_ulong) sysconf(_SC_CLK_TCK));
+ if (k_platform)
+ NEW_AUX_ENT(AT_PLATFORM, u_platform);
#ifdef ARCH_DLINFO
- /*
- * ARCH_DLINFO must come last so platform specific code can enforce
- * special alignment requirements on the AUXV if necessary (eg. PPC).
- */
- ARCH_DLINFO;
+ /*
+ * ARCH_DLINFO must come last so platform specific code can enforce
+ * special alignment requirements on the AUXV if necessary (eg. PPC).
+ */
+ ARCH_DLINFO;
#endif
#undef NEW_AUX_ENT
- info->saved_auxv = sp;
+ info->saved_auxv = sp;
- sp = loader_build_argptr(envc, argc, sp, p, !ibcs);
- return sp;
+ sp = loader_build_argptr(envc, argc, sp, p, 0);
+ return sp;
}
+/* Load an ELF image into the address space.
-static abi_ulong load_elf_interp(struct elfhdr * interp_elf_ex,
- int interpreter_fd,
- abi_ulong *interp_load_addr)
+ IMAGE_NAME is the filename of the image, to use in error messages.
+ IMAGE_FD is the open file descriptor for the image.
+
+ BPRM_BUF is a copy of the beginning of the file; this of course
+ contains the elf file header at offset 0. It is assumed that this
+ buffer is sufficiently aligned to present no problems to the host
+ in accessing data at aligned offsets within the buffer.
+
+ On return: INFO values will be filled in, as necessary or available. */
+
+static void load_elf_image(const char *image_name, int image_fd,
+ struct image_info *info, char **pinterp_name,
+ char bprm_buf[BPRM_BUF_SIZE])
{
- struct elf_phdr *elf_phdata = NULL;
- struct elf_phdr *eppnt;
- abi_ulong load_addr = 0;
- int load_addr_set = 0;
- int retval;
- abi_ulong last_bss, elf_bss;
- abi_ulong error;
- int i;
+ struct elfhdr *ehdr = (struct elfhdr *)bprm_buf;
+ struct elf_phdr *phdr;
+ abi_ulong load_addr, load_bias, loaddr, hiaddr, error;
+ int i, retval;
+ const char *errmsg;
- elf_bss = 0;
- last_bss = 0;
- error = 0;
+ /* First of all, some simple consistency checks */
+ errmsg = "Invalid ELF image for this architecture";
+ if (!elf_check_ident(ehdr)) {
+ goto exit_errmsg;
+ }
+ bswap_ehdr(ehdr);
+ if (!elf_check_ehdr(ehdr)) {
+ goto exit_errmsg;
+ }
-#ifdef BSWAP_NEEDED
- bswap_ehdr(interp_elf_ex);
+ i = ehdr->e_phnum * sizeof(struct elf_phdr);
+ if (ehdr->e_phoff + i <= BPRM_BUF_SIZE) {
+ phdr = (struct elf_phdr *)(bprm_buf + ehdr->e_phoff);
+ } else {
+ phdr = (struct elf_phdr *) alloca(i);
+ retval = pread(image_fd, phdr, i, ehdr->e_phoff);
+ if (retval != i) {
+ goto exit_read;
+ }
+ }
+ bswap_phdr(phdr, ehdr->e_phnum);
+
+ /* Find the maximum size of the image and allocate an appropriate
+ amount of memory to handle that. */
+ loaddr = -1, hiaddr = 0;
+ for (i = 0; i < ehdr->e_phnum; ++i) {
+ if (phdr[i].p_type == PT_LOAD) {
+ abi_ulong a = phdr[i].p_vaddr;
+ if (a < loaddr) {
+ loaddr = a;
+ }
+ a += phdr[i].p_memsz;
+ if (a > hiaddr) {
+ hiaddr = a;
+ }
+ }
+ }
+
+ load_addr = loaddr;
+ if (ehdr->e_type == ET_DYN) {
+ /* The image indicates that it can be loaded anywhere. Find a
+ location that can hold the memory space required. If the
+ image is pre-linked, LOADDR will be non-zero. Since we do
+ not supply MAP_FIXED here we'll use that address if and
+ only if it remains available. */
+ load_addr = target_mmap(loaddr, hiaddr - loaddr, PROT_NONE,
+ MAP_PRIVATE | MAP_ANON | MAP_NORESERVE,
+ -1, 0);
+ if (load_addr == -1) {
+ goto exit_perror;
+ }
+ } else if (pinterp_name != NULL) {
+ /* This is the main executable. Make sure that the low
+ address does not conflict with MMAP_MIN_ADDR or the
+ QEMU application itself. */
+#if defined(CONFIG_USE_GUEST_BASE)
+ /*
+ * In case where user has not explicitly set the guest_base, we
+ * probe here that should we set it automatically.
+ */
+ if (!have_guest_base && !reserved_va) {
+ unsigned long host_start, real_start, host_size;
+
+ /* Round addresses to page boundaries. */
+ loaddr &= qemu_host_page_mask;
+ hiaddr = HOST_PAGE_ALIGN(hiaddr);
+
+ if (loaddr < mmap_min_addr) {
+ host_start = HOST_PAGE_ALIGN(mmap_min_addr);
+ } else {
+ host_start = loaddr;
+ if (host_start != loaddr) {
+ errmsg = "Address overflow loading ELF binary";
+ goto exit_errmsg;
+ }
+ }
+ host_size = hiaddr - loaddr;
+ while (1) {
+ /* Do not use mmap_find_vma here because that is limited to the
+ guest address space. We are going to make the
+ guest address space fit whatever we're given. */
+ real_start = (unsigned long)
+ mmap((void *)host_start, host_size, PROT_NONE,
+ MAP_ANONYMOUS | MAP_PRIVATE | MAP_NORESERVE, -1, 0);
+ if (real_start == (unsigned long)-1) {
+ goto exit_perror;
+ }
+ if (real_start == host_start) {
+ break;
+ }
+ /* That address didn't work. Unmap and try a different one.
+ The address the host picked because is typically right at
+ the top of the host address space and leaves the guest with
+ no usable address space. Resort to a linear search. We
+ already compensated for mmap_min_addr, so this should not
+ happen often. Probably means we got unlucky and host
+ address space randomization put a shared library somewhere
+ inconvenient. */
+ munmap((void *)real_start, host_size);
+ host_start += qemu_host_page_size;
+ if (host_start == loaddr) {
+ /* Theoretically possible if host doesn't have any suitably
+ aligned areas. Normally the first mmap will fail. */
+ errmsg = "Unable to find space for application";
+ goto exit_errmsg;
+ }
+ }
+ qemu_log("Relocating guest address space from 0x"
+ TARGET_ABI_FMT_lx " to 0x%lx\n", loaddr, real_start);
+ guest_base = real_start - loaddr;
+ }
#endif
- /* First of all, some simple consistency checks */
- if ((interp_elf_ex->e_type != ET_EXEC &&
- interp_elf_ex->e_type != ET_DYN) ||
- !elf_check_arch(interp_elf_ex->e_machine)) {
- return ~((abi_ulong)0UL);
- }
+ }
+ load_bias = load_addr - loaddr;
+ info->load_bias = load_bias;
+ info->load_addr = load_addr;
+ info->entry = ehdr->e_entry + load_bias;
+ info->start_code = -1;
+ info->end_code = 0;
+ info->start_data = -1;
+ info->end_data = 0;
+ info->brk = 0;
- /* Now read in all of the header information */
+ for (i = 0; i < ehdr->e_phnum; i++) {
+ struct elf_phdr *eppnt = phdr + i;
+ if (eppnt->p_type == PT_LOAD) {
+ abi_ulong vaddr, vaddr_po, vaddr_ps, vaddr_ef, vaddr_em;
+ int elf_prot = 0;
- if (sizeof(struct elf_phdr) * interp_elf_ex->e_phnum > TARGET_PAGE_SIZE)
- return ~(abi_ulong)0UL;
+ if (eppnt->p_flags & PF_R) elf_prot = PROT_READ;
+ if (eppnt->p_flags & PF_W) elf_prot |= PROT_WRITE;
+ if (eppnt->p_flags & PF_X) elf_prot |= PROT_EXEC;
- elf_phdata = (struct elf_phdr *)
- malloc(sizeof(struct elf_phdr) * interp_elf_ex->e_phnum);
+ vaddr = load_bias + eppnt->p_vaddr;
+ vaddr_po = TARGET_ELF_PAGEOFFSET(vaddr);
+ vaddr_ps = TARGET_ELF_PAGESTART(vaddr);
- if (!elf_phdata)
- return ~((abi_ulong)0UL);
+ error = target_mmap(vaddr_ps, eppnt->p_filesz + vaddr_po,
+ elf_prot, MAP_PRIVATE | MAP_FIXED,
+ image_fd, eppnt->p_offset - vaddr_po);
+ if (error == -1) {
+ goto exit_perror;
+ }
- /*
- * If the size of this structure has changed, then punt, since
- * we will be doing the wrong thing.
- */
- if (interp_elf_ex->e_phentsize != sizeof(struct elf_phdr)) {
- free(elf_phdata);
- return ~((abi_ulong)0UL);
- }
+ vaddr_ef = vaddr + eppnt->p_filesz;
+ vaddr_em = vaddr + eppnt->p_memsz;
- retval = lseek(interpreter_fd, interp_elf_ex->e_phoff, SEEK_SET);
- if(retval >= 0) {
- retval = read(interpreter_fd,
- (char *) elf_phdata,
- sizeof(struct elf_phdr) * interp_elf_ex->e_phnum);
- }
- if (retval < 0) {
- perror("load_elf_interp");
- exit(-1);
- free (elf_phdata);
- return retval;
- }
-#ifdef BSWAP_NEEDED
- eppnt = elf_phdata;
- for (i=0; i<interp_elf_ex->e_phnum; i++, eppnt++) {
- bswap_phdr(eppnt);
- }
-#endif
+ /* If the load segment requests extra zeros (e.g. bss), map it. */
+ if (vaddr_ef < vaddr_em) {
+ zero_bss(vaddr_ef, vaddr_em, elf_prot);
+ }
- if (interp_elf_ex->e_type == ET_DYN) {
- /* in order to avoid hardcoding the interpreter load
- address in qemu, we allocate a big enough memory zone */
- error = target_mmap(0, INTERP_MAP_SIZE,
- PROT_NONE, MAP_PRIVATE | MAP_ANON,
- -1, 0);
- if (error == -1) {
- perror("mmap");
- exit(-1);
+ /* Find the full program boundaries. */
+ if (elf_prot & PROT_EXEC) {
+ if (vaddr < info->start_code) {
+ info->start_code = vaddr;
+ }
+ if (vaddr_ef > info->end_code) {
+ info->end_code = vaddr_ef;
+ }
+ }
+ if (elf_prot & PROT_WRITE) {
+ if (vaddr < info->start_data) {
+ info->start_data = vaddr;
+ }
+ if (vaddr_ef > info->end_data) {
+ info->end_data = vaddr_ef;
+ }
+ if (vaddr_em > info->brk) {
+ info->brk = vaddr_em;
+ }
+ }
+ } else if (eppnt->p_type == PT_INTERP && pinterp_name) {
+ char *interp_name;
+
+ if (*pinterp_name) {
+ errmsg = "Multiple PT_INTERP entries";
+ goto exit_errmsg;
+ }
+ interp_name = malloc(eppnt->p_filesz);
+ if (!interp_name) {
+ goto exit_perror;
}
- load_addr = error;
- load_addr_set = 1;
+
+ if (eppnt->p_offset + eppnt->p_filesz <= BPRM_BUF_SIZE) {
+ memcpy(interp_name, bprm_buf + eppnt->p_offset,
+ eppnt->p_filesz);
+ } else {
+ retval = pread(image_fd, interp_name, eppnt->p_filesz,
+ eppnt->p_offset);
+ if (retval != eppnt->p_filesz) {
+ goto exit_perror;
+ }
+ }
+ if (interp_name[eppnt->p_filesz - 1] != 0) {
+ errmsg = "Invalid PT_INTERP entry";
+ goto exit_errmsg;
+ }
+ *pinterp_name = interp_name;
}
+ }
+
+ if (info->end_data == 0) {
+ info->start_data = info->end_code;
+ info->end_data = info->end_code;
+ info->brk = info->end_code;
+ }
+
+ if (qemu_log_enabled()) {
+ load_symbols(ehdr, image_fd, load_bias);
+ }
+
+ close(image_fd);
+ return;
- eppnt = elf_phdata;
- for(i=0; i<interp_elf_ex->e_phnum; i++, eppnt++)
- if (eppnt->p_type == PT_LOAD) {
- int elf_type = MAP_PRIVATE | MAP_DENYWRITE;
- int elf_prot = 0;
- abi_ulong vaddr = 0;
- abi_ulong k;
-
- if (eppnt->p_flags & PF_R) elf_prot = PROT_READ;
- if (eppnt->p_flags & PF_W) elf_prot |= PROT_WRITE;
- if (eppnt->p_flags & PF_X) elf_prot |= PROT_EXEC;
- if (interp_elf_ex->e_type == ET_EXEC || load_addr_set) {
- elf_type |= MAP_FIXED;
- vaddr = eppnt->p_vaddr;
- }
- error = target_mmap(load_addr+TARGET_ELF_PAGESTART(vaddr),
- eppnt->p_filesz + TARGET_ELF_PAGEOFFSET(eppnt->p_vaddr),
- elf_prot,
- elf_type,
- interpreter_fd,
- eppnt->p_offset - TARGET_ELF_PAGEOFFSET(eppnt->p_vaddr));
-
- if (error == -1) {
- /* Real error */
- close(interpreter_fd);
- free(elf_phdata);
- return ~((abi_ulong)0UL);
- }
-
- if (!load_addr_set && interp_elf_ex->e_type == ET_DYN) {
- load_addr = error;
- load_addr_set = 1;
- }
-
- /*
- * Find the end of the file mapping for this phdr, and keep
- * track of the largest address we see for this.
- */
- k = load_addr + eppnt->p_vaddr + eppnt->p_filesz;
- if (k > elf_bss) elf_bss = k;
-
- /*
- * Do the same thing for the memory mapping - between
- * elf_bss and last_bss is the bss section.
- */
- k = load_addr + eppnt->p_memsz + eppnt->p_vaddr;
- if (k > last_bss) last_bss = k;
- }
-
- /* Now use mmap to map the library into memory. */
-
- close(interpreter_fd);
-
- /*
- * Now fill out the bss section. First pad the last page up
- * to the page boundary, and then perform a mmap to make sure
- * that there are zeromapped pages up to and including the last
- * bss page.
- */
- padzero(elf_bss, last_bss);
- elf_bss = TARGET_ELF_PAGESTART(elf_bss + qemu_host_page_size - 1); /* What we have mapped so far */
-
- /* Map the last of the bss segment */
- if (last_bss > elf_bss) {
- target_mmap(elf_bss, last_bss-elf_bss,
- PROT_READ|PROT_WRITE|PROT_EXEC,
- MAP_FIXED|MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
- }
- free(elf_phdata);
-
- *interp_load_addr = load_addr;
- return ((abi_ulong) interp_elf_ex->e_entry) + load_addr;
+ exit_read:
+ if (retval >= 0) {
+ errmsg = "Incomplete read of file header";
+ goto exit_errmsg;
+ }
+ exit_perror:
+ errmsg = strerror(errno);
+ exit_errmsg:
+ fprintf(stderr, "%s: %s\n", image_name, errmsg);
+ exit(-1);
+}
+
+static void load_elf_interp(const char *filename, struct image_info *info,
+ char bprm_buf[BPRM_BUF_SIZE])
+{
+ int fd, retval;
+
+ fd = open(path(filename), O_RDONLY);
+ if (fd < 0) {
+ goto exit_perror;
+ }
+
+ retval = read(fd, bprm_buf, BPRM_BUF_SIZE);
+ if (retval < 0) {
+ goto exit_perror;
+ }
+ if (retval < BPRM_BUF_SIZE) {
+ memset(bprm_buf + retval, 0, BPRM_BUF_SIZE - retval);
+ }
+
+ load_elf_image(filename, fd, info, NULL, bprm_buf);
+ return;
+
+ exit_perror:
+ fprintf(stderr, "%s: %s\n", filename, strerror(errno));
+ exit(-1);
}
static int symfind(const void *s0, const void *s1)
@@ -1371,88 +1475,97 @@ static int symcmp(const void *s0, const void *s1)
}
/* Best attempt to load symbols from this ELF object. */
-static void load_symbols(struct elfhdr *hdr, int fd)
+static void load_symbols(struct elfhdr *hdr, int fd, abi_ulong load_bias)
{
- unsigned int i, nsyms;
- struct elf_shdr sechdr, symtab, strtab;
+ int i, shnum, nsyms, sym_idx = 0, str_idx = 0;
+ struct elf_shdr *shdr;
char *strings;
struct syminfo *s;
struct elf_sym *syms;
- lseek(fd, hdr->e_shoff, SEEK_SET);
- for (i = 0; i < hdr->e_shnum; i++) {
- if (read(fd, &sechdr, sizeof(sechdr)) != sizeof(sechdr))
- return;
-#ifdef BSWAP_NEEDED
- bswap_shdr(&sechdr);
-#endif
- if (sechdr.sh_type == SHT_SYMTAB) {
- symtab = sechdr;
- lseek(fd, hdr->e_shoff
- + sizeof(sechdr) * sechdr.sh_link, SEEK_SET);
- if (read(fd, &strtab, sizeof(strtab))
- != sizeof(strtab))
- return;
-#ifdef BSWAP_NEEDED
- bswap_shdr(&strtab);
-#endif
+ shnum = hdr->e_shnum;
+ i = shnum * sizeof(struct elf_shdr);
+ shdr = (struct elf_shdr *)alloca(i);
+ if (pread(fd, shdr, i, hdr->e_shoff) != i) {
+ return;
+ }
+
+ bswap_shdr(shdr, shnum);
+ for (i = 0; i < shnum; ++i) {
+ if (shdr[i].sh_type == SHT_SYMTAB) {
+ sym_idx = i;
+ str_idx = shdr[i].sh_link;
goto found;
}
}
- return; /* Shouldn't happen... */
+
+ /* There will be no symbol table if the file was stripped. */
+ return;
found:
- /* Now know where the strtab and symtab are. Snarf them. */
+ /* Now know where the strtab and symtab are. Snarf them. */
s = malloc(sizeof(*s));
- syms = malloc(symtab.sh_size);
- if (!syms)
- return;
- s->disas_strtab = strings = malloc(strtab.sh_size);
- if (!s->disas_strtab)
+ if (!s) {
return;
+ }
- lseek(fd, symtab.sh_offset, SEEK_SET);
- if (read(fd, syms, symtab.sh_size) != symtab.sh_size)
+ i = shdr[str_idx].sh_size;
+ s->disas_strtab = strings = malloc(i);
+ if (!strings || pread(fd, strings, i, shdr[str_idx].sh_offset) != i) {
+ free(s);
+ free(strings);
return;
+ }
- nsyms = symtab.sh_size / sizeof(struct elf_sym);
+ i = shdr[sym_idx].sh_size;
+ syms = malloc(i);
+ if (!syms || pread(fd, syms, i, shdr[sym_idx].sh_offset) != i) {
+ free(s);
+ free(strings);
+ free(syms);
+ return;
+ }
- i = 0;
- while (i < nsyms) {
-#ifdef BSWAP_NEEDED
+ nsyms = i / sizeof(struct elf_sym);
+ for (i = 0; i < nsyms; ) {
bswap_sym(syms + i);
-#endif
- // Throw away entries which we do not need.
- if (syms[i].st_shndx == SHN_UNDEF ||
- syms[i].st_shndx >= SHN_LORESERVE ||
- ELF_ST_TYPE(syms[i].st_info) != STT_FUNC) {
- nsyms--;
- if (i < nsyms) {
+ /* Throw away entries which we do not need. */
+ if (syms[i].st_shndx == SHN_UNDEF
+ || syms[i].st_shndx >= SHN_LORESERVE
+ || ELF_ST_TYPE(syms[i].st_info) != STT_FUNC) {
+ if (i < --nsyms) {
syms[i] = syms[nsyms];
}
- continue;
- }
+ } else {
#if defined(TARGET_ARM) || defined (TARGET_MIPS)
- /* The bottom address bit marks a Thumb or MIPS16 symbol. */
- syms[i].st_value &= ~(target_ulong)1;
+ /* The bottom address bit marks a Thumb or MIPS16 symbol. */
+ syms[i].st_value &= ~(target_ulong)1;
#endif
- i++;
+ syms[i].st_value += load_bias;
+ i++;
+ }
}
+
+ /* Attempt to free the storage associated with the local symbols
+ that we threw away. Whether or not this has any effect on the
+ memory allocation depends on the malloc implementation and how
+ many symbols we managed to discard. */
syms = realloc(syms, nsyms * sizeof(*syms));
+ if (syms == NULL) {
+ free(s);
+ free(strings);
+ return;
+ }
qsort(syms, nsyms, sizeof(*syms), symcmp);
- lseek(fd, strtab.sh_offset, SEEK_SET);
- if (read(fd, strings, strtab.sh_size) != strtab.sh_size)
- return;
s->disas_num_syms = nsyms;
#if ELF_CLASS == ELFCLASS32
s->disas_symtab.elf32 = syms;
- s->lookup_symbol = lookup_symbolxx;
#else
s->disas_symtab.elf64 = syms;
- s->lookup_symbol = lookup_symbolxx;
#endif
+ s->lookup_symbol = lookup_symbolxx;
s->next = syminfos;
syminfos = s;
}
@@ -1460,477 +1573,67 @@ static void load_symbols(struct elfhdr *hdr, int fd)
int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * regs,
struct image_info * info)
{
+ struct image_info interp_info;
struct elfhdr elf_ex;
- struct elfhdr interp_elf_ex;
- struct exec interp_ex;
- int interpreter_fd = -1; /* avoid warning */
- abi_ulong load_addr, load_bias;
- int load_addr_set = 0;
- unsigned int interpreter_type = INTERPRETER_NONE;
- unsigned char ibcs2_interpreter;
- int i;
- abi_ulong mapped_addr;
- struct elf_phdr * elf_ppnt;
- struct elf_phdr *elf_phdata;
- abi_ulong elf_bss, k, elf_brk;
- int retval;
- char * elf_interpreter;
- abi_ulong elf_entry, interp_load_addr = 0;
- int status;
- abi_ulong start_code, end_code, start_data, end_data;
- abi_ulong reloc_func_desc = 0;
- abi_ulong elf_stack;
- char passed_fileno[6];
-
- ibcs2_interpreter = 0;
- status = 0;
- load_addr = 0;
- load_bias = 0;
- elf_ex = *((struct elfhdr *) bprm->buf); /* exec-header */
-#ifdef BSWAP_NEEDED
- bswap_ehdr(&elf_ex);
-#endif
-
- /* First of all, some simple consistency checks */
- if ((elf_ex.e_type != ET_EXEC && elf_ex.e_type != ET_DYN) ||
- (! elf_check_arch(elf_ex.e_machine))) {
- return -ENOEXEC;
- }
-
- bprm->p = copy_elf_strings(1, &bprm->filename, bprm->page, bprm->p);
- bprm->p = copy_elf_strings(bprm->envc,bprm->envp,bprm->page,bprm->p);
- bprm->p = copy_elf_strings(bprm->argc,bprm->argv,bprm->page,bprm->p);
- if (!bprm->p) {
- retval = -E2BIG;
- }
-
- /* Now read in all of the header information */
- elf_phdata = (struct elf_phdr *)malloc(elf_ex.e_phentsize*elf_ex.e_phnum);
- if (elf_phdata == NULL) {
- return -ENOMEM;
- }
-
- retval = lseek(bprm->fd, elf_ex.e_phoff, SEEK_SET);
- if(retval > 0) {
- retval = read(bprm->fd, (char *) elf_phdata,
- elf_ex.e_phentsize * elf_ex.e_phnum);
- }
-
- if (retval < 0) {
- perror("load_elf_binary");
- exit(-1);
- free (elf_phdata);
- return -errno;
- }
-
-#ifdef BSWAP_NEEDED
- elf_ppnt = elf_phdata;
- for (i=0; i<elf_ex.e_phnum; i++, elf_ppnt++) {
- bswap_phdr(elf_ppnt);
- }
-#endif
- elf_ppnt = elf_phdata;
-
- elf_bss = 0;
- elf_brk = 0;
-
-
- elf_stack = ~((abi_ulong)0UL);
- elf_interpreter = NULL;
- start_code = ~((abi_ulong)0UL);
- end_code = 0;
- start_data = 0;
- end_data = 0;
- interp_ex.a_info = 0;
-
- for(i=0;i < elf_ex.e_phnum; i++) {
- if (elf_ppnt->p_type == PT_INTERP) {
- if ( elf_interpreter != NULL )
- {
- free (elf_phdata);
- free(elf_interpreter);
- close(bprm->fd);
- return -EINVAL;
- }
-
- /* This is the program interpreter used for
- * shared libraries - for now assume that this
- * is an a.out format binary
- */
-
- elf_interpreter = (char *)malloc(elf_ppnt->p_filesz);
-
- if (elf_interpreter == NULL) {
- free (elf_phdata);
- close(bprm->fd);
- return -ENOMEM;
- }
-
- retval = lseek(bprm->fd, elf_ppnt->p_offset, SEEK_SET);
- if(retval >= 0) {
- retval = read(bprm->fd, elf_interpreter, elf_ppnt->p_filesz);
- }
- if(retval < 0) {
- perror("load_elf_binary2");
- exit(-1);
- }
-
- /* If the program interpreter is one of these two,
- then assume an iBCS2 image. Otherwise assume
- a native linux image. */
-
- /* JRP - Need to add X86 lib dir stuff here... */
-
- if (strcmp(elf_interpreter,"/usr/lib/libc.so.1") == 0 ||
- strcmp(elf_interpreter,"/usr/lib/ld.so.1") == 0) {
- ibcs2_interpreter = 1;
- }
+ char *elf_interpreter = NULL;
-#if 0
- printf("Using ELF interpreter %s\n", path(elf_interpreter));
-#endif
- if (retval >= 0) {
- retval = open(path(elf_interpreter), O_RDONLY);
- if(retval >= 0) {
- interpreter_fd = retval;
- }
- else {
- perror(elf_interpreter);
- exit(-1);
- /* retval = -errno; */
- }
- }
-
- if (retval >= 0) {
- retval = lseek(interpreter_fd, 0, SEEK_SET);
- if(retval >= 0) {
- retval = read(interpreter_fd,bprm->buf,128);
- }
- }
- if (retval >= 0) {
- interp_ex = *((struct exec *) bprm->buf); /* aout exec-header */
- interp_elf_ex = *((struct elfhdr *) bprm->buf); /* elf exec-header */
- }
- if (retval < 0) {
- perror("load_elf_binary3");
- exit(-1);
- free (elf_phdata);
- free(elf_interpreter);
- close(bprm->fd);
- return retval;
- }
- }
- elf_ppnt++;
- }
-
- /* Some simple consistency checks for the interpreter */
- if (elf_interpreter){
- interpreter_type = INTERPRETER_ELF | INTERPRETER_AOUT;
-
- /* Now figure out which format our binary is */
- if ((N_MAGIC(interp_ex) != OMAGIC) && (N_MAGIC(interp_ex) != ZMAGIC) &&
- (N_MAGIC(interp_ex) != QMAGIC)) {
- interpreter_type = INTERPRETER_ELF;
- }
-
- if (interp_elf_ex.e_ident[0] != 0x7f ||
- strncmp((char *)&interp_elf_ex.e_ident[1], "ELF",3) != 0) {
- interpreter_type &= ~INTERPRETER_ELF;
- }
-
- if (!interpreter_type) {
- free(elf_interpreter);
- free(elf_phdata);
- close(bprm->fd);
- return -ELIBBAD;
- }
- }
-
- /* OK, we are done with that, now set up the arg stuff,
- and then start this sucker up */
-
- {
- char * passed_p;
-
- if (interpreter_type == INTERPRETER_AOUT) {
- snprintf(passed_fileno, sizeof(passed_fileno), "%d", bprm->fd);
- passed_p = passed_fileno;
-
- if (elf_interpreter) {
- bprm->p = copy_elf_strings(1,&passed_p,bprm->page,bprm->p);
- bprm->argc++;
- }
- }
- if (!bprm->p) {
- if (elf_interpreter) {
- free(elf_interpreter);
- }
- free (elf_phdata);
- close(bprm->fd);
- return -E2BIG;
- }
- }
-
- /* OK, This is the point of no return */
- info->end_data = 0;
- info->end_code = 0;
info->start_mmap = (abi_ulong)ELF_START_MMAP;
info->mmap = 0;
- elf_entry = (abi_ulong) elf_ex.e_entry;
+ info->rss = 0;
-#if defined(CONFIG_USE_GUEST_BASE)
- /*
- * In case where user has not explicitly set the guest_base, we
- * probe here that should we set it automatically.
- */
- if (!(have_guest_base || reserved_va)) {
- /*
- * Go through ELF program header table and find the address
- * range used by loadable segments. Check that this is available on
- * the host, and if not find a suitable value for guest_base. */
- abi_ulong app_start = ~0;
- abi_ulong app_end = 0;
- abi_ulong addr;
- unsigned long host_start;
- unsigned long real_start;
- unsigned long host_size;
- for (i = 0, elf_ppnt = elf_phdata; i < elf_ex.e_phnum;
- i++, elf_ppnt++) {
- if (elf_ppnt->p_type != PT_LOAD)
- continue;
- addr = elf_ppnt->p_vaddr;
- if (addr < app_start) {
- app_start = addr;
- }
- addr += elf_ppnt->p_memsz;
- if (addr > app_end) {
- app_end = addr;
- }
- }
+ load_elf_image(bprm->filename, bprm->fd, info,
+ &elf_interpreter, bprm->buf);
- /* If we don't have any loadable segments then something
- is very wrong. */
- assert(app_start < app_end);
+ /* ??? We need a copy of the elf header for passing to create_elf_tables.
+ If we do nothing, we'll have overwritten this when we re-use bprm->buf
+ when we load the interpreter. */
+ elf_ex = *(struct elfhdr *)bprm->buf;
- /* Round addresses to page boundaries. */
- app_start = app_start & qemu_host_page_mask;
- app_end = HOST_PAGE_ALIGN(app_end);
- if (app_start < mmap_min_addr) {
- host_start = HOST_PAGE_ALIGN(mmap_min_addr);
- } else {
- host_start = app_start;
- if (host_start != app_start) {
- fprintf(stderr, "qemu: Address overflow loading ELF binary\n");
- abort();
- }
- }
- host_size = app_end - app_start;
- while (1) {
- /* Do not use mmap_find_vma here because that is limited to the
- guest address space. We are going to make the
- guest address space fit whatever we're given. */
- real_start = (unsigned long)mmap((void *)host_start, host_size,
- PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_NORESERVE, -1, 0);
- if (real_start == (unsigned long)-1) {
- fprintf(stderr, "qemu: Virtual memory exausted\n");
- abort();
- }
- if (real_start == host_start) {
- break;
- }
- /* That address didn't work. Unmap and try a different one.
- The address the host picked because is typically
- right at the top of the host address space and leaves the
- guest with no usable address space. Resort to a linear search.
- We already compensated for mmap_min_addr, so this should not
- happen often. Probably means we got unlucky and host address
- space randomization put a shared library somewhere
- inconvenient. */
- munmap((void *)real_start, host_size);
- host_start += qemu_host_page_size;
- if (host_start == app_start) {
- /* Theoretically possible if host doesn't have any
- suitably aligned areas. Normally the first mmap will
- fail. */
- fprintf(stderr, "qemu: Unable to find space for application\n");
- abort();
- }
- }
- qemu_log("Relocating guest address space from 0x" TARGET_ABI_FMT_lx
- " to 0x%lx\n", app_start, real_start);
- guest_base = real_start - app_start;
+ bprm->p = copy_elf_strings(1, &bprm->filename, bprm->page, bprm->p);
+ bprm->p = copy_elf_strings(bprm->envc,bprm->envp,bprm->page,bprm->p);
+ bprm->p = copy_elf_strings(bprm->argc,bprm->argv,bprm->page,bprm->p);
+ if (!bprm->p) {
+ fprintf(stderr, "%s: %s\n", bprm->filename, strerror(E2BIG));
+ exit(-1);
}
-#endif /* CONFIG_USE_GUEST_BASE */
/* Do this so that we can load the interpreter, if need be. We will
change some of these later */
- info->rss = 0;
bprm->p = setup_arg_pages(bprm->p, bprm, info);
- info->start_stack = bprm->p;
- /* Now we do a little grungy work by mmaping the ELF image into
- * the correct location in memory. At this point, we assume that
- * the image should be loaded at fixed address, not at a variable
- * address.
- */
+ if (elf_interpreter) {
+ load_elf_interp(elf_interpreter, &interp_info, bprm->buf);
- for(i = 0, elf_ppnt = elf_phdata; i < elf_ex.e_phnum; i++, elf_ppnt++) {
- int elf_prot = 0;
- int elf_flags = 0;
- abi_ulong error;
+ /* If the program interpreter is one of these two, then assume
+ an iBCS2 image. Otherwise assume a native linux image. */
- if (elf_ppnt->p_type != PT_LOAD)
- continue;
+ if (strcmp(elf_interpreter, "/usr/lib/libc.so.1") == 0
+ || strcmp(elf_interpreter, "/usr/lib/ld.so.1") == 0) {
+ info->personality = PER_SVR4;
- if (elf_ppnt->p_flags & PF_R) elf_prot |= PROT_READ;
- if (elf_ppnt->p_flags & PF_W) elf_prot |= PROT_WRITE;
- if (elf_ppnt->p_flags & PF_X) elf_prot |= PROT_EXEC;
- elf_flags = MAP_PRIVATE | MAP_DENYWRITE;
- if (elf_ex.e_type == ET_EXEC || load_addr_set) {
- elf_flags |= MAP_FIXED;
- } else if (elf_ex.e_type == ET_DYN) {
- /* Try and get dynamic programs out of the way of the default mmap
- base, as well as whatever program they might try to exec. This
- is because the brk will follow the loader, and is not movable. */
- /* NOTE: for qemu, we do a big mmap to get enough space
- without hardcoding any address */
- error = target_mmap(0, ET_DYN_MAP_SIZE,
- PROT_NONE, MAP_PRIVATE | MAP_ANON,
- -1, 0);
- if (error == -1) {
- perror("mmap");
- exit(-1);
- }
- load_bias = TARGET_ELF_PAGESTART(error - elf_ppnt->p_vaddr);
+ /* Why this, you ask??? Well SVr4 maps page 0 as read-only,
+ and some applications "depend" upon this behavior. Since
+ we do not have the power to recompile these, we emulate
+ the SVr4 behavior. Sigh. */
+ target_mmap(0, qemu_host_page_size, PROT_READ | PROT_EXEC,
+ MAP_FIXED | MAP_PRIVATE, -1, 0);
}
-
- error = target_mmap(TARGET_ELF_PAGESTART(load_bias + elf_ppnt->p_vaddr),
- (elf_ppnt->p_filesz +
- TARGET_ELF_PAGEOFFSET(elf_ppnt->p_vaddr)),
- elf_prot,
- (MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE),
- bprm->fd,
- (elf_ppnt->p_offset -
- TARGET_ELF_PAGEOFFSET(elf_ppnt->p_vaddr)));
- if (error == -1) {
- perror("mmap");
- exit(-1);
- }
-
-#ifdef LOW_ELF_STACK
- if (TARGET_ELF_PAGESTART(elf_ppnt->p_vaddr) < elf_stack)
- elf_stack = TARGET_ELF_PAGESTART(elf_ppnt->p_vaddr);
-#endif
-
- if (!load_addr_set) {
- load_addr_set = 1;
- load_addr = elf_ppnt->p_vaddr - elf_ppnt->p_offset;
- if (elf_ex.e_type == ET_DYN) {
- load_bias += error -
- TARGET_ELF_PAGESTART(load_bias + elf_ppnt->p_vaddr);
- load_addr += load_bias;
- reloc_func_desc = load_bias;
- }
- }
- k = elf_ppnt->p_vaddr;
- if (k < start_code)
- start_code = k;
- if (start_data < k)
- start_data = k;
- k = elf_ppnt->p_vaddr + elf_ppnt->p_filesz;
- if (k > elf_bss)
- elf_bss = k;
- if ((elf_ppnt->p_flags & PF_X) && end_code < k)
- end_code = k;
- if (end_data < k)
- end_data = k;
- k = elf_ppnt->p_vaddr + elf_ppnt->p_memsz;
- if (k > elf_brk) elf_brk = k;
- }
-
- elf_entry += load_bias;
- elf_bss += load_bias;
- elf_brk += load_bias;
- start_code += load_bias;
- end_code += load_bias;
- start_data += load_bias;
- end_data += load_bias;
-
- if (elf_interpreter) {
- if (interpreter_type & 1) {
- elf_entry = load_aout_interp(&interp_ex, interpreter_fd);
- }
- else if (interpreter_type & 2) {
- elf_entry = load_elf_interp(&interp_elf_ex, interpreter_fd,
- &interp_load_addr);
- }
- reloc_func_desc = interp_load_addr;
-
- close(interpreter_fd);
- free(elf_interpreter);
-
- if (elf_entry == ~((abi_ulong)0UL)) {
- printf("Unable to load interpreter\n");
- free(elf_phdata);
- exit(-1);
- return 0;
- }
}
- free(elf_phdata);
-
- if (qemu_log_enabled())
- load_symbols(&elf_ex, bprm->fd);
-
- if (interpreter_type != INTERPRETER_AOUT) close(bprm->fd);
- info->personality = (ibcs2_interpreter ? PER_SVR4 : PER_LINUX);
-
-#ifdef LOW_ELF_STACK
- info->start_stack = bprm->p = elf_stack - 4;
-#endif
- bprm->p = create_elf_tables(bprm->p,
- bprm->argc,
- bprm->envc,
- &elf_ex,
- load_addr, load_bias,
- interp_load_addr,
- (interpreter_type == INTERPRETER_AOUT ? 0 : 1),
- info);
- info->load_addr = reloc_func_desc;
- info->start_brk = info->brk = elf_brk;
- info->end_code = end_code;
- info->start_code = start_code;
- info->start_data = start_data;
- info->end_data = end_data;
+ bprm->p = create_elf_tables(bprm->p, bprm->argc, bprm->envc, &elf_ex,
+ info, (elf_interpreter ? &interp_info : NULL));
info->start_stack = bprm->p;
- /* Calling set_brk effectively mmaps the pages that we need for the bss and break
- sections */
- set_brk(elf_bss, elf_brk);
-
- padzero(elf_bss, elf_brk);
-
-#if 0
- printf("(start_brk) %x\n" , info->start_brk);
- printf("(end_code) %x\n" , info->end_code);
- printf("(start_code) %x\n" , info->start_code);
- printf("(end_data) %x\n" , info->end_data);
- printf("(start_stack) %x\n" , info->start_stack);
- printf("(brk) %x\n" , info->brk);
-#endif
-
- if ( info->personality == PER_SVR4 )
- {
- /* Why this, you ask??? Well SVr4 maps page 0 as read-only,
- and some applications "depend" upon this behavior.
- Since we do not have the power to recompile these, we
- emulate the SVr4 behavior. Sigh. */
- mapped_addr = target_mmap(0, qemu_host_page_size, PROT_READ | PROT_EXEC,
- MAP_FIXED | MAP_PRIVATE, -1, 0);
+ /* If we have an interpreter, set that as the program's entry point.
+ Copy the load_addr as well, to help PPC64 interpret the entry
+ point as a function descriptor. Do this after creating elf tables
+ so that we copy the original program entry point into the AUXV. */
+ if (elf_interpreter) {
+ info->load_addr = interp_info.load_addr;
+ info->entry = interp_info.entry;
+ free(elf_interpreter);
}
- info->entry = elf_entry;
-
#ifdef USE_ELF_CORE_DUMP
bprm->core_dump = &elf_core_dump;
#endif
@@ -1939,7 +1642,6 @@ int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * regs,
}
#ifdef USE_ELF_CORE_DUMP
-
/*
* Definitions to generate Intel SVR4-like core files.
* These mostly have the same names as the SVR4 types with "target_elf_"
@@ -2076,17 +1778,17 @@ struct mm_struct {
static struct mm_struct *vma_init(void);
static void vma_delete(struct mm_struct *);
static int vma_add_mapping(struct mm_struct *, abi_ulong,
- abi_ulong, abi_ulong);
+ abi_ulong, abi_ulong);
static int vma_get_mapping_count(const struct mm_struct *);
static struct vm_area_struct *vma_first(const struct mm_struct *);
static struct vm_area_struct *vma_next(struct vm_area_struct *);
static abi_ulong vma_dump_size(const struct vm_area_struct *);
static int vma_walker(void *priv, abi_ulong start, abi_ulong end,
- unsigned long flags);
+ unsigned long flags);
static void fill_elf_header(struct elfhdr *, int, uint16_t, uint32_t);
static void fill_note(struct memelfnote *, const char *, int,
- unsigned int, void *);
+ unsigned int, void *);
static void fill_prstatus(struct target_elf_prstatus *, const TaskState *, int);
static int fill_psinfo(struct target_elf_prpsinfo *, const TaskState *);
static void fill_auxv_note(struct memelfnote *, const TaskState *);
@@ -2102,9 +1804,6 @@ static int write_note(struct memelfnote *, int);
static int write_note_info(struct elf_note_info *, int);
#ifdef BSWAP_NEEDED
-static void bswap_prstatus(struct target_elf_prstatus *);
-static void bswap_psinfo(struct target_elf_prpsinfo *);
-
static void bswap_prstatus(struct target_elf_prstatus *prstatus)
{
prstatus->pr_info.si_signo = tswapl(prstatus->pr_info.si_signo);
@@ -2132,6 +1831,17 @@ static void bswap_psinfo(struct target_elf_prpsinfo *psinfo)
psinfo->pr_pgrp = tswap32(psinfo->pr_pgrp);
psinfo->pr_sid = tswap32(psinfo->pr_sid);
}
+
+static void bswap_note(struct elf_note *en)
+{
+ bswap32s(&en->n_namesz);
+ bswap32s(&en->n_descsz);
+ bswap32s(&en->n_type);
+}
+#else
+static inline void bswap_prstatus(struct target_elf_prstatus *p) { }
+static inline void bswap_psinfo(struct target_elf_prpsinfo *p) {}
+static inline void bswap_note(struct elf_note *en) { }
#endif /* BSWAP_NEEDED */
/*
@@ -2166,7 +1876,7 @@ static void vma_delete(struct mm_struct *mm)
}
static int vma_add_mapping(struct mm_struct *mm, abi_ulong start,
- abi_ulong end, abi_ulong flags)
+ abi_ulong end, abi_ulong flags)
{
struct vm_area_struct *vma;
@@ -2235,7 +1945,7 @@ static abi_ulong vma_dump_size(const struct vm_area_struct *vma)
}
static int vma_walker(void *priv, abi_ulong start, abi_ulong end,
- unsigned long flags)
+ unsigned long flags)
{
struct mm_struct *mm = (struct mm_struct *)priv;
@@ -2244,7 +1954,7 @@ static int vma_walker(void *priv, abi_ulong start, abi_ulong end,
}
static void fill_note(struct memelfnote *note, const char *name, int type,
- unsigned int sz, void *data)
+ unsigned int sz, void *data)
{
unsigned int namesz;
@@ -2265,7 +1975,7 @@ static void fill_note(struct memelfnote *note, const char *name, int type,
}
static void fill_elf_header(struct elfhdr *elf, int segs, uint16_t machine,
- uint32_t flags)
+ uint32_t flags)
{
(void) memset(elf, 0, sizeof(*elf));
@@ -2284,9 +1994,7 @@ static void fill_elf_header(struct elfhdr *elf, int segs, uint16_t machine,
elf->e_phentsize = sizeof(struct elf_phdr);
elf->e_phnum = segs;
-#ifdef BSWAP_NEEDED
bswap_ehdr(elf);
-#endif
}
static void fill_elf_note_phdr(struct elf_phdr *phdr, int sz, off_t offset)
@@ -2300,9 +2008,7 @@ static void fill_elf_note_phdr(struct elf_phdr *phdr, int sz, off_t offset)
phdr->p_flags = 0;
phdr->p_align = 0;
-#ifdef BSWAP_NEEDED
- bswap_phdr(phdr);
-#endif
+ bswap_phdr(phdr, 1);
}
static size_t note_size(const struct memelfnote *note)
@@ -2311,7 +2017,7 @@ static size_t note_size(const struct memelfnote *note)
}
static void fill_prstatus(struct target_elf_prstatus *prstatus,
- const TaskState *ts, int signr)
+ const TaskState *ts, int signr)
{
(void) memset(prstatus, 0, sizeof (*prstatus));
prstatus->pr_info.si_signo = prstatus->pr_cursig = signr;
@@ -2320,9 +2026,7 @@ static void fill_prstatus(struct target_elf_prstatus *prstatus,
prstatus->pr_pgrp = getpgrp();
prstatus->pr_sid = getsid(0);
-#ifdef BSWAP_NEEDED
bswap_prstatus(prstatus);
-#endif
}
static int fill_psinfo(struct target_elf_prpsinfo *psinfo, const TaskState *ts)
@@ -2352,13 +2056,11 @@ static int fill_psinfo(struct target_elf_prpsinfo *psinfo, const TaskState *ts)
filename = strdup(ts->bprm->filename);
base_filename = strdup(basename(filename));
(void) strncpy(psinfo->pr_fname, base_filename,
- sizeof(psinfo->pr_fname));
+ sizeof(psinfo->pr_fname));
free(base_filename);
free(filename);
-#ifdef BSWAP_NEEDED
bswap_psinfo(psinfo);
-#endif
return (0);
}
@@ -2401,7 +2103,7 @@ static void fill_auxv_note(struct memelfnote *note, const TaskState *ts)
* Returns 0 in case of success, -1 otherwise (errno is set).
*/
static int core_dump_filename(const TaskState *ts, char *buf,
- size_t bufsize)
+ size_t bufsize)
{
char timestamp[64];
char *filename = NULL;
@@ -2413,16 +2115,16 @@ static int core_dump_filename(const TaskState *ts, char *buf,
if (gettimeofday(&tv, NULL) < 0) {
(void) fprintf(stderr, "unable to get current timestamp: %s",
- strerror(errno));
+ strerror(errno));
return (-1);
}
filename = strdup(ts->bprm->filename);
base_filename = strdup(basename(filename));
(void) strftime(timestamp, sizeof (timestamp), "%Y%m%d-%H%M%S",
- localtime_r(&tv.tv_sec, &tm));
+ localtime_r(&tv.tv_sec, &tm));
(void) snprintf(buf, bufsize, "qemu_%s_%s_%d.core",
- base_filename, timestamp, (int)getpid());
+ base_filename, timestamp, (int)getpid());
free(base_filename);
free(filename);
@@ -2483,9 +2185,7 @@ static int write_note(struct memelfnote *men, int fd)
en.n_type = men->type;
en.n_descsz = men->datasz;
-#ifdef BSWAP_NEEDED
bswap_note(&en);
-#endif
if (dump_write(fd, &en, sizeof(en)) != 0)
return (-1);
@@ -2507,7 +2207,7 @@ static void fill_thread_info(struct elf_note_info *info, const CPUState *env)
fill_prstatus(&ets->prstatus, ts, 0);
elf_core_copy_regs(&ets->prstatus.pr_reg, env);
fill_note(&ets->notes[0], "CORE", NT_PRSTATUS, sizeof (ets->prstatus),
- &ets->prstatus);
+ &ets->prstatus);
QTAILQ_INSERT_TAIL(&info->thread_list, ets, ets_link);
@@ -2515,7 +2215,7 @@ static void fill_thread_info(struct elf_note_info *info, const CPUState *env)
}
static int fill_note_info(struct elf_note_info *info,
- long signr, const CPUState *env)
+ long signr, const CPUState *env)
{
#define NUMNOTES 3
CPUState *cpu = NULL;
@@ -2543,10 +2243,10 @@ static int fill_note_info(struct elf_note_info *info,
fill_prstatus(info->prstatus, ts, signr);
elf_core_copy_regs(&info->prstatus->pr_reg, env);
fill_note(&info->notes[0], "CORE", NT_PRSTATUS,
- sizeof (*info->prstatus), info->prstatus);
+ sizeof (*info->prstatus), info->prstatus);
fill_psinfo(info->psinfo, ts);
fill_note(&info->notes[1], "CORE", NT_PRPSINFO,
- sizeof (*info->psinfo), info->psinfo);
+ sizeof (*info->psinfo), info->psinfo);
fill_auxv_note(&info->notes[2], ts);
info->numnote = 3;
@@ -2593,7 +2293,7 @@ static int write_note_info(struct elf_note_info *info, int fd)
/* write prstatus for each thread */
for (ets = info->thread_list.tqh_first; ets != NULL;
- ets = ets->ets_link.tqe_next) {
+ ets = ets->ets_link.tqe_next) {
if ((error = write_note(&ets->notes[0], fd)) != 0)
return (error);
}
@@ -2661,13 +2361,13 @@ static int elf_core_dump(int signr, const CPUState *env)
errno = 0;
getrlimit(RLIMIT_CORE, &dumpsize);
if (dumpsize.rlim_cur == 0)
- return 0;
+ return 0;
if (core_dump_filename(ts, corefile, sizeof (corefile)) < 0)
return (-errno);
if ((fd = open(corefile, O_WRONLY | O_CREAT,
- S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) < 0)
+ S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) < 0)
return (-errno);
/*
@@ -2756,7 +2456,7 @@ static int elf_core_dump(int signr, const CPUState *env)
end = vma->vma_start + vma_dump_size(vma);
for (addr = vma->vma_start; addr < end;
- addr += TARGET_PAGE_SIZE) {
+ addr += TARGET_PAGE_SIZE) {
char page[TARGET_PAGE_SIZE];
int error;
@@ -2767,7 +2467,7 @@ static int elf_core_dump(int signr, const CPUState *env)
error = copy_from_user(page, addr, sizeof (page));
if (error != 0) {
(void) fprintf(stderr, "unable to dump " TARGET_ABI_FMT_lx "\n",
- addr);
+ addr);
errno = -error;
goto out;
}
@@ -2776,7 +2476,7 @@ static int elf_core_dump(int signr, const CPUState *env)
}
}
-out:
+ out:
free_note_info(&info);
if (mm != NULL)
vma_delete(mm);
@@ -2786,15 +2486,8 @@ out:
return (-errno);
return (0);
}
-
#endif /* USE_ELF_CORE_DUMP */
-static int load_aout_interp(void * exptr, int interp_fd)
-{
- printf("a.out interpreter not yet supported\n");
- return(0);
-}
-
void do_init_thread(struct target_pt_regs *regs, struct image_info *infop)
{
init_thread(regs, infop);
diff --git a/linux-user/flatload.c b/linux-user/flatload.c
index 8ad130a2bd..8f9f4a5fcc 100644
--- a/linux-user/flatload.c
+++ b/linux-user/flatload.c
@@ -383,7 +383,8 @@ static int load_flat_file(struct linux_binprm * bprm,
struct lib_info *libinfo, int id, abi_ulong *extra_stack)
{
struct flat_hdr * hdr;
- abi_ulong textpos = 0, datapos = 0, result;
+ abi_ulong textpos = 0, datapos = 0;
+ abi_long result;
abi_ulong realdatastart = 0;
abi_ulong text_len, data_len, bss_len, stack_len, flags;
abi_ulong memp = 0; /* for finding the brk area */
diff --git a/linux-user/linuxload.c b/linux-user/linuxload.c
index 13ad9aaebe..9ee27c3558 100644
--- a/linux-user/linuxload.c
+++ b/linux-user/linuxload.c
@@ -96,18 +96,16 @@ static int prepare_binprm(struct linux_binprm *bprm)
}
}
- retval = lseek(bprm->fd, 0L, SEEK_SET);
- if(retval >= 0) {
- retval = read(bprm->fd, bprm->buf, 128);
- }
- if(retval < 0) {
+ retval = read(bprm->fd, bprm->buf, BPRM_BUF_SIZE);
+ if (retval < 0) {
perror("prepare_binprm");
exit(-1);
- /* return(-errno); */
}
- else {
- return(retval);
+ if (retval < BPRM_BUF_SIZE) {
+ /* Make sure the rest of the loader won't read garbage. */
+ memset(bprm->buf + retval, 0, BPRM_BUF_SIZE - retval);
}
+ return retval;
}
/* Construct the envp and argv tables on the target stack. */
@@ -163,8 +161,7 @@ int loader_exec(const char * filename, char ** argv, char ** envp,
int i;
bprm->p = TARGET_PAGE_SIZE*MAX_ARG_PAGES-sizeof(unsigned int);
- for (i=0 ; i<MAX_ARG_PAGES ; i++) /* clear page-table */
- bprm->page[i] = NULL;
+ memset(bprm->page, 0, sizeof(bprm->page));
retval = open(filename, O_RDONLY);
if (retval < 0)
return retval;
diff --git a/linux-user/m68k-sim.c b/linux-user/m68k-sim.c
index 64d3b23d68..d5926eec4b 100644
--- a/linux-user/m68k-sim.c
+++ b/linux-user/m68k-sim.c
@@ -38,7 +38,7 @@
#define SYS_ISATTY 29
#define SYS_LSEEK 199
-struct m86k_sim_stat {
+struct m68k_sim_stat {
uint16_t sim_st_dev;
uint16_t sim_st_ino;
uint32_t sim_st_mode;
@@ -138,10 +138,10 @@ void do_m68k_simcall(CPUM68KState *env, int nr)
{
struct stat s;
int rc;
- struct m86k_sim_stat *p;
+ struct m68k_sim_stat *p;
rc = check_err(env, fstat(ARG(0), &s));
if (rc == 0) {
- p = (struct m86k_sim_stat *)(unsigned long)ARG(1);
+ p = (struct m68k_sim_stat *)(unsigned long)ARG(1);
p->sim_st_dev = tswap16(s.st_dev);
p->sim_st_ino = tswap16(s.st_ino);
p->sim_st_mode = tswap32(s.st_mode);
diff --git a/linux-user/main.c b/linux-user/main.c
index 403c8d3b96..dbba8befe7 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -2233,6 +2233,37 @@ void cpu_loop (CPUState *env)
env->regs[3] = ret;
env->sregs[SR_PC] = env->regs[14];
break;
+ case EXCP_HW_EXCP:
+ env->regs[17] = env->sregs[SR_PC] + 4;
+ if (env->iflags & D_FLAG) {
+ env->sregs[SR_ESR] |= 1 << 12;
+ env->sregs[SR_PC] -= 4;
+ /* FIXME: if branch was immed, replay the imm aswell. */
+ }
+
+ env->iflags &= ~(IMM_FLAG | D_FLAG);
+
+ switch (env->sregs[SR_ESR] & 31) {
+ case ESR_EC_FPU:
+ info.si_signo = SIGFPE;
+ info.si_errno = 0;
+ if (env->sregs[SR_FSR] & FSR_IO) {
+ info.si_code = TARGET_FPE_FLTINV;
+ }
+ if (env->sregs[SR_FSR] & FSR_DZ) {
+ info.si_code = TARGET_FPE_FLTDIV;
+ }
+ info._sifields._sigfault._addr = 0;
+ queue_signal(env, info.si_signo, &info);
+ break;
+ default:
+ printf ("Unhandled hw-exception: 0x%x\n",
+ env->sregs[SR_ESR] & 5);
+ cpu_dump_state(env, stderr, fprintf, 0);
+ exit (1);
+ break;
+ }
+ break;
case EXCP_DEBUG:
{
int sig;
@@ -2759,6 +2790,12 @@ int main(int argc, char **argv, char **envp)
r = argv[optind++];
if (envlist_setenv(envlist, r) != 0)
usage();
+ } else if (!strcmp(r, "ignore-environment")) {
+ envlist_free(envlist);
+ if ((envlist = envlist_create()) == NULL) {
+ (void) fprintf(stderr, "Unable to allocate envlist\n");
+ exit(1);
+ }
} else if (!strcmp(r, "U")) {
r = argv[optind++];
if (envlist_unsetenv(envlist, r) != 0)
@@ -2800,6 +2837,8 @@ int main(int argc, char **argv, char **envp)
/* XXX: implement xxx_cpu_list for targets that still miss it */
#if defined(cpu_list_id)
cpu_list_id(stdout, &fprintf, "");
+#elif defined(cpu_list)
+ cpu_list(stdout, &fprintf); /* deprecated */
#endif
exit(1);
}
diff --git a/linux-user/mmap.c b/linux-user/mmap.c
index 39da6dfb40..035dfbd076 100644
--- a/linux-user/mmap.c
+++ b/linux-user/mmap.c
@@ -225,13 +225,13 @@ static abi_ulong mmap_find_vma_reserved(abi_ulong start, abi_ulong size)
int prot;
int looped = 0;
- if (size > reserved_va) {
+ if (size > RESERVED_VA) {
return (abi_ulong)-1;
}
last_addr = start;
for (addr = start; last_addr + size != addr; addr += qemu_host_page_size) {
- if (last_addr + size >= reserved_va
+ if (last_addr + size >= RESERVED_VA
|| (abi_ulong)(last_addr + size) < last_addr) {
if (looped) {
return (abi_ulong)-1;
@@ -271,7 +271,7 @@ abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size)
size = HOST_PAGE_ALIGN(size);
- if (reserved_va) {
+ if (RESERVED_VA) {
return mmap_find_vma_reserved(start, size);
}
@@ -342,7 +342,7 @@ abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size)
munmap(ptr, size);
/* ENOMEM if we checked the whole of the target address space. */
- if (addr == -1ul) {
+ if (addr == (abi_ulong)-1) {
return (abi_ulong)-1;
} else if (addr == 0) {
if (wrapped) {
@@ -651,7 +651,7 @@ int target_munmap(abi_ulong start, abi_ulong len)
ret = 0;
/* unmap what we can */
if (real_start < real_end) {
- if (reserved_va) {
+ if (RESERVED_VA) {
mmap_reserve(real_start, real_end - real_start);
} else {
ret = munmap(g2h(real_start), real_end - real_start);
@@ -679,7 +679,7 @@ abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size,
flags,
g2h(new_addr));
- if (reserved_va && host_addr != MAP_FAILED) {
+ if (RESERVED_VA && host_addr != MAP_FAILED) {
/* If new and old addresses overlap then the above mremap will
already have failed with EINVAL. */
mmap_reserve(old_addr, old_size);
@@ -701,7 +701,7 @@ abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size,
}
} else {
int prot = 0;
- if (reserved_va && old_size < new_size) {
+ if (RESERVED_VA && old_size < new_size) {
abi_ulong addr;
for (addr = old_addr + old_size;
addr < old_addr + new_size;
@@ -711,7 +711,7 @@ abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size,
}
if (prot == 0) {
host_addr = mremap(g2h(old_addr), old_size, new_size, flags);
- if (host_addr != MAP_FAILED && reserved_va && old_size > new_size) {
+ if (host_addr != MAP_FAILED && RESERVED_VA && old_size > new_size) {
mmap_reserve(old_addr + old_size, new_size - old_size);
}
} else {
diff --git a/linux-user/qemu.h b/linux-user/qemu.h
index 1878d5a61e..708021e006 100644
--- a/linux-user/qemu.h
+++ b/linux-user/qemu.h
@@ -31,6 +31,7 @@
* task_struct fields in the kernel
*/
struct image_info {
+ abi_ulong load_bias;
abi_ulong load_addr;
abi_ulong start_code;
abi_ulong end_code;
@@ -144,12 +145,16 @@ extern unsigned long mmap_min_addr;
*/
#define MAX_ARG_PAGES 33
+/* Read a good amount of data initially, to hopefully get all the
+ program headers loaded. */
+#define BPRM_BUF_SIZE 1024
+
/*
* This structure is used to hold the arguments that are
* used when loading binaries.
*/
struct linux_binprm {
- char buf[128];
+ char buf[BPRM_BUF_SIZE] __attribute__((aligned));
void *page[MAX_ARG_PAGES];
abi_ulong p;
int fd;
@@ -181,7 +186,7 @@ void syscall_init(void);
abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
abi_long arg2, abi_long arg3, abi_long arg4,
abi_long arg5, abi_long arg6);
-void gemu_log(const char *fmt, ...) __attribute__((format(printf,1,2)));
+void gemu_log(const char *fmt, ...) GCC_FMT_ATTR(1, 2);
extern THREAD CPUState *thread_env;
void cpu_loop(CPUState *env);
char *target_strerror(int err);
diff --git a/linux-user/signal.c b/linux-user/signal.c
index e5a187e106..77683f7534 100644
--- a/linux-user/signal.c
+++ b/linux-user/signal.c
@@ -3064,9 +3064,23 @@ struct target_sigcontext {
uint32_t oldmask;
};
+struct target_stack_t {
+ abi_ulong ss_sp;
+ int ss_flags;
+ unsigned int ss_size;
+};
+
+struct target_ucontext {
+ abi_ulong uc_flags;
+ abi_ulong uc_link;
+ struct target_stack_t uc_stack;
+ struct target_sigcontext sc;
+ uint32_t extramask[TARGET_NSIG_WORDS - 1];
+};
+
/* Signal frames. */
struct target_signal_frame {
- struct target_sigcontext sc;
+ struct target_ucontext uc;
uint32_t extramask[TARGET_NSIG_WORDS - 1];
uint32_t tramp[2];
};
@@ -3175,7 +3189,7 @@ static void setup_frame(int sig, struct target_sigaction *ka,
goto badframe;
/* Save the mask. */
- err |= __put_user(set->sig[0], &frame->sc.oldmask);
+ err |= __put_user(set->sig[0], &frame->uc.sc.oldmask);
if (err)
goto badframe;
@@ -3184,7 +3198,7 @@ static void setup_frame(int sig, struct target_sigaction *ka,
goto badframe;
}
- setup_sigcontext(&frame->sc, env);
+ setup_sigcontext(&frame->uc.sc, env);
/* Set up to return from userspace. If provided, use a stub
already in userspace. */
@@ -3213,7 +3227,8 @@ static void setup_frame(int sig, struct target_sigaction *ka,
env->regs[1] = (unsigned long) frame;
/* Signal handler args: */
env->regs[5] = sig; /* Arg 0: signum */
- env->regs[6] = (unsigned long) &frame->sc; /* arg 1: sigcontext */
+ env->regs[6] = 0;
+ env->regs[7] = (unsigned long) &frame->uc; /* arg 1: sigcontext */
/* Offset of 4 to handle microblaze rtid r14, 0 */
env->sregs[SR_PC] = (unsigned long)ka->_sa_handler;
@@ -3246,7 +3261,7 @@ long do_sigreturn(CPUState *env)
goto badframe;
/* Restore blocked signals */
- if (__get_user(target_set.sig[0], &frame->sc.oldmask))
+ if (__get_user(target_set.sig[0], &frame->uc.sc.oldmask))
goto badframe;
for(i = 1; i < TARGET_NSIG_WORDS; i++) {
if (__get_user(target_set.sig[i], &frame->extramask[i - 1]))
@@ -3255,7 +3270,7 @@ long do_sigreturn(CPUState *env)
target_to_host_sigset_internal(&set, &target_set);
sigprocmask(SIG_SETMASK, &set, NULL);
- restore_sigcontext(&frame->sc, env);
+ restore_sigcontext(&frame->uc.sc, env);
/* We got here through a sigreturn syscall, our path back is via an
rtb insn so setup r14 for that. */
env->regs[14] = env->sregs[SR_PC];
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index 0ebe7e1c26..d44f512ed3 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -1551,8 +1551,9 @@ static abi_long do_bind(int sockfd, abi_ulong target_addr,
void *addr;
abi_long ret;
- if (addrlen < 0)
+ if ((int)addrlen < 0) {
return -TARGET_EINVAL;
+ }
addr = alloca(addrlen+1);
@@ -1570,8 +1571,9 @@ static abi_long do_connect(int sockfd, abi_ulong target_addr,
void *addr;
abi_long ret;
- if (addrlen < 0)
+ if ((int)addrlen < 0) {
return -TARGET_EINVAL;
+ }
addr = alloca(addrlen);
@@ -1656,8 +1658,9 @@ static abi_long do_accept(int fd, abi_ulong target_addr,
if (get_user_u32(addrlen, target_addrlen_addr))
return -TARGET_EINVAL;
- if (addrlen < 0)
+ if ((int)addrlen < 0) {
return -TARGET_EINVAL;
+ }
if (!access_ok(VERIFY_WRITE, target_addr, addrlen))
return -TARGET_EINVAL;
@@ -1684,8 +1687,9 @@ static abi_long do_getpeername(int fd, abi_ulong target_addr,
if (get_user_u32(addrlen, target_addrlen_addr))
return -TARGET_EFAULT;
- if (addrlen < 0)
+ if ((int)addrlen < 0) {
return -TARGET_EINVAL;
+ }
if (!access_ok(VERIFY_WRITE, target_addr, addrlen))
return -TARGET_EFAULT;
@@ -1712,8 +1716,9 @@ static abi_long do_getsockname(int fd, abi_ulong target_addr,
if (get_user_u32(addrlen, target_addrlen_addr))
return -TARGET_EFAULT;
- if (addrlen < 0)
+ if ((int)addrlen < 0) {
return -TARGET_EINVAL;
+ }
if (!access_ok(VERIFY_WRITE, target_addr, addrlen))
return -TARGET_EFAULT;
@@ -1753,8 +1758,9 @@ static abi_long do_sendto(int fd, abi_ulong msg, size_t len, int flags,
void *host_msg;
abi_long ret;
- if (addrlen < 0)
+ if ((int)addrlen < 0) {
return -TARGET_EINVAL;
+ }
host_msg = lock_user(VERIFY_READ, msg, len, 1);
if (!host_msg)
@@ -1792,7 +1798,7 @@ static abi_long do_recvfrom(int fd, abi_ulong msg, size_t len, int flags,
ret = -TARGET_EFAULT;
goto fail;
}
- if (addrlen < 0) {
+ if ((int)addrlen < 0) {
ret = -TARGET_EINVAL;
goto fail;
}
diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h
index 46cb05ea9f..6c57e2481a 100644
--- a/linux-user/syscall_defs.h
+++ b/linux-user/syscall_defs.h
@@ -1282,7 +1282,10 @@ struct target_stat {
/* FIXME: Microblaze no-mmu user-space has a difference stat64 layout... */
struct __attribute__((__packed__)) target_stat64 {
uint64_t st_dev;
- uint64_t st_ino;
+#define TARGET_STAT64_HAS_BROKEN_ST_INO 1
+ uint32_t pad0;
+ uint32_t __st_ino;
+
uint32_t st_mode;
uint32_t st_nlink;
uint32_t st_uid;
@@ -1296,13 +1299,12 @@ struct __attribute__((__packed__)) target_stat64 {
int64_t st_blocks; /* Number 512-byte blocks allocated. */
int target_st_atime;
- unsigned int target_st_atime_nsec;
+ unsigned int target_st_atime_nsec;
int target_st_mtime;
- unsigned int target_st_mtime_nsec;
+ unsigned int target_st_mtime_nsec;
int target_st_ctime;
- unsigned int target_st_ctime_nsec;
- uint32_t __unused4;
- uint32_t __unused5;
+ unsigned int target_st_ctime_nsec;
+ uint64_t st_ino;
};
#elif defined(TARGET_M68K)
diff --git a/migration-tcp.c b/migration-tcp.c
index 78b56dc3f6..b55f419b65 100644
--- a/migration-tcp.c
+++ b/migration-tcp.c
@@ -151,7 +151,7 @@ static void tcp_accept_incoming_migration(void *opaque)
if (c == -1) {
fprintf(stderr, "could not accept migration connection\n");
- return;
+ goto out2;
}
f = qemu_fopen_socket(c);
@@ -163,9 +163,10 @@ static void tcp_accept_incoming_migration(void *opaque)
process_incoming_migration(f);
qemu_fclose(f);
out:
+ close(c);
+out2:
qemu_set_fd_handler2(s, NULL, NULL, NULL, NULL);
close(s);
- close(c);
}
int tcp_start_incoming_migration(const char *host_port)
diff --git a/migration.c b/migration.c
index 650eb78d26..468d51749f 100644
--- a/migration.c
+++ b/migration.c
@@ -67,6 +67,8 @@ void process_incoming_migration(QEMUFile *f)
qemu_announce_self();
DPRINTF("successfully loaded vm state\n");
+ incoming_expected = false;
+
if (autostart)
vm_start();
}
@@ -314,8 +316,14 @@ ssize_t migrate_fd_put_buffer(void *opaque, const void *data, size_t size)
if (ret == -1)
ret = -(s->get_error(s));
- if (ret == -EAGAIN)
+ if (ret == -EAGAIN) {
qemu_set_fd_handler2(s->fd, NULL, NULL, migrate_fd_put_notify, s);
+ } else if (ret < 0) {
+ if (s->mon) {
+ monitor_resume(s->mon);
+ }
+ s->state = MIG_STATE_ERROR;
+ }
return ret;
}
diff --git a/monitor.c b/monitor.c
index 45fd48291a..260cc02963 100644
--- a/monitor.c
+++ b/monitor.c
@@ -56,6 +56,9 @@
#include "json-parser.h"
#include "osdep.h"
#include "exec-all.h"
+#ifdef CONFIG_SIMPLE_TRACE
+#include "trace.h"
+#endif
//#define DEBUG
//#define DEBUG_COMPLETION
@@ -186,6 +189,9 @@ static QLIST_HEAD(mon_list, Monitor) mon_list;
static const mon_cmd_t mon_cmds[];
static const mon_cmd_t info_cmds[];
+static const mon_cmd_t qmp_cmds[];
+static const mon_cmd_t qmp_query_cmds[];
+
Monitor *cur_mon;
Monitor *default_mon;
@@ -313,7 +319,8 @@ void monitor_print_filename(Monitor *mon, const char *filename)
}
}
-static int monitor_fprintf(FILE *stream, const char *fmt, ...)
+static int GCC_FMT_ATTR(2, 3) monitor_fprintf(FILE *stream,
+ const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
@@ -324,21 +331,16 @@ static int monitor_fprintf(FILE *stream, const char *fmt, ...)
static void monitor_user_noop(Monitor *mon, const QObject *data) { }
-static inline int monitor_handler_ported(const mon_cmd_t *cmd)
+static inline int handler_is_qobject(const mon_cmd_t *cmd)
{
return cmd->user_print != NULL;
}
-static inline bool monitor_handler_is_async(const mon_cmd_t *cmd)
+static inline bool handler_is_async(const mon_cmd_t *cmd)
{
return cmd->flags & MONITOR_CMD_ASYNC;
}
-static inline bool monitor_cmd_user_only(const mon_cmd_t *cmd)
-{
- return (cmd->flags & MONITOR_CMD_USER_ONLY);
-}
-
static inline int monitor_has_error(const Monitor *mon)
{
return mon->error != NULL;
@@ -348,7 +350,10 @@ static void monitor_json_emitter(Monitor *mon, const QObject *data)
{
QString *json;
- json = qobject_to_json(data);
+ if (mon->flags & MONITOR_USE_PRETTY)
+ json = qobject_to_json_pretty(data);
+ else
+ json = qobject_to_json(data);
assert(json != NULL);
qstring_append_chr(json, '\n');
@@ -539,6 +544,42 @@ static void do_help_cmd(Monitor *mon, const QDict *qdict)
help_cmd(mon, qdict_get_try_str(qdict, "name"));
}
+#ifdef CONFIG_SIMPLE_TRACE
+static void do_change_trace_event_state(Monitor *mon, const QDict *qdict)
+{
+ const char *tp_name = qdict_get_str(qdict, "name");
+ bool new_state = qdict_get_bool(qdict, "option");
+ int ret = st_change_trace_event_state(tp_name, new_state);
+
+ if (!ret) {
+ monitor_printf(mon, "unknown event name \"%s\"\n", tp_name);
+ }
+}
+
+static void do_trace_file(Monitor *mon, const QDict *qdict)
+{
+ const char *op = qdict_get_try_str(qdict, "op");
+ const char *arg = qdict_get_try_str(qdict, "arg");
+
+ if (!op) {
+ st_print_trace_file_status((FILE *)mon, &monitor_fprintf);
+ } else if (!strcmp(op, "on")) {
+ st_set_trace_file_enabled(true);
+ } else if (!strcmp(op, "off")) {
+ st_set_trace_file_enabled(false);
+ } else if (!strcmp(op, "flush")) {
+ st_flush_trace_buffer();
+ } else if (!strcmp(op, "set")) {
+ if (arg) {
+ st_set_trace_file(arg);
+ }
+ } else {
+ monitor_printf(mon, "unexpected argument \"%s\"\n", op);
+ help_cmd(mon, "trace-file");
+ }
+}
+#endif
+
static void user_monitor_complete(void *opaque, QObject *ret_data)
{
MonitorCompletionData *data = (MonitorCompletionData *)opaque;
@@ -598,13 +639,12 @@ static void user_async_info_handler(Monitor *mon, const mon_cmd_t *cmd)
}
}
-static int do_info(Monitor *mon, const QDict *qdict, QObject **ret_data)
+static void do_info(Monitor *mon, const QDict *qdict)
{
const mon_cmd_t *cmd;
const char *item = qdict_get_try_str(qdict, "item");
if (!item) {
- assert(monitor_ctrl_mode(mon) == 0);
goto help;
}
@@ -614,72 +654,58 @@ static int do_info(Monitor *mon, const QDict *qdict, QObject **ret_data)
}
if (cmd->name == NULL) {
- if (monitor_ctrl_mode(mon)) {
- qerror_report(QERR_COMMAND_NOT_FOUND, item);
- return -1;
- }
goto help;
}
- if (monitor_ctrl_mode(mon) && monitor_cmd_user_only(cmd)) {
- qerror_report(QERR_COMMAND_NOT_FOUND, item);
- return -1;
- }
+ if (handler_is_async(cmd)) {
+ user_async_info_handler(mon, cmd);
+ } else if (handler_is_qobject(cmd)) {
+ QObject *info_data = NULL;
- if (monitor_handler_is_async(cmd)) {
- if (monitor_ctrl_mode(mon)) {
- qmp_async_info_handler(mon, cmd);
- } else {
- user_async_info_handler(mon, cmd);
- }
- /*
- * Indicate that this command is asynchronous and will not return any
- * data (not even empty). Instead, the data will be returned via a
- * completion callback.
- */
- *ret_data = qobject_from_jsonf("{ '__mon_async': 'return' }");
- } else if (monitor_handler_ported(cmd)) {
- cmd->mhandler.info_new(mon, ret_data);
-
- if (!monitor_ctrl_mode(mon)) {
- /*
- * User Protocol function is called here, Monitor Protocol is
- * handled by monitor_call_handler()
- */
- if (*ret_data)
- cmd->user_print(mon, *ret_data);
+ cmd->mhandler.info_new(mon, &info_data);
+ if (info_data) {
+ cmd->user_print(mon, info_data);
+ qobject_decref(info_data);
}
} else {
- if (monitor_ctrl_mode(mon)) {
- /* handler not converted yet */
- qerror_report(QERR_COMMAND_NOT_FOUND, item);
- return -1;
- } else {
- cmd->mhandler.info(mon);
- }
+ cmd->mhandler.info(mon);
}
- return 0;
+ return;
help:
help_cmd(mon, "info");
- return 0;
}
static void do_info_version_print(Monitor *mon, const QObject *data)
{
QDict *qdict;
+ QDict *qemu;
qdict = qobject_to_qdict(data);
+ qemu = qdict_get_qdict(qdict, "qemu");
- monitor_printf(mon, "%s%s\n", qdict_get_str(qdict, "qemu"),
- qdict_get_str(qdict, "package"));
+ monitor_printf(mon, "%" PRId64 ".%" PRId64 ".%" PRId64 "%s\n",
+ qdict_get_int(qemu, "major"),
+ qdict_get_int(qemu, "minor"),
+ qdict_get_int(qemu, "micro"),
+ qdict_get_str(qdict, "package"));
}
static void do_info_version(Monitor *mon, QObject **ret_data)
{
- *ret_data = qobject_from_jsonf("{ 'qemu': %s, 'package': %s }",
- QEMU_VERSION, QEMU_PKGVERSION);
+ const char *version = QEMU_VERSION;
+ int major = 0, minor = 0, micro = 0;
+ char *tmp;
+
+ major = strtol(version, &tmp, 10);
+ tmp++;
+ minor = strtol(tmp, &tmp, 10);
+ tmp++;
+ micro = strtol(tmp, &tmp, 10);
+
+ *ret_data = qobject_from_jsonf("{ 'qemu': { 'major': %d, 'minor': %d, \
+ 'micro': %d }, 'package': %s }", major, minor, micro, QEMU_PKGVERSION);
}
static void do_info_name_print(Monitor *mon, const QObject *data)
@@ -722,19 +748,14 @@ static void do_info_commands(Monitor *mon, QObject **ret_data)
cmd_list = qlist_new();
- for (cmd = mon_cmds; cmd->name != NULL; cmd++) {
- if (monitor_handler_ported(cmd) && !monitor_cmd_user_only(cmd) &&
- !compare_cmd(cmd->name, "info")) {
- qlist_append_obj(cmd_list, get_cmd_dict(cmd->name));
- }
+ for (cmd = qmp_cmds; cmd->name != NULL; cmd++) {
+ qlist_append_obj(cmd_list, get_cmd_dict(cmd->name));
}
- for (cmd = info_cmds; cmd->name != NULL; cmd++) {
- if (monitor_handler_ported(cmd) && !monitor_cmd_user_only(cmd)) {
- char buf[128];
- snprintf(buf, sizeof(buf), "query-%s", cmd->name);
- qlist_append_obj(cmd_list, get_cmd_dict(buf));
- }
+ for (cmd = qmp_query_cmds; cmd->name != NULL; cmd++) {
+ char buf[128];
+ snprintf(buf, sizeof(buf), "query-%s", cmd->name);
+ qlist_append_obj(cmd_list, get_cmd_dict(buf));
}
*ret_data = QOBJECT(cmd_list);
@@ -923,6 +944,18 @@ static void do_info_cpu_stats(Monitor *mon)
}
#endif
+#if defined(CONFIG_SIMPLE_TRACE)
+static void do_info_trace(Monitor *mon)
+{
+ st_print_trace((FILE *)mon, &monitor_fprintf);
+}
+
+static void do_info_trace_events(Monitor *mon)
+{
+ st_print_trace_events((FILE *)mon, &monitor_fprintf);
+}
+#endif
+
/**
* do_quit(): Quit QEMU execution
*/
@@ -1056,6 +1089,10 @@ static int do_cont(Monitor *mon, const QDict *qdict, QObject **ret_data)
{
struct bdrv_iterate_context context = { mon, 0 };
+ if (incoming_expected) {
+ qerror_report(QERR_MIGRATION_EXPECTED);
+ return -1;
+ }
bdrv_iterate(encrypted_bdrv_it, &context);
/* only resume the vm if all keys are set and valid */
if (!context.err) {
@@ -2270,8 +2307,9 @@ static void do_loadvm(Monitor *mon, const QDict *qdict)
vm_stop(0);
- if (load_vmstate(name) >= 0 && saved_vm_running)
+ if (load_vmstate(name) == 0 && saved_vm_running) {
vm_start();
+ }
}
int monitor_get_fd(Monitor *mon, const char *fdname)
@@ -2299,11 +2337,11 @@ int monitor_get_fd(Monitor *mon, const char *fdname)
}
static const mon_cmd_t mon_cmds[] = {
-#include "qemu-monitor.h"
+#include "hmp-commands.h"
{ NULL, NULL, },
};
-/* Please update qemu-monitor.hx when adding or changing commands */
+/* Please update hmp-commands.hx when adding or changing commands */
static const mon_cmd_t info_cmds[] = {
{
.name = "version",
@@ -2314,14 +2352,6 @@ static const mon_cmd_t info_cmds[] = {
.mhandler.info_new = do_info_version,
},
{
- .name = "commands",
- .args_type = "",
- .params = "",
- .help = "list QMP available commands",
- .user_print = monitor_user_noop,
- .mhandler.info_new = do_info_commands,
- },
- {
.name = "network",
.args_type = "",
.params = "",
@@ -2574,11 +2604,157 @@ static const mon_cmd_t info_cmds[] = {
.help = "show roms",
.mhandler.info = do_info_roms,
},
+#if defined(CONFIG_SIMPLE_TRACE)
+ {
+ .name = "trace",
+ .args_type = "",
+ .params = "",
+ .help = "show current contents of trace buffer",
+ .mhandler.info = do_info_trace,
+ },
+ {
+ .name = "trace-events",
+ .args_type = "",
+ .params = "",
+ .help = "show available trace-events & their state",
+ .mhandler.info = do_info_trace_events,
+ },
+#endif
{
.name = NULL,
},
};
+static const mon_cmd_t qmp_cmds[] = {
+#include "qmp-commands.h"
+ { /* NULL */ },
+};
+
+static const mon_cmd_t qmp_query_cmds[] = {
+ {
+ .name = "version",
+ .args_type = "",
+ .params = "",
+ .help = "show the version of QEMU",
+ .user_print = do_info_version_print,
+ .mhandler.info_new = do_info_version,
+ },
+ {
+ .name = "commands",
+ .args_type = "",
+ .params = "",
+ .help = "list QMP available commands",
+ .user_print = monitor_user_noop,
+ .mhandler.info_new = do_info_commands,
+ },
+ {
+ .name = "chardev",
+ .args_type = "",
+ .params = "",
+ .help = "show the character devices",
+ .user_print = qemu_chr_info_print,
+ .mhandler.info_new = qemu_chr_info,
+ },
+ {
+ .name = "block",
+ .args_type = "",
+ .params = "",
+ .help = "show the block devices",
+ .user_print = bdrv_info_print,
+ .mhandler.info_new = bdrv_info,
+ },
+ {
+ .name = "blockstats",
+ .args_type = "",
+ .params = "",
+ .help = "show block device statistics",
+ .user_print = bdrv_stats_print,
+ .mhandler.info_new = bdrv_info_stats,
+ },
+ {
+ .name = "cpus",
+ .args_type = "",
+ .params = "",
+ .help = "show infos for each CPU",
+ .user_print = monitor_print_cpus,
+ .mhandler.info_new = do_info_cpus,
+ },
+ {
+ .name = "pci",
+ .args_type = "",
+ .params = "",
+ .help = "show PCI info",
+ .user_print = do_pci_info_print,
+ .mhandler.info_new = do_pci_info,
+ },
+ {
+ .name = "kvm",
+ .args_type = "",
+ .params = "",
+ .help = "show KVM information",
+ .user_print = do_info_kvm_print,
+ .mhandler.info_new = do_info_kvm,
+ },
+ {
+ .name = "status",
+ .args_type = "",
+ .params = "",
+ .help = "show the current VM status (running|paused)",
+ .user_print = do_info_status_print,
+ .mhandler.info_new = do_info_status,
+ },
+ {
+ .name = "mice",
+ .args_type = "",
+ .params = "",
+ .help = "show which guest mouse is receiving events",
+ .user_print = do_info_mice_print,
+ .mhandler.info_new = do_info_mice,
+ },
+ {
+ .name = "vnc",
+ .args_type = "",
+ .params = "",
+ .help = "show the vnc server status",
+ .user_print = do_info_vnc_print,
+ .mhandler.info_new = do_info_vnc,
+ },
+ {
+ .name = "name",
+ .args_type = "",
+ .params = "",
+ .help = "show the current VM name",
+ .user_print = do_info_name_print,
+ .mhandler.info_new = do_info_name,
+ },
+ {
+ .name = "uuid",
+ .args_type = "",
+ .params = "",
+ .help = "show the current VM UUID",
+ .user_print = do_info_uuid_print,
+ .mhandler.info_new = do_info_uuid,
+ },
+ {
+ .name = "migrate",
+ .args_type = "",
+ .params = "",
+ .help = "show migration status",
+ .user_print = do_info_migrate_print,
+ .mhandler.info_new = do_info_migrate,
+ },
+ {
+ .name = "balloon",
+ .args_type = "",
+ .params = "",
+ .help = "show balloon information",
+ .user_print = monitor_print_balloon,
+ .mhandler.info_async = do_info_balloon,
+ .flags = MONITOR_CMD_ASYNC,
+ },
+ { /* NULL */ },
+};
+
/*******************************************************************/
static const char *pch;
@@ -3285,11 +3461,12 @@ static int is_valid_option(const char *c, const char *typestr)
return (typestr != NULL);
}
-static const mon_cmd_t *monitor_find_command(const char *cmdname)
+static const mon_cmd_t *search_dispatch_table(const mon_cmd_t *disp_table,
+ const char *cmdname)
{
const mon_cmd_t *cmd;
- for (cmd = mon_cmds; cmd->name != NULL; cmd++) {
+ for (cmd = disp_table; cmd->name != NULL; cmd++) {
if (compare_cmd(cmdname, cmd->name)) {
return cmd;
}
@@ -3298,6 +3475,21 @@ static const mon_cmd_t *monitor_find_command(const char *cmdname)
return NULL;
}
+static const mon_cmd_t *monitor_find_command(const char *cmdname)
+{
+ return search_dispatch_table(mon_cmds, cmdname);
+}
+
+static const mon_cmd_t *qmp_find_query_cmd(const char *info_item)
+{
+ return search_dispatch_table(qmp_query_cmds, info_item);
+}
+
+static const mon_cmd_t *qmp_find_cmd(const char *cmdname)
+{
+ return search_dispatch_table(qmp_cmds, cmdname);
+}
+
static const mon_cmd_t *monitor_parse_command(Monitor *mon,
const char *cmdline,
QDict *qdict)
@@ -3646,15 +3838,6 @@ void monitor_set_error(Monitor *mon, QError *qerror)
}
}
-static int is_async_return(const QObject *data)
-{
- if (data && qobject_type(data) == QTYPE_QDICT) {
- return qdict_haskey(qobject_to_qdict(data), "__mon_async");
- }
-
- return 0;
-}
-
static void handler_audit(Monitor *mon, const mon_cmd_t *cmd, int ret)
{
if (monitor_ctrl_mode(mon)) {
@@ -3702,37 +3885,6 @@ static void handler_audit(Monitor *mon, const mon_cmd_t *cmd, int ret)
}
}
-static void monitor_call_handler(Monitor *mon, const mon_cmd_t *cmd,
- const QDict *params)
-{
- int ret;
- QObject *data = NULL;
-
- mon_print_count_init(mon);
-
- ret = cmd->mhandler.cmd_new(mon, params, &data);
- handler_audit(mon, cmd, ret);
-
- if (is_async_return(data)) {
- /*
- * Asynchronous commands have no initial return data but they can
- * generate errors. Data is returned via the async completion handler.
- */
- if (monitor_ctrl_mode(mon) && monitor_has_error(mon)) {
- monitor_protocol_emitter(mon, NULL);
- }
- } else if (monitor_ctrl_mode(mon)) {
- /* Monitor Protocol */
- monitor_protocol_emitter(mon, data);
- } else {
- /* User Protocol */
- if (data)
- cmd->user_print(mon, data);
- }
-
- qobject_decref(data);
-}
-
static void handle_user_command(Monitor *mon, const char *cmdline)
{
QDict *qdict;
@@ -3744,10 +3896,18 @@ static void handle_user_command(Monitor *mon, const char *cmdline)
if (!cmd)
goto out;
- if (monitor_handler_is_async(cmd)) {
+ if (handler_is_async(cmd)) {
user_async_cmd_handler(mon, cmd, qdict);
- } else if (monitor_handler_ported(cmd)) {
- monitor_call_handler(mon, cmd, qdict);
+ } else if (handler_is_qobject(cmd)) {
+ QObject *data = NULL;
+
+ /* XXX: ignores the error code */
+ cmd->mhandler.cmd_new(mon, qdict, &data);
+ assert(!monitor_has_error(mon));
+ if (data) {
+ cmd->user_print(mon, data);
+ qobject_decref(data);
+ }
} else {
cmd->mhandler.cmd(mon, qdict);
}
@@ -4237,6 +4397,38 @@ static QDict *qmp_check_input_obj(QObject *input_obj)
return input_dict;
}
+static void qmp_call_query_cmd(Monitor *mon, const mon_cmd_t *cmd)
+{
+ QObject *ret_data = NULL;
+
+ if (handler_is_async(cmd)) {
+ qmp_async_info_handler(mon, cmd);
+ if (monitor_has_error(mon)) {
+ monitor_protocol_emitter(mon, NULL);
+ }
+ } else {
+ cmd->mhandler.info_new(mon, &ret_data);
+ if (ret_data) {
+ monitor_protocol_emitter(mon, ret_data);
+ qobject_decref(ret_data);
+ }
+ }
+}
+
+static void qmp_call_cmd(Monitor *mon, const mon_cmd_t *cmd,
+ const QDict *params)
+{
+ int ret;
+ QObject *data = NULL;
+
+ mon_print_count_init(mon);
+
+ ret = cmd->mhandler.cmd_new(mon, params, &data);
+ handler_audit(mon, cmd, ret);
+ monitor_protocol_emitter(mon, data);
+ qobject_decref(data);
+}
+
static void handle_qmp_command(JSONMessageParser *parser, QList *tokens)
{
int err;
@@ -4244,8 +4436,9 @@ static void handle_qmp_command(JSONMessageParser *parser, QList *tokens)
QDict *input, *args;
const mon_cmd_t *cmd;
Monitor *mon = cur_mon;
- const char *cmd_name, *info_item;
+ const char *cmd_name, *query_cmd;
+ query_cmd = NULL;
args = input = NULL;
obj = json_parser_parse(tokens, NULL);
@@ -4270,24 +4463,15 @@ static void handle_qmp_command(JSONMessageParser *parser, QList *tokens)
goto err_out;
}
- /*
- * XXX: We need this special case until we get info handlers
- * converted into 'query-' commands
- */
- if (compare_cmd(cmd_name, "info")) {
+ if (strstart(cmd_name, "query-", &query_cmd)) {
+ cmd = qmp_find_query_cmd(query_cmd);
+ } else {
+ cmd = qmp_find_cmd(cmd_name);
+ }
+
+ if (!cmd) {
qerror_report(QERR_COMMAND_NOT_FOUND, cmd_name);
goto err_out;
- } else if (strstart(cmd_name, "query-", &info_item)) {
- cmd = monitor_find_command("info");
- qdict_put_obj(input, "arguments",
- qobject_from_jsonf("{ 'item': %s }", info_item));
- } else {
- cmd = monitor_find_command(cmd_name);
- if (!cmd || !monitor_handler_ported(cmd)
- || monitor_cmd_user_only(cmd)) {
- qerror_report(QERR_COMMAND_NOT_FOUND, cmd_name);
- goto err_out;
- }
}
obj = qdict_get(input, "arguments");
@@ -4303,14 +4487,16 @@ static void handle_qmp_command(JSONMessageParser *parser, QList *tokens)
goto err_out;
}
- if (monitor_handler_is_async(cmd)) {
+ if (query_cmd) {
+ qmp_call_query_cmd(mon, cmd);
+ } else if (handler_is_async(cmd)) {
err = qmp_async_cmd_handler(mon, cmd, args);
if (err) {
/* emit the error response */
goto err_out;
}
} else {
- monitor_call_handler(mon, cmd, args);
+ qmp_call_cmd(mon, cmd, args);
}
goto out;
diff --git a/monitor.h b/monitor.h
index 38b22a4b28..2d36bba87f 100644
--- a/monitor.h
+++ b/monitor.h
@@ -14,10 +14,10 @@ extern Monitor *default_mon;
#define MONITOR_IS_DEFAULT 0x01
#define MONITOR_USE_READLINE 0x02
#define MONITOR_USE_CONTROL 0x04
+#define MONITOR_USE_PRETTY 0x08
/* flags for monitor commands */
#define MONITOR_CMD_ASYNC 0x0001
-#define MONITOR_CMD_USER_ONLY 0x0002
/* QMP events */
typedef enum MonitorEvent {
@@ -49,9 +49,9 @@ int monitor_read_bdrv_key_start(Monitor *mon, BlockDriverState *bs,
int monitor_get_fd(Monitor *mon, const char *fdname);
-void monitor_vprintf(Monitor *mon, const char *fmt, va_list ap);
-void monitor_printf(Monitor *mon, const char *fmt, ...)
- __attribute__ ((__format__ (__printf__, 2, 3)));
+void monitor_vprintf(Monitor *mon, const char *fmt, va_list ap)
+ GCC_FMT_ATTR(2, 0);
+void monitor_printf(Monitor *mon, const char *fmt, ...) GCC_FMT_ATTR(2, 3);
void monitor_print_filename(Monitor *mon, const char *filename);
void monitor_flush(Monitor *mon);
diff --git a/nbd.c b/nbd.c
index a9f295f2bb..d8ebc4298f 100644
--- a/nbd.c
+++ b/nbd.c
@@ -23,7 +23,7 @@
#ifndef _WIN32
#include <sys/ioctl.h>
#endif
-#ifdef __sun__
+#if defined(__sun__) || defined(__HAIKU__)
#include <sys/ioccom.h>
#endif
#include <ctype.h>
@@ -49,6 +49,7 @@
/* This is all part of the "official" NBD API */
+#define NBD_REPLY_SIZE (4 + 4 + 8)
#define NBD_REQUEST_MAGIC 0x25609513
#define NBD_REPLY_MAGIC 0x67446698
@@ -62,6 +63,8 @@
#define NBD_SET_SIZE_BLOCKS _IO(0xab, 7)
#define NBD_DISCONNECT _IO(0xab, 8)
+#define NBD_OPT_EXPORT_NAME (1 << 0)
+
/* That's all folks */
#define read_sync(fd, buffer, size) nbd_wr_sync(fd, buffer, size, true)
@@ -296,22 +299,27 @@ int nbd_negotiate(int csock, off_t size)
return 0;
}
-int nbd_receive_negotiate(int csock, off_t *size, size_t *blocksize)
+int nbd_receive_negotiate(int csock, const char *name, uint32_t *flags,
+ off_t *size, size_t *blocksize)
{
- char buf[8 + 8 + 8 + 128];
- uint64_t magic;
+ char buf[256];
+ uint64_t magic, s;
+ uint16_t tmp;
TRACE("Receiving negotation.");
- if (read_sync(csock, buf, sizeof(buf)) != sizeof(buf)) {
+ if (read_sync(csock, buf, 8) != 8) {
LOG("read failed");
errno = EINVAL;
return -1;
}
- magic = be64_to_cpup((uint64_t*)(buf + 8));
- *size = be64_to_cpup((uint64_t*)(buf + 16));
- *blocksize = 1024;
+ buf[8] = '\0';
+ if (strlen(buf) == 0) {
+ LOG("server connection closed");
+ errno = EINVAL;
+ return -1;
+ }
TRACE("Magic is %c%c%c%c%c%c%c%c",
qemu_isprint(buf[0]) ? buf[0] : '.',
@@ -322,8 +330,6 @@ int nbd_receive_negotiate(int csock, off_t *size, size_t *blocksize)
qemu_isprint(buf[5]) ? buf[5] : '.',
qemu_isprint(buf[6]) ? buf[6] : '.',
qemu_isprint(buf[7]) ? buf[7] : '.');
- TRACE("Magic is 0x%" PRIx64, magic);
- TRACE("Size is %" PRIu64, *size);
if (memcmp(buf, "NBDMAGIC", 8) != 0) {
LOG("Invalid magic received");
@@ -331,10 +337,99 @@ int nbd_receive_negotiate(int csock, off_t *size, size_t *blocksize)
return -1;
}
- TRACE("Checking magic");
+ if (read_sync(csock, &magic, sizeof(magic)) != sizeof(magic)) {
+ LOG("read failed");
+ errno = EINVAL;
+ return -1;
+ }
+ magic = be64_to_cpu(magic);
+ TRACE("Magic is 0x%" PRIx64, magic);
+
+ if (name) {
+ uint32_t reserved = 0;
+ uint32_t opt;
+ uint32_t namesize;
+
+ TRACE("Checking magic (opts_magic)");
+ if (magic != 0x49484156454F5054LL) {
+ LOG("Bad magic received");
+ errno = EINVAL;
+ return -1;
+ }
+ if (read_sync(csock, &tmp, sizeof(tmp)) != sizeof(tmp)) {
+ LOG("flags read failed");
+ errno = EINVAL;
+ return -1;
+ }
+ *flags = be16_to_cpu(tmp) << 16;
+ /* reserved for future use */
+ if (write_sync(csock, &reserved, sizeof(reserved)) !=
+ sizeof(reserved)) {
+ LOG("write failed (reserved)");
+ errno = EINVAL;
+ return -1;
+ }
+ /* write the export name */
+ magic = cpu_to_be64(magic);
+ if (write_sync(csock, &magic, sizeof(magic)) != sizeof(magic)) {
+ LOG("write failed (magic)");
+ errno = EINVAL;
+ return -1;
+ }
+ opt = cpu_to_be32(NBD_OPT_EXPORT_NAME);
+ if (write_sync(csock, &opt, sizeof(opt)) != sizeof(opt)) {
+ LOG("write failed (opt)");
+ errno = EINVAL;
+ return -1;
+ }
+ namesize = cpu_to_be32(strlen(name));
+ if (write_sync(csock, &namesize, sizeof(namesize)) !=
+ sizeof(namesize)) {
+ LOG("write failed (namesize)");
+ errno = EINVAL;
+ return -1;
+ }
+ if (write_sync(csock, (char*)name, strlen(name)) != strlen(name)) {
+ LOG("write failed (name)");
+ errno = EINVAL;
+ return -1;
+ }
+ } else {
+ TRACE("Checking magic (cli_magic)");
+
+ if (magic != 0x00420281861253LL) {
+ LOG("Bad magic received");
+ errno = EINVAL;
+ return -1;
+ }
+ }
+
+ if (read_sync(csock, &s, sizeof(s)) != sizeof(s)) {
+ LOG("read failed");
+ errno = EINVAL;
+ return -1;
+ }
+ *size = be64_to_cpu(s);
+ *blocksize = 1024;
+ TRACE("Size is %" PRIu64, *size);
- if (magic != 0x00420281861253LL) {
- LOG("Bad magic received");
+ if (!name) {
+ if (read_sync(csock, flags, sizeof(*flags)) != sizeof(*flags)) {
+ LOG("read failed (flags)");
+ errno = EINVAL;
+ return -1;
+ }
+ *flags = be32_to_cpup(flags);
+ } else {
+ if (read_sync(csock, &tmp, sizeof(tmp)) != sizeof(tmp)) {
+ LOG("read failed (tmp)");
+ errno = EINVAL;
+ return -1;
+ }
+ *flags |= be32_to_cpu(tmp);
+ }
+ if (read_sync(csock, &buf, 124) != 124) {
+ LOG("read failed (buf)");
errno = EINVAL;
return -1;
}
@@ -393,7 +488,7 @@ int nbd_disconnect(int fd)
return 0;
}
-int nbd_client(int fd, int csock)
+int nbd_client(int fd)
{
int ret;
int serrno;
@@ -427,7 +522,7 @@ int nbd_disconnect(int fd)
return -1;
}
-int nbd_client(int fd, int csock)
+int nbd_client(int fd)
{
errno = ENOTSUP;
return -1;
@@ -494,7 +589,7 @@ static int nbd_receive_request(int csock, struct nbd_request *request)
int nbd_receive_reply(int csock, struct nbd_reply *reply)
{
- uint8_t buf[4 + 4 + 8];
+ uint8_t buf[NBD_REPLY_SIZE];
uint32_t magic;
memset(buf, 0xAA, sizeof(buf));
@@ -561,9 +656,9 @@ int nbd_trip(BlockDriverState *bs, int csock, off_t size, uint64_t dev_offset,
if (nbd_receive_request(csock, &request) == -1)
return -1;
- if (request.len > data_size) {
+ if (request.len + NBD_REPLY_SIZE > data_size) {
LOG("len (%u) is larger than max len (%u)",
- request.len, data_size);
+ request.len + NBD_REPLY_SIZE, data_size);
errno = EINVAL;
return -1;
}
@@ -593,7 +688,8 @@ int nbd_trip(BlockDriverState *bs, int csock, off_t size, uint64_t dev_offset,
case NBD_CMD_READ:
TRACE("Request type is READ");
- if (bdrv_read(bs, (request.from + dev_offset) / 512, data,
+ if (bdrv_read(bs, (request.from + dev_offset) / 512,
+ data + NBD_REPLY_SIZE,
request.len / 512) == -1) {
LOG("reading from file failed");
errno = EINVAL;
@@ -603,12 +699,21 @@ int nbd_trip(BlockDriverState *bs, int csock, off_t size, uint64_t dev_offset,
TRACE("Read %u byte(s)", request.len);
- if (nbd_send_reply(csock, &reply) == -1)
- return -1;
+ /* Reply
+ [ 0 .. 3] magic (NBD_REPLY_MAGIC)
+ [ 4 .. 7] error (0 == no error)
+ [ 7 .. 15] handle
+ */
+
+ cpu_to_be32w((uint32_t*)data, NBD_REPLY_MAGIC);
+ cpu_to_be32w((uint32_t*)(data + 4), reply.error);
+ cpu_to_be64w((uint64_t*)(data + 8), reply.handle);
TRACE("Sending data to client");
- if (write_sync(csock, data, request.len) != request.len) {
+ if (write_sync(csock, data,
+ request.len + NBD_REPLY_SIZE) !=
+ request.len + NBD_REPLY_SIZE) {
LOG("writing to socket failed");
errno = EINVAL;
return -1;
diff --git a/nbd.h b/nbd.h
index 5a1fbdf512..fc3a5944f4 100644
--- a/nbd.h
+++ b/nbd.h
@@ -42,6 +42,8 @@ enum {
NBD_CMD_DISC = 2
};
+#define NBD_DEFAULT_PORT 10809
+
size_t nbd_wr_sync(int fd, void *buffer, size_t size, bool do_read);
int tcp_socket_outgoing(const char *address, uint16_t port);
int tcp_socket_incoming(const char *address, uint16_t port);
@@ -49,13 +51,14 @@ int unix_socket_outgoing(const char *path);
int unix_socket_incoming(const char *path);
int nbd_negotiate(int csock, off_t size);
-int nbd_receive_negotiate(int csock, off_t *size, size_t *blocksize);
+int nbd_receive_negotiate(int csock, const char *name, uint32_t *flags,
+ off_t *size, size_t *blocksize);
int nbd_init(int fd, int csock, off_t size, size_t blocksize);
int nbd_send_request(int csock, struct nbd_request *request);
int nbd_receive_reply(int csock, struct nbd_reply *reply);
int nbd_trip(BlockDriverState *bs, int csock, off_t size, uint64_t dev_offset,
off_t *offset, bool readonly, uint8_t *data, int data_size);
-int nbd_client(int fd, int csock);
+int nbd_client(int fd);
int nbd_disconnect(int fd);
#endif
diff --git a/net.c b/net.c
index 8ddf872a6f..c5e6063fcf 100644
--- a/net.c
+++ b/net.c
@@ -281,29 +281,64 @@ NICState *qemu_new_nic(NetClientInfo *info,
return nic;
}
-void qemu_del_vlan_client(VLANClientState *vc)
+static void qemu_cleanup_vlan_client(VLANClientState *vc)
{
if (vc->vlan) {
QTAILQ_REMOVE(&vc->vlan->clients, vc, next);
} else {
- if (vc->send_queue) {
- qemu_del_net_queue(vc->send_queue);
- }
QTAILQ_REMOVE(&non_vlan_clients, vc, next);
- if (vc->peer) {
- vc->peer->peer = NULL;
- }
}
if (vc->info->cleanup) {
vc->info->cleanup(vc);
}
+}
+static void qemu_free_vlan_client(VLANClientState *vc)
+{
+ if (!vc->vlan) {
+ if (vc->send_queue) {
+ qemu_del_net_queue(vc->send_queue);
+ }
+ if (vc->peer) {
+ vc->peer->peer = NULL;
+ }
+ }
qemu_free(vc->name);
qemu_free(vc->model);
qemu_free(vc);
}
+void qemu_del_vlan_client(VLANClientState *vc)
+{
+ /* If there is a peer NIC, delete and cleanup client, but do not free. */
+ if (!vc->vlan && vc->peer && vc->peer->info->type == NET_CLIENT_TYPE_NIC) {
+ NICState *nic = DO_UPCAST(NICState, nc, vc->peer);
+ if (nic->peer_deleted) {
+ return;
+ }
+ nic->peer_deleted = true;
+ /* Let NIC know peer is gone. */
+ vc->peer->link_down = true;
+ if (vc->peer->info->link_status_changed) {
+ vc->peer->info->link_status_changed(vc->peer);
+ }
+ qemu_cleanup_vlan_client(vc);
+ return;
+ }
+
+ /* If this is a peer NIC and peer has already been deleted, free it now. */
+ if (!vc->vlan && vc->peer && vc->info->type == NET_CLIENT_TYPE_NIC) {
+ NICState *nic = DO_UPCAST(NICState, nc, vc);
+ if (nic->peer_deleted) {
+ qemu_free_vlan_client(vc->peer);
+ }
+ }
+
+ qemu_cleanup_vlan_client(vc);
+ qemu_free_vlan_client(vc);
+}
+
VLANClientState *
qemu_find_vlan_client_by_name(Monitor *mon, int vlan_id,
const char *client_str)
@@ -739,19 +774,25 @@ int qemu_find_nic_model(NICInfo *nd, const char * const *models,
int net_handle_fd_param(Monitor *mon, const char *param)
{
- if (!qemu_isdigit(param[0])) {
- int fd;
+ int fd;
+
+ if (!qemu_isdigit(param[0]) && mon) {
fd = monitor_get_fd(mon, param);
if (fd == -1) {
error_report("No file descriptor named %s found", param);
return -1;
}
-
- return fd;
} else {
- return strtol(param, NULL, 0);
+ char *endptr = NULL;
+
+ fd = strtol(param, &endptr, 10);
+ if (*endptr || (fd == 0 && param == endptr)) {
+ return -1;
+ }
}
+
+ return fd;
}
static int net_init_nic(QemuOpts *opts,
@@ -1168,7 +1209,7 @@ void net_host_device_add(Monitor *mon, const QDict *qdict)
return;
}
- opts = qemu_opts_parse(&qemu_net_opts, opts_str ? opts_str : "", 0);
+ opts = qemu_opts_parse(qemu_find_opts("net"), opts_str ? opts_str : "", 0);
if (!opts) {
return;
}
@@ -1202,7 +1243,7 @@ int do_netdev_add(Monitor *mon, const QDict *qdict, QObject **ret_data)
QemuOpts *opts;
int res;
- opts = qemu_opts_from_qdict(&qemu_netdev_opts, qdict);
+ opts = qemu_opts_from_qdict(qemu_find_opts("netdev"), qdict);
if (!opts) {
return -1;
}
@@ -1226,7 +1267,7 @@ int do_netdev_del(Monitor *mon, const QDict *qdict, QObject **ret_data)
return -1;
}
qemu_del_vlan_client(vc);
- qemu_opts_del(qemu_opts_find(&qemu_netdev_opts, id));
+ qemu_opts_del(qemu_opts_find(qemu_find_opts("netdev"), id));
return 0;
}
@@ -1349,21 +1390,23 @@ static int net_init_netdev(QemuOpts *opts, void *dummy)
int net_init_clients(void)
{
+ QemuOptsList *net = qemu_find_opts("net");
+
if (default_net) {
/* if no clients, we use a default config */
- qemu_opts_set(&qemu_net_opts, NULL, "type", "nic");
+ qemu_opts_set(net, NULL, "type", "nic");
#ifdef CONFIG_SLIRP
- qemu_opts_set(&qemu_net_opts, NULL, "type", "user");
+ qemu_opts_set(net, NULL, "type", "user");
#endif
}
QTAILQ_INIT(&vlans);
QTAILQ_INIT(&non_vlan_clients);
- if (qemu_opts_foreach(&qemu_netdev_opts, net_init_netdev, NULL, 1) == -1)
+ if (qemu_opts_foreach(qemu_find_opts("netdev"), net_init_netdev, NULL, 1) == -1)
return -1;
- if (qemu_opts_foreach(&qemu_net_opts, net_init_client, NULL, 1) == -1) {
+ if (qemu_opts_foreach(net, net_init_client, NULL, 1) == -1) {
return -1;
}
diff --git a/net.h b/net.h
index 518cf9c1f7..44c31a9b32 100644
--- a/net.h
+++ b/net.h
@@ -72,6 +72,7 @@ typedef struct NICState {
VLANClientState nc;
NICConf *conf;
void *opaque;
+ bool peer_deleted;
} NICState;
struct VLANState {
diff --git a/net/tap-aix.c b/net/tap-aix.c
index 4bc9f2f6d2..e19aaba110 100644
--- a/net/tap-aix.c
+++ b/net/tap-aix.c
@@ -46,6 +46,15 @@ int tap_probe_has_ufo(int fd)
return 0;
}
+int tap_probe_vnet_hdr_len(int fd, int len)
+{
+ return 0;
+}
+
+void tap_fd_set_vnet_hdr_len(int fd, int len)
+{
+}
+
void tap_fd_set_offload(int fd, int csum, int tso4,
int tso6, int ecn, int ufo)
{
diff --git a/net/tap-bsd.c b/net/tap-bsd.c
index 3773d5da5e..efccfe08a7 100644
--- a/net/tap-bsd.c
+++ b/net/tap-bsd.c
@@ -37,10 +37,6 @@
#include <util.h>
#endif
-#if defined(__OpenBSD__)
-#include <util.h>
-#endif
-
int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required)
{
int fd;
@@ -116,6 +112,15 @@ int tap_probe_has_ufo(int fd)
return 0;
}
+int tap_probe_vnet_hdr_len(int fd, int len)
+{
+ return 0;
+}
+
+void tap_fd_set_vnet_hdr_len(int fd, int len)
+{
+}
+
void tap_fd_set_offload(int fd, int csum, int tso4,
int tso6, int ecn, int ufo)
{
diff --git a/net/tap-haiku.c b/net/tap-haiku.c
new file mode 100644
index 0000000000..91dda8ebc0
--- /dev/null
+++ b/net/tap-haiku.c
@@ -0,0 +1,61 @@
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "net/tap.h"
+#include <stdio.h>
+
+int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required)
+{
+ fprintf(stderr, "no tap on Haiku\n");
+ return -1;
+}
+
+int tap_set_sndbuf(int fd, QemuOpts *opts)
+{
+ return 0;
+}
+
+int tap_probe_vnet_hdr(int fd)
+{
+ return 0;
+}
+
+int tap_probe_has_ufo(int fd)
+{
+ return 0;
+}
+
+int tap_probe_vnet_hdr_len(int fd, int len)
+{
+ return 0;
+}
+
+void tap_fd_set_vnet_hdr_len(int fd, int len)
+{
+}
+
+void tap_fd_set_offload(int fd, int csum, int tso4,
+ int tso6, int ecn, int ufo)
+{
+}
diff --git a/net/tap-linux.c b/net/tap-linux.c
index c92983cb53..f7aa9041b8 100644
--- a/net/tap-linux.c
+++ b/net/tap-linux.c
@@ -129,6 +129,35 @@ int tap_probe_has_ufo(int fd)
return 1;
}
+/* Verify that we can assign given length */
+int tap_probe_vnet_hdr_len(int fd, int len)
+{
+ int orig;
+ if (ioctl(fd, TUNGETVNETHDRSZ, &orig) == -1) {
+ return 0;
+ }
+ if (ioctl(fd, TUNSETVNETHDRSZ, &len) == -1) {
+ return 0;
+ }
+ /* Restore original length: we can't handle failure. */
+ if (ioctl(fd, TUNSETVNETHDRSZ, &orig) == -1) {
+ fprintf(stderr, "TUNGETVNETHDRSZ ioctl() failed: %s. Exiting.\n",
+ strerror(errno));
+ assert(0);
+ return -errno;
+ }
+ return 1;
+}
+
+void tap_fd_set_vnet_hdr_len(int fd, int len)
+{
+ if (ioctl(fd, TUNSETVNETHDRSZ, &len) == -1) {
+ fprintf(stderr, "TUNSETVNETHDRSZ ioctl() failed: %s. Exiting.\n",
+ strerror(errno));
+ assert(0);
+ }
+}
+
void tap_fd_set_offload(int fd, int csum, int tso4,
int tso6, int ecn, int ufo)
{
diff --git a/net/tap-linux.h b/net/tap-linux.h
index 9f943589bf..659e98122b 100644
--- a/net/tap-linux.h
+++ b/net/tap-linux.h
@@ -27,6 +27,8 @@
#define TUNSETOFFLOAD _IOW('T', 208, unsigned int)
#define TUNGETIFF _IOR('T', 210, unsigned int)
#define TUNSETSNDBUF _IOW('T', 212, int)
+#define TUNGETVNETHDRSZ _IOR('T', 215, int)
+#define TUNSETVNETHDRSZ _IOW('T', 216, int)
#endif
@@ -52,4 +54,10 @@ struct virtio_net_hdr
uint16_t csum_offset;
};
+struct virtio_net_hdr_mrg_rxbuf
+{
+ struct virtio_net_hdr hdr;
+ uint16_t num_buffers; /* Number of merged rx buffers */
+};
+
#endif /* QEMU_TAP_H */
diff --git a/net/tap-solaris.c b/net/tap-solaris.c
index 50d127ad3f..c216d28267 100644
--- a/net/tap-solaris.c
+++ b/net/tap-solaris.c
@@ -38,6 +38,7 @@
#include <net/if.h>
#include <syslog.h>
#include <stropts.h>
+#include "qemu-error.h"
ssize_t tap_read_packet(int tapfd, uint8_t *buf, int maxlen)
{
@@ -211,6 +212,15 @@ int tap_probe_has_ufo(int fd)
return 0;
}
+int tap_probe_vnet_hdr_len(int fd, int len)
+{
+ return 0;
+}
+
+void tap_fd_set_vnet_hdr_len(int fd, int len)
+{
+}
+
void tap_fd_set_offload(int fd, int csum, int tso4,
int tso6, int ecn, int ufo)
{
diff --git a/net/tap-win32.c b/net/tap-win32.c
index 74348daf5a..081904e8d7 100644
--- a/net/tap-win32.c
+++ b/net/tap-win32.c
@@ -579,7 +579,6 @@ static int tap_win32_open(tap_win32_overlapped_t **phandle,
} version;
DWORD version_len;
DWORD idThread;
- HANDLE hThread;
if (prefered_name != NULL)
snprintf(name_buffer, sizeof(name_buffer), "%s", prefered_name);
@@ -623,8 +622,8 @@ static int tap_win32_open(tap_win32_overlapped_t **phandle,
*phandle = &tap_overlapped;
- hThread = CreateThread(NULL, 0, tap_win32_thread_entry,
- (LPVOID)&tap_overlapped, 0, &idThread);
+ CreateThread(NULL, 0, tap_win32_thread_entry,
+ (LPVOID)&tap_overlapped, 0, &idThread);
return 0;
}
@@ -728,6 +727,15 @@ int tap_has_vnet_hdr(VLANClientState *vc)
return 0;
}
+int tap_probe_vnet_hdr_len(int fd, int len)
+{
+ return 0;
+}
+
+void tap_fd_set_vnet_hdr_len(int fd, int len)
+{
+}
+
void tap_using_vnet_hdr(VLANClientState *vc, int using_vnet_hdr)
{
}
diff --git a/net/tap.c b/net/tap.c
index 0147dabc15..4afb314fde 100644
--- a/net/tap.c
+++ b/net/tap.c
@@ -57,10 +57,10 @@ typedef struct TAPState {
uint8_t buf[TAP_BUFSIZE];
unsigned int read_poll : 1;
unsigned int write_poll : 1;
- unsigned int has_vnet_hdr : 1;
unsigned int using_vnet_hdr : 1;
unsigned int has_ufo: 1;
VHostNetState *vhost_net;
+ unsigned host_vnet_hdr_len;
} TAPState;
static int launch_script(const char *setup_script, const char *ifname, int fd);
@@ -121,11 +121,11 @@ static ssize_t tap_receive_iov(VLANClientState *nc, const struct iovec *iov,
TAPState *s = DO_UPCAST(TAPState, nc, nc);
const struct iovec *iovp = iov;
struct iovec iov_copy[iovcnt + 1];
- struct virtio_net_hdr hdr = { 0, };
+ struct virtio_net_hdr_mrg_rxbuf hdr = { };
- if (s->has_vnet_hdr && !s->using_vnet_hdr) {
+ if (s->host_vnet_hdr_len && !s->using_vnet_hdr) {
iov_copy[0].iov_base = &hdr;
- iov_copy[0].iov_len = sizeof(hdr);
+ iov_copy[0].iov_len = s->host_vnet_hdr_len;
memcpy(&iov_copy[1], iov, iovcnt * sizeof(*iov));
iovp = iov_copy;
iovcnt++;
@@ -139,11 +139,11 @@ static ssize_t tap_receive_raw(VLANClientState *nc, const uint8_t *buf, size_t s
TAPState *s = DO_UPCAST(TAPState, nc, nc);
struct iovec iov[2];
int iovcnt = 0;
- struct virtio_net_hdr hdr = { 0, };
+ struct virtio_net_hdr_mrg_rxbuf hdr = { };
- if (s->has_vnet_hdr) {
+ if (s->host_vnet_hdr_len) {
iov[iovcnt].iov_base = &hdr;
- iov[iovcnt].iov_len = sizeof(hdr);
+ iov[iovcnt].iov_len = s->host_vnet_hdr_len;
iovcnt++;
}
@@ -159,7 +159,7 @@ static ssize_t tap_receive(VLANClientState *nc, const uint8_t *buf, size_t size)
TAPState *s = DO_UPCAST(TAPState, nc, nc);
struct iovec iov[1];
- if (s->has_vnet_hdr && !s->using_vnet_hdr) {
+ if (s->host_vnet_hdr_len && !s->using_vnet_hdr) {
return tap_receive_raw(nc, buf, size);
}
@@ -202,9 +202,9 @@ static void tap_send(void *opaque)
break;
}
- if (s->has_vnet_hdr && !s->using_vnet_hdr) {
- buf += sizeof(struct virtio_net_hdr);
- size -= sizeof(struct virtio_net_hdr);
+ if (s->host_vnet_hdr_len && !s->using_vnet_hdr) {
+ buf += s->host_vnet_hdr_len;
+ size -= s->host_vnet_hdr_len;
}
size = qemu_send_packet_async(&s->nc, buf, size, tap_send_completed);
@@ -229,7 +229,28 @@ int tap_has_vnet_hdr(VLANClientState *nc)
assert(nc->info->type == NET_CLIENT_TYPE_TAP);
- return s->has_vnet_hdr;
+ return !!s->host_vnet_hdr_len;
+}
+
+int tap_has_vnet_hdr_len(VLANClientState *nc, int len)
+{
+ TAPState *s = DO_UPCAST(TAPState, nc, nc);
+
+ assert(nc->info->type == NET_CLIENT_TYPE_TAP);
+
+ return tap_probe_vnet_hdr_len(s->fd, len);
+}
+
+void tap_set_vnet_hdr_len(VLANClientState *nc, int len)
+{
+ TAPState *s = DO_UPCAST(TAPState, nc, nc);
+
+ assert(nc->info->type == NET_CLIENT_TYPE_TAP);
+ assert(len == sizeof(struct virtio_net_hdr_mrg_rxbuf) ||
+ len == sizeof(struct virtio_net_hdr));
+
+ tap_fd_set_vnet_hdr_len(s->fd, len);
+ s->host_vnet_hdr_len = len;
}
void tap_using_vnet_hdr(VLANClientState *nc, int using_vnet_hdr)
@@ -239,7 +260,7 @@ void tap_using_vnet_hdr(VLANClientState *nc, int using_vnet_hdr)
using_vnet_hdr = using_vnet_hdr != 0;
assert(nc->info->type == NET_CLIENT_TYPE_TAP);
- assert(s->has_vnet_hdr == using_vnet_hdr);
+ assert(!!s->host_vnet_hdr_len == using_vnet_hdr);
s->using_vnet_hdr = using_vnet_hdr;
}
@@ -310,7 +331,7 @@ static TAPState *net_tap_fd_init(VLANState *vlan,
s = DO_UPCAST(TAPState, nc, nc);
s->fd = fd;
- s->has_vnet_hdr = vnet_hdr != 0;
+ s->host_vnet_hdr_len = vnet_hdr ? sizeof(struct virtio_net_hdr) : 0;
s->using_vnet_hdr = 0;
s->has_ufo = tap_probe_has_ufo(s->fd);
tap_set_offload(&s->nc, 0, 0, 0, 0, 0);
diff --git a/net/tap.h b/net/tap.h
index b8cec8320c..e44bd2bc5f 100644
--- a/net/tap.h
+++ b/net/tap.h
@@ -40,13 +40,17 @@ ssize_t tap_read_packet(int tapfd, uint8_t *buf, int maxlen);
int tap_has_ufo(VLANClientState *vc);
int tap_has_vnet_hdr(VLANClientState *vc);
+int tap_has_vnet_hdr_len(VLANClientState *vc, int len);
void tap_using_vnet_hdr(VLANClientState *vc, int using_vnet_hdr);
void tap_set_offload(VLANClientState *vc, int csum, int tso4, int tso6, int ecn, int ufo);
+void tap_set_vnet_hdr_len(VLANClientState *vc, int len);
int tap_set_sndbuf(int fd, QemuOpts *opts);
int tap_probe_vnet_hdr(int fd);
+int tap_probe_vnet_hdr_len(int fd, int len);
int tap_probe_has_ufo(int fd);
void tap_fd_set_offload(int fd, int csum, int tso4, int tso6, int ecn, int ufo);
+void tap_fd_set_vnet_hdr_len(int fd, int len);
int tap_get_fd(VLANClientState *vc);
diff --git a/os-posix.c b/os-posix.c
index 00133a0c76..6321e990c5 100644
--- a/os-posix.c
+++ b/os-posix.c
@@ -110,7 +110,7 @@ char *os_find_datadir(const char *argv0)
size_t len = sizeof(buf) - 1;
*buf = '\0';
- if (!sysctl(mib, sizeof(mib)/sizeof(*mib), buf, &len, NULL, 0) &&
+ if (!sysctl(mib, ARRAY_SIZE(mib), buf, &len, NULL, 0) &&
*buf) {
buf[sizeof(buf) - 1] = '\0';
p = buf;
diff --git a/os-win32.c b/os-win32.c
index d98fd77c12..3c6f50fa94 100644
--- a/os-win32.c
+++ b/os-win32.c
@@ -34,6 +34,21 @@
#include "qemu-options.h"
/***********************************************************/
+/* Functions missing in mingw */
+
+int setenv(const char *name, const char *value, int overwrite)
+{
+ int result = 0;
+ if (overwrite || !getenv(name)) {
+ size_t length = strlen(name) + strlen(value) + 2;
+ char *string = qemu_malloc(length);
+ snprintf(string, length, "%s=%s", name, value);
+ result = putenv(string);
+ }
+ return result;
+}
+
+/***********************************************************/
/* Polling handling */
typedef struct PollingEntry {
@@ -206,6 +221,12 @@ char *os_find_datadir(const char *argv0)
return NULL;
}
+void os_set_line_buffering(void)
+{
+ setbuf(stdout, NULL);
+ setbuf(stderr, NULL);
+}
+
/*
* Parse OS specific command line options.
* return 0 if option handled, -1 otherwise
diff --git a/osdep.c b/osdep.c
index dbf872aea7..2e05b21ad9 100644
--- a/osdep.c
+++ b/osdep.c
@@ -32,9 +32,16 @@
/* Needed early for CONFIG_BSD etc. */
#include "config-host.h"
+#if defined(CONFIG_MADVISE) || defined(CONFIG_POSIX_MADVISE)
+#include <sys/mman.h>
+#endif
+
#ifdef CONFIG_SOLARIS
#include <sys/types.h>
#include <sys/statvfs.h>
+/* See MySQL bug #7156 (http://bugs.mysql.com/bug.php?id=7156) for
+ discussion about Solaris header problems */
+extern int madvise(caddr_t, size_t, int);
#endif
#ifdef CONFIG_EVENTFD
@@ -50,6 +57,7 @@
#endif
#include "qemu-common.h"
+#include "trace.h"
#include "sysemu.h"
#include "qemu_socket.h"
@@ -71,25 +79,34 @@ static void *oom_check(void *ptr)
#if defined(_WIN32)
void *qemu_memalign(size_t alignment, size_t size)
{
+ void *ptr;
+
if (!size) {
abort();
}
- return oom_check(VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE));
+ ptr = oom_check(VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE));
+ trace_qemu_memalign(alignment, size, ptr);
+ return ptr;
}
void *qemu_vmalloc(size_t size)
{
+ void *ptr;
+
/* FIXME: this is not exactly optimal solution since VirtualAlloc
has 64Kb granularity, but at least it guarantees us that the
memory is page aligned. */
if (!size) {
abort();
}
- return oom_check(VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE));
+ ptr = oom_check(VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE));
+ trace_qemu_vmalloc(size, ptr);
+ return ptr;
}
void qemu_vfree(void *ptr)
{
+ trace_qemu_vfree(ptr);
VirtualFree(ptr, 0, MEM_RELEASE);
}
@@ -97,21 +114,22 @@ void qemu_vfree(void *ptr)
void *qemu_memalign(size_t alignment, size_t size)
{
+ void *ptr;
#if defined(_POSIX_C_SOURCE) && !defined(__sun__)
int ret;
- void *ptr;
ret = posix_memalign(&ptr, alignment, size);
if (ret != 0) {
fprintf(stderr, "Failed to allocate %zu B: %s\n",
size, strerror(ret));
abort();
}
- return ptr;
#elif defined(CONFIG_BSD)
- return oom_check(valloc(size));
+ ptr = oom_check(valloc(size));
#else
- return oom_check(memalign(alignment, size));
+ ptr = oom_check(memalign(alignment, size));
#endif
+ trace_qemu_memalign(alignment, size, ptr);
+ return ptr;
}
/* alloc shared memory pages */
@@ -122,11 +140,28 @@ void *qemu_vmalloc(size_t size)
void qemu_vfree(void *ptr)
{
+ trace_qemu_vfree(ptr);
free(ptr);
}
#endif
+int qemu_madvise(void *addr, size_t len, int advice)
+{
+ if (advice == QEMU_MADV_INVALID) {
+ errno = EINVAL;
+ return -1;
+ }
+#if defined(CONFIG_MADVISE)
+ return madvise(addr, len, advice);
+#elif defined(CONFIG_POSIX_MADVISE)
+ return posix_madvise(addr, len, advice);
+#else
+ errno = EINVAL;
+ return -1;
+#endif
+}
+
int qemu_create_pidfile(const char *filename)
{
char buffer[128];
diff --git a/osdep.h b/osdep.h
index 75b581630f..671628114a 100644
--- a/osdep.h
+++ b/osdep.h
@@ -90,11 +90,48 @@ void *qemu_memalign(size_t alignment, size_t size);
void *qemu_vmalloc(size_t size);
void qemu_vfree(void *ptr);
+#define QEMU_MADV_INVALID -1
+
+#if defined(CONFIG_MADVISE)
+
+#define QEMU_MADV_WILLNEED MADV_WILLNEED
+#define QEMU_MADV_DONTNEED MADV_DONTNEED
+#ifdef MADV_DONTFORK
+#define QEMU_MADV_DONTFORK MADV_DONTFORK
+#else
+#define QEMU_MADV_DONTFORK QEMU_MADV_INVALID
+#endif
+#ifdef MADV_MERGEABLE
+#define QEMU_MADV_MERGEABLE MADV_MERGEABLE
+#else
+#define QEMU_MADV_MERGEABLE QEMU_MADV_INVALID
+#endif
+
+#elif defined(CONFIG_POSIX_MADVISE)
+
+#define QEMU_MADV_WILLNEED POSIX_MADV_WILLNEED
+#define QEMU_MADV_DONTNEED POSIX_MADV_DONTNEED
+#define QEMU_MADV_DONTFORK QEMU_MADV_INVALID
+#define QEMU_MADV_MERGEABLE QEMU_MADV_INVALID
+
+#else /* no-op */
+
+#define QEMU_MADV_WILLNEED QEMU_MADV_INVALID
+#define QEMU_MADV_DONTNEED QEMU_MADV_INVALID
+#define QEMU_MADV_DONTFORK QEMU_MADV_INVALID
+#define QEMU_MADV_MERGEABLE QEMU_MADV_INVALID
+
+#endif
+
+int qemu_madvise(void *addr, size_t len, int advice);
+
int qemu_create_pidfile(const char *filename);
#ifdef _WIN32
int ffs(int i);
+int setenv(const char *name, const char *value, int overwrite);
+
typedef struct {
long tv_sec;
long tv_usec;
diff --git a/pc-bios/README b/pc-bios/README
index 40f35c5bd1..3172cf7896 100644
--- a/pc-bios/README
+++ b/pc-bios/README
@@ -7,15 +7,11 @@
- The PowerPC Open Hack'Ware Open Firmware Compatible BIOS is
available at http://perso.magic.fr/l_indien/OpenHackWare/index.htm.
-- video.x is a PowerMac NDRV compatible driver for a VGA frame
- buffer. It comes from the Mac-on-Linux project
- (http://www.maconlinux.org/).
-
- OpenBIOS (http://www.openbios.org/) is a free (GPL v2) portable
firmware implementation. The goal is to implement a 100% IEEE
1275-1994 (referred to as Open Firmware) compliant firmware.
The included image for PowerPC (for 32 and 64 bit PPC CPUs), Sparc32
- and Sparc64 are built from OpenBIOS SVN revision 795.
+ and Sparc64 are built from OpenBIOS SVN revision 859.
- The PXE roms come from Rom-o-Matic gPXE 0.9.9 with BANNER_TIMEOUT=0
diff --git a/pc-bios/bios.bin b/pc-bios/bios.bin
index f27ea89f8d..d0d4b6aa43 100644
--- a/pc-bios/bios.bin
+++ b/pc-bios/bios.bin
Binary files differ
diff --git a/pc-bios/openbios-ppc b/pc-bios/openbios-ppc
index 9e660ed538..cb0af05575 100644
--- a/pc-bios/openbios-ppc
+++ b/pc-bios/openbios-ppc
Binary files differ
diff --git a/pc-bios/openbios-sparc32 b/pc-bios/openbios-sparc32
index 1acaa31acf..aaff1f00c9 100644
--- a/pc-bios/openbios-sparc32
+++ b/pc-bios/openbios-sparc32
Binary files differ
diff --git a/pc-bios/openbios-sparc64 b/pc-bios/openbios-sparc64
index 93c7269c15..a1b692e7e5 100644
--- a/pc-bios/openbios-sparc64
+++ b/pc-bios/openbios-sparc64
Binary files differ
diff --git a/pc-bios/video.x b/pc-bios/video.x
deleted file mode 100644
index 761aa0c9d4..0000000000
--- a/pc-bios/video.x
+++ /dev/null
Binary files differ
diff --git a/pflib.c b/pflib.c
new file mode 100644
index 0000000000..1154d0c9a3
--- /dev/null
+++ b/pflib.c
@@ -0,0 +1,213 @@
+/*
+ * PixelFormat conversion library.
+ *
+ * Author: Gerd Hoffmann <kraxel@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+#include "qemu-common.h"
+#include "console.h"
+#include "pflib.h"
+
+typedef struct QemuPixel QemuPixel;
+
+typedef void (*pf_convert)(QemuPfConv *conv,
+ void *dst, void *src, uint32_t cnt);
+typedef void (*pf_convert_from)(PixelFormat *pf,
+ QemuPixel *dst, void *src, uint32_t cnt);
+typedef void (*pf_convert_to)(PixelFormat *pf,
+ void *dst, QemuPixel *src, uint32_t cnt);
+
+struct QemuPfConv {
+ pf_convert convert;
+ PixelFormat src;
+ PixelFormat dst;
+
+ /* for copy_generic() */
+ pf_convert_from conv_from;
+ pf_convert_to conv_to;
+ QemuPixel *conv_buf;
+ uint32_t conv_cnt;
+};
+
+struct QemuPixel {
+ uint8_t red;
+ uint8_t green;
+ uint8_t blue;
+ uint8_t alpha;
+};
+
+/* ----------------------------------------------------------------------- */
+/* PixelFormat -> QemuPixel conversions */
+
+static void conv_16_to_pixel(PixelFormat *pf,
+ QemuPixel *dst, void *src, uint32_t cnt)
+{
+ uint16_t *src16 = src;
+
+ while (cnt > 0) {
+ dst->red = ((*src16 & pf->rmask) >> pf->rshift) << (8 - pf->rbits);
+ dst->green = ((*src16 & pf->gmask) >> pf->gshift) << (8 - pf->gbits);
+ dst->blue = ((*src16 & pf->bmask) >> pf->bshift) << (8 - pf->bbits);
+ dst->alpha = ((*src16 & pf->amask) >> pf->ashift) << (8 - pf->abits);
+ dst++, src16++, cnt--;
+ }
+}
+
+/* assumes pf->{r,g,b,a}bits == 8 */
+static void conv_32_to_pixel_fast(PixelFormat *pf,
+ QemuPixel *dst, void *src, uint32_t cnt)
+{
+ uint32_t *src32 = src;
+
+ while (cnt > 0) {
+ dst->red = (*src32 & pf->rmask) >> pf->rshift;
+ dst->green = (*src32 & pf->gmask) >> pf->gshift;
+ dst->blue = (*src32 & pf->bmask) >> pf->bshift;
+ dst->alpha = (*src32 & pf->amask) >> pf->ashift;
+ dst++, src32++, cnt--;
+ }
+}
+
+static void conv_32_to_pixel_generic(PixelFormat *pf,
+ QemuPixel *dst, void *src, uint32_t cnt)
+{
+ uint32_t *src32 = src;
+
+ while (cnt > 0) {
+ if (pf->rbits < 8) {
+ dst->red = ((*src32 & pf->rmask) >> pf->rshift) << (8 - pf->rbits);
+ } else {
+ dst->red = ((*src32 & pf->rmask) >> pf->rshift) >> (pf->rbits - 8);
+ }
+ if (pf->gbits < 8) {
+ dst->green = ((*src32 & pf->gmask) >> pf->gshift) << (8 - pf->gbits);
+ } else {
+ dst->green = ((*src32 & pf->gmask) >> pf->gshift) >> (pf->gbits - 8);
+ }
+ if (pf->bbits < 8) {
+ dst->blue = ((*src32 & pf->bmask) >> pf->bshift) << (8 - pf->bbits);
+ } else {
+ dst->blue = ((*src32 & pf->bmask) >> pf->bshift) >> (pf->bbits - 8);
+ }
+ if (pf->abits < 8) {
+ dst->alpha = ((*src32 & pf->amask) >> pf->ashift) << (8 - pf->abits);
+ } else {
+ dst->alpha = ((*src32 & pf->amask) >> pf->ashift) >> (pf->abits - 8);
+ }
+ dst++, src32++, cnt--;
+ }
+}
+
+/* ----------------------------------------------------------------------- */
+/* QemuPixel -> PixelFormat conversions */
+
+static void conv_pixel_to_16(PixelFormat *pf,
+ void *dst, QemuPixel *src, uint32_t cnt)
+{
+ uint16_t *dst16 = dst;
+
+ while (cnt > 0) {
+ *dst16 = ((uint16_t)src->red >> (8 - pf->rbits)) << pf->rshift;
+ *dst16 |= ((uint16_t)src->green >> (8 - pf->gbits)) << pf->gshift;
+ *dst16 |= ((uint16_t)src->blue >> (8 - pf->bbits)) << pf->bshift;
+ *dst16 |= ((uint16_t)src->alpha >> (8 - pf->abits)) << pf->ashift;
+ dst16++, src++, cnt--;
+ }
+}
+
+static void conv_pixel_to_32(PixelFormat *pf,
+ void *dst, QemuPixel *src, uint32_t cnt)
+{
+ uint32_t *dst32 = dst;
+
+ while (cnt > 0) {
+ *dst32 = ((uint32_t)src->red >> (8 - pf->rbits)) << pf->rshift;
+ *dst32 |= ((uint32_t)src->green >> (8 - pf->gbits)) << pf->gshift;
+ *dst32 |= ((uint32_t)src->blue >> (8 - pf->bbits)) << pf->bshift;
+ *dst32 |= ((uint32_t)src->alpha >> (8 - pf->abits)) << pf->ashift;
+ dst32++, src++, cnt--;
+ }
+}
+
+/* ----------------------------------------------------------------------- */
+/* PixelFormat -> PixelFormat conversions */
+
+static void convert_copy(QemuPfConv *conv, void *dst, void *src, uint32_t cnt)
+{
+ uint32_t bytes = cnt * conv->src.bytes_per_pixel;
+ memcpy(dst, src, bytes);
+}
+
+static void convert_generic(QemuPfConv *conv, void *dst, void *src, uint32_t cnt)
+{
+ if (conv->conv_cnt < cnt) {
+ conv->conv_cnt = cnt;
+ conv->conv_buf = qemu_realloc(conv->conv_buf, sizeof(QemuPixel) * conv->conv_cnt);
+ }
+ conv->conv_from(&conv->src, conv->conv_buf, src, cnt);
+ conv->conv_to(&conv->dst, dst, conv->conv_buf, cnt);
+}
+
+/* ----------------------------------------------------------------------- */
+/* public interface */
+
+QemuPfConv *qemu_pf_conv_get(PixelFormat *dst, PixelFormat *src)
+{
+ QemuPfConv *conv = qemu_mallocz(sizeof(QemuPfConv));
+
+ conv->src = *src;
+ conv->dst = *dst;
+
+ if (memcmp(&conv->src, &conv->dst, sizeof(PixelFormat)) == 0) {
+ /* formats identical, can simply copy */
+ conv->convert = convert_copy;
+ } else {
+ /* generic two-step conversion: src -> QemuPixel -> dst */
+ switch (conv->src.bytes_per_pixel) {
+ case 2:
+ conv->conv_from = conv_16_to_pixel;
+ break;
+ case 4:
+ if (conv->src.rbits == 8 && conv->src.gbits == 8 && conv->src.bbits == 8) {
+ conv->conv_from = conv_32_to_pixel_fast;
+ } else {
+ conv->conv_from = conv_32_to_pixel_generic;
+ }
+ break;
+ default:
+ goto err;
+ }
+ switch (conv->dst.bytes_per_pixel) {
+ case 2:
+ conv->conv_to = conv_pixel_to_16;
+ break;
+ case 4:
+ conv->conv_to = conv_pixel_to_32;
+ break;
+ default:
+ goto err;
+ }
+ conv->convert = convert_generic;
+ }
+ return conv;
+
+err:
+ qemu_free(conv);
+ return NULL;
+}
+
+void qemu_pf_conv_run(QemuPfConv *conv, void *dst, void *src, uint32_t cnt)
+{
+ conv->convert(conv, dst, src, cnt);
+}
+
+void qemu_pf_conv_put(QemuPfConv *conv)
+{
+ if (conv) {
+ qemu_free(conv->conv_buf);
+ qemu_free(conv);
+ }
+}
diff --git a/pflib.h b/pflib.h
new file mode 100644
index 0000000000..b70c313acf
--- /dev/null
+++ b/pflib.h
@@ -0,0 +1,20 @@
+#ifndef __QEMU_PFLIB_H
+#define __QEMU_PFLIB_H
+
+/*
+ * PixelFormat conversion library.
+ *
+ * Author: Gerd Hoffmann <kraxel@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+typedef struct QemuPfConv QemuPfConv;
+
+QemuPfConv *qemu_pf_conv_get(PixelFormat *dst, PixelFormat *src);
+void qemu_pf_conv_run(QemuPfConv *conv, void *dst, void *src, uint32_t cnt);
+void qemu_pf_conv_put(QemuPfConv *conv);
+
+#endif
diff --git a/posix-aio-compat.c b/posix-aio-compat.c
index a67ffe3113..7b862b5400 100644
--- a/posix-aio-compat.c
+++ b/posix-aio-compat.c
@@ -25,6 +25,7 @@
#include "qemu-queue.h"
#include "osdep.h"
#include "qemu-common.h"
+#include "trace.h"
#include "block_int.h"
#include "block/raw-posix-aio.h"
@@ -127,7 +128,7 @@ static ssize_t handle_aiocb_ioctl(struct qemu_paiocb *aiocb)
/*
* This looks weird, but the aio code only consideres a request
- * successfull if it has written the number full number of bytes.
+ * successful if it has written the number full number of bytes.
*
* Now we overload aio_nbytes as aio_ioctl_cmd for the ioctl command,
* so in fact we return the ioctl command here to make posix_aio_read()
@@ -269,7 +270,7 @@ static ssize_t handle_aiocb_rw(struct qemu_paiocb *aiocb)
* Ok, we have to do it the hard way, copy all segments into
* a single aligned buffer.
*/
- buf = qemu_memalign(512, aiocb->aio_nbytes);
+ buf = qemu_blockalign(aiocb->common.bs, aiocb->aio_nbytes);
if (aiocb->aio_type & QEMU_AIO_WRITE) {
char *p = buf;
int i;
@@ -583,6 +584,7 @@ BlockDriverAIOCB *paio_submit(BlockDriverState *bs, int fd,
acb->next = posix_aio_state->first_aio;
posix_aio_state->first_aio = acb;
+ trace_paio_submit(acb, opaque, sector_num, nb_sectors, type);
qemu_paio_submit(acb);
return &acb->common;
}
@@ -599,6 +601,7 @@ BlockDriverAIOCB *paio_ioctl(BlockDriverState *bs, int fd,
acb->aio_type = QEMU_AIO_IOCTL;
acb->aio_fildes = fd;
acb->ev_signo = SIGUSR2;
+ acb->async_context_id = get_async_context_id();
acb->aio_offset = 0;
acb->aio_ioctl_buf = buf;
acb->aio_ioctl_cmd = req;
diff --git a/qemu-barrier.h b/qemu-barrier.h
index 3bd1075d66..b77fce23a9 100644
--- a/qemu-barrier.h
+++ b/qemu-barrier.h
@@ -4,4 +4,7 @@
/* FIXME: arch dependant, x86 version */
#define smp_wmb() asm volatile("" ::: "memory")
+/* Compiler barrier */
+#define barrier() asm volatile("" ::: "memory")
+
#endif
diff --git a/qemu-char.c b/qemu-char.c
index 9b69d928ef..6d2dce7a9b 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -2087,6 +2087,13 @@ static void tcp_chr_read(void *opaque)
}
}
+#ifndef _WIN32
+CharDriverState *qemu_chr_open_eventfd(int eventfd)
+{
+ return qemu_chr_open_fd(eventfd, eventfd);
+}
+#endif
+
static void tcp_chr_connect(void *opaque)
{
CharDriverState *chr = opaque;
@@ -2279,7 +2286,7 @@ QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename)
const char *p;
QemuOpts *opts;
- opts = qemu_opts_create(&qemu_chardev_opts, label, 1);
+ opts = qemu_opts_create(qemu_find_opts("chardev"), label, 1);
if (NULL == opts)
return NULL;
diff --git a/qemu-char.h b/qemu-char.h
index e3a07838a4..18ad12bb5d 100644
--- a/qemu-char.h
+++ b/qemu-char.h
@@ -76,7 +76,8 @@ CharDriverState *qemu_chr_open_opts(QemuOpts *opts,
void (*init)(struct CharDriverState *s));
CharDriverState *qemu_chr_open(const char *label, const char *filename, void (*init)(struct CharDriverState *s));
void qemu_chr_close(CharDriverState *chr);
-void qemu_chr_printf(CharDriverState *s, const char *fmt, ...);
+void qemu_chr_printf(CharDriverState *s, const char *fmt, ...)
+ GCC_FMT_ATTR(2, 3);
int qemu_chr_write(CharDriverState *s, const uint8_t *buf, int len);
void qemu_chr_send_event(CharDriverState *s, int event);
void qemu_chr_add_handlers(CharDriverState *s,
@@ -94,6 +95,9 @@ void qemu_chr_info_print(Monitor *mon, const QObject *ret_data);
void qemu_chr_info(Monitor *mon, QObject **ret_data);
CharDriverState *qemu_chr_find(const char *name);
+/* add an eventfd to the qemu devices that are polled */
+CharDriverState *qemu_chr_open_eventfd(int eventfd);
+
extern int term_escape_char;
/* async I/O support */
diff --git a/qemu-common.h b/qemu-common.h
index b97b16eeca..2498769efa 100644
--- a/qemu-common.h
+++ b/qemu-common.h
@@ -18,11 +18,6 @@ typedef struct QEMUFile QEMUFile;
typedef struct QEMUBH QEMUBH;
typedef struct DeviceState DeviceState;
-/* Hack around the mess dyngen-exec.h causes: We need QEMU_NORETURN in files that
- cannot include the following headers without conflicts. This condition has
- to be removed once dyngen is gone. */
-#ifndef __DYNGEN_EXEC_H__
-
/* we put basic includes here to avoid repeating them in device drivers */
#include <stdlib.h>
#include <stdio.h>
@@ -70,6 +65,22 @@ struct iovec {
#include <sys/uio.h>
#endif
+#if defined __GNUC__
+# if (__GNUC__ < 4) || \
+ defined(__GNUC_MINOR__) && (__GNUC__ == 4) && (__GNUC_MINOR__ < 4)
+ /* gcc versions before 4.4.x don't support gnu_printf, so use printf. */
+# define GCC_ATTR __attribute__((__unused__, format(printf, 1, 2)))
+# define GCC_FMT_ATTR(n, m) __attribute__((format(printf, n, m)))
+# else
+ /* Use gnu_printf when supported (qemu uses standard format strings). */
+# define GCC_ATTR __attribute__((__unused__, format(gnu_printf, 1, 2)))
+# define GCC_FMT_ATTR(n, m) __attribute__((format(gnu_printf, n, m)))
+# endif
+#else
+#define GCC_ATTR /**/
+#define GCC_FMT_ATTR(n, m)
+#endif
+
#ifdef _WIN32
#define fsync _commit
#define lseek _lseeki64
@@ -122,8 +133,6 @@ void qemu_bh_delete(QEMUBH *bh);
int qemu_bh_poll(void);
void qemu_bh_update_timeout(int *timeout);
-uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c);
-
void qemu_get_timedate(struct tm *tm, int offset);
int qemu_timedate_diff(struct tm *tm);
@@ -180,8 +189,7 @@ int qemu_pipe(int pipefd[2]);
/* Error handling. */
-void QEMU_NORETURN hw_error(const char *fmt, ...)
- __attribute__ ((__format__ (__printf__, 1, 2)));
+void QEMU_NORETURN hw_error(const char *fmt, ...) GCC_FMT_ATTR(1, 2);
/* IO callbacks. */
typedef void IOReadHandler(void *opaque, const uint8_t *buf, int size);
@@ -201,6 +209,7 @@ typedef struct NICInfo NICInfo;
typedef struct HCIInfo HCIInfo;
typedef struct AudioState AudioState;
typedef struct BlockDriverState BlockDriverState;
+typedef struct DriveInfo DriveInfo;
typedef struct DisplayState DisplayState;
typedef struct DisplayChangeListener DisplayChangeListener;
typedef struct DisplaySurface DisplaySurface;
@@ -235,6 +244,12 @@ typedef struct VirtIODevice VirtIODevice;
typedef uint64_t pcibus_t;
+typedef enum {
+ IF_NONE,
+ IF_IDE, IF_SCSI, IF_FLOPPY, IF_PFLASH, IF_MTD, IF_SD, IF_VIRTIO, IF_XEN,
+ IF_COUNT
+} BlockInterfaceType;
+
void cpu_exec_init_all(unsigned long tb_size);
/* CPU save/load. */
@@ -275,11 +290,14 @@ typedef struct QEMUIOVector {
void qemu_iovec_init(QEMUIOVector *qiov, int alloc_hint);
void qemu_iovec_init_external(QEMUIOVector *qiov, struct iovec *iov, int niov);
void qemu_iovec_add(QEMUIOVector *qiov, void *base, size_t len);
+void qemu_iovec_copy(QEMUIOVector *dst, QEMUIOVector *src, uint64_t skip,
+ size_t size);
void qemu_iovec_concat(QEMUIOVector *dst, QEMUIOVector *src, size_t size);
void qemu_iovec_destroy(QEMUIOVector *qiov);
void qemu_iovec_reset(QEMUIOVector *qiov);
void qemu_iovec_to_buffer(QEMUIOVector *qiov, void *buf);
void qemu_iovec_from_buffer(QEMUIOVector *qiov, const void *buf, size_t count);
+void qemu_iovec_memset(QEMUIOVector *qiov, int c, size_t count);
struct Monitor;
typedef struct Monitor Monitor;
@@ -297,6 +315,4 @@ static inline uint8_t from_bcd(uint8_t val)
#include "module.h"
-#endif /* dyngen-exec.h hack */
-
#endif
diff --git a/qemu-config.c b/qemu-config.c
index 95abe61fab..32917cbda4 100644
--- a/qemu-config.c
+++ b/qemu-config.c
@@ -5,7 +5,7 @@
#include "sysemu.h"
#include "hw/qdev.h"
-QemuOptsList qemu_drive_opts = {
+static QemuOptsList qemu_drive_opts = {
.name = "drive",
.head = QTAILQ_HEAD_INITIALIZER(qemu_drive_opts.head),
.desc = {
@@ -80,11 +80,11 @@ QemuOptsList qemu_drive_opts = {
.name = "readonly",
.type = QEMU_OPT_BOOL,
},
- { /* end if list */ }
+ { /* end of list */ }
},
};
-QemuOptsList qemu_chardev_opts = {
+static QemuOptsList qemu_chardev_opts = {
.name = "chardev",
.implied_opt_name = "backend",
.head = QTAILQ_HEAD_INITIALIZER(qemu_chardev_opts.head),
@@ -147,11 +147,10 @@ QemuOptsList qemu_chardev_opts = {
.name = "signal",
.type = QEMU_OPT_BOOL,
},
- { /* end if list */ }
+ { /* end of list */ }
},
};
-#ifdef CONFIG_LINUX
QemuOptsList qemu_fsdev_opts = {
.name = "fsdev",
.implied_opt_name = "fstype",
@@ -170,9 +169,7 @@ QemuOptsList qemu_fsdev_opts = {
{ /*End of list */ }
},
};
-#endif
-#ifdef CONFIG_LINUX
QemuOptsList qemu_virtfs_opts = {
.name = "virtfs",
.implied_opt_name = "fstype",
@@ -195,9 +192,8 @@ QemuOptsList qemu_virtfs_opts = {
{ /*End of list */ }
},
};
-#endif
-QemuOptsList qemu_device_opts = {
+static QemuOptsList qemu_device_opts = {
.name = "device",
.implied_opt_name = "driver",
.head = QTAILQ_HEAD_INITIALIZER(qemu_device_opts.head),
@@ -207,11 +203,11 @@ QemuOptsList qemu_device_opts = {
* sanity checking will happen later
* when setting device properties
*/
- { /* end if list */ }
+ { /* end of list */ }
},
};
-QemuOptsList qemu_netdev_opts = {
+static QemuOptsList qemu_netdev_opts = {
.name = "netdev",
.implied_opt_name = "type",
.head = QTAILQ_HEAD_INITIALIZER(qemu_netdev_opts.head),
@@ -224,7 +220,7 @@ QemuOptsList qemu_netdev_opts = {
},
};
-QemuOptsList qemu_net_opts = {
+static QemuOptsList qemu_net_opts = {
.name = "net",
.implied_opt_name = "type",
.head = QTAILQ_HEAD_INITIALIZER(qemu_net_opts.head),
@@ -237,7 +233,7 @@ QemuOptsList qemu_net_opts = {
},
};
-QemuOptsList qemu_rtc_opts = {
+static QemuOptsList qemu_rtc_opts = {
.name = "rtc",
.head = QTAILQ_HEAD_INITIALIZER(qemu_rtc_opts.head),
.desc = {
@@ -247,17 +243,15 @@ QemuOptsList qemu_rtc_opts = {
},{
.name = "clock",
.type = QEMU_OPT_STRING,
-#ifdef TARGET_I386
},{
.name = "driftfix",
.type = QEMU_OPT_STRING,
-#endif
},
- { /* end if list */ }
+ { /* end of list */ }
},
};
-QemuOptsList qemu_global_opts = {
+static QemuOptsList qemu_global_opts = {
.name = "global",
.head = QTAILQ_HEAD_INITIALIZER(qemu_global_opts.head),
.desc = {
@@ -271,11 +265,11 @@ QemuOptsList qemu_global_opts = {
.name = "value",
.type = QEMU_OPT_STRING,
},
- { /* end if list */ }
+ { /* end of list */ }
},
};
-QemuOptsList qemu_mon_opts = {
+static QemuOptsList qemu_mon_opts = {
.name = "mon",
.implied_opt_name = "chardev",
.head = QTAILQ_HEAD_INITIALIZER(qemu_mon_opts.head),
@@ -289,12 +283,30 @@ QemuOptsList qemu_mon_opts = {
},{
.name = "default",
.type = QEMU_OPT_BOOL,
+ },{
+ .name = "pretty",
+ .type = QEMU_OPT_BOOL,
+ },
+ { /* end of list */ }
+ },
+};
+
+#ifdef CONFIG_SIMPLE_TRACE
+static QemuOptsList qemu_trace_opts = {
+ .name = "trace",
+ .implied_opt_name = "trace",
+ .head = QTAILQ_HEAD_INITIALIZER(qemu_trace_opts.head),
+ .desc = {
+ {
+ .name = "file",
+ .type = QEMU_OPT_STRING,
},
{ /* end if list */ }
},
};
+#endif
-QemuOptsList qemu_cpudef_opts = {
+static QemuOptsList qemu_cpudef_opts = {
.name = "cpudef",
.head = QTAILQ_HEAD_INITIALIZER(qemu_cpudef_opts.head),
.desc = {
@@ -342,7 +354,25 @@ QemuOptsList qemu_cpudef_opts = {
},
};
-static QemuOptsList *vm_config_groups[] = {
+QemuOptsList qemu_spice_opts = {
+ .name = "spice",
+ .head = QTAILQ_HEAD_INITIALIZER(qemu_spice_opts.head),
+ .desc = {
+ {
+ .name = "port",
+ .type = QEMU_OPT_NUMBER,
+ },{
+ .name = "password",
+ .type = QEMU_OPT_STRING,
+ },{
+ .name = "disable-ticketing",
+ .type = QEMU_OPT_BOOL,
+ },
+ { /* end if list */ }
+ },
+};
+
+static QemuOptsList *vm_config_groups[32] = {
&qemu_drive_opts,
&qemu_chardev_opts,
&qemu_device_opts,
@@ -352,6 +382,9 @@ static QemuOptsList *vm_config_groups[] = {
&qemu_global_opts,
&qemu_mon_opts,
&qemu_cpudef_opts,
+#ifdef CONFIG_SIMPLE_TRACE
+ &qemu_trace_opts,
+#endif
NULL,
};
@@ -374,6 +407,22 @@ QemuOptsList *qemu_find_opts(const char *group)
return find_list(vm_config_groups, group);
}
+void qemu_add_opts(QemuOptsList *list)
+{
+ int entries, i;
+
+ entries = ARRAY_SIZE(vm_config_groups);
+ entries--; /* keep list NULL terminated */
+ for (i = 0; i < entries; i++) {
+ if (vm_config_groups[i] == NULL) {
+ vm_config_groups[i] = list;
+ return;
+ }
+ }
+ fprintf(stderr, "ran out of space in vm_config_groups");
+ abort();
+}
+
int qemu_set_option(const char *str)
{
char group[64], id[64], arg[64];
diff --git a/qemu-config.h b/qemu-config.h
index dca69d454b..20d707fa1f 100644
--- a/qemu-config.h
+++ b/qemu-config.h
@@ -1,21 +1,12 @@
#ifndef QEMU_CONFIG_H
#define QEMU_CONFIG_H
-extern QemuOptsList qemu_drive_opts;
-extern QemuOptsList qemu_chardev_opts;
-#ifdef CONFIG_LINUX
extern QemuOptsList qemu_fsdev_opts;
extern QemuOptsList qemu_virtfs_opts;
-#endif
-extern QemuOptsList qemu_device_opts;
-extern QemuOptsList qemu_netdev_opts;
-extern QemuOptsList qemu_net_opts;
-extern QemuOptsList qemu_rtc_opts;
-extern QemuOptsList qemu_global_opts;
-extern QemuOptsList qemu_mon_opts;
-extern QemuOptsList qemu_cpudef_opts;
+extern QemuOptsList qemu_spice_opts;
QemuOptsList *qemu_find_opts(const char *group);
+void qemu_add_opts(QemuOptsList *list);
int qemu_set_option(const char *str);
int qemu_global_option(const char *str);
void qemu_add_globals(void);
diff --git a/qemu-doc.texi b/qemu-doc.texi
index e67bf44ff3..c3765293d3 100644
--- a/qemu-doc.texi
+++ b/qemu-doc.texi
@@ -620,6 +620,13 @@ qemu linux1.img -hdb nbd:unix:/tmp/my_socket
qemu linux2.img -hdb nbd:unix:/tmp/my_socket
@end example
+If the nbd-server uses named exports (since NBD 2.9.18), you must use the
+"exportname" option:
+@example
+qemu -cdrom nbd:localhost:exportname=debian-500-ppc-netinst
+qemu -cdrom nbd:localhost:exportname=openSUSE-11.1-ppc-netinst
+@end example
+
@node pcsys_network
@section Network emulation
@@ -706,6 +713,49 @@ Using the @option{-net socket} option, it is possible to make VLANs
that span several QEMU instances. See @ref{sec_invocation} to have a
basic example.
+@section Other Devices
+
+@subsection Inter-VM Shared Memory device
+
+With KVM enabled on a Linux host, a shared memory device is available. Guests
+map a POSIX shared memory region into the guest as a PCI device that enables
+zero-copy communication to the application level of the guests. The basic
+syntax is:
+
+@example
+qemu -device ivshmem,size=<size in format accepted by -m>[,shm=<shm name>]
+@end example
+
+If desired, interrupts can be sent between guest VMs accessing the same shared
+memory region. Interrupt support requires using a shared memory server and
+using a chardev socket to connect to it. The code for the shared memory server
+is qemu.git/contrib/ivshmem-server. An example syntax when using the shared
+memory server is:
+
+@example
+qemu -device ivshmem,size=<size in format accepted by -m>[,chardev=<id>]
+ [,msi=on][,ioeventfd=on][,vectors=n][,role=peer|master]
+qemu -chardev socket,path=<path>,id=<id>
+@end example
+
+When using the server, the guest will be assigned a VM ID (>=0) that allows guests
+using the same server to communicate via interrupts. Guests can read their
+VM ID from a device register (see example code). Since receiving the shared
+memory region from the server is asynchronous, there is a (small) chance the
+guest may boot before the shared memory is attached. To allow an application
+to ensure shared memory is attached, the VM ID register will return -1 (an
+invalid VM ID) until the memory is attached. Once the shared memory is
+attached, the VM ID will return the guest's valid VM ID. With these semantics,
+the guest application can check to ensure the shared memory is attached to the
+guest before proceeding.
+
+The @option{role} argument can be set to either master or peer and will affect
+how the shared memory is migrated. With @option{role=master}, the guest will
+copy the shared memory on migration to the destination host. With
+@option{role=peer}, the guest will not be able to migrate with the device attached.
+With the @option{peer} case, the device should be detached and then reattached
+after migration using the PCI hotplug support.
+
@node direct_linux_boot
@section Direct Linux Boot
@@ -2136,6 +2186,13 @@ Set the x86 elf interpreter prefix (default=/usr/local/qemu-i386)
Set the x86 stack size in bytes (default=524288)
@item -cpu model
Select CPU model (-cpu ? for list and additional feature selection)
+@item -ignore-environment
+Start with an empty environment. Without this option,
+the inital environment is a copy of the caller's environment.
+@item -E @var{var}=@var{value}
+Set environment @var{var} to @var{value}.
+@item -U @var{var}
+Remove @var{var} from the environment.
@item -B offset
Offset guest address by the specified number of bytes. This is useful when
the address region required by guest applications is reserved on the host.
@@ -2359,6 +2416,13 @@ Print the help
Set the library root path (default=/)
@item -s size
Set the stack size in bytes (default=524288)
+@item -ignore-environment
+Start with an empty environment. Without this option,
+the inital environment is a copy of the caller's environment.
+@item -E @var{var}=@var{value}
+Set environment @var{var} to @var{value}.
+@item -U @var{var}
+Remove @var{var} from the environment.
@item -bsd type
Set the type of the emulated BSD Operating system. Valid values are
FreeBSD, NetBSD and OpenBSD (default).
diff --git a/qemu-error.h b/qemu-error.h
index a45609f8e7..4d5c53700e 100644
--- a/qemu-error.h
+++ b/qemu-error.h
@@ -30,12 +30,11 @@ void loc_set_none(void);
void loc_set_cmdline(char **argv, int idx, int cnt);
void loc_set_file(const char *fname, int lno);
-void error_vprintf(const char *fmt, va_list ap);
-void error_printf(const char *fmt, ...) __attribute__ ((format(printf, 1, 2)));
-void error_printf_unless_qmp(const char *fmt, ...)
- __attribute__ ((format(printf, 1, 2)));
+void error_vprintf(const char *fmt, va_list ap) GCC_FMT_ATTR(1, 0);
+void error_printf(const char *fmt, ...) GCC_FMT_ATTR(1, 2);
+void error_printf_unless_qmp(const char *fmt, ...) GCC_FMT_ATTR(1, 2);
void error_print_loc(void);
void error_set_progname(const char *argv0);
-void error_report(const char *fmt, ...) __attribute__ ((format(printf, 1, 2)));
+void error_report(const char *fmt, ...) GCC_FMT_ATTR(1, 2);
#endif
diff --git a/qemu-img-cmds.hx b/qemu-img-cmds.hx
index c4cf3e7542..6d3e5f8e69 100644
--- a/qemu-img-cmds.hx
+++ b/qemu-img-cmds.hx
@@ -54,6 +54,6 @@ ETEXI
DEF("resize", img_resize,
"resize filename [+ | -]size")
STEXI
-@item rebase @var{filename} [+ | -]@var{size}
+@item resize @var{filename} [+ | -]@var{size}
@end table
ETEXI
diff --git a/qemu-img.c b/qemu-img.c
index e300f911cb..578b8ebe8c 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -39,7 +39,7 @@ typedef struct img_cmd_t {
/* Default to cache=writeback as data integrity is not important for qemu-tcg. */
#define BDRV_O_FLAGS BDRV_O_CACHE_WB
-static void error(const char *fmt, ...)
+static void GCC_FMT_ATTR(1, 2) error(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
@@ -783,7 +783,8 @@ static int img_convert(int argc, char **argv)
goto out;
}
- out_bs = bdrv_new_open(out_filename, out_fmt, BDRV_O_FLAGS | BDRV_O_RDWR);
+ out_bs = bdrv_new_open(out_filename, out_fmt,
+ BDRV_O_FLAGS | BDRV_O_RDWR | BDRV_O_NO_FLUSH);
if (!out_bs) {
ret = -1;
goto out;
@@ -1286,7 +1287,7 @@ static int img_rebase(int argc, char **argv)
}
bs_new_backing = bdrv_new("new_backing");
- ret = bdrv_open(bs_new_backing, out_baseimg, BDRV_O_FLAGS | BDRV_O_RDWR,
+ ret = bdrv_open(bs_new_backing, out_baseimg, BDRV_O_FLAGS,
new_backing_drv);
if (ret) {
error("Could not open new backing file '%s'", out_baseimg);
diff --git a/qemu-io.c b/qemu-io.c
index 7c6120b6ac..b4e5cc8fe6 100644
--- a/qemu-io.c
+++ b/qemu-io.c
@@ -61,7 +61,7 @@ static void *qemu_io_alloc(size_t len, int pattern)
if (misalign)
len += MISALIGN_OFFSET;
- buf = qemu_memalign(512, len);
+ buf = qemu_blockalign(bs, len);
memset(buf, pattern, len);
if (misalign)
buf += MISALIGN_OFFSET;
@@ -135,7 +135,7 @@ create_iovec(QEMUIOVector *qiov, char **argv, int nr_iov, int pattern)
for (i = 0; i < nr_iov; i++) {
char *arg = argv[i];
- uint64_t len;
+ int64_t len;
len = cvtnum(arg);
if (len < 0) {
@@ -144,7 +144,7 @@ create_iovec(QEMUIOVector *qiov, char **argv, int nr_iov, int pattern)
}
/* should be SIZE_T_MAX, but that doesn't exist */
- if (len > UINT_MAX) {
+ if (len > INT_MAX) {
printf("too large length argument -- %s\n", arg);
goto fail;
}
@@ -1427,11 +1427,8 @@ alloc_f(int argc, char **argv)
cvtstr(offset, s1, sizeof(s1));
- if (nb_sectors == 1)
- printf("sector allocated at offset %s\n", s1);
- else
- printf("%d/%d sectors allocated at offset %s\n",
- sum_alloc, nb_sectors, s1);
+ printf("%d/%d sectors allocated at offset %s\n",
+ sum_alloc, nb_sectors, s1);
return 0;
}
diff --git a/qemu-malloc.c b/qemu-malloc.c
index 36b0b3641e..ecffb676e2 100644
--- a/qemu-malloc.c
+++ b/qemu-malloc.c
@@ -22,6 +22,7 @@
* THE SOFTWARE.
*/
#include "qemu-common.h"
+#include "trace.h"
#include <stdlib.h>
static void *oom_check(void *ptr)
@@ -34,6 +35,7 @@ static void *oom_check(void *ptr)
void qemu_free(void *ptr)
{
+ trace_qemu_free(ptr);
free(ptr);
}
@@ -48,18 +50,24 @@ static int allow_zero_malloc(void)
void *qemu_malloc(size_t size)
{
+ void *ptr;
if (!size && !allow_zero_malloc()) {
abort();
}
- return oom_check(malloc(size ? size : 1));
+ ptr = oom_check(malloc(size ? size : 1));
+ trace_qemu_malloc(size, ptr);
+ return ptr;
}
void *qemu_realloc(void *ptr, size_t size)
{
+ void *newptr;
if (!size && !allow_zero_malloc()) {
abort();
}
- return oom_check(realloc(ptr, size ? size : 1));
+ newptr = oom_check(realloc(ptr, size ? size : 1));
+ trace_qemu_realloc(ptr, size, newptr);
+ return newptr;
}
void *qemu_mallocz(size_t size)
diff --git a/qemu-nbd.c b/qemu-nbd.c
index 4e607cfb61..99f1d22884 100644
--- a/qemu-nbd.c
+++ b/qemu-nbd.c
@@ -44,7 +44,7 @@ static void usage(const char *name)
"Usage: %s [OPTIONS] FILE\n"
"QEMU Disk Network Block Device Server\n"
"\n"
-" -p, --port=PORT port to listen on (default `1024')\n"
+" -p, --port=PORT port to listen on (default `%d')\n"
" -o, --offset=OFFSET offset into the image\n"
" -b, --bind=IFACE interface to bind to (default `0.0.0.0')\n"
" -k, --socket=PATH path to the unix socket\n"
@@ -62,7 +62,7 @@ static void usage(const char *name)
" -V, --version output version information and exit\n"
"\n"
"Report bugs to <anthony@codemonkey.ws>\n"
- , name, "DEVICE");
+ , name, NBD_DEFAULT_PORT, "DEVICE");
}
static void version(const char *name)
@@ -188,7 +188,7 @@ int main(int argc, char **argv)
bool readonly = false;
bool disconnect = false;
const char *bindto = "0.0.0.0";
- int port = 1024;
+ int port = NBD_DEFAULT_PORT;
struct sockaddr_in addr;
socklen_t addr_len = sizeof(addr);
off_t fd_size;
@@ -230,6 +230,7 @@ int main(int argc, char **argv)
int nb_fds = 0;
int max_fd;
int persistent = 0;
+ uint32_t nbdflags;
while ((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) {
switch (ch) {
@@ -398,7 +399,8 @@ int main(int argc, char **argv)
goto out;
}
- ret = nbd_receive_negotiate(sock, &size, &blocksize);
+ ret = nbd_receive_negotiate(sock, NULL, &nbdflags,
+ &size, &blocksize);
if (ret == -1) {
ret = 1;
goto out;
@@ -417,7 +419,10 @@ int main(int argc, char **argv)
show_parts(device);
- nbd_client(fd, sock);
+ ret = nbd_client(fd);
+ if (ret) {
+ ret = 1;
+ }
close(fd);
out:
kill(pid, SIGTERM);
@@ -441,7 +446,7 @@ int main(int argc, char **argv)
max_fd = sharing_fds[0];
nb_fds++;
- data = qemu_memalign(512, NBD_BUFFER_SIZE);
+ data = qemu_blockalign(bs, NBD_BUFFER_SIZE);
if (data == NULL)
errx(EXIT_FAILURE, "Cannot allocate data buffer");
diff --git a/qemu-options.hx b/qemu-options.hx
index d1d22726b8..718d47a2a3 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -118,7 +118,7 @@ ETEXI
DEF("drive", HAS_ARG, QEMU_OPTION_drive,
"-drive [file=file][,if=type][,bus=n][,unit=m][,media=d][,index=i]\n"
" [,cyls=c,heads=h,secs=s[,trans=t]][,snapshot=on|off]\n"
- " [,cache=writethrough|writeback|unsafe|none][,format=f]\n"
+ " [,cache=writethrough|writeback|none|unsafe][,format=f]\n"
" [,serial=s][,addr=A][,id=name][,aio=threads|native]\n"
" [,readonly=on|off]\n"
" use 'file' as a drive image\n", QEMU_ARCH_ALL)
@@ -482,11 +482,10 @@ possible drivers and properties, use @code{-device ?} and
@code{-device @var{driver},?}.
ETEXI
-#ifdef CONFIG_LINUX
DEFHEADING(File system options:)
DEF("fsdev", HAS_ARG, QEMU_OPTION_fsdev,
- "-fsdev local,id=id,path=path,security_model=[mapped|passthrough]\n",
+ "-fsdev local,id=id,path=path,security_model=[mapped|passthrough|none]\n",
QEMU_ARCH_ALL)
STEXI
@@ -515,13 +514,11 @@ Create a file-system-"device" for local-filesystem.
@end table
ETEXI
-#endif
-#ifdef CONFIG_LINUX
DEFHEADING(Virtual File system pass-through options:)
DEF("virtfs", HAS_ARG, QEMU_OPTION_virtfs,
- "-virtfs local,path=path,mount_tag=tag,security_model=[mapped|passthrough]\n",
+ "-virtfs local,path=path,mount_tag=tag,security_model=[mapped|passthrough|none]\n",
QEMU_ARCH_ALL)
STEXI
@@ -554,7 +551,6 @@ Create a Virtual file-system-pass through for local-filesystem.
@end table
ETEXI
-#endif
DEFHEADING()
@@ -674,6 +670,27 @@ STEXI
Enable SDL.
ETEXI
+DEF("spice", HAS_ARG, QEMU_OPTION_spice,
+ "-spice <args> enable spice\n", QEMU_ARCH_ALL)
+STEXI
+@item -spice @var{option}[,@var{option}[,...]]
+@findex -spice
+Enable the spice remote desktop protocol. Valid options are
+
+@table @option
+
+@item port=<nr>
+Set the TCP port spice is listening on.
+
+@item password=<secret>
+Set the password you need to authenticate.
+
+@item disable-ticketing
+Allow client connects without authentication.
+
+@end table
+ETEXI
+
DEF("portrait", 0, QEMU_OPTION_portrait,
"-portrait rotate graphical output 90 deg left (only PXA LCD)\n",
QEMU_ARCH_ALL)
@@ -835,6 +852,13 @@ empty, with a @code{deny} policy. Thus no one will be allowed to
use the VNC server until the ACLs have been loaded. This can be
achieved using the @code{acl} monitor command.
+@item lossy
+
+Enable lossy compression methods (gradient, JPEG, ...). If this
+option is set, VNC client may receive lossy framebuffer updates
+depending on its encoding settings. Enabling this option can save
+a lot of bandwidth at the expense of quality.
+
@end table
ETEXI
@@ -1271,7 +1295,7 @@ DEF("chardev", HAS_ARG, QEMU_OPTION_chardev,
"-chardev serial,id=id,path=path[,mux=on|off]\n"
#else
"-chardev pty,id=id[,mux=on|off]\n"
- "-chardev stdio,id=id[,mux=on|off]\n"
+ "-chardev stdio,id=id[,mux=on|off][,signal=on|off]\n"
#endif
#ifdef CONFIG_BRLAPI
"-chardev braille,id=id[,mux=on|off]\n"
@@ -1452,10 +1476,14 @@ not take any options.
@option{pty} is not available on Windows hosts.
-@item -chardev stdio ,id=@var{id}
+@item -chardev stdio ,id=@var{id} [,signal=on|off]
Connect to standard input and standard output of the qemu process.
-@option{stdio} does not take any options. @option{stdio} is not available on
-Windows hosts.
+
+@option{signal} controls if signals are enabled on the terminal, that includes
+exiting QEMU with the key sequence @key{Control-c}. This option is enabled by
+default, use @option{signal=off} to disable it.
+
+@option{stdio} is not available on Windows hosts.
@item -chardev braille ,id=@var{id}
@@ -2223,6 +2251,17 @@ Normally QEMU loads a configuration file from @var{sysconfdir}/qemu.conf and
@var{sysconfdir}/target-@var{ARCH}.conf on startup. The @code{-nodefconfig}
option will prevent QEMU from loading these configuration files at startup.
ETEXI
+#ifdef CONFIG_SIMPLE_TRACE
+DEF("trace", HAS_ARG, QEMU_OPTION_trace,
+ "-trace\n"
+ " Specify a trace file to log traces to\n",
+ QEMU_ARCH_ALL)
+STEXI
+@item -trace
+@findex -trace
+Specify a trace file to log output traces to.
+ETEXI
+#endif
HXCOMM This is the last statement. Insert new options before this line!
STEXI
diff --git a/qemu-os-win32.h b/qemu-os-win32.h
index 2ff9f45a6c..c63778d2fc 100644
--- a/qemu-os-win32.h
+++ b/qemu-os-win32.h
@@ -45,8 +45,7 @@ void os_host_main_loop_wait(int *timeout);
static inline void os_setup_signal_handling(void) {}
static inline void os_daemonize(void) {}
static inline void os_setup_post(void) {}
-/* Win32 doesn't support line-buffering and requires size >= 2 */
-static inline void os_set_line_buffering(void) {}
+void os_set_line_buffering(void);
static inline void os_set_proc_name(const char *dummy) {}
#if !defined(EPROTONOSUPPORT)
diff --git a/qemu-thread.c b/qemu-thread.c
index faf406142d..fbc78fef4b 100644
--- a/qemu-thread.c
+++ b/qemu-thread.c
@@ -34,6 +34,15 @@ void qemu_mutex_init(QemuMutex *mutex)
error_exit(err, __func__);
}
+void qemu_mutex_destroy(QemuMutex *mutex)
+{
+ int err;
+
+ err = pthread_mutex_destroy(&mutex->lock);
+ if (err)
+ error_exit(err, __func__);
+}
+
void qemu_mutex_lock(QemuMutex *mutex)
{
int err;
@@ -90,6 +99,15 @@ void qemu_cond_init(QemuCond *cond)
error_exit(err, __func__);
}
+void qemu_cond_destroy(QemuCond *cond)
+{
+ int err;
+
+ err = pthread_cond_destroy(&cond->cond);
+ if (err)
+ error_exit(err, __func__);
+}
+
void qemu_cond_signal(QemuCond *cond)
{
int err;
@@ -168,3 +186,7 @@ int qemu_thread_equal(QemuThread *thread1, QemuThread *thread2)
return pthread_equal(thread1->thread, thread2->thread);
}
+void qemu_thread_exit(void *retval)
+{
+ pthread_exit(retval);
+}
diff --git a/qemu-thread.h b/qemu-thread.h
index 5ef4a3aed5..19bb30c940 100644
--- a/qemu-thread.h
+++ b/qemu-thread.h
@@ -20,12 +20,14 @@ typedef struct QemuCond QemuCond;
typedef struct QemuThread QemuThread;
void qemu_mutex_init(QemuMutex *mutex);
+void qemu_mutex_destroy(QemuMutex *mutex);
void qemu_mutex_lock(QemuMutex *mutex);
int qemu_mutex_trylock(QemuMutex *mutex);
int qemu_mutex_timedlock(QemuMutex *mutex, uint64_t msecs);
void qemu_mutex_unlock(QemuMutex *mutex);
void qemu_cond_init(QemuCond *cond);
+void qemu_cond_destroy(QemuCond *cond);
void qemu_cond_signal(QemuCond *cond);
void qemu_cond_broadcast(QemuCond *cond);
void qemu_cond_wait(QemuCond *cond, QemuMutex *mutex);
@@ -37,4 +39,6 @@ void qemu_thread_create(QemuThread *thread,
void qemu_thread_signal(QemuThread *thread, int sig);
void qemu_thread_self(QemuThread *thread);
int qemu_thread_equal(QemuThread *thread1, QemuThread *thread2);
+void qemu_thread_exit(void *retval);
+
#endif
diff --git a/qemu-timer-common.c b/qemu-timer-common.c
new file mode 100644
index 0000000000..fff43996d8
--- /dev/null
+++ b/qemu-timer-common.c
@@ -0,0 +1,62 @@
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu-timer.h"
+
+/***********************************************************/
+/* real time host monotonic timer */
+
+#ifdef _WIN32
+
+int64_t clock_freq;
+
+static void __attribute__((constructor)) init_get_clock(void)
+{
+ LARGE_INTEGER freq;
+ int ret;
+ ret = QueryPerformanceFrequency(&freq);
+ if (ret == 0) {
+ fprintf(stderr, "Could not calibrate ticks\n");
+ exit(1);
+ }
+ clock_freq = freq.QuadPart;
+}
+
+#else
+
+int use_rt_clock;
+
+static void __attribute__((constructor)) init_get_clock(void)
+{
+ use_rt_clock = 0;
+#if defined(__linux__) || (defined(__FreeBSD__) && __FreeBSD_version >= 500000) \
+ || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
+ {
+ struct timespec ts;
+ if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
+ use_rt_clock = 1;
+ }
+ }
+#endif
+}
+#endif
diff --git a/qemu-timer.c b/qemu-timer.c
index bc5f207bb6..95814af798 100644
--- a/qemu-timer.c
+++ b/qemu-timer.c
@@ -64,78 +64,6 @@ int64_t qemu_icount_bias;
static QEMUTimer *icount_rt_timer;
static QEMUTimer *icount_vm_timer;
-
-/***********************************************************/
-/* real time host monotonic timer */
-
-
-static int64_t get_clock_realtime(void)
-{
- struct timeval tv;
-
- gettimeofday(&tv, NULL);
- return tv.tv_sec * 1000000000LL + (tv.tv_usec * 1000);
-}
-
-#ifdef WIN32
-
-static int64_t clock_freq;
-
-static void init_get_clock(void)
-{
- LARGE_INTEGER freq;
- int ret;
- ret = QueryPerformanceFrequency(&freq);
- if (ret == 0) {
- fprintf(stderr, "Could not calibrate ticks\n");
- exit(1);
- }
- clock_freq = freq.QuadPart;
-}
-
-static int64_t get_clock(void)
-{
- LARGE_INTEGER ti;
- QueryPerformanceCounter(&ti);
- return muldiv64(ti.QuadPart, get_ticks_per_sec(), clock_freq);
-}
-
-#else
-
-static int use_rt_clock;
-
-static void init_get_clock(void)
-{
- use_rt_clock = 0;
-#if defined(__linux__) || (defined(__FreeBSD__) && __FreeBSD_version >= 500000) \
- || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
- {
- struct timespec ts;
- if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
- use_rt_clock = 1;
- }
- }
-#endif
-}
-
-static int64_t get_clock(void)
-{
-#if defined(__linux__) || (defined(__FreeBSD__) && __FreeBSD_version >= 500000) \
- || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
- if (use_rt_clock) {
- struct timespec ts;
- clock_gettime(CLOCK_MONOTONIC, &ts);
- return ts.tv_sec * 1000000000LL + ts.tv_nsec;
- } else
-#endif
- {
- /* XXX: using gettimeofday leads to problems if the date
- changes, so it should be avoided. */
- return get_clock_realtime();
- }
-}
-#endif
-
/***********************************************************/
/* guest cycle counter */
@@ -614,7 +542,6 @@ int64_t qemu_get_clock_ns(QEMUClock *clock)
void init_clocks(void)
{
- init_get_clock();
rt_clock = qemu_new_clock(QEMU_CLOCK_REALTIME);
vm_clock = qemu_new_clock(QEMU_CLOCK_VIRTUAL);
host_clock = qemu_new_clock(QEMU_CLOCK_HOST);
diff --git a/qemu-timer.h b/qemu-timer.h
index 1494f79406..299e387768 100644
--- a/qemu-timer.h
+++ b/qemu-timer.h
@@ -2,6 +2,13 @@
#define QEMU_TIMER_H
#include "qemu-common.h"
+#include <time.h>
+#include <sys/time.h>
+
+#ifdef _WIN32
+#include <windows.h>
+#include <mmsystem.h>
+#endif
/* timers */
@@ -52,6 +59,73 @@ static inline int64_t get_ticks_per_sec(void)
return 1000000000LL;
}
+/* compute with 96 bit intermediate result: (a*b)/c */
+static inline uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c)
+{
+ union {
+ uint64_t ll;
+ struct {
+#ifdef HOST_WORDS_BIGENDIAN
+ uint32_t high, low;
+#else
+ uint32_t low, high;
+#endif
+ } l;
+ } u, res;
+ uint64_t rl, rh;
+
+ u.ll = a;
+ rl = (uint64_t)u.l.low * (uint64_t)b;
+ rh = (uint64_t)u.l.high * (uint64_t)b;
+ rh += (rl >> 32);
+ res.l.high = rh / c;
+ res.l.low = (((rh % c) << 32) + (rl & 0xffffffff)) / c;
+ return res.ll;
+}
+
+/* real time host monotonic timer */
+static inline int64_t get_clock_realtime(void)
+{
+ struct timeval tv;
+
+ gettimeofday(&tv, NULL);
+ return tv.tv_sec * 1000000000LL + (tv.tv_usec * 1000);
+}
+
+/* Warning: don't insert tracepoints into these functions, they are
+ also used by simpletrace backend and tracepoints would cause
+ an infinite recursion! */
+#ifdef _WIN32
+extern int64_t clock_freq;
+
+static inline int64_t get_clock(void)
+{
+ LARGE_INTEGER ti;
+ QueryPerformanceCounter(&ti);
+ return muldiv64(ti.QuadPart, get_ticks_per_sec(), clock_freq);
+}
+
+#else
+
+extern int use_rt_clock;
+
+static inline int64_t get_clock(void)
+{
+#if defined(__linux__) || (defined(__FreeBSD__) && __FreeBSD_version >= 500000) \
+ || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
+ if (use_rt_clock) {
+ struct timespec ts;
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ return ts.tv_sec * 1000000000LL + ts.tv_nsec;
+ } else
+#endif
+ {
+ /* XXX: using gettimeofday leads to problems if the date
+ changes, so it should be avoided. */
+ return get_clock_realtime();
+ }
+}
+#endif
void qemu_get_timer(QEMUFile *f, QEMUTimer *ts);
void qemu_put_timer(QEMUFile *f, QEMUTimer *ts);
diff --git a/qemu-tool.c b/qemu-tool.c
index b39af86e01..9ccca655ef 100644
--- a/qemu-tool.c
+++ b/qemu-tool.c
@@ -110,10 +110,3 @@ int qemu_set_fd_handler2(int fd,
{
return 0;
}
-
-int64_t qemu_get_clock(QEMUClock *clock)
-{
- qemu_timeval tv;
- qemu_gettimeofday(&tv);
- return (tv.tv_sec * 1000000000LL + (tv.tv_usec * 1000)) / 1000000;
-}
diff --git a/qemu_socket.h b/qemu_socket.h
index 164ae3eb4e..897a8ae50b 100644
--- a/qemu_socket.h
+++ b/qemu_socket.h
@@ -17,6 +17,7 @@ int inet_aton(const char *cp, struct in_addr *ia);
#else
+#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
diff --git a/qerror.c b/qerror.c
index 2f6f59061f..ac2cdafa65 100644
--- a/qerror.c
+++ b/qerror.c
@@ -141,6 +141,10 @@ static const QErrorStringTable qerror_table[] = {
.desc = "Using KVM without %(capability), %(feature) unavailable",
},
{
+ .error_fmt = QERR_MIGRATION_EXPECTED,
+ .desc = "An incoming migration is expected before this command can be executed",
+ },
+ {
.error_fmt = QERR_MISSING_PARAMETER,
.desc = "Parameter '%(name)' is missing",
},
@@ -214,7 +218,8 @@ QError *qerror_new(void)
return qerr;
}
-static void qerror_abort(const QError *qerr, const char *fmt, ...)
+static void GCC_FMT_ATTR(2, 3) qerror_abort(const QError *qerr,
+ const char *fmt, ...)
{
va_list ap;
@@ -229,7 +234,8 @@ static void qerror_abort(const QError *qerr, const char *fmt, ...)
abort();
}
-static void qerror_set_data(QError *qerr, const char *fmt, va_list *va)
+static void GCC_FMT_ATTR(2, 0) qerror_set_data(QError *qerr,
+ const char *fmt, va_list *va)
{
QObject *obj;
diff --git a/qerror.h b/qerror.h
index 9ad00b4b82..943a24b4e5 100644
--- a/qerror.h
+++ b/qerror.h
@@ -34,12 +34,11 @@ typedef struct QError {
QError *qerror_new(void);
QError *qerror_from_info(const char *file, int linenr, const char *func,
- const char *fmt, va_list *va);
+ const char *fmt, va_list *va) GCC_FMT_ATTR(4, 0);
QString *qerror_human(const QError *qerror);
void qerror_print(QError *qerror);
void qerror_report_internal(const char *file, int linenr, const char *func,
- const char *fmt, ...)
- __attribute__ ((format(printf, 4, 5)));
+ const char *fmt, ...) GCC_FMT_ATTR(4, 5);
#define qerror_report(fmt, ...) \
qerror_report_internal(__FILE__, __LINE__, __func__, fmt, ## __VA_ARGS__)
QError *qobject_to_qerror(const QObject *obj);
@@ -121,6 +120,9 @@ QError *qobject_to_qerror(const QObject *obj);
#define QERR_KVM_MISSING_CAP \
"{ 'class': 'KVMMissingCap', 'data': { 'capability': %s, 'feature': %s } }"
+#define QERR_MIGRATION_EXPECTED \
+ "{ 'class': 'MigrationExpected', 'data': {} }"
+
#define QERR_MISSING_PARAMETER \
"{ 'class': 'MissingParameter', 'data': { 'name': %s } }"
diff --git a/qjson.c b/qjson.c
index e4ee433760..f9c8e77516 100644
--- a/qjson.c
+++ b/qjson.c
@@ -72,43 +72,57 @@ QObject *qobject_from_jsonf(const char *string, ...)
typedef struct ToJsonIterState
{
+ int indent;
+ int pretty;
int count;
QString *str;
} ToJsonIterState;
-static void to_json(const QObject *obj, QString *str);
+static void to_json(const QObject *obj, QString *str, int pretty, int indent);
static void to_json_dict_iter(const char *key, QObject *obj, void *opaque)
{
ToJsonIterState *s = opaque;
QString *qkey;
+ int j;
- if (s->count) {
+ if (s->count)
qstring_append(s->str, ", ");
+
+ if (s->pretty) {
+ qstring_append(s->str, "\n");
+ for (j = 0 ; j < s->indent ; j++)
+ qstring_append(s->str, " ");
}
qkey = qstring_from_str(key);
- to_json(QOBJECT(qkey), s->str);
+ to_json(QOBJECT(qkey), s->str, s->pretty, s->indent);
QDECREF(qkey);
qstring_append(s->str, ": ");
- to_json(obj, s->str);
+ to_json(obj, s->str, s->pretty, s->indent);
s->count++;
}
static void to_json_list_iter(QObject *obj, void *opaque)
{
ToJsonIterState *s = opaque;
+ int j;
- if (s->count) {
+ if (s->count)
qstring_append(s->str, ", ");
+
+ if (s->pretty) {
+ qstring_append(s->str, "\n");
+ for (j = 0 ; j < s->indent ; j++)
+ qstring_append(s->str, " ");
}
- to_json(obj, s->str);
+ to_json(obj, s->str, s->pretty, s->indent);
s->count++;
}
-static void to_json(const QObject *obj, QString *str)
+static void to_json(const QObject *obj, QString *str, int pretty, int indent)
{
switch (qobject_type(obj)) {
case QTYPE_QINT: {
@@ -193,8 +207,16 @@ static void to_json(const QObject *obj, QString *str)
s.count = 0;
s.str = str;
+ s.indent = indent + 1;
+ s.pretty = pretty;
qstring_append(str, "{");
qdict_iter(val, to_json_dict_iter, &s);
+ if (pretty) {
+ int j;
+ qstring_append(str, "\n");
+ for (j = 0 ; j < indent ; j++)
+ qstring_append(str, " ");
+ }
qstring_append(str, "}");
break;
}
@@ -204,8 +226,16 @@ static void to_json(const QObject *obj, QString *str)
s.count = 0;
s.str = str;
+ s.indent = indent + 1;
+ s.pretty = pretty;
qstring_append(str, "[");
qlist_iter(val, (void *)to_json_list_iter, &s);
+ if (pretty) {
+ int j;
+ qstring_append(str, "\n");
+ for (j = 0 ; j < indent ; j++)
+ qstring_append(str, " ");
+ }
qstring_append(str, "]");
break;
}
@@ -249,7 +279,16 @@ QString *qobject_to_json(const QObject *obj)
{
QString *str = qstring_new();
- to_json(obj, str);
+ to_json(obj, str, 0, 0);
+
+ return str;
+}
+
+QString *qobject_to_json_pretty(const QObject *obj)
+{
+ QString *str = qstring_new();
+
+ to_json(obj, str, 1, 0);
return str;
}
diff --git a/qjson.h b/qjson.h
index 7afec2eec1..65b10ea369 100644
--- a/qjson.h
+++ b/qjson.h
@@ -18,11 +18,11 @@
#include "qobject.h"
#include "qstring.h"
-QObject *qobject_from_json(const char *string);
-QObject *qobject_from_jsonf(const char *string, ...)
- __attribute__((__format__ (__printf__, 1, 2)));
-QObject *qobject_from_jsonv(const char *string, va_list *ap);
+QObject *qobject_from_json(const char *string) GCC_FMT_ATTR(1, 0);
+QObject *qobject_from_jsonf(const char *string, ...) GCC_FMT_ATTR(1, 2);
+QObject *qobject_from_jsonv(const char *string, va_list *ap) GCC_FMT_ATTR(1, 0);
QString *qobject_to_json(const QObject *obj);
+QString *qobject_to_json_pretty(const QObject *obj);
#endif /* QJSON_H */
diff --git a/qemu-monitor.hx b/qmp-commands.hx
index 2af3de6c22..793cf1c0f9 100644
--- a/qemu-monitor.hx
+++ b/qmp-commands.hx
@@ -1,11 +1,6 @@
-HXCOMM Use DEFHEADING() to define headings in both help text and texi
-HXCOMM Text between STEXI and ETEXI are copied to texi version and
-HXCOMM discarded from C version
+HXCOMM QMP dispatch table and documentation
HXCOMM Text between SQMP and EQMP is copied to the QMP documention file and
HXCOMM does not show up in the other formats.
-HXCOMM DEF(command, args, callback, arg_string, help) is used to construct
-HXCOMM monitor commands
-HXCOMM HXCOMM can be used for comments, discarded from both texi and C
SQMP
QMP Supported Commands
@@ -35,48 +30,38 @@ information on the Server command and response formats.
NOTE: This document is temporary and will be replaced soon.
-1. Regular Commands
-===================
+1. Stability Considerations
+===========================
-Server's responses in the examples below are always a success response, please
-refer to the QMP specification for more details on error responses.
+The current QMP command set (described in this file) may be useful for a
+number of use cases, however it's limited and several commands have bad
+defined semantics, specially with regard to command completion.
-EQMP
+These problems are going to be solved incrementally in the next QEMU releases
+and we're going to establish a deprecation policy for badly defined commands.
-STEXI
-@table @option
-ETEXI
+If you're planning to adopt QMP, please observe the following:
- {
- .name = "help|?",
- .args_type = "name:s?",
- .params = "[cmd]",
- .help = "show the help",
- .mhandler.cmd = do_help_cmd,
- },
+ 1. The deprecation policy will take efect and be documented soon, please
+ check the documentation of each used command as soon as a new release of
+ QEMU is available
-STEXI
-@item help or ? [@var{cmd}]
-@findex help
-Show the help for all commands or just for command @var{cmd}.
-ETEXI
+ 2. DO NOT rely on anything which is not explicit documented
- {
- .name = "commit",
- .args_type = "device:B",
- .params = "device|all",
- .help = "commit changes to the disk images (if -snapshot is used) or backing files",
- .mhandler.cmd = do_commit,
- },
+ 3. Errors, in special, are not documented. Applications should NOT check
+ for specific errors classes or data (it's strongly recommended to only
+ check for the "error" key)
+
+2. Regular Commands
+===================
-STEXI
-@item commit
-@findex commit
-Commit changes to the disk images (if -snapshot is used) or backing files.
-ETEXI
+Server's responses in the examples below are always a success response, please
+refer to the QMP specification for more details on error responses.
+
+EQMP
{
- .name = "q|quit",
+ .name = "quit",
.args_type = "",
.params = "",
.help = "quit the emulator",
@@ -84,11 +69,6 @@ ETEXI
.mhandler.cmd_new = do_quit,
},
-STEXI
-@item q or quit
-@findex quit
-Quit the emulator.
-ETEXI
SQMP
quit
----
@@ -113,11 +93,6 @@ EQMP
.mhandler.cmd_new = do_eject,
},
-STEXI
-@item eject [-f] @var{device}
-@findex eject
-Eject a removable medium (use -f to force it).
-ETEXI
SQMP
eject
-----
@@ -147,43 +122,6 @@ EQMP
.mhandler.cmd_new = do_change,
},
-STEXI
-@item change @var{device} @var{setting}
-@findex change
-
-Change the configuration of a device.
-
-@table @option
-@item change @var{diskdevice} @var{filename} [@var{format}]
-Change the medium for a removable disk device to point to @var{filename}. eg
-
-@example
-(qemu) change ide1-cd0 /path/to/some.iso
-@end example
-
-@var{format} is optional.
-
-@item change vnc @var{display},@var{options}
-Change the configuration of the VNC server. The valid syntax for @var{display}
-and @var{options} are described at @ref{sec_invocation}. eg
-
-@example
-(qemu) change vnc localhost:1
-@end example
-
-@item change vnc password [@var{password}]
-
-Change the password associated with the VNC server. If the new password is not
-supplied, the monitor will prompt for it to be entered. VNC passwords are only
-significant up to 8 letters. eg
-
-@example
-(qemu) change vnc password
-Password: ********
-@end example
-
-@end table
-ETEXI
SQMP
change
------
@@ -223,11 +161,6 @@ EQMP
.mhandler.cmd_new = do_screen_dump,
},
-STEXI
-@item screendump @var{filename}
-@findex screendump
-Save screen into PPM image @var{filename}.
-ETEXI
SQMP
screendump
----------
@@ -246,95 +179,6 @@ Example:
EQMP
{
- .name = "logfile",
- .args_type = "filename:F",
- .params = "filename",
- .help = "output logs to 'filename'",
- .mhandler.cmd = do_logfile,
- },
-
-STEXI
-@item logfile @var{filename}
-@findex logfile
-Output logs to @var{filename}.
-ETEXI
-
- {
- .name = "log",
- .args_type = "items:s",
- .params = "item1[,...]",
- .help = "activate logging of the specified items to '/tmp/qemu.log'",
- .mhandler.cmd = do_log,
- },
-
-STEXI
-@item log @var{item1}[,...]
-@findex log
-Activate logging of the specified items to @file{/tmp/qemu.log}.
-ETEXI
-
- {
- .name = "savevm",
- .args_type = "name:s?",
- .params = "[tag|id]",
- .help = "save a VM snapshot. If no tag or id are provided, a new snapshot is created",
- .mhandler.cmd = do_savevm,
- },
-
-STEXI
-@item savevm [@var{tag}|@var{id}]
-@findex savevm
-Create a snapshot of the whole virtual machine. If @var{tag} is
-provided, it is used as human readable identifier. If there is already
-a snapshot with the same tag or ID, it is replaced. More info at
-@ref{vm_snapshots}.
-ETEXI
-
- {
- .name = "loadvm",
- .args_type = "name:s",
- .params = "tag|id",
- .help = "restore a VM snapshot from its tag or id",
- .mhandler.cmd = do_loadvm,
- },
-
-STEXI
-@item loadvm @var{tag}|@var{id}
-@findex loadvm
-Set the whole virtual machine to the snapshot identified by the tag
-@var{tag} or the unique snapshot ID @var{id}.
-ETEXI
-
- {
- .name = "delvm",
- .args_type = "name:s",
- .params = "tag|id",
- .help = "delete a VM snapshot from its tag or id",
- .mhandler.cmd = do_delvm,
- },
-
-STEXI
-@item delvm @var{tag}|@var{id}
-@findex delvm
-Delete the snapshot identified by @var{tag} or @var{id}.
-ETEXI
-
- {
- .name = "singlestep",
- .args_type = "option:s?",
- .params = "[on|off]",
- .help = "run emulation in singlestep mode or switch to normal mode",
- .mhandler.cmd = do_singlestep,
- },
-
-STEXI
-@item singlestep [off]
-@findex singlestep
-Run the emulation in single step mode.
-If called with option off, the emulation returns to normal mode.
-ETEXI
-
- {
.name = "stop",
.args_type = "",
.params = "",
@@ -343,11 +187,6 @@ ETEXI
.mhandler.cmd_new = do_stop,
},
-STEXI
-@item stop
-@findex stop
-Stop emulation.
-ETEXI
SQMP
stop
----
@@ -364,7 +203,7 @@ Example:
EQMP
{
- .name = "c|cont",
+ .name = "cont",
.args_type = "",
.params = "",
.help = "resume emulation",
@@ -372,11 +211,6 @@ EQMP
.mhandler.cmd_new = do_cont,
},
-STEXI
-@item c or cont
-@findex cont
-Resume emulation.
-ETEXI
SQMP
cont
----
@@ -393,164 +227,6 @@ Example:
EQMP
{
- .name = "gdbserver",
- .args_type = "device:s?",
- .params = "[device]",
- .help = "start gdbserver on given device (default 'tcp::1234'), stop with 'none'",
- .mhandler.cmd = do_gdbserver,
- },
-
-STEXI
-@item gdbserver [@var{port}]
-@findex gdbserver
-Start gdbserver session (default @var{port}=1234)
-ETEXI
-
- {
- .name = "x",
- .args_type = "fmt:/,addr:l",
- .params = "/fmt addr",
- .help = "virtual memory dump starting at 'addr'",
- .mhandler.cmd = do_memory_dump,
- },
-
-STEXI
-@item x/fmt @var{addr}
-@findex x
-Virtual memory dump starting at @var{addr}.
-ETEXI
-
- {
- .name = "xp",
- .args_type = "fmt:/,addr:l",
- .params = "/fmt addr",
- .help = "physical memory dump starting at 'addr'",
- .mhandler.cmd = do_physical_memory_dump,
- },
-
-STEXI
-@item xp /@var{fmt} @var{addr}
-@findex xp
-Physical memory dump starting at @var{addr}.
-
-@var{fmt} is a format which tells the command how to format the
-data. Its syntax is: @option{/@{count@}@{format@}@{size@}}
-
-@table @var
-@item count
-is the number of items to be dumped.
-
-@item format
-can be x (hex), d (signed decimal), u (unsigned decimal), o (octal),
-c (char) or i (asm instruction).
-
-@item size
-can be b (8 bits), h (16 bits), w (32 bits) or g (64 bits). On x86,
-@code{h} or @code{w} can be specified with the @code{i} format to
-respectively select 16 or 32 bit code instruction size.
-
-@end table
-
-Examples:
-@itemize
-@item
-Dump 10 instructions at the current instruction pointer:
-@example
-(qemu) x/10i $eip
-0x90107063: ret
-0x90107064: sti
-0x90107065: lea 0x0(%esi,1),%esi
-0x90107069: lea 0x0(%edi,1),%edi
-0x90107070: ret
-0x90107071: jmp 0x90107080
-0x90107073: nop
-0x90107074: nop
-0x90107075: nop
-0x90107076: nop
-@end example
-
-@item
-Dump 80 16 bit values at the start of the video memory.
-@smallexample
-(qemu) xp/80hx 0xb8000
-0x000b8000: 0x0b50 0x0b6c 0x0b65 0x0b78 0x0b38 0x0b36 0x0b2f 0x0b42
-0x000b8010: 0x0b6f 0x0b63 0x0b68 0x0b73 0x0b20 0x0b56 0x0b47 0x0b41
-0x000b8020: 0x0b42 0x0b69 0x0b6f 0x0b73 0x0b20 0x0b63 0x0b75 0x0b72
-0x000b8030: 0x0b72 0x0b65 0x0b6e 0x0b74 0x0b2d 0x0b63 0x0b76 0x0b73
-0x000b8040: 0x0b20 0x0b30 0x0b35 0x0b20 0x0b4e 0x0b6f 0x0b76 0x0b20
-0x000b8050: 0x0b32 0x0b30 0x0b30 0x0b33 0x0720 0x0720 0x0720 0x0720
-0x000b8060: 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720
-0x000b8070: 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720
-0x000b8080: 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720
-0x000b8090: 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720
-@end smallexample
-@end itemize
-ETEXI
-
- {
- .name = "p|print",
- .args_type = "fmt:/,val:l",
- .params = "/fmt expr",
- .help = "print expression value (use $reg for CPU register access)",
- .mhandler.cmd = do_print,
- },
-
-STEXI
-@item p or print/@var{fmt} @var{expr}
-@findex print
-
-Print expression value. Only the @var{format} part of @var{fmt} is
-used.
-ETEXI
-
- {
- .name = "i",
- .args_type = "fmt:/,addr:i,index:i.",
- .params = "/fmt addr",
- .help = "I/O port read",
- .mhandler.cmd = do_ioport_read,
- },
-
-STEXI
-Read I/O port.
-ETEXI
-
- {
- .name = "o",
- .args_type = "fmt:/,addr:i,val:i",
- .params = "/fmt addr value",
- .help = "I/O port write",
- .mhandler.cmd = do_ioport_write,
- },
-
-STEXI
-Write to I/O port.
-ETEXI
-
- {
- .name = "sendkey",
- .args_type = "string:s,hold_time:i?",
- .params = "keys [hold_ms]",
- .help = "send keys to the VM (e.g. 'sendkey ctrl-alt-f1', default hold time=100 ms)",
- .mhandler.cmd = do_sendkey,
- },
-
-STEXI
-@item sendkey @var{keys}
-@findex sendkey
-
-Send @var{keys} to the emulator. @var{keys} could be the name of the
-key or @code{#} followed by the raw value in either decimal or hexadecimal
-format. Use @code{-} to press several keys simultaneously. Example:
-@example
-sendkey ctrl-alt-f1
-@end example
-
-This command is useful to send keys that your graphical user interface
-intercepts at low level, such as @code{ctrl-alt-f1} in X Window.
-ETEXI
-
- {
.name = "system_reset",
.args_type = "",
.params = "",
@@ -559,12 +235,6 @@ ETEXI
.mhandler.cmd_new = do_system_reset,
},
-STEXI
-@item system_reset
-@findex system_reset
-
-Reset the system.
-ETEXI
SQMP
system_reset
------------
@@ -589,12 +259,6 @@ EQMP
.mhandler.cmd_new = do_system_powerdown,
},
-STEXI
-@item system_powerdown
-@findex system_powerdown
-
-Power down the system (if supported).
-ETEXI
SQMP
system_powerdown
----------------
@@ -611,54 +275,6 @@ Example:
EQMP
{
- .name = "sum",
- .args_type = "start:i,size:i",
- .params = "addr size",
- .help = "compute the checksum of a memory region",
- .mhandler.cmd = do_sum,
- },
-
-STEXI
-@item sum @var{addr} @var{size}
-@findex sum
-
-Compute the checksum of a memory region.
-ETEXI
-
- {
- .name = "usb_add",
- .args_type = "devname:s",
- .params = "device",
- .help = "add USB device (e.g. 'host:bus.addr' or 'host:vendor_id:product_id')",
- .mhandler.cmd = do_usb_add,
- },
-
-STEXI
-@item usb_add @var{devname}
-@findex usb_add
-
-Add the USB device @var{devname}. For details of available devices see
-@ref{usb_devices}
-ETEXI
-
- {
- .name = "usb_del",
- .args_type = "devname:s",
- .params = "device",
- .help = "remove USB device 'bus.addr'",
- .mhandler.cmd = do_usb_del,
- },
-
-STEXI
-@item usb_del @var{devname}
-@findex usb_del
-
-Remove the USB device @var{devname} from the QEMU virtual USB
-hub. @var{devname} has the syntax @code{bus.addr}. Use the monitor
-command @code{info usb} to see the devices you can remove.
-ETEXI
-
- {
.name = "device_add",
.args_type = "device:O",
.params = "driver[,prop=value][,...]",
@@ -667,12 +283,6 @@ ETEXI
.mhandler.cmd_new = do_device_add,
},
-STEXI
-@item device_add @var{config}
-@findex device_add
-
-Add device.
-ETEXI
SQMP
device_add
----------
@@ -710,12 +320,6 @@ EQMP
.mhandler.cmd_new = do_device_del,
},
-STEXI
-@item device_del @var{id}
-@findex device_del
-
-Remove device @var{id}.
-ETEXI
SQMP
device_del
----------
@@ -742,11 +346,6 @@ EQMP
.mhandler.cmd_new = do_cpu_set,
},
-STEXI
-@item cpu @var{index}
-@findex cpu
-Set the default CPU.
-ETEXI
SQMP
cpu
---
@@ -767,94 +366,6 @@ Note: CPUs' indexes are obtained with the 'query-cpus' command.
EQMP
{
- .name = "mouse_move",
- .args_type = "dx_str:s,dy_str:s,dz_str:s?",
- .params = "dx dy [dz]",
- .help = "send mouse move events",
- .mhandler.cmd = do_mouse_move,
- },
-
-STEXI
-@item mouse_move @var{dx} @var{dy} [@var{dz}]
-@findex mouse_move
-Move the active mouse to the specified coordinates @var{dx} @var{dy}
-with optional scroll axis @var{dz}.
-ETEXI
-
- {
- .name = "mouse_button",
- .args_type = "button_state:i",
- .params = "state",
- .help = "change mouse button state (1=L, 2=M, 4=R)",
- .mhandler.cmd = do_mouse_button,
- },
-
-STEXI
-@item mouse_button @var{val}
-@findex mouse_button
-Change the active mouse button state @var{val} (1=L, 2=M, 4=R).
-ETEXI
-
- {
- .name = "mouse_set",
- .args_type = "index:i",
- .params = "index",
- .help = "set which mouse device receives events",
- .mhandler.cmd = do_mouse_set,
- },
-
-STEXI
-@item mouse_set @var{index}
-@findex mouse_set
-Set which mouse device receives events at given @var{index}, index
-can be obtained with
-@example
-info mice
-@end example
-ETEXI
-
-#ifdef HAS_AUDIO
- {
- .name = "wavcapture",
- .args_type = "path:F,freq:i?,bits:i?,nchannels:i?",
- .params = "path [frequency [bits [channels]]]",
- .help = "capture audio to a wave file (default frequency=44100 bits=16 channels=2)",
- .mhandler.cmd = do_wav_capture,
- },
-#endif
-STEXI
-@item wavcapture @var{filename} [@var{frequency} [@var{bits} [@var{channels}]]]
-@findex wavcapture
-Capture audio into @var{filename}. Using sample rate @var{frequency}
-bits per sample @var{bits} and number of channels @var{channels}.
-
-Defaults:
-@itemize @minus
-@item Sample rate = 44100 Hz - CD quality
-@item Bits = 16
-@item Number of channels = 2 - Stereo
-@end itemize
-ETEXI
-
-#ifdef HAS_AUDIO
- {
- .name = "stopcapture",
- .args_type = "n:i",
- .params = "capture index",
- .help = "stop capture",
- .mhandler.cmd = do_stop_capture,
- },
-#endif
-STEXI
-@item stopcapture @var{index}
-@findex stopcapture
-Stop capture with a given @var{index}, index can be obtained with
-@example
-info capture
-@end example
-ETEXI
-
- {
.name = "memsave",
.args_type = "val:l,size:i,filename:s",
.params = "addr size file",
@@ -863,11 +374,6 @@ ETEXI
.mhandler.cmd_new = do_memory_save,
},
-STEXI
-@item memsave @var{addr} @var{size} @var{file}
-@findex memsave
-save to disk virtual memory dump starting at @var{addr} of size @var{size}.
-ETEXI
SQMP
memsave
-------
@@ -901,11 +407,6 @@ EQMP
.mhandler.cmd_new = do_physical_memory_save,
},
-STEXI
-@item pmemsave @var{addr} @var{size} @var{file}
-@findex pmemsave
-save to disk physical memory dump starting at @var{addr} of size @var{size}.
-ETEXI
SQMP
pmemsave
--------
@@ -929,40 +430,6 @@ Example:
EQMP
{
- .name = "boot_set",
- .args_type = "bootdevice:s",
- .params = "bootdevice",
- .help = "define new values for the boot device list",
- .mhandler.cmd = do_boot_set,
- },
-
-STEXI
-@item boot_set @var{bootdevicelist}
-@findex boot_set
-
-Define new values for the boot device list. Those values will override
-the values specified on the command line through the @code{-boot} option.
-
-The values that can be specified here depend on the machine type, but are
-the same that can be specified in the @code{-boot} command line option.
-ETEXI
-
-#if defined(TARGET_I386)
- {
- .name = "nmi",
- .args_type = "cpu_index:i",
- .params = "cpu",
- .help = "inject an NMI on the given CPU",
- .mhandler.cmd = do_inject_nmi,
- },
-#endif
-STEXI
-@item nmi @var{cpu}
-@findex nmi
-Inject an NMI on the given CPU (x86 only).
-ETEXI
-
- {
.name = "migrate",
.args_type = "detach:-d,blk:-b,inc:-i,uri:s",
.params = "[-d] [-b] [-i] uri",
@@ -975,14 +442,6 @@ ETEXI
.mhandler.cmd_new = do_migrate,
},
-
-STEXI
-@item migrate [-d] [-b] [-i] @var{uri}
-@findex migrate
-Migrate to @var{uri} (using -d to not wait for completion).
- -b for migration with full copy of disk
- -i for migration with incremental copy of disk (base image is shared)
-ETEXI
SQMP
migrate
-------
@@ -1019,11 +478,6 @@ EQMP
.mhandler.cmd_new = do_migrate_cancel,
},
-STEXI
-@item migrate_cancel
-@findex migrate_cancel
-Cancel the current VM migration.
-ETEXI
SQMP
migrate_cancel
--------------
@@ -1048,11 +502,6 @@ EQMP
.mhandler.cmd_new = do_migrate_set_speed,
},
-STEXI
-@item migrate_set_speed @var{value}
-@findex migrate_set_speed
-Set maximum speed to @var{value} (in bytes) for migrations.
-ETEXI
SQMP
migrate_set_speed
-----------------
@@ -1079,11 +528,6 @@ EQMP
.mhandler.cmd_new = do_migrate_set_downtime,
},
-STEXI
-@item migrate_set_downtime @var{second}
-@findex migrate_set_downtime
-Set maximum tolerated downtime (in seconds) for migration.
-ETEXI
SQMP
migrate_set_downtime
--------------------
@@ -1101,86 +545,6 @@ Example:
EQMP
-#if defined(TARGET_I386)
- {
- .name = "drive_add",
- .args_type = "pci_addr:s,opts:s",
- .params = "[[<domain>:]<bus>:]<slot>\n"
- "[file=file][,if=type][,bus=n]\n"
- "[,unit=m][,media=d][index=i]\n"
- "[,cyls=c,heads=h,secs=s[,trans=t]]\n"
- "[snapshot=on|off][,cache=on|off]",
- .help = "add drive to PCI storage controller",
- .mhandler.cmd = drive_hot_add,
- },
-#endif
-
-STEXI
-@item drive_add
-@findex drive_add
-Add drive to PCI storage controller.
-ETEXI
-
-#if defined(TARGET_I386)
- {
- .name = "pci_add",
- .args_type = "pci_addr:s,type:s,opts:s?",
- .params = "auto|[[<domain>:]<bus>:]<slot> nic|storage [[vlan=n][,macaddr=addr][,model=type]] [file=file][,if=type][,bus=nr]...",
- .help = "hot-add PCI device",
- .mhandler.cmd = pci_device_hot_add,
- },
-#endif
-
-STEXI
-@item pci_add
-@findex pci_add
-Hot-add PCI device.
-ETEXI
-
-#if defined(TARGET_I386)
- {
- .name = "pci_del",
- .args_type = "pci_addr:s",
- .params = "[[<domain>:]<bus>:]<slot>",
- .help = "hot remove PCI device",
- .mhandler.cmd = do_pci_device_hot_remove,
- },
-#endif
-
-STEXI
-@item pci_del
-@findex pci_del
-Hot remove PCI device.
-ETEXI
-
- {
- .name = "host_net_add",
- .args_type = "device:s,opts:s?",
- .params = "tap|user|socket|vde|dump [options]",
- .help = "add host VLAN client",
- .mhandler.cmd = net_host_device_add,
- },
-
-STEXI
-@item host_net_add
-@findex host_net_add
-Add host VLAN client.
-ETEXI
-
- {
- .name = "host_net_remove",
- .args_type = "vlan_id:i,device:s",
- .params = "vlan_id name",
- .help = "remove host VLAN client",
- .mhandler.cmd = net_host_device_remove,
- },
-
-STEXI
-@item host_net_remove
-@findex host_net_remove
-Remove host VLAN client.
-ETEXI
-
{
.name = "netdev_add",
.args_type = "netdev:O",
@@ -1190,11 +554,6 @@ ETEXI
.mhandler.cmd_new = do_netdev_add,
},
-STEXI
-@item netdev_add
-@findex netdev_add
-Add host network device.
-ETEXI
SQMP
netdev_add
----------
@@ -1227,11 +586,6 @@ EQMP
.mhandler.cmd_new = do_netdev_del,
},
-STEXI
-@item netdev_del
-@findex netdev_del
-Remove host network device.
-ETEXI
SQMP
netdev_del
----------
@@ -1249,37 +603,6 @@ Example:
EQMP
-#ifdef CONFIG_SLIRP
- {
- .name = "hostfwd_add",
- .args_type = "arg1:s,arg2:s?,arg3:s?",
- .params = "[vlan_id name] [tcp|udp]:[hostaddr]:hostport-[guestaddr]:guestport",
- .help = "redirect TCP or UDP connections from host to guest (requires -net user)",
- .mhandler.cmd = net_slirp_hostfwd_add,
- },
-#endif
-STEXI
-@item hostfwd_add
-@findex hostfwd_add
-Redirect TCP or UDP connections from host to guest (requires -net user).
-ETEXI
-
-#ifdef CONFIG_SLIRP
- {
- .name = "hostfwd_remove",
- .args_type = "arg1:s,arg2:s?,arg3:s?",
- .params = "[vlan_id name] [tcp|udp]:[hostaddr]:hostport",
- .help = "remove host-to-guest TCP or UDP redirection",
- .mhandler.cmd = net_slirp_hostfwd_remove,
- },
-
-#endif
-STEXI
-@item hostfwd_remove
-@findex hostfwd_remove
-Remove host-to-guest TCP or UDP redirection.
-ETEXI
-
{
.name = "balloon",
.args_type = "value:M",
@@ -1290,11 +613,6 @@ ETEXI
.flags = MONITOR_CMD_ASYNC,
},
-STEXI
-@item balloon @var{value}
-@findex balloon
-Request VM to change its memory allocation to @var{value} (in MB).
-ETEXI
SQMP
balloon
-------
@@ -1321,11 +639,6 @@ EQMP
.mhandler.cmd_new = do_set_link,
},
-STEXI
-@item set_link @var{name} [on|off]
-@findex set_link
-Switch link @var{name} on (i.e. up) or off (i.e. down).
-ETEXI
SQMP
set_link
--------
@@ -1345,118 +658,6 @@ Example:
EQMP
{
- .name = "watchdog_action",
- .args_type = "action:s",
- .params = "[reset|shutdown|poweroff|pause|debug|none]",
- .help = "change watchdog action",
- .mhandler.cmd = do_watchdog_action,
- },
-
-STEXI
-@item watchdog_action
-@findex watchdog_action
-Change watchdog action.
-ETEXI
-
- {
- .name = "acl_show",
- .args_type = "aclname:s",
- .params = "aclname",
- .help = "list rules in the access control list",
- .mhandler.cmd = do_acl_show,
- },
-
-STEXI
-@item acl_show @var{aclname}
-@findex acl_show
-List all the matching rules in the access control list, and the default
-policy. There are currently two named access control lists,
-@var{vnc.x509dname} and @var{vnc.username} matching on the x509 client
-certificate distinguished name, and SASL username respectively.
-ETEXI
-
- {
- .name = "acl_policy",
- .args_type = "aclname:s,policy:s",
- .params = "aclname allow|deny",
- .help = "set default access control list policy",
- .mhandler.cmd = do_acl_policy,
- },
-
-STEXI
-@item acl_policy @var{aclname} @code{allow|deny}
-@findex acl_policy
-Set the default access control list policy, used in the event that
-none of the explicit rules match. The default policy at startup is
-always @code{deny}.
-ETEXI
-
- {
- .name = "acl_add",
- .args_type = "aclname:s,match:s,policy:s,index:i?",
- .params = "aclname match allow|deny [index]",
- .help = "add a match rule to the access control list",
- .mhandler.cmd = do_acl_add,
- },
-
-STEXI
-@item acl_add @var{aclname} @var{match} @code{allow|deny} [@var{index}]
-@findex acl_add
-Add a match rule to the access control list, allowing or denying access.
-The match will normally be an exact username or x509 distinguished name,
-but can optionally include wildcard globs. eg @code{*@@EXAMPLE.COM} to
-allow all users in the @code{EXAMPLE.COM} kerberos realm. The match will
-normally be appended to the end of the ACL, but can be inserted
-earlier in the list if the optional @var{index} parameter is supplied.
-ETEXI
-
- {
- .name = "acl_remove",
- .args_type = "aclname:s,match:s",
- .params = "aclname match",
- .help = "remove a match rule from the access control list",
- .mhandler.cmd = do_acl_remove,
- },
-
-STEXI
-@item acl_remove @var{aclname} @var{match}
-@findex acl_remove
-Remove the specified match rule from the access control list.
-ETEXI
-
- {
- .name = "acl_reset",
- .args_type = "aclname:s",
- .params = "aclname",
- .help = "reset the access control list",
- .mhandler.cmd = do_acl_reset,
- },
-
-STEXI
-@item acl_reset @var{aclname}
-@findex acl_reset
-Remove all matches from the access control list, and set the default
-policy back to @code{deny}.
-ETEXI
-
-#if defined(TARGET_I386)
-
- {
- .name = "mce",
- .args_type = "cpu_index:i,bank:i,status:l,mcg_status:l,addr:l,misc:l",
- .params = "cpu bank status mcgstatus addr misc",
- .help = "inject a MCE on the given CPU",
- .mhandler.cmd = do_inject_mce,
- },
-
-#endif
-STEXI
-@item mce @var{cpu} @var{bank} @var{status} @var{mcgstatus} @var{addr} @var{misc}
-@findex mce (x86)
-Inject an MCE on the given CPU (x86 only).
-ETEXI
-
- {
.name = "getfd",
.args_type = "fdname:s",
.params = "getfd name",
@@ -1465,13 +666,6 @@ ETEXI
.mhandler.cmd_new = do_getfd,
},
-STEXI
-@item getfd @var{fdname}
-@findex getfd
-If a file descriptor is passed alongside this command using the SCM_RIGHTS
-mechanism on unix sockets, it is stored using the name @var{fdname} for
-later use by other monitor commands.
-ETEXI
SQMP
getfd
-----
@@ -1498,13 +692,6 @@ EQMP
.mhandler.cmd_new = do_closefd,
},
-STEXI
-@item closefd @var{fdname}
-@findex closefd
-Close the file descriptor previously assigned to @var{fdname} using the
-@code{getfd} command. This is only needed if the file descriptor was never
-used by another monitor command.
-ETEXI
SQMP
closefd
-------
@@ -1531,11 +718,6 @@ EQMP
.mhandler.cmd_new = do_block_set_passwd,
},
-STEXI
-@item block_passwd @var{device} @var{password}
-@findex block_passwd
-Set the encrypted device @var{device} password to @var{password}
-ETEXI
SQMP
block_passwd
------------
@@ -1564,11 +746,6 @@ EQMP
.mhandler.cmd_new = do_qmp_capabilities,
},
-STEXI
-@item qmp_capabilities
-@findex qmp_capabilities
-Enable the specified QMP capabilities
-ETEXI
SQMP
qmp_capabilities
----------------
@@ -1584,37 +761,15 @@ Example:
Note: This command must be issued before issuing any other command.
-EQMP
-
-
-HXCOMM Keep the 'info' command at the end!
-HXCOMM This is required for the QMP documentation layout.
-
-SQMP
-
-2. Query Commands
+3. Query Commands
=================
-EQMP
+HXCOMM Each query command below is inside a SQMP/EQMP section, do NOT change
+HXCOMM this! We will possibly move query commands definitions inside those
+HXCOMM sections, just like regular commands.
- {
- .name = "info",
- .args_type = "item:s?",
- .params = "[subcommand]",
- .help = "show various information about the system state",
- .user_print = monitor_user_noop,
- .mhandler.cmd_new = do_info,
- },
-
-STEXI
-@item info @var{subcommand}
-@findex info
-Show various information about the system state.
+EQMP
-@table @option
-@item info version
-show the version of QEMU
-ETEXI
SQMP
query-version
-------------
@@ -1623,20 +778,28 @@ Show QEMU version.
Return a json-object with the following information:
-- "qemu": QEMU's version (json-string)
+- "qemu": A json-object containing three integer values:
+ - "major": QEMU's major version (json-int)
+ - "minor": QEMU's minor version (json-int)
+ - "micro": QEMU's micro version (json-int)
- "package": package's version (json-string)
Example:
-> { "execute": "query-version" }
-<- { "return": { "qemu": "0.11.50", "package": "" } }
+<- {
+ "return":{
+ "qemu":{
+ "major":0,
+ "minor":11,
+ "micro":5
+ },
+ "package":""
+ }
+ }
EQMP
-STEXI
-@item info commands
-list QMP available commands
-ETEXI
SQMP
query-commands
--------------
@@ -1668,15 +831,6 @@ Note: This example has been shortened as the real response is too long.
EQMP
-STEXI
-@item info network
-show the various VLANs and the associated devices
-ETEXI
-
-STEXI
-@item info chardev
-show the character devices
-ETEXI
SQMP
query-chardev
-------------
@@ -1707,10 +861,6 @@ Example:
EQMP
-STEXI
-@item info block
-show the block devices
-ETEXI
SQMP
query-block
-----------
@@ -1780,10 +930,6 @@ Example:
EQMP
-STEXI
-@item info blockstats
-show block device statistics
-ETEXI
SQMP
query-blockstats
----------------
@@ -1867,12 +1013,6 @@ Example:
EQMP
-STEXI
-@item info registers
-show the cpu registers
-@item info cpus
-show infos for each CPU
-ETEXI
SQMP
query-cpus
----------
@@ -1912,19 +1052,6 @@ Example:
EQMP
-STEXI
-@item info history
-show the command line history
-@item info irq
-show the interrupts statistics (if available)
-@item info pic
-show i8259 (PIC) state
-ETEXI
-
-STEXI
-@item info pci
-show emulated PCI device info
-ETEXI
SQMP
query-pci
---------
@@ -2136,26 +1263,6 @@ Note: This example has been shortened as the real response is too long.
EQMP
-STEXI
-@item info tlb
-show virtual to physical memory mappings (i386 only)
-@item info mem
-show the active virtual memory mappings (i386 only)
-ETEXI
-
-STEXI
-@item info jit
-show dynamic compiler info
-@item info kvm
-show KVM information
-@item info numa
-show NUMA information
-ETEXI
-
-STEXI
-@item info kvm
-show KVM information
-ETEXI
SQMP
query-kvm
---------
@@ -2174,23 +1281,6 @@ Example:
EQMP
-STEXI
-@item info usb
-show USB devices plugged on the virtual USB hub
-@item info usbhost
-show all USB host devices
-@item info profile
-show profiling information
-@item info capture
-show information about active capturing
-@item info snapshots
-show list of VM snapshots
-ETEXI
-
-STEXI
-@item info status
-show the current VM status (running|paused)
-ETEXI
SQMP
query-status
------------
@@ -2208,15 +1298,6 @@ Example:
EQMP
-STEXI
-@item info pcmcia
-show guest PCMCIA status
-ETEXI
-
-STEXI
-@item info mice
-show which guest mouse is receiving events
-ETEXI
SQMP
query-mice
----------
@@ -2255,10 +1336,6 @@ Example:
EQMP
-STEXI
-@item info vnc
-show the vnc server status
-ETEXI
SQMP
query-vnc
---------
@@ -2316,10 +1393,6 @@ Example:
EQMP
-STEXI
-@item info name
-show the current VM name
-ETEXI
SQMP
query-name
----------
@@ -2337,10 +1410,6 @@ Example:
EQMP
-STEXI
-@item info uuid
-show the current VM UUID
-ETEXI
SQMP
query-uuid
----------
@@ -2358,17 +1427,6 @@ Example:
EQMP
-STEXI
-@item info cpustats
-show CPU statistics
-@item info usernet
-show user network stack connection states
-ETEXI
-
-STEXI
-@item info migrate
-show migration status
-ETEXI
SQMP
query-migrate
-------------
@@ -2446,10 +1504,6 @@ Examples:
EQMP
-STEXI
-@item info balloon
-show balloon information
-ETEXI
SQMP
query-balloon
-------------
@@ -2485,18 +1539,3 @@ Example:
EQMP
-STEXI
-@item info qtree
-show device tree
-@item info qdm
-show qdev device model list
-@item info roms
-show roms
-@end table
-ETEXI
-
-HXCOMM DO NOT add new commands after 'info', move your addition before it!
-
-STEXI
-@end table
-ETEXI
diff --git a/range.h b/range.h
new file mode 100644
index 0000000000..350237212b
--- /dev/null
+++ b/range.h
@@ -0,0 +1,29 @@
+#ifndef QEMU_RANGE_H
+#define QEMU_RANGE_H
+
+/* Get last byte of a range from offset + length.
+ * Undefined for ranges that wrap around 0. */
+static inline uint64_t range_get_last(uint64_t offset, uint64_t len)
+{
+ return offset + len - 1;
+}
+
+/* Check whether a given range covers a given byte. */
+static inline int range_covers_byte(uint64_t offset, uint64_t len,
+ uint64_t byte)
+{
+ return offset <= byte && byte <= range_get_last(offset, len);
+}
+
+/* Check whether 2 given ranges overlap.
+ * Undefined if ranges that wrap around 0. */
+static inline int ranges_overlap(uint64_t first1, uint64_t len1,
+ uint64_t first2, uint64_t len2)
+{
+ uint64_t last1 = range_get_last(first1, len1);
+ uint64_t last2 = range_get_last(first2, len2);
+
+ return !(last2 < first1 || last1 < first2);
+}
+
+#endif
diff --git a/roms/seabios b/roms/seabios
-Subproject 7d09d0e3ba11310e973d4302c7fcc3fc2184e04
+Subproject 17d3e46511aeedc9f09a8216d194d749187b80a
diff --git a/rules.mak b/rules.mak
index c843a131e8..6dac7773d5 100644
--- a/rules.mak
+++ b/rules.mak
@@ -42,6 +42,15 @@ cc-option = $(if $(shell $(CC) $1 $2 -S -o /dev/null -xc /dev/null \
VPATH_SUFFIXES = %.c %.h %.S %.m %.mak %.texi
set-vpath = $(if $1,$(foreach PATTERN,$(VPATH_SUFFIXES),$(eval vpath $(PATTERN) $1)))
+# find-in-path
+# Usage: $(call find-in-path, prog)
+# Looks in the PATH if the argument contains no slash, else only considers one
+# specific directory. Returns an # empty string if the program doesn't exist
+# there.
+find-in-path = $(if $(find-string /, $1), \
+ $(wildcard $1), \
+ $(wildcard $(patsubst %, %/$1, $(subst :, ,$(PATH)))))
+
# Generate timestamp files for .h include files
%.h: %.h-timestamp
diff --git a/savevm.c b/savevm.c
index ee279895cd..2d8cadc2c2 100644
--- a/savevm.c
+++ b/savevm.c
@@ -551,6 +551,19 @@ int qemu_get_buffer(QEMUFile *f, uint8_t *buf, int size1)
return size1 - size;
}
+static int qemu_peek_byte(QEMUFile *f)
+{
+ if (f->is_write)
+ abort();
+
+ if (f->buf_index >= f->buf_size) {
+ qemu_fill_buffer(f);
+ if (f->buf_index >= f->buf_size)
+ return 0;
+ }
+ return f->buf[f->buf_index];
+}
+
int qemu_get_byte(QEMUFile *f)
{
if (f->is_write)
@@ -1005,6 +1018,7 @@ typedef struct SaveStateEntry {
const VMStateDescription *vmsd;
void *opaque;
CompatEntry *compat;
+ int no_migrate;
} SaveStateEntry;
@@ -1068,6 +1082,7 @@ int register_savevm_live(DeviceState *dev,
se->load_state = load_state;
se->opaque = opaque;
se->vmsd = NULL;
+ se->no_migrate = 0;
if (dev && dev->parent_bus && dev->parent_bus->info->get_dev_path) {
char *id = dev->parent_bus->info->get_dev_path(dev);
@@ -1126,11 +1141,39 @@ void unregister_savevm(DeviceState *dev, const char *idstr, void *opaque)
QTAILQ_FOREACH_SAFE(se, &savevm_handlers, entry, new_se) {
if (strcmp(se->idstr, id) == 0 && se->opaque == opaque) {
QTAILQ_REMOVE(&savevm_handlers, se, entry);
+ if (se->compat) {
+ qemu_free(se->compat);
+ }
qemu_free(se);
}
}
}
+/* mark a device as not to be migrated, that is the device should be
+ unplugged before migration */
+void register_device_unmigratable(DeviceState *dev, const char *idstr,
+ void *opaque)
+{
+ SaveStateEntry *se;
+ char id[256] = "";
+
+ if (dev && dev->parent_bus && dev->parent_bus->info->get_dev_path) {
+ char *path = dev->parent_bus->info->get_dev_path(dev);
+ if (path) {
+ pstrcpy(id, sizeof(id), path);
+ pstrcat(id, sizeof(id), "/");
+ qemu_free(path);
+ }
+ }
+ pstrcat(id, sizeof(id), idstr);
+
+ QTAILQ_FOREACH(se, &savevm_handlers, entry) {
+ if (strcmp(se->idstr, id) == 0 && se->opaque == opaque) {
+ se->no_migrate = 1;
+ }
+ }
+}
+
int vmstate_register_with_alias_id(DeviceState *dev, int instance_id,
const VMStateDescription *vmsd,
void *opaque, int alias_id,
@@ -1193,15 +1236,24 @@ void vmstate_unregister(DeviceState *dev, const VMStateDescription *vmsd,
QTAILQ_FOREACH_SAFE(se, &savevm_handlers, entry, new_se) {
if (se->vmsd == vmsd && se->opaque == opaque) {
QTAILQ_REMOVE(&savevm_handlers, se, entry);
+ if (se->compat) {
+ qemu_free(se->compat);
+ }
qemu_free(se);
}
}
}
+static void vmstate_subsection_save(QEMUFile *f, const VMStateDescription *vmsd,
+ void *opaque);
+static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd,
+ void *opaque);
+
int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,
void *opaque, int version_id)
{
VMStateField *field = vmsd->fields;
+ int ret;
if (version_id > vmsd->version_id) {
return -EINVAL;
@@ -1223,7 +1275,7 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,
(!field->field_exists &&
field->version_id <= version_id)) {
void *base_addr = opaque + field->offset;
- int ret, i, n_elems = 1;
+ int i, n_elems = 1;
int size = field->size;
if (field->flags & VMS_VBUFFER) {
@@ -1261,6 +1313,10 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,
}
field++;
}
+ ret = vmstate_subsection_load(f, vmsd, opaque);
+ if (ret != 0) {
+ return ret;
+ }
if (vmsd->post_load) {
return vmsd->post_load(opaque, version_id);
}
@@ -1313,6 +1369,7 @@ void vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd,
}
field++;
}
+ vmstate_subsection_save(f, vmsd, opaque);
}
static int vmstate_load(QEMUFile *f, SaveStateEntry *se, int version_id)
@@ -1323,13 +1380,19 @@ static int vmstate_load(QEMUFile *f, SaveStateEntry *se, int version_id)
return vmstate_load_state(f, se->vmsd, se->opaque, version_id);
}
-static void vmstate_save(QEMUFile *f, SaveStateEntry *se)
+static int vmstate_save(QEMUFile *f, SaveStateEntry *se)
{
+ if (se->no_migrate) {
+ return -1;
+ }
+
if (!se->vmsd) { /* Old style */
se->save_state(f, se->opaque);
- return;
+ return 0;
}
vmstate_save_state(f,se->vmsd, se->opaque);
+
+ return 0;
}
#define QEMU_VM_FILE_MAGIC 0x5145564d
@@ -1341,6 +1404,7 @@ static void vmstate_save(QEMUFile *f, SaveStateEntry *se)
#define QEMU_VM_SECTION_PART 0x02
#define QEMU_VM_SECTION_END 0x03
#define QEMU_VM_SECTION_FULL 0x04
+#define QEMU_VM_SUBSECTION 0x05
int qemu_savevm_state_begin(Monitor *mon, QEMUFile *f, int blk_enable,
int shared)
@@ -1423,6 +1487,7 @@ int qemu_savevm_state_iterate(Monitor *mon, QEMUFile *f)
int qemu_savevm_state_complete(Monitor *mon, QEMUFile *f)
{
SaveStateEntry *se;
+ int r;
cpu_synchronize_all_states();
@@ -1455,7 +1520,11 @@ int qemu_savevm_state_complete(Monitor *mon, QEMUFile *f)
qemu_put_be32(f, se->instance_id);
qemu_put_be32(f, se->version_id);
- vmstate_save(f, se);
+ r = vmstate_save(f, se);
+ if (r < 0) {
+ monitor_printf(mon, "cannot migrate with device '%s'\n", se->idstr);
+ return r;
+ }
}
qemu_put_byte(f, QEMU_VM_EOF);
@@ -1529,6 +1598,65 @@ static SaveStateEntry *find_se(const char *idstr, int instance_id)
return NULL;
}
+static const VMStateDescription *vmstate_get_subsection(const VMStateSubsection *sub, char *idstr)
+{
+ while(sub && sub->needed) {
+ if (strcmp(idstr, sub->vmsd->name) == 0) {
+ return sub->vmsd;
+ }
+ sub++;
+ }
+ return NULL;
+}
+
+static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd,
+ void *opaque)
+{
+ while (qemu_peek_byte(f) == QEMU_VM_SUBSECTION) {
+ char idstr[256];
+ int ret;
+ uint8_t version_id, len;
+ const VMStateDescription *sub_vmsd;
+
+ qemu_get_byte(f); /* subsection */
+ len = qemu_get_byte(f);
+ qemu_get_buffer(f, (uint8_t *)idstr, len);
+ idstr[len] = 0;
+ version_id = qemu_get_be32(f);
+
+ sub_vmsd = vmstate_get_subsection(vmsd->subsections, idstr);
+ if (sub_vmsd == NULL) {
+ return -ENOENT;
+ }
+ ret = vmstate_load_state(f, sub_vmsd, opaque, version_id);
+ if (ret) {
+ return ret;
+ }
+ }
+ return 0;
+}
+
+static void vmstate_subsection_save(QEMUFile *f, const VMStateDescription *vmsd,
+ void *opaque)
+{
+ const VMStateSubsection *sub = vmsd->subsections;
+
+ while (sub && sub->needed) {
+ if (sub->needed(opaque)) {
+ const VMStateDescription *vmsd = sub->vmsd;
+ uint8_t len;
+
+ qemu_put_byte(f, QEMU_VM_SUBSECTION);
+ len = strlen(vmsd->name);
+ qemu_put_byte(f, len);
+ qemu_put_buffer(f, (uint8_t *)vmsd->name, len);
+ qemu_put_be32(f, vmsd->version_id);
+ vmstate_save_state(f, vmsd, opaque);
+ }
+ sub++;
+ }
+}
+
typedef struct LoadStateEntry {
QLIST_ENTRY(LoadStateEntry) entry;
SaveStateEntry *se;
@@ -1709,8 +1837,10 @@ void do_savevm(Monitor *mon, const QDict *qdict)
uint32_t vm_state_size;
#ifdef _WIN32
struct _timeb tb;
+ struct tm *ptm;
#else
struct timeval tv;
+ struct tm tm;
#endif
const char *name = qdict_get_try_str(qdict, "name");
@@ -1741,15 +1871,6 @@ void do_savevm(Monitor *mon, const QDict *qdict)
vm_stop(0);
memset(sn, 0, sizeof(*sn));
- if (name) {
- ret = bdrv_snapshot_find(bs, old_sn, name);
- if (ret >= 0) {
- pstrcpy(sn->name, sizeof(sn->name), old_sn->name);
- pstrcpy(sn->id_str, sizeof(sn->id_str), old_sn->id_str);
- } else {
- pstrcpy(sn->name, sizeof(sn->name), name);
- }
- }
/* fill auxiliary fields */
#ifdef _WIN32
@@ -1763,6 +1884,25 @@ void do_savevm(Monitor *mon, const QDict *qdict)
#endif
sn->vm_clock_nsec = qemu_get_clock(vm_clock);
+ if (name) {
+ ret = bdrv_snapshot_find(bs, old_sn, name);
+ if (ret >= 0) {
+ pstrcpy(sn->name, sizeof(sn->name), old_sn->name);
+ pstrcpy(sn->id_str, sizeof(sn->id_str), old_sn->id_str);
+ } else {
+ pstrcpy(sn->name, sizeof(sn->name), name);
+ }
+ } else {
+#ifdef _WIN32
+ ptm = localtime(&tb.time);
+ strftime(sn->name, sizeof(sn->name), "vm-%Y%m%d%H%M%S", ptm);
+#else
+ /* cast below needed for OpenBSD where tv_sec is still 'long' */
+ localtime_r((const time_t *)&tv.tv_sec, &tm);
+ strftime(sn->name, sizeof(sn->name), "vm-%Y%m%d%H%M%S", &tm);
+#endif
+ }
+
/* Delete old snapshots of the same name */
if (name && del_existing_snapshots(mon, name) < 0) {
goto the_end;
@@ -1804,12 +1944,27 @@ void do_savevm(Monitor *mon, const QDict *qdict)
int load_vmstate(const char *name)
{
- BlockDriverState *bs, *bs1;
+ BlockDriverState *bs, *bs_vm_state;
QEMUSnapshotInfo sn;
QEMUFile *f;
int ret;
- /* Verify if there is a device that doesn't support snapshots and is writable */
+ bs_vm_state = bdrv_snapshots();
+ if (!bs_vm_state) {
+ error_report("No block device supports snapshots");
+ return -ENOTSUP;
+ }
+
+ /* Don't even try to load empty VM states */
+ ret = bdrv_snapshot_find(bs_vm_state, &sn, name);
+ if (ret < 0) {
+ return ret;
+ } else if (sn.vm_state_size == 0) {
+ return -EINVAL;
+ }
+
+ /* Verify if there is any device that doesn't support snapshots and is
+ writable and check if the requested snapshot is available too. */
bs = NULL;
while ((bs = bdrv_next(bs))) {
@@ -1822,63 +1977,45 @@ int load_vmstate(const char *name)
bdrv_get_device_name(bs));
return -ENOTSUP;
}
- }
- bs = bdrv_snapshots();
- if (!bs) {
- error_report("No block device supports snapshots");
- return -EINVAL;
+ ret = bdrv_snapshot_find(bs, &sn, name);
+ if (ret < 0) {
+ error_report("Device '%s' does not have the requested snapshot '%s'",
+ bdrv_get_device_name(bs), name);
+ return ret;
+ }
}
/* Flush all IO requests so they don't interfere with the new state. */
qemu_aio_flush();
- bs1 = NULL;
- while ((bs1 = bdrv_next(bs1))) {
- if (bdrv_can_snapshot(bs1)) {
- ret = bdrv_snapshot_goto(bs1, name);
+ bs = NULL;
+ while ((bs = bdrv_next(bs))) {
+ if (bdrv_can_snapshot(bs)) {
+ ret = bdrv_snapshot_goto(bs, name);
if (ret < 0) {
- switch(ret) {
- case -ENOTSUP:
- error_report("%sSnapshots not supported on device '%s'",
- bs != bs1 ? "Warning: " : "",
- bdrv_get_device_name(bs1));
- break;
- case -ENOENT:
- error_report("%sCould not find snapshot '%s' on device '%s'",
- bs != bs1 ? "Warning: " : "",
- name, bdrv_get_device_name(bs1));
- break;
- default:
- error_report("%sError %d while activating snapshot on '%s'",
- bs != bs1 ? "Warning: " : "",
- ret, bdrv_get_device_name(bs1));
- break;
- }
- /* fatal on snapshot block device */
- if (bs == bs1)
- return 0;
+ error_report("Error %d while activating snapshot '%s' on '%s'",
+ ret, name, bdrv_get_device_name(bs));
+ return ret;
}
}
}
- /* Don't even try to load empty VM states */
- ret = bdrv_snapshot_find(bs, &sn, name);
- if ((ret >= 0) && (sn.vm_state_size == 0))
- return -EINVAL;
-
/* restore the VM state */
- f = qemu_fopen_bdrv(bs, 0);
+ f = qemu_fopen_bdrv(bs_vm_state, 0);
if (!f) {
error_report("Could not open VM state file");
return -EINVAL;
}
+
ret = qemu_loadvm_state(f);
+
qemu_fclose(f);
if (ret < 0) {
error_report("Error %d while loading VM state", ret);
return ret;
}
+
return 0;
}
@@ -1914,8 +2051,10 @@ void do_delvm(Monitor *mon, const QDict *qdict)
void do_info_snapshots(Monitor *mon)
{
BlockDriverState *bs, *bs1;
- QEMUSnapshotInfo *sn_tab, *sn;
- int nb_sns, i;
+ QEMUSnapshotInfo *sn_tab, *sn, s, *sn_info = &s;
+ int nb_sns, i, ret, available;
+ int total;
+ int *available_snapshots;
char buf[256];
bs = bdrv_snapshots();
@@ -1923,27 +2062,52 @@ void do_info_snapshots(Monitor *mon)
monitor_printf(mon, "No available block device supports snapshots\n");
return;
}
- monitor_printf(mon, "Snapshot devices:");
- bs1 = NULL;
- while ((bs1 = bdrv_next(bs1))) {
- if (bdrv_can_snapshot(bs1)) {
- if (bs == bs1)
- monitor_printf(mon, " %s", bdrv_get_device_name(bs1));
- }
- }
- monitor_printf(mon, "\n");
nb_sns = bdrv_snapshot_list(bs, &sn_tab);
if (nb_sns < 0) {
monitor_printf(mon, "bdrv_snapshot_list: error %d\n", nb_sns);
return;
}
- monitor_printf(mon, "Snapshot list (from %s):\n",
- bdrv_get_device_name(bs));
- monitor_printf(mon, "%s\n", bdrv_snapshot_dump(buf, sizeof(buf), NULL));
- for(i = 0; i < nb_sns; i++) {
+
+ if (nb_sns == 0) {
+ monitor_printf(mon, "There is no snapshot available.\n");
+ return;
+ }
+
+ available_snapshots = qemu_mallocz(sizeof(int) * nb_sns);
+ total = 0;
+ for (i = 0; i < nb_sns; i++) {
sn = &sn_tab[i];
- monitor_printf(mon, "%s\n", bdrv_snapshot_dump(buf, sizeof(buf), sn));
+ available = 1;
+ bs1 = NULL;
+
+ while ((bs1 = bdrv_next(bs1))) {
+ if (bdrv_can_snapshot(bs1) && bs1 != bs) {
+ ret = bdrv_snapshot_find(bs1, sn_info, sn->id_str);
+ if (ret < 0) {
+ available = 0;
+ break;
+ }
+ }
+ }
+
+ if (available) {
+ available_snapshots[total] = i;
+ total++;
+ }
}
+
+ if (total > 0) {
+ monitor_printf(mon, "%s\n", bdrv_snapshot_dump(buf, sizeof(buf), NULL));
+ for (i = 0; i < total; i++) {
+ sn = &sn_tab[available_snapshots[i]];
+ monitor_printf(mon, "%s\n", bdrv_snapshot_dump(buf, sizeof(buf), sn));
+ }
+ } else {
+ monitor_printf(mon, "There is no suitable snapshot available\n");
+ }
+
qemu_free(sn_tab);
+ qemu_free(available_snapshots);
+
}
diff --git a/simpletrace.c b/simpletrace.c
new file mode 100644
index 0000000000..9ea0d1f984
--- /dev/null
+++ b/simpletrace.c
@@ -0,0 +1,255 @@
+/*
+ * Simple trace backend
+ *
+ * Copyright IBM, Corp. 2010
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <time.h>
+#include "qemu-timer.h"
+#include "trace.h"
+
+/** Trace file header event ID */
+#define HEADER_EVENT_ID (~(uint64_t)0) /* avoids conflicting with TraceEventIDs */
+
+/** Trace file magic number */
+#define HEADER_MAGIC 0xf2b177cb0aa429b4ULL
+
+/** Trace file version number, bump if format changes */
+#define HEADER_VERSION 0
+
+/** Trace buffer entry */
+typedef struct {
+ uint64_t event;
+ uint64_t timestamp_ns;
+ uint64_t x1;
+ uint64_t x2;
+ uint64_t x3;
+ uint64_t x4;
+ uint64_t x5;
+ uint64_t x6;
+} TraceRecord;
+
+enum {
+ TRACE_BUF_LEN = 64 * 1024 / sizeof(TraceRecord),
+};
+
+static TraceRecord trace_buf[TRACE_BUF_LEN];
+static unsigned int trace_idx;
+static FILE *trace_fp;
+static char *trace_file_name = NULL;
+static bool trace_file_enabled = false;
+
+void st_print_trace_file_status(FILE *stream, int (*stream_printf)(FILE *stream, const char *fmt, ...))
+{
+ stream_printf(stream, "Trace file \"%s\" %s.\n",
+ trace_file_name, trace_file_enabled ? "on" : "off");
+}
+
+static bool write_header(FILE *fp)
+{
+ static const TraceRecord header = {
+ .event = HEADER_EVENT_ID,
+ .timestamp_ns = HEADER_MAGIC,
+ .x1 = HEADER_VERSION,
+ };
+
+ return fwrite(&header, sizeof header, 1, fp) == 1;
+}
+
+/**
+ * set_trace_file : To set the name of a trace file.
+ * @file : pointer to the name to be set.
+ * If NULL, set to the default name-<pid> set at config time.
+ */
+bool st_set_trace_file(const char *file)
+{
+ st_set_trace_file_enabled(false);
+
+ free(trace_file_name);
+
+ if (!file) {
+ if (asprintf(&trace_file_name, CONFIG_TRACE_FILE, getpid()) < 0) {
+ trace_file_name = NULL;
+ return false;
+ }
+ } else {
+ if (asprintf(&trace_file_name, "%s", file) < 0) {
+ trace_file_name = NULL;
+ return false;
+ }
+ }
+
+ st_set_trace_file_enabled(true);
+ return true;
+}
+
+static void flush_trace_file(void)
+{
+ /* If the trace file is not open yet, open it now */
+ if (!trace_fp) {
+ trace_fp = fopen(trace_file_name, "w");
+ if (!trace_fp) {
+ /* Avoid repeatedly trying to open file on failure */
+ trace_file_enabled = false;
+ return;
+ }
+ write_header(trace_fp);
+ }
+
+ if (trace_fp) {
+ size_t unused; /* for when fwrite(3) is declared warn_unused_result */
+ unused = fwrite(trace_buf, trace_idx * sizeof(trace_buf[0]), 1, trace_fp);
+ }
+}
+
+void st_flush_trace_buffer(void)
+{
+ if (trace_file_enabled) {
+ flush_trace_file();
+ }
+
+ /* Discard written trace records */
+ trace_idx = 0;
+}
+
+void st_set_trace_file_enabled(bool enable)
+{
+ if (enable == trace_file_enabled) {
+ return; /* no change */
+ }
+
+ /* Flush/discard trace buffer */
+ st_flush_trace_buffer();
+
+ /* To disable, close trace file */
+ if (!enable) {
+ fclose(trace_fp);
+ trace_fp = NULL;
+ }
+
+ trace_file_enabled = enable;
+}
+
+static void trace(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3,
+ uint64_t x4, uint64_t x5, uint64_t x6)
+{
+ TraceRecord *rec = &trace_buf[trace_idx];
+
+ if (!trace_list[event].state) {
+ return;
+ }
+
+ rec->event = event;
+ rec->timestamp_ns = get_clock();
+ rec->x1 = x1;
+ rec->x2 = x2;
+ rec->x3 = x3;
+ rec->x4 = x4;
+ rec->x5 = x5;
+ rec->x6 = x6;
+
+ if (++trace_idx == TRACE_BUF_LEN) {
+ st_flush_trace_buffer();
+ }
+}
+
+void trace0(TraceEventID event)
+{
+ trace(event, 0, 0, 0, 0, 0, 0);
+}
+
+void trace1(TraceEventID event, uint64_t x1)
+{
+ trace(event, x1, 0, 0, 0, 0, 0);
+}
+
+void trace2(TraceEventID event, uint64_t x1, uint64_t x2)
+{
+ trace(event, x1, x2, 0, 0, 0, 0);
+}
+
+void trace3(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3)
+{
+ trace(event, x1, x2, x3, 0, 0, 0);
+}
+
+void trace4(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4)
+{
+ trace(event, x1, x2, x3, x4, 0, 0);
+}
+
+void trace5(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, uint64_t x5)
+{
+ trace(event, x1, x2, x3, x4, x5, 0);
+}
+
+void trace6(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, uint64_t x5, uint64_t x6)
+{
+ trace(event, x1, x2, x3, x4, x5, x6);
+}
+
+/**
+ * Flush the trace buffer on exit
+ */
+static void __attribute__((constructor)) st_init(void)
+{
+ atexit(st_flush_trace_buffer);
+}
+
+void st_print_trace(FILE *stream, int (*stream_printf)(FILE *stream, const char *fmt, ...))
+{
+ unsigned int i;
+
+ for (i = 0; i < trace_idx; i++) {
+ stream_printf(stream, "Event %" PRIu64 " : %" PRIx64 " %" PRIx64
+ " %" PRIx64 " %" PRIx64 " %" PRIx64 " %" PRIx64 "\n",
+ trace_buf[i].event, trace_buf[i].x1, trace_buf[i].x2,
+ trace_buf[i].x3, trace_buf[i].x4, trace_buf[i].x5,
+ trace_buf[i].x6);
+ }
+}
+
+void st_print_trace_events(FILE *stream, int (*stream_printf)(FILE *stream, const char *fmt, ...))
+{
+ unsigned int i;
+
+ for (i = 0; i < NR_TRACE_EVENTS; i++) {
+ stream_printf(stream, "%s [Event ID %u] : state %u\n",
+ trace_list[i].tp_name, i, trace_list[i].state);
+ }
+}
+
+static TraceEvent* find_trace_event_by_name(const char *tname)
+{
+ unsigned int i;
+
+ if (!tname) {
+ return NULL;
+ }
+
+ for (i = 0; i < NR_TRACE_EVENTS; i++) {
+ if (!strcmp(trace_list[i].tp_name, tname)) {
+ return &trace_list[i];
+ }
+ }
+ return NULL; /* indicates end of list reached without a match */
+}
+
+bool st_change_trace_event_state(const char *tname, bool tstate)
+{
+ TraceEvent *tp;
+
+ tp = find_trace_event_by_name(tname);
+ if (tp) {
+ tp->state = tstate;
+ return true;
+ }
+ return false;
+}
diff --git a/simpletrace.h b/simpletrace.h
new file mode 100644
index 0000000000..72614ec1d1
--- /dev/null
+++ b/simpletrace.h
@@ -0,0 +1,40 @@
+/*
+ * Simple trace backend
+ *
+ * Copyright IBM, Corp. 2010
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#ifndef SIMPLETRACE_H
+#define SIMPLETRACE_H
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdio.h>
+
+typedef uint64_t TraceEventID;
+
+typedef struct {
+ const char *tp_name;
+ bool state;
+} TraceEvent;
+
+void trace0(TraceEventID event);
+void trace1(TraceEventID event, uint64_t x1);
+void trace2(TraceEventID event, uint64_t x1, uint64_t x2);
+void trace3(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3);
+void trace4(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4);
+void trace5(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, uint64_t x5);
+void trace6(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, uint64_t x5, uint64_t x6);
+void st_print_trace(FILE *stream, int (*stream_printf)(FILE *stream, const char *fmt, ...));
+void st_print_trace_events(FILE *stream, int (*stream_printf)(FILE *stream, const char *fmt, ...));
+bool st_change_trace_event_state(const char *tname, bool tstate);
+void st_print_trace_file_status(FILE *stream, int (*stream_printf)(FILE *stream, const char *fmt, ...));
+void st_set_trace_file_enabled(bool enable);
+bool st_set_trace_file(const char *file);
+void st_flush_trace_buffer(void);
+
+#endif /* SIMPLETRACE_H */
diff --git a/simpletrace.py b/simpletrace.py
new file mode 100755
index 0000000000..553a72709f
--- /dev/null
+++ b/simpletrace.py
@@ -0,0 +1,93 @@
+#!/usr/bin/env python
+#
+# Pretty-printer for simple trace backend binary trace files
+#
+# Copyright IBM, Corp. 2010
+#
+# This work is licensed under the terms of the GNU GPL, version 2. See
+# the COPYING file in the top-level directory.
+#
+# For help see docs/tracing.txt
+
+import sys
+import struct
+import re
+
+header_event_id = 0xffffffffffffffff
+header_magic = 0xf2b177cb0aa429b4
+header_version = 0
+
+trace_fmt = '=QQQQQQQQ'
+trace_len = struct.calcsize(trace_fmt)
+event_re = re.compile(r'(disable\s+)?([a-zA-Z0-9_]+)\(([^)]*)\).*')
+
+def err(msg):
+ sys.stderr.write(msg + '\n')
+ sys.exit(1)
+
+def parse_events(fobj):
+ """Parse a trace-events file."""
+
+ def get_argnames(args):
+ """Extract argument names from a parameter list."""
+ return tuple(arg.split()[-1].lstrip('*') for arg in args.split(','))
+
+ events = {}
+ event_num = 0
+ for line in fobj:
+ m = event_re.match(line.strip())
+ if m is None:
+ continue
+
+ disable, name, args = m.groups()
+ events[event_num] = (name,) + get_argnames(args)
+ event_num += 1
+ return events
+
+def read_record(fobj):
+ """Deserialize a trace record from a file."""
+ s = fobj.read(trace_len)
+ if len(s) != trace_len:
+ return None
+ return struct.unpack(trace_fmt, s)
+
+def read_trace_file(fobj):
+ """Deserialize trace records from a file."""
+ header = read_record(fobj)
+ if header is None or \
+ header[0] != header_event_id or \
+ header[1] != header_magic or \
+ header[2] != header_version:
+ err('not a trace file or incompatible version')
+
+ while True:
+ rec = read_record(fobj)
+ if rec is None:
+ break
+
+ yield rec
+
+class Formatter(object):
+ def __init__(self, events):
+ self.events = events
+ self.last_timestamp = None
+
+ def format_record(self, rec):
+ if self.last_timestamp is None:
+ self.last_timestamp = rec[1]
+ delta_ns = rec[1] - self.last_timestamp
+ self.last_timestamp = rec[1]
+
+ event = self.events[rec[0]]
+ fields = [event[0], '%0.3f' % (delta_ns / 1000.0)]
+ for i in xrange(1, len(event)):
+ fields.append('%s=0x%x' % (event[i], rec[i + 1]))
+ return ' '.join(fields)
+
+if len(sys.argv) != 3:
+ err('usage: %s <trace-events> <trace-file>' % sys.argv[0])
+
+events = parse_events(open(sys.argv[1], 'r'))
+formatter = Formatter(events)
+for rec in read_trace_file(open(sys.argv[2], 'rb')):
+ print formatter.format_record(rec)
diff --git a/slirp/bootp.c b/slirp/bootp.c
index 3e4e8810be..41460ffaa5 100644
--- a/slirp/bootp.c
+++ b/slirp/bootp.c
@@ -33,7 +33,7 @@ static const uint8_t rfc1533_cookie[] = { RFC1533_COOKIE };
#define DPRINTF(fmt, ...) \
do if (slirp_debug & DBG_CALL) { fprintf(dfd, fmt, ## __VA_ARGS__); fflush(dfd); } while (0)
#else
-#define DPRINTF(fmt, ...)
+#define DPRINTF(fmt, ...) do{}while(0)
#endif
static BOOTPClient *get_new_addr(Slirp *slirp, struct in_addr *paddr,
diff --git a/slirp/cksum.c b/slirp/cksum.c
index 48a1792199..e43867da37 100644
--- a/slirp/cksum.c
+++ b/slirp/cksum.c
@@ -47,23 +47,23 @@
int cksum(struct mbuf *m, int len)
{
- register u_int16_t *w;
+ register uint16_t *w;
register int sum = 0;
register int mlen = 0;
int byte_swapped = 0;
union {
- u_int8_t c[2];
- u_int16_t s;
+ uint8_t c[2];
+ uint16_t s;
} s_util;
union {
- u_int16_t s[2];
- u_int32_t l;
+ uint16_t s[2];
+ uint32_t l;
} l_util;
if (m->m_len == 0)
goto cont;
- w = mtod(m, u_int16_t *);
+ w = mtod(m, uint16_t *);
mlen = m->m_len;
@@ -78,8 +78,8 @@ int cksum(struct mbuf *m, int len)
if ((1 & (long) w) && (mlen > 0)) {
REDUCE;
sum <<= 8;
- s_util.c[0] = *(u_int8_t *)w;
- w = (u_int16_t *)((int8_t *)w + 1);
+ s_util.c[0] = *(uint8_t *)w;
+ w = (uint16_t *)((int8_t *)w + 1);
mlen--;
byte_swapped = 1;
}
@@ -111,14 +111,14 @@ int cksum(struct mbuf *m, int len)
REDUCE;
sum <<= 8;
if (mlen == -1) {
- s_util.c[1] = *(u_int8_t *)w;
+ s_util.c[1] = *(uint8_t *)w;
sum += s_util.s;
mlen = 0;
} else
mlen = -1;
} else if (mlen == -1)
- s_util.c[0] = *(u_int8_t *)w;
+ s_util.c[0] = *(uint8_t *)w;
cont:
#ifdef DEBUG
diff --git a/slirp/ip.h b/slirp/ip.h
index 8d185a199a..48ea38e5ec 100644
--- a/slirp/ip.h
+++ b/slirp/ip.h
@@ -51,17 +51,17 @@
# define NTOHL(d) ((d) = ntohl((d)))
# endif
# ifndef NTOHS
-# define NTOHS(d) ((d) = ntohs((u_int16_t)(d)))
+# define NTOHS(d) ((d) = ntohs((uint16_t)(d)))
# endif
# ifndef HTONL
# define HTONL(d) ((d) = htonl((d)))
# endif
# ifndef HTONS
-# define HTONS(d) ((d) = htons((u_int16_t)(d)))
+# define HTONS(d) ((d) = htons((uint16_t)(d)))
# endif
#endif
-typedef u_int32_t n_long; /* long as received from the net */
+typedef uint32_t n_long; /* long as received from the net */
/*
* Definitions for internet protocol version 4.
@@ -80,16 +80,16 @@ struct ip {
u_int ip_hl:4, /* header length */
ip_v:4; /* version */
#endif
- u_int8_t ip_tos; /* type of service */
- u_int16_t ip_len; /* total length */
- u_int16_t ip_id; /* identification */
- u_int16_t ip_off; /* fragment offset field */
+ uint8_t ip_tos; /* type of service */
+ uint16_t ip_len; /* total length */
+ uint16_t ip_id; /* identification */
+ uint16_t ip_off; /* fragment offset field */
#define IP_DF 0x4000 /* don't fragment flag */
#define IP_MF 0x2000 /* more fragments flag */
#define IP_OFFMASK 0x1fff /* mask for fragmenting bits */
- u_int8_t ip_ttl; /* time to live */
- u_int8_t ip_p; /* protocol */
- u_int16_t ip_sum; /* checksum */
+ uint8_t ip_ttl; /* time to live */
+ uint8_t ip_p; /* protocol */
+ uint16_t ip_sum; /* checksum */
struct in_addr ip_src,ip_dst; /* source and dest address */
} __attribute__((packed));
@@ -136,9 +136,9 @@ struct ip {
* Time stamp option structure.
*/
struct ip_timestamp {
- u_int8_t ipt_code; /* IPOPT_TS */
- u_int8_t ipt_len; /* size of structure (variable) */
- u_int8_t ipt_ptr; /* index of current entry */
+ uint8_t ipt_code; /* IPOPT_TS */
+ uint8_t ipt_len; /* size of structure (variable) */
+ uint8_t ipt_ptr; /* index of current entry */
#ifdef HOST_WORDS_BIGENDIAN
u_int ipt_oflw:4, /* overflow counter */
ipt_flg:4; /* flags, see below */
@@ -198,9 +198,9 @@ struct qlink {
*/
struct ipovly {
struct mbuf_ptr ih_mbuf; /* backpointer to mbuf */
- u_int8_t ih_x1; /* (unused) */
- u_int8_t ih_pr; /* protocol */
- u_int16_t ih_len; /* protocol length */
+ uint8_t ih_x1; /* (unused) */
+ uint8_t ih_pr; /* protocol */
+ uint16_t ih_len; /* protocol length */
struct in_addr ih_src; /* source internet address */
struct in_addr ih_dst; /* destination internet address */
} __attribute__((packed));
@@ -215,9 +215,9 @@ struct ipovly {
struct ipq {
struct qlink frag_link; /* to ip headers of fragments */
struct qlink ip_link; /* to other reass headers */
- u_int8_t ipq_ttl; /* time for reass q to live */
- u_int8_t ipq_p; /* protocol of this fragment */
- u_int16_t ipq_id; /* sequence id for reassembly */
+ uint8_t ipq_ttl; /* time for reass q to live */
+ uint8_t ipq_p; /* protocol of this fragment */
+ uint16_t ipq_id; /* sequence id for reassembly */
struct in_addr ipq_src,ipq_dst;
} __attribute__((packed));
@@ -235,7 +235,7 @@ struct ipasfrag {
#define ipf_tos ipf_ip.ip_tos
#define ipf_len ipf_ip.ip_len
#define ipf_next ipf_link.next
-#define ipf_prev ipf_link.prev
+#define ipf_prev ipf_link.prev
/*
* Structure stored in mbuf in inpcb.ip_options
diff --git a/slirp/ip_icmp.h b/slirp/ip_icmp.h
index e793990d28..2692822f87 100644
--- a/slirp/ip_icmp.h
+++ b/slirp/ip_icmp.h
@@ -38,7 +38,7 @@
* Per RFC 792, September 1981.
*/
-typedef u_int32_t n_time;
+typedef uint32_t n_time;
/*
* Structure of an icmp header.
diff --git a/slirp/ip_input.c b/slirp/ip_input.c
index bb101da1a6..768ab0cd49 100644
--- a/slirp/ip_input.c
+++ b/slirp/ip_input.c
@@ -144,7 +144,7 @@ ip_input(struct mbuf *m)
m_adj(m, ip->ip_len - m->m_len);
/* check ip_ttl for a correct ICMP reply */
- if(ip->ip_ttl==0 || ip->ip_ttl==1) {
+ if(ip->ip_ttl==0) {
icmp_error(m, ICMP_TIMXCEED,ICMP_TIMXCEED_INTRANS, 0,"ttl");
goto bad;
}
@@ -477,7 +477,7 @@ ip_dooptions(m)
register struct in_ifaddr *ia;
int opt, optlen, cnt, off, code, type, forward = 0;
struct in_addr *sin, dst;
-typedef u_int32_t n_time;
+typedef uint32_t n_time;
n_time ntime;
dst = ip->ip_dst;
diff --git a/slirp/ip_output.c b/slirp/ip_output.c
index dba278478b..542f3180be 100644
--- a/slirp/ip_output.c
+++ b/slirp/ip_output.c
@@ -75,9 +75,9 @@ ip_output(struct socket *so, struct mbuf *m0)
/*
* If small enough for interface, can just send directly.
*/
- if ((u_int16_t)ip->ip_len <= IF_MTU) {
- ip->ip_len = htons((u_int16_t)ip->ip_len);
- ip->ip_off = htons((u_int16_t)ip->ip_off);
+ if ((uint16_t)ip->ip_len <= IF_MTU) {
+ ip->ip_len = htons((uint16_t)ip->ip_len);
+ ip->ip_off = htons((uint16_t)ip->ip_off);
ip->ip_sum = 0;
ip->ip_sum = cksum(m, hlen);
@@ -110,7 +110,7 @@ ip_output(struct socket *so, struct mbuf *m0)
*/
m0 = m;
mhlen = sizeof (struct ip);
- for (off = hlen + len; off < (u_int16_t)ip->ip_len; off += len) {
+ for (off = hlen + len; off < (uint16_t)ip->ip_len; off += len) {
register struct ip *mhip;
m = m_get(slirp);
if (m == NULL) {
@@ -125,18 +125,18 @@ ip_output(struct socket *so, struct mbuf *m0)
mhip->ip_off = ((off - hlen) >> 3) + (ip->ip_off & ~IP_MF);
if (ip->ip_off & IP_MF)
mhip->ip_off |= IP_MF;
- if (off + len >= (u_int16_t)ip->ip_len)
- len = (u_int16_t)ip->ip_len - off;
+ if (off + len >= (uint16_t)ip->ip_len)
+ len = (uint16_t)ip->ip_len - off;
else
mhip->ip_off |= IP_MF;
- mhip->ip_len = htons((u_int16_t)(len + mhlen));
+ mhip->ip_len = htons((uint16_t)(len + mhlen));
if (m_copy(m, m0, off, len) < 0) {
error = -1;
goto sendorfree;
}
- mhip->ip_off = htons((u_int16_t)mhip->ip_off);
+ mhip->ip_off = htons((uint16_t)mhip->ip_off);
mhip->ip_sum = 0;
mhip->ip_sum = cksum(m, mhlen);
*mnext = m;
@@ -147,9 +147,9 @@ ip_output(struct socket *so, struct mbuf *m0)
* and updating header, then send each fragment (in order).
*/
m = m0;
- m_adj(m, hlen + firstlen - (u_int16_t)ip->ip_len);
- ip->ip_len = htons((u_int16_t)m->m_len);
- ip->ip_off = htons((u_int16_t)(ip->ip_off | IP_MF));
+ m_adj(m, hlen + firstlen - (uint16_t)ip->ip_len);
+ ip->ip_len = htons((uint16_t)m->m_len);
+ ip->ip_off = htons((uint16_t)(ip->ip_off | IP_MF));
ip->ip_sum = 0;
ip->ip_sum = cksum(m, hlen);
sendorfree:
diff --git a/slirp/main.h b/slirp/main.h
index 8d09df9d4c..0dd8d81ce4 100644
--- a/slirp/main.h
+++ b/slirp/main.h
@@ -14,7 +14,7 @@
extern int slirp_socket;
extern int slirp_socket_unit;
extern int slirp_socket_port;
-extern u_int32_t slirp_socket_addr;
+extern uint32_t slirp_socket_addr;
extern char *slirp_socket_passwd;
extern int ctty_closed;
diff --git a/slirp/misc.h b/slirp/misc.h
index da68d09950..ed40a103c5 100644
--- a/slirp/misc.h
+++ b/slirp/misc.h
@@ -37,24 +37,24 @@ void do_wait(int);
#define EMU_NOCONNECT 0x10 /* Don't connect */
struct tos_t {
- u_int16_t lport;
- u_int16_t fport;
- u_int8_t tos;
- u_int8_t emu;
+ uint16_t lport;
+ uint16_t fport;
+ uint8_t tos;
+ uint8_t emu;
};
struct emu_t {
- u_int16_t lport;
- u_int16_t fport;
- u_int8_t tos;
- u_int8_t emu;
- struct emu_t *next;
+ uint16_t lport;
+ uint16_t fport;
+ uint8_t tos;
+ uint8_t emu;
+ struct emu_t *next;
};
extern int x_port, x_server, x_display;
int show_x(char *, struct socket *);
-void redir_x(u_int32_t, int, int, int);
+void redir_x(uint32_t, int, int, int);
void slirp_insque(void *, void *);
void slirp_remque(void *);
int add_exec(struct ex_list **, int, char *, struct in_addr, int);
diff --git a/slirp/slirp.c b/slirp/slirp.c
index 82fd9b424f..332d83b64d 100644
--- a/slirp/slirp.c
+++ b/slirp/slirp.c
@@ -599,7 +599,7 @@ static void arp_input(Slirp *slirp, const uint8_t *pkt, int pkt_len)
{
struct ethhdr *eh = (struct ethhdr *)pkt;
struct arphdr *ah = (struct arphdr *)(pkt + ETH_HLEN);
- uint8_t arp_reply[ETH_HLEN + sizeof(struct arphdr)];
+ uint8_t arp_reply[max(ETH_HLEN + sizeof(struct arphdr), 64)];
struct ethhdr *reh = (struct ethhdr *)arp_reply;
struct arphdr *rah = (struct arphdr *)(arp_reply + ETH_HLEN);
int ar_op;
@@ -619,6 +619,7 @@ static void arp_input(Slirp *slirp, const uint8_t *pkt, int pkt_len)
}
return;
arp_ok:
+ memset(arp_reply, 0, sizeof(arp_reply));
/* XXX: make an ARP request to have the client address */
memcpy(slirp->client_ethaddr, eh->h_source, ETH_ALEN);
diff --git a/slirp/slirp.h b/slirp/slirp.h
index 98a26442a3..462292d577 100644
--- a/slirp/slirp.h
+++ b/slirp/slirp.h
@@ -7,10 +7,6 @@
#ifdef _WIN32
# include <inttypes.h>
-typedef uint8_t u_int8_t;
-typedef uint16_t u_int16_t;
-typedef uint32_t u_int32_t;
-typedef uint64_t u_int64_t;
typedef char *caddr_t;
# include <windows.h>
@@ -28,7 +24,9 @@ typedef char *caddr_t;
#else
# define ioctlsocket ioctl
# define closesocket(s) close(s)
-# define O_BINARY 0
+# if !defined(__HAIKU__)
+# define O_BINARY 0
+# endif
#endif
#include <sys/types.h>
@@ -38,35 +36,6 @@ typedef char *caddr_t;
#include <sys/time.h>
-#ifdef NEED_TYPEDEFS
-typedef char int8_t;
-typedef unsigned char u_int8_t;
-
-# if SIZEOF_SHORT == 2
- typedef short int16_t;
- typedef unsigned short u_int16_t;
-# else
-# if SIZEOF_INT == 2
- typedef int int16_t;
- typedef unsigned int u_int16_t;
-# else
- #error Cannot find a type with sizeof() == 2
-# endif
-# endif
-
-# if SIZEOF_SHORT == 4
- typedef short int32_t;
- typedef unsigned short u_int32_t;
-# else
-# if SIZEOF_INT == 4
- typedef int int32_t;
- typedef unsigned int u_int32_t;
-# else
- #error Cannot find a type with sizeof() == 4
-# endif
-# endif
-#endif /* NEED_TYPEDEFS */
-
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
@@ -233,7 +202,7 @@ struct Slirp {
/* ip states */
struct ipq ipq; /* ip reass. queue */
- u_int16_t ip_id; /* ip packet ctr, for ids */
+ uint16_t ip_id; /* ip packet ctr, for ids */
/* bootp/dhcp states */
BOOTPClient bootp_clients[NB_BOOTP_CLIENTS];
@@ -243,7 +212,7 @@ struct Slirp {
struct socket tcb;
struct socket *tcp_last_so;
tcp_seq tcp_iss; /* tcp initial send seq # */
- u_int32_t tcp_now; /* for RFC 1323 timestamps */
+ uint32_t tcp_now; /* for RFC 1323 timestamps */
/* udp states */
struct socket udb;
@@ -294,7 +263,7 @@ void if_start(struct ttys *);
long gethostid(void);
#endif
-void lprint(const char *, ...);
+void lprint(const char *, ...) GCC_FMT_ATTR(1, 2);
#ifndef _WIN32
#include <netdb.h>
@@ -339,7 +308,7 @@ void tcp_sockclosed(struct tcpcb *);
int tcp_fconnect(struct socket *);
void tcp_connect(struct socket *);
int tcp_attach(struct socket *);
-u_int8_t tcp_tos(struct socket *);
+uint8_t tcp_tos(struct socket *);
int tcp_emu(struct socket *, struct mbuf *);
int tcp_ctl(struct socket *);
struct tcpcb *tcp_drop(struct tcpcb *tp, int err);
diff --git a/slirp/slirp_config.h b/slirp/slirp_config.h
index a40248eb72..f19c7034ca 100644
--- a/slirp/slirp_config.h
+++ b/slirp/slirp_config.h
@@ -133,12 +133,6 @@
/* Define if your compiler doesn't like prototypes */
#undef NO_PROTOTYPES
-/* Define if you don't have u_int32_t etc. typedef'd */
-#undef NEED_TYPEDEFS
-#ifdef __sun__
-#define NEED_TYPEDEFS
-#endif
-
/* Define to sizeof(char) */
#define SIZEOF_CHAR 1
diff --git a/slirp/socket.c b/slirp/socket.c
index eaad77af8d..611923424c 100644
--- a/slirp/socket.c
+++ b/slirp/socket.c
@@ -580,7 +580,7 @@ sosendto(struct socket *so, struct mbuf *m)
* Listen for incoming TCP connections
*/
struct socket *
-tcp_listen(Slirp *slirp, u_int32_t haddr, u_int hport, u_int32_t laddr,
+tcp_listen(Slirp *slirp, uint32_t haddr, u_int hport, uint32_t laddr,
u_int lport, int flags)
{
struct sockaddr_in addr;
diff --git a/slirp/socket.h b/slirp/socket.h
index 6e85d03588..857b0da311 100644
--- a/slirp/socket.h
+++ b/slirp/socket.h
@@ -31,11 +31,11 @@ struct socket {
int so_urgc;
struct in_addr so_faddr; /* foreign host table entry */
struct in_addr so_laddr; /* local host table entry */
- u_int16_t so_fport; /* foreign port */
- u_int16_t so_lport; /* local port */
+ uint16_t so_fport; /* foreign port */
+ uint16_t so_lport; /* local port */
- u_int8_t so_iptos; /* Type of service */
- u_int8_t so_emu; /* Is the socket emulated? */
+ uint8_t so_iptos; /* Type of service */
+ uint8_t so_emu; /* Is the socket emulated? */
u_char so_type; /* Type of socket, UDP or TCP */
int so_state; /* internal state flags SS_*, below */
@@ -83,7 +83,7 @@ int sosendoob(struct socket *);
int sowrite(struct socket *);
void sorecvfrom(struct socket *);
int sosendto(struct socket *, struct mbuf *);
-struct socket * tcp_listen(Slirp *, u_int32_t, u_int, u_int32_t, u_int,
+struct socket * tcp_listen(Slirp *, uint32_t, u_int, uint32_t, u_int,
int);
void soisfconnecting(register struct socket *);
void soisfconnected(register struct socket *);
diff --git a/slirp/tcp.h b/slirp/tcp.h
index c7e3457b7a..9d06836626 100644
--- a/slirp/tcp.h
+++ b/slirp/tcp.h
@@ -33,7 +33,7 @@
#ifndef _TCP_H_
#define _TCP_H_
-typedef u_int32_t tcp_seq;
+typedef uint32_t tcp_seq;
#define PR_SLOWHZ 2 /* 2 slow timeouts per second (approx) */
#define PR_FASTHZ 5 /* 5 fast timeouts per second (not important) */
@@ -46,8 +46,8 @@ typedef u_int32_t tcp_seq;
* Per RFC 793, September, 1981.
*/
struct tcphdr {
- u_int16_t th_sport; /* source port */
- u_int16_t th_dport; /* destination port */
+ uint16_t th_sport; /* source port */
+ uint16_t th_dport; /* destination port */
tcp_seq th_seq; /* sequence number */
tcp_seq th_ack; /* acknowledgement number */
#ifdef HOST_WORDS_BIGENDIAN
@@ -57,16 +57,16 @@ struct tcphdr {
u_int th_x2:4, /* (unused) */
th_off:4; /* data offset */
#endif
- u_int8_t th_flags;
+ uint8_t th_flags;
#define TH_FIN 0x01
#define TH_SYN 0x02
#define TH_RST 0x04
#define TH_PUSH 0x08
#define TH_ACK 0x10
#define TH_URG 0x20
- u_int16_t th_win; /* window */
- u_int16_t th_sum; /* checksum */
- u_int16_t th_urp; /* urgent pointer */
+ uint16_t th_win; /* window */
+ uint16_t th_sum; /* checksum */
+ uint16_t th_urp; /* urgent pointer */
};
#include "tcp_var.h"
diff --git a/slirp/tcp_input.c b/slirp/tcp_input.c
index 2808e3e4e4..e4a77310d0 100644
--- a/slirp/tcp_input.c
+++ b/slirp/tcp_input.c
@@ -280,7 +280,7 @@ tcp_input(struct mbuf *m, int iphlen, struct socket *inso)
tcpiphdr2qlink(ti)->next = tcpiphdr2qlink(ti)->prev = NULL;
memset(&ti->ti_i.ih_mbuf, 0 , sizeof(struct mbuf_ptr));
ti->ti_x1 = 0;
- ti->ti_len = htons((u_int16_t)tlen);
+ ti->ti_len = htons((uint16_t)tlen);
len = sizeof(struct ip ) + tlen;
if(cksum(m, len)) {
goto drop;
@@ -1289,7 +1289,7 @@ drop:
static void
tcp_dooptions(struct tcpcb *tp, u_char *cp, int cnt, struct tcpiphdr *ti)
{
- u_int16_t mss;
+ uint16_t mss;
int opt, optlen;
DEBUG_CALL("tcp_dooptions");
diff --git a/slirp/tcp_output.c b/slirp/tcp_output.c
index 0d6011ac64..779314bf9a 100644
--- a/slirp/tcp_output.c
+++ b/slirp/tcp_output.c
@@ -263,11 +263,11 @@ send:
if (flags & TH_SYN) {
tp->snd_nxt = tp->iss;
if ((tp->t_flags & TF_NOOPT) == 0) {
- u_int16_t mss;
+ uint16_t mss;
opt[0] = TCPOPT_MAXSEG;
opt[1] = 4;
- mss = htons((u_int16_t) tcp_mss(tp, 0));
+ mss = htons((uint16_t) tcp_mss(tp, 0));
memcpy((caddr_t)(opt + 2), (caddr_t)&mss, sizeof(mss));
optlen = 4;
}
@@ -364,10 +364,10 @@ send:
win = (long)TCP_MAXWIN << tp->rcv_scale;
if (win < (long)(tp->rcv_adv - tp->rcv_nxt))
win = (long)(tp->rcv_adv - tp->rcv_nxt);
- ti->ti_win = htons((u_int16_t) (win>>tp->rcv_scale));
+ ti->ti_win = htons((uint16_t) (win>>tp->rcv_scale));
if (SEQ_GT(tp->snd_up, tp->snd_una)) {
- ti->ti_urp = htons((u_int16_t)(tp->snd_up - ntohl(ti->ti_seq)));
+ ti->ti_urp = htons((uint16_t)(tp->snd_up - ntohl(ti->ti_seq)));
ti->ti_flags |= TH_URG;
} else
/*
@@ -383,7 +383,7 @@ send:
* checksum extended header and data.
*/
if (len + optlen)
- ti->ti_len = htons((u_int16_t)(sizeof (struct tcphdr) +
+ ti->ti_len = htons((uint16_t)(sizeof (struct tcphdr) +
optlen + len));
ti->ti_sum = cksum(m, (int)(hdrlen + len));
diff --git a/slirp/tcp_subr.c b/slirp/tcp_subr.c
index 0a370f101e..b661d2623c 100644
--- a/slirp/tcp_subr.c
+++ b/slirp/tcp_subr.c
@@ -134,8 +134,8 @@ tcp_respond(struct tcpcb *tp, struct tcpiphdr *ti, struct mbuf *m,
m->m_len = sizeof (struct tcpiphdr);
tlen = 0;
#define xchg(a,b,type) { type t; t=a; a=b; b=t; }
- xchg(ti->ti_dst.s_addr, ti->ti_src.s_addr, u_int32_t);
- xchg(ti->ti_dport, ti->ti_sport, u_int16_t);
+ xchg(ti->ti_dst.s_addr, ti->ti_src.s_addr, uint32_t);
+ xchg(ti->ti_dport, ti->ti_sport, uint16_t);
#undef xchg
}
ti->ti_len = htons((u_short)(sizeof (struct tcphdr) + tlen));
@@ -150,9 +150,9 @@ tcp_respond(struct tcpcb *tp, struct tcpiphdr *ti, struct mbuf *m,
ti->ti_off = sizeof (struct tcphdr) >> 2;
ti->ti_flags = flags;
if (tp)
- ti->ti_win = htons((u_int16_t) (win >> tp->rcv_scale));
+ ti->ti_win = htons((uint16_t) (win >> tp->rcv_scale));
else
- ti->ti_win = htons((u_int16_t)win);
+ ti->ti_win = htons((uint16_t)win);
ti->ti_urp = 0;
ti->ti_sum = 0;
ti->ti_sum = cksum(m, tlen);
@@ -491,7 +491,7 @@ static struct emu_t *tcpemu = NULL;
/*
* Return TOS according to the above table
*/
-u_int8_t
+uint8_t
tcp_tos(struct socket *so)
{
int i = 0;
@@ -548,7 +548,7 @@ tcp_emu(struct socket *so, struct mbuf *m)
Slirp *slirp = so->slirp;
u_int n1, n2, n3, n4, n5, n6;
char buff[257];
- u_int32_t laddr;
+ uint32_t laddr;
u_int lport;
char *bptr;
diff --git a/slirp/tcp_var.h b/slirp/tcp_var.h
index 4ffbe04a18..004193fb6d 100644
--- a/slirp/tcp_var.h
+++ b/slirp/tcp_var.h
@@ -75,9 +75,9 @@ struct tcpcb {
tcp_seq snd_wl1; /* window update seg seq number */
tcp_seq snd_wl2; /* window update seg ack number */
tcp_seq iss; /* initial send sequence number */
- u_int32_t snd_wnd; /* send window */
+ uint32_t snd_wnd; /* send window */
/* receive sequence variables */
- u_int32_t rcv_wnd; /* receive window */
+ uint32_t rcv_wnd; /* receive window */
tcp_seq rcv_nxt; /* receive next */
tcp_seq rcv_up; /* receive urgent pointer */
tcp_seq irs; /* initial receive sequence number */
@@ -91,8 +91,8 @@ struct tcpcb {
* used to recognize retransmits
*/
/* congestion control (for slow start, source quench, retransmit after loss) */
- u_int32_t snd_cwnd; /* congestion-controlled window */
- u_int32_t snd_ssthresh; /* snd_cwnd size threshold for
+ uint32_t snd_cwnd; /* congestion-controlled window */
+ uint32_t snd_ssthresh; /* snd_cwnd size threshold for
* for slow start exponential to
* linear switch
*/
@@ -106,7 +106,7 @@ struct tcpcb {
short t_srtt; /* smoothed round-trip time */
short t_rttvar; /* variance in round-trip time */
u_short t_rttmin; /* minimum rtt allowed */
- u_int32_t max_sndwnd; /* largest window peer has offered */
+ uint32_t max_sndwnd; /* largest window peer has offered */
/* out-of-band data */
char t_oobflags; /* have some */
@@ -120,8 +120,8 @@ struct tcpcb {
u_char rcv_scale; /* window scaling for recv window */
u_char request_r_scale; /* pending window scaling */
u_char requested_s_scale;
- u_int32_t ts_recent; /* timestamp echo data */
- u_int32_t ts_recent_age; /* when last updated */
+ uint32_t ts_recent; /* timestamp echo data */
+ uint32_t ts_recent_age; /* when last updated */
tcp_seq last_ack_sent;
};
diff --git a/slirp/tftp.c b/slirp/tftp.c
index 67e9f2b9d6..55e4692acc 100644
--- a/slirp/tftp.c
+++ b/slirp/tftp.c
@@ -92,8 +92,8 @@ static int tftp_session_find(Slirp *slirp, struct tftp_t *tp)
return -1;
}
-static int tftp_read_data(struct tftp_session *spt, u_int16_t block_nr,
- u_int8_t *buf, int len)
+static int tftp_read_data(struct tftp_session *spt, uint16_t block_nr,
+ uint8_t *buf, int len)
{
int fd;
int bytes_read = 0;
@@ -155,7 +155,7 @@ static int tftp_send_oack(struct tftp_session *spt,
}
static void tftp_send_error(struct tftp_session *spt,
- u_int16_t errorcode, const char *msg,
+ uint16_t errorcode, const char *msg,
struct tftp_t *recv_tp)
{
struct sockaddr_in saddr, daddr;
@@ -194,7 +194,7 @@ out:
}
static int tftp_send_data(struct tftp_session *spt,
- u_int16_t block_nr,
+ uint16_t block_nr,
struct tftp_t *recv_tp)
{
struct sockaddr_in saddr, daddr;
diff --git a/slirp/tftp.h b/slirp/tftp.h
index 1415c8527b..b9f0847eb9 100644
--- a/slirp/tftp.h
+++ b/slirp/tftp.h
@@ -16,17 +16,17 @@
struct tftp_t {
struct ip ip;
struct udphdr udp;
- u_int16_t tp_op;
+ uint16_t tp_op;
union {
struct {
- u_int16_t tp_block_nr;
- u_int8_t tp_buf[512];
+ uint16_t tp_block_nr;
+ uint8_t tp_buf[512];
} tp_data;
struct {
- u_int16_t tp_error_code;
- u_int8_t tp_msg[512];
+ uint16_t tp_error_code;
+ uint8_t tp_msg[512];
} tp_error;
- u_int8_t tp_buf[512 + 2];
+ uint8_t tp_buf[512 + 2];
} x;
};
@@ -35,7 +35,7 @@ struct tftp_session {
char *filename;
struct in_addr client_ip;
- u_int16_t client_port;
+ uint16_t client_port;
int timestamp;
};
diff --git a/slirp/udp.c b/slirp/udp.c
index d6c39b97be..02b3793e9f 100644
--- a/slirp/udp.c
+++ b/slirp/udp.c
@@ -41,7 +41,7 @@
#include <slirp.h>
#include "ip_icmp.h"
-static u_int8_t udp_tos(struct socket *so);
+static uint8_t udp_tos(struct socket *so);
void
udp_init(Slirp *slirp)
@@ -88,7 +88,7 @@ udp_input(register struct mbuf *m, int iphlen)
* Make mbuf data length reflect UDP length.
* If not enough data to reflect UDP length, drop.
*/
- len = ntohs((u_int16_t)uh->uh_ulen);
+ len = ntohs((uint16_t)uh->uh_ulen);
if (ip->ip_len != len) {
if (len > ip->ip_len) {
@@ -321,7 +321,7 @@ static const struct tos_t udptos[] = {
{0, 0, 0, 0}
};
-static u_int8_t
+static uint8_t
udp_tos(struct socket *so)
{
int i = 0;
@@ -339,7 +339,7 @@ udp_tos(struct socket *so)
}
struct socket *
-udp_listen(Slirp *slirp, u_int32_t haddr, u_int hport, u_int32_t laddr,
+udp_listen(Slirp *slirp, uint32_t haddr, u_int hport, uint32_t laddr,
u_int lport, int flags)
{
struct sockaddr_in addr;
diff --git a/slirp/udp.h b/slirp/udp.h
index 47d2f3d4cd..9b5c3cf56a 100644
--- a/slirp/udp.h
+++ b/slirp/udp.h
@@ -41,10 +41,10 @@
* Per RFC 768, September, 1981.
*/
struct udphdr {
- u_int16_t uh_sport; /* source port */
- u_int16_t uh_dport; /* destination port */
- int16_t uh_ulen; /* udp length */
- u_int16_t uh_sum; /* udp checksum */
+ uint16_t uh_sport; /* source port */
+ uint16_t uh_dport; /* destination port */
+ int16_t uh_ulen; /* udp length */
+ uint16_t uh_sum; /* udp checksum */
};
/*
@@ -78,7 +78,7 @@ void udp_input(register struct mbuf *, int);
int udp_output(struct socket *, struct mbuf *, struct sockaddr_in *);
int udp_attach(struct socket *);
void udp_detach(struct socket *);
-struct socket * udp_listen(Slirp *, u_int32_t, u_int, u_int32_t, u_int,
+struct socket * udp_listen(Slirp *, uint32_t, u_int, uint32_t, u_int,
int);
int udp_output2(struct socket *so, struct mbuf *m,
struct sockaddr_in *saddr, struct sockaddr_in *daddr,
diff --git a/sysemu.h b/sysemu.h
index 9c988bb2a3..b81a70ec3e 100644
--- a/sysemu.h
+++ b/sysemu.h
@@ -94,11 +94,11 @@ typedef enum DisplayType
DT_DEFAULT,
DT_CURSES,
DT_SDL,
- DT_VNC,
DT_NOGRAPHIC,
} DisplayType;
extern int autostart;
+extern int incoming_expected;
extern int bios_size;
typedef enum {
diff --git a/target-cris/cpu.h b/target-cris/cpu.h
index fce0804a90..e1d48ed77e 100644
--- a/target-cris/cpu.h
+++ b/target-cris/cpu.h
@@ -155,9 +155,10 @@ typedef struct CPUCRISState {
uint32_t lo;
} tlbsets[2][4][16];
- void *load_info;
-
CPU_COMMON
+
+ /* Members after CPU_COMMON are preserved across resets. */
+ void *load_info;
} CPUCRISState;
CPUCRISState *cpu_cris_init(const char *cpu_model);
diff --git a/target-cris/helper.c b/target-cris/helper.c
index 053ed4ab2a..2a4403b847 100644
--- a/target-cris/helper.c
+++ b/target-cris/helper.c
@@ -101,7 +101,7 @@ int cpu_cris_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
phy = res.phy & ~0x80000000;
prot = res.prot;
tlb_set_page(env, address & TARGET_PAGE_MASK, phy,
- prot | PAGE_EXEC, mmu_idx, TARGET_PAGE_SIZE);
+ prot, mmu_idx, TARGET_PAGE_SIZE);
r = 0;
}
if (r > 0)
@@ -235,9 +235,15 @@ void do_interrupt(CPUState *env)
/* Apply the CRIS CCS shift. Clears U if set. */
cris_shift_ccs(env);
- /* Now that we are in kernel mode, load the handlers address. */
+ /* Now that we are in kernel mode, load the handlers address.
+ This load may not fault, real hw leaves that behaviour as
+ undefined. */
env->pc = ldl_code(env->pregs[PR_EBP] + ex_vec * 4);
+ /* Clear the excption_index to avoid spurios hw_aborts for recursive
+ bus faults. */
+ env->exception_index = -1;
+
D_LOG("%s isr=%x vec=%x ccs=%x pid=%d erp=%x\n",
__func__, env->pc, ex_vec,
env->pregs[PR_CCS],
diff --git a/target-cris/mmu.c b/target-cris/mmu.c
index 773438e9f4..1243745598 100644
--- a/target-cris/mmu.c
+++ b/target-cris/mmu.c
@@ -33,7 +33,7 @@
#define D(x) x
#define D_LOG(...) qemu_log(__VA_ARGS__)
#else
-#define D(x)
+#define D(x) do { } while (0)
#define D_LOG(...) do { } while (0)
#endif
@@ -251,7 +251,7 @@ static int cris_mmu_translate_page(struct cris_mmu_result *res,
res->prot |= PAGE_READ;
if (tlb_w)
res->prot |= PAGE_WRITE;
- if (tlb_x)
+ if (mmu == 0 && (cfg_x || tlb_x))
res->prot |= PAGE_EXEC;
}
else
diff --git a/target-cris/op_helper.c b/target-cris/op_helper.c
index a60da94f30..be9eb06fd0 100644
--- a/target-cris/op_helper.c
+++ b/target-cris/op_helper.c
@@ -164,7 +164,9 @@ void helper_movl_sreg_reg (uint32_t sreg, uint32_t reg)
D_LOG("tlb flush vaddr=%x v=%d pc=%x\n",
vaddr, tlb_v, env->pc);
- tlb_flush_page(env, vaddr);
+ if (tlb_v) {
+ tlb_flush_page(env, vaddr);
+ }
}
}
#endif
diff --git a/target-cris/translate.c b/target-cris/translate.c
index 45c7682706..8361369214 100644
--- a/target-cris/translate.c
+++ b/target-cris/translate.c
@@ -3409,7 +3409,7 @@ gen_intermediate_code_internal(CPUState *env, TranslationBlock *tb,
if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) {
log_target_disas(pc_start, dc->pc - pc_start,
dc->env->pregs[PR_VR]);
- qemu_log("\nisize=%d osize=%zd\n",
+ qemu_log("\nisize=%d osize=%td\n",
dc->pc - pc_start, gen_opc_ptr - gen_opc_buf);
}
#endif
diff --git a/target-cris/translate_v10.c b/target-cris/translate_v10.c
index 14e590da3c..6944827a57 100644
--- a/target-cris/translate_v10.c
+++ b/target-cris/translate_v10.c
@@ -108,16 +108,15 @@ static unsigned int crisv10_post_memaddr(DisasContext *dc, unsigned int size)
static int dec10_prep_move_m(DisasContext *dc, int s_ext, int memsize,
TCGv dst)
{
- unsigned int rs, rd;
+ unsigned int rs;
uint32_t imm;
int is_imm;
int insn_len = 0;
rs = dc->src;
- rd = dc->dst;
is_imm = rs == 15 && !(dc->tb_flags & PFIX_FLAG);
LOG_DIS("rs=%d rd=%d is_imm=%d mode=%d pfix=%d\n",
- rs, rd, is_imm, dc->mode, dc->tb_flags & PFIX_FLAG);
+ rs, dc->dst, is_imm, dc->mode, dc->tb_flags & PFIX_FLAG);
/* Load [$rs] onto T1. */
if (is_imm) {
diff --git a/target-i386/cpu.h b/target-i386/cpu.h
index 1144d4e387..85ed30f7d3 100644
--- a/target-i386/cpu.h
+++ b/target-i386/cpu.h
@@ -250,16 +250,32 @@
#define PG_ERROR_RSVD_MASK 0x08
#define PG_ERROR_I_D_MASK 0x10
-#define MCG_CTL_P (1UL<<8) /* MCG_CAP register available */
+#define MCG_CTL_P (1ULL<<8) /* MCG_CAP register available */
+#define MCG_SER_P (1ULL<<24) /* MCA recovery/new status bits */
-#define MCE_CAP_DEF MCG_CTL_P
+#define MCE_CAP_DEF (MCG_CTL_P|MCG_SER_P)
#define MCE_BANKS_DEF 10
+#define MCG_STATUS_RIPV (1ULL<<0) /* restart ip valid */
+#define MCG_STATUS_EIPV (1ULL<<1) /* ip points to correct instruction */
#define MCG_STATUS_MCIP (1ULL<<2) /* machine check in progress */
#define MCI_STATUS_VAL (1ULL<<63) /* valid error */
#define MCI_STATUS_OVER (1ULL<<62) /* previous errors lost */
#define MCI_STATUS_UC (1ULL<<61) /* uncorrected error */
+#define MCI_STATUS_EN (1ULL<<60) /* error enabled */
+#define MCI_STATUS_MISCV (1ULL<<59) /* misc error reg. valid */
+#define MCI_STATUS_ADDRV (1ULL<<58) /* addr reg. valid */
+#define MCI_STATUS_PCC (1ULL<<57) /* processor context corrupt */
+#define MCI_STATUS_S (1ULL<<56) /* Signaled machine check */
+#define MCI_STATUS_AR (1ULL<<55) /* Action required */
+
+/* MISC register defines */
+#define MCM_ADDR_SEGOFF 0 /* segment offset */
+#define MCM_ADDR_LINEAR 1 /* linear address */
+#define MCM_ADDR_PHYS 2 /* physical address */
+#define MCM_ADDR_MEM 3 /* memory address */
+#define MCM_ADDR_GENERIC 7 /* generic */
#define MSR_IA32_TSC 0x10
#define MSR_IA32_APICBASE 0x1b
@@ -405,6 +421,17 @@
#define CPUID_EXT3_IBS (1 << 10)
#define CPUID_EXT3_SKINIT (1 << 12)
+#define CPUID_SVM_NPT (1 << 0)
+#define CPUID_SVM_LBRV (1 << 1)
+#define CPUID_SVM_SVMLOCK (1 << 2)
+#define CPUID_SVM_NRIPSAVE (1 << 3)
+#define CPUID_SVM_TSCSCALE (1 << 4)
+#define CPUID_SVM_VMCBCLEAN (1 << 5)
+#define CPUID_SVM_FLUSHASID (1 << 6)
+#define CPUID_SVM_DECODEASSIST (1 << 7)
+#define CPUID_SVM_PAUSEFILTER (1 << 10)
+#define CPUID_SVM_PFTHRESHOLD (1 << 12)
+
#define CPUID_VENDOR_INTEL_1 0x756e6547 /* "Genu" */
#define CPUID_VENDOR_INTEL_2 0x49656e69 /* "ineI" */
#define CPUID_VENDOR_INTEL_3 0x6c65746e /* "ntel" */
@@ -702,6 +729,7 @@ typedef struct CPUX86State {
uint8_t has_error_code;
uint32_t sipi_vector;
uint32_t cpuid_kvm_features;
+ uint32_t cpuid_svm_features;
/* in order to simplify APIC support, we leave this pointer to the
user */
diff --git a/target-i386/cpuid.c b/target-i386/cpuid.c
index dcfd81b7ac..0e0bf60f4c 100644
--- a/target-i386/cpuid.c
+++ b/target-i386/cpuid.c
@@ -79,6 +79,17 @@ static const char *kvm_feature_name[] = {
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
};
+static const char *svm_feature_name[] = {
+ "npt", "lbrv", "svm_lock", "nrip_save",
+ "tsc_scale", "vmcb_clean", "flushbyasid", "decodeassists",
+ NULL, NULL, "pause_filter", NULL,
+ "pfthreshold", NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+};
+
/* collects per-function cpuid data
*/
typedef struct model_features_t {
@@ -192,13 +203,15 @@ static void add_flagname_to_bitmaps(const char *flagname, uint32_t *features,
uint32_t *ext_features,
uint32_t *ext2_features,
uint32_t *ext3_features,
- uint32_t *kvm_features)
+ uint32_t *kvm_features,
+ uint32_t *svm_features)
{
if (!lookup_feature(features, flagname, NULL, feature_name) &&
!lookup_feature(ext_features, flagname, NULL, ext_feature_name) &&
!lookup_feature(ext2_features, flagname, NULL, ext2_feature_name) &&
!lookup_feature(ext3_features, flagname, NULL, ext3_feature_name) &&
- !lookup_feature(kvm_features, flagname, NULL, kvm_feature_name))
+ !lookup_feature(kvm_features, flagname, NULL, kvm_feature_name) &&
+ !lookup_feature(svm_features, flagname, NULL, svm_feature_name))
fprintf(stderr, "CPU feature %s not found\n", flagname);
}
@@ -210,7 +223,8 @@ typedef struct x86_def_t {
int family;
int model;
int stepping;
- uint32_t features, ext_features, ext2_features, ext3_features, kvm_features;
+ uint32_t features, ext_features, ext2_features, ext3_features;
+ uint32_t kvm_features, svm_features;
uint32_t xlevel;
char model_id[48];
int vendor_override;
@@ -253,6 +267,7 @@ typedef struct x86_def_t {
CPUID_EXT2_PDPE1GB */
#define TCG_EXT3_FEATURES (CPUID_EXT3_LAHF_LM | CPUID_EXT3_SVM | \
CPUID_EXT3_CR8LEG | CPUID_EXT3_ABM | CPUID_EXT3_SSE4A)
+#define TCG_SVM_FEATURES 0
/* maintains list of cpu model definitions
*/
@@ -305,6 +320,7 @@ static x86_def_t builtin_x86_defs[] = {
CPUID_EXT3_OSVW, CPUID_EXT3_IBS */
.ext3_features = CPUID_EXT3_LAHF_LM | CPUID_EXT3_SVM |
CPUID_EXT3_ABM | CPUID_EXT3_SSE4A,
+ .svm_features = CPUID_SVM_NPT | CPUID_SVM_LBRV,
.xlevel = 0x8000001A,
.model_id = "AMD Phenom(tm) 9550 Quad-Core Processor"
},
@@ -505,6 +521,15 @@ static int cpu_x86_fill_host(x86_def_t *x86_cpu_def)
cpu_x86_fill_model_id(x86_cpu_def->model_id);
x86_cpu_def->vendor_override = 0;
+
+ /*
+ * Every SVM feature requires emulation support in KVM - so we can't just
+ * read the host features here. KVM might even support SVM features not
+ * available on the host hardware. Just set all bits and mask out the
+ * unsupported ones later.
+ */
+ x86_cpu_def->svm_features = -1;
+
return 0;
}
@@ -543,7 +568,7 @@ static int check_features_against_host(x86_def_t *guest_def)
~CPUID_EXT3_SVM, ext3_feature_name, 0x80000001}};
cpu_x86_fill_host(&host_def);
- for (rv = 0, i = 0; i < sizeof (ft) / sizeof (ft[0]); ++i)
+ for (rv = 0, i = 0; i < ARRAY_SIZE(ft); ++i)
for (mask = 1; mask; mask <<= 1)
if (ft[i].check_feat & mask && *ft[i].guest_feat & mask &&
!(*ft[i].host_feat & mask)) {
@@ -560,8 +585,14 @@ static int cpu_x86_find_by_name(x86_def_t *x86_cpu_def, const char *cpu_model)
char *s = strdup(cpu_model);
char *featurestr, *name = strtok(s, ",");
- uint32_t plus_features = 0, plus_ext_features = 0, plus_ext2_features = 0, plus_ext3_features = 0, plus_kvm_features = 0;
- uint32_t minus_features = 0, minus_ext_features = 0, minus_ext2_features = 0, minus_ext3_features = 0, minus_kvm_features = 0;
+ /* Features to be added*/
+ uint32_t plus_features = 0, plus_ext_features = 0;
+ uint32_t plus_ext2_features = 0, plus_ext3_features = 0;
+ uint32_t plus_kvm_features = 0, plus_svm_features = 0;
+ /* Features to be removed */
+ uint32_t minus_features = 0, minus_ext_features = 0;
+ uint32_t minus_ext2_features = 0, minus_ext3_features = 0;
+ uint32_t minus_kvm_features = 0, minus_svm_features = 0;
uint32_t numvalue;
for (def = x86_defs; def; def = def->next)
@@ -579,16 +610,22 @@ static int cpu_x86_find_by_name(x86_def_t *x86_cpu_def, const char *cpu_model)
add_flagname_to_bitmaps("hypervisor", &plus_features,
&plus_ext_features, &plus_ext2_features, &plus_ext3_features,
- &plus_kvm_features);
+ &plus_kvm_features, &plus_svm_features);
featurestr = strtok(NULL, ",");
while (featurestr) {
char *val;
if (featurestr[0] == '+') {
- add_flagname_to_bitmaps(featurestr + 1, &plus_features, &plus_ext_features, &plus_ext2_features, &plus_ext3_features, &plus_kvm_features);
+ add_flagname_to_bitmaps(featurestr + 1, &plus_features,
+ &plus_ext_features, &plus_ext2_features,
+ &plus_ext3_features, &plus_kvm_features,
+ &plus_svm_features);
} else if (featurestr[0] == '-') {
- add_flagname_to_bitmaps(featurestr + 1, &minus_features, &minus_ext_features, &minus_ext2_features, &minus_ext3_features, &minus_kvm_features);
+ add_flagname_to_bitmaps(featurestr + 1, &minus_features,
+ &minus_ext_features, &minus_ext2_features,
+ &minus_ext3_features, &minus_kvm_features,
+ &minus_svm_features);
} else if ((val = strchr(featurestr, '='))) {
*val = 0; val++;
if (!strcmp(featurestr, "family")) {
@@ -670,11 +707,13 @@ static int cpu_x86_find_by_name(x86_def_t *x86_cpu_def, const char *cpu_model)
x86_cpu_def->ext2_features |= plus_ext2_features;
x86_cpu_def->ext3_features |= plus_ext3_features;
x86_cpu_def->kvm_features |= plus_kvm_features;
+ x86_cpu_def->svm_features |= plus_svm_features;
x86_cpu_def->features &= ~minus_features;
x86_cpu_def->ext_features &= ~minus_ext_features;
x86_cpu_def->ext2_features &= ~minus_ext2_features;
x86_cpu_def->ext3_features &= ~minus_ext3_features;
x86_cpu_def->kvm_features &= ~minus_kvm_features;
+ x86_cpu_def->svm_features &= ~minus_svm_features;
if (check_cpuid) {
if (check_features_against_host(x86_cpu_def) && enforce_cpuid)
goto error;
@@ -788,6 +827,8 @@ int cpu_x86_register (CPUX86State *env, const char *cpu_model)
{
x86_def_t def1, *def = &def1;
+ memset(def, 0, sizeof(*def));
+
if (cpu_x86_find_by_name(def, cpu_model) < 0)
return -1;
if (def->vendor1) {
@@ -814,6 +855,7 @@ int cpu_x86_register (CPUX86State *env, const char *cpu_model)
env->cpuid_ext3_features = def->ext3_features;
env->cpuid_xlevel = def->xlevel;
env->cpuid_kvm_features = def->kvm_features;
+ env->cpuid_svm_features = def->svm_features;
if (!kvm_enabled()) {
env->cpuid_features &= TCG_FEATURES;
env->cpuid_ext_features &= TCG_EXT_FEATURES;
@@ -823,6 +865,7 @@ int cpu_x86_register (CPUX86State *env, const char *cpu_model)
#endif
);
env->cpuid_ext3_features &= TCG_EXT3_FEATURES;
+ env->cpuid_svm_features &= TCG_SVM_FEATURES;
}
{
const char *model_id = def->model_id;
@@ -965,7 +1008,7 @@ void x86_cpudef_setup(void)
x86_defs = &builtin_x86_defs[i];
}
#if !defined(CONFIG_USER_ONLY)
- qemu_opts_foreach(&qemu_cpudef_opts, cpudef_register, NULL, 0);
+ qemu_opts_foreach(qemu_find_opts("cpudef"), cpudef_register, NULL, 0);
#endif
}
@@ -1133,11 +1176,6 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count,
*ecx |= 1 << 1; /* CmpLegacy bit */
}
}
-
- if (kvm_enabled()) {
- /* Nested SVM not yet supported in upstream QEMU */
- *ecx &= ~CPUID_EXT3_SVM;
- }
break;
case 0x80000002:
case 0x80000003:
@@ -1182,10 +1220,17 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count,
}
break;
case 0x8000000A:
- *eax = 0x00000001; /* SVM Revision */
- *ebx = 0x00000010; /* nr of ASIDs */
- *ecx = 0;
- *edx = 0; /* optional features */
+ if (env->cpuid_ext3_features & CPUID_EXT3_SVM) {
+ *eax = 0x00000001; /* SVM Revision */
+ *ebx = 0x00000010; /* nr of ASIDs */
+ *ecx = 0;
+ *edx = env->cpuid_svm_features; /* optional features */
+ } else {
+ *eax = 0;
+ *ebx = 0;
+ *ecx = 0;
+ *edx = 0;
+ }
break;
default:
/* reserved values: zero */
diff --git a/target-i386/helper.c b/target-i386/helper.c
index e13434097f..4fff4a871f 100644
--- a/target-i386/helper.c
+++ b/target-i386/helper.c
@@ -27,6 +27,7 @@
#include "exec-all.h"
#include "qemu-common.h"
#include "kvm.h"
+#include "kvm_x86.h"
//#define DEBUG_MMU
@@ -1030,6 +1031,11 @@ void cpu_inject_x86_mce(CPUState *cenv, int bank, uint64_t status,
if (bank >= bank_num || !(status & MCI_STATUS_VAL))
return;
+ if (kvm_enabled()) {
+ kvm_inject_x86_mce(cenv, bank, status, mcg_status, addr, misc, 0);
+ return;
+ }
+
/*
* if MSR_MCG_CTL is not all 1s, the uncorrected error
* reporting is disabled
diff --git a/target-i386/kvm.c b/target-i386/kvm.c
index a33d2fad6a..ae0a034ab0 100644
--- a/target-i386/kvm.c
+++ b/target-i386/kvm.c
@@ -15,6 +15,7 @@
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
+#include <sys/utsname.h>
#include <linux/kvm.h>
@@ -27,6 +28,7 @@
#include "hw/pc.h"
#include "hw/apic.h"
#include "ioport.h"
+#include "kvm_x86.h"
#ifdef CONFIG_KVM_PARA
#include <linux/kvm_para.h>
@@ -45,6 +47,15 @@
#define MSR_KVM_WALL_CLOCK 0x11
#define MSR_KVM_SYSTEM_TIME 0x12
+#ifndef BUS_MCEERR_AR
+#define BUS_MCEERR_AR 4
+#endif
+#ifndef BUS_MCEERR_AO
+#define BUS_MCEERR_AO 5
+#endif
+
+static int lm_capable_kernel;
+
#ifdef KVM_CAP_EXT_CPUID
static struct kvm_cpuid2 *try_get_cpuid(KVMState *s, int max)
@@ -167,6 +178,120 @@ static int get_para_features(CPUState *env)
}
#endif
+#ifdef KVM_CAP_MCE
+static int kvm_get_mce_cap_supported(KVMState *s, uint64_t *mce_cap,
+ int *max_banks)
+{
+ int r;
+
+ r = kvm_ioctl(s, KVM_CHECK_EXTENSION, KVM_CAP_MCE);
+ if (r > 0) {
+ *max_banks = r;
+ return kvm_ioctl(s, KVM_X86_GET_MCE_CAP_SUPPORTED, mce_cap);
+ }
+ return -ENOSYS;
+}
+
+static int kvm_setup_mce(CPUState *env, uint64_t *mcg_cap)
+{
+ return kvm_vcpu_ioctl(env, KVM_X86_SETUP_MCE, mcg_cap);
+}
+
+static int kvm_set_mce(CPUState *env, struct kvm_x86_mce *m)
+{
+ return kvm_vcpu_ioctl(env, KVM_X86_SET_MCE, m);
+}
+
+static int kvm_get_msr(CPUState *env, struct kvm_msr_entry *msrs, int n)
+{
+ struct kvm_msrs *kmsrs = qemu_malloc(sizeof *kmsrs + n * sizeof *msrs);
+ int r;
+
+ kmsrs->nmsrs = n;
+ memcpy(kmsrs->entries, msrs, n * sizeof *msrs);
+ r = kvm_vcpu_ioctl(env, KVM_GET_MSRS, kmsrs);
+ memcpy(msrs, kmsrs->entries, n * sizeof *msrs);
+ free(kmsrs);
+ return r;
+}
+
+/* FIXME: kill this and kvm_get_msr, use env->mcg_status instead */
+static int kvm_mce_in_exception(CPUState *env)
+{
+ struct kvm_msr_entry msr_mcg_status = {
+ .index = MSR_MCG_STATUS,
+ };
+ int r;
+
+ r = kvm_get_msr(env, &msr_mcg_status, 1);
+ if (r == -1 || r == 0) {
+ return -1;
+ }
+ return !!(msr_mcg_status.data & MCG_STATUS_MCIP);
+}
+
+struct kvm_x86_mce_data
+{
+ CPUState *env;
+ struct kvm_x86_mce *mce;
+ int abort_on_error;
+};
+
+static void kvm_do_inject_x86_mce(void *_data)
+{
+ struct kvm_x86_mce_data *data = _data;
+ int r;
+
+ /* If there is an MCE exception being processed, ignore this SRAO MCE */
+ if ((data->env->mcg_cap & MCG_SER_P) &&
+ !(data->mce->status & MCI_STATUS_AR)) {
+ r = kvm_mce_in_exception(data->env);
+ if (r == -1) {
+ fprintf(stderr, "Failed to get MCE status\n");
+ } else if (r) {
+ return;
+ }
+ }
+
+ r = kvm_set_mce(data->env, data->mce);
+ if (r < 0) {
+ perror("kvm_set_mce FAILED");
+ if (data->abort_on_error) {
+ abort();
+ }
+ }
+}
+#endif
+
+void kvm_inject_x86_mce(CPUState *cenv, int bank, uint64_t status,
+ uint64_t mcg_status, uint64_t addr, uint64_t misc,
+ int abort_on_error)
+{
+#ifdef KVM_CAP_MCE
+ struct kvm_x86_mce mce = {
+ .bank = bank,
+ .status = status,
+ .mcg_status = mcg_status,
+ .addr = addr,
+ .misc = misc,
+ };
+ struct kvm_x86_mce_data data = {
+ .env = cenv,
+ .mce = &mce,
+ };
+
+ if (!cenv->mcg_cap) {
+ fprintf(stderr, "MCE support is not enabled!\n");
+ return;
+ }
+
+ run_on_cpu(cenv, kvm_do_inject_x86_mce, &data);
+#else
+ if (abort_on_error)
+ abort();
+#endif
+}
+
int kvm_arch_init_vcpu(CPUState *env)
{
struct {
@@ -192,6 +317,9 @@ int kvm_arch_init_vcpu(CPUState *env)
0, R_EDX);
env->cpuid_ext3_features &= kvm_arch_get_supported_cpuid(env, 0x80000001,
0, R_ECX);
+ env->cpuid_svm_features &= kvm_arch_get_supported_cpuid(env, 0x8000000A,
+ 0, R_EDX);
+
cpuid_i = 0;
@@ -274,6 +402,28 @@ int kvm_arch_init_vcpu(CPUState *env)
cpuid_data.cpuid.nent = cpuid_i;
+#ifdef KVM_CAP_MCE
+ if (((env->cpuid_version >> 8)&0xF) >= 6
+ && (env->cpuid_features&(CPUID_MCE|CPUID_MCA)) == (CPUID_MCE|CPUID_MCA)
+ && kvm_check_extension(env->kvm_state, KVM_CAP_MCE) > 0) {
+ uint64_t mcg_cap;
+ int banks;
+
+ if (kvm_get_mce_cap_supported(env->kvm_state, &mcg_cap, &banks))
+ perror("kvm_get_mce_cap_supported FAILED");
+ else {
+ if (banks > MCE_BANKS_DEF)
+ banks = MCE_BANKS_DEF;
+ mcg_cap &= MCE_CAP_DEF;
+ mcg_cap |= banks;
+ if (kvm_setup_mce(env, &mcg_cap))
+ perror("kvm_setup_mce FAILED");
+ else
+ env->mcg_cap = mcg_cap;
+ }
+ }
+#endif
+
return kvm_vcpu_ioctl(env, KVM_SET_CPUID2, &cpuid_data);
}
@@ -291,23 +441,26 @@ void kvm_arch_reset_vcpu(CPUState *env)
}
}
-static int kvm_has_msr_star(CPUState *env)
+int has_msr_star;
+int has_msr_hsave_pa;
+
+static void kvm_supported_msrs(CPUState *env)
{
- static int has_msr_star;
+ static int kvm_supported_msrs;
int ret;
/* first time */
- if (has_msr_star == 0) {
+ if (kvm_supported_msrs == 0) {
struct kvm_msr_list msr_list, *kvm_msr_list;
- has_msr_star = -1;
+ kvm_supported_msrs = -1;
/* Obtain MSR list from KVM. These are the MSRs that we must
* save/restore */
msr_list.nmsrs = 0;
ret = kvm_ioctl(env->kvm_state, KVM_GET_MSR_INDEX_LIST, &msr_list);
if (ret < 0 && ret != -E2BIG) {
- return 0;
+ return;
}
/* Old kernel modules had a bug and could write beyond the provided
memory. Allocate at least a safe amount of 1K. */
@@ -323,7 +476,11 @@ static int kvm_has_msr_star(CPUState *env)
for (i = 0; i < kvm_msr_list->nmsrs; i++) {
if (kvm_msr_list->indices[i] == MSR_STAR) {
has_msr_star = 1;
- break;
+ continue;
+ }
+ if (kvm_msr_list->indices[i] == MSR_VM_HSAVE_PA) {
+ has_msr_hsave_pa = 1;
+ continue;
}
}
}
@@ -331,9 +488,19 @@ static int kvm_has_msr_star(CPUState *env)
free(kvm_msr_list);
}
- if (has_msr_star == 1)
- return 1;
- return 0;
+ return;
+}
+
+static int kvm_has_msr_hsave_pa(CPUState *env)
+{
+ kvm_supported_msrs(env);
+ return has_msr_hsave_pa;
+}
+
+static int kvm_has_msr_star(CPUState *env)
+{
+ kvm_supported_msrs(env);
+ return has_msr_star;
}
static int kvm_init_identity_map_page(KVMState *s)
@@ -359,6 +526,11 @@ int kvm_arch_init(KVMState *s, int smp_cpus)
{
int ret;
+ struct utsname utsname;
+
+ uname(&utsname);
+ lm_capable_kernel = strcmp(utsname.machine, "x86_64") == 0;
+
/* create vm86 tss. KVM uses vm86 mode to emulate 16-bit code
* directly. In order to use vm86 mode, a TSS is needed. Since this
* must be part of guest physical memory, we need to allocate it. Older
@@ -517,7 +689,7 @@ static int kvm_put_fpu(CPUState *env)
static int kvm_put_xsave(CPUState *env)
{
#ifdef KVM_CAP_XSAVE
- int i;
+ int i, r;
struct kvm_xsave* xsave;
uint16_t cwd, swd, twd, fop;
@@ -542,7 +714,9 @@ static int kvm_put_xsave(CPUState *env)
*(uint64_t *)&xsave->region[XSAVE_XSTATE_BV] = env->xstate_bv;
memcpy(&xsave->region[XSAVE_YMMH_SPACE], env->ymmh_regs,
sizeof env->ymmh_regs);
- return kvm_vcpu_ioctl(env, KVM_SET_XSAVE, xsave);
+ r = kvm_vcpu_ioctl(env, KVM_SET_XSAVE, xsave);
+ qemu_free(xsave);
+ return r;
#else
return kvm_put_fpu(env);
#endif
@@ -641,19 +815,43 @@ static int kvm_put_msrs(CPUState *env, int level)
kvm_msr_entry_set(&msrs[n++], MSR_IA32_SYSENTER_EIP, env->sysenter_eip);
if (kvm_has_msr_star(env))
kvm_msr_entry_set(&msrs[n++], MSR_STAR, env->star);
+ if (kvm_has_msr_hsave_pa(env))
+ kvm_msr_entry_set(&msrs[n++], MSR_VM_HSAVE_PA, env->vm_hsave);
#ifdef TARGET_X86_64
- /* FIXME if lm capable */
- kvm_msr_entry_set(&msrs[n++], MSR_CSTAR, env->cstar);
- kvm_msr_entry_set(&msrs[n++], MSR_KERNELGSBASE, env->kernelgsbase);
- kvm_msr_entry_set(&msrs[n++], MSR_FMASK, env->fmask);
- kvm_msr_entry_set(&msrs[n++], MSR_LSTAR, env->lstar);
+ if (lm_capable_kernel) {
+ kvm_msr_entry_set(&msrs[n++], MSR_CSTAR, env->cstar);
+ kvm_msr_entry_set(&msrs[n++], MSR_KERNELGSBASE, env->kernelgsbase);
+ kvm_msr_entry_set(&msrs[n++], MSR_FMASK, env->fmask);
+ kvm_msr_entry_set(&msrs[n++], MSR_LSTAR, env->lstar);
+ }
#endif
if (level == KVM_PUT_FULL_STATE) {
- kvm_msr_entry_set(&msrs[n++], MSR_IA32_TSC, env->tsc);
+ /*
+ * KVM is yet unable to synchronize TSC values of multiple VCPUs on
+ * writeback. Until this is fixed, we only write the offset to SMP
+ * guests after migration, desynchronizing the VCPUs, but avoiding
+ * huge jump-backs that would occur without any writeback at all.
+ */
+ if (smp_cpus == 1 || env->tsc != 0) {
+ kvm_msr_entry_set(&msrs[n++], MSR_IA32_TSC, env->tsc);
+ }
kvm_msr_entry_set(&msrs[n++], MSR_KVM_SYSTEM_TIME,
env->system_time_msr);
kvm_msr_entry_set(&msrs[n++], MSR_KVM_WALL_CLOCK, env->wall_clock_msr);
}
+#ifdef KVM_CAP_MCE
+ if (env->mcg_cap) {
+ int i;
+ if (level == KVM_PUT_RESET_STATE)
+ kvm_msr_entry_set(&msrs[n++], MSR_MCG_STATUS, env->mcg_status);
+ else if (level == KVM_PUT_FULL_STATE) {
+ kvm_msr_entry_set(&msrs[n++], MSR_MCG_STATUS, env->mcg_status);
+ kvm_msr_entry_set(&msrs[n++], MSR_MCG_CTL, env->mcg_ctl);
+ for (i = 0; i < (env->mcg_cap & 0xff) * 4; i++)
+ kvm_msr_entry_set(&msrs[n++], MSR_MC0_CTL + i, env->mce_banks[i]);
+ }
+ }
+#endif
msr_data.info.nmsrs = n;
@@ -695,8 +893,10 @@ static int kvm_get_xsave(CPUState *env)
xsave = qemu_memalign(4096, sizeof(struct kvm_xsave));
ret = kvm_vcpu_ioctl(env, KVM_GET_XSAVE, xsave);
- if (ret < 0)
+ if (ret < 0) {
+ qemu_free(xsave);
return ret;
+ }
cwd = (uint16_t)xsave->region[0];
swd = (uint16_t)(xsave->region[0] >> 16);
@@ -715,6 +915,7 @@ static int kvm_get_xsave(CPUState *env)
env->xstate_bv = *(uint64_t *)&xsave->region[XSAVE_XSTATE_BV];
memcpy(env->ymmh_regs, &xsave->region[XSAVE_YMMH_SPACE],
sizeof env->ymmh_regs);
+ qemu_free(xsave);
return 0;
#else
return kvm_get_fpu(env);
@@ -850,17 +1051,29 @@ static int kvm_get_msrs(CPUState *env)
msrs[n++].index = MSR_IA32_SYSENTER_EIP;
if (kvm_has_msr_star(env))
msrs[n++].index = MSR_STAR;
+ if (kvm_has_msr_hsave_pa(env))
+ msrs[n++].index = MSR_VM_HSAVE_PA;
msrs[n++].index = MSR_IA32_TSC;
#ifdef TARGET_X86_64
- /* FIXME lm_capable_kernel */
- msrs[n++].index = MSR_CSTAR;
- msrs[n++].index = MSR_KERNELGSBASE;
- msrs[n++].index = MSR_FMASK;
- msrs[n++].index = MSR_LSTAR;
+ if (lm_capable_kernel) {
+ msrs[n++].index = MSR_CSTAR;
+ msrs[n++].index = MSR_KERNELGSBASE;
+ msrs[n++].index = MSR_FMASK;
+ msrs[n++].index = MSR_LSTAR;
+ }
#endif
msrs[n++].index = MSR_KVM_SYSTEM_TIME;
msrs[n++].index = MSR_KVM_WALL_CLOCK;
+#ifdef KVM_CAP_MCE
+ if (env->mcg_cap) {
+ msrs[n++].index = MSR_MCG_STATUS;
+ msrs[n++].index = MSR_MCG_CTL;
+ for (i = 0; i < (env->mcg_cap & 0xff) * 4; i++)
+ msrs[n++].index = MSR_MC0_CTL + i;
+ }
+#endif
+
msr_data.info.nmsrs = n;
ret = kvm_vcpu_ioctl(env, KVM_GET_MSRS, &msr_data);
if (ret < 0)
@@ -897,12 +1110,31 @@ static int kvm_get_msrs(CPUState *env)
case MSR_IA32_TSC:
env->tsc = msrs[i].data;
break;
+ case MSR_VM_HSAVE_PA:
+ env->vm_hsave = msrs[i].data;
+ break;
case MSR_KVM_SYSTEM_TIME:
env->system_time_msr = msrs[i].data;
break;
case MSR_KVM_WALL_CLOCK:
env->wall_clock_msr = msrs[i].data;
break;
+#ifdef KVM_CAP_MCE
+ case MSR_MCG_STATUS:
+ env->mcg_status = msrs[i].data;
+ break;
+ case MSR_MCG_CTL:
+ env->mcg_ctl = msrs[i].data;
+ break;
+#endif
+ default:
+#ifdef KVM_CAP_MCE
+ if (msrs[i].index >= MSR_MC0_CTL &&
+ msrs[i].index < MSR_MC0_CTL + (env->mcg_cap & 0xff) * 4) {
+ env->mce_banks[msrs[i].index - MSR_MC0_CTL] = msrs[i].data;
+ }
+#endif
+ break;
}
}
@@ -1441,3 +1673,140 @@ bool kvm_arch_stop_on_emulation_error(CPUState *env)
((env->segs[R_CS].selector & 3) != 3);
}
+static void hardware_memory_error(void)
+{
+ fprintf(stderr, "Hardware memory error!\n");
+ exit(1);
+}
+
+#ifdef KVM_CAP_MCE
+static void kvm_mce_broadcast_rest(CPUState *env)
+{
+ CPUState *cenv;
+ int family, model, cpuver = env->cpuid_version;
+
+ family = (cpuver >> 8) & 0xf;
+ model = ((cpuver >> 12) & 0xf0) + ((cpuver >> 4) & 0xf);
+
+ /* Broadcast MCA signal for processor version 06H_EH and above */
+ if ((family == 6 && model >= 14) || family > 6) {
+ for (cenv = first_cpu; cenv != NULL; cenv = cenv->next_cpu) {
+ if (cenv == env) {
+ continue;
+ }
+ kvm_inject_x86_mce(cenv, 1, MCI_STATUS_VAL | MCI_STATUS_UC,
+ MCG_STATUS_MCIP | MCG_STATUS_RIPV, 0, 0, 1);
+ }
+ }
+}
+#endif
+
+int kvm_on_sigbus_vcpu(CPUState *env, int code, void *addr)
+{
+#if defined(KVM_CAP_MCE)
+ struct kvm_x86_mce mce = {
+ .bank = 9,
+ };
+ void *vaddr;
+ ram_addr_t ram_addr;
+ target_phys_addr_t paddr;
+ int r;
+
+ if ((env->mcg_cap & MCG_SER_P) && addr
+ && (code == BUS_MCEERR_AR
+ || code == BUS_MCEERR_AO)) {
+ if (code == BUS_MCEERR_AR) {
+ /* Fake an Intel architectural Data Load SRAR UCR */
+ mce.status = MCI_STATUS_VAL | MCI_STATUS_UC | MCI_STATUS_EN
+ | MCI_STATUS_MISCV | MCI_STATUS_ADDRV | MCI_STATUS_S
+ | MCI_STATUS_AR | 0x134;
+ mce.misc = (MCM_ADDR_PHYS << 6) | 0xc;
+ mce.mcg_status = MCG_STATUS_MCIP | MCG_STATUS_EIPV;
+ } else {
+ /*
+ * If there is an MCE excpetion being processed, ignore
+ * this SRAO MCE
+ */
+ r = kvm_mce_in_exception(env);
+ if (r == -1) {
+ fprintf(stderr, "Failed to get MCE status\n");
+ } else if (r) {
+ return 0;
+ }
+ /* Fake an Intel architectural Memory scrubbing UCR */
+ mce.status = MCI_STATUS_VAL | MCI_STATUS_UC | MCI_STATUS_EN
+ | MCI_STATUS_MISCV | MCI_STATUS_ADDRV | MCI_STATUS_S
+ | 0xc0;
+ mce.misc = (MCM_ADDR_PHYS << 6) | 0xc;
+ mce.mcg_status = MCG_STATUS_MCIP | MCG_STATUS_RIPV;
+ }
+ vaddr = (void *)addr;
+ if (qemu_ram_addr_from_host(vaddr, &ram_addr) ||
+ !kvm_physical_memory_addr_from_ram(env->kvm_state, ram_addr, &paddr)) {
+ fprintf(stderr, "Hardware memory error for memory used by "
+ "QEMU itself instead of guest system!\n");
+ /* Hope we are lucky for AO MCE */
+ if (code == BUS_MCEERR_AO) {
+ return 0;
+ } else {
+ hardware_memory_error();
+ }
+ }
+ mce.addr = paddr;
+ r = kvm_set_mce(env, &mce);
+ if (r < 0) {
+ fprintf(stderr, "kvm_set_mce: %s\n", strerror(errno));
+ abort();
+ }
+ kvm_mce_broadcast_rest(env);
+ } else
+#endif
+ {
+ if (code == BUS_MCEERR_AO) {
+ return 0;
+ } else if (code == BUS_MCEERR_AR) {
+ hardware_memory_error();
+ } else {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+int kvm_on_sigbus(int code, void *addr)
+{
+#if defined(KVM_CAP_MCE)
+ if ((first_cpu->mcg_cap & MCG_SER_P) && addr && code == BUS_MCEERR_AO) {
+ uint64_t status;
+ void *vaddr;
+ ram_addr_t ram_addr;
+ target_phys_addr_t paddr;
+
+ /* Hope we are lucky for AO MCE */
+ vaddr = addr;
+ if (qemu_ram_addr_from_host(vaddr, &ram_addr) ||
+ !kvm_physical_memory_addr_from_ram(first_cpu->kvm_state, ram_addr, &paddr)) {
+ fprintf(stderr, "Hardware memory error for memory used by "
+ "QEMU itself instead of guest system!: %p\n", addr);
+ return 0;
+ }
+ status = MCI_STATUS_VAL | MCI_STATUS_UC | MCI_STATUS_EN
+ | MCI_STATUS_MISCV | MCI_STATUS_ADDRV | MCI_STATUS_S
+ | 0xc0;
+ kvm_inject_x86_mce(first_cpu, 9, status,
+ MCG_STATUS_MCIP | MCG_STATUS_RIPV, paddr,
+ (MCM_ADDR_PHYS << 6) | 0xc, 1);
+ kvm_mce_broadcast_rest(first_cpu);
+ } else
+#endif
+ {
+ if (code == BUS_MCEERR_AO) {
+ return 0;
+ } else if (code == BUS_MCEERR_AR) {
+ hardware_memory_error();
+ } else {
+ return 1;
+ }
+ }
+ return 0;
+}
diff --git a/target-i386/kvm_x86.h b/target-i386/kvm_x86.h
new file mode 100644
index 0000000000..04932cf4c8
--- /dev/null
+++ b/target-i386/kvm_x86.h
@@ -0,0 +1,22 @@
+/*
+ * QEMU KVM support
+ *
+ * Copyright (C) 2009 Red Hat Inc.
+ * Copyright IBM, Corp. 2008
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#ifndef __KVM_X86_H__
+#define __KVM_X86_H__
+
+void kvm_inject_x86_mce(CPUState *cenv, int bank, uint64_t status,
+ uint64_t mcg_status, uint64_t addr, uint64_t misc,
+ int abort_on_error);
+
+#endif
diff --git a/target-i386/op_helper.c b/target-i386/op_helper.c
index c50e818e72..43fbd0c778 100644
--- a/target-i386/op_helper.c
+++ b/target-i386/op_helper.c
@@ -349,6 +349,10 @@ static void switch_tss(int tss_selector,
new_segs[R_GS] = 0;
new_trap = 0;
}
+ /* XXX: avoid a compiler warning, see
+ http://support.amd.com/us/Processor_TechDocs/24593.pdf
+ chapters 12.2.5 and 13.2.4 on how to implement TSS Trap bit */
+ (void)new_trap;
/* NOTE: we must avoid memory exceptions during the task switch,
so we make dummy accesses before */
@@ -5237,7 +5241,7 @@ void helper_svm_check_intercept_param(uint32_t type, uint64_t param)
switch((uint32_t)ECX) {
case 0 ... 0x1fff:
t0 = (ECX * 2) % 8;
- t1 = ECX / 8;
+ t1 = (ECX * 2) / 8;
break;
case 0xc0000000 ... 0xc0001fff:
t0 = (8192 + ECX - 0xc0000000) * 2;
diff --git a/target-i386/translate.c b/target-i386/translate.c
index 2fcc026165..7b6e3c2eae 100644
--- a/target-i386/translate.c
+++ b/target-i386/translate.c
@@ -2310,10 +2310,7 @@ static inline void gen_jcc(DisasContext *s, int b,
int l1, l2, cc_op;
cc_op = s->cc_op;
- if (s->cc_op != CC_OP_DYNAMIC) {
- gen_op_set_cc_op(s->cc_op);
- s->cc_op = CC_OP_DYNAMIC;
- }
+ gen_update_cc_op(s);
if (s->jmp_opt) {
l1 = gen_new_label();
gen_jcc1(s, cc_op, b, l1);
@@ -2322,7 +2319,7 @@ static inline void gen_jcc(DisasContext *s, int b,
gen_set_label(l1);
gen_goto_tb(s, 1, val);
- s->is_jmp = 3;
+ s->is_jmp = DISAS_TB_JUMP;
} else {
l1 = gen_new_label();
@@ -2400,11 +2397,11 @@ static void gen_movl_seg_T0(DisasContext *s, int seg_reg, target_ulong cur_eip)
stop as a special handling must be done to disable hardware
interrupts for the next instruction */
if (seg_reg == R_SS || (s->code32 && seg_reg < R_FS))
- s->is_jmp = 3;
+ s->is_jmp = DISAS_TB_JUMP;
} else {
gen_op_movl_seg_T0_vm(seg_reg);
if (seg_reg == R_SS)
- s->is_jmp = 3;
+ s->is_jmp = DISAS_TB_JUMP;
}
}
@@ -2672,7 +2669,7 @@ static void gen_exception(DisasContext *s, int trapno, target_ulong cur_eip)
gen_op_set_cc_op(s->cc_op);
gen_jmp_im(cur_eip);
gen_helper_raise_exception(tcg_const_i32(trapno));
- s->is_jmp = 3;
+ s->is_jmp = DISAS_TB_JUMP;
}
/* an interrupt is different from an exception because of the
@@ -2685,7 +2682,7 @@ static void gen_interrupt(DisasContext *s, int intno,
gen_jmp_im(cur_eip);
gen_helper_raise_interrupt(tcg_const_i32(intno),
tcg_const_i32(next_eip - cur_eip));
- s->is_jmp = 3;
+ s->is_jmp = DISAS_TB_JUMP;
}
static void gen_debug(DisasContext *s, target_ulong cur_eip)
@@ -2694,7 +2691,7 @@ static void gen_debug(DisasContext *s, target_ulong cur_eip)
gen_op_set_cc_op(s->cc_op);
gen_jmp_im(cur_eip);
gen_helper_debug();
- s->is_jmp = 3;
+ s->is_jmp = DISAS_TB_JUMP;
}
/* generate a generic end of block. Trace exception is also generated
@@ -2716,7 +2713,7 @@ static void gen_eob(DisasContext *s)
} else {
tcg_gen_exit_tb(0);
}
- s->is_jmp = 3;
+ s->is_jmp = DISAS_TB_JUMP;
}
/* generate a jump to eip. No segment change must happen before as a
@@ -2724,12 +2721,9 @@ static void gen_eob(DisasContext *s)
static void gen_jmp_tb(DisasContext *s, target_ulong eip, int tb_num)
{
if (s->jmp_opt) {
- if (s->cc_op != CC_OP_DYNAMIC) {
- gen_op_set_cc_op(s->cc_op);
- s->cc_op = CC_OP_DYNAMIC;
- }
+ gen_update_cc_op(s);
gen_goto_tb(s, tb_num, eip);
- s->is_jmp = 3;
+ s->is_jmp = DISAS_TB_JUMP;
} else {
gen_jmp_im(eip);
gen_eob(s);
@@ -6901,10 +6895,7 @@ static target_ulong disas_insn(DisasContext *s, target_ulong pc_start)
if (!s->pe) {
gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
} else {
- if (s->cc_op != CC_OP_DYNAMIC) {
- gen_op_set_cc_op(s->cc_op);
- s->cc_op = CC_OP_DYNAMIC;
- }
+ gen_update_cc_op(s);
gen_jmp_im(pc_start - s->cs_base);
gen_helper_sysenter();
gen_eob(s);
@@ -6917,10 +6908,7 @@ static target_ulong disas_insn(DisasContext *s, target_ulong pc_start)
if (!s->pe) {
gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
} else {
- if (s->cc_op != CC_OP_DYNAMIC) {
- gen_op_set_cc_op(s->cc_op);
- s->cc_op = CC_OP_DYNAMIC;
- }
+ gen_update_cc_op(s);
gen_jmp_im(pc_start - s->cs_base);
gen_helper_sysexit(tcg_const_i32(dflag));
gen_eob(s);
@@ -6929,10 +6917,7 @@ static target_ulong disas_insn(DisasContext *s, target_ulong pc_start)
#ifdef TARGET_X86_64
case 0x105: /* syscall */
/* XXX: is it usable in real mode ? */
- if (s->cc_op != CC_OP_DYNAMIC) {
- gen_op_set_cc_op(s->cc_op);
- s->cc_op = CC_OP_DYNAMIC;
- }
+ gen_update_cc_op(s);
gen_jmp_im(pc_start - s->cs_base);
gen_helper_syscall(tcg_const_i32(s->pc - pc_start));
gen_eob(s);
@@ -6941,10 +6926,7 @@ static target_ulong disas_insn(DisasContext *s, target_ulong pc_start)
if (!s->pe) {
gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
} else {
- if (s->cc_op != CC_OP_DYNAMIC) {
- gen_op_set_cc_op(s->cc_op);
- s->cc_op = CC_OP_DYNAMIC;
- }
+ gen_update_cc_op(s);
gen_jmp_im(pc_start - s->cs_base);
gen_helper_sysret(tcg_const_i32(s->dflag));
/* condition codes are modified only in long mode */
@@ -6968,7 +6950,7 @@ static target_ulong disas_insn(DisasContext *s, target_ulong pc_start)
gen_op_set_cc_op(s->cc_op);
gen_jmp_im(pc_start - s->cs_base);
gen_helper_hlt(tcg_const_i32(s->pc - pc_start));
- s->is_jmp = 3;
+ s->is_jmp = DISAS_TB_JUMP;
}
break;
case 0x100:
@@ -7085,10 +7067,7 @@ static target_ulong disas_insn(DisasContext *s, target_ulong pc_start)
if (!(s->cpuid_ext_features & CPUID_EXT_MONITOR) ||
s->cpl != 0)
goto illegal_op;
- if (s->cc_op != CC_OP_DYNAMIC) {
- gen_op_set_cc_op(s->cc_op);
- s->cc_op = CC_OP_DYNAMIC;
- }
+ gen_update_cc_op(s);
gen_jmp_im(pc_start - s->cs_base);
gen_helper_mwait(tcg_const_i32(s->pc - pc_start));
gen_eob(s);
@@ -7125,7 +7104,7 @@ static target_ulong disas_insn(DisasContext *s, target_ulong pc_start)
gen_helper_vmrun(tcg_const_i32(s->aflag),
tcg_const_i32(s->pc - pc_start));
tcg_gen_exit_tb(0);
- s->is_jmp = 3;
+ s->is_jmp = DISAS_TB_JUMP;
}
break;
case 1: /* VMMCALL */
@@ -7613,10 +7592,7 @@ static target_ulong disas_insn(DisasContext *s, target_ulong pc_start)
gen_svm_check_intercept(s, pc_start, SVM_EXIT_RSM);
if (!(s->flags & HF_SMM_MASK))
goto illegal_op;
- if (s->cc_op != CC_OP_DYNAMIC) {
- gen_op_set_cc_op(s->cc_op);
- s->cc_op = CC_OP_DYNAMIC;
- }
+ gen_update_cc_op(s);
gen_jmp_im(s->pc - s->cs_base);
gen_helper_rsm();
gen_eob(s);
diff --git a/target-microblaze/cpu.h b/target-microblaze/cpu.h
index 360ac0abaa..3aa28bfd40 100644
--- a/target-microblaze/cpu.h
+++ b/target-microblaze/cpu.h
@@ -24,6 +24,7 @@
#define CPUState struct CPUMBState
#include "cpu-defs.h"
+#include "softfloat.h"
struct CPUMBState;
#if !defined(CONFIG_USER_ONLY)
#include "mmu.h"
@@ -91,6 +92,13 @@ struct CPUMBState;
#define ESR_EC_DATA_TLB 10
#define ESR_EC_INSN_TLB 11
+/* Floating Point Status Register (FSR) Bits */
+#define FSR_IO (1<<4) /* Invalid operation */
+#define FSR_DZ (1<<3) /* Divide-by-zero */
+#define FSR_OF (1<<2) /* Overflow */
+#define FSR_UF (1<<1) /* Underflow */
+#define FSR_DO (1<<0) /* Denormalized operand error */
+
/* Version reg. */
/* Basic PVR mask */
#define PVR0_PVR_FULL_MASK 0x80000000
@@ -208,6 +216,7 @@ typedef struct CPUMBState {
uint32_t imm;
uint32_t regs[33];
uint32_t sregs[24];
+ float_status fp_status;
/* Internal flags. */
#define IMM_FLAG 4
diff --git a/target-microblaze/helper.h b/target-microblaze/helper.h
index 28f251cc64..11ad1b6dcb 100644
--- a/target-microblaze/helper.h
+++ b/target-microblaze/helper.h
@@ -10,6 +10,22 @@ DEF_HELPER_2(cmpu, i32, i32, i32)
DEF_HELPER_2(divs, i32, i32, i32)
DEF_HELPER_2(divu, i32, i32, i32)
+DEF_HELPER_2(fadd, i32, i32, i32)
+DEF_HELPER_2(frsub, i32, i32, i32)
+DEF_HELPER_2(fmul, i32, i32, i32)
+DEF_HELPER_2(fdiv, i32, i32, i32)
+DEF_HELPER_1(flt, i32, i32)
+DEF_HELPER_1(fint, i32, i32)
+DEF_HELPER_1(fsqrt, i32, i32)
+
+DEF_HELPER_2(fcmp_un, i32, i32, i32)
+DEF_HELPER_2(fcmp_lt, i32, i32, i32)
+DEF_HELPER_2(fcmp_eq, i32, i32, i32)
+DEF_HELPER_2(fcmp_le, i32, i32, i32)
+DEF_HELPER_2(fcmp_gt, i32, i32, i32)
+DEF_HELPER_2(fcmp_ne, i32, i32, i32)
+DEF_HELPER_2(fcmp_ge, i32, i32, i32)
+
DEF_HELPER_FLAGS_2(pcmpbf, TCG_CALL_PURE | TCG_CALL_CONST, i32, i32, i32)
#if !defined(CONFIG_USER_ONLY)
DEF_HELPER_1(mmu_read, i32, i32)
diff --git a/target-microblaze/op_helper.c b/target-microblaze/op_helper.c
index be0c829735..3d2b313fb3 100644
--- a/target-microblaze/op_helper.c
+++ b/target-microblaze/op_helper.c
@@ -202,6 +202,250 @@ uint32_t helper_divu(uint32_t a, uint32_t b)
return a / b;
}
+/* raise FPU exception. */
+static void raise_fpu_exception(void)
+{
+ env->sregs[SR_ESR] = ESR_EC_FPU;
+ helper_raise_exception(EXCP_HW_EXCP);
+}
+
+static void update_fpu_flags(int flags)
+{
+ int raise = 0;
+
+ if (flags & float_flag_invalid) {
+ env->sregs[SR_FSR] |= FSR_IO;
+ raise = 1;
+ }
+ if (flags & float_flag_divbyzero) {
+ env->sregs[SR_FSR] |= FSR_DZ;
+ raise = 1;
+ }
+ if (flags & float_flag_overflow) {
+ env->sregs[SR_FSR] |= FSR_OF;
+ raise = 1;
+ }
+ if (flags & float_flag_underflow) {
+ env->sregs[SR_FSR] |= FSR_UF;
+ raise = 1;
+ }
+ if (raise
+ && (env->pvr.regs[2] & PVR2_FPU_EXC_MASK)
+ && (env->sregs[SR_MSR] & MSR_EE)) {
+ raise_fpu_exception();
+ }
+}
+
+uint32_t helper_fadd(uint32_t a, uint32_t b)
+{
+ CPU_FloatU fd, fa, fb;
+ int flags;
+
+ set_float_exception_flags(0, &env->fp_status);
+ fa.l = a;
+ fb.l = b;
+ fd.f = float32_add(fa.f, fb.f, &env->fp_status);
+
+ flags = get_float_exception_flags(&env->fp_status);
+ update_fpu_flags(flags);
+ return fd.l;
+}
+
+uint32_t helper_frsub(uint32_t a, uint32_t b)
+{
+ CPU_FloatU fd, fa, fb;
+ int flags;
+
+ set_float_exception_flags(0, &env->fp_status);
+ fa.l = a;
+ fb.l = b;
+ fd.f = float32_sub(fb.f, fa.f, &env->fp_status);
+ flags = get_float_exception_flags(&env->fp_status);
+ update_fpu_flags(flags);
+ return fd.l;
+}
+
+uint32_t helper_fmul(uint32_t a, uint32_t b)
+{
+ CPU_FloatU fd, fa, fb;
+ int flags;
+
+ set_float_exception_flags(0, &env->fp_status);
+ fa.l = a;
+ fb.l = b;
+ fd.f = float32_mul(fa.f, fb.f, &env->fp_status);
+ flags = get_float_exception_flags(&env->fp_status);
+ update_fpu_flags(flags);
+
+ return fd.l;
+}
+
+uint32_t helper_fdiv(uint32_t a, uint32_t b)
+{
+ CPU_FloatU fd, fa, fb;
+ int flags;
+
+ set_float_exception_flags(0, &env->fp_status);
+ fa.l = a;
+ fb.l = b;
+ fd.f = float32_div(fb.f, fa.f, &env->fp_status);
+ flags = get_float_exception_flags(&env->fp_status);
+ update_fpu_flags(flags);
+
+ return fd.l;
+}
+
+uint32_t helper_fcmp_un(uint32_t a, uint32_t b)
+{
+ CPU_FloatU fa, fb;
+ uint32_t r = 0;
+
+ fa.l = a;
+ fb.l = b;
+
+ if (float32_is_signaling_nan(fa.f) || float32_is_signaling_nan(fb.f)) {
+ update_fpu_flags(float_flag_invalid);
+ r = 1;
+ }
+
+ if (float32_is_nan(fa.f) || float32_is_nan(fb.f)) {
+ r = 1;
+ }
+
+ return r;
+}
+
+uint32_t helper_fcmp_lt(uint32_t a, uint32_t b)
+{
+ CPU_FloatU fa, fb;
+ int r;
+ int flags;
+
+ set_float_exception_flags(0, &env->fp_status);
+ fa.l = a;
+ fb.l = b;
+ r = float32_lt(fb.f, fa.f, &env->fp_status);
+ flags = get_float_exception_flags(&env->fp_status);
+ update_fpu_flags(flags & float_flag_invalid);
+
+ return r;
+}
+
+uint32_t helper_fcmp_eq(uint32_t a, uint32_t b)
+{
+ CPU_FloatU fa, fb;
+ int flags;
+ int r;
+
+ set_float_exception_flags(0, &env->fp_status);
+ fa.l = a;
+ fb.l = b;
+ r = float32_eq(fa.f, fb.f, &env->fp_status);
+ flags = get_float_exception_flags(&env->fp_status);
+ update_fpu_flags(flags & float_flag_invalid);
+
+ return r;
+}
+
+uint32_t helper_fcmp_le(uint32_t a, uint32_t b)
+{
+ CPU_FloatU fa, fb;
+ int flags;
+ int r;
+
+ fa.l = a;
+ fb.l = b;
+ set_float_exception_flags(0, &env->fp_status);
+ r = float32_le(fa.f, fb.f, &env->fp_status);
+ flags = get_float_exception_flags(&env->fp_status);
+ update_fpu_flags(flags & float_flag_invalid);
+
+
+ return r;
+}
+
+uint32_t helper_fcmp_gt(uint32_t a, uint32_t b)
+{
+ CPU_FloatU fa, fb;
+ int flags, r;
+
+ fa.l = a;
+ fb.l = b;
+ set_float_exception_flags(0, &env->fp_status);
+ r = float32_lt(fa.f, fb.f, &env->fp_status);
+ flags = get_float_exception_flags(&env->fp_status);
+ update_fpu_flags(flags & float_flag_invalid);
+ return r;
+}
+
+uint32_t helper_fcmp_ne(uint32_t a, uint32_t b)
+{
+ CPU_FloatU fa, fb;
+ int flags, r;
+
+ fa.l = a;
+ fb.l = b;
+ set_float_exception_flags(0, &env->fp_status);
+ r = !float32_eq(fa.f, fb.f, &env->fp_status);
+ flags = get_float_exception_flags(&env->fp_status);
+ update_fpu_flags(flags & float_flag_invalid);
+
+ return r;
+}
+
+uint32_t helper_fcmp_ge(uint32_t a, uint32_t b)
+{
+ CPU_FloatU fa, fb;
+ int flags, r;
+
+ fa.l = a;
+ fb.l = b;
+ set_float_exception_flags(0, &env->fp_status);
+ r = !float32_lt(fa.f, fb.f, &env->fp_status);
+ flags = get_float_exception_flags(&env->fp_status);
+ update_fpu_flags(flags & float_flag_invalid);
+
+ return r;
+}
+
+uint32_t helper_flt(uint32_t a)
+{
+ CPU_FloatU fd, fa;
+
+ fa.l = a;
+ fd.f = int32_to_float32(fa.l, &env->fp_status);
+ return fd.l;
+}
+
+uint32_t helper_fint(uint32_t a)
+{
+ CPU_FloatU fa;
+ uint32_t r;
+ int flags;
+
+ set_float_exception_flags(0, &env->fp_status);
+ fa.l = a;
+ r = float32_to_int32(fa.f, &env->fp_status);
+ flags = get_float_exception_flags(&env->fp_status);
+ update_fpu_flags(flags);
+
+ return r;
+}
+
+uint32_t helper_fsqrt(uint32_t a)
+{
+ CPU_FloatU fd, fa;
+ int flags;
+
+ set_float_exception_flags(0, &env->fp_status);
+ fa.l = a;
+ fd.l = float32_sqrt(fa.f, &env->fp_status);
+ flags = get_float_exception_flags(&env->fp_status);
+ update_fpu_flags(flags);
+
+ return fd.l;
+}
+
uint32_t helper_pcmpbf(uint32_t a, uint32_t b)
{
unsigned int i;
diff --git a/target-microblaze/translate.c b/target-microblaze/translate.c
index ca54e2c30e..38149bb357 100644
--- a/target-microblaze/translate.c
+++ b/target-microblaze/translate.c
@@ -63,8 +63,7 @@ static TCGv env_iflags;
/* This is the state at translation time. */
typedef struct DisasContext {
CPUState *env;
- target_ulong pc, ppc;
- target_ulong cache_pc;
+ target_ulong pc;
/* Decoder. */
int type_b;
@@ -153,6 +152,14 @@ static void gen_goto_tb(DisasContext *dc, int n, target_ulong dest)
}
}
+/* True if ALU operand b is a small immediate that may deserve
+ faster treatment. */
+static inline int dec_alu_op_b_is_small_imm(DisasContext *dc)
+{
+ /* Immediate insn without the imm prefix ? */
+ return dc->type_b && !(dc->tb_flags & IMM_FLAG);
+}
+
static inline TCGv *dec_alu_op_b(DisasContext *dc)
{
if (dc->type_b) {
@@ -450,7 +457,7 @@ static void dec_msr(DisasContext *dc)
tcg_gen_mov_tl(cpu_SR[SR_ESR], cpu_R[dc->ra]);
break;
case 0x7:
- /* Ignored at the moment. */
+ tcg_gen_andi_tl(cpu_SR[SR_FSR], cpu_R[dc->ra], 31);
break;
default:
cpu_abort(dc->env, "unknown mts reg %x\n", sr);
@@ -473,7 +480,7 @@ static void dec_msr(DisasContext *dc)
tcg_gen_mov_tl(cpu_R[dc->rd], cpu_SR[SR_ESR]);
break;
case 0x7:
- tcg_gen_movi_tl(cpu_R[dc->rd], 0);
+ tcg_gen_mov_tl(cpu_R[dc->rd], cpu_SR[SR_FSR]);
break;
case 0xb:
tcg_gen_mov_tl(cpu_R[dc->rd], cpu_SR[SR_BTR]);
@@ -780,6 +787,13 @@ static inline TCGv *compute_ldst_addr(DisasContext *dc, TCGv *t)
/* Treat the fast cases first. */
if (!dc->type_b) {
+ /* If any of the regs is r0, return a ptr to the other. */
+ if (dc->ra == 0) {
+ return &cpu_R[dc->rb];
+ } else if (dc->rb == 0) {
+ return &cpu_R[dc->ra];
+ }
+
*t = tcg_temp_new();
tcg_gen_add_tl(*t, cpu_R[dc->ra], cpu_R[dc->rb]);
return t;
@@ -904,50 +918,24 @@ static void dec_store(DisasContext *dc)
static inline void eval_cc(DisasContext *dc, unsigned int cc,
TCGv d, TCGv a, TCGv b)
{
- int l1;
-
switch (cc) {
case CC_EQ:
- l1 = gen_new_label();
- tcg_gen_movi_tl(env_btaken, 1);
- tcg_gen_brcond_tl(TCG_COND_EQ, a, b, l1);
- tcg_gen_movi_tl(env_btaken, 0);
- gen_set_label(l1);
+ tcg_gen_setcond_tl(TCG_COND_EQ, d, a, b);
break;
case CC_NE:
- l1 = gen_new_label();
- tcg_gen_movi_tl(env_btaken, 1);
- tcg_gen_brcond_tl(TCG_COND_NE, a, b, l1);
- tcg_gen_movi_tl(env_btaken, 0);
- gen_set_label(l1);
+ tcg_gen_setcond_tl(TCG_COND_NE, d, a, b);
break;
case CC_LT:
- l1 = gen_new_label();
- tcg_gen_movi_tl(env_btaken, 1);
- tcg_gen_brcond_tl(TCG_COND_LT, a, b, l1);
- tcg_gen_movi_tl(env_btaken, 0);
- gen_set_label(l1);
+ tcg_gen_setcond_tl(TCG_COND_LT, d, a, b);
break;
case CC_LE:
- l1 = gen_new_label();
- tcg_gen_movi_tl(env_btaken, 1);
- tcg_gen_brcond_tl(TCG_COND_LE, a, b, l1);
- tcg_gen_movi_tl(env_btaken, 0);
- gen_set_label(l1);
+ tcg_gen_setcond_tl(TCG_COND_LE, d, a, b);
break;
case CC_GE:
- l1 = gen_new_label();
- tcg_gen_movi_tl(env_btaken, 1);
- tcg_gen_brcond_tl(TCG_COND_GE, a, b, l1);
- tcg_gen_movi_tl(env_btaken, 0);
- gen_set_label(l1);
+ tcg_gen_setcond_tl(TCG_COND_GE, d, a, b);
break;
case CC_GT:
- l1 = gen_new_label();
- tcg_gen_movi_tl(env_btaken, 1);
- tcg_gen_brcond_tl(TCG_COND_GT, a, b, l1);
- tcg_gen_movi_tl(env_btaken, 0);
- gen_set_label(l1);
+ tcg_gen_setcond_tl(TCG_COND_GT, d, a, b);
break;
default:
cpu_abort(dc->env, "Unknown condition code %x.\n", cc);
@@ -984,10 +972,16 @@ static void dec_bcc(DisasContext *dc)
cpu_env, offsetof(CPUState, bimm));
}
- tcg_gen_movi_tl(env_btarget, dc->pc);
- tcg_gen_add_tl(env_btarget, env_btarget, *(dec_alu_op_b(dc)));
- eval_cc(dc, cc, env_btaken, cpu_R[dc->ra], tcg_const_tl(0));
+ if (dec_alu_op_b_is_small_imm(dc)) {
+ int32_t offset = (int32_t)((int16_t)dc->imm); /* sign-extend. */
+
+ tcg_gen_movi_tl(env_btarget, dc->pc + offset);
+ } else {
+ tcg_gen_movi_tl(env_btarget, dc->pc);
+ tcg_gen_add_tl(env_btarget, env_btarget, *(dec_alu_op_b(dc)));
+ }
dc->jmp = JMP_INDIRECT;
+ eval_cc(dc, cc, env_btaken, cpu_R[dc->ra], tcg_const_tl(0));
}
static void dec_br(DisasContext *dc)
@@ -1031,13 +1025,13 @@ static void dec_br(DisasContext *dc)
}
}
} else {
- if (!dc->type_b || (dc->tb_flags & IMM_FLAG)) {
+ if (dec_alu_op_b_is_small_imm(dc)) {
+ dc->jmp = JMP_DIRECT;
+ dc->jmp_pc = dc->pc + (int32_t)((int16_t)dc->imm);
+ } else {
tcg_gen_movi_tl(env_btaken, 1);
tcg_gen_movi_tl(env_btarget, dc->pc);
tcg_gen_add_tl(env_btarget, env_btarget, *(dec_alu_op_b(dc)));
- } else {
- dc->jmp = JMP_DIRECT;
- dc->jmp_pc = dc->pc + (int32_t)((int16_t)dc->imm);
}
}
}
@@ -1140,18 +1134,115 @@ static void dec_rts(DisasContext *dc)
tcg_gen_add_tl(env_btarget, cpu_R[dc->ra], *(dec_alu_op_b(dc)));
}
+static int dec_check_fpuv2(DisasContext *dc)
+{
+ int r;
+
+ r = dc->env->pvr.regs[2] & PVR2_USE_FPU2_MASK;
+
+ if (!r && (dc->tb_flags & MSR_EE_FLAG)) {
+ tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_FPU);
+ t_gen_raise_exception(dc, EXCP_HW_EXCP);
+ }
+ return r;
+}
+
static void dec_fpu(DisasContext *dc)
{
+ unsigned int fpu_insn;
+
if ((dc->tb_flags & MSR_EE_FLAG)
&& (dc->env->pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK)
&& !((dc->env->pvr.regs[2] & PVR2_USE_FPU_MASK))) {
- tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_FPU);
+ tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP);
t_gen_raise_exception(dc, EXCP_HW_EXCP);
return;
}
- qemu_log ("unimplemented FPU insn pc=%x opc=%x\n", dc->pc, dc->opcode);
- dc->abort_at_next_insn = 1;
+ fpu_insn = (dc->ir >> 7) & 7;
+
+ switch (fpu_insn) {
+ case 0:
+ gen_helper_fadd(cpu_R[dc->rd], cpu_R[dc->ra], cpu_R[dc->rb]);
+ break;
+
+ case 1:
+ gen_helper_frsub(cpu_R[dc->rd], cpu_R[dc->ra], cpu_R[dc->rb]);
+ break;
+
+ case 2:
+ gen_helper_fmul(cpu_R[dc->rd], cpu_R[dc->ra], cpu_R[dc->rb]);
+ break;
+
+ case 3:
+ gen_helper_fdiv(cpu_R[dc->rd], cpu_R[dc->ra], cpu_R[dc->rb]);
+ break;
+
+ case 4:
+ switch ((dc->ir >> 4) & 7) {
+ case 0:
+ gen_helper_fcmp_un(cpu_R[dc->rd],
+ cpu_R[dc->ra], cpu_R[dc->rb]);
+ break;
+ case 1:
+ gen_helper_fcmp_lt(cpu_R[dc->rd],
+ cpu_R[dc->ra], cpu_R[dc->rb]);
+ break;
+ case 2:
+ gen_helper_fcmp_eq(cpu_R[dc->rd],
+ cpu_R[dc->ra], cpu_R[dc->rb]);
+ break;
+ case 3:
+ gen_helper_fcmp_le(cpu_R[dc->rd],
+ cpu_R[dc->ra], cpu_R[dc->rb]);
+ break;
+ case 4:
+ gen_helper_fcmp_gt(cpu_R[dc->rd],
+ cpu_R[dc->ra], cpu_R[dc->rb]);
+ break;
+ case 5:
+ gen_helper_fcmp_ne(cpu_R[dc->rd],
+ cpu_R[dc->ra], cpu_R[dc->rb]);
+ break;
+ case 6:
+ gen_helper_fcmp_ge(cpu_R[dc->rd],
+ cpu_R[dc->ra], cpu_R[dc->rb]);
+ break;
+ default:
+ qemu_log ("unimplemented fcmp fpu_insn=%x pc=%x opc=%x\n",
+ fpu_insn, dc->pc, dc->opcode);
+ dc->abort_at_next_insn = 1;
+ break;
+ }
+ break;
+
+ case 5:
+ if (!dec_check_fpuv2(dc)) {
+ return;
+ }
+ gen_helper_flt(cpu_R[dc->rd], cpu_R[dc->ra]);
+ break;
+
+ case 6:
+ if (!dec_check_fpuv2(dc)) {
+ return;
+ }
+ gen_helper_fint(cpu_R[dc->rd], cpu_R[dc->ra]);
+ break;
+
+ case 7:
+ if (!dec_check_fpuv2(dc)) {
+ return;
+ }
+ gen_helper_fsqrt(cpu_R[dc->rd], cpu_R[dc->ra]);
+ break;
+
+ default:
+ qemu_log ("unimplemented FPU insn fpu_insn=%x pc=%x opc=%x\n",
+ fpu_insn, dc->pc, dc->opcode);
+ dc->abort_at_next_insn = 1;
+ break;
+ }
}
static void dec_null(DisasContext *dc)
@@ -1279,9 +1370,7 @@ gen_intermediate_code_internal(CPUState *env, TranslationBlock *tb,
dc->is_jmp = DISAS_NEXT;
dc->jmp = 0;
dc->delayed_branch = !!(dc->tb_flags & D_FLAG);
- dc->ppc = pc_start;
dc->pc = pc_start;
- dc->cache_pc = -1;
dc->singlestep_enabled = env->singlestep_enabled;
dc->cpustate_changed = 0;
dc->abort_at_next_insn = 0;
@@ -1337,7 +1426,6 @@ gen_intermediate_code_internal(CPUState *env, TranslationBlock *tb,
decode(dc);
if (dc->clear_imm)
dc->tb_flags &= ~IMM_FLAG;
- dc->ppc = dc->pc;
dc->pc += 4;
num_insns++;
@@ -1428,7 +1516,7 @@ gen_intermediate_code_internal(CPUState *env, TranslationBlock *tb,
#if DISAS_GNU
log_target_disas(pc_start, dc->pc - pc_start, 0);
#endif
- qemu_log("\nisize=%d osize=%zd\n",
+ qemu_log("\nisize=%d osize=%td\n",
dc->pc - pc_start, gen_opc_ptr - gen_opc_buf);
}
#endif
@@ -1457,9 +1545,9 @@ void cpu_dump_state (CPUState *env, FILE *f,
cpu_fprintf(f, "IN: PC=%x %s\n",
env->sregs[SR_PC], lookup_symbol(env->sregs[SR_PC]));
- cpu_fprintf(f, "rmsr=%x resr=%x rear=%x debug[%x] imm=%x iflags=%x\n",
+ cpu_fprintf(f, "rmsr=%x resr=%x rear=%x debug=%x imm=%x iflags=%x fsr=%x\n",
env->sregs[SR_MSR], env->sregs[SR_ESR], env->sregs[SR_EAR],
- env->debug, env->imm, env->iflags);
+ env->debug, env->imm, env->iflags, env->sregs[SR_FSR]);
cpu_fprintf(f, "btaken=%d btarget=%x mode=%s(saved=%s) eip=%d ie=%d\n",
env->btaken, env->btarget,
(env->sregs[SR_MSR] & MSR_UM) ? "user" : "kernel",
@@ -1485,7 +1573,7 @@ CPUState *cpu_mb_init (const char *cpu_model)
cpu_exec_init(env);
cpu_reset(env);
-
+ set_float_rounding_mode(float_round_nearest_even, &env->fp_status);
if (tcg_initialized)
return env;
@@ -1554,15 +1642,19 @@ void cpu_reset (CPUState *env)
| PVR2_USE_DIV_MASK \
| PVR2_USE_HW_MUL_MASK \
| PVR2_USE_MUL64_MASK \
+ | PVR2_USE_FPU_MASK \
+ | PVR2_USE_FPU2_MASK \
+ | PVR2_FPU_EXC_MASK \
| 0;
env->pvr.regs[10] = 0x0c000000; /* Default to spartan 3a dsp family. */
env->pvr.regs[11] = PVR11_USE_MMU | (16 << 17);
- env->sregs[SR_MSR] = 0;
#if defined(CONFIG_USER_ONLY)
/* start in user mode with interrupts enabled. */
+ env->sregs[SR_MSR] = MSR_EE | MSR_IE | MSR_VM | MSR_UM;
env->pvr.regs[10] = 0x0c000000; /* Spartan 3a dsp. */
#else
+ env->sregs[SR_MSR] = 0;
mmu_init(&env->mmu);
env->mmu.c_mmu = 3;
env->mmu.c_mmu_tlb_access = 3;
diff --git a/target-mips/cpu.h b/target-mips/cpu.h
index 81051aa004..19511d7f02 100644
--- a/target-mips/cpu.h
+++ b/target-mips/cpu.h
@@ -525,6 +525,29 @@ static inline void cpu_clone_regs(CPUState *env, target_ulong newsp)
env->active_tc.gpr[2] = 0;
}
+static inline int cpu_mips_hw_interrupts_pending(CPUState *env)
+{
+ int32_t pending;
+ int32_t status;
+ int r;
+
+ pending = env->CP0_Cause & CP0Ca_IP_mask;
+ status = env->CP0_Status & CP0Ca_IP_mask;
+
+ if (env->CP0_Config3 & (1 << CP0C3_VEIC)) {
+ /* A MIPS configured with a vectorizing external interrupt controller
+ will feed a vector into the Cause pending lines. The core treats
+ the status lines as a vector level, not as indiviual masks. */
+ r = pending > status;
+ } else {
+ /* A MIPS configured with compatibility or VInt (Vectored Interrupts)
+ treats the pending lines as individual interrupt lines, the status
+ lines are individual masks. */
+ r = pending & status;
+ }
+ return r;
+}
+
#include "cpu-all.h"
/* Memory access type :
@@ -598,7 +621,7 @@ void cpu_mips_start_count(CPUState *env);
void cpu_mips_stop_count(CPUState *env);
/* mips_int.c */
-void cpu_mips_update_irq (CPUState *env);
+void cpu_mips_soft_irq(CPUState *env, int irq, int level);
/* helper.c */
int cpu_mips_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
diff --git a/target-mips/helper.c b/target-mips/helper.c
index ea221ab53f..bdc1e53669 100644
--- a/target-mips/helper.c
+++ b/target-mips/helper.c
@@ -478,6 +478,33 @@ void do_interrupt (CPUState *env)
cause = 0;
if (env->CP0_Cause & (1 << CP0Ca_IV))
offset = 0x200;
+
+ if (env->CP0_Config3 & ((1 << CP0C3_VInt) | (1 << CP0C3_VEIC))) {
+ /* Vectored Interrupts. */
+ unsigned int spacing;
+ unsigned int vector;
+ unsigned int pending = (env->CP0_Cause & CP0Ca_IP_mask) >> 8;
+
+ /* Compute the Vector Spacing. */
+ spacing = (env->CP0_IntCtl >> CP0IntCtl_VS) & ((1 << 6) - 1);
+ spacing <<= 5;
+
+ if (env->CP0_Config3 & (1 << CP0C3_VInt)) {
+ /* For VInt mode, the MIPS computes the vector internally. */
+ for (vector = 0; vector < 8; vector++) {
+ if (pending & 1) {
+ /* Found it. */
+ break;
+ }
+ pending >>= 1;
+ }
+ } else {
+ /* For VEIC mode, the external interrupt controller feeds the
+ vector throught the CP0Cause IP lines. */
+ vector = pending;
+ }
+ offset = 0x200 + vector * spacing;
+ }
goto set_EPC;
case EXCP_LTLBL:
cause = 1;
@@ -491,7 +518,8 @@ void do_interrupt (CPUState *env)
int SX = (env->CP0_Status & (1 << CP0St_SX)) != 0;
int KX = (env->CP0_Status & (1 << CP0St_KX)) != 0;
- if ((R == 0 && UX) || (R == 1 && SX) || (R == 3 && KX))
+ if (((R == 0 && UX) || (R == 1 && SX) || (R == 3 && KX)) &&
+ (!(env->insn_flags & (INSN_LOONGSON2E | INSN_LOONGSON2F))))
offset = 0x080;
else
#endif
@@ -507,7 +535,8 @@ void do_interrupt (CPUState *env)
int SX = (env->CP0_Status & (1 << CP0St_SX)) != 0;
int KX = (env->CP0_Status & (1 << CP0St_KX)) != 0;
- if ((R == 0 && UX) || (R == 1 && SX) || (R == 3 && KX))
+ if (((R == 0 && UX) || (R == 1 && SX) || (R == 3 && KX)) &&
+ (!(env->insn_flags & (INSN_LOONGSON2E | INSN_LOONGSON2F))))
offset = 0x080;
else
#endif
diff --git a/target-mips/helper.h b/target-mips/helper.h
index a6ba75dfbc..cb13fb2352 100644
--- a/target-mips/helper.h
+++ b/target-mips/helper.h
@@ -2,7 +2,6 @@
DEF_HELPER_2(raise_exception_err, void, i32, int)
DEF_HELPER_1(raise_exception, void, i32)
-DEF_HELPER_0(interrupt_restart, void)
#ifdef TARGET_MIPS64
DEF_HELPER_3(ldl, tl, tl, tl, int)
diff --git a/target-mips/op_helper.c b/target-mips/op_helper.c
index 8ae510adc1..41abd575f9 100644
--- a/target-mips/op_helper.c
+++ b/target-mips/op_helper.c
@@ -46,18 +46,6 @@ void helper_raise_exception (uint32_t exception)
helper_raise_exception_err(exception, 0);
}
-void helper_interrupt_restart (void)
-{
- if (!(env->CP0_Status & (1 << CP0St_EXL)) &&
- !(env->CP0_Status & (1 << CP0St_ERL)) &&
- !(env->hflags & MIPS_HFLAG_DM) &&
- (env->CP0_Status & (1 << CP0St_IE)) &&
- (env->CP0_Status & env->CP0_Cause & CP0Ca_IP_mask)) {
- env->CP0_Cause &= ~(0x1f << CP0Ca_EC);
- helper_raise_exception(EXCP_EXT_INTERRUPT);
- }
-}
-
#if !defined(CONFIG_USER_ONLY)
static void do_restore_state (void *pc_ptr)
{
@@ -1313,7 +1301,6 @@ void helper_mtc0_status (target_ulong arg1)
default: cpu_abort(env, "Invalid MMU mode!\n"); break;
}
}
- cpu_mips_update_irq(env);
}
void helper_mttc0_status(target_ulong arg1)
@@ -1347,6 +1334,7 @@ void helper_mtc0_cause (target_ulong arg1)
{
uint32_t mask = 0x00C00300;
uint32_t old = env->CP0_Cause;
+ int i;
if (env->insn_flags & ISA_MIPS32R2)
mask |= 1 << CP0Ca_DC;
@@ -1360,18 +1348,18 @@ void helper_mtc0_cause (target_ulong arg1)
cpu_mips_start_count(env);
}
- /* Handle the software interrupt as an hardware one, as they
- are very similar */
- if (arg1 & CP0Ca_IP_mask) {
- cpu_mips_update_irq(env);
+ /* Set/reset software interrupts */
+ for (i = 0 ; i < 2 ; i++) {
+ if ((old ^ env->CP0_Cause) & (1 << (CP0Ca_IP + i))) {
+ cpu_mips_soft_irq(env, i, env->CP0_Cause & (1 << (CP0Ca_IP + i)));
+ }
}
}
void helper_mtc0_ebase (target_ulong arg1)
{
/* vectored interrupts not implemented */
- /* Multi-CPU not implemented */
- env->CP0_EBase = 0x80000000 | (arg1 & 0x3FFFF000);
+ env->CP0_EBase = (env->CP0_EBase & ~0x3FFFF000) | (arg1 & 0x3FFFF000);
}
void helper_mtc0_config0 (target_ulong arg1)
@@ -1610,8 +1598,10 @@ void helper_fork(target_ulong arg1, target_ulong arg2)
// TODO: store to TC register
}
-target_ulong helper_yield(target_ulong arg1)
+target_ulong helper_yield(target_ulong arg)
{
+ target_long arg1 = arg;
+
if (arg1 < 0) {
/* No scheduling policy implemented. */
if (arg1 != -2) {
@@ -1793,8 +1783,6 @@ target_ulong helper_di (void)
target_ulong t0 = env->CP0_Status;
env->CP0_Status = t0 & ~(1 << CP0St_IE);
- cpu_mips_update_irq(env);
-
return t0;
}
@@ -1803,8 +1791,6 @@ target_ulong helper_ei (void)
target_ulong t0 = env->CP0_Status;
env->CP0_Status = t0 | (1 << CP0St_IE);
- cpu_mips_update_irq(env);
-
return t0;
}
diff --git a/target-mips/translate.c b/target-mips/translate.c
index 7168273381..d62c6158fc 100644
--- a/target-mips/translate.c
+++ b/target-mips/translate.c
@@ -1153,6 +1153,7 @@ static void gen_ld (CPUState *env, DisasContext *ctx, uint32_t opc,
opn = "ll";
break;
}
+ (void)opn; /* avoid a compiler warning */
MIPS_DEBUG("%s %s, %d(%s)", opn, regnames[rt], offset, regnames[base]);
tcg_temp_free(t0);
tcg_temp_free(t1);
@@ -1212,6 +1213,7 @@ static void gen_st (DisasContext *ctx, uint32_t opc, int rt,
opn = "swr";
break;
}
+ (void)opn; /* avoid a compiler warning */
MIPS_DEBUG("%s %s, %d(%s)", opn, regnames[rt], offset, regnames[base]);
tcg_temp_free(t0);
tcg_temp_free(t1);
@@ -1247,6 +1249,7 @@ static void gen_st_cond (DisasContext *ctx, uint32_t opc, int rt,
opn = "sc";
break;
}
+ (void)opn; /* avoid a compiler warning */
MIPS_DEBUG("%s %s, %d(%s)", opn, regnames[rt], offset, regnames[base]);
tcg_temp_free(t1);
tcg_temp_free(t0);
@@ -1312,6 +1315,7 @@ static void gen_flt_ldst (DisasContext *ctx, uint32_t opc, int ft,
generate_exception(ctx, EXCP_RI);
goto out;
}
+ (void)opn; /* avoid a compiler warning */
MIPS_DEBUG("%s %s, %d(%s)", opn, fregnames[ft], offset, regnames[base]);
out:
tcg_temp_free(t0);
@@ -1412,6 +1416,7 @@ static void gen_arith_imm (CPUState *env, DisasContext *ctx, uint32_t opc,
break;
#endif
}
+ (void)opn; /* avoid a compiler warning */
MIPS_DEBUG("%s %s, %s, " TARGET_FMT_lx, opn, regnames[rt], regnames[rs], uimm);
}
@@ -1454,6 +1459,7 @@ static void gen_logic_imm (CPUState *env, uint32_t opc, int rt, int rs, int16_t
opn = "lui";
break;
}
+ (void)opn; /* avoid a compiler warning */
MIPS_DEBUG("%s %s, %s, " TARGET_FMT_lx, opn, regnames[rt], regnames[rs], uimm);
}
@@ -1481,6 +1487,7 @@ static void gen_slt_imm (CPUState *env, uint32_t opc, int rt, int rs, int16_t im
opn = "sltiu";
break;
}
+ (void)opn; /* avoid a compiler warning */
MIPS_DEBUG("%s %s, %s, " TARGET_FMT_lx, opn, regnames[rt], regnames[rs], uimm);
tcg_temp_free(t0);
}
@@ -1572,6 +1579,7 @@ static void gen_shift_imm(CPUState *env, DisasContext *ctx, uint32_t opc,
break;
#endif
}
+ (void)opn; /* avoid a compiler warning */
MIPS_DEBUG("%s %s, %s, " TARGET_FMT_lx, opn, regnames[rt], regnames[rs], uimm);
tcg_temp_free(t0);
}
@@ -1752,6 +1760,7 @@ static void gen_arith (CPUState *env, DisasContext *ctx, uint32_t opc,
opn = "mul";
break;
}
+ (void)opn; /* avoid a compiler warning */
MIPS_DEBUG("%s %s, %s, %s", opn, regnames[rd], regnames[rs], regnames[rt]);
}
@@ -1789,6 +1798,7 @@ static void gen_cond_move (CPUState *env, uint32_t opc, int rd, int rs, int rt)
tcg_gen_movi_tl(cpu_gpr[rd], 0);
gen_set_label(l1);
+ (void)opn; /* avoid a compiler warning */
MIPS_DEBUG("%s %s, %s, %s", opn, regnames[rd], regnames[rs], regnames[rt]);
}
@@ -1849,6 +1859,7 @@ static void gen_logic (CPUState *env, uint32_t opc, int rd, int rs, int rt)
opn = "xor";
break;
}
+ (void)opn; /* avoid a compiler warning */
MIPS_DEBUG("%s %s, %s, %s", opn, regnames[rd], regnames[rs], regnames[rt]);
}
@@ -1878,6 +1889,7 @@ static void gen_slt (CPUState *env, uint32_t opc, int rd, int rs, int rt)
opn = "sltu";
break;
}
+ (void)opn; /* avoid a compiler warning */
MIPS_DEBUG("%s %s, %s, %s", opn, regnames[rd], regnames[rs], regnames[rt]);
tcg_temp_free(t0);
tcg_temp_free(t1);
@@ -1958,6 +1970,7 @@ static void gen_shift (CPUState *env, DisasContext *ctx, uint32_t opc,
break;
#endif
}
+ (void)opn; /* avoid a compiler warning */
MIPS_DEBUG("%s %s, %s, %s", opn, regnames[rd], regnames[rs], regnames[rt]);
tcg_temp_free(t0);
tcg_temp_free(t1);
@@ -1997,6 +2010,7 @@ static void gen_HILO (DisasContext *ctx, uint32_t opc, int reg)
opn = "mtlo";
break;
}
+ (void)opn; /* avoid a compiler warning */
MIPS_DEBUG("%s %s", opn, regnames[reg]);
}
@@ -2229,6 +2243,7 @@ static void gen_muldiv (DisasContext *ctx, uint32_t opc,
generate_exception(ctx, EXCP_RI);
goto out;
}
+ (void)opn; /* avoid a compiler warning */
MIPS_DEBUG("%s %s %s", opn, regnames[rs], regnames[rt]);
out:
tcg_temp_free(t0);
@@ -2308,6 +2323,7 @@ static void gen_mul_vr54xx (DisasContext *ctx, uint32_t opc,
goto out;
}
gen_store_gpr(t0, rd);
+ (void)opn; /* avoid a compiler warning */
MIPS_DEBUG("%s %s, %s, %s", opn, regnames[rd], regnames[rs], regnames[rt]);
out:
@@ -2348,6 +2364,7 @@ static void gen_cl (DisasContext *ctx, uint32_t opc,
break;
#endif
}
+ (void)opn; /* avoid a compiler warning */
MIPS_DEBUG("%s %s, %s", opn, regnames[rd], regnames[rs]);
tcg_temp_free(t0);
}
@@ -2561,6 +2578,7 @@ static void gen_loongson_integer (DisasContext *ctx, uint32_t opc,
#endif
}
+ (void)opn; /* avoid a compiler warning */
MIPS_DEBUG("%s %s, %s", opn, regnames[rd], regnames[rs]);
tcg_temp_free(t0);
tcg_temp_free(t1);
@@ -3730,6 +3748,7 @@ static void gen_mfc0 (CPUState *env, DisasContext *ctx, TCGv arg, int reg, int s
default:
goto die;
}
+ (void)rn; /* avoid a compiler warning */
LOG_DISAS("mfc0 %s (reg %d sel %d)\n", rn, reg, sel);
return;
@@ -4320,6 +4339,7 @@ static void gen_mtc0 (CPUState *env, DisasContext *ctx, TCGv arg, int reg, int s
default:
goto die;
}
+ (void)rn; /* avoid a compiler warning */
LOG_DISAS("mtc0 %s (reg %d sel %d)\n", rn, reg, sel);
/* For simplicity assume that all writes can cause interrupts. */
if (use_icount) {
@@ -4892,6 +4912,7 @@ static void gen_dmfc0 (CPUState *env, DisasContext *ctx, TCGv arg, int reg, int
default:
goto die;
}
+ (void)rn; /* avoid a compiler warning */
LOG_DISAS("dmfc0 %s (reg %d sel %d)\n", rn, reg, sel);
return;
@@ -5190,7 +5211,17 @@ static void gen_dmtc0 (CPUState *env, DisasContext *ctx, TCGv arg, int reg, int
switch (sel) {
case 0:
save_cpu_state(ctx, 1);
+ /* Mark as an IO operation because we may trigger a software
+ interrupt. */
+ if (use_icount) {
+ gen_io_start();
+ }
gen_helper_mtc0_cause(arg);
+ if (use_icount) {
+ gen_io_end();
+ }
+ /* Stop translation as we may have triggered an intetrupt */
+ ctx->bstate = BS_STOP;
rn = "Cause";
break;
default:
@@ -5473,6 +5504,7 @@ static void gen_dmtc0 (CPUState *env, DisasContext *ctx, TCGv arg, int reg, int
default:
goto die;
}
+ (void)rn; /* avoid a compiler warning */
LOG_DISAS("dmtc0 %s (reg %d sel %d)\n", rn, reg, sel);
/* For simplicity assume that all writes can cause interrupts. */
if (use_icount) {
@@ -5933,6 +5965,7 @@ static void gen_cp0 (CPUState *env, DisasContext *ctx, uint32_t opc, int rt, int
generate_exception(ctx, EXCP_RI);
return;
}
+ (void)opn; /* avoid a compiler warning */
MIPS_DEBUG("%s %s %d", opn, regnames[rt], rd);
}
#endif /* !CONFIG_USER_ONLY */
@@ -6042,6 +6075,7 @@ static void gen_compute_branch1 (CPUState *env, DisasContext *ctx, uint32_t op,
generate_exception (ctx, EXCP_RI);
goto out;
}
+ (void)opn; /* avoid a compiler warning */
MIPS_DEBUG("%s: cond %02x target " TARGET_FMT_lx, opn,
ctx->hflags, btarget);
ctx->btarget = btarget;
@@ -6271,6 +6305,7 @@ static void gen_cp1 (DisasContext *ctx, uint32_t opc, int rt, int fs)
generate_exception (ctx, EXCP_RI);
goto out;
}
+ (void)opn; /* avoid a compiler warning */
MIPS_DEBUG("%s %s %s", opn, regnames[rt], fregnames[fs]);
out:
@@ -7598,6 +7633,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1,
generate_exception (ctx, EXCP_RI);
return;
}
+ (void)opn; /* avoid a compiler warning */
switch (optype) {
case BINOP:
MIPS_DEBUG("%s %s, %s, %s", opn, fregnames[fd], fregnames[fs], fregnames[ft]);
@@ -7710,6 +7746,7 @@ static void gen_flt3_ldst (DisasContext *ctx, uint32_t opc,
break;
}
tcg_temp_free(t0);
+ (void)opn; (void)store; /* avoid compiler warnings */
MIPS_DEBUG("%s %s, %s(%s)", opn, fregnames[store ? fs : fd],
regnames[index], regnames[base]);
}
@@ -7983,6 +8020,7 @@ static void gen_flt3_arith (DisasContext *ctx, uint32_t opc,
generate_exception (ctx, EXCP_RI);
return;
}
+ (void)opn; /* avoid a compiler warning */
MIPS_DEBUG("%s %s, %s, %s, %s", opn, fregnames[fd], fregnames[fr],
fregnames[fs], fregnames[ft]);
}
@@ -9961,6 +9999,7 @@ static void gen_ldst_pair (DisasContext *ctx, uint32_t opc, int rd,
break;
#endif
}
+ (void)opn; /* avoid a compiler warning */
MIPS_DEBUG("%s, %s, %d(%s)", opn, regnames[rd], offset, regnames[base]);
tcg_temp_free(t0);
tcg_temp_free(t1);
@@ -12365,7 +12404,6 @@ gen_intermediate_code_internal (CPUState *env, TranslationBlock *tb,
} else {
switch (ctx.bstate) {
case BS_STOP:
- gen_helper_interrupt_restart();
gen_goto_tb(&ctx, 0, ctx.pc);
break;
case BS_NONE:
@@ -12373,7 +12411,6 @@ gen_intermediate_code_internal (CPUState *env, TranslationBlock *tb,
gen_goto_tb(&ctx, 0, ctx.pc);
break;
case BS_EXCP:
- gen_helper_interrupt_restart();
tcg_gen_exit_tb(0);
break;
case BS_BRANCH:
@@ -12671,8 +12708,7 @@ void cpu_reset (CPUMIPSState *env)
env->CP0_Random = env->tlb->nb_tlb - 1;
env->tlb->tlb_in_use = env->tlb->nb_tlb;
env->CP0_Wired = 0;
- /* SMP not implemented */
- env->CP0_EBase = 0x80000000;
+ env->CP0_EBase = 0x80000000 | (env->cpu_index & 0x3FF);
env->CP0_Status = (1 << CP0St_BEV) | (1 << CP0St_ERL);
/* vectored interrupts not implemented, timer on int 7,
no performance counters. */
diff --git a/target-mips/translate_init.c b/target-mips/translate_init.c
index 0d9899e1bd..8d1ece704d 100644
--- a/target-mips/translate_init.c
+++ b/target-mips/translate_init.c
@@ -45,10 +45,6 @@
(0 << CP0C3_VEIC) | (0 << CP0C3_VInt) | (0 << CP0C3_SP) | \
(0 << CP0C3_SM) | (0 << CP0C3_TL))
-/* Define a implementation number of 1.
- Define a major version 1, minor version 0. */
-#define MIPS_FCR0 ((0 << FCR0_S) | (0x1 << FCR0_PRID) | (0x10 << FCR0_REV))
-
/* MMU types, the first four entries have the same layout as the
CP0C0_MT field. */
enum mips_mmu_types {
diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h
index 9c8d774c9d..1334dd1b46 100644
--- a/target-ppc/cpu.h
+++ b/target-ppc/cpu.h
@@ -453,6 +453,9 @@ struct ppc_slb_t {
#endif
#endif
+/* Exception state register bits definition */
+#define ESR_ST 23 /* Exception was caused by a store type access. */
+
enum {
POWERPC_FLAG_NONE = 0x00000000,
/* Flag for MSR bit 25 signification (VRE/SPE) */
@@ -697,8 +700,9 @@ struct CPUPPCState {
int power_mode;
int (*check_pow)(CPUPPCState *env);
- /* temporary hack to handle OSI calls (only used if non NULL) */
- int (*osi_call)(struct CPUPPCState *env);
+#if !defined(CONFIG_USER_ONLY)
+ void *load_info; /* Holds boot loading state. */
+#endif
};
#if !defined(CONFIG_USER_ONLY)
diff --git a/target-ppc/helper.c b/target-ppc/helper.c
index d342b09c8e..4b491012d7 100644
--- a/target-ppc/helper.c
+++ b/target-ppc/helper.c
@@ -1050,7 +1050,6 @@ static inline int ppcemb_tlb_check(CPUState *env, ppcemb_tlb_t *tlb,
/* Check valid flag */
if (!(tlb->prot & PAGE_VALID)) {
- qemu_log("%s: TLB %d not valid\n", __func__, i);
return -1;
}
mask = ~(tlb->size - 1);
@@ -1173,9 +1172,7 @@ static int mmu40x_get_physical_address (CPUState *env, mmu_ctx_t *ctx,
case 0x1:
check_perms:
/* Check from TLB entry */
- /* XXX: there is a problem here or in the TLB fill code... */
ctx->prot = tlb->prot;
- ctx->prot |= PAGE_EXEC;
ret = check_prot(ctx->prot, rw, access_type);
if (ret == -2)
env->spr[SPR_40x_ESR] = 0;
@@ -1326,8 +1323,15 @@ int get_physical_address (CPUState *env, mmu_ctx_t *ctx, target_ulong eaddr,
#endif
if ((access_type == ACCESS_CODE && msr_ir == 0) ||
(access_type != ACCESS_CODE && msr_dr == 0)) {
- /* No address translation */
- ret = check_physical(env, ctx, eaddr, rw);
+ if (env->mmu_model == POWERPC_MMU_BOOKE) {
+ /* The BookE MMU always performs address translation. The
+ IS and DS bits only affect the address space. */
+ ret = mmubooke_get_physical_address(env, ctx, eaddr,
+ rw, access_type);
+ } else {
+ /* No address translation. */
+ ret = check_physical(env, ctx, eaddr, rw);
+ }
} else {
ret = -1;
switch (env->mmu_model) {
@@ -1445,8 +1449,9 @@ int cpu_ppc_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
env->error_code = 0x40000000;
break;
case POWERPC_MMU_BOOKE:
- /* XXX: TODO */
- cpu_abort(env, "BookE MMU model is not implemented\n");
+ env->exception_index = POWERPC_EXCP_ITLB;
+ env->error_code = 0;
+ env->spr[SPR_BOOKE_DEAR] = address;
return -1;
case POWERPC_MMU_BOOKE_FSL:
/* XXX: TODO */
@@ -1472,6 +1477,9 @@ int cpu_ppc_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
break;
case -3:
/* No execute protection violation */
+ if (env->mmu_model == POWERPC_MMU_BOOKE) {
+ env->spr[SPR_BOOKE_ESR] = 0x00000000;
+ }
env->exception_index = POWERPC_EXCP_ISI;
env->error_code = 0x10000000;
break;
@@ -1557,8 +1565,10 @@ int cpu_ppc_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
cpu_abort(env, "MPC8xx MMU model is not implemented\n");
break;
case POWERPC_MMU_BOOKE:
- /* XXX: TODO */
- cpu_abort(env, "BookE MMU model is not implemented\n");
+ env->exception_index = POWERPC_EXCP_DTLB;
+ env->error_code = 0;
+ env->spr[SPR_BOOKE_DEAR] = address;
+ env->spr[SPR_BOOKE_ESR] = rw ? 1 << ESR_ST : 0;
return -1;
case POWERPC_MMU_BOOKE_FSL:
/* XXX: TODO */
@@ -1583,6 +1593,9 @@ int cpu_ppc_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
if (rw) {
env->spr[SPR_40x_ESR] |= 0x00800000;
}
+ } else if (env->mmu_model == POWERPC_MMU_BOOKE) {
+ env->spr[SPR_BOOKE_DEAR] = address;
+ env->spr[SPR_BOOKE_ESR] = rw ? 1 << ESR_ST : 0;
} else {
env->spr[SPR_DAR] = address;
if (rw == 1) {
@@ -1849,8 +1862,7 @@ void ppc_tlb_invalidate_all (CPUPPCState *env)
cpu_abort(env, "MPC8xx MMU model is not implemented\n");
break;
case POWERPC_MMU_BOOKE:
- /* XXX: TODO */
- cpu_abort(env, "BookE MMU model is not implemented\n");
+ tlb_flush(env, 1);
break;
case POWERPC_MMU_BOOKE_FSL:
/* XXX: TODO */
@@ -2074,18 +2086,24 @@ static inline void powerpc_excp(CPUState *env, int excp_model, int excp)
qemu_log_mask(CPU_LOG_INT, "Raise exception at " TARGET_FMT_lx
" => %08x (%02x)\n", env->nip, excp, env->error_code);
- msr = env->msr;
- new_msr = msr;
+
+ /* new srr1 value excluding must-be-zero bits */
+ msr = env->msr & ~0x783f0000ULL;
+
+ /* new interrupt handler msr */
+ new_msr = env->msr & ((target_ulong)1 << MSR_ME);
+
+ /* target registers */
srr0 = SPR_SRR0;
srr1 = SPR_SRR1;
asrr0 = -1;
asrr1 = -1;
+
switch (excp) {
case POWERPC_EXCP_NONE:
/* Should never happen */
return;
case POWERPC_EXCP_CRITICAL: /* Critical input */
- new_msr &= ~((target_ulong)1 << MSR_RI); /* XXX: check this */
switch (excp_model) {
case POWERPC_EXCP_40x:
srr0 = SPR_40x_SRR2;
@@ -2116,12 +2134,14 @@ static inline void powerpc_excp(CPUState *env, int excp_model, int excp)
env->halted = 1;
env->interrupt_request |= CPU_INTERRUPT_EXITTB;
}
- new_msr &= ~((target_ulong)1 << MSR_RI);
- new_msr &= ~((target_ulong)1 << MSR_ME);
if (0) {
/* XXX: find a suitable condition to enable the hypervisor mode */
new_msr |= (target_ulong)MSR_HVB;
}
+
+ /* machine check exceptions don't have ME set */
+ new_msr &= ~((target_ulong)1 << MSR_ME);
+
/* XXX: should also have something loaded in DAR / DSISR */
switch (excp_model) {
case POWERPC_EXCP_40x:
@@ -2141,25 +2161,21 @@ static inline void powerpc_excp(CPUState *env, int excp_model, int excp)
case POWERPC_EXCP_DSI: /* Data storage exception */
LOG_EXCP("DSI exception: DSISR=" TARGET_FMT_lx" DAR=" TARGET_FMT_lx
"\n", env->spr[SPR_DSISR], env->spr[SPR_DAR]);
- new_msr &= ~((target_ulong)1 << MSR_RI);
if (lpes1 == 0)
new_msr |= (target_ulong)MSR_HVB;
goto store_next;
case POWERPC_EXCP_ISI: /* Instruction storage exception */
LOG_EXCP("ISI exception: msr=" TARGET_FMT_lx ", nip=" TARGET_FMT_lx
"\n", msr, env->nip);
- new_msr &= ~((target_ulong)1 << MSR_RI);
if (lpes1 == 0)
new_msr |= (target_ulong)MSR_HVB;
msr |= env->error_code;
goto store_next;
case POWERPC_EXCP_EXTERNAL: /* External input */
- new_msr &= ~((target_ulong)1 << MSR_RI);
if (lpes0 == 1)
new_msr |= (target_ulong)MSR_HVB;
goto store_next;
case POWERPC_EXCP_ALIGN: /* Alignment exception */
- new_msr &= ~((target_ulong)1 << MSR_RI);
if (lpes1 == 0)
new_msr |= (target_ulong)MSR_HVB;
/* XXX: this is false */
@@ -2175,7 +2191,6 @@ static inline void powerpc_excp(CPUState *env, int excp_model, int excp)
env->error_code = 0;
return;
}
- new_msr &= ~((target_ulong)1 << MSR_RI);
if (lpes1 == 0)
new_msr |= (target_ulong)MSR_HVB;
msr |= 0x00100000;
@@ -2185,19 +2200,16 @@ static inline void powerpc_excp(CPUState *env, int excp_model, int excp)
break;
case POWERPC_EXCP_INVAL:
LOG_EXCP("Invalid instruction at " TARGET_FMT_lx "\n", env->nip);
- new_msr &= ~((target_ulong)1 << MSR_RI);
if (lpes1 == 0)
new_msr |= (target_ulong)MSR_HVB;
msr |= 0x00080000;
break;
case POWERPC_EXCP_PRIV:
- new_msr &= ~((target_ulong)1 << MSR_RI);
if (lpes1 == 0)
new_msr |= (target_ulong)MSR_HVB;
msr |= 0x00040000;
break;
case POWERPC_EXCP_TRAP:
- new_msr &= ~((target_ulong)1 << MSR_RI);
if (lpes1 == 0)
new_msr |= (target_ulong)MSR_HVB;
msr |= 0x00020000;
@@ -2210,40 +2222,24 @@ static inline void powerpc_excp(CPUState *env, int excp_model, int excp)
}
goto store_current;
case POWERPC_EXCP_FPU: /* Floating-point unavailable exception */
- new_msr &= ~((target_ulong)1 << MSR_RI);
if (lpes1 == 0)
new_msr |= (target_ulong)MSR_HVB;
goto store_current;
case POWERPC_EXCP_SYSCALL: /* System call exception */
- /* NOTE: this is a temporary hack to support graphics OSI
- calls from the MOL driver */
- /* XXX: To be removed */
- if (env->gpr[3] == 0x113724fa && env->gpr[4] == 0x77810f9b &&
- env->osi_call) {
- if (env->osi_call(env) != 0) {
- env->exception_index = POWERPC_EXCP_NONE;
- env->error_code = 0;
- return;
- }
- }
dump_syscall(env);
- new_msr &= ~((target_ulong)1 << MSR_RI);
lev = env->error_code;
if (lev == 1 || (lpes0 == 0 && lpes1 == 0))
new_msr |= (target_ulong)MSR_HVB;
goto store_next;
case POWERPC_EXCP_APU: /* Auxiliary processor unavailable */
- new_msr &= ~((target_ulong)1 << MSR_RI);
goto store_current;
case POWERPC_EXCP_DECR: /* Decrementer exception */
- new_msr &= ~((target_ulong)1 << MSR_RI);
if (lpes1 == 0)
new_msr |= (target_ulong)MSR_HVB;
goto store_next;
case POWERPC_EXCP_FIT: /* Fixed-interval timer interrupt */
/* FIT on 4xx */
LOG_EXCP("FIT exception\n");
- new_msr &= ~((target_ulong)1 << MSR_RI); /* XXX: check this */
goto store_next;
case POWERPC_EXCP_WDT: /* Watchdog timer interrupt */
LOG_EXCP("WDT exception\n");
@@ -2255,13 +2251,10 @@ static inline void powerpc_excp(CPUState *env, int excp_model, int excp)
default:
break;
}
- new_msr &= ~((target_ulong)1 << MSR_RI); /* XXX: check this */
goto store_next;
case POWERPC_EXCP_DTLB: /* Data TLB error */
- new_msr &= ~((target_ulong)1 << MSR_RI); /* XXX: check this */
goto store_next;
case POWERPC_EXCP_ITLB: /* Instruction TLB error */
- new_msr &= ~((target_ulong)1 << MSR_RI); /* XXX: check this */
goto store_next;
case POWERPC_EXCP_DEBUG: /* Debug interrupt */
switch (excp_model) {
@@ -2278,7 +2271,6 @@ static inline void powerpc_excp(CPUState *env, int excp_model, int excp)
cpu_abort(env, "Debug exception is not implemented yet !\n");
goto store_next;
case POWERPC_EXCP_SPEU: /* SPE/embedded floating-point unavailable */
- new_msr &= ~((target_ulong)1 << MSR_RI); /* XXX: check this */
goto store_current;
case POWERPC_EXCP_EFPDI: /* Embedded floating-point data interrupt */
/* XXX: TODO */
@@ -2291,7 +2283,6 @@ static inline void powerpc_excp(CPUState *env, int excp_model, int excp)
"is not implemented yet !\n");
goto store_next;
case POWERPC_EXCP_EPERFM: /* Embedded performance monitor interrupt */
- new_msr &= ~((target_ulong)1 << MSR_RI);
/* XXX: TODO */
cpu_abort(env,
"Performance counter exception is not implemented yet !\n");
@@ -2315,19 +2306,23 @@ static inline void powerpc_excp(CPUState *env, int excp_model, int excp)
"is not implemented yet !\n");
goto store_next;
case POWERPC_EXCP_RESET: /* System reset exception */
- new_msr &= ~((target_ulong)1 << MSR_RI);
+ if (msr_pow) {
+ /* indicate that we resumed from power save mode */
+ msr |= 0x10000;
+ } else {
+ new_msr &= ~((target_ulong)1 << MSR_ME);
+ }
+
if (0) {
/* XXX: find a suitable condition to enable the hypervisor mode */
new_msr |= (target_ulong)MSR_HVB;
}
goto store_next;
case POWERPC_EXCP_DSEG: /* Data segment exception */
- new_msr &= ~((target_ulong)1 << MSR_RI);
if (lpes1 == 0)
new_msr |= (target_ulong)MSR_HVB;
goto store_next;
case POWERPC_EXCP_ISEG: /* Instruction segment exception */
- new_msr &= ~((target_ulong)1 << MSR_RI);
if (lpes1 == 0)
new_msr |= (target_ulong)MSR_HVB;
goto store_next;
@@ -2335,9 +2330,9 @@ static inline void powerpc_excp(CPUState *env, int excp_model, int excp)
srr0 = SPR_HSRR0;
srr1 = SPR_HSRR1;
new_msr |= (target_ulong)MSR_HVB;
+ new_msr |= env->msr & ((target_ulong)1 << MSR_RI);
goto store_next;
case POWERPC_EXCP_TRACE: /* Trace exception */
- new_msr &= ~((target_ulong)1 << MSR_RI);
if (lpes1 == 0)
new_msr |= (target_ulong)MSR_HVB;
goto store_next;
@@ -2345,30 +2340,32 @@ static inline void powerpc_excp(CPUState *env, int excp_model, int excp)
srr0 = SPR_HSRR0;
srr1 = SPR_HSRR1;
new_msr |= (target_ulong)MSR_HVB;
+ new_msr |= env->msr & ((target_ulong)1 << MSR_RI);
goto store_next;
case POWERPC_EXCP_HISI: /* Hypervisor instruction storage exception */
srr0 = SPR_HSRR0;
srr1 = SPR_HSRR1;
new_msr |= (target_ulong)MSR_HVB;
+ new_msr |= env->msr & ((target_ulong)1 << MSR_RI);
goto store_next;
case POWERPC_EXCP_HDSEG: /* Hypervisor data segment exception */
srr0 = SPR_HSRR0;
srr1 = SPR_HSRR1;
new_msr |= (target_ulong)MSR_HVB;
+ new_msr |= env->msr & ((target_ulong)1 << MSR_RI);
goto store_next;
case POWERPC_EXCP_HISEG: /* Hypervisor instruction segment exception */
srr0 = SPR_HSRR0;
srr1 = SPR_HSRR1;
new_msr |= (target_ulong)MSR_HVB;
+ new_msr |= env->msr & ((target_ulong)1 << MSR_RI);
goto store_next;
case POWERPC_EXCP_VPU: /* Vector unavailable exception */
- new_msr &= ~((target_ulong)1 << MSR_RI);
if (lpes1 == 0)
new_msr |= (target_ulong)MSR_HVB;
goto store_current;
case POWERPC_EXCP_PIT: /* Programmable interval timer interrupt */
LOG_EXCP("PIT exception\n");
- new_msr &= ~((target_ulong)1 << MSR_RI); /* XXX: check this */
goto store_next;
case POWERPC_EXCP_IO: /* IO error exception */
/* XXX: TODO */
@@ -2384,7 +2381,6 @@ static inline void powerpc_excp(CPUState *env, int excp_model, int excp)
"is not implemented yet !\n");
goto store_next;
case POWERPC_EXCP_IFTLB: /* Instruction fetch TLB error */
- new_msr &= ~((target_ulong)1 << MSR_RI); /* XXX: check this */
if (lpes1 == 0) /* XXX: check this */
new_msr |= (target_ulong)MSR_HVB;
switch (excp_model) {
@@ -2403,7 +2399,6 @@ static inline void powerpc_excp(CPUState *env, int excp_model, int excp)
}
break;
case POWERPC_EXCP_DLTLB: /* Data load TLB miss */
- new_msr &= ~((target_ulong)1 << MSR_RI); /* XXX: check this */
if (lpes1 == 0) /* XXX: check this */
new_msr |= (target_ulong)MSR_HVB;
switch (excp_model) {
@@ -2422,7 +2417,6 @@ static inline void powerpc_excp(CPUState *env, int excp_model, int excp)
}
break;
case POWERPC_EXCP_DSTLB: /* Data store TLB miss */
- new_msr &= ~((target_ulong)1 << MSR_RI); /* XXX: check this */
if (lpes1 == 0) /* XXX: check this */
new_msr |= (target_ulong)MSR_HVB;
switch (excp_model) {
@@ -2526,7 +2520,6 @@ static inline void powerpc_excp(CPUState *env, int excp_model, int excp)
"is not implemented yet !\n");
goto store_next;
case POWERPC_EXCP_PERFM: /* Embedded performance monitor interrupt */
- new_msr &= ~((target_ulong)1 << MSR_RI);
if (lpes1 == 0)
new_msr |= (target_ulong)MSR_HVB;
/* XXX: TODO */
@@ -2580,23 +2573,11 @@ static inline void powerpc_excp(CPUState *env, int excp_model, int excp)
/* If we disactivated any translation, flush TLBs */
if (new_msr & ((1 << MSR_IR) | (1 << MSR_DR)))
tlb_flush(env, 1);
- /* reload MSR with correct bits */
- new_msr &= ~((target_ulong)1 << MSR_EE);
- new_msr &= ~((target_ulong)1 << MSR_PR);
- new_msr &= ~((target_ulong)1 << MSR_FP);
- new_msr &= ~((target_ulong)1 << MSR_FE0);
- new_msr &= ~((target_ulong)1 << MSR_SE);
- new_msr &= ~((target_ulong)1 << MSR_BE);
- new_msr &= ~((target_ulong)1 << MSR_FE1);
- new_msr &= ~((target_ulong)1 << MSR_IR);
- new_msr &= ~((target_ulong)1 << MSR_DR);
-#if 0 /* Fix this: not on all targets */
- new_msr &= ~((target_ulong)1 << MSR_PMM);
-#endif
- if (msr_ile)
+
+ if (msr_ile) {
new_msr |= (target_ulong)1 << MSR_LE;
- else
- new_msr &= ~((target_ulong)1 << MSR_LE);
+ }
+
/* Jump to handler */
vector = env->excp_vectors[excp];
if (vector == (target_ulong)-1ULL) {
@@ -2607,14 +2588,12 @@ static inline void powerpc_excp(CPUState *env, int excp_model, int excp)
#if defined(TARGET_PPC64)
if (excp_model == POWERPC_EXCP_BOOKE) {
if (!msr_icm) {
- new_msr &= ~((target_ulong)1 << MSR_CM);
vector = (uint32_t)vector;
} else {
new_msr |= (target_ulong)1 << MSR_CM;
}
} else {
if (!msr_isf && !(env->mmu_model & POWERPC_MMU_64)) {
- new_msr &= ~((target_ulong)1 << MSR_SF);
vector = (uint32_t)vector;
} else {
new_msr |= (target_ulong)1 << MSR_SF;
@@ -2630,6 +2609,13 @@ static inline void powerpc_excp(CPUState *env, int excp_model, int excp)
/* Reset exception state */
env->exception_index = POWERPC_EXCP_NONE;
env->error_code = 0;
+
+ if (env->mmu_model == POWERPC_MMU_BOOKE) {
+ /* XXX: The BookE changes address space when switching modes,
+ we should probably implement that as different MMU indexes,
+ but for the moment we do it the slow way and flush all. */
+ tlb_flush(env, 1);
+ }
}
void do_interrupt (CPUState *env)
diff --git a/target-ppc/helper.h b/target-ppc/helper.h
index c025a2f08a..2bf9283486 100644
--- a/target-ppc/helper.h
+++ b/target-ppc/helper.h
@@ -246,6 +246,7 @@ DEF_HELPER_2(vrefp, void, avr, avr)
DEF_HELPER_2(vrsqrtefp, void, avr, avr)
DEF_HELPER_4(vmaddfp, void, avr, avr, avr, avr)
DEF_HELPER_4(vnmsubfp, void, avr, avr, avr, avr)
+DEF_HELPER_2(vexptefp, void, avr, avr)
DEF_HELPER_2(vlogefp, void, avr, avr)
DEF_HELPER_2(vrfim, void, avr, avr)
DEF_HELPER_2(vrfin, void, avr, avr)
diff --git a/target-ppc/kvm.c b/target-ppc/kvm.c
index 1079ce1e61..5cacef7b58 100644
--- a/target-ppc/kvm.c
+++ b/target-ppc/kvm.c
@@ -37,6 +37,9 @@
do { } while (0)
#endif
+static int cap_interrupt_unset = false;
+static int cap_interrupt_level = false;
+
/* XXX We have a race condition where we actually have a level triggered
* interrupt, but the infrastructure can't expose that yet, so the guest
* takes but ignores it, goes to sleep and never gets notified that there's
@@ -55,6 +58,18 @@ static void kvm_kick_env(void *env)
int kvm_arch_init(KVMState *s, int smp_cpus)
{
+#ifdef KVM_CAP_PPC_UNSET_IRQ
+ cap_interrupt_unset = kvm_check_extension(s, KVM_CAP_PPC_UNSET_IRQ);
+#endif
+#ifdef KVM_CAP_PPC_IRQ_LEVEL
+ cap_interrupt_level = kvm_check_extension(s, KVM_CAP_PPC_IRQ_LEVEL);
+#endif
+
+ if (!cap_interrupt_level) {
+ fprintf(stderr, "KVM: Couldn't find level irq capability. Expect the "
+ "VM to stall at times!\n");
+ }
+
return 0;
}
@@ -178,6 +193,23 @@ int kvm_arch_get_registers(CPUState *env)
return 0;
}
+int kvmppc_set_interrupt(CPUState *env, int irq, int level)
+{
+ unsigned virq = level ? KVM_INTERRUPT_SET_LEVEL : KVM_INTERRUPT_UNSET;
+
+ if (irq != PPC_INTERRUPT_EXT) {
+ return 0;
+ }
+
+ if (!kvm_enabled() || !cap_interrupt_unset || !cap_interrupt_level) {
+ return 0;
+ }
+
+ kvm_vcpu_ioctl(env, KVM_INTERRUPT, &virq);
+
+ return 0;
+}
+
#if defined(TARGET_PPCEMB)
#define PPC_INPUT_INT PPC40x_INPUT_INT
#elif defined(TARGET_PPC64)
@@ -193,7 +225,8 @@ int kvm_arch_pre_run(CPUState *env, struct kvm_run *run)
/* PowerPC Qemu tracks the various core input pins (interrupt, critical
* interrupt, reset, etc) in PPC-specific env->irq_input_state. */
- if (run->ready_for_interrupt_injection &&
+ if (!cap_interrupt_level &&
+ run->ready_for_interrupt_injection &&
(env->interrupt_request & CPU_INTERRUPT_HARD) &&
(env->irq_input_state & (1<<PPC_INPUT_INT)))
{
@@ -201,7 +234,7 @@ int kvm_arch_pre_run(CPUState *env, struct kvm_run *run)
* future KVM could cache it in-kernel to avoid a heavyweight exit
* when reading the UIC.
*/
- irq = -1U;
+ irq = KVM_INTERRUPT_SET;
dprintf("injected interrupt %d\n", irq);
r = kvm_vcpu_ioctl(env, KVM_INTERRUPT, &irq);
@@ -327,6 +360,38 @@ uint32_t kvmppc_get_tbfreq(void)
return retval;
}
+int kvmppc_get_hypercall(CPUState *env, uint8_t *buf, int buf_len)
+{
+ uint32_t *hc = (uint32_t*)buf;
+
+#ifdef KVM_CAP_PPC_GET_PVINFO
+ struct kvm_ppc_pvinfo pvinfo;
+
+ if (kvm_check_extension(env->kvm_state, KVM_CAP_PPC_GET_PVINFO) &&
+ !kvm_vm_ioctl(env->kvm_state, KVM_PPC_GET_PVINFO, &pvinfo)) {
+ memcpy(buf, pvinfo.hcall, buf_len);
+
+ return 0;
+ }
+#endif
+
+ /*
+ * Fallback to always fail hypercalls:
+ *
+ * li r3, -1
+ * nop
+ * nop
+ * nop
+ */
+
+ hc[0] = 0x3860ffff;
+ hc[1] = 0x60000000;
+ hc[2] = 0x60000000;
+ hc[3] = 0x60000000;
+
+ return 0;
+}
+
bool kvm_arch_stop_on_emulation_error(CPUState *env)
{
return true;
diff --git a/target-ppc/kvm_ppc.h b/target-ppc/kvm_ppc.h
index e8d66e88e4..911b19e378 100644
--- a/target-ppc/kvm_ppc.h
+++ b/target-ppc/kvm_ppc.h
@@ -15,5 +15,19 @@ int kvmppc_read_host_property(const char *node_path, const char *prop,
void *val, size_t len);
uint32_t kvmppc_get_tbfreq(void);
+int kvmppc_get_hypercall(CPUState *env, uint8_t *buf, int buf_len);
+int kvmppc_set_interrupt(CPUState *env, int irq, int level);
+
+#ifndef KVM_INTERRUPT_SET
+#define KVM_INTERRUPT_SET -1
+#endif
+
+#ifndef KVM_INTERRUPT_UNSET
+#define KVM_INTERRUPT_UNSET -2
+#endif
+
+#ifndef KVM_INTERRUPT_SET_LEVEL
+#define KVM_INTERRUPT_SET_LEVEL -3
+#endif
#endif /* __KVM_PPC_H__ */
diff --git a/target-ppc/op_helper.c b/target-ppc/op_helper.c
index 3c3aa60bc3..f32a5fffd6 100644
--- a/target-ppc/op_helper.c
+++ b/target-ppc/op_helper.c
@@ -348,15 +348,13 @@ void helper_dcbz_970(target_ulong addr)
void helper_icbi(target_ulong addr)
{
- uint32_t tmp;
-
addr &= ~(env->dcache_line_size - 1);
/* Invalidate one cache line :
* PowerPC specification says this is to be treated like a load
* (not a fetch) by the MMU. To be sure it will be so,
* do the load "by hand".
*/
- tmp = ldl(addr);
+ ldl(addr);
tb_invalidate_page_range(addr, addr + env->icache_line_size);
}
@@ -1955,14 +1953,14 @@ target_ulong helper_dlmzb (target_ulong high, target_ulong low, uint32_t update_
DO_HANDLE_NAN(result, x) DO_HANDLE_NAN(result, y) DO_HANDLE_NAN(result, z)
/* Saturating arithmetic helpers. */
-#define SATCVT(from, to, from_type, to_type, min, max, use_min, use_max) \
+#define SATCVT(from, to, from_type, to_type, min, max) \
static inline to_type cvt##from##to(from_type x, int *sat) \
{ \
to_type r; \
- if (use_min && x < min) { \
+ if (x < (from_type)min) { \
r = min; \
*sat = 1; \
- } else if (use_max && x > max) { \
+ } else if (x > (from_type)max) { \
r = max; \
*sat = 1; \
} else { \
@@ -1970,30 +1968,30 @@ target_ulong helper_dlmzb (target_ulong high, target_ulong low, uint32_t update_
} \
return r; \
}
-SATCVT(sh, sb, int16_t, int8_t, INT8_MIN, INT8_MAX, 1, 1)
-SATCVT(sw, sh, int32_t, int16_t, INT16_MIN, INT16_MAX, 1, 1)
-SATCVT(sd, sw, int64_t, int32_t, INT32_MIN, INT32_MAX, 1, 1)
-
-/* Work around gcc problems with the macro version */
-static inline uint8_t cvtuhub(uint16_t x, int *sat)
-{
- uint8_t r;
-
- if (x > UINT8_MAX) {
- r = UINT8_MAX;
- *sat = 1;
- } else {
- r = x;
+#define SATCVTU(from, to, from_type, to_type, min, max) \
+ static inline to_type cvt##from##to(from_type x, int *sat) \
+ { \
+ to_type r; \
+ if (x > (from_type)max) { \
+ r = max; \
+ *sat = 1; \
+ } else { \
+ r = x; \
+ } \
+ return r; \
}
- return r;
-}
-//SATCVT(uh, ub, uint16_t, uint8_t, 0, UINT8_MAX, 0, 1)
-SATCVT(uw, uh, uint32_t, uint16_t, 0, UINT16_MAX, 0, 1)
-SATCVT(ud, uw, uint64_t, uint32_t, 0, UINT32_MAX, 0, 1)
-SATCVT(sh, ub, int16_t, uint8_t, 0, UINT8_MAX, 1, 1)
-SATCVT(sw, uh, int32_t, uint16_t, 0, UINT16_MAX, 1, 1)
-SATCVT(sd, uw, int64_t, uint32_t, 0, UINT32_MAX, 1, 1)
+SATCVT(sh, sb, int16_t, int8_t, INT8_MIN, INT8_MAX)
+SATCVT(sw, sh, int32_t, int16_t, INT16_MIN, INT16_MAX)
+SATCVT(sd, sw, int64_t, int32_t, INT32_MIN, INT32_MAX)
+
+SATCVTU(uh, ub, uint16_t, uint8_t, 0, UINT8_MAX)
+SATCVTU(uw, uh, uint32_t, uint16_t, 0, UINT16_MAX)
+SATCVTU(ud, uw, uint64_t, uint32_t, 0, UINT32_MAX)
+SATCVT(sh, ub, int16_t, uint8_t, 0, UINT8_MAX)
+SATCVT(sw, uh, int32_t, uint16_t, 0, UINT16_MAX)
+SATCVT(sd, uw, int64_t, uint32_t, 0, UINT32_MAX)
#undef SATCVT
+#undef SATCVTU
#define LVE(name, access, swap, element) \
void helper_##name (ppc_avr_t *r, target_ulong addr) \
@@ -2713,6 +2711,16 @@ void helper_vsel (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c)
r->u64[1] = (a->u64[1] & ~c->u64[1]) | (b->u64[1] & c->u64[1]);
}
+void helper_vexptefp (ppc_avr_t *r, ppc_avr_t *b)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(r->f); i++) {
+ HANDLE_NAN1(r->f[i], b->f[i]) {
+ r->f[i] = float32_exp2(b->f[i], &env->vec_status);
+ }
+ }
+}
+
void helper_vlogefp (ppc_avr_t *r, ppc_avr_t *b)
{
int i;
@@ -3804,6 +3812,7 @@ static void do_6xx_tlb (target_ulong new_EPN, int is_code)
EPN = env->spr[SPR_DMISS];
}
way = (env->spr[SPR_SRR1] >> 17) & 1;
+ (void)EPN; /* avoid a compiler warning */
LOG_SWTLB("%s: EPN " TARGET_FMT_lx " " TARGET_FMT_lx " PTE0 " TARGET_FMT_lx
" PTE1 " TARGET_FMT_lx " way %d\n", __func__, new_EPN, EPN, CMP,
RPN, way);
@@ -3832,6 +3841,7 @@ static void do_74xx_tlb (target_ulong new_EPN, int is_code)
CMP = env->spr[SPR_PTEHI];
EPN = env->spr[SPR_TLBMISS] & ~0x3;
way = env->spr[SPR_TLBMISS] & 0x3;
+ (void)EPN; /* avoid a compiler warning */
LOG_SWTLB("%s: EPN " TARGET_FMT_lx " " TARGET_FMT_lx " PTE0 " TARGET_FMT_lx
" PTE1 " TARGET_FMT_lx " way %d\n", __func__, new_EPN, EPN, CMP,
RPN, way);
@@ -3919,37 +3929,56 @@ static inline int booke_page_size_to_tlb(target_ulong page_size)
}
/* Helpers for 4xx TLB management */
-target_ulong helper_4xx_tlbre_lo (target_ulong entry)
+#define PPC4XX_TLB_ENTRY_MASK 0x0000003f /* Mask for 64 TLB entries */
+
+#define PPC4XX_TLBHI_V 0x00000040
+#define PPC4XX_TLBHI_E 0x00000020
+#define PPC4XX_TLBHI_SIZE_MIN 0
+#define PPC4XX_TLBHI_SIZE_MAX 7
+#define PPC4XX_TLBHI_SIZE_DEFAULT 1
+#define PPC4XX_TLBHI_SIZE_SHIFT 7
+#define PPC4XX_TLBHI_SIZE_MASK 0x00000007
+
+#define PPC4XX_TLBLO_EX 0x00000200
+#define PPC4XX_TLBLO_WR 0x00000100
+#define PPC4XX_TLBLO_ATTR_MASK 0x000000FF
+#define PPC4XX_TLBLO_RPN_MASK 0xFFFFFC00
+
+target_ulong helper_4xx_tlbre_hi (target_ulong entry)
{
ppcemb_tlb_t *tlb;
target_ulong ret;
int size;
- entry &= 0x3F;
+ entry &= PPC4XX_TLB_ENTRY_MASK;
tlb = &env->tlb[entry].tlbe;
ret = tlb->EPN;
- if (tlb->prot & PAGE_VALID)
- ret |= 0x400;
+ if (tlb->prot & PAGE_VALID) {
+ ret |= PPC4XX_TLBHI_V;
+ }
size = booke_page_size_to_tlb(tlb->size);
- if (size < 0 || size > 0x7)
- size = 1;
- ret |= size << 7;
+ if (size < PPC4XX_TLBHI_SIZE_MIN || size > PPC4XX_TLBHI_SIZE_MAX) {
+ size = PPC4XX_TLBHI_SIZE_DEFAULT;
+ }
+ ret |= size << PPC4XX_TLBHI_SIZE_SHIFT;
env->spr[SPR_40x_PID] = tlb->PID;
return ret;
}
-target_ulong helper_4xx_tlbre_hi (target_ulong entry)
+target_ulong helper_4xx_tlbre_lo (target_ulong entry)
{
ppcemb_tlb_t *tlb;
target_ulong ret;
- entry &= 0x3F;
+ entry &= PPC4XX_TLB_ENTRY_MASK;
tlb = &env->tlb[entry].tlbe;
ret = tlb->RPN;
- if (tlb->prot & PAGE_EXEC)
- ret |= 0x200;
- if (tlb->prot & PAGE_WRITE)
- ret |= 0x100;
+ if (tlb->prot & PAGE_EXEC) {
+ ret |= PPC4XX_TLBLO_EX;
+ }
+ if (tlb->prot & PAGE_WRITE) {
+ ret |= PPC4XX_TLBLO_WR;
+ }
return ret;
}
@@ -3960,30 +3989,32 @@ void helper_4xx_tlbwe_hi (target_ulong entry, target_ulong val)
LOG_SWTLB("%s entry %d val " TARGET_FMT_lx "\n", __func__, (int)entry,
val);
- entry &= 0x3F;
+ entry &= PPC4XX_TLB_ENTRY_MASK;
tlb = &env->tlb[entry].tlbe;
/* Invalidate previous TLB (if it's valid) */
if (tlb->prot & PAGE_VALID) {
end = tlb->EPN + tlb->size;
LOG_SWTLB("%s: invalidate old TLB %d start " TARGET_FMT_lx " end "
TARGET_FMT_lx "\n", __func__, (int)entry, tlb->EPN, end);
- for (page = tlb->EPN; page < end; page += TARGET_PAGE_SIZE)
+ for (page = tlb->EPN; page < end; page += TARGET_PAGE_SIZE) {
tlb_flush_page(env, page);
+ }
}
- tlb->size = booke_tlb_to_page_size((val >> 7) & 0x7);
+ tlb->size = booke_tlb_to_page_size((val >> PPC4XX_TLBHI_SIZE_SHIFT)
+ & PPC4XX_TLBHI_SIZE_MASK);
/* We cannot handle TLB size < TARGET_PAGE_SIZE.
* If this ever occurs, one should use the ppcemb target instead
* of the ppc or ppc64 one
*/
- if ((val & 0x40) && tlb->size < TARGET_PAGE_SIZE) {
+ if ((val & PPC4XX_TLBHI_V) && tlb->size < TARGET_PAGE_SIZE) {
cpu_abort(env, "TLB size " TARGET_FMT_lu " < %u "
"are not supported (%d)\n",
tlb->size, TARGET_PAGE_SIZE, (int)((val >> 7) & 0x7));
}
tlb->EPN = val & ~(tlb->size - 1);
- if (val & 0x40) {
+ if (val & PPC4XX_TLBHI_V) {
tlb->prot |= PAGE_VALID;
- if (val & 0x20) {
+ if (val & PPC4XX_TLBHI_E) {
/* XXX: TO BE FIXED */
cpu_abort(env,
"Little-endian TLB entries are not supported by now\n");
@@ -4004,8 +4035,9 @@ void helper_4xx_tlbwe_hi (target_ulong entry, target_ulong val)
end = tlb->EPN + tlb->size;
LOG_SWTLB("%s: invalidate TLB %d start " TARGET_FMT_lx " end "
TARGET_FMT_lx "\n", __func__, (int)entry, tlb->EPN, end);
- for (page = tlb->EPN; page < end; page += TARGET_PAGE_SIZE)
+ for (page = tlb->EPN; page < end; page += TARGET_PAGE_SIZE) {
tlb_flush_page(env, page);
+ }
}
}
@@ -4015,15 +4047,17 @@ void helper_4xx_tlbwe_lo (target_ulong entry, target_ulong val)
LOG_SWTLB("%s entry %i val " TARGET_FMT_lx "\n", __func__, (int)entry,
val);
- entry &= 0x3F;
+ entry &= PPC4XX_TLB_ENTRY_MASK;
tlb = &env->tlb[entry].tlbe;
- tlb->attr = val & 0xFF;
- tlb->RPN = val & 0xFFFFFC00;
+ tlb->attr = val & PPC4XX_TLBLO_ATTR_MASK;
+ tlb->RPN = val & PPC4XX_TLBLO_RPN_MASK;
tlb->prot = PAGE_READ;
- if (val & 0x200)
+ if (val & PPC4XX_TLBLO_EX) {
tlb->prot |= PAGE_EXEC;
- if (val & 0x100)
+ }
+ if (val & PPC4XX_TLBLO_WR) {
tlb->prot |= PAGE_WRITE;
+ }
LOG_SWTLB("%s: set up TLB %d RPN " TARGET_FMT_plx " EPN " TARGET_FMT_lx
" size " TARGET_FMT_lx " prot %c%c%c%c PID %d\n", __func__,
(int)entry, tlb->RPN, tlb->EPN, tlb->size,
diff --git a/target-ppc/translate.c b/target-ppc/translate.c
index 66e1c0d3bf..fd0686182e 100644
--- a/target-ppc/translate.c
+++ b/target-ppc/translate.c
@@ -6382,6 +6382,7 @@ GEN_VXFORM_NOA(vupkhpx, 7, 13);
GEN_VXFORM_NOA(vupklpx, 7, 15);
GEN_VXFORM_NOA(vrefp, 5, 4);
GEN_VXFORM_NOA(vrsqrtefp, 5, 5);
+GEN_VXFORM_NOA(vexptefp, 5, 6);
GEN_VXFORM_NOA(vlogefp, 5, 7);
GEN_VXFORM_NOA(vrfim, 5, 8);
GEN_VXFORM_NOA(vrfin, 5, 9);
@@ -8047,10 +8048,10 @@ GEN_HANDLER(stswi, 0x1F, 0x15, 0x16, 0x00000001, PPC_STRING),
GEN_HANDLER(stswx, 0x1F, 0x15, 0x14, 0x00000001, PPC_STRING),
GEN_HANDLER(eieio, 0x1F, 0x16, 0x1A, 0x03FFF801, PPC_MEM_EIEIO),
GEN_HANDLER(isync, 0x13, 0x16, 0x04, 0x03FFF801, PPC_MEM),
-GEN_HANDLER(lwarx, 0x1F, 0x14, 0x00, 0x00000001, PPC_RES),
+GEN_HANDLER(lwarx, 0x1F, 0x14, 0x00, 0x00000000, PPC_RES),
GEN_HANDLER2(stwcx_, "stwcx.", 0x1F, 0x16, 0x04, 0x00000000, PPC_RES),
#if defined(TARGET_PPC64)
-GEN_HANDLER(ldarx, 0x1F, 0x14, 0x02, 0x00000001, PPC_64B),
+GEN_HANDLER(ldarx, 0x1F, 0x14, 0x02, 0x00000000, PPC_64B),
GEN_HANDLER2(stdcx_, "stdcx.", 0x1F, 0x16, 0x06, 0x00000000, PPC_64B),
#endif
GEN_HANDLER(sync, 0x1F, 0x16, 0x12, 0x039FF801, PPC_MEM_SYNC),
@@ -8696,6 +8697,7 @@ GEN_VXFORM_NOA(vupkhpx, 7, 13),
GEN_VXFORM_NOA(vupklpx, 7, 15),
GEN_VXFORM_NOA(vrefp, 5, 4),
GEN_VXFORM_NOA(vrsqrtefp, 5, 5),
+GEN_VXFORM_NOA(vexptefp, 5, 6),
GEN_VXFORM_NOA(vlogefp, 5, 7),
GEN_VXFORM_NOA(vrfim, 5, 8),
GEN_VXFORM_NOA(vrfin, 5, 9),
diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c
index e8eadf48aa..05ffe95762 100644
--- a/target-ppc/translate_init.c
+++ b/target-ppc/translate_init.c
@@ -3596,7 +3596,6 @@ static void init_proc_440x4 (CPUPPCState *env)
POWERPC_FLAG_DE | POWERPC_FLAG_BUS_CLK)
#define check_pow_440x5 check_pow_nocheck
-__attribute__ (( unused ))
static void init_proc_440x5 (CPUPPCState *env)
{
/* Time base */
@@ -3656,7 +3655,7 @@ static void init_proc_440x5 (CPUPPCState *env)
init_excp_BookE(env);
env->dcache_line_size = 32;
env->icache_line_size = 32;
- /* XXX: TODO: allocate internal IRQ controller */
+ ppc40x_irq_init(env);
}
/* PowerPC 460 (guessed) */
@@ -5237,7 +5236,7 @@ static void init_proc_755 (CPUPPCState *env)
#define POWERPC_FLAG_7400 (POWERPC_FLAG_VRE | POWERPC_FLAG_SE | \
POWERPC_FLAG_BE | POWERPC_FLAG_PMM | \
POWERPC_FLAG_BUS_CLK)
-#define check_pow_7400 check_pow_hid0_74xx
+#define check_pow_7400 check_pow_hid0
static void init_proc_7400 (CPUPPCState *env)
{
@@ -5289,7 +5288,7 @@ static void init_proc_7400 (CPUPPCState *env)
#define POWERPC_FLAG_7410 (POWERPC_FLAG_VRE | POWERPC_FLAG_SE | \
POWERPC_FLAG_BE | POWERPC_FLAG_PMM | \
POWERPC_FLAG_BUS_CLK)
-#define check_pow_7410 check_pow_hid0_74xx
+#define check_pow_7410 check_pow_hid0
static void init_proc_7410 (CPUPPCState *env)
{
@@ -6536,6 +6535,7 @@ enum {
#if 0
CPU_POWERPC_440A4 = xxx,
#endif
+ CPU_POWERPC_440_XILINX = 0x7ff21910,
#if 0
CPU_POWERPC_440A5 = xxx,
#endif
@@ -7464,6 +7464,8 @@ static const ppc_def_t ppc_defs[] = {
/* PowerPC 440 A4 */
POWERPC_DEF("440A4", CPU_POWERPC_440A4, 440x4),
#endif
+ /* PowerPC 440 Xilinx 5 */
+ POWERPC_DEF("440-Xilinx", CPU_POWERPC_440_XILINX, 440x5),
#if defined (TODO)
/* PowerPC 440 A5 */
POWERPC_DEF("440A5", CPU_POWERPC_440A5, 440x5),
diff --git a/tcg/ppc/tcg-target.h b/tcg/ppc/tcg-target.h
index 53024282da..a1f85997c6 100644
--- a/tcg/ppc/tcg-target.h
+++ b/tcg/ppc/tcg-target.h
@@ -65,7 +65,7 @@ enum {
/* used for function call generation */
#define TCG_REG_CALL_STACK TCG_REG_R1
#define TCG_TARGET_STACK_ALIGN 16
-#if defined _CALL_DARWIN
+#if defined _CALL_DARWIN || defined __APPLE__
#define TCG_TARGET_CALL_STACK_OFFSET 24
#elif defined _CALL_AIX
#define TCG_TARGET_CALL_STACK_OFFSET 52
diff --git a/tcg/ppc64/tcg-target.c b/tcg/ppc64/tcg-target.c
index 5ba5d053b7..ebbee343fd 100644
--- a/tcg/ppc64/tcg-target.c
+++ b/tcg/ppc64/tcg-target.c
@@ -746,7 +746,7 @@ static void tcg_out_qemu_ld (TCGContext *s, const TCGArg *args, int opc)
else tcg_out32 (s, LDX | TAB (data_reg, rbase, r0));
#else
if (bswap) {
- tcg_out_movi32 (s, TCG_TYPE_I64, 0, 4);
+ tcg_out_movi32 (s, 0, 4);
tcg_out32 (s, LWBRX | RT (data_reg) | RB (r0));
tcg_out32 (s, LWBRX | RT ( r1) | RA (r0));
tcg_out_rld (s, RLDIMI, data_reg, r1, 32, 0);
diff --git a/tcg/tcg.c b/tcg/tcg.c
index e0a90302a7..0cdef0d213 100644
--- a/tcg/tcg.c
+++ b/tcg/tcg.c
@@ -1027,7 +1027,7 @@ void tcg_add_target_add_op_defs(const TCGTargetOpDef *tdefs)
if (tdefs->op == (TCGOpcode)-1)
break;
op = tdefs->op;
- assert(op >= 0 && op < NB_OPS);
+ assert((unsigned)op < NB_OPS);
def = &tcg_op_defs[op];
#if defined(CONFIG_DEBUG_TCG)
/* Duplicate entry in op definitions? */
diff --git a/tests/Makefile b/tests/Makefile
index ff7f787a9a..e43ec70b6d 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -1,83 +1,120 @@
-include ../config-host.mak
+-include $(SRC_PATH)/rules.mak
$(call set-vpath, $(SRC_PATH)/tests)
-CFLAGS=-Wall -O2 -g -fno-strict-aliasing
+QEMU=../i386-linux-user/qemu-i386
+QEMU_X86_64=../x86_64-linux-user/qemu-x86_64
+CC_X86_64=$(CC_I386) -m64
+
+CFLAGS=-Wall -O2 -g -fno-strict-aliasing -I..
#CFLAGS+=-msse2
LDFLAGS=
-ifeq ($(ARCH),i386)
-TESTS=linux-test testthread sha1-i386 test-i386
+# TODO: automatically detect ARM and MIPS compilers, and run those too
+
+# runcom maps page 0, so it requires root privileges
+# also, pi_10.com runs indefinitely
+
+I386_TESTS=hello-i386 \
+ linux-test \
+ testthread \
+ sha1-i386 \
+ test-i386 \
+ test-mmap \
+ # runcom
+
+# native i386 compilers sometimes are not biarch. assume cross-compilers are
+ifneq ($(ARCH),i386)
+I386_TESTS+=run-test-x86_64
endif
-ifeq ($(ARCH),x86_64)
-TESTS=test-x86_64
+
+TESTS = test_path
+ifneq ($(call find-in-path, $(CC_I386)),)
+TESTS += $(I386_TESTS)
endif
-TESTS+=sha1# test_path
-#TESTS+=test_path
-#TESTS+=runcom
-QEMU=../i386-linux-user/qemu-i386
+all: $(patsubst %,run-%,$(TESTS))
+
+# rules to run tests
+
+.PHONY: $(patsubst %,run-%,$(TESTS))
+
+run-%: %
+ -$(QEMU) ./$*
+
+run-hello-i386: hello-i386
+run-linux-test: linux-test
+run-testthread: testthread
+run-sha1-i386: sha1-i386
+
+run-test-i386: test-i386
+ ./test-i386 > test-i386.ref
+ -$(QEMU) test-i386 > test-i386.out
+ @if diff -u test-i386.ref test-i386.out ; then echo "Auto Test OK"; fi
+
+run-test-x86_64: test-x86_64
+ ./test-x86_64 > test-x86_64.ref
+ -$(QEMU_X86_64) test-x86_64 > test-x86_64.out
+ @if diff -u test-x86_64.ref test-x86_64.out ; then echo "Auto Test OK"; fi
+
+run-test-mmap: test-mmap
+ -$(QEMU) ./test-mmap
+ -$(QEMU) -p 8192 ./test-mmap 8192
+ -$(QEMU) -p 16384 ./test-mmap 16384
+ -$(QEMU) -p 32768 ./test-mmap 32768
+
+run-runcom: runcom
+ -$(QEMU) ./runcom $(SRC_PATH)/tests/pi_10.com
+
+run-test_path: test_path
+ ./test_path
+
+# rules to compile tests
-all: $(TESTS)
+test_path: test_path.o
+test_path.o: test_path.c
hello-i386: hello-i386.c
- $(CC) -nostdlib $(CFLAGS) -static $(LDFLAGS) -o $@ $<
+ $(CC_I386) -nostdlib $(CFLAGS) -static $(LDFLAGS) -o $@ $<
strip $@
testthread: testthread.c
- $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< -lpthread
-
-test_path: test_path.c
- $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<
- ./$@ || { rm $@; exit 1; }
+ $(CC_I386) $(CFLAGS) $(LDFLAGS) -o $@ $< -lpthread
# i386/x86_64 emulation test (test various opcodes) */
test-i386: test-i386.c test-i386-code16.S test-i386-vm86.S \
test-i386.h test-i386-shift.h test-i386-muldiv.h
- $(CC) -m32 $(CFLAGS) $(LDFLAGS) -static -o $@ \
+ $(CC_I386) $(CFLAGS) $(LDFLAGS) -o $@ \
$(<D)/test-i386.c $(<D)/test-i386-code16.S $(<D)/test-i386-vm86.S -lm
test-x86_64: test-i386.c \
test-i386.h test-i386-shift.h test-i386-muldiv.h
- $(CC) -m64 $(CFLAGS) $(LDFLAGS) -static -o $@ $(<D)/test-i386.c -lm
-
-ifeq ($(ARCH),i386)
-test: test-i386
- ./test-i386 > test-i386.ref
-else
-test:
-endif
- $(QEMU) test-i386 > test-i386.out
- @if diff -u test-i386.ref test-i386.out ; then echo "Auto Test OK"; fi
-
-.PHONY: test-mmap
-test-mmap: test-mmap.c
- $(CC) $(CFLAGS) -Wall -static -O2 $(LDFLAGS) -o $@ $<
- -./test-mmap
- -$(QEMU) ./test-mmap
- -$(QEMU) -p 8192 ./test-mmap 8192
- -$(QEMU) -p 16384 ./test-mmap 16384
- -$(QEMU) -p 32768 ./test-mmap 32768
+ $(CC_X86_64) $(CFLAGS) $(LDFLAGS) -o $@ $(<D)/test-i386.c -lm
# generic Linux and CPU test
linux-test: linux-test.c
- $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< -lm
+ $(CC_I386) $(CFLAGS) $(LDFLAGS) -o $@ $< -lm
+
+# vm86 test
+runcom: runcom.c
+ $(CC_I386) $(CFLAGS) $(LDFLAGS) -o $@ $<
+
+test-mmap: test-mmap.c
+ $(CC_I386) -m32 $(CFLAGS) -Wall -O2 $(LDFLAGS) -o $@ $<
# speed test
sha1-i386: sha1.c
- $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<
+ $(CC_I386) $(CFLAGS) $(LDFLAGS) -o $@ $<
sha1: sha1.c
- $(HOST_CC) $(CFLAGS) $(LDFLAGS) -o $@ $<
+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<
speed: sha1 sha1-i386
time ./sha1
time $(QEMU) ./sha1-i386
-# vm86 test
-runcom: runcom.c
- $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<
-
+# broken test
# NOTE: -fomit-frame-pointer is currently needed : this is a bug in libqemu
qruncom: qruncom.c ../ioport-user.c ../i386-user/libqemu.a
$(CC) $(CFLAGS) -fomit-frame-pointer $(LDFLAGS) -I../target-i386 -I.. -I../i386-user -I../fpu \
diff --git a/tests/cris/check_addo.c b/tests/cris/check_addo.c
index 8a0565a1a9..3d8e789f5a 100644
--- a/tests/cris/check_addo.c
+++ b/tests/cris/check_addo.c
@@ -85,7 +85,7 @@ int main(void)
cris_tst_cc_init();
asm volatile ("setf\tzvnc\n");
cris_addo_pi_b(p, t);
- cris_tst_cc(1, 1, 1, 1);
+ cris_tst_cc(0, 0, 0, 0);
asm volatile ("move.d\t$acr, %0\n" : "=r" (r));
if (*(uint16_t*)r != 0xff22)
err();
@@ -114,7 +114,7 @@ int main(void)
cris_tst_cc_init();
asm volatile ("setf\tzvnc\n");
cris_addo_pi_d(p, t);
- cris_tst_cc(1, 1, 1, 1);
+ cris_tst_cc(0, 0, 0, 0);
asm volatile ("move.d\t$acr, %0\n" : "=r" (r));
r = (void*)(((char *)r) + 76789885);
if (*r != 0x55aa77ff)
diff --git a/tests/cris/check_addoq.c b/tests/cris/check_addoq.c
index b8b15c3090..ed509e27e0 100644
--- a/tests/cris/check_addoq.c
+++ b/tests/cris/check_addoq.c
@@ -27,7 +27,7 @@ int main(void)
cris_tst_cc_init();
asm volatile ("setf\tzvnc\n");
cris_addoq(4, t);
- cris_tst_cc(1, 1, 1, 1);
+ cris_tst_cc(0, 0, 0, 0);
asm volatile ("move.d\t$acr, %0\n" : "=r" (p));
if (*p != 0x88ccee19)
err();
diff --git a/tests/cris/check_settls1.c b/tests/cris/check_settls1.c
index 0ed99cf4c1..69d202652a 100644
--- a/tests/cris/check_settls1.c
+++ b/tests/cris/check_settls1.c
@@ -11,11 +11,15 @@
int main (void)
{
- unsigned long tp;
+ unsigned long tp, old_tp;
int ret;
+ asm volatile ("move $pid,%0" : "=r" (old_tp));
+ old_tp &= ~0xff;
+
ret = syscall (SYS_set_thread_area, 0xf0);
if (ret != -1 || errno != EINVAL) {
+ syscall (SYS_set_thread_area, old_tp);
perror ("Invalid thread area accepted:");
abort();
}
@@ -26,10 +30,12 @@ int main (void)
abort ();
}
- asm ("move $pid,%0" : "=r" (tp));
+ asm volatile ("move $pid,%0" : "=r" (tp));
tp &= ~0xff;
+ syscall (SYS_set_thread_area, old_tp);
if (tp != 0xeddeed00) {
+ * (volatile int *) 0 = 0;
perror ("tls2");
abort ();
}
diff --git a/tests/cris/check_swap.c b/tests/cris/check_swap.c
index 743cfc54d3..824a685517 100644
--- a/tests/cris/check_swap.c
+++ b/tests/cris/check_swap.c
@@ -41,7 +41,7 @@ do { \
cris_tst_mov_cc(n, z); \
if (r != expected) \
err(); \
-} while(0);
+} while(0)
void check_swap(void)
{
diff --git a/tests/runcom.c b/tests/runcom.c
index 6380566635..d60342bfc6 100644
--- a/tests/runcom.c
+++ b/tests/runcom.c
@@ -13,15 +13,12 @@
#include <linux/unistd.h>
#include <asm/vm86.h>
-//#define SIGTEST
+extern int vm86 (unsigned long int subfunction,
+ struct vm86plus_struct *info);
-#undef __syscall_return
-#define __syscall_return(type, res) \
-do { \
- return (type) (res); \
-} while (0)
+#define VIF_MASK 0x00080000
-_syscall2(int, vm86, int, func, struct vm86plus_struct *, v86)
+//#define SIGTEST
#define COM_BASE_ADDR 0x10100
diff --git a/tests/sha1.c b/tests/sha1.c
index 3a76555825..93b7c8e808 100644
--- a/tests/sha1.c
+++ b/tests/sha1.c
@@ -23,7 +23,7 @@ A million repetitions of "a"
#include <stdio.h>
#include <string.h>
-#include <sys/types.h> /* for u_int*_t */
+#include <stdint.h>
/* ================ sha1.h ================ */
/*
@@ -33,14 +33,14 @@ By Steve Reid <steve@edmweb.com>
*/
typedef struct {
- u_int32_t state[5];
- u_int32_t count[2];
+ uint32_t state[5];
+ uint32_t count[2];
unsigned char buffer[64];
} SHA1_CTX;
-void SHA1Transform(u_int32_t state[5], const unsigned char buffer[64]);
+void SHA1Transform(uint32_t state[5], const unsigned char buffer[64]);
void SHA1Init(SHA1_CTX* context);
-void SHA1Update(SHA1_CTX* context, const unsigned char* data, u_int32_t len);
+void SHA1Update(SHA1_CTX* context, const unsigned char* data, uint32_t len);
void SHA1Final(unsigned char digest[20], SHA1_CTX* context);
/* ================ end of sha1.h ================ */
#include <endian.h>
@@ -70,12 +70,12 @@ void SHA1Final(unsigned char digest[20], SHA1_CTX* context);
/* Hash a single 512-bit block. This is the core of the algorithm. */
-void SHA1Transform(u_int32_t state[5], const unsigned char buffer[64])
+void SHA1Transform(uint32_t state[5], const unsigned char buffer[64])
{
-u_int32_t a, b, c, d, e;
+uint32_t a, b, c, d, e;
typedef union {
unsigned char c[64];
- u_int32_t l[16];
+ uint32_t l[16];
} CHAR64LONG16;
#ifdef SHA1HANDSOFF
CHAR64LONG16 block[1]; /* use array to appear as a pointer */
@@ -145,10 +145,10 @@ void SHA1Init(SHA1_CTX* context)
/* Run your data through this. */
-void SHA1Update(SHA1_CTX* context, const unsigned char* data, u_int32_t len)
+void SHA1Update(SHA1_CTX* context, const unsigned char* data, uint32_t len)
{
-u_int32_t i;
-u_int32_t j;
+uint32_t i;
+uint32_t j;
j = context->count[0];
if ((context->count[0] += len << 3) < j)
@@ -186,7 +186,7 @@ unsigned char c;
for (i = 0; i < 2; i++)
{
- u_int32_t t = context->count[i];
+ uint32_t t = context->count[i];
int j;
for (j = 0; j < 4; t >>= 8, j++)
diff --git a/tests/test-i386.c b/tests/test-i386.c
index b28b257f81..8f481c7f7a 100644
--- a/tests/test-i386.c
+++ b/tests/test-i386.c
@@ -2047,6 +2047,10 @@ long enter_stack[4096];
#define RBP "%%ebp"
#endif
+#if !defined(__x86_64__)
+/* causes an infinite loop, disable it for now. */
+#define TEST_ENTER(size, stack_type, level)
+#else
#define TEST_ENTER(size, stack_type, level)\
{\
long esp_save, esp_val, ebp_val, ebp_save, i;\
@@ -2078,6 +2082,7 @@ long enter_stack[4096];
for(ptr = (stack_type *)esp_val; ptr < stack_end; ptr++)\
printf(FMTLX "\n", (long)ptr[0]);\
}
+#endif
static void test_enter(void)
{
diff --git a/tests/test_path.c b/tests/test_path.c
index def7441c8c..234ed97088 100644
--- a/tests/test_path.c
+++ b/tests/test_path.c
@@ -1,12 +1,21 @@
/* Test path override code */
-#define _GNU_SOURCE
+#include "../config-host.h"
+#include "../qemu-malloc.c"
+#include "../cutils.c"
#include "../path.c"
+#include "../trace.c"
+#ifdef CONFIG_SIMPLE_TRACE
+#include "../simpletrace.c"
+#endif
+
#include <stdarg.h>
#include <sys/stat.h>
#include <fcntl.h>
+void qemu_log(const char *fmt, ...);
+
/* Any log message kills the test. */
-void gemu_log(const char *fmt, ...)
+void qemu_log(const char *fmt, ...)
{
va_list ap;
diff --git a/trace-events b/trace-events
new file mode 100644
index 0000000000..ed2055e425
--- /dev/null
+++ b/trace-events
@@ -0,0 +1,83 @@
+# Trace events for debugging and performance instrumentation
+#
+# This file is processed by the tracetool script during the build.
+#
+# To add a new trace event:
+#
+# 1. Choose a name for the trace event. Declare its arguments and format
+# string.
+#
+# 2. Call the trace event from code using trace_##name, e.g. multiwrite_cb() ->
+# trace_multiwrite_cb(). The source file must #include "trace.h".
+#
+# Format of a trace event:
+#
+# [disable] <name>(<type1> <arg1>[, <type2> <arg2>] ...) "<format-string>"
+#
+# Example: qemu_malloc(size_t size) "size %zu"
+#
+# The "disable" keyword will build without the trace event.
+# In case of 'simple' trace backend, it will allow the trace event to be
+# compiled, but this would be turned off by default. It can be toggled on via
+# the monitor.
+#
+# The <name> must be a valid as a C function name.
+#
+# Types should be standard C types. Use void * for pointers because the trace
+# system may not have the necessary headers included.
+#
+# The <format-string> should be a sprintf()-compatible format string.
+
+# qemu-malloc.c
+disable qemu_malloc(size_t size, void *ptr) "size %zu ptr %p"
+disable qemu_realloc(void *ptr, size_t size, void *newptr) "ptr %p size %zu newptr %p"
+disable qemu_free(void *ptr) "ptr %p"
+
+# osdep.c
+disable qemu_memalign(size_t alignment, size_t size, void *ptr) "alignment %zu size %zu ptr %p"
+disable qemu_vmalloc(size_t size, void *ptr) "size %zu ptr %p"
+disable qemu_vfree(void *ptr) "ptr %p"
+
+# hw/virtio.c
+disable virtqueue_fill(void *vq, const void *elem, unsigned int len, unsigned int idx) "vq %p elem %p len %u idx %u"
+disable virtqueue_flush(void *vq, unsigned int count) "vq %p count %u"
+disable virtqueue_pop(void *vq, void *elem, unsigned int in_num, unsigned int out_num) "vq %p elem %p in_num %u out_num %u"
+disable virtio_queue_notify(void *vdev, int n, void *vq) "vdev %p n %d vq %p"
+disable virtio_irq(void *vq) "vq %p"
+disable virtio_notify(void *vdev, void *vq) "vdev %p vq %p"
+
+# block.c
+disable multiwrite_cb(void *mcb, int ret) "mcb %p ret %d"
+disable bdrv_aio_multiwrite(void *mcb, int num_callbacks, int num_reqs) "mcb %p num_callbacks %d num_reqs %d"
+disable bdrv_aio_multiwrite_earlyfail(void *mcb) "mcb %p"
+disable bdrv_aio_multiwrite_latefail(void *mcb, int i) "mcb %p i %d"
+disable bdrv_aio_readv(void *bs, int64_t sector_num, int nb_sectors, void *opaque) "bs %p sector_num %"PRId64" nb_sectors %d opaque %p"
+disable bdrv_aio_writev(void *bs, int64_t sector_num, int nb_sectors, void *opaque) "bs %p sector_num %"PRId64" nb_sectors %d opaque %p"
+
+# hw/virtio-blk.c
+disable virtio_blk_req_complete(void *req, int status) "req %p status %d"
+disable virtio_blk_rw_complete(void *req, int ret) "req %p ret %d"
+disable virtio_blk_handle_write(void *req, uint64_t sector, size_t nsectors) "req %p sector %"PRIu64" nsectors %zu"
+
+# posix-aio-compat.c
+disable paio_submit(void *acb, void *opaque, int64_t sector_num, int nb_sectors, int type) "acb %p opaque %p sector_num %"PRId64" nb_sectors %d type %d"
+
+# ioport.c
+disable cpu_in(unsigned int addr, unsigned int val) "addr %#x value %u"
+disable cpu_out(unsigned int addr, unsigned int val) "addr %#x value %u"
+
+# balloon.c
+# Since requests are raised via monitor, not many tracepoints are needed.
+disable balloon_event(void *opaque, unsigned long addr) "opaque %p addr %lu"
+
+# hw/apic.c
+disable apic_local_deliver(int vector, uint32_t lvt) "vector %d delivery mode %d"
+disable apic_deliver_irq(uint8_t dest, uint8_t dest_mode, uint8_t delivery_mode, uint8_t vector_num, uint8_t polarity, uint8_t trigger_mode) "dest %d dest_mode %d delivery_mode %d vector %d polarity %d trigger_mode %d"
+disable cpu_set_apic_base(uint64_t val) "%016"PRIx64""
+disable cpu_get_apic_base(uint64_t val) "%016"PRIx64""
+disable apic_mem_readl(uint64_t addr, uint32_t val) "%"PRIx64" = %08x"
+disable apic_mem_writel(uint64_t addr, uint32_t val) "%"PRIx64" = %08x"
+# coalescing
+disable apic_reset_irq_delivered(int apic_irq_delivered) "old coalescing %d"
+disable apic_get_irq_delivered(int apic_irq_delivered) "returning coalescing %d"
+disable apic_set_irq(int apic_irq_delivered) "coalescing %d"
diff --git a/tracetool b/tracetool
new file mode 100755
index 0000000000..701085837f
--- /dev/null
+++ b/tracetool
@@ -0,0 +1,377 @@
+#!/bin/sh
+#
+# Code generator for trace events
+#
+# Copyright IBM, Corp. 2010
+#
+# This work is licensed under the terms of the GNU GPL, version 2. See
+# the COPYING file in the top-level directory.
+
+# Disable pathname expansion, makes processing text with '*' characters simpler
+set -f
+
+usage()
+{
+ cat >&2 <<EOF
+usage: $0 [--nop | --simple | --ust] [-h | -c]
+Generate tracing code for a file on stdin.
+
+Backends:
+ --nop Tracing disabled
+ --simple Simple built-in backend
+ --ust LTTng User Space Tracing backend
+
+Output formats:
+ -h Generate .h file
+ -c Generate .c file
+EOF
+ exit 1
+}
+
+# Get the name of a trace event
+get_name()
+{
+ echo ${1%%\(*}
+}
+
+# Get the argument list of a trace event, including types and names
+get_args()
+{
+ local args
+ args=${1#*\(}
+ args=${args%\)*}
+ echo "$args"
+}
+
+# Get the argument name list of a trace event
+get_argnames()
+{
+ local nfields field name
+ nfields=0
+ for field in $(get_args "$1"); do
+ nfields=$((nfields + 1))
+
+ # Drop pointer star
+ field=${field#\*}
+
+ # Only argument names have commas at the end
+ name=${field%,}
+ test "$field" = "$name" && continue
+
+ printf "%s" "$name, "
+ done
+
+ # Last argument name
+ if [ "$nfields" -gt 1 ]
+ then
+ printf "%s" "$name"
+ fi
+}
+
+# Get the number of arguments to a trace event
+get_argc()
+{
+ local name argc
+ argc=0
+ for name in $(get_argnames "$1"); do
+ argc=$((argc + 1))
+ done
+ echo $argc
+}
+
+# Get the format string for a trace event
+get_fmt()
+{
+ local fmt
+ fmt=${1#*\"}
+ fmt=${fmt%\"*}
+ echo "$fmt"
+}
+
+# Get the state of a trace event
+get_state()
+{
+ local str disable state
+ str=$(get_name "$1")
+ disable=${str##disable }
+ if [ "$disable" = "$str" ] ; then
+ state=1
+ else
+ state=0
+ fi
+ echo "$state"
+}
+
+linetoh_begin_nop()
+{
+ return
+}
+
+linetoh_nop()
+{
+ local name args
+ name=$(get_name "$1")
+ args=$(get_args "$1")
+
+ # Define an empty function for the trace event
+ cat <<EOF
+static inline void trace_$name($args)
+{
+}
+EOF
+}
+
+linetoh_end_nop()
+{
+ return
+}
+
+linetoc_begin_nop()
+{
+ return
+}
+
+linetoc_nop()
+{
+ # No need for function definitions in nop backend
+ return
+}
+
+linetoc_end_nop()
+{
+ return
+}
+
+linetoh_begin_simple()
+{
+ cat <<EOF
+#include "simpletrace.h"
+EOF
+
+ simple_event_num=0
+}
+
+cast_args_to_uint64_t()
+{
+ local arg
+ for arg in $(get_argnames "$1"); do
+ printf "%s" "(uint64_t)(uintptr_t)$arg"
+ done
+}
+
+linetoh_simple()
+{
+ local name args argc trace_args state
+ name=$(get_name "$1")
+ args=$(get_args "$1")
+ argc=$(get_argc "$1")
+ state=$(get_state "$1")
+ if [ "$state" = "0" ]; then
+ name=${name##disable }
+ fi
+
+ trace_args="$simple_event_num"
+ if [ "$argc" -gt 0 ]
+ then
+ trace_args="$trace_args, $(cast_args_to_uint64_t "$1")"
+ fi
+
+ cat <<EOF
+static inline void trace_$name($args)
+{
+ trace$argc($trace_args);
+}
+EOF
+
+ simple_event_num=$((simple_event_num + 1))
+}
+
+linetoh_end_simple()
+{
+ cat <<EOF
+#define NR_TRACE_EVENTS $simple_event_num
+extern TraceEvent trace_list[NR_TRACE_EVENTS];
+EOF
+}
+
+linetoc_begin_simple()
+{
+ cat <<EOF
+#include "trace.h"
+
+TraceEvent trace_list[] = {
+EOF
+ simple_event_num=0
+
+}
+
+linetoc_simple()
+{
+ local name state
+ name=$(get_name "$1")
+ state=$(get_state "$1")
+ if [ "$state" = "0" ] ; then
+ name=${name##disable }
+ fi
+ cat <<EOF
+{.tp_name = "$name", .state=$state},
+EOF
+ simple_event_num=$((simple_event_num + 1))
+}
+
+linetoc_end_simple()
+{
+ cat <<EOF
+};
+EOF
+}
+
+# Clean up after UST headers which pollute the namespace
+ust_clean_namespace() {
+ cat <<EOF
+#undef mutex_lock
+#undef mutex_unlock
+#undef inline
+#undef wmb
+EOF
+}
+
+linetoh_begin_ust()
+{
+ echo "#include <ust/tracepoint.h>"
+ ust_clean_namespace
+}
+
+linetoh_ust()
+{
+ local name args argnames
+ name=$(get_name "$1")
+ args=$(get_args "$1")
+ argnames=$(get_argnames "$1")
+
+ cat <<EOF
+DECLARE_TRACE(ust_$name, TP_PROTO($args), TP_ARGS($argnames));
+#define trace_$name trace_ust_$name
+EOF
+}
+
+linetoh_end_ust()
+{
+ return
+}
+
+linetoc_begin_ust()
+{
+ cat <<EOF
+#include <ust/marker.h>
+$(ust_clean_namespace)
+#include "trace.h"
+EOF
+}
+
+linetoc_ust()
+{
+ local name args argnames fmt
+ name=$(get_name "$1")
+ args=$(get_args "$1")
+ argnames=$(get_argnames "$1")
+ fmt=$(get_fmt "$1")
+
+ cat <<EOF
+DEFINE_TRACE(ust_$name);
+
+static void ust_${name}_probe($args)
+{
+ trace_mark(ust, $name, "$fmt", $argnames);
+}
+EOF
+
+ # Collect names for later
+ names="$names $name"
+}
+
+linetoc_end_ust()
+{
+ cat <<EOF
+static void __attribute__((constructor)) trace_init(void)
+{
+EOF
+
+ for name in $names; do
+ cat <<EOF
+ register_trace_ust_$name(ust_${name}_probe);
+EOF
+ done
+
+ echo "}"
+}
+
+# Process stdin by calling begin, line, and end functions for the backend
+convert()
+{
+ local begin process_line end str disable
+ begin="lineto$1_begin_$backend"
+ process_line="lineto$1_$backend"
+ end="lineto$1_end_$backend"
+
+ "$begin"
+
+ while read -r str; do
+ # Skip comments and empty lines
+ test -z "${str%%#*}" && continue
+
+ # Process the line. The nop backend handles disabled lines.
+ disable=${str%%disable *}
+ echo
+ if test -z "$disable"; then
+ # Pass the disabled state as an arg to lineto$1_simple().
+ # For all other cases, call lineto$1_nop()
+ if [ $backend = "simple" ]; then
+ "$process_line" "$str"
+ else
+ "lineto$1_nop" "${str##disable }"
+ fi
+ else
+ "$process_line" "$str"
+ fi
+ done
+
+ echo
+ "$end"
+}
+
+tracetoh()
+{
+ cat <<EOF
+#ifndef TRACE_H
+#define TRACE_H
+
+/* This file is autogenerated by tracetool, do not edit. */
+
+#include "qemu-common.h"
+EOF
+ convert h
+ echo "#endif /* TRACE_H */"
+}
+
+tracetoc()
+{
+ echo "/* This file is autogenerated by tracetool, do not edit. */"
+ convert c
+}
+
+# Choose backend
+case "$1" in
+"--nop" | "--simple" | "--ust") backend="${1#--}" ;;
+*) usage ;;
+esac
+shift
+
+case "$1" in
+"-h") tracetoh ;;
+"-c") tracetoc ;;
+"--check-backend") exit 0 ;; # used by ./configure to test for backend
+*) usage ;;
+esac
+
+exit 0
diff --git a/cocoa.m b/ui/cocoa.m
index 56c789a98c..56c789a98c 100644
--- a/cocoa.m
+++ b/ui/cocoa.m
diff --git a/curses.c b/ui/curses.c
index ed3165e45e..82bc614040 100644
--- a/curses.c
+++ b/ui/curses.c
@@ -238,9 +238,12 @@ static void curses_refresh(DisplayState *ds)
keysym = curses2keysym[chr];
if (keysym == -1) {
- if (chr < ' ')
- keysym = (chr + '@' - 'A' + 'a') | KEYSYM_CNTRL;
- else
+ if (chr < ' ') {
+ keysym = chr + '@';
+ if (keysym >= 'A' && keysym <= 'Z')
+ keysym += 'a' - 'A';
+ keysym |= KEYSYM_CNTRL;
+ } else
keysym = chr;
}
diff --git a/curses_keys.h b/ui/curses_keys.h
index 1decd1119d..c0d5eb452f 100644
--- a/curses_keys.h
+++ b/ui/curses_keys.h
@@ -55,6 +55,7 @@ static const int curses2keysym[CURSES_KEYS] = {
[0x7f] = KEY_BACKSPACE,
['\r'] = KEY_ENTER,
['\n'] = KEY_ENTER,
+ [27] = 27,
[KEY_BTAB] = '\t' | KEYSYM_SHIFT,
};
diff --git a/d3des.c b/ui/d3des.c
index 60c840ed53..60c840ed53 100644
--- a/d3des.c
+++ b/ui/d3des.c
diff --git a/d3des.h b/ui/d3des.h
index ea3da44ce9..ea3da44ce9 100644
--- a/d3des.h
+++ b/ui/d3des.h
diff --git a/keymaps.c b/ui/keymaps.c
index 78c7ea375c..78c7ea375c 100644
--- a/keymaps.c
+++ b/ui/keymaps.c
diff --git a/keymaps.h b/ui/keymaps.h
index a7600d5751..a7600d5751 100644
--- a/keymaps.h
+++ b/ui/keymaps.h
diff --git a/ui/qemu-spice.h b/ui/qemu-spice.h
new file mode 100644
index 0000000000..063c7dc8c8
--- /dev/null
+++ b/ui/qemu-spice.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef QEMU_SPICE_H
+#define QEMU_SPICE_H
+
+#ifdef CONFIG_SPICE
+
+#include <spice.h>
+
+#include "qemu-option.h"
+#include "qemu-config.h"
+
+extern int using_spice;
+
+void qemu_spice_init(void);
+void qemu_spice_input_init(void);
+void qemu_spice_display_init(DisplayState *ds);
+int qemu_spice_add_interface(SpiceBaseInstance *sin);
+
+#else /* CONFIG_SPICE */
+
+#define using_spice 0
+
+#endif /* CONFIG_SPICE */
+
+#endif /* QEMU_SPICE_H */
diff --git a/sdl.c b/ui/sdl.c
index 0072680f4c..f599d42425 100644
--- a/sdl.c
+++ b/ui/sdl.c
@@ -21,6 +21,10 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+
+/* Avoid compiler warning because macro is redefined in SDL_syswm.h. */
+#undef WIN32_LEAN_AND_MEAN
+
#include <SDL.h>
#include <SDL_syswm.h>
diff --git a/sdl_keysym.h b/ui/sdl_keysym.h
index ee904805da..ee904805da 100644
--- a/sdl_keysym.h
+++ b/ui/sdl_keysym.h
diff --git a/sdl_zoom.c b/ui/sdl_zoom.c
index a986c7c14c..a986c7c14c 100644
--- a/sdl_zoom.c
+++ b/ui/sdl_zoom.c
diff --git a/sdl_zoom.h b/ui/sdl_zoom.h
index 74955bc944..74955bc944 100644
--- a/sdl_zoom.h
+++ b/ui/sdl_zoom.h
diff --git a/sdl_zoom_template.h b/ui/sdl_zoom_template.h
index 64bbca849b..64bbca849b 100644
--- a/sdl_zoom_template.h
+++ b/ui/sdl_zoom_template.h
diff --git a/ui/spice-core.c b/ui/spice-core.c
new file mode 100644
index 0000000000..8b5e4a8096
--- /dev/null
+++ b/ui/spice-core.c
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <spice.h>
+#include <spice-experimental.h>
+
+#include "qemu-common.h"
+#include "qemu-spice.h"
+#include "qemu-timer.h"
+#include "qemu-queue.h"
+#include "monitor.h"
+
+/* core bits */
+
+static SpiceServer *spice_server;
+int using_spice = 0;
+
+struct SpiceTimer {
+ QEMUTimer *timer;
+ QTAILQ_ENTRY(SpiceTimer) next;
+};
+static QTAILQ_HEAD(, SpiceTimer) timers = QTAILQ_HEAD_INITIALIZER(timers);
+
+static SpiceTimer *timer_add(SpiceTimerFunc func, void *opaque)
+{
+ SpiceTimer *timer;
+
+ timer = qemu_mallocz(sizeof(*timer));
+ timer->timer = qemu_new_timer(rt_clock, func, opaque);
+ QTAILQ_INSERT_TAIL(&timers, timer, next);
+ return timer;
+}
+
+static void timer_start(SpiceTimer *timer, uint32_t ms)
+{
+ qemu_mod_timer(timer->timer, qemu_get_clock(rt_clock) + ms);
+}
+
+static void timer_cancel(SpiceTimer *timer)
+{
+ qemu_del_timer(timer->timer);
+}
+
+static void timer_remove(SpiceTimer *timer)
+{
+ qemu_del_timer(timer->timer);
+ qemu_free_timer(timer->timer);
+ QTAILQ_REMOVE(&timers, timer, next);
+ qemu_free(timer);
+}
+
+struct SpiceWatch {
+ int fd;
+ int event_mask;
+ SpiceWatchFunc func;
+ void *opaque;
+ QTAILQ_ENTRY(SpiceWatch) next;
+};
+static QTAILQ_HEAD(, SpiceWatch) watches = QTAILQ_HEAD_INITIALIZER(watches);
+
+static void watch_read(void *opaque)
+{
+ SpiceWatch *watch = opaque;
+ watch->func(watch->fd, SPICE_WATCH_EVENT_READ, watch->opaque);
+}
+
+static void watch_write(void *opaque)
+{
+ SpiceWatch *watch = opaque;
+ watch->func(watch->fd, SPICE_WATCH_EVENT_WRITE, watch->opaque);
+}
+
+static void watch_update_mask(SpiceWatch *watch, int event_mask)
+{
+ IOHandler *on_read = NULL;
+ IOHandler *on_write = NULL;
+
+ watch->event_mask = event_mask;
+ if (watch->event_mask & SPICE_WATCH_EVENT_READ) {
+ on_read = watch_read;
+ }
+ if (watch->event_mask & SPICE_WATCH_EVENT_WRITE) {
+ on_read = watch_write;
+ }
+ qemu_set_fd_handler(watch->fd, on_read, on_write, watch);
+}
+
+static SpiceWatch *watch_add(int fd, int event_mask, SpiceWatchFunc func, void *opaque)
+{
+ SpiceWatch *watch;
+
+ watch = qemu_mallocz(sizeof(*watch));
+ watch->fd = fd;
+ watch->func = func;
+ watch->opaque = opaque;
+ QTAILQ_INSERT_TAIL(&watches, watch, next);
+
+ watch_update_mask(watch, event_mask);
+ return watch;
+}
+
+static void watch_remove(SpiceWatch *watch)
+{
+ watch_update_mask(watch, 0);
+ QTAILQ_REMOVE(&watches, watch, next);
+ qemu_free(watch);
+}
+
+static SpiceCoreInterface core_interface = {
+ .base.type = SPICE_INTERFACE_CORE,
+ .base.description = "qemu core services",
+ .base.major_version = SPICE_INTERFACE_CORE_MAJOR,
+ .base.minor_version = SPICE_INTERFACE_CORE_MINOR,
+
+ .timer_add = timer_add,
+ .timer_start = timer_start,
+ .timer_cancel = timer_cancel,
+ .timer_remove = timer_remove,
+
+ .watch_add = watch_add,
+ .watch_update_mask = watch_update_mask,
+ .watch_remove = watch_remove,
+};
+
+/* functions for the rest of qemu */
+
+void qemu_spice_init(void)
+{
+ QemuOpts *opts = QTAILQ_FIRST(&qemu_spice_opts.head);
+ const char *password;
+ int port;
+
+ if (!opts) {
+ return;
+ }
+ port = qemu_opt_get_number(opts, "port", 0);
+ if (!port) {
+ return;
+ }
+ password = qemu_opt_get(opts, "password");
+
+ spice_server = spice_server_new();
+ spice_server_set_port(spice_server, port);
+ if (password) {
+ spice_server_set_ticket(spice_server, password, 0, 0, 0);
+ }
+ if (qemu_opt_get_bool(opts, "disable-ticketing", 0)) {
+ spice_server_set_noauth(spice_server);
+ }
+
+ /* TODO: make configurable via cmdline */
+ spice_server_set_image_compression(spice_server, SPICE_IMAGE_COMPRESS_AUTO_GLZ);
+
+ spice_server_init(spice_server, &core_interface);
+ using_spice = 1;
+
+ qemu_spice_input_init();
+}
+
+int qemu_spice_add_interface(SpiceBaseInstance *sin)
+{
+ return spice_server_add_interface(spice_server, sin);
+}
+
+static void spice_register_config(void)
+{
+ qemu_add_opts(&qemu_spice_opts);
+}
+machine_init(spice_register_config);
+
+static void spice_initialize(void)
+{
+ qemu_spice_init();
+}
+device_init(spice_initialize);
diff --git a/ui/spice-display.c b/ui/spice-display.c
new file mode 100644
index 0000000000..7b4f5c1bc9
--- /dev/null
+++ b/ui/spice-display.c
@@ -0,0 +1,411 @@
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <pthread.h>
+
+#include "qemu-common.h"
+#include "qemu-spice.h"
+#include "qemu-timer.h"
+#include "qemu-queue.h"
+#include "monitor.h"
+#include "console.h"
+#include "sysemu.h"
+
+#include "spice-display.h"
+
+static int debug = 0;
+
+static void GCC_FMT_ATTR(2, 3) dprint(int level, const char *fmt, ...)
+{
+ va_list args;
+
+ if (level <= debug) {
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ }
+}
+
+int qemu_spice_rect_is_empty(const QXLRect* r)
+{
+ return r->top == r->bottom || r->left == r->right;
+}
+
+void qemu_spice_rect_union(QXLRect *dest, const QXLRect *r)
+{
+ if (qemu_spice_rect_is_empty(r)) {
+ return;
+ }
+
+ if (qemu_spice_rect_is_empty(dest)) {
+ *dest = *r;
+ return;
+ }
+
+ dest->top = MIN(dest->top, r->top);
+ dest->left = MIN(dest->left, r->left);
+ dest->bottom = MAX(dest->bottom, r->bottom);
+ dest->right = MAX(dest->right, r->right);
+}
+
+/*
+ * Called from spice server thread context (via interface_get_command).
+ * We do *not* hold the global qemu mutex here, so extra care is needed
+ * when calling qemu functions. Qemu interfaces used:
+ * - pflib (is re-entrant).
+ * - qemu_malloc (underlying glibc malloc is re-entrant).
+ */
+SimpleSpiceUpdate *qemu_spice_create_update(SimpleSpiceDisplay *ssd)
+{
+ SimpleSpiceUpdate *update;
+ QXLDrawable *drawable;
+ QXLImage *image;
+ QXLCommand *cmd;
+ uint8_t *src, *dst;
+ int by, bw, bh;
+
+ if (qemu_spice_rect_is_empty(&ssd->dirty)) {
+ return NULL;
+ };
+
+ pthread_mutex_lock(&ssd->lock);
+ dprint(2, "%s: lr %d -> %d, tb -> %d -> %d\n", __FUNCTION__,
+ ssd->dirty.left, ssd->dirty.right,
+ ssd->dirty.top, ssd->dirty.bottom);
+
+ update = qemu_mallocz(sizeof(*update));
+ drawable = &update->drawable;
+ image = &update->image;
+ cmd = &update->ext.cmd;
+
+ bw = ssd->dirty.right - ssd->dirty.left;
+ bh = ssd->dirty.bottom - ssd->dirty.top;
+ update->bitmap = qemu_malloc(bw * bh * 4);
+
+ drawable->bbox = ssd->dirty;
+ drawable->clip.type = SPICE_CLIP_TYPE_NONE;
+ drawable->effect = QXL_EFFECT_OPAQUE;
+ drawable->release_info.id = (intptr_t)update;
+ drawable->type = QXL_DRAW_COPY;
+ drawable->surfaces_dest[0] = -1;
+ drawable->surfaces_dest[1] = -1;
+ drawable->surfaces_dest[2] = -1;
+
+ drawable->u.copy.rop_descriptor = SPICE_ROPD_OP_PUT;
+ drawable->u.copy.src_bitmap = (intptr_t)image;
+ drawable->u.copy.src_area.right = bw;
+ drawable->u.copy.src_area.bottom = bh;
+
+ QXL_SET_IMAGE_ID(image, QXL_IMAGE_GROUP_DEVICE, ssd->unique++);
+ image->descriptor.type = SPICE_IMAGE_TYPE_BITMAP;
+ image->bitmap.flags = QXL_BITMAP_DIRECT | QXL_BITMAP_TOP_DOWN;
+ image->bitmap.stride = bw * 4;
+ image->descriptor.width = image->bitmap.x = bw;
+ image->descriptor.height = image->bitmap.y = bh;
+ image->bitmap.data = (intptr_t)(update->bitmap);
+ image->bitmap.palette = 0;
+ image->bitmap.format = SPICE_BITMAP_FMT_32BIT;
+
+ if (ssd->conv == NULL) {
+ PixelFormat dst = qemu_default_pixelformat(32);
+ ssd->conv = qemu_pf_conv_get(&dst, &ssd->ds->surface->pf);
+ assert(ssd->conv);
+ }
+
+ src = ds_get_data(ssd->ds) +
+ ssd->dirty.top * ds_get_linesize(ssd->ds) +
+ ssd->dirty.left * ds_get_bytes_per_pixel(ssd->ds);
+ dst = update->bitmap;
+ for (by = 0; by < bh; by++) {
+ qemu_pf_conv_run(ssd->conv, dst, src, bw);
+ src += ds_get_linesize(ssd->ds);
+ dst += image->bitmap.stride;
+ }
+
+ cmd->type = QXL_CMD_DRAW;
+ cmd->data = (intptr_t)drawable;
+
+ memset(&ssd->dirty, 0, sizeof(ssd->dirty));
+ pthread_mutex_unlock(&ssd->lock);
+ return update;
+}
+
+/*
+ * Called from spice server thread context (via interface_release_ressource)
+ * We do *not* hold the global qemu mutex here, so extra care is needed
+ * when calling qemu functions. Qemu interfaces used:
+ * - qemu_free (underlying glibc free is re-entrant).
+ */
+void qemu_spice_destroy_update(SimpleSpiceDisplay *sdpy, SimpleSpiceUpdate *update)
+{
+ qemu_free(update->bitmap);
+ qemu_free(update);
+}
+
+void qemu_spice_create_host_memslot(SimpleSpiceDisplay *ssd)
+{
+ QXLDevMemSlot memslot;
+
+ dprint(1, "%s:\n", __FUNCTION__);
+
+ memset(&memslot, 0, sizeof(memslot));
+ memslot.slot_group_id = MEMSLOT_GROUP_HOST;
+ memslot.virt_end = ~0;
+ ssd->worker->add_memslot(ssd->worker, &memslot);
+}
+
+void qemu_spice_create_host_primary(SimpleSpiceDisplay *ssd)
+{
+ QXLDevSurfaceCreate surface;
+
+ dprint(1, "%s: %dx%d\n", __FUNCTION__,
+ ds_get_width(ssd->ds), ds_get_height(ssd->ds));
+
+ surface.format = SPICE_SURFACE_FMT_32_xRGB;
+ surface.width = ds_get_width(ssd->ds);
+ surface.height = ds_get_height(ssd->ds);
+ surface.stride = -surface.width * 4;
+ surface.mouse_mode = true;
+ surface.flags = 0;
+ surface.type = 0;
+ surface.mem = (intptr_t)ssd->buf;
+ surface.group_id = MEMSLOT_GROUP_HOST;
+ ssd->worker->create_primary_surface(ssd->worker, 0, &surface);
+}
+
+void qemu_spice_destroy_host_primary(SimpleSpiceDisplay *ssd)
+{
+ dprint(1, "%s:\n", __FUNCTION__);
+
+ ssd->worker->destroy_primary_surface(ssd->worker, 0);
+}
+
+void qemu_spice_vm_change_state_handler(void *opaque, int running, int reason)
+{
+ SimpleSpiceDisplay *ssd = opaque;
+
+ if (running) {
+ ssd->worker->start(ssd->worker);
+ } else {
+ ssd->worker->stop(ssd->worker);
+ }
+ ssd->running = running;
+}
+
+/* display listener callbacks */
+
+void qemu_spice_display_update(SimpleSpiceDisplay *ssd,
+ int x, int y, int w, int h)
+{
+ QXLRect update_area;
+
+ dprint(2, "%s: x %d y %d w %d h %d\n", __FUNCTION__, x, y, w, h);
+ update_area.left = x,
+ update_area.right = x + w;
+ update_area.top = y;
+ update_area.bottom = y + h;
+
+ pthread_mutex_lock(&ssd->lock);
+ if (qemu_spice_rect_is_empty(&ssd->dirty)) {
+ ssd->notify++;
+ }
+ qemu_spice_rect_union(&ssd->dirty, &update_area);
+ pthread_mutex_unlock(&ssd->lock);
+}
+
+void qemu_spice_display_resize(SimpleSpiceDisplay *ssd)
+{
+ dprint(1, "%s:\n", __FUNCTION__);
+
+ pthread_mutex_lock(&ssd->lock);
+ memset(&ssd->dirty, 0, sizeof(ssd->dirty));
+ qemu_pf_conv_put(ssd->conv);
+ ssd->conv = NULL;
+ pthread_mutex_unlock(&ssd->lock);
+
+ qemu_spice_destroy_host_primary(ssd);
+ qemu_spice_create_host_primary(ssd);
+
+ pthread_mutex_lock(&ssd->lock);
+ memset(&ssd->dirty, 0, sizeof(ssd->dirty));
+ ssd->notify++;
+ pthread_mutex_unlock(&ssd->lock);
+}
+
+void qemu_spice_display_refresh(SimpleSpiceDisplay *ssd)
+{
+ dprint(3, "%s:\n", __FUNCTION__);
+ vga_hw_update();
+ if (ssd->notify) {
+ ssd->notify = 0;
+ ssd->worker->wakeup(ssd->worker);
+ dprint(2, "%s: notify\n", __FUNCTION__);
+ }
+}
+
+/* spice display interface callbacks */
+
+static void interface_attach_worker(QXLInstance *sin, QXLWorker *qxl_worker)
+{
+ SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl);
+
+ dprint(1, "%s:\n", __FUNCTION__);
+ ssd->worker = qxl_worker;
+}
+
+static void interface_set_compression_level(QXLInstance *sin, int level)
+{
+ dprint(1, "%s:\n", __FUNCTION__);
+ /* nothing to do */
+}
+
+static void interface_set_mm_time(QXLInstance *sin, uint32_t mm_time)
+{
+ dprint(3, "%s:\n", __FUNCTION__);
+ /* nothing to do */
+}
+
+static void interface_get_init_info(QXLInstance *sin, QXLDevInitInfo *info)
+{
+ SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl);
+
+ info->memslot_gen_bits = MEMSLOT_GENERATION_BITS;
+ info->memslot_id_bits = MEMSLOT_SLOT_BITS;
+ info->num_memslots = NUM_MEMSLOTS;
+ info->num_memslots_groups = NUM_MEMSLOTS_GROUPS;
+ info->internal_groupslot_id = 0;
+ info->qxl_ram_size = ssd->bufsize;
+ info->n_surfaces = NUM_SURFACES;
+}
+
+static int interface_get_command(QXLInstance *sin, struct QXLCommandExt *ext)
+{
+ SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl);
+ SimpleSpiceUpdate *update;
+
+ dprint(3, "%s:\n", __FUNCTION__);
+ update = qemu_spice_create_update(ssd);
+ if (update == NULL) {
+ return false;
+ }
+ *ext = update->ext;
+ return true;
+}
+
+static int interface_req_cmd_notification(QXLInstance *sin)
+{
+ dprint(1, "%s:\n", __FUNCTION__);
+ return 1;
+}
+
+static void interface_release_resource(QXLInstance *sin,
+ struct QXLReleaseInfoExt ext)
+{
+ SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl);
+ uintptr_t id;
+
+ dprint(2, "%s:\n", __FUNCTION__);
+ id = ext.info->id;
+ qemu_spice_destroy_update(ssd, (void*)id);
+}
+
+static int interface_get_cursor_command(QXLInstance *sin, struct QXLCommandExt *ext)
+{
+ dprint(3, "%s:\n", __FUNCTION__);
+ return false;
+}
+
+static int interface_req_cursor_notification(QXLInstance *sin)
+{
+ dprint(1, "%s:\n", __FUNCTION__);
+ return 1;
+}
+
+static void interface_notify_update(QXLInstance *sin, uint32_t update_id)
+{
+ fprintf(stderr, "%s: abort()\n", __FUNCTION__);
+ abort();
+}
+
+static int interface_flush_resources(QXLInstance *sin)
+{
+ fprintf(stderr, "%s: abort()\n", __FUNCTION__);
+ abort();
+ return 0;
+}
+
+static const QXLInterface dpy_interface = {
+ .base.type = SPICE_INTERFACE_QXL,
+ .base.description = "qemu simple display",
+ .base.major_version = SPICE_INTERFACE_QXL_MAJOR,
+ .base.minor_version = SPICE_INTERFACE_QXL_MINOR,
+
+ .attache_worker = interface_attach_worker,
+ .set_compression_level = interface_set_compression_level,
+ .set_mm_time = interface_set_mm_time,
+ .get_init_info = interface_get_init_info,
+
+ /* the callbacks below are called from spice server thread context */
+ .get_command = interface_get_command,
+ .req_cmd_notification = interface_req_cmd_notification,
+ .release_resource = interface_release_resource,
+ .get_cursor_command = interface_get_cursor_command,
+ .req_cursor_notification = interface_req_cursor_notification,
+ .notify_update = interface_notify_update,
+ .flush_resources = interface_flush_resources,
+};
+
+static SimpleSpiceDisplay sdpy;
+
+static void display_update(struct DisplayState *ds, int x, int y, int w, int h)
+{
+ qemu_spice_display_update(&sdpy, x, y, w, h);
+}
+
+static void display_resize(struct DisplayState *ds)
+{
+ qemu_spice_display_resize(&sdpy);
+}
+
+static void display_refresh(struct DisplayState *ds)
+{
+ qemu_spice_display_refresh(&sdpy);
+}
+
+static DisplayChangeListener display_listener = {
+ .dpy_update = display_update,
+ .dpy_resize = display_resize,
+ .dpy_refresh = display_refresh,
+};
+
+void qemu_spice_display_init(DisplayState *ds)
+{
+ assert(sdpy.ds == NULL);
+ sdpy.ds = ds;
+ sdpy.bufsize = (16 * 1024 * 1024);
+ sdpy.buf = qemu_malloc(sdpy.bufsize);
+ pthread_mutex_init(&sdpy.lock, NULL);
+ register_displaychangelistener(ds, &display_listener);
+
+ sdpy.qxl.base.sif = &dpy_interface.base;
+ qemu_spice_add_interface(&sdpy.qxl.base);
+ assert(sdpy.worker);
+
+ qemu_add_vm_change_state_handler(qemu_spice_vm_change_state_handler, &sdpy);
+ qemu_spice_create_host_memslot(&sdpy);
+ qemu_spice_create_host_primary(&sdpy);
+}
diff --git a/ui/spice-display.h b/ui/spice-display.h
new file mode 100644
index 0000000000..e17671c12a
--- /dev/null
+++ b/ui/spice-display.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <spice/ipc_ring.h>
+#include <spice/enums.h>
+#include <spice/qxl_dev.h>
+
+#include "pflib.h"
+
+#define NUM_MEMSLOTS 8
+#define MEMSLOT_GENERATION_BITS 8
+#define MEMSLOT_SLOT_BITS 8
+
+#define MEMSLOT_GROUP_HOST 0
+#define MEMSLOT_GROUP_GUEST 1
+#define NUM_MEMSLOTS_GROUPS 2
+
+#define NUM_SURFACES 1024
+
+typedef struct SimpleSpiceDisplay {
+ DisplayState *ds;
+ void *buf;
+ int bufsize;
+ QXLWorker *worker;
+ QXLInstance qxl;
+ uint32_t unique;
+ QemuPfConv *conv;
+
+ pthread_mutex_t lock;
+ QXLRect dirty;
+ int notify;
+ int running;
+} SimpleSpiceDisplay;
+
+typedef struct SimpleSpiceUpdate {
+ QXLDrawable drawable;
+ QXLImage image;
+ QXLCommandExt ext;
+ uint8_t *bitmap;
+} SimpleSpiceUpdate;
+
+int qemu_spice_rect_is_empty(const QXLRect* r);
+void qemu_spice_rect_union(QXLRect *dest, const QXLRect *r);
+
+SimpleSpiceUpdate *qemu_spice_create_update(SimpleSpiceDisplay *sdpy);
+void qemu_spice_destroy_update(SimpleSpiceDisplay *sdpy, SimpleSpiceUpdate *update);
+void qemu_spice_create_host_memslot(SimpleSpiceDisplay *ssd);
+void qemu_spice_create_host_primary(SimpleSpiceDisplay *ssd);
+void qemu_spice_destroy_host_primary(SimpleSpiceDisplay *ssd);
+void qemu_spice_vm_change_state_handler(void *opaque, int running, int reason);
+
+void qemu_spice_display_update(SimpleSpiceDisplay *ssd,
+ int x, int y, int w, int h);
+void qemu_spice_display_resize(SimpleSpiceDisplay *ssd);
+void qemu_spice_display_refresh(SimpleSpiceDisplay *ssd);
diff --git a/ui/spice-input.c b/ui/spice-input.c
new file mode 100644
index 0000000000..37c8578a2c
--- /dev/null
+++ b/ui/spice-input.c
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <string.h>
+
+#include <spice.h>
+#include <spice/enums.h>
+
+#include "qemu-common.h"
+#include "qemu-spice.h"
+#include "console.h"
+
+/* keyboard bits */
+
+typedef struct QemuSpiceKbd {
+ SpiceKbdInstance sin;
+ int ledstate;
+} QemuSpiceKbd;
+
+static void kbd_push_key(SpiceKbdInstance *sin, uint8_t frag);
+static uint8_t kbd_get_leds(SpiceKbdInstance *sin);
+static void kbd_leds(void *opaque, int l);
+
+static const SpiceKbdInterface kbd_interface = {
+ .base.type = SPICE_INTERFACE_KEYBOARD,
+ .base.description = "qemu keyboard",
+ .base.major_version = SPICE_INTERFACE_KEYBOARD_MAJOR,
+ .base.minor_version = SPICE_INTERFACE_KEYBOARD_MINOR,
+ .push_scan_freg = kbd_push_key,
+ .get_leds = kbd_get_leds,
+};
+
+static void kbd_push_key(SpiceKbdInstance *sin, uint8_t frag)
+{
+ kbd_put_keycode(frag);
+}
+
+static uint8_t kbd_get_leds(SpiceKbdInstance *sin)
+{
+ QemuSpiceKbd *kbd = container_of(sin, QemuSpiceKbd, sin);
+ return kbd->ledstate;
+}
+
+static void kbd_leds(void *opaque, int ledstate)
+{
+ QemuSpiceKbd *kbd = opaque;
+
+ kbd->ledstate = 0;
+ if (ledstate & QEMU_SCROLL_LOCK_LED) {
+ kbd->ledstate |= SPICE_KEYBOARD_MODIFIER_FLAGS_SCROLL_LOCK;
+ }
+ if (ledstate & QEMU_NUM_LOCK_LED) {
+ kbd->ledstate |= SPICE_KEYBOARD_MODIFIER_FLAGS_NUM_LOCK;
+ }
+ if (ledstate & QEMU_CAPS_LOCK_LED) {
+ kbd->ledstate |= SPICE_KEYBOARD_MODIFIER_FLAGS_CAPS_LOCK;
+ }
+ spice_server_kbd_leds(&kbd->sin, ledstate);
+}
+
+/* mouse bits */
+
+typedef struct QemuSpicePointer {
+ SpiceMouseInstance mouse;
+ SpiceTabletInstance tablet;
+ int width, height, x, y;
+ Notifier mouse_mode;
+ bool absolute;
+} QemuSpicePointer;
+
+static int map_buttons(int spice_buttons)
+{
+ int qemu_buttons = 0;
+
+ /*
+ * Note: SPICE_MOUSE_BUTTON_* specifies the wire protocol but this
+ * isn't what we get passed in via interface callbacks for the
+ * middle and right button ...
+ */
+ if (spice_buttons & SPICE_MOUSE_BUTTON_MASK_LEFT) {
+ qemu_buttons |= MOUSE_EVENT_LBUTTON;
+ }
+ if (spice_buttons & 0x04 /* SPICE_MOUSE_BUTTON_MASK_MIDDLE */) {
+ qemu_buttons |= MOUSE_EVENT_MBUTTON;
+ }
+ if (spice_buttons & 0x02 /* SPICE_MOUSE_BUTTON_MASK_RIGHT */) {
+ qemu_buttons |= MOUSE_EVENT_RBUTTON;
+ }
+ return qemu_buttons;
+}
+
+static void mouse_motion(SpiceMouseInstance *sin, int dx, int dy, int dz,
+ uint32_t buttons_state)
+{
+ kbd_mouse_event(dx, dy, dz, map_buttons(buttons_state));
+}
+
+static void mouse_buttons(SpiceMouseInstance *sin, uint32_t buttons_state)
+{
+ kbd_mouse_event(0, 0, 0, map_buttons(buttons_state));
+}
+
+static const SpiceMouseInterface mouse_interface = {
+ .base.type = SPICE_INTERFACE_MOUSE,
+ .base.description = "mouse",
+ .base.major_version = SPICE_INTERFACE_MOUSE_MAJOR,
+ .base.minor_version = SPICE_INTERFACE_MOUSE_MINOR,
+ .motion = mouse_motion,
+ .buttons = mouse_buttons,
+};
+
+static void tablet_set_logical_size(SpiceTabletInstance* sin, int width, int height)
+{
+ QemuSpicePointer *pointer = container_of(sin, QemuSpicePointer, tablet);
+
+ if (height < 16) {
+ height = 16;
+ }
+ if (width < 16) {
+ width = 16;
+ }
+ pointer->width = width;
+ pointer->height = height;
+}
+
+static void tablet_position(SpiceTabletInstance* sin, int x, int y,
+ uint32_t buttons_state)
+{
+ QemuSpicePointer *pointer = container_of(sin, QemuSpicePointer, tablet);
+
+ pointer->x = x * 0x7FFF / (pointer->width - 1);
+ pointer->y = y * 0x7FFF / (pointer->height - 1);
+ kbd_mouse_event(pointer->x, pointer->y, 0, map_buttons(buttons_state));
+}
+
+
+static void tablet_wheel(SpiceTabletInstance* sin, int wheel,
+ uint32_t buttons_state)
+{
+ QemuSpicePointer *pointer = container_of(sin, QemuSpicePointer, tablet);
+
+ kbd_mouse_event(pointer->x, pointer->y, wheel, map_buttons(buttons_state));
+}
+
+static void tablet_buttons(SpiceTabletInstance *sin,
+ uint32_t buttons_state)
+{
+ QemuSpicePointer *pointer = container_of(sin, QemuSpicePointer, tablet);
+
+ kbd_mouse_event(pointer->x, pointer->y, 0, map_buttons(buttons_state));
+}
+
+static const SpiceTabletInterface tablet_interface = {
+ .base.type = SPICE_INTERFACE_TABLET,
+ .base.description = "tablet",
+ .base.major_version = SPICE_INTERFACE_TABLET_MAJOR,
+ .base.minor_version = SPICE_INTERFACE_TABLET_MINOR,
+ .set_logical_size = tablet_set_logical_size,
+ .position = tablet_position,
+ .wheel = tablet_wheel,
+ .buttons = tablet_buttons,
+};
+
+static void mouse_mode_notifier(Notifier *notifier)
+{
+ QemuSpicePointer *pointer = container_of(notifier, QemuSpicePointer, mouse_mode);
+ bool is_absolute = kbd_mouse_is_absolute();
+
+ if (pointer->absolute == is_absolute) {
+ return;
+ }
+
+ if (is_absolute) {
+ qemu_spice_add_interface(&pointer->tablet.base);
+ } else {
+ spice_server_remove_interface(&pointer->tablet.base);
+ }
+ pointer->absolute = is_absolute;
+}
+
+void qemu_spice_input_init(void)
+{
+ QemuSpiceKbd *kbd;
+ QemuSpicePointer *pointer;
+
+ kbd = qemu_mallocz(sizeof(*kbd));
+ kbd->sin.base.sif = &kbd_interface.base;
+ qemu_spice_add_interface(&kbd->sin.base);
+ qemu_add_led_event_handler(kbd_leds, kbd);
+
+ pointer = qemu_mallocz(sizeof(*pointer));
+ pointer->mouse.base.sif = &mouse_interface.base;
+ pointer->tablet.base.sif = &tablet_interface.base;
+ qemu_spice_add_interface(&pointer->mouse.base);
+
+ pointer->absolute = false;
+ pointer->mouse_mode.notify = mouse_mode_notifier;
+ qemu_add_mouse_mode_change_notifier(&pointer->mouse_mode);
+ mouse_mode_notifier(&pointer->mouse_mode);
+}
diff --git a/vnc-auth-sasl.c b/ui/vnc-auth-sasl.c
index a51ddc8a18..a51ddc8a18 100644
--- a/vnc-auth-sasl.c
+++ b/ui/vnc-auth-sasl.c
diff --git a/vnc-auth-sasl.h b/ui/vnc-auth-sasl.h
index fd9b18a8fe..fd9b18a8fe 100644
--- a/vnc-auth-sasl.h
+++ b/ui/vnc-auth-sasl.h
diff --git a/vnc-auth-vencrypt.c b/ui/vnc-auth-vencrypt.c
index 07c169186a..07c169186a 100644
--- a/vnc-auth-vencrypt.c
+++ b/ui/vnc-auth-vencrypt.c
diff --git a/vnc-auth-vencrypt.h b/ui/vnc-auth-vencrypt.h
index 9f674c5173..9f674c5173 100644
--- a/vnc-auth-vencrypt.h
+++ b/ui/vnc-auth-vencrypt.h
diff --git a/vnchextile.h b/ui/vnc-enc-hextile-template.h
index b9f9f5ef89..b9f9f5ef89 100644
--- a/vnchextile.h
+++ b/ui/vnc-enc-hextile-template.h
diff --git a/vnc-encoding-hextile.c b/ui/vnc-enc-hextile.c
index 728f25e5d8..364a491157 100644
--- a/vnc-encoding-hextile.c
+++ b/ui/vnc-enc-hextile.c
@@ -33,32 +33,32 @@ static void hextile_enc_cord(uint8_t *ptr, int x, int y, int w, int h)
}
#define BPP 8
-#include "vnchextile.h"
+#include "vnc-enc-hextile-template.h"
#undef BPP
#define BPP 16
-#include "vnchextile.h"
+#include "vnc-enc-hextile-template.h"
#undef BPP
#define BPP 32
-#include "vnchextile.h"
+#include "vnc-enc-hextile-template.h"
#undef BPP
#define GENERIC
#define BPP 8
-#include "vnchextile.h"
+#include "vnc-enc-hextile-template.h"
#undef BPP
#undef GENERIC
#define GENERIC
#define BPP 16
-#include "vnchextile.h"
+#include "vnc-enc-hextile-template.h"
#undef BPP
#undef GENERIC
#define GENERIC
#define BPP 32
-#include "vnchextile.h"
+#include "vnc-enc-hextile-template.h"
#undef BPP
#undef GENERIC
@@ -75,7 +75,7 @@ int vnc_hextile_send_framebuffer_update(VncState *vs, int x,
has_fg = has_bg = 0;
for (j = y; j < (y + h); j += 16) {
for (i = x; i < (x + w); i += 16) {
- vs->send_hextile_tile(vs, i, j,
+ vs->hextile.send_tile(vs, i, j,
MIN(16, x + w - i), MIN(16, y + h - j),
last_bg, last_fg, &has_bg, &has_fg);
}
@@ -91,25 +91,25 @@ void vnc_hextile_set_pixel_conversion(VncState *vs, int generic)
if (!generic) {
switch (vs->ds->surface->pf.bits_per_pixel) {
case 8:
- vs->send_hextile_tile = send_hextile_tile_8;
+ vs->hextile.send_tile = send_hextile_tile_8;
break;
case 16:
- vs->send_hextile_tile = send_hextile_tile_16;
+ vs->hextile.send_tile = send_hextile_tile_16;
break;
case 32:
- vs->send_hextile_tile = send_hextile_tile_32;
+ vs->hextile.send_tile = send_hextile_tile_32;
break;
}
} else {
switch (vs->ds->surface->pf.bits_per_pixel) {
case 8:
- vs->send_hextile_tile = send_hextile_tile_generic_8;
+ vs->hextile.send_tile = send_hextile_tile_generic_8;
break;
case 16:
- vs->send_hextile_tile = send_hextile_tile_generic_16;
+ vs->hextile.send_tile = send_hextile_tile_generic_16;
break;
case 32:
- vs->send_hextile_tile = send_hextile_tile_generic_32;
+ vs->hextile.send_tile = send_hextile_tile_generic_32;
break;
}
}
diff --git a/ui/vnc-enc-tight.c b/ui/vnc-enc-tight.c
new file mode 100644
index 0000000000..af45edd872
--- /dev/null
+++ b/ui/vnc-enc-tight.c
@@ -0,0 +1,1717 @@
+/*
+ * QEMU VNC display driver: tight encoding
+ *
+ * From libvncserver/libvncserver/tight.c
+ * Copyright (C) 2000, 2001 Const Kaplinsky. All Rights Reserved.
+ * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
+ *
+ * Copyright (C) 2010 Corentin Chary <corentin.chary@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config-host.h"
+
+#ifdef CONFIG_VNC_PNG
+#include <png.h>
+#endif
+#ifdef CONFIG_VNC_JPEG
+#include <stdio.h>
+#include <jpeglib.h>
+#endif
+
+#include "qemu-common.h"
+
+#include "bswap.h"
+#include "qint.h"
+#include "vnc.h"
+#include "vnc-enc-tight.h"
+#include "vnc-palette.h"
+
+/* Compression level stuff. The following array contains various
+ encoder parameters for each of 10 compression levels (0..9).
+ Last three parameters correspond to JPEG quality levels (0..9). */
+
+static const struct {
+ int max_rect_size, max_rect_width;
+ int mono_min_rect_size, gradient_min_rect_size;
+ int idx_zlib_level, mono_zlib_level, raw_zlib_level, gradient_zlib_level;
+ int gradient_threshold, gradient_threshold24;
+ int idx_max_colors_divisor;
+ int jpeg_quality, jpeg_threshold, jpeg_threshold24;
+} tight_conf[] = {
+ { 512, 32, 6, 65536, 0, 0, 0, 0, 0, 0, 4, 5, 10000, 23000 },
+ { 2048, 128, 6, 65536, 1, 1, 1, 0, 0, 0, 8, 10, 8000, 18000 },
+ { 6144, 256, 8, 65536, 3, 3, 2, 0, 0, 0, 24, 15, 6500, 15000 },
+ { 10240, 1024, 12, 65536, 5, 5, 3, 0, 0, 0, 32, 25, 5000, 12000 },
+ { 16384, 2048, 12, 65536, 6, 6, 4, 0, 0, 0, 32, 37, 4000, 10000 },
+ { 32768, 2048, 12, 4096, 7, 7, 5, 4, 150, 380, 32, 50, 3000, 8000 },
+ { 65536, 2048, 16, 4096, 7, 7, 6, 4, 170, 420, 48, 60, 2000, 5000 },
+ { 65536, 2048, 16, 4096, 8, 8, 7, 5, 180, 450, 64, 70, 1000, 2500 },
+ { 65536, 2048, 32, 8192, 9, 9, 8, 6, 190, 475, 64, 75, 500, 1200 },
+ { 65536, 2048, 32, 8192, 9, 9, 9, 6, 200, 500, 96, 80, 200, 500 }
+};
+
+
+static int tight_send_framebuffer_update(VncState *vs, int x, int y,
+ int w, int h);
+
+#ifdef CONFIG_VNC_PNG
+static const struct {
+ int png_zlib_level, png_filters;
+} tight_png_conf[] = {
+ { 0, PNG_NO_FILTERS },
+ { 1, PNG_NO_FILTERS },
+ { 2, PNG_NO_FILTERS },
+ { 3, PNG_NO_FILTERS },
+ { 4, PNG_NO_FILTERS },
+ { 5, PNG_ALL_FILTERS },
+ { 6, PNG_ALL_FILTERS },
+ { 7, PNG_ALL_FILTERS },
+ { 8, PNG_ALL_FILTERS },
+ { 9, PNG_ALL_FILTERS },
+};
+
+static int send_png_rect(VncState *vs, int x, int y, int w, int h,
+ VncPalette *palette);
+
+static bool tight_can_send_png_rect(VncState *vs, int w, int h)
+{
+ if (vs->tight.type != VNC_ENCODING_TIGHT_PNG) {
+ return false;
+ }
+
+ if (ds_get_bytes_per_pixel(vs->ds) == 1 ||
+ vs->clientds.pf.bytes_per_pixel == 1) {
+ return false;
+ }
+
+ return true;
+}
+#endif
+
+/*
+ * Code to guess if given rectangle is suitable for smooth image
+ * compression (by applying "gradient" filter or JPEG coder).
+ */
+
+static unsigned int
+tight_detect_smooth_image24(VncState *vs, int w, int h)
+{
+ int off;
+ int x, y, d, dx;
+ unsigned int c;
+ unsigned int stats[256];
+ int pixels = 0;
+ int pix, left[3];
+ unsigned int errors;
+ unsigned char *buf = vs->tight.tight.buffer;
+
+ /*
+ * If client is big-endian, color samples begin from the second
+ * byte (offset 1) of a 32-bit pixel value.
+ */
+ off = !!(vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG);
+
+ memset(stats, 0, sizeof (stats));
+
+ for (y = 0, x = 0; y < h && x < w;) {
+ for (d = 0; d < h - y && d < w - x - VNC_TIGHT_DETECT_SUBROW_WIDTH;
+ d++) {
+ for (c = 0; c < 3; c++) {
+ left[c] = buf[((y+d)*w+x+d)*4+off+c] & 0xFF;
+ }
+ for (dx = 1; dx <= VNC_TIGHT_DETECT_SUBROW_WIDTH; dx++) {
+ for (c = 0; c < 3; c++) {
+ pix = buf[((y+d)*w+x+d+dx)*4+off+c] & 0xFF;
+ stats[abs(pix - left[c])]++;
+ left[c] = pix;
+ }
+ pixels++;
+ }
+ }
+ if (w > h) {
+ x += h;
+ y = 0;
+ } else {
+ x = 0;
+ y += w;
+ }
+ }
+
+ /* 95% smooth or more ... */
+ if (stats[0] * 33 / pixels >= 95) {
+ return 0;
+ }
+
+ errors = 0;
+ for (c = 1; c < 8; c++) {
+ errors += stats[c] * (c * c);
+ if (stats[c] == 0 || stats[c] > stats[c-1] * 2) {
+ return 0;
+ }
+ }
+ for (; c < 256; c++) {
+ errors += stats[c] * (c * c);
+ }
+ errors /= (pixels * 3 - stats[0]);
+
+ return errors;
+}
+
+#define DEFINE_DETECT_FUNCTION(bpp) \
+ \
+ static unsigned int \
+ tight_detect_smooth_image##bpp(VncState *vs, int w, int h) { \
+ bool endian; \
+ uint##bpp##_t pix; \
+ int max[3], shift[3]; \
+ int x, y, d, dx; \
+ unsigned int c; \
+ unsigned int stats[256]; \
+ int pixels = 0; \
+ int sample, sum, left[3]; \
+ unsigned int errors; \
+ unsigned char *buf = vs->tight.tight.buffer; \
+ \
+ endian = ((vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG) != \
+ (vs->ds->surface->flags & QEMU_BIG_ENDIAN_FLAG)); \
+ \
+ \
+ max[0] = vs->clientds.pf.rmax; \
+ max[1] = vs->clientds.pf.gmax; \
+ max[2] = vs->clientds.pf.bmax; \
+ shift[0] = vs->clientds.pf.rshift; \
+ shift[1] = vs->clientds.pf.gshift; \
+ shift[2] = vs->clientds.pf.bshift; \
+ \
+ memset(stats, 0, sizeof(stats)); \
+ \
+ y = 0, x = 0; \
+ while (y < h && x < w) { \
+ for (d = 0; d < h - y && \
+ d < w - x - VNC_TIGHT_DETECT_SUBROW_WIDTH; d++) { \
+ pix = ((uint##bpp##_t *)buf)[(y+d)*w+x+d]; \
+ if (endian) { \
+ pix = bswap##bpp(pix); \
+ } \
+ for (c = 0; c < 3; c++) { \
+ left[c] = (int)(pix >> shift[c] & max[c]); \
+ } \
+ for (dx = 1; dx <= VNC_TIGHT_DETECT_SUBROW_WIDTH; \
+ dx++) { \
+ pix = ((uint##bpp##_t *)buf)[(y+d)*w+x+d+dx]; \
+ if (endian) { \
+ pix = bswap##bpp(pix); \
+ } \
+ sum = 0; \
+ for (c = 0; c < 3; c++) { \
+ sample = (int)(pix >> shift[c] & max[c]); \
+ sum += abs(sample - left[c]); \
+ left[c] = sample; \
+ } \
+ if (sum > 255) { \
+ sum = 255; \
+ } \
+ stats[sum]++; \
+ pixels++; \
+ } \
+ } \
+ if (w > h) { \
+ x += h; \
+ y = 0; \
+ } else { \
+ x = 0; \
+ y += w; \
+ } \
+ } \
+ \
+ if ((stats[0] + stats[1]) * 100 / pixels >= 90) { \
+ return 0; \
+ } \
+ \
+ errors = 0; \
+ for (c = 1; c < 8; c++) { \
+ errors += stats[c] * (c * c); \
+ if (stats[c] == 0 || stats[c] > stats[c-1] * 2) { \
+ return 0; \
+ } \
+ } \
+ for (; c < 256; c++) { \
+ errors += stats[c] * (c * c); \
+ } \
+ errors /= (pixels - stats[0]); \
+ \
+ return errors; \
+ }
+
+DEFINE_DETECT_FUNCTION(16)
+DEFINE_DETECT_FUNCTION(32)
+
+static int
+tight_detect_smooth_image(VncState *vs, int w, int h)
+{
+ unsigned int errors;
+ int compression = vs->tight.compression;
+ int quality = vs->tight.quality;
+
+ if (!vs->vd->lossy) {
+ return 0;
+ }
+
+ if (ds_get_bytes_per_pixel(vs->ds) == 1 ||
+ vs->clientds.pf.bytes_per_pixel == 1 ||
+ w < VNC_TIGHT_DETECT_MIN_WIDTH || h < VNC_TIGHT_DETECT_MIN_HEIGHT) {
+ return 0;
+ }
+
+ if (vs->tight.quality != (uint8_t)-1) {
+ if (w * h < VNC_TIGHT_JPEG_MIN_RECT_SIZE) {
+ return 0;
+ }
+ } else {
+ if (w * h < tight_conf[compression].gradient_min_rect_size) {
+ return 0;
+ }
+ }
+
+ if (vs->clientds.pf.bytes_per_pixel == 4) {
+ if (vs->tight.pixel24) {
+ errors = tight_detect_smooth_image24(vs, w, h);
+ if (vs->tight.quality != (uint8_t)-1) {
+ return (errors < tight_conf[quality].jpeg_threshold24);
+ }
+ return (errors < tight_conf[compression].gradient_threshold24);
+ } else {
+ errors = tight_detect_smooth_image32(vs, w, h);
+ }
+ } else {
+ errors = tight_detect_smooth_image16(vs, w, h);
+ }
+ if (quality != -1) {
+ return (errors < tight_conf[quality].jpeg_threshold);
+ }
+ return (errors < tight_conf[compression].gradient_threshold);
+}
+
+/*
+ * Code to determine how many different colors used in rectangle.
+ */
+#define DEFINE_FILL_PALETTE_FUNCTION(bpp) \
+ \
+ static int \
+ tight_fill_palette##bpp(VncState *vs, int x, int y, \
+ int max, size_t count, \
+ uint32_t *bg, uint32_t *fg, \
+ VncPalette **palette) { \
+ uint##bpp##_t *data; \
+ uint##bpp##_t c0, c1, ci; \
+ int i, n0, n1; \
+ \
+ data = (uint##bpp##_t *)vs->tight.tight.buffer; \
+ \
+ c0 = data[0]; \
+ i = 1; \
+ while (i < count && data[i] == c0) \
+ i++; \
+ if (i >= count) { \
+ *bg = *fg = c0; \
+ return 1; \
+ } \
+ \
+ if (max < 2) { \
+ return 0; \
+ } \
+ \
+ n0 = i; \
+ c1 = data[i]; \
+ n1 = 0; \
+ for (i++; i < count; i++) { \
+ ci = data[i]; \
+ if (ci == c0) { \
+ n0++; \
+ } else if (ci == c1) { \
+ n1++; \
+ } else \
+ break; \
+ } \
+ if (i >= count) { \
+ if (n0 > n1) { \
+ *bg = (uint32_t)c0; \
+ *fg = (uint32_t)c1; \
+ } else { \
+ *bg = (uint32_t)c1; \
+ *fg = (uint32_t)c0; \
+ } \
+ return 2; \
+ } \
+ \
+ if (max == 2) { \
+ return 0; \
+ } \
+ \
+ *palette = palette_new(max, bpp); \
+ palette_put(*palette, c0); \
+ palette_put(*palette, c1); \
+ palette_put(*palette, ci); \
+ \
+ for (i++; i < count; i++) { \
+ if (data[i] == ci) { \
+ continue; \
+ } else { \
+ ci = data[i]; \
+ if (!palette_put(*palette, (uint32_t)ci)) { \
+ return 0; \
+ } \
+ } \
+ } \
+ \
+ return palette_size(*palette); \
+ }
+
+DEFINE_FILL_PALETTE_FUNCTION(8)
+DEFINE_FILL_PALETTE_FUNCTION(16)
+DEFINE_FILL_PALETTE_FUNCTION(32)
+
+static int tight_fill_palette(VncState *vs, int x, int y,
+ size_t count, uint32_t *bg, uint32_t *fg,
+ VncPalette **palette)
+{
+ int max;
+
+ max = count / tight_conf[vs->tight.compression].idx_max_colors_divisor;
+ if (max < 2 &&
+ count >= tight_conf[vs->tight.compression].mono_min_rect_size) {
+ max = 2;
+ }
+ if (max >= 256) {
+ max = 256;
+ }
+
+ switch(vs->clientds.pf.bytes_per_pixel) {
+ case 4:
+ return tight_fill_palette32(vs, x, y, max, count, bg, fg, palette);
+ case 2:
+ return tight_fill_palette16(vs, x, y, max, count, bg, fg, palette);
+ default:
+ max = 2;
+ return tight_fill_palette8(vs, x, y, max, count, bg, fg, palette);
+ }
+ return 0;
+}
+
+/*
+ * Converting truecolor samples into palette indices.
+ */
+#define DEFINE_IDX_ENCODE_FUNCTION(bpp) \
+ \
+ static void \
+ tight_encode_indexed_rect##bpp(uint8_t *buf, int count, \
+ VncPalette *palette) { \
+ uint##bpp##_t *src; \
+ uint##bpp##_t rgb; \
+ int i, rep; \
+ uint8_t idx; \
+ \
+ src = (uint##bpp##_t *) buf; \
+ \
+ for (i = 0; i < count; i++) { \
+ \
+ rgb = *src++; \
+ rep = 0; \
+ while (i < count && *src == rgb) { \
+ rep++, src++, i++; \
+ } \
+ idx = palette_idx(palette, rgb); \
+ /* \
+ * Should never happen, but don't break everything \
+ * if it does, use the first color instead \
+ */ \
+ if (idx == (uint8_t)-1) { \
+ idx = 0; \
+ } \
+ while (rep >= 0) { \
+ *buf++ = idx; \
+ rep--; \
+ } \
+ } \
+ }
+
+DEFINE_IDX_ENCODE_FUNCTION(16)
+DEFINE_IDX_ENCODE_FUNCTION(32)
+
+#define DEFINE_MONO_ENCODE_FUNCTION(bpp) \
+ \
+ static void \
+ tight_encode_mono_rect##bpp(uint8_t *buf, int w, int h, \
+ uint##bpp##_t bg, uint##bpp##_t fg) { \
+ uint##bpp##_t *ptr; \
+ unsigned int value, mask; \
+ int aligned_width; \
+ int x, y, bg_bits; \
+ \
+ ptr = (uint##bpp##_t *) buf; \
+ aligned_width = w - w % 8; \
+ \
+ for (y = 0; y < h; y++) { \
+ for (x = 0; x < aligned_width; x += 8) { \
+ for (bg_bits = 0; bg_bits < 8; bg_bits++) { \
+ if (*ptr++ != bg) { \
+ break; \
+ } \
+ } \
+ if (bg_bits == 8) { \
+ *buf++ = 0; \
+ continue; \
+ } \
+ mask = 0x80 >> bg_bits; \
+ value = mask; \
+ for (bg_bits++; bg_bits < 8; bg_bits++) { \
+ mask >>= 1; \
+ if (*ptr++ != bg) { \
+ value |= mask; \
+ } \
+ } \
+ *buf++ = (uint8_t)value; \
+ } \
+ \
+ mask = 0x80; \
+ value = 0; \
+ if (x >= w) { \
+ continue; \
+ } \
+ \
+ for (; x < w; x++) { \
+ if (*ptr++ != bg) { \
+ value |= mask; \
+ } \
+ mask >>= 1; \
+ } \
+ *buf++ = (uint8_t)value; \
+ } \
+ }
+
+DEFINE_MONO_ENCODE_FUNCTION(8)
+DEFINE_MONO_ENCODE_FUNCTION(16)
+DEFINE_MONO_ENCODE_FUNCTION(32)
+
+/*
+ * ``Gradient'' filter for 24-bit color samples.
+ * Should be called only when redMax, greenMax and blueMax are 255.
+ * Color components assumed to be byte-aligned.
+ */
+
+static void
+tight_filter_gradient24(VncState *vs, uint8_t *buf, int w, int h)
+{
+ uint32_t *buf32;
+ uint32_t pix32;
+ int shift[3];
+ int *prev;
+ int here[3], upper[3], left[3], upperleft[3];
+ int prediction;
+ int x, y, c;
+
+ buf32 = (uint32_t *)buf;
+ memset(vs->tight.gradient.buffer, 0, w * 3 * sizeof(int));
+
+ if ((vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG) ==
+ (vs->ds->surface->flags & QEMU_BIG_ENDIAN_FLAG)) {
+ shift[0] = vs->clientds.pf.rshift;
+ shift[1] = vs->clientds.pf.gshift;
+ shift[2] = vs->clientds.pf.bshift;
+ } else {
+ shift[0] = 24 - vs->clientds.pf.rshift;
+ shift[1] = 24 - vs->clientds.pf.gshift;
+ shift[2] = 24 - vs->clientds.pf.bshift;
+ }
+
+ for (y = 0; y < h; y++) {
+ for (c = 0; c < 3; c++) {
+ upper[c] = 0;
+ here[c] = 0;
+ }
+ prev = (int *)vs->tight.gradient.buffer;
+ for (x = 0; x < w; x++) {
+ pix32 = *buf32++;
+ for (c = 0; c < 3; c++) {
+ upperleft[c] = upper[c];
+ left[c] = here[c];
+ upper[c] = *prev;
+ here[c] = (int)(pix32 >> shift[c] & 0xFF);
+ *prev++ = here[c];
+
+ prediction = left[c] + upper[c] - upperleft[c];
+ if (prediction < 0) {
+ prediction = 0;
+ } else if (prediction > 0xFF) {
+ prediction = 0xFF;
+ }
+ *buf++ = (char)(here[c] - prediction);
+ }
+ }
+ }
+}
+
+
+/*
+ * ``Gradient'' filter for other color depths.
+ */
+
+#define DEFINE_GRADIENT_FILTER_FUNCTION(bpp) \
+ \
+ static void \
+ tight_filter_gradient##bpp(VncState *vs, uint##bpp##_t *buf, \
+ int w, int h) { \
+ uint##bpp##_t pix, diff; \
+ bool endian; \
+ int *prev; \
+ int max[3], shift[3]; \
+ int here[3], upper[3], left[3], upperleft[3]; \
+ int prediction; \
+ int x, y, c; \
+ \
+ memset (vs->tight.gradient.buffer, 0, w * 3 * sizeof(int)); \
+ \
+ endian = ((vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG) != \
+ (vs->ds->surface->flags & QEMU_BIG_ENDIAN_FLAG)); \
+ \
+ max[0] = vs->clientds.pf.rmax; \
+ max[1] = vs->clientds.pf.gmax; \
+ max[2] = vs->clientds.pf.bmax; \
+ shift[0] = vs->clientds.pf.rshift; \
+ shift[1] = vs->clientds.pf.gshift; \
+ shift[2] = vs->clientds.pf.bshift; \
+ \
+ for (y = 0; y < h; y++) { \
+ for (c = 0; c < 3; c++) { \
+ upper[c] = 0; \
+ here[c] = 0; \
+ } \
+ prev = (int *)vs->tight.gradient.buffer; \
+ for (x = 0; x < w; x++) { \
+ pix = *buf; \
+ if (endian) { \
+ pix = bswap##bpp(pix); \
+ } \
+ diff = 0; \
+ for (c = 0; c < 3; c++) { \
+ upperleft[c] = upper[c]; \
+ left[c] = here[c]; \
+ upper[c] = *prev; \
+ here[c] = (int)(pix >> shift[c] & max[c]); \
+ *prev++ = here[c]; \
+ \
+ prediction = left[c] + upper[c] - upperleft[c]; \
+ if (prediction < 0) { \
+ prediction = 0; \
+ } else if (prediction > max[c]) { \
+ prediction = max[c]; \
+ } \
+ diff |= ((here[c] - prediction) & max[c]) \
+ << shift[c]; \
+ } \
+ if (endian) { \
+ diff = bswap##bpp(diff); \
+ } \
+ *buf++ = diff; \
+ } \
+ } \
+ }
+
+DEFINE_GRADIENT_FILTER_FUNCTION(16)
+DEFINE_GRADIENT_FILTER_FUNCTION(32)
+
+/*
+ * Check if a rectangle is all of the same color. If needSameColor is
+ * set to non-zero, then also check that its color equals to the
+ * *colorPtr value. The result is 1 if the test is successful, and in
+ * that case new color will be stored in *colorPtr.
+ */
+
+#define DEFINE_CHECK_SOLID_FUNCTION(bpp) \
+ \
+ static bool \
+ check_solid_tile##bpp(VncState *vs, int x, int y, int w, int h, \
+ uint32_t* color, bool samecolor) \
+ { \
+ VncDisplay *vd = vs->vd; \
+ uint##bpp##_t *fbptr; \
+ uint##bpp##_t c; \
+ int dx, dy; \
+ \
+ fbptr = (uint##bpp##_t *) \
+ (vd->server->data + y * ds_get_linesize(vs->ds) + \
+ x * ds_get_bytes_per_pixel(vs->ds)); \
+ \
+ c = *fbptr; \
+ if (samecolor && (uint32_t)c != *color) { \
+ return false; \
+ } \
+ \
+ for (dy = 0; dy < h; dy++) { \
+ for (dx = 0; dx < w; dx++) { \
+ if (c != fbptr[dx]) { \
+ return false; \
+ } \
+ } \
+ fbptr = (uint##bpp##_t *) \
+ ((uint8_t *)fbptr + ds_get_linesize(vs->ds)); \
+ } \
+ \
+ *color = (uint32_t)c; \
+ return true; \
+ }
+
+DEFINE_CHECK_SOLID_FUNCTION(32)
+DEFINE_CHECK_SOLID_FUNCTION(16)
+DEFINE_CHECK_SOLID_FUNCTION(8)
+
+static bool check_solid_tile(VncState *vs, int x, int y, int w, int h,
+ uint32_t* color, bool samecolor)
+{
+ VncDisplay *vd = vs->vd;
+
+ switch(vd->server->pf.bytes_per_pixel) {
+ case 4:
+ return check_solid_tile32(vs, x, y, w, h, color, samecolor);
+ case 2:
+ return check_solid_tile16(vs, x, y, w, h, color, samecolor);
+ default:
+ return check_solid_tile8(vs, x, y, w, h, color, samecolor);
+ }
+}
+
+static void find_best_solid_area(VncState *vs, int x, int y, int w, int h,
+ uint32_t color, int *w_ptr, int *h_ptr)
+{
+ int dx, dy, dw, dh;
+ int w_prev;
+ int w_best = 0, h_best = 0;
+
+ w_prev = w;
+
+ for (dy = y; dy < y + h; dy += VNC_TIGHT_MAX_SPLIT_TILE_SIZE) {
+
+ dh = MIN(VNC_TIGHT_MAX_SPLIT_TILE_SIZE, y + h - dy);
+ dw = MIN(VNC_TIGHT_MAX_SPLIT_TILE_SIZE, w_prev);
+
+ if (!check_solid_tile(vs, x, dy, dw, dh, &color, true)) {
+ break;
+ }
+
+ for (dx = x + dw; dx < x + w_prev;) {
+ dw = MIN(VNC_TIGHT_MAX_SPLIT_TILE_SIZE, x + w_prev - dx);
+
+ if (!check_solid_tile(vs, dx, dy, dw, dh, &color, true)) {
+ break;
+ }
+ dx += dw;
+ }
+
+ w_prev = dx - x;
+ if (w_prev * (dy + dh - y) > w_best * h_best) {
+ w_best = w_prev;
+ h_best = dy + dh - y;
+ }
+ }
+
+ *w_ptr = w_best;
+ *h_ptr = h_best;
+}
+
+static void extend_solid_area(VncState *vs, int x, int y, int w, int h,
+ uint32_t color, int *x_ptr, int *y_ptr,
+ int *w_ptr, int *h_ptr)
+{
+ int cx, cy;
+
+ /* Try to extend the area upwards. */
+ for ( cy = *y_ptr - 1;
+ cy >= y && check_solid_tile(vs, *x_ptr, cy, *w_ptr, 1, &color, true);
+ cy-- );
+ *h_ptr += *y_ptr - (cy + 1);
+ *y_ptr = cy + 1;
+
+ /* ... downwards. */
+ for ( cy = *y_ptr + *h_ptr;
+ cy < y + h &&
+ check_solid_tile(vs, *x_ptr, cy, *w_ptr, 1, &color, true);
+ cy++ );
+ *h_ptr += cy - (*y_ptr + *h_ptr);
+
+ /* ... to the left. */
+ for ( cx = *x_ptr - 1;
+ cx >= x && check_solid_tile(vs, cx, *y_ptr, 1, *h_ptr, &color, true);
+ cx-- );
+ *w_ptr += *x_ptr - (cx + 1);
+ *x_ptr = cx + 1;
+
+ /* ... to the right. */
+ for ( cx = *x_ptr + *w_ptr;
+ cx < x + w &&
+ check_solid_tile(vs, cx, *y_ptr, 1, *h_ptr, &color, true);
+ cx++ );
+ *w_ptr += cx - (*x_ptr + *w_ptr);
+}
+
+static int tight_init_stream(VncState *vs, int stream_id,
+ int level, int strategy)
+{
+ z_streamp zstream = &vs->tight.stream[stream_id];
+
+ if (zstream->opaque == NULL) {
+ int err;
+
+ VNC_DEBUG("VNC: TIGHT: initializing zlib stream %d\n", stream_id);
+ VNC_DEBUG("VNC: TIGHT: opaque = %p | vs = %p\n", zstream->opaque, vs);
+ zstream->zalloc = vnc_zlib_zalloc;
+ zstream->zfree = vnc_zlib_zfree;
+
+ err = deflateInit2(zstream, level, Z_DEFLATED, MAX_WBITS,
+ MAX_MEM_LEVEL, strategy);
+
+ if (err != Z_OK) {
+ fprintf(stderr, "VNC: error initializing zlib\n");
+ return -1;
+ }
+
+ vs->tight.levels[stream_id] = level;
+ zstream->opaque = vs;
+ }
+
+ if (vs->tight.levels[stream_id] != level) {
+ if (deflateParams(zstream, level, strategy) != Z_OK) {
+ return -1;
+ }
+ vs->tight.levels[stream_id] = level;
+ }
+ return 0;
+}
+
+static void tight_send_compact_size(VncState *vs, size_t len)
+{
+ int lpc = 0;
+ int bytes = 0;
+ char buf[3] = {0, 0, 0};
+
+ buf[bytes++] = len & 0x7F;
+ if (len > 0x7F) {
+ buf[bytes-1] |= 0x80;
+ buf[bytes++] = (len >> 7) & 0x7F;
+ if (len > 0x3FFF) {
+ buf[bytes-1] |= 0x80;
+ buf[bytes++] = (len >> 14) & 0xFF;
+ }
+ }
+ for (lpc = 0; lpc < bytes; lpc++) {
+ vnc_write_u8(vs, buf[lpc]);
+ }
+}
+
+static int tight_compress_data(VncState *vs, int stream_id, size_t bytes,
+ int level, int strategy)
+{
+ z_streamp zstream = &vs->tight.stream[stream_id];
+ int previous_out;
+
+ if (bytes < VNC_TIGHT_MIN_TO_COMPRESS) {
+ vnc_write(vs, vs->tight.tight.buffer, vs->tight.tight.offset);
+ return bytes;
+ }
+
+ if (tight_init_stream(vs, stream_id, level, strategy)) {
+ return -1;
+ }
+
+ /* reserve memory in output buffer */
+ buffer_reserve(&vs->tight.zlib, bytes + 64);
+
+ /* set pointers */
+ zstream->next_in = vs->tight.tight.buffer;
+ zstream->avail_in = vs->tight.tight.offset;
+ zstream->next_out = vs->tight.zlib.buffer + vs->tight.zlib.offset;
+ zstream->avail_out = vs->tight.zlib.capacity - vs->tight.zlib.offset;
+ zstream->data_type = Z_BINARY;
+ previous_out = zstream->total_out;
+
+ /* start encoding */
+ if (deflate(zstream, Z_SYNC_FLUSH) != Z_OK) {
+ fprintf(stderr, "VNC: error during tight compression\n");
+ return -1;
+ }
+
+ vs->tight.zlib.offset = vs->tight.zlib.capacity - zstream->avail_out;
+ bytes = zstream->total_out - previous_out;
+
+ tight_send_compact_size(vs, bytes);
+ vnc_write(vs, vs->tight.zlib.buffer, bytes);
+
+ buffer_reset(&vs->tight.zlib);
+
+ return bytes;
+}
+
+/*
+ * Subencoding implementations.
+ */
+static void tight_pack24(VncState *vs, uint8_t *buf, size_t count, size_t *ret)
+{
+ uint32_t *buf32;
+ uint32_t pix;
+ int rshift, gshift, bshift;
+
+ buf32 = (uint32_t *)buf;
+
+ if ((vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG) ==
+ (vs->ds->surface->flags & QEMU_BIG_ENDIAN_FLAG)) {
+ rshift = vs->clientds.pf.rshift;
+ gshift = vs->clientds.pf.gshift;
+ bshift = vs->clientds.pf.bshift;
+ } else {
+ rshift = 24 - vs->clientds.pf.rshift;
+ gshift = 24 - vs->clientds.pf.gshift;
+ bshift = 24 - vs->clientds.pf.bshift;
+ }
+
+ if (ret) {
+ *ret = count * 3;
+ }
+
+ while (count--) {
+ pix = *buf32++;
+ *buf++ = (char)(pix >> rshift);
+ *buf++ = (char)(pix >> gshift);
+ *buf++ = (char)(pix >> bshift);
+ }
+}
+
+static int send_full_color_rect(VncState *vs, int x, int y, int w, int h)
+{
+ int stream = 0;
+ ssize_t bytes;
+
+#ifdef CONFIG_VNC_PNG
+ if (tight_can_send_png_rect(vs, w, h)) {
+ return send_png_rect(vs, x, y, w, h, NULL);
+ }
+#endif
+
+ vnc_write_u8(vs, stream << 4); /* no flushing, no filter */
+
+ if (vs->tight.pixel24) {
+ tight_pack24(vs, vs->tight.tight.buffer, w * h, &vs->tight.tight.offset);
+ bytes = 3;
+ } else {
+ bytes = vs->clientds.pf.bytes_per_pixel;
+ }
+
+ bytes = tight_compress_data(vs, stream, w * h * bytes,
+ tight_conf[vs->tight.compression].raw_zlib_level,
+ Z_DEFAULT_STRATEGY);
+
+ return (bytes >= 0);
+}
+
+static int send_solid_rect(VncState *vs)
+{
+ size_t bytes;
+
+ vnc_write_u8(vs, VNC_TIGHT_FILL << 4); /* no flushing, no filter */
+
+ if (vs->tight.pixel24) {
+ tight_pack24(vs, vs->tight.tight.buffer, 1, &vs->tight.tight.offset);
+ bytes = 3;
+ } else {
+ bytes = vs->clientds.pf.bytes_per_pixel;
+ }
+
+ vnc_write(vs, vs->tight.tight.buffer, bytes);
+ return 1;
+}
+
+static int send_mono_rect(VncState *vs, int x, int y,
+ int w, int h, uint32_t bg, uint32_t fg)
+{
+ ssize_t bytes;
+ int stream = 1;
+ int level = tight_conf[vs->tight.compression].mono_zlib_level;
+
+#ifdef CONFIG_VNC_PNG
+ if (tight_can_send_png_rect(vs, w, h)) {
+ int ret;
+ int bpp = vs->clientds.pf.bytes_per_pixel * 8;
+ VncPalette *palette = palette_new(2, bpp);
+
+ palette_put(palette, bg);
+ palette_put(palette, fg);
+ ret = send_png_rect(vs, x, y, w, h, palette);
+ palette_destroy(palette);
+ return ret;
+ }
+#endif
+
+ bytes = ((w + 7) / 8) * h;
+
+ vnc_write_u8(vs, (stream | VNC_TIGHT_EXPLICIT_FILTER) << 4);
+ vnc_write_u8(vs, VNC_TIGHT_FILTER_PALETTE);
+ vnc_write_u8(vs, 1);
+
+ switch(vs->clientds.pf.bytes_per_pixel) {
+ case 4:
+ {
+ uint32_t buf[2] = {bg, fg};
+ size_t ret = sizeof (buf);
+
+ if (vs->tight.pixel24) {
+ tight_pack24(vs, (unsigned char*)buf, 2, &ret);
+ }
+ vnc_write(vs, buf, ret);
+
+ tight_encode_mono_rect32(vs->tight.tight.buffer, w, h, bg, fg);
+ break;
+ }
+ case 2:
+ vnc_write(vs, &bg, 2);
+ vnc_write(vs, &fg, 2);
+ tight_encode_mono_rect16(vs->tight.tight.buffer, w, h, bg, fg);
+ break;
+ default:
+ vnc_write_u8(vs, bg);
+ vnc_write_u8(vs, fg);
+ tight_encode_mono_rect8(vs->tight.tight.buffer, w, h, bg, fg);
+ break;
+ }
+ vs->tight.tight.offset = bytes;
+
+ bytes = tight_compress_data(vs, stream, bytes, level, Z_DEFAULT_STRATEGY);
+ return (bytes >= 0);
+}
+
+struct palette_cb_priv {
+ VncState *vs;
+ uint8_t *header;
+#ifdef CONFIG_VNC_PNG
+ png_colorp png_palette;
+#endif
+};
+
+static void write_palette(int idx, uint32_t color, void *opaque)
+{
+ struct palette_cb_priv *priv = opaque;
+ VncState *vs = priv->vs;
+ uint32_t bytes = vs->clientds.pf.bytes_per_pixel;
+
+ if (bytes == 4) {
+ ((uint32_t*)priv->header)[idx] = color;
+ } else {
+ ((uint16_t*)priv->header)[idx] = color;
+ }
+}
+
+static bool send_gradient_rect(VncState *vs, int x, int y, int w, int h)
+{
+ int stream = 3;
+ int level = tight_conf[vs->tight.compression].gradient_zlib_level;
+ ssize_t bytes;
+
+ if (vs->clientds.pf.bytes_per_pixel == 1)
+ return send_full_color_rect(vs, x, y, w, h);
+
+ vnc_write_u8(vs, (stream | VNC_TIGHT_EXPLICIT_FILTER) << 4);
+ vnc_write_u8(vs, VNC_TIGHT_FILTER_GRADIENT);
+
+ buffer_reserve(&vs->tight.gradient, w * 3 * sizeof (int));
+
+ if (vs->tight.pixel24) {
+ tight_filter_gradient24(vs, vs->tight.tight.buffer, w, h);
+ bytes = 3;
+ } else if (vs->clientds.pf.bytes_per_pixel == 4) {
+ tight_filter_gradient32(vs, (uint32_t *)vs->tight.tight.buffer, w, h);
+ bytes = 4;
+ } else {
+ tight_filter_gradient16(vs, (uint16_t *)vs->tight.tight.buffer, w, h);
+ bytes = 2;
+ }
+
+ buffer_reset(&vs->tight.gradient);
+
+ bytes = w * h * bytes;
+ vs->tight.tight.offset = bytes;
+
+ bytes = tight_compress_data(vs, stream, bytes,
+ level, Z_FILTERED);
+ return (bytes >= 0);
+}
+
+static int send_palette_rect(VncState *vs, int x, int y,
+ int w, int h, VncPalette *palette)
+{
+ int stream = 2;
+ int level = tight_conf[vs->tight.compression].idx_zlib_level;
+ int colors;
+ ssize_t bytes;
+
+#ifdef CONFIG_VNC_PNG
+ if (tight_can_send_png_rect(vs, w, h)) {
+ return send_png_rect(vs, x, y, w, h, palette);
+ }
+#endif
+
+ colors = palette_size(palette);
+
+ vnc_write_u8(vs, (stream | VNC_TIGHT_EXPLICIT_FILTER) << 4);
+ vnc_write_u8(vs, VNC_TIGHT_FILTER_PALETTE);
+ vnc_write_u8(vs, colors - 1);
+
+ switch(vs->clientds.pf.bytes_per_pixel) {
+ case 4:
+ {
+ size_t old_offset, offset;
+ uint32_t header[palette_size(palette)];
+ struct palette_cb_priv priv = { vs, (uint8_t *)header };
+
+ old_offset = vs->output.offset;
+ palette_iter(palette, write_palette, &priv);
+ vnc_write(vs, header, sizeof(header));
+
+ if (vs->tight.pixel24) {
+ tight_pack24(vs, vs->output.buffer + old_offset, colors, &offset);
+ vs->output.offset = old_offset + offset;
+ }
+
+ tight_encode_indexed_rect32(vs->tight.tight.buffer, w * h, palette);
+ break;
+ }
+ case 2:
+ {
+ uint16_t header[palette_size(palette)];
+ struct palette_cb_priv priv = { vs, (uint8_t *)header };
+
+ palette_iter(palette, write_palette, &priv);
+ vnc_write(vs, header, sizeof(header));
+ tight_encode_indexed_rect16(vs->tight.tight.buffer, w * h, palette);
+ break;
+ }
+ default:
+ return -1; /* No palette for 8bits colors */
+ break;
+ }
+ bytes = w * h;
+ vs->tight.tight.offset = bytes;
+
+ bytes = tight_compress_data(vs, stream, bytes,
+ level, Z_DEFAULT_STRATEGY);
+ return (bytes >= 0);
+}
+
+#if defined(CONFIG_VNC_JPEG) || defined(CONFIG_VNC_PNG)
+static void rgb_prepare_row24(VncState *vs, uint8_t *dst, int x, int y,
+ int count)
+{
+ VncDisplay *vd = vs->vd;
+ uint32_t *fbptr;
+ uint32_t pix;
+
+ fbptr = (uint32_t *)(vd->server->data + y * ds_get_linesize(vs->ds) +
+ x * ds_get_bytes_per_pixel(vs->ds));
+
+ while (count--) {
+ pix = *fbptr++;
+ *dst++ = (uint8_t)(pix >> vs->ds->surface->pf.rshift);
+ *dst++ = (uint8_t)(pix >> vs->ds->surface->pf.gshift);
+ *dst++ = (uint8_t)(pix >> vs->ds->surface->pf.bshift);
+ }
+}
+
+#define DEFINE_RGB_GET_ROW_FUNCTION(bpp) \
+ \
+ static void \
+ rgb_prepare_row##bpp(VncState *vs, uint8_t *dst, \
+ int x, int y, int count) \
+ { \
+ VncDisplay *vd = vs->vd; \
+ uint##bpp##_t *fbptr; \
+ uint##bpp##_t pix; \
+ int r, g, b; \
+ \
+ fbptr = (uint##bpp##_t *) \
+ (vd->server->data + y * ds_get_linesize(vs->ds) + \
+ x * ds_get_bytes_per_pixel(vs->ds)); \
+ \
+ while (count--) { \
+ pix = *fbptr++; \
+ \
+ r = (int)((pix >> vs->ds->surface->pf.rshift) \
+ & vs->ds->surface->pf.rmax); \
+ g = (int)((pix >> vs->ds->surface->pf.gshift) \
+ & vs->ds->surface->pf.gmax); \
+ b = (int)((pix >> vs->ds->surface->pf.bshift) \
+ & vs->ds->surface->pf.bmax); \
+ \
+ *dst++ = (uint8_t)((r * 255 + vs->ds->surface->pf.rmax / 2) \
+ / vs->ds->surface->pf.rmax); \
+ *dst++ = (uint8_t)((g * 255 + vs->ds->surface->pf.gmax / 2) \
+ / vs->ds->surface->pf.gmax); \
+ *dst++ = (uint8_t)((b * 255 + vs->ds->surface->pf.bmax / 2) \
+ / vs->ds->surface->pf.bmax); \
+ } \
+ }
+
+DEFINE_RGB_GET_ROW_FUNCTION(16)
+DEFINE_RGB_GET_ROW_FUNCTION(32)
+
+static void rgb_prepare_row(VncState *vs, uint8_t *dst, int x, int y,
+ int count)
+{
+ if (ds_get_bytes_per_pixel(vs->ds) == 4) {
+ if (vs->ds->surface->pf.rmax == 0xFF &&
+ vs->ds->surface->pf.gmax == 0xFF &&
+ vs->ds->surface->pf.bmax == 0xFF) {
+ rgb_prepare_row24(vs, dst, x, y, count);
+ } else {
+ rgb_prepare_row32(vs, dst, x, y, count);
+ }
+ } else {
+ rgb_prepare_row16(vs, dst, x, y, count);
+ }
+}
+#endif /* CONFIG_VNC_JPEG or CONFIG_VNC_PNG */
+
+/*
+ * JPEG compression stuff.
+ */
+#ifdef CONFIG_VNC_JPEG
+/*
+ * Destination manager implementation for JPEG library.
+ */
+
+/* This is called once per encoding */
+static void jpeg_init_destination(j_compress_ptr cinfo)
+{
+ VncState *vs = cinfo->client_data;
+ Buffer *buffer = &vs->tight.jpeg;
+
+ cinfo->dest->next_output_byte = (JOCTET *)buffer->buffer + buffer->offset;
+ cinfo->dest->free_in_buffer = (size_t)(buffer->capacity - buffer->offset);
+}
+
+/* This is called when we ran out of buffer (shouldn't happen!) */
+static boolean jpeg_empty_output_buffer(j_compress_ptr cinfo)
+{
+ VncState *vs = cinfo->client_data;
+ Buffer *buffer = &vs->tight.jpeg;
+
+ buffer->offset = buffer->capacity;
+ buffer_reserve(buffer, 2048);
+ jpeg_init_destination(cinfo);
+ return TRUE;
+}
+
+/* This is called when we are done processing data */
+static void jpeg_term_destination(j_compress_ptr cinfo)
+{
+ VncState *vs = cinfo->client_data;
+ Buffer *buffer = &vs->tight.jpeg;
+
+ buffer->offset = buffer->capacity - cinfo->dest->free_in_buffer;
+}
+
+static int send_jpeg_rect(VncState *vs, int x, int y, int w, int h, int quality)
+{
+ struct jpeg_compress_struct cinfo;
+ struct jpeg_error_mgr jerr;
+ struct jpeg_destination_mgr manager;
+ JSAMPROW row[1];
+ uint8_t *buf;
+ int dy;
+
+ if (ds_get_bytes_per_pixel(vs->ds) == 1)
+ return send_full_color_rect(vs, x, y, w, h);
+
+ buffer_reserve(&vs->tight.jpeg, 2048);
+
+ cinfo.err = jpeg_std_error(&jerr);
+ jpeg_create_compress(&cinfo);
+
+ cinfo.client_data = vs;
+ cinfo.image_width = w;
+ cinfo.image_height = h;
+ cinfo.input_components = 3;
+ cinfo.in_color_space = JCS_RGB;
+
+ jpeg_set_defaults(&cinfo);
+ jpeg_set_quality(&cinfo, quality, true);
+
+ manager.init_destination = jpeg_init_destination;
+ manager.empty_output_buffer = jpeg_empty_output_buffer;
+ manager.term_destination = jpeg_term_destination;
+ cinfo.dest = &manager;
+
+ jpeg_start_compress(&cinfo, true);
+
+ buf = qemu_malloc(w * 3);
+ row[0] = buf;
+ for (dy = 0; dy < h; dy++) {
+ rgb_prepare_row(vs, buf, x, y + dy, w);
+ jpeg_write_scanlines(&cinfo, row, 1);
+ }
+ qemu_free(buf);
+
+ jpeg_finish_compress(&cinfo);
+ jpeg_destroy_compress(&cinfo);
+
+ vnc_write_u8(vs, VNC_TIGHT_JPEG << 4);
+
+ tight_send_compact_size(vs, vs->tight.jpeg.offset);
+ vnc_write(vs, vs->tight.jpeg.buffer, vs->tight.jpeg.offset);
+ buffer_reset(&vs->tight.jpeg);
+
+ return 1;
+}
+#endif /* CONFIG_VNC_JPEG */
+
+/*
+ * PNG compression stuff.
+ */
+#ifdef CONFIG_VNC_PNG
+static void write_png_palette(int idx, uint32_t pix, void *opaque)
+{
+ struct palette_cb_priv *priv = opaque;
+ VncState *vs = priv->vs;
+ png_colorp color = &priv->png_palette[idx];
+
+ if (vs->tight.pixel24)
+ {
+ color->red = (pix >> vs->clientds.pf.rshift) & vs->clientds.pf.rmax;
+ color->green = (pix >> vs->clientds.pf.gshift) & vs->clientds.pf.gmax;
+ color->blue = (pix >> vs->clientds.pf.bshift) & vs->clientds.pf.bmax;
+ }
+ else
+ {
+ int red, green, blue;
+
+ red = (pix >> vs->clientds.pf.rshift) & vs->clientds.pf.rmax;
+ green = (pix >> vs->clientds.pf.gshift) & vs->clientds.pf.gmax;
+ blue = (pix >> vs->clientds.pf.bshift) & vs->clientds.pf.bmax;
+ color->red = ((red * 255 + vs->clientds.pf.rmax / 2) /
+ vs->clientds.pf.rmax);
+ color->green = ((green * 255 + vs->clientds.pf.gmax / 2) /
+ vs->clientds.pf.gmax);
+ color->blue = ((blue * 255 + vs->clientds.pf.bmax / 2) /
+ vs->clientds.pf.bmax);
+ }
+}
+
+static void png_write_data(png_structp png_ptr, png_bytep data,
+ png_size_t length)
+{
+ VncState *vs = png_get_io_ptr(png_ptr);
+
+ buffer_reserve(&vs->tight.png, vs->tight.png.offset + length);
+ memcpy(vs->tight.png.buffer + vs->tight.png.offset, data, length);
+
+ vs->tight.png.offset += length;
+}
+
+static void png_flush_data(png_structp png_ptr)
+{
+}
+
+static void *vnc_png_malloc(png_structp png_ptr, png_size_t size)
+{
+ return qemu_malloc(size);
+}
+
+static void vnc_png_free(png_structp png_ptr, png_voidp ptr)
+{
+ qemu_free(ptr);
+}
+
+static int send_png_rect(VncState *vs, int x, int y, int w, int h,
+ VncPalette *palette)
+{
+ png_byte color_type;
+ png_structp png_ptr;
+ png_infop info_ptr;
+ png_colorp png_palette = NULL;
+ int level = tight_png_conf[vs->tight.compression].png_zlib_level;
+ int filters = tight_png_conf[vs->tight.compression].png_filters;
+ uint8_t *buf;
+ int dy;
+
+ png_ptr = png_create_write_struct_2(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL,
+ NULL, vnc_png_malloc, vnc_png_free);
+
+ if (png_ptr == NULL)
+ return -1;
+
+ info_ptr = png_create_info_struct(png_ptr);
+
+ if (info_ptr == NULL) {
+ png_destroy_write_struct(&png_ptr, NULL);
+ return -1;
+ }
+
+ png_set_write_fn(png_ptr, (void *) vs, png_write_data, png_flush_data);
+ png_set_compression_level(png_ptr, level);
+ png_set_filter(png_ptr, PNG_FILTER_TYPE_DEFAULT, filters);
+
+ if (palette) {
+ color_type = PNG_COLOR_TYPE_PALETTE;
+ } else {
+ color_type = PNG_COLOR_TYPE_RGB;
+ }
+
+ png_set_IHDR(png_ptr, info_ptr, w, h,
+ 8, color_type, PNG_INTERLACE_NONE,
+ PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
+
+ if (color_type == PNG_COLOR_TYPE_PALETTE) {
+ struct palette_cb_priv priv;
+
+ png_palette = png_malloc(png_ptr, sizeof(*png_palette) *
+ palette_size(palette));
+
+ priv.vs = vs;
+ priv.png_palette = png_palette;
+ palette_iter(palette, write_png_palette, &priv);
+
+ png_set_PLTE(png_ptr, info_ptr, png_palette, palette_size(palette));
+
+ if (vs->clientds.pf.bytes_per_pixel == 4) {
+ tight_encode_indexed_rect32(vs->tight.tight.buffer, w * h, palette);
+ } else {
+ tight_encode_indexed_rect16(vs->tight.tight.buffer, w * h, palette);
+ }
+ }
+
+ png_write_info(png_ptr, info_ptr);
+
+ buffer_reserve(&vs->tight.png, 2048);
+ buf = qemu_malloc(w * 3);
+ for (dy = 0; dy < h; dy++)
+ {
+ if (color_type == PNG_COLOR_TYPE_PALETTE) {
+ memcpy(buf, vs->tight.tight.buffer + (dy * w), w);
+ } else {
+ rgb_prepare_row(vs, buf, x, y + dy, w);
+ }
+ png_write_row(png_ptr, buf);
+ }
+ qemu_free(buf);
+
+ png_write_end(png_ptr, NULL);
+
+ if (color_type == PNG_COLOR_TYPE_PALETTE) {
+ png_free(png_ptr, png_palette);
+ }
+
+ png_destroy_write_struct(&png_ptr, &info_ptr);
+
+ vnc_write_u8(vs, VNC_TIGHT_PNG << 4);
+
+ tight_send_compact_size(vs, vs->tight.png.offset);
+ vnc_write(vs, vs->tight.png.buffer, vs->tight.png.offset);
+ buffer_reset(&vs->tight.png);
+ return 1;
+}
+#endif /* CONFIG_VNC_PNG */
+
+static void vnc_tight_start(VncState *vs)
+{
+ buffer_reset(&vs->tight.tight);
+
+ // make the output buffer be the zlib buffer, so we can compress it later
+ vs->tight.tmp = vs->output;
+ vs->output = vs->tight.tight;
+}
+
+static void vnc_tight_stop(VncState *vs)
+{
+ // switch back to normal output/zlib buffers
+ vs->tight.tight = vs->output;
+ vs->output = vs->tight.tmp;
+}
+
+static int send_sub_rect_nojpeg(VncState *vs, int x, int y, int w, int h,
+ int bg, int fg, int colors, VncPalette *palette)
+{
+ int ret;
+
+ if (colors == 0) {
+ if (tight_detect_smooth_image(vs, w, h)) {
+ ret = send_gradient_rect(vs, x, y, w, h);
+ } else {
+ ret = send_full_color_rect(vs, x, y, w, h);
+ }
+ } else if (colors == 1) {
+ ret = send_solid_rect(vs);
+ } else if (colors == 2) {
+ ret = send_mono_rect(vs, x, y, w, h, bg, fg);
+ } else if (colors <= 256) {
+ ret = send_palette_rect(vs, x, y, w, h, palette);
+ } else {
+ ret = 0;
+ }
+ return ret;
+}
+
+#ifdef CONFIG_VNC_JPEG
+static int send_sub_rect_jpeg(VncState *vs, int x, int y, int w, int h,
+ int bg, int fg, int colors,
+ VncPalette *palette)
+{
+ int ret;
+
+ if (colors == 0) {
+ if (tight_detect_smooth_image(vs, w, h)) {
+ int quality = tight_conf[vs->tight.quality].jpeg_quality;
+
+ ret = send_jpeg_rect(vs, x, y, w, h, quality);
+ } else {
+ ret = send_full_color_rect(vs, x, y, w, h);
+ }
+ } else if (colors == 1) {
+ ret = send_solid_rect(vs);
+ } else if (colors == 2) {
+ ret = send_mono_rect(vs, x, y, w, h, bg, fg);
+ } else if (colors <= 256) {
+ if (colors > 96 &&
+ tight_detect_smooth_image(vs, w, h)) {
+ int quality = tight_conf[vs->tight.quality].jpeg_quality;
+
+ ret = send_jpeg_rect(vs, x, y, w, h, quality);
+ } else {
+ ret = send_palette_rect(vs, x, y, w, h, palette);
+ }
+ } else {
+ ret = 0;
+ }
+ return ret;
+}
+#endif
+
+static int send_sub_rect(VncState *vs, int x, int y, int w, int h)
+{
+ VncPalette *palette = NULL;
+ uint32_t bg = 0, fg = 0;
+ int colors;
+ int ret = 0;
+
+ vnc_framebuffer_update(vs, x, y, w, h, vs->tight.type);
+
+ vnc_tight_start(vs);
+ vnc_raw_send_framebuffer_update(vs, x, y, w, h);
+ vnc_tight_stop(vs);
+
+ colors = tight_fill_palette(vs, x, y, w * h, &fg, &bg, &palette);
+
+#ifdef CONFIG_VNC_JPEG
+ if (vs->tight.quality != (uint8_t)-1) {
+ ret = send_sub_rect_jpeg(vs, x, y, w, h, bg, fg, colors, palette);
+ } else {
+ ret = send_sub_rect_nojpeg(vs, x, y, w, h, bg, fg, colors, palette);
+ }
+#else
+ ret = send_sub_rect_nojpeg(vs, x, y, w, h, bg, fg, colors, palette);
+#endif
+
+ palette_destroy(palette);
+ return ret;
+}
+
+static int send_sub_rect_solid(VncState *vs, int x, int y, int w, int h)
+{
+ vnc_framebuffer_update(vs, x, y, w, h, vs->tight.type);
+
+ vnc_tight_start(vs);
+ vnc_raw_send_framebuffer_update(vs, x, y, w, h);
+ vnc_tight_stop(vs);
+
+ return send_solid_rect(vs);
+}
+
+static int send_rect_simple(VncState *vs, int x, int y, int w, int h)
+{
+ int max_size, max_width;
+ int max_sub_width, max_sub_height;
+ int dx, dy;
+ int rw, rh;
+ int n = 0;
+
+ max_size = tight_conf[vs->tight.compression].max_rect_size;
+ max_width = tight_conf[vs->tight.compression].max_rect_width;
+
+ if (w > max_width || w * h > max_size) {
+ max_sub_width = (w > max_width) ? max_width : w;
+ max_sub_height = max_size / max_sub_width;
+
+ for (dy = 0; dy < h; dy += max_sub_height) {
+ for (dx = 0; dx < w; dx += max_width) {
+ rw = MIN(max_sub_width, w - dx);
+ rh = MIN(max_sub_height, h - dy);
+ n += send_sub_rect(vs, x+dx, y+dy, rw, rh);
+ }
+ }
+ } else {
+ n += send_sub_rect(vs, x, y, w, h);
+ }
+
+ return n;
+}
+
+static int find_large_solid_color_rect(VncState *vs, int x, int y,
+ int w, int h, int max_rows)
+{
+ int dx, dy, dw, dh;
+ int n = 0;
+
+ /* Try to find large solid-color areas and send them separately. */
+
+ for (dy = y; dy < y + h; dy += VNC_TIGHT_MAX_SPLIT_TILE_SIZE) {
+
+ /* If a rectangle becomes too large, send its upper part now. */
+
+ if (dy - y >= max_rows) {
+ n += send_rect_simple(vs, x, y, w, max_rows);
+ y += max_rows;
+ h -= max_rows;
+ }
+
+ dh = MIN(VNC_TIGHT_MAX_SPLIT_TILE_SIZE, (y + h - dy));
+
+ for (dx = x; dx < x + w; dx += VNC_TIGHT_MAX_SPLIT_TILE_SIZE) {
+ uint32_t color_value;
+ int x_best, y_best, w_best, h_best;
+
+ dw = MIN(VNC_TIGHT_MAX_SPLIT_TILE_SIZE, (x + w - dx));
+
+ if (!check_solid_tile(vs, dx, dy, dw, dh, &color_value, false)) {
+ continue ;
+ }
+
+ /* Get dimensions of solid-color area. */
+
+ find_best_solid_area(vs, dx, dy, w - (dx - x), h - (dy - y),
+ color_value, &w_best, &h_best);
+
+ /* Make sure a solid rectangle is large enough
+ (or the whole rectangle is of the same color). */
+
+ if (w_best * h_best != w * h &&
+ w_best * h_best < VNC_TIGHT_MIN_SOLID_SUBRECT_SIZE) {
+ continue;
+ }
+
+ /* Try to extend solid rectangle to maximum size. */
+
+ x_best = dx; y_best = dy;
+ extend_solid_area(vs, x, y, w, h, color_value,
+ &x_best, &y_best, &w_best, &h_best);
+
+ /* Send rectangles at top and left to solid-color area. */
+
+ if (y_best != y) {
+ n += send_rect_simple(vs, x, y, w, y_best-y);
+ }
+ if (x_best != x) {
+ n += tight_send_framebuffer_update(vs, x, y_best,
+ x_best-x, h_best);
+ }
+
+ /* Send solid-color rectangle. */
+ n += send_sub_rect_solid(vs, x_best, y_best, w_best, h_best);
+
+ /* Send remaining rectangles (at right and bottom). */
+
+ if (x_best + w_best != x + w) {
+ n += tight_send_framebuffer_update(vs, x_best+w_best,
+ y_best,
+ w-(x_best-x)-w_best,
+ h_best);
+ }
+ if (y_best + h_best != y + h) {
+ n += tight_send_framebuffer_update(vs, x, y_best+h_best,
+ w, h-(y_best-y)-h_best);
+ }
+
+ /* Return after all recursive calls are done. */
+ return n;
+ }
+ }
+ return n + send_rect_simple(vs, x, y, w, h);
+}
+
+static int tight_send_framebuffer_update(VncState *vs, int x, int y,
+ int w, int h)
+{
+ int max_rows;
+
+ if (vs->clientds.pf.bytes_per_pixel == 4 && vs->clientds.pf.rmax == 0xFF &&
+ vs->clientds.pf.bmax == 0xFF && vs->clientds.pf.gmax == 0xFF) {
+ vs->tight.pixel24 = true;
+ } else {
+ vs->tight.pixel24 = false;
+ }
+
+ if (w * h < VNC_TIGHT_MIN_SPLIT_RECT_SIZE)
+ return send_rect_simple(vs, x, y, w, h);
+
+ /* Calculate maximum number of rows in one non-solid rectangle. */
+
+ max_rows = tight_conf[vs->tight.compression].max_rect_size;
+ max_rows /= MIN(tight_conf[vs->tight.compression].max_rect_width, w);
+
+ return find_large_solid_color_rect(vs, x, y, w, h, max_rows);
+}
+
+int vnc_tight_send_framebuffer_update(VncState *vs, int x, int y,
+ int w, int h)
+{
+ vs->tight.type = VNC_ENCODING_TIGHT;
+ return tight_send_framebuffer_update(vs, x, y, w, h);
+}
+
+int vnc_tight_png_send_framebuffer_update(VncState *vs, int x, int y,
+ int w, int h)
+{
+ vs->tight.type = VNC_ENCODING_TIGHT_PNG;
+ return tight_send_framebuffer_update(vs, x, y, w, h);
+}
+
+void vnc_tight_clear(VncState *vs)
+{
+ int i;
+ for (i=0; i<ARRAY_SIZE(vs->tight.stream); i++) {
+ if (vs->tight.stream[i].opaque) {
+ deflateEnd(&vs->tight.stream[i]);
+ }
+ }
+
+ buffer_free(&vs->tight.tight);
+ buffer_free(&vs->tight.zlib);
+ buffer_free(&vs->tight.gradient);
+#ifdef CONFIG_VNC_JPEG
+ buffer_free(&vs->tight.jpeg);
+#endif
+#ifdef CONFIG_VNC_PNG
+ buffer_free(&vs->tight.png);
+#endif
+}
diff --git a/vnc-encoding-tight.h b/ui/vnc-enc-tight.h
index 64d10625fe..a3add788e2 100644
--- a/vnc-encoding-tight.h
+++ b/ui/vnc-enc-tight.h
@@ -42,8 +42,9 @@
* bit 3: if 1, then compression stream 3 should be reset;
* bits 7-4: if 1000 (0x08), then the compression type is "fill",
* if 1001 (0x09), then the compression type is "jpeg",
+ * if 1010 (0x0A), then the compression type is "png",
* if 0xxx, then the compression type is "basic",
- * values greater than 1001 are not valid.
+ * values greater than 1010 are not valid.
*
* If the compression type is "basic", then bits 6..4 of the
* compression control byte (those xxx in 0xxx) specify the following:
@@ -53,17 +54,17 @@
* bit 6: if 1, then a "filter id" byte is following this byte.
*
*-- The data that follows after the compression control byte described
- * above depends on the compression type ("fill", "jpeg" or "basic").
+ * above depends on the compression type ("fill", "jpeg", "png" or "basic").
*
*-- If the compression type is "fill", then the only pixel value follows, in
* client pixel format (see NOTE 1). This value applies to all pixels of the
* rectangle.
*
- *-- If the compression type is "jpeg", the following data stream looks like
- * this:
+ *-- If the compression type is "jpeg" or "png", the following data stream
+ * looks like this:
*
* 1..3 bytes: data size (N) in compact representation;
- * N bytes: JPEG image.
+ * N bytes: JPEG or PNG image.
*
* Data size is compactly represented in one, two or three bytes, according
* to the following scheme:
@@ -144,7 +145,7 @@
*-- NOTE 2. The decoder must reset compression streams' states before
* decoding the rectangle, if some of bits 0,1,2,3 in the compression control
* byte are set to 1. Note that the decoder must reset zlib streams even if
- * the compression type is "fill" or "jpeg".
+ * the compression type is "fill", "jpeg" or "png".
*
*-- NOTE 3. The "gradient" filter and "jpeg" compression may be used only
* when bits-per-pixel value is either 16 or 32, not 8.
@@ -158,7 +159,8 @@
#define VNC_TIGHT_EXPLICIT_FILTER 0x04
#define VNC_TIGHT_FILL 0x08
#define VNC_TIGHT_JPEG 0x09
-#define VNC_TIGHT_MAX_SUBENCODING 0x09
+#define VNC_TIGHT_PNG 0x0A
+#define VNC_TIGHT_MAX_SUBENCODING 0x0A
/* Filters to improve compression efficiency */
#define VNC_TIGHT_FILTER_COPY 0x00
@@ -173,4 +175,9 @@
#define VNC_TIGHT_MIN_SOLID_SUBRECT_SIZE 2048
#define VNC_TIGHT_MAX_SPLIT_TILE_SIZE 16
+#define VNC_TIGHT_JPEG_MIN_RECT_SIZE 4096
+#define VNC_TIGHT_DETECT_SUBROW_WIDTH 7
+#define VNC_TIGHT_DETECT_MIN_WIDTH 8
+#define VNC_TIGHT_DETECT_MIN_HEIGHT 8
+
#endif /* VNC_ENCODING_TIGHT_H */
diff --git a/vnc-encoding-zlib.c b/ui/vnc-enc-zlib.c
index a99bc387dc..3c6e6abf94 100644
--- a/vnc-encoding-zlib.c
+++ b/ui/vnc-enc-zlib.c
@@ -47,21 +47,21 @@ void vnc_zlib_zfree(void *x, void *addr)
static void vnc_zlib_start(VncState *vs)
{
- buffer_reset(&vs->zlib);
+ buffer_reset(&vs->zlib.zlib);
// make the output buffer be the zlib buffer, so we can compress it later
- vs->zlib_tmp = vs->output;
- vs->output = vs->zlib;
+ vs->zlib.tmp = vs->output;
+ vs->output = vs->zlib.zlib;
}
static int vnc_zlib_stop(VncState *vs)
{
- z_streamp zstream = &vs->zlib_stream;
+ z_streamp zstream = &vs->zlib.stream;
int previous_out;
// switch back to normal output/zlib buffers
- vs->zlib = vs->output;
- vs->output = vs->zlib_tmp;
+ vs->zlib.zlib = vs->output;
+ vs->output = vs->zlib.tmp;
// compress the zlib buffer
@@ -75,7 +75,7 @@ static int vnc_zlib_stop(VncState *vs)
zstream->zalloc = vnc_zlib_zalloc;
zstream->zfree = vnc_zlib_zfree;
- err = deflateInit2(zstream, vs->tight_compression, Z_DEFLATED, MAX_WBITS,
+ err = deflateInit2(zstream, vs->tight.compression, Z_DEFLATED, MAX_WBITS,
MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY);
if (err != Z_OK) {
@@ -83,24 +83,24 @@ static int vnc_zlib_stop(VncState *vs)
return -1;
}
- vs->zlib_level = vs->tight_compression;
+ vs->zlib.level = vs->tight.compression;
zstream->opaque = vs;
}
- if (vs->tight_compression != vs->zlib_level) {
- if (deflateParams(zstream, vs->tight_compression,
+ if (vs->tight.compression != vs->zlib.level) {
+ if (deflateParams(zstream, vs->tight.compression,
Z_DEFAULT_STRATEGY) != Z_OK) {
return -1;
}
- vs->zlib_level = vs->tight_compression;
+ vs->zlib.level = vs->tight.compression;
}
// reserve memory in output buffer
- buffer_reserve(&vs->output, vs->zlib.offset + 64);
+ buffer_reserve(&vs->output, vs->zlib.zlib.offset + 64);
// set pointers
- zstream->next_in = vs->zlib.buffer;
- zstream->avail_in = vs->zlib.offset;
+ zstream->next_in = vs->zlib.zlib.buffer;
+ zstream->avail_in = vs->zlib.zlib.offset;
zstream->next_out = vs->output.buffer + vs->output.offset;
zstream->avail_out = vs->output.capacity - vs->output.offset;
zstream->data_type = Z_BINARY;
@@ -145,8 +145,8 @@ int vnc_zlib_send_framebuffer_update(VncState *vs, int x, int y, int w, int h)
void vnc_zlib_clear(VncState *vs)
{
- if (vs->zlib_stream.opaque) {
- deflateEnd(&vs->zlib_stream);
+ if (vs->zlib.stream.opaque) {
+ deflateEnd(&vs->zlib.stream);
}
- buffer_free(&vs->zlib);
+ buffer_free(&vs->zlib.zlib);
}
diff --git a/ui/vnc-jobs-async.c b/ui/vnc-jobs-async.c
new file mode 100644
index 0000000000..6e9cf08b69
--- /dev/null
+++ b/ui/vnc-jobs-async.c
@@ -0,0 +1,331 @@
+/*
+ * QEMU VNC display driver
+ *
+ * Copyright (C) 2006 Anthony Liguori <anthony@codemonkey.ws>
+ * Copyright (C) 2006 Fabrice Bellard
+ * Copyright (C) 2009 Red Hat, Inc
+ * Copyright (C) 2010 Corentin Chary <corentin.chary@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+#include "vnc.h"
+#include "vnc-jobs.h"
+
+/*
+ * Locking:
+ *
+ * There is three levels of locking:
+ * - jobs queue lock: for each operation on the queue (push, pop, isEmpty?)
+ * - VncDisplay global lock: mainly used for framebuffer updates to avoid
+ * screen corruption if the framebuffer is updated
+ * while the worker is doing something.
+ * - VncState::output lock: used to make sure the output buffer is not corrupted
+ * if two threads try to write on it at the same time
+ *
+ * While the VNC worker thread is working, the VncDisplay global lock is hold
+ * to avoid screen corruptions (this does not block vnc_refresh() because it
+ * uses trylock()) but the output lock is not hold because the thread work on
+ * its own output buffer.
+ * When the encoding job is done, the worker thread will hold the output lock
+ * and copy its output buffer in vs->output.
+*/
+
+struct VncJobQueue {
+ QemuCond cond;
+ QemuMutex mutex;
+ QemuThread thread;
+ Buffer buffer;
+ bool exit;
+ QTAILQ_HEAD(, VncJob) jobs;
+};
+
+typedef struct VncJobQueue VncJobQueue;
+
+/*
+ * We use a single global queue, but most of the functions are
+ * already reetrant, so we can easilly add more than one encoding thread
+ */
+static VncJobQueue *queue;
+
+static void vnc_lock_queue(VncJobQueue *queue)
+{
+ qemu_mutex_lock(&queue->mutex);
+}
+
+static void vnc_unlock_queue(VncJobQueue *queue)
+{
+ qemu_mutex_unlock(&queue->mutex);
+}
+
+VncJob *vnc_job_new(VncState *vs)
+{
+ VncJob *job = qemu_mallocz(sizeof(VncJob));
+
+ job->vs = vs;
+ vnc_lock_queue(queue);
+ QLIST_INIT(&job->rectangles);
+ vnc_unlock_queue(queue);
+ return job;
+}
+
+int vnc_job_add_rect(VncJob *job, int x, int y, int w, int h)
+{
+ VncRectEntry *entry = qemu_mallocz(sizeof(VncRectEntry));
+
+ entry->rect.x = x;
+ entry->rect.y = y;
+ entry->rect.w = w;
+ entry->rect.h = h;
+
+ vnc_lock_queue(queue);
+ QLIST_INSERT_HEAD(&job->rectangles, entry, next);
+ vnc_unlock_queue(queue);
+ return 1;
+}
+
+void vnc_job_push(VncJob *job)
+{
+ vnc_lock_queue(queue);
+ if (queue->exit || QLIST_EMPTY(&job->rectangles)) {
+ qemu_free(job);
+ } else {
+ QTAILQ_INSERT_TAIL(&queue->jobs, job, next);
+ qemu_cond_broadcast(&queue->cond);
+ }
+ vnc_unlock_queue(queue);
+}
+
+static bool vnc_has_job_locked(VncState *vs)
+{
+ VncJob *job;
+
+ QTAILQ_FOREACH(job, &queue->jobs, next) {
+ if (job->vs == vs || !vs) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool vnc_has_job(VncState *vs)
+{
+ bool ret;
+
+ vnc_lock_queue(queue);
+ ret = vnc_has_job_locked(vs);
+ vnc_unlock_queue(queue);
+ return ret;
+}
+
+void vnc_jobs_clear(VncState *vs)
+{
+ VncJob *job, *tmp;
+
+ vnc_lock_queue(queue);
+ QTAILQ_FOREACH_SAFE(job, &queue->jobs, next, tmp) {
+ if (job->vs == vs || !vs) {
+ QTAILQ_REMOVE(&queue->jobs, job, next);
+ }
+ }
+ vnc_unlock_queue(queue);
+}
+
+void vnc_jobs_join(VncState *vs)
+{
+ vnc_lock_queue(queue);
+ while (vnc_has_job_locked(vs)) {
+ qemu_cond_wait(&queue->cond, &queue->mutex);
+ }
+ vnc_unlock_queue(queue);
+}
+
+/*
+ * Copy data for local use
+ */
+static void vnc_async_encoding_start(VncState *orig, VncState *local)
+{
+ local->vnc_encoding = orig->vnc_encoding;
+ local->features = orig->features;
+ local->ds = orig->ds;
+ local->vd = orig->vd;
+ local->write_pixels = orig->write_pixels;
+ local->clientds = orig->clientds;
+ local->tight = orig->tight;
+ local->zlib = orig->zlib;
+ local->hextile = orig->hextile;
+ local->output = queue->buffer;
+ local->csock = -1; /* Don't do any network work on this thread */
+
+ buffer_reset(&local->output);
+}
+
+static void vnc_async_encoding_end(VncState *orig, VncState *local)
+{
+ orig->tight = local->tight;
+ orig->zlib = local->zlib;
+ orig->hextile = local->hextile;
+}
+
+static int vnc_worker_thread_loop(VncJobQueue *queue)
+{
+ VncJob *job;
+ VncRectEntry *entry, *tmp;
+ VncState vs;
+ int n_rectangles;
+ int saved_offset;
+ bool flush;
+
+ vnc_lock_queue(queue);
+ while (QTAILQ_EMPTY(&queue->jobs) && !queue->exit) {
+ qemu_cond_wait(&queue->cond, &queue->mutex);
+ }
+ /* Here job can only be NULL if queue->exit is true */
+ job = QTAILQ_FIRST(&queue->jobs);
+ vnc_unlock_queue(queue);
+
+ if (queue->exit) {
+ return -1;
+ }
+
+ vnc_lock_output(job->vs);
+ if (job->vs->csock == -1 || job->vs->abort == true) {
+ goto disconnected;
+ }
+ vnc_unlock_output(job->vs);
+
+ /* Make a local copy of vs and switch output buffers */
+ vnc_async_encoding_start(job->vs, &vs);
+
+ /* Start sending rectangles */
+ n_rectangles = 0;
+ vnc_write_u8(&vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
+ vnc_write_u8(&vs, 0);
+ saved_offset = vs.output.offset;
+ vnc_write_u16(&vs, 0);
+
+ vnc_lock_display(job->vs->vd);
+ QLIST_FOREACH_SAFE(entry, &job->rectangles, next, tmp) {
+ int n;
+
+ if (job->vs->csock == -1) {
+ vnc_unlock_display(job->vs->vd);
+ goto disconnected;
+ }
+
+ n = vnc_send_framebuffer_update(&vs, entry->rect.x, entry->rect.y,
+ entry->rect.w, entry->rect.h);
+
+ if (n >= 0) {
+ n_rectangles += n;
+ }
+ qemu_free(entry);
+ }
+ vnc_unlock_display(job->vs->vd);
+
+ /* Put n_rectangles at the beginning of the message */
+ vs.output.buffer[saved_offset] = (n_rectangles >> 8) & 0xFF;
+ vs.output.buffer[saved_offset + 1] = n_rectangles & 0xFF;
+
+ /* Switch back buffers */
+ vnc_lock_output(job->vs);
+ if (job->vs->csock == -1) {
+ goto disconnected;
+ }
+
+ vnc_write(job->vs, vs.output.buffer, vs.output.offset);
+
+disconnected:
+ /* Copy persistent encoding data */
+ vnc_async_encoding_end(job->vs, &vs);
+ flush = (job->vs->csock != -1 && job->vs->abort != true);
+ vnc_unlock_output(job->vs);
+
+ if (flush) {
+ vnc_flush(job->vs);
+ }
+
+ vnc_lock_queue(queue);
+ QTAILQ_REMOVE(&queue->jobs, job, next);
+ vnc_unlock_queue(queue);
+ qemu_cond_broadcast(&queue->cond);
+ qemu_free(job);
+ return 0;
+}
+
+static VncJobQueue *vnc_queue_init(void)
+{
+ VncJobQueue *queue = qemu_mallocz(sizeof(VncJobQueue));
+
+ qemu_cond_init(&queue->cond);
+ qemu_mutex_init(&queue->mutex);
+ QTAILQ_INIT(&queue->jobs);
+ return queue;
+}
+
+static void vnc_queue_clear(VncJobQueue *q)
+{
+ qemu_cond_destroy(&queue->cond);
+ qemu_mutex_destroy(&queue->mutex);
+ buffer_free(&queue->buffer);
+ qemu_free(q);
+ queue = NULL; /* Unset global queue */
+}
+
+static void *vnc_worker_thread(void *arg)
+{
+ VncJobQueue *queue = arg;
+
+ qemu_thread_self(&queue->thread);
+
+ while (!vnc_worker_thread_loop(queue)) ;
+ vnc_queue_clear(queue);
+ return NULL;
+}
+
+void vnc_start_worker_thread(void)
+{
+ VncJobQueue *q;
+
+ if (vnc_worker_thread_running())
+ return ;
+
+ q = vnc_queue_init();
+ qemu_thread_create(&q->thread, vnc_worker_thread, q);
+ queue = q; /* Set global queue */
+}
+
+bool vnc_worker_thread_running(void)
+{
+ return queue; /* Check global queue */
+}
+
+void vnc_stop_worker_thread(void)
+{
+ if (!vnc_worker_thread_running())
+ return ;
+
+ /* Remove all jobs and wake up the thread */
+ vnc_lock_queue(queue);
+ queue->exit = true;
+ vnc_unlock_queue(queue);
+ vnc_jobs_clear(NULL);
+ qemu_cond_broadcast(&queue->cond);
+}
diff --git a/ui/vnc-jobs-sync.c b/ui/vnc-jobs-sync.c
new file mode 100644
index 0000000000..49b77afcc9
--- /dev/null
+++ b/ui/vnc-jobs-sync.c
@@ -0,0 +1,73 @@
+/*
+ * QEMU VNC display driver
+ *
+ * Copyright (C) 2006 Anthony Liguori <anthony@codemonkey.ws>
+ * Copyright (C) 2006 Fabrice Bellard
+ * Copyright (C) 2009 Red Hat, Inc
+ * Copyright (C) 2010 Corentin Chary <corentin.chary@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "vnc.h"
+#include "vnc-jobs.h"
+
+void vnc_jobs_clear(VncState *vs)
+{
+}
+
+void vnc_jobs_join(VncState *vs)
+{
+}
+
+VncJob *vnc_job_new(VncState *vs)
+{
+ vs->job.vs = vs;
+ vs->job.rectangles = 0;
+
+ vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
+ vnc_write_u8(vs, 0);
+ vs->job.saved_offset = vs->output.offset;
+ vnc_write_u16(vs, 0);
+ return &vs->job;
+}
+
+void vnc_job_push(VncJob *job)
+{
+ VncState *vs = job->vs;
+
+ vs->output.buffer[job->saved_offset] = (job->rectangles >> 8) & 0xFF;
+ vs->output.buffer[job->saved_offset + 1] = job->rectangles & 0xFF;
+ vnc_flush(job->vs);
+}
+
+int vnc_job_add_rect(VncJob *job, int x, int y, int w, int h)
+{
+ int n;
+
+ n = vnc_send_framebuffer_update(job->vs, x, y, w, h);
+ if (n >= 0)
+ job->rectangles += n;
+ return n;
+}
+
+bool vnc_has_job(VncState *vs)
+{
+ return false;
+}
diff --git a/ui/vnc-jobs.h b/ui/vnc-jobs.h
new file mode 100644
index 0000000000..b8dab8169f
--- /dev/null
+++ b/ui/vnc-jobs.h
@@ -0,0 +1,87 @@
+/*
+ * QEMU VNC display driver
+ *
+ * From libvncserver/rfb/rfbproto.h
+ * Copyright (C) 2005 Rohit Kumar, Johannes E. Schindelin
+ * Copyright (C) 2000-2002 Constantin Kaplinsky. All Rights Reserved.
+ * Copyright (C) 2000 Tridia Corporation. All Rights Reserved.
+ * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
+ *
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef VNC_JOBS_H
+#define VNC_JOBS_H
+
+/* Jobs */
+VncJob *vnc_job_new(VncState *vs);
+int vnc_job_add_rect(VncJob *job, int x, int y, int w, int h);
+void vnc_job_push(VncJob *job);
+bool vnc_has_job(VncState *vs);
+void vnc_jobs_clear(VncState *vs);
+void vnc_jobs_join(VncState *vs);
+
+#ifdef CONFIG_VNC_THREAD
+
+void vnc_start_worker_thread(void);
+bool vnc_worker_thread_running(void);
+void vnc_stop_worker_thread(void);
+
+#endif /* CONFIG_VNC_THREAD */
+
+/* Locks */
+static inline int vnc_trylock_display(VncDisplay *vd)
+{
+#ifdef CONFIG_VNC_THREAD
+ return qemu_mutex_trylock(&vd->mutex);
+#else
+ return 0;
+#endif
+}
+
+static inline void vnc_lock_display(VncDisplay *vd)
+{
+#ifdef CONFIG_VNC_THREAD
+ qemu_mutex_lock(&vd->mutex);
+#endif
+}
+
+static inline void vnc_unlock_display(VncDisplay *vd)
+{
+#ifdef CONFIG_VNC_THREAD
+ qemu_mutex_unlock(&vd->mutex);
+#endif
+}
+
+static inline void vnc_lock_output(VncState *vs)
+{
+#ifdef CONFIG_VNC_THREAD
+ qemu_mutex_lock(&vs->output_mutex);
+#endif
+}
+
+static inline void vnc_unlock_output(VncState *vs)
+{
+#ifdef CONFIG_VNC_THREAD
+ qemu_mutex_unlock(&vs->output_mutex);
+#endif
+}
+
+#endif /* VNC_JOBS_H */
diff --git a/ui/vnc-palette.c b/ui/vnc-palette.c
new file mode 100644
index 0000000000..bff6445cc1
--- /dev/null
+++ b/ui/vnc-palette.c
@@ -0,0 +1,136 @@
+/*
+ * QEMU VNC display driver: palette hash table
+ *
+ * From libvncserver/libvncserver/tight.c
+ * Copyright (C) 2000, 2001 Const Kaplinsky. All Rights Reserved.
+ * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
+ *
+ * Copyright (C) 2010 Corentin Chary <corentin.chary@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "vnc-palette.h"
+
+static VncPaletteEntry *palette_find(const VncPalette *palette,
+ uint32_t color, unsigned int hash)
+{
+ VncPaletteEntry *entry;
+
+ QLIST_FOREACH(entry, &palette->table[hash], next) {
+ if (entry->color == color) {
+ return entry;
+ }
+ }
+
+ return NULL;
+}
+
+static unsigned int palette_hash(uint32_t rgb, int bpp)
+{
+ if (bpp == 16) {
+ return ((unsigned int)(((rgb >> 8) + rgb) & 0xFF));
+ } else {
+ return ((unsigned int)(((rgb >> 16) + (rgb >> 8)) & 0xFF));
+ }
+}
+
+VncPalette *palette_new(size_t max, int bpp)
+{
+ VncPalette *palette;
+
+ palette = qemu_mallocz(sizeof(*palette));
+ palette->max = max;
+ palette->bpp = bpp;
+ return palette;
+}
+
+void palette_destroy(VncPalette *palette)
+{
+ int i;
+
+ if (palette == NULL) {
+ return ;
+ }
+
+ for (i = 0; i < VNC_PALETTE_HASH_SIZE; i++) {
+ VncPaletteEntry *entry = QLIST_FIRST(&palette->table[i]);
+ while (entry) {
+ VncPaletteEntry *tmp = QLIST_NEXT(entry, next);
+ QLIST_REMOVE(entry, next);
+ qemu_free(entry);
+ entry = tmp;
+ }
+ }
+
+ qemu_free(palette);
+}
+
+int palette_put(VncPalette *palette, uint32_t color)
+{
+ unsigned int hash;
+ unsigned int idx = palette->size;
+ VncPaletteEntry *entry;
+
+ hash = palette_hash(color, palette->bpp) % VNC_PALETTE_HASH_SIZE;
+ entry = palette_find(palette, color, hash);
+
+ if (!entry && palette->size >= palette->max) {
+ return 0;
+ }
+ if (!entry) {
+ VncPaletteEntry *entry;
+
+ entry = qemu_mallocz(sizeof(*entry));
+ entry->color = color;
+ entry->idx = idx;
+ QLIST_INSERT_HEAD(&palette->table[hash], entry, next);
+ palette->size++;
+ }
+ return palette->size;
+}
+
+int palette_idx(const VncPalette *palette, uint32_t color)
+{
+ VncPaletteEntry *entry;
+ unsigned int hash;
+
+ hash = palette_hash(color, palette->bpp) % VNC_PALETTE_HASH_SIZE;
+ entry = palette_find(palette, color, hash);
+ return (entry == NULL ? -1 : entry->idx);
+}
+
+size_t palette_size(const VncPalette *palette)
+{
+ return palette->size;
+}
+
+void palette_iter(const VncPalette *palette,
+ void (*iter)(int idx, uint32_t color, void *opaque),
+ void *opaque)
+{
+ int i;
+ VncPaletteEntry *entry;
+
+ for (i = 0; i < VNC_PALETTE_HASH_SIZE; i++) {
+ QLIST_FOREACH(entry, &palette->table[i], next) {
+ iter(entry->idx, entry->color, opaque);
+ }
+ }
+}
diff --git a/ui/vnc-palette.h b/ui/vnc-palette.h
new file mode 100644
index 0000000000..d0645ebde8
--- /dev/null
+++ b/ui/vnc-palette.h
@@ -0,0 +1,63 @@
+/*
+ * QEMU VNC display driver: palette hash table
+ *
+ * From libvncserver/libvncserver/tight.c
+ * Copyright (C) 2000, 2001 Const Kaplinsky. All Rights Reserved.
+ * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
+ *
+ * Copyright (C) 2010 Corentin Chary <corentin.chary@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef VNC_PALETTE_H
+#define VNC_PALETTE_H
+
+#include "qlist.h"
+#include "qemu-queue.h"
+#include <stdint.h>
+
+#define VNC_PALETTE_HASH_SIZE 256
+
+typedef struct VncPaletteEntry {
+ int idx;
+ uint32_t color;
+ QLIST_ENTRY(VncPaletteEntry) next;
+} VncPaletteEntry;
+
+typedef struct VncPalette {
+ QObject_HEAD;
+ size_t size;
+ size_t max;
+ int bpp;
+ QLIST_HEAD(,VncPaletteEntry) table[VNC_PALETTE_HASH_SIZE];
+} VncPalette;
+
+VncPalette *palette_new(size_t max, int bpp);
+void palette_destroy(VncPalette *palette);
+
+int palette_put(VncPalette *palette, uint32_t color);
+int palette_idx(const VncPalette *palette, uint32_t color);
+size_t palette_size(const VncPalette *palette);
+
+void palette_iter(const VncPalette *palette,
+ void (*iter)(int idx, uint32_t color, void *opaque),
+ void *opaque);
+
+#endif /* VNC_PALETTE_H */
diff --git a/vnc-tls.c b/ui/vnc-tls.c
index dec626c539..dec626c539 100644
--- a/vnc-tls.c
+++ b/ui/vnc-tls.c
diff --git a/vnc-tls.h b/ui/vnc-tls.h
index 2b93633896..2b93633896 100644
--- a/vnc-tls.h
+++ b/ui/vnc-tls.h
diff --git a/vnc.c b/ui/vnc.c
index ed0e096000..864342e84d 100644
--- a/vnc.c
+++ b/ui/vnc.c
@@ -25,6 +25,7 @@
*/
#include "vnc.h"
+#include "vnc-jobs.h"
#include "sysemu.h"
#include "qemu_socket.h"
#include "qemu-timer.h"
@@ -45,7 +46,6 @@
} \
}
-
static VncDisplay *vnc_display; /* needed for info vnc */
static DisplayChangeListener *dcl;
@@ -214,13 +214,17 @@ static int vnc_server_info_put(QDict *qdict)
static void vnc_client_cache_auth(VncState *client)
{
+#if defined(CONFIG_VNC_TLS) || defined(CONFIG_VNC_SASL)
QDict *qdict;
+#endif
if (!client->info) {
return;
}
+#if defined(CONFIG_VNC_TLS) || defined(CONFIG_VNC_SASL)
qdict = qobject_to_qdict(client->info);
+#endif
#ifdef CONFIG_VNC_TLS
if (client->tls.session &&
@@ -351,10 +355,6 @@ void do_info_vnc(Monitor *mon, QObject **ret_data)
}
}
-static inline uint32_t vnc_has_feature(VncState *vs, int feature) {
- return (vs->features & (1 << feature));
-}
-
/* TODO
1) Get the queue working for IO.
2) there is some weirdness when using the -S option (the screen is grey
@@ -363,6 +363,7 @@ static inline uint32_t vnc_has_feature(VncState *vs, int feature) {
*/
static int vnc_update_client(VncState *vs, int has_dirty);
+static int vnc_update_client_sync(VncState *vs, int has_dirty);
static void vnc_disconnect_start(VncState *vs);
static void vnc_disconnect_finish(VncState *vs);
static void vnc_init_timer(VncDisplay *vd);
@@ -506,19 +507,48 @@ static void vnc_desktop_resize(VncState *vs)
}
vs->client_width = ds_get_width(ds);
vs->client_height = ds_get_height(ds);
+ vnc_lock_output(vs);
vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
vnc_write_u8(vs, 0);
vnc_write_u16(vs, 1); /* number of rects */
vnc_framebuffer_update(vs, 0, 0, vs->client_width, vs->client_height,
VNC_ENCODING_DESKTOPRESIZE);
+ vnc_unlock_output(vs);
vnc_flush(vs);
}
+#ifdef CONFIG_VNC_THREAD
+static void vnc_abort_display_jobs(VncDisplay *vd)
+{
+ VncState *vs;
+
+ QTAILQ_FOREACH(vs, &vd->clients, next) {
+ vnc_lock_output(vs);
+ vs->abort = true;
+ vnc_unlock_output(vs);
+ }
+ QTAILQ_FOREACH(vs, &vd->clients, next) {
+ vnc_jobs_join(vs);
+ }
+ QTAILQ_FOREACH(vs, &vd->clients, next) {
+ vnc_lock_output(vs);
+ vs->abort = false;
+ vnc_unlock_output(vs);
+ }
+}
+#else
+static void vnc_abort_display_jobs(VncDisplay *vd)
+{
+}
+#endif
+
static void vnc_dpy_resize(DisplayState *ds)
{
VncDisplay *vd = ds->opaque;
VncState *vs;
+ vnc_abort_display_jobs(vd);
+
/* server surface */
if (!vd->server)
vd->server = qemu_mallocz(sizeof(*vd->server));
@@ -646,7 +676,7 @@ int vnc_raw_send_framebuffer_update(VncState *vs, int x, int y, int w, int h)
return 1;
}
-static int send_framebuffer_update(VncState *vs, int x, int y, int w, int h)
+int vnc_send_framebuffer_update(VncState *vs, int x, int y, int w, int h)
{
int n = 0;
@@ -661,6 +691,9 @@ static int send_framebuffer_update(VncState *vs, int x, int y, int w, int h)
case VNC_ENCODING_TIGHT:
n = vnc_tight_send_framebuffer_update(vs, x, y, w, h);
break;
+ case VNC_ENCODING_TIGHT_PNG:
+ n = vnc_tight_png_send_framebuffer_update(vs, x, y, w, h);
+ break;
default:
vnc_framebuffer_update(vs, x, y, w, h, VNC_ENCODING_RAW);
n = vnc_raw_send_framebuffer_update(vs, x, y, w, h);
@@ -672,12 +705,14 @@ static int send_framebuffer_update(VncState *vs, int x, int y, int w, int h)
static void vnc_copy(VncState *vs, int src_x, int src_y, int dst_x, int dst_y, int w, int h)
{
/* send bitblit op to the vnc client */
+ vnc_lock_output(vs);
vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
vnc_write_u8(vs, 0);
vnc_write_u16(vs, 1); /* number of rects */
vnc_framebuffer_update(vs, dst_x, dst_y, w, h, VNC_ENCODING_COPYRECT);
vnc_write_u16(vs, src_x);
vnc_write_u16(vs, src_y);
+ vnc_unlock_output(vs);
vnc_flush(vs);
}
@@ -694,7 +729,7 @@ static void vnc_dpy_copy(DisplayState *ds, int src_x, int src_y, int dst_x, int
QTAILQ_FOREACH_SAFE(vs, &vd->clients, next, vn) {
if (vnc_has_feature(vs, VNC_FEATURE_COPYRECT)) {
vs->force_update = 1;
- vnc_update_client(vs, 1);
+ vnc_update_client_sync(vs, 1);
/* vs might be free()ed here */
}
}
@@ -765,6 +800,7 @@ static int vnc_cursor_define(VncState *vs)
int isize;
if (vnc_has_feature(vs, VNC_FEATURE_RICH_CURSOR)) {
+ vnc_lock_output(vs);
vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
vnc_write_u8(vs, 0); /* padding */
vnc_write_u16(vs, 1); /* # of rects */
@@ -773,6 +809,7 @@ static int vnc_cursor_define(VncState *vs)
isize = c->width * c->height * vs->clientds.pf.bytes_per_pixel;
vnc_write_pixels_generic(vs, &pf, c->data, isize);
vnc_write(vs, vs->vd->cursor_mask, vs->vd->cursor_msize);
+ vnc_unlock_output(vs);
return 0;
}
return -1;
@@ -814,15 +851,29 @@ static int find_and_clear_dirty_height(struct VncState *vs,
return h;
}
+#ifdef CONFIG_VNC_THREAD
+static int vnc_update_client_sync(VncState *vs, int has_dirty)
+{
+ int ret = vnc_update_client(vs, has_dirty);
+ vnc_jobs_join(vs);
+ return ret;
+}
+#else
+static int vnc_update_client_sync(VncState *vs, int has_dirty)
+{
+ return vnc_update_client(vs, has_dirty);
+}
+#endif
+
static int vnc_update_client(VncState *vs, int has_dirty)
{
if (vs->need_update && vs->csock != -1) {
VncDisplay *vd = vs->vd;
+ VncJob *job;
int y;
- int n_rectangles;
- int saved_offset;
int width, height;
- int n;
+ int n = 0;
+
if (vs->output.offset && !vs->audio_cap && !vs->force_update)
/* kernel send buffers are full -> drop frames to throttle */
@@ -837,11 +888,7 @@ static int vnc_update_client(VncState *vs, int has_dirty)
* happening in parallel don't disturb us, the next pass will
* send them to the client.
*/
- n_rectangles = 0;
- vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
- vnc_write_u8(vs, 0);
- saved_offset = vs->output.offset;
- vnc_write_u16(vs, 0);
+ job = vnc_job_new(vs);
width = MIN(vd->server->width, vs->client_width);
height = MIN(vd->server->height, vs->client_height);
@@ -858,25 +905,23 @@ static int vnc_update_client(VncState *vs, int has_dirty)
} else {
if (last_x != -1) {
int h = find_and_clear_dirty_height(vs, y, last_x, x);
- n = send_framebuffer_update(vs, last_x * 16, y,
- (x - last_x) * 16, h);
- n_rectangles += n;
+
+ n += vnc_job_add_rect(job, last_x * 16, y,
+ (x - last_x) * 16, h);
}
last_x = -1;
}
}
if (last_x != -1) {
int h = find_and_clear_dirty_height(vs, y, last_x, x);
- n = send_framebuffer_update(vs, last_x * 16, y,
- (x - last_x) * 16, h);
- n_rectangles += n;
+ n += vnc_job_add_rect(job, last_x * 16, y,
+ (x - last_x) * 16, h);
}
}
- vs->output.buffer[saved_offset] = (n_rectangles >> 8) & 0xFF;
- vs->output.buffer[saved_offset + 1] = n_rectangles & 0xFF;
- vnc_flush(vs);
+
+ vnc_job_push(job);
vs->force_update = 0;
- return n_rectangles;
+ return n;
}
if (vs->csock == -1)
@@ -892,16 +937,20 @@ static void audio_capture_notify(void *opaque, audcnotification_e cmd)
switch (cmd) {
case AUD_CNOTIFY_DISABLE:
+ vnc_lock_output(vs);
vnc_write_u8(vs, VNC_MSG_SERVER_QEMU);
vnc_write_u8(vs, VNC_MSG_SERVER_QEMU_AUDIO);
vnc_write_u16(vs, VNC_MSG_SERVER_QEMU_AUDIO_END);
+ vnc_unlock_output(vs);
vnc_flush(vs);
break;
case AUD_CNOTIFY_ENABLE:
+ vnc_lock_output(vs);
vnc_write_u8(vs, VNC_MSG_SERVER_QEMU);
vnc_write_u8(vs, VNC_MSG_SERVER_QEMU_AUDIO);
vnc_write_u16(vs, VNC_MSG_SERVER_QEMU_AUDIO_BEGIN);
+ vnc_unlock_output(vs);
vnc_flush(vs);
break;
}
@@ -915,11 +964,13 @@ static void audio_capture(void *opaque, void *buf, int size)
{
VncState *vs = opaque;
+ vnc_lock_output(vs);
vnc_write_u8(vs, VNC_MSG_SERVER_QEMU);
vnc_write_u8(vs, VNC_MSG_SERVER_QEMU_AUDIO);
vnc_write_u16(vs, VNC_MSG_SERVER_QEMU_AUDIO_DATA);
vnc_write_u32(vs, size);
vnc_write(vs, buf, size);
+ vnc_unlock_output(vs);
vnc_flush(vs);
}
@@ -961,6 +1012,9 @@ static void vnc_disconnect_start(VncState *vs)
static void vnc_disconnect_finish(VncState *vs)
{
+ vnc_jobs_join(vs); /* Wait encoding jobs */
+
+ vnc_lock_output(vs);
vnc_qmp_event(vs, QEVENT_VNC_DISCONNECTED);
buffer_free(&vs->input);
@@ -989,6 +1043,11 @@ static void vnc_disconnect_finish(VncState *vs)
vnc_remove_timer(vs->vd);
if (vs->vd->lock_key_sync)
qemu_remove_led_event_handler(vs->led);
+ vnc_unlock_output(vs);
+
+#ifdef CONFIG_VNC_THREAD
+ qemu_mutex_destroy(&vs->output_mutex);
+#endif
qemu_free(vs);
}
@@ -1108,7 +1167,7 @@ static long vnc_client_write_plain(VncState *vs)
* the client socket. Will delegate actual work according to whether
* SASL SSF layers are enabled (thus requiring encryption calls)
*/
-void vnc_client_write(void *opaque)
+static void vnc_client_write_locked(void *opaque)
{
VncState *vs = opaque;
@@ -1122,6 +1181,19 @@ void vnc_client_write(void *opaque)
vnc_client_write_plain(vs);
}
+void vnc_client_write(void *opaque)
+{
+ VncState *vs = opaque;
+
+ vnc_lock_output(vs);
+ if (vs->output.offset) {
+ vnc_client_write_locked(opaque);
+ } else if (vs->csock != -1) {
+ qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs);
+ }
+ vnc_unlock_output(vs);
+}
+
void vnc_read_when(VncState *vs, VncReadEvent *func, size_t expecting)
{
vs->read_handler = func;
@@ -1273,8 +1345,11 @@ void vnc_write_u8(VncState *vs, uint8_t value)
void vnc_flush(VncState *vs)
{
- if (vs->csock != -1 && vs->output.offset)
- vnc_client_write(vs);
+ vnc_lock_output(vs);
+ if (vs->csock != -1 && vs->output.offset) {
+ vnc_client_write_locked(vs);
+ }
+ vnc_unlock_output(vs);
}
uint8_t read_u8(uint8_t *data, size_t offset)
@@ -1309,12 +1384,14 @@ static void check_pointer_type_change(Notifier *notifier)
int absolute = kbd_mouse_is_absolute();
if (vnc_has_feature(vs, VNC_FEATURE_POINTER_TYPE_CHANGE) && vs->absolute != absolute) {
+ vnc_lock_output(vs);
vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
vnc_write_u8(vs, 0);
vnc_write_u16(vs, 1);
vnc_framebuffer_update(vs, absolute, 0,
ds_get_width(vs->ds), ds_get_height(vs->ds),
VNC_ENCODING_POINTER_TYPE_CHANGE);
+ vnc_unlock_output(vs);
vnc_flush(vs);
}
vs->absolute = absolute;
@@ -1618,21 +1695,25 @@ static void framebuffer_update_request(VncState *vs, int incremental,
static void send_ext_key_event_ack(VncState *vs)
{
+ vnc_lock_output(vs);
vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
vnc_write_u8(vs, 0);
vnc_write_u16(vs, 1);
vnc_framebuffer_update(vs, 0, 0, ds_get_width(vs->ds), ds_get_height(vs->ds),
VNC_ENCODING_EXT_KEY_EVENT);
+ vnc_unlock_output(vs);
vnc_flush(vs);
}
static void send_ext_audio_ack(VncState *vs)
{
+ vnc_lock_output(vs);
vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
vnc_write_u8(vs, 0);
vnc_write_u16(vs, 1);
vnc_framebuffer_update(vs, 0, 0, ds_get_width(vs->ds), ds_get_height(vs->ds),
VNC_ENCODING_AUDIO);
+ vnc_unlock_output(vs);
vnc_flush(vs);
}
@@ -1643,8 +1724,8 @@ static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings)
vs->features = 0;
vs->vnc_encoding = 0;
- vs->tight_compression = 9;
- vs->tight_quality = 9;
+ vs->tight.compression = 9;
+ vs->tight.quality = -1; /* Lossless by default */
vs->absolute = -1;
/*
@@ -1669,6 +1750,10 @@ static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings)
vs->features |= VNC_FEATURE_TIGHT_MASK;
vs->vnc_encoding = enc;
break;
+ case VNC_ENCODING_TIGHT_PNG:
+ vs->features |= VNC_FEATURE_TIGHT_PNG_MASK;
+ vs->vnc_encoding = enc;
+ break;
case VNC_ENCODING_ZLIB:
vs->features |= VNC_FEATURE_ZLIB_MASK;
vs->vnc_encoding = enc;
@@ -1692,10 +1777,10 @@ static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings)
vs->features |= VNC_FEATURE_WMVI_MASK;
break;
case VNC_ENCODING_COMPRESSLEVEL0 ... VNC_ENCODING_COMPRESSLEVEL0 + 9:
- vs->tight_compression = (enc & 0x0F);
+ vs->tight.compression = (enc & 0x0F);
break;
case VNC_ENCODING_QUALITYLEVEL0 ... VNC_ENCODING_QUALITYLEVEL0 + 9:
- vs->tight_quality = (enc & 0x0F);
+ vs->tight.quality = (enc & 0x0F);
break;
default:
VNC_DEBUG("Unknown encoding: %d (0x%.8x): %d\n", i, enc, enc);
@@ -1791,12 +1876,14 @@ static void vnc_colordepth(VncState *vs)
{
if (vnc_has_feature(vs, VNC_FEATURE_WMVI)) {
/* Sending a WMVi message to notify the client*/
+ vnc_lock_output(vs);
vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
vnc_write_u8(vs, 0);
vnc_write_u16(vs, 1); /* number of rects */
vnc_framebuffer_update(vs, 0, 0, ds_get_width(vs->ds),
ds_get_height(vs->ds), VNC_ENCODING_WMVi);
pixel_format_message(vs);
+ vnc_unlock_output(vs);
vnc_flush(vs);
} else {
set_pixel_conversion(vs);
@@ -2224,12 +2311,21 @@ static void vnc_refresh(void *opaque)
vga_hw_update();
+ if (vnc_trylock_display(vd)) {
+ vd->timer_interval = VNC_REFRESH_INTERVAL_BASE;
+ qemu_mod_timer(vd->timer, qemu_get_clock(rt_clock) +
+ vd->timer_interval);
+ return;
+ }
+
has_dirty = vnc_refresh_server_surface(vd);
+ vnc_unlock_display(vd);
QTAILQ_FOREACH_SAFE(vs, &vd->clients, next, vn) {
rects += vnc_update_client(vs, has_dirty);
/* vs might be free()ed here */
}
+
/* vd->timer could be NULL now if the last client disconnected,
* in this case don't update the timer */
if (vd->timer == NULL)
@@ -2288,6 +2384,10 @@ static void vnc_connect(VncDisplay *vd, int csock)
vs->as.fmt = AUD_FMT_S16;
vs->as.endianness = 0;
+#ifdef CONFIG_VNC_THREAD
+ qemu_mutex_init(&vs->output_mutex);
+#endif
+
QTAILQ_INSERT_HEAD(&vd->clients, vs, next);
vga_hw_update();
@@ -2345,6 +2445,11 @@ void vnc_display_init(DisplayState *ds)
if (!vs->kbd_layout)
exit(1);
+#ifdef CONFIG_VNC_THREAD
+ qemu_mutex_init(&vs->mutex);
+ vnc_start_worker_thread();
+#endif
+
dcl->dpy_copy = vnc_dpy_copy;
dcl->dpy_update = vnc_dpy_update;
dcl->dpy_resize = vnc_dpy_resize;
@@ -2422,7 +2527,9 @@ int vnc_display_open(DisplayState *ds, const char *display)
int sasl = 0;
int saslErr;
#endif
+#if defined(CONFIG_VNC_TLS) || defined(CONFIG_VNC_SASL)
int acl = 0;
+#endif
int lock_key_sync = 1;
if (!vnc_display)
@@ -2480,8 +2587,12 @@ int vnc_display_open(DisplayState *ds, const char *display)
return -1;
}
#endif
+#if defined(CONFIG_VNC_TLS) || defined(CONFIG_VNC_SASL)
} else if (strncmp(options, "acl", 3) == 0) {
acl = 1;
+#endif
+ } else if (strncmp(options, "lossy", 5) == 0) {
+ vs->lossy = true;
}
}
diff --git a/vnc.h b/ui/vnc.h
index 7b64cf730b..9619b247fb 100644
--- a/vnc.h
+++ b/ui/vnc.h
@@ -29,10 +29,14 @@
#include "qemu-common.h"
#include "qemu-queue.h"
+#ifdef CONFIG_VNC_THREAD
+#include "qemu-thread.h"
+#endif
#include "console.h"
#include "monitor.h"
#include "audio/audio.h"
#include <zlib.h>
+#include <stdbool.h>
#include "keymaps.h"
@@ -58,6 +62,9 @@ typedef struct Buffer
} Buffer;
typedef struct VncState VncState;
+typedef struct VncJob VncJob;
+typedef struct VncRect VncRect;
+typedef struct VncRectEntry VncRectEntry;
typedef int VncReadEvent(VncState *vs, uint8_t *data, size_t len);
@@ -100,6 +107,9 @@ struct VncDisplay
DisplayState *ds;
kbd_layout_t *kbd_layout;
int lock_key_sync;
+#ifdef CONFIG_VNC_THREAD
+ QemuMutex mutex;
+#endif
QEMUCursor *cursor;
int cursor_msize;
@@ -111,6 +121,7 @@ struct VncDisplay
char *display;
char *password;
int auth;
+ bool lossy;
#ifdef CONFIG_VNC_TLS
int subauth; /* Used by VeNCrypt */
VncDisplayTLS tls;
@@ -120,6 +131,67 @@ struct VncDisplay
#endif
};
+typedef struct VncTight {
+ int type;
+ uint8_t quality;
+ uint8_t compression;
+ uint8_t pixel24;
+ Buffer tight;
+ Buffer tmp;
+ Buffer zlib;
+ Buffer gradient;
+#ifdef CONFIG_VNC_JPEG
+ Buffer jpeg;
+#endif
+#ifdef CONFIG_VNC_PNG
+ Buffer png;
+#endif
+ int levels[4];
+ z_stream stream[4];
+} VncTight;
+
+typedef struct VncHextile {
+ VncSendHextileTile *send_tile;
+} VncHextile;
+
+typedef struct VncZlib {
+ Buffer zlib;
+ Buffer tmp;
+ z_stream stream;
+ int level;
+} VncZlib;
+
+#ifdef CONFIG_VNC_THREAD
+struct VncRect
+{
+ int x;
+ int y;
+ int w;
+ int h;
+};
+
+struct VncRectEntry
+{
+ struct VncRect rect;
+ QLIST_ENTRY(VncRectEntry) next;
+};
+
+struct VncJob
+{
+ VncState *vs;
+
+ QLIST_HEAD(, VncRectEntry) rectangles;
+ QTAILQ_ENTRY(VncJob) next;
+};
+#else
+struct VncJob
+{
+ VncState *vs;
+ int rectangles;
+ size_t saved_offset;
+};
+#endif
+
struct VncState
{
int csock;
@@ -167,26 +239,20 @@ struct VncState
uint8_t modifiers_state[256];
QEMUPutLEDEntry *led;
- /* Encoding specific */
-
- /* Tight */
- uint8_t tight_quality;
- uint8_t tight_compression;
- uint8_t tight_pixel24;
- Buffer tight;
- Buffer tight_tmp;
- Buffer tight_zlib;
- int tight_levels[4];
- z_stream tight_stream[4];
+ bool abort;
+#ifndef CONFIG_VNC_THREAD
+ VncJob job;
+#else
+ QemuMutex output_mutex;
+#endif
- /* Hextile */
- VncSendHextileTile *send_hextile_tile;
+ /* Encoding specific, if you add something here, don't forget to
+ * update vnc_async_encoding_start()
+ */
+ VncTight tight;
+ VncZlib zlib;
+ VncHextile hextile;
- /* Zlib */
- Buffer zlib;
- Buffer zlib_tmp;
- z_stream zlib_stream;
- int zlib_level;
Notifier mouse_mode_notifier;
@@ -253,6 +319,7 @@ enum {
#define VNC_ENCODING_POINTER_TYPE_CHANGE 0XFFFFFEFF /* -257 */
#define VNC_ENCODING_EXT_KEY_EVENT 0XFFFFFEFE /* -258 */
#define VNC_ENCODING_AUDIO 0XFFFFFEFD /* -259 */
+#define VNC_ENCODING_TIGHT_PNG 0xFFFFFEFC /* -260 */
#define VNC_ENCODING_WMVi 0x574D5669
/*****************************************************************************
@@ -269,6 +336,7 @@ enum {
#define VNC_TIGHT_CCB_TYPE_MASK (0x0f << 4)
#define VNC_TIGHT_CCB_TYPE_FILL (0x08 << 4)
#define VNC_TIGHT_CCB_TYPE_JPEG (0x09 << 4)
+#define VNC_TIGHT_CCB_TYPE_PNG (0x0A << 4)
#define VNC_TIGHT_CCB_BASIC_MAX (0x07 << 4)
#define VNC_TIGHT_CCB_BASIC_ZLIB (0x03 << 4)
#define VNC_TIGHT_CCB_BASIC_FILTER (0x04 << 4)
@@ -287,6 +355,7 @@ enum {
#define VNC_FEATURE_ZLIB 5
#define VNC_FEATURE_COPYRECT 6
#define VNC_FEATURE_RICH_CURSOR 7
+#define VNC_FEATURE_TIGHT_PNG 8
#define VNC_FEATURE_RESIZE_MASK (1 << VNC_FEATURE_RESIZE)
#define VNC_FEATURE_HEXTILE_MASK (1 << VNC_FEATURE_HEXTILE)
@@ -296,6 +365,7 @@ enum {
#define VNC_FEATURE_ZLIB_MASK (1 << VNC_FEATURE_ZLIB)
#define VNC_FEATURE_COPYRECT_MASK (1 << VNC_FEATURE_COPYRECT)
#define VNC_FEATURE_RICH_CURSOR_MASK (1 << VNC_FEATURE_RICH_CURSOR)
+#define VNC_FEATURE_TIGHT_PNG_MASK (1 << VNC_FEATURE_TIGHT_PNG)
/* Client -> Server message IDs */
@@ -399,6 +469,10 @@ void buffer_append(Buffer *buffer, const void *data, size_t len);
char *vnc_socket_local_addr(const char *format, int fd);
char *vnc_socket_remote_addr(const char *format, int fd);
+static inline uint32_t vnc_has_feature(VncState *vs, int feature) {
+ return (vs->features & (1 << feature));
+}
+
/* Framebuffer */
void vnc_framebuffer_update(VncState *vs, int x, int y, int w, int h,
int32_t encoding);
@@ -406,6 +480,8 @@ void vnc_framebuffer_update(VncState *vs, int x, int y, int w, int h,
void vnc_convert_pixel(VncState *vs, uint8_t *buf, uint32_t v);
/* Encodings */
+int vnc_send_framebuffer_update(VncState *vs, int x, int y, int w, int h);
+
int vnc_raw_send_framebuffer_update(VncState *vs, int x, int y, int w, int h);
int vnc_hextile_send_framebuffer_update(VncState *vs, int x,
@@ -417,8 +493,9 @@ void vnc_zlib_zfree(void *x, void *addr);
int vnc_zlib_send_framebuffer_update(VncState *vs, int x, int y, int w, int h);
void vnc_zlib_clear(VncState *vs);
-
int vnc_tight_send_framebuffer_update(VncState *vs, int x, int y, int w, int h);
+int vnc_tight_png_send_framebuffer_update(VncState *vs, int x, int y,
+ int w, int h);
void vnc_tight_clear(VncState *vs);
#endif /* __QEMU_VNC_H */
diff --git a/vnc_keysym.h b/ui/vnc_keysym.h
index 55cb87edec..55cb87edec 100644
--- a/vnc_keysym.h
+++ b/ui/vnc_keysym.h
diff --git a/x_keymap.c b/ui/x_keymap.c
index b9b0944180..b9b0944180 100644
--- a/x_keymap.c
+++ b/ui/x_keymap.c
diff --git a/x_keymap.h b/ui/x_keymap.h
index 2042ce0ed2..2042ce0ed2 100644
--- a/x_keymap.h
+++ b/ui/x_keymap.h
diff --git a/version.rc b/version.rc
new file mode 100644
index 0000000000..82e10ecf26
--- /dev/null
+++ b/version.rc
@@ -0,0 +1,28 @@
+#include <winver.h>
+#include "config-host.h"
+
+VS_VERSION_INFO VERSIONINFO
+FILEVERSION CONFIG_FILEVERSION
+PRODUCTVERSION CONFIG_PRODUCTVERSION
+FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+FILEOS VOS_NT_WINDOWS32
+FILETYPE VFT_APP
+FILESUBTYPE VFT2_UNKNOWN
+{
+ BLOCK "StringFileInfo"
+ {
+ BLOCK "040904E4"
+ {
+ VALUE "CompanyName", "http://www.qemu.org"
+ VALUE "FileDescription", "QEMU machine emulators and tools"
+ VALUE "FileVersion", QEMU_VERSION
+ VALUE "LegalCopyright", "Copyright various authors. Released under the GNU General Public License."
+ VALUE "LegalTrademarks", "QEMU is a trademark of Fabrice Bellard."
+ VALUE "ProductName", "QEMU"
+ }
+ }
+ BLOCK "VarFileInfo"
+ {
+ VALUE "Translation", 0x0409, 1252
+ }
+}
diff --git a/vl.c b/vl.c
index 8a5de9fac1..7038952dc6 100644
--- a/vl.c
+++ b/vl.c
@@ -47,6 +47,10 @@
#include <dirent.h>
#include <netdb.h>
#include <sys/select.h>
+#ifdef CONFIG_SIMPLE_TRACE
+#include "trace.h"
+#endif
+
#ifdef CONFIG_BSD
#include <sys/stat.h>
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
@@ -76,9 +80,6 @@
#include <net/if.h>
#include <syslog.h>
#include <stropts.h>
-/* See MySQL bug #7156 (http://bugs.mysql.com/bug.php?id=7156) for
- discussion about Solaris header problems */
-extern int madvise(caddr_t, size_t, int);
#endif
#endif
#endif
@@ -161,6 +162,8 @@ int main(int argc, char **argv)
#include "cpus.h"
#include "arch_init.h"
+#include "ui/qemu-spice.h"
+
//#define DEBUG_NET
//#define DEBUG_SLIRP
@@ -172,6 +175,7 @@ static const char *data_dir;
const char *bios_name = NULL;
enum vga_retrace_method vga_retrace_method = VGA_RETRACE_DUMB;
DisplayType display_type = DT_DEFAULT;
+int display_remote = 0;
const char* keyboard_layout = NULL;
ram_addr_t ram_size;
const char *mem_path = NULL;
@@ -182,6 +186,7 @@ int nb_nics;
NICInfo nd_table[MAX_NICS];
int vm_running;
int autostart;
+int incoming_expected; /* Started with -incoming and waiting for incoming */
static int rtc_utc = 1;
static int rtc_date_offset = -1; /* -1 means no change */
QEMUClock *rtc_clock;
@@ -284,30 +289,6 @@ static int default_driver_check(QemuOpts *opts, void *opaque)
/***********************************************************/
/* real time host monotonic timer */
-/* compute with 96 bit intermediate result: (a*b)/c */
-uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c)
-{
- union {
- uint64_t ll;
- struct {
-#ifdef HOST_WORDS_BIGENDIAN
- uint32_t high, low;
-#else
- uint32_t low, high;
-#endif
- } l;
- } u, res;
- uint64_t rl, rh;
-
- u.ll = a;
- rl = (uint64_t)u.l.low * (uint64_t)b;
- rh = (uint64_t)u.l.high * (uint64_t)b;
- rh += (rl >> 32);
- res.l.high = rh / c;
- res.l.low = (((rh % c) << 32) + (rl & 0xffffffff)) / c;
- return res.ll;
-}
-
/***********************************************************/
/* host time/date access */
void qemu_get_timedate(struct tm *tm, int offset)
@@ -801,9 +782,7 @@ static void smp_parse(const char *optarg)
threads = threads > 0 ? threads : 1;
cores = smp / (sockets * threads);
} else {
- if (sockets) {
- threads = smp / (cores * sockets);
- }
+ threads = smp / (cores * sockets);
}
}
smp_cpus = smp;
@@ -1325,7 +1304,7 @@ static void main_loop(void)
int64_t ti;
#endif
#ifndef CONFIG_IOTHREAD
- nonblocking = tcg_cpu_exec();
+ nonblocking = cpu_exec_all();
#endif
#ifdef CONFIG_PROFILER
ti = profile_getclock();
@@ -1462,12 +1441,12 @@ static int balloon_parse(const char *arg)
if (!strncmp(arg, "virtio", 6)) {
if (arg[6] == ',') {
/* have params -> parse them */
- opts = qemu_opts_parse(&qemu_device_opts, arg+7, 0);
+ opts = qemu_opts_parse(qemu_find_opts("device"), arg+7, 0);
if (!opts)
return -1;
} else {
/* create empty opts */
- opts = qemu_opts_create(&qemu_device_opts, NULL, 0);
+ opts = qemu_opts_create(qemu_find_opts("device"), NULL, 0);
}
qemu_opt_set(opts, "driver", "virtio-balloon-pci");
return 0;
@@ -1562,6 +1541,9 @@ static int mon_init_func(QemuOpts *opts, void *opaque)
exit(1);
}
+ if (qemu_opt_get_bool(opts, "pretty", 0))
+ flags |= MONITOR_USE_PRETTY;
+
if (qemu_opt_get_bool(opts, "default", 0))
flags |= MONITOR_IS_DEFAULT;
@@ -1599,7 +1581,7 @@ static void monitor_parse(const char *optarg, const char *mode)
}
}
- opts = qemu_opts_create(&qemu_mon_opts, label, 1);
+ opts = qemu_opts_create(qemu_find_opts("mon"), label, 1);
if (!opts) {
fprintf(stderr, "duplicate chardev: %s\n", label);
exit(1);
@@ -1696,6 +1678,7 @@ static int parallel_parse(const char *devname)
static int virtcon_parse(const char *devname)
{
+ QemuOptsList *device = qemu_find_opts("device");
static int index = 0;
char label[32];
QemuOpts *bus_opts, *dev_opts;
@@ -1707,10 +1690,10 @@ static int virtcon_parse(const char *devname)
exit(1);
}
- bus_opts = qemu_opts_create(&qemu_device_opts, NULL, 0);
+ bus_opts = qemu_opts_create(device, NULL, 0);
qemu_opt_set(bus_opts, "driver", "virtio-serial");
- dev_opts = qemu_opts_create(&qemu_device_opts, NULL, 0);
+ dev_opts = qemu_opts_create(device, NULL, 0);
qemu_opt_set(dev_opts, "driver", "virtconsole");
snprintf(label, sizeof(label), "virtcon%d", index);
@@ -1733,7 +1716,7 @@ static int debugcon_parse(const char *devname)
if (!qemu_chr_open("debugcon", devname, NULL)) {
exit(1);
}
- opts = qemu_opts_create(&qemu_device_opts, "debugcon", 1);
+ opts = qemu_opts_create(qemu_find_opts("device"), "debugcon", 1);
if (!opts) {
fprintf(stderr, "qemu: already have a debugcon device\n");
exit(1);
@@ -1811,6 +1794,7 @@ int main(int argc, char **argv, char **envp)
DisplayChangeListener *dcl;
int cyls, heads, secs, translation;
QemuOpts *hda_opts = NULL, *opts;
+ QemuOptsList *olist;
int optind;
const char *optarg;
const char *loadvm = NULL;
@@ -1822,6 +1806,9 @@ int main(int argc, char **argv, char **envp)
int show_vnc_port = 0;
int defconfig = 1;
+#ifdef CONFIG_SIMPLE_TRACE
+ const char *trace_file = NULL;
+#endif
atexit(qemu_run_exit_notifiers);
error_set_progname(argv[0]);
@@ -2105,12 +2092,12 @@ int main(int argc, char **argv, char **envp)
fd_bootchk = 0;
break;
case QEMU_OPTION_netdev:
- if (net_client_parse(&qemu_netdev_opts, optarg) == -1) {
+ if (net_client_parse(qemu_find_opts("netdev"), optarg) == -1) {
exit(1);
}
break;
case QEMU_OPTION_net:
- if (net_client_parse(&qemu_net_opts, optarg) == -1) {
+ if (net_client_parse(qemu_find_opts("net"), optarg) == -1) {
exit(1);
}
break;
@@ -2269,21 +2256,25 @@ int main(int argc, char **argv, char **envp)
default_monitor = 0;
break;
case QEMU_OPTION_mon:
- opts = qemu_opts_parse(&qemu_mon_opts, optarg, 1);
+ opts = qemu_opts_parse(qemu_find_opts("mon"), optarg, 1);
if (!opts) {
exit(1);
}
default_monitor = 0;
break;
case QEMU_OPTION_chardev:
- opts = qemu_opts_parse(&qemu_chardev_opts, optarg, 1);
+ opts = qemu_opts_parse(qemu_find_opts("chardev"), optarg, 1);
if (!opts) {
exit(1);
}
break;
-#ifdef CONFIG_VIRTFS
case QEMU_OPTION_fsdev:
- opts = qemu_opts_parse(&qemu_fsdev_opts, optarg, 1);
+ olist = qemu_find_opts("fsdev");
+ if (!olist) {
+ fprintf(stderr, "fsdev is not supported by this qemu build.\n");
+ exit(1);
+ }
+ opts = qemu_opts_parse(olist, optarg, 1);
if (!opts) {
fprintf(stderr, "parse error: %s\n", optarg);
exit(1);
@@ -2294,7 +2285,12 @@ int main(int argc, char **argv, char **envp)
char *arg_9p = NULL;
int len = 0;
- opts = qemu_opts_parse(&qemu_virtfs_opts, optarg, 1);
+ olist = qemu_find_opts("virtfs");
+ if (!olist) {
+ fprintf(stderr, "virtfs is not supported by this qemu build.\n");
+ exit(1);
+ }
+ opts = qemu_opts_parse(olist, optarg, 1);
if (!opts) {
fprintf(stderr, "parse error: %s\n", optarg);
exit(1);
@@ -2305,7 +2301,7 @@ int main(int argc, char **argv, char **envp)
qemu_opt_get(opts, "path") == NULL ||
qemu_opt_get(opts, "security_model") == NULL) {
fprintf(stderr, "Usage: -virtfs fstype,path=/share_path/,"
- "security_model=[mapped|passthrough],"
+ "security_model=[mapped|passthrough|none],"
"mnt_tag=tag.\n");
exit(1);
}
@@ -2317,38 +2313,28 @@ int main(int argc, char **argv, char **envp)
len += strlen(qemu_opt_get(opts, "security_model"));
arg_fsdev = qemu_malloc((len + 1) * sizeof(*arg_fsdev));
- if (!arg_fsdev) {
- fprintf(stderr, "No memory to parse -fsdev for %s\n",
- optarg);
- exit(1);
- }
-
- sprintf(arg_fsdev, "%s,id=%s,path=%s,security_model=%s",
- qemu_opt_get(opts, "fstype"),
- qemu_opt_get(opts, "mount_tag"),
- qemu_opt_get(opts, "path"),
- qemu_opt_get(opts, "security_model"));
+ snprintf(arg_fsdev, (len + 1) * sizeof(*arg_fsdev),
+ "%s,id=%s,path=%s,security_model=%s",
+ qemu_opt_get(opts, "fstype"),
+ qemu_opt_get(opts, "mount_tag"),
+ qemu_opt_get(opts, "path"),
+ qemu_opt_get(opts, "security_model"));
len = strlen("virtio-9p-pci,fsdev=,mount_tag=");
len += 2*strlen(qemu_opt_get(opts, "mount_tag"));
arg_9p = qemu_malloc((len + 1) * sizeof(*arg_9p));
- if (!arg_9p) {
- fprintf(stderr, "No memory to parse -device for %s\n",
- optarg);
- exit(1);
- }
-
- sprintf(arg_9p, "virtio-9p-pci,fsdev=%s,mount_tag=%s",
- qemu_opt_get(opts, "mount_tag"),
- qemu_opt_get(opts, "mount_tag"));
+ snprintf(arg_9p, (len + 1) * sizeof(*arg_9p),
+ "virtio-9p-pci,fsdev=%s,mount_tag=%s",
+ qemu_opt_get(opts, "mount_tag"),
+ qemu_opt_get(opts, "mount_tag"));
- if (!qemu_opts_parse(&qemu_fsdev_opts, arg_fsdev, 1)) {
+ if (!qemu_opts_parse(qemu_find_opts("fsdev"), arg_fsdev, 1)) {
fprintf(stderr, "parse error [fsdev]: %s\n", optarg);
exit(1);
}
- if (!qemu_opts_parse(&qemu_device_opts, arg_9p, 1)) {
+ if (!qemu_opts_parse(qemu_find_opts("device"), arg_9p, 1)) {
fprintf(stderr, "parse error [device]: %s\n", optarg);
exit(1);
}
@@ -2357,7 +2343,6 @@ int main(int argc, char **argv, char **envp)
qemu_free(arg_9p);
break;
}
-#endif
case QEMU_OPTION_serial:
add_device_config(DEV_SERIAL, optarg);
default_serial = 0;
@@ -2445,7 +2430,7 @@ int main(int argc, char **argv, char **envp)
add_device_config(DEV_USB, optarg);
break;
case QEMU_OPTION_device:
- if (!qemu_opts_parse(&qemu_device_opts, optarg, 1)) {
+ if (!qemu_opts_parse(qemu_find_opts("device"), optarg, 1)) {
exit(1);
}
break;
@@ -2466,7 +2451,7 @@ int main(int argc, char **argv, char **envp)
}
break;
case QEMU_OPTION_vnc:
- display_type = DT_VNC;
+ display_remote++;
vnc_display = optarg;
break;
case QEMU_OPTION_no_acpi:
@@ -2541,7 +2526,7 @@ int main(int argc, char **argv, char **envp)
configure_rtc_date_offset(optarg, 1);
break;
case QEMU_OPTION_rtc:
- opts = qemu_opts_parse(&qemu_rtc_opts, optarg, 0);
+ opts = qemu_opts_parse(qemu_find_opts("rtc"), optarg, 0);
if (!opts) {
exit(1);
}
@@ -2557,6 +2542,7 @@ int main(int argc, char **argv, char **envp)
break;
case QEMU_OPTION_incoming:
incoming = optarg;
+ incoming_expected = true;
break;
case QEMU_OPTION_nodefaults:
default_serial = 0;
@@ -2590,6 +2576,14 @@ int main(int argc, char **argv, char **envp)
}
xen_mode = XEN_ATTACH;
break;
+#ifdef CONFIG_SIMPLE_TRACE
+ case QEMU_OPTION_trace:
+ opts = qemu_opts_parse(qemu_find_opts("trace"), optarg, 0);
+ if (opts) {
+ trace_file = qemu_opt_get(opts, "file");
+ }
+ break;
+#endif
case QEMU_OPTION_readconfig:
{
int ret = qemu_read_config_file(optarg);
@@ -2600,6 +2594,18 @@ int main(int argc, char **argv, char **envp)
}
break;
}
+ case QEMU_OPTION_spice:
+ olist = qemu_find_opts("spice");
+ if (!olist) {
+ fprintf(stderr, "spice is not supported by this qemu build.\n");
+ exit(1);
+ }
+ opts = qemu_opts_parse(olist, optarg, 0);
+ if (!opts) {
+ fprintf(stderr, "parse error: %s\n", optarg);
+ exit(1);
+ }
+ break;
case QEMU_OPTION_writeconfig:
{
FILE *fp;
@@ -2633,6 +2639,12 @@ int main(int argc, char **argv, char **envp)
data_dir = CONFIG_QEMU_DATADIR;
}
+#ifdef CONFIG_SIMPLE_TRACE
+ /*
+ * Set the trace file name, if specified.
+ */
+ st_set_trace_file(trace_file);
+#endif
/*
* Default to max_cpus = smp_cpus, in case the user doesn't
* specify a max_cpus value.
@@ -2648,8 +2660,8 @@ int main(int argc, char **argv, char **envp)
exit(1);
}
- qemu_opts_foreach(&qemu_device_opts, default_driver_check, NULL, 0);
- qemu_opts_foreach(&qemu_global_opts, default_driver_check, NULL, 0);
+ qemu_opts_foreach(qemu_find_opts("device"), default_driver_check, NULL, 0);
+ qemu_opts_foreach(qemu_find_opts("global"), default_driver_check, NULL, 0);
if (machine->no_serial) {
default_serial = 0;
@@ -2703,10 +2715,10 @@ int main(int argc, char **argv, char **envp)
socket_init();
- if (qemu_opts_foreach(&qemu_chardev_opts, chardev_init_func, NULL, 1) != 0)
+ if (qemu_opts_foreach(qemu_find_opts("chardev"), chardev_init_func, NULL, 1) != 0)
exit(1);
#ifdef CONFIG_VIRTFS
- if (qemu_opts_foreach(&qemu_fsdev_opts, fsdev_init_func, NULL, 1) != 0) {
+ if (qemu_opts_foreach(qemu_find_opts("fsdev"), fsdev_init_func, NULL, 1) != 0) {
exit(1);
}
#endif
@@ -2790,8 +2802,8 @@ int main(int argc, char **argv, char **envp)
/* open the virtual block devices */
if (snapshot)
- qemu_opts_foreach(&qemu_drive_opts, drive_enable_snapshot, NULL, 0);
- if (qemu_opts_foreach(&qemu_drive_opts, drive_init_func, &machine->use_scsi, 1) != 0)
+ qemu_opts_foreach(qemu_find_opts("drive"), drive_enable_snapshot, NULL, 0);
+ if (qemu_opts_foreach(qemu_find_opts("drive"), drive_init_func, &machine->use_scsi, 1) != 0)
exit(1);
register_savevm_live(NULL, "ram", 0, 4, NULL, ram_save_live, NULL,
@@ -2839,7 +2851,7 @@ int main(int argc, char **argv, char **envp)
}
}
- if (qemu_opts_foreach(&qemu_mon_opts, mon_init_func, NULL, 1) != 0) {
+ if (qemu_opts_foreach(qemu_find_opts("mon"), mon_init_func, NULL, 1) != 0) {
exit(1);
}
@@ -2854,7 +2866,7 @@ int main(int argc, char **argv, char **envp)
module_call_init(MODULE_INIT_DEVICE);
- if (qemu_opts_foreach(&qemu_device_opts, device_help_func, NULL, 0) != 0)
+ if (qemu_opts_foreach(qemu_find_opts("device"), device_help_func, NULL, 0) != 0)
exit(0);
if (watchdog) {
@@ -2887,7 +2899,7 @@ int main(int argc, char **argv, char **envp)
}
/* init generic devices */
- if (qemu_opts_foreach(&qemu_device_opts, device_init_func, NULL, 1) != 0)
+ if (qemu_opts_foreach(qemu_find_opts("device"), device_init_func, NULL, 1) != 0)
exit(1);
net_check_clients();
@@ -2895,17 +2907,19 @@ int main(int argc, char **argv, char **envp)
/* just use the first displaystate for the moment */
ds = get_displaystate();
- if (display_type == DT_DEFAULT) {
+ if (using_spice)
+ display_remote++;
+ if (display_type == DT_DEFAULT && !display_remote) {
#if defined(CONFIG_SDL) || defined(CONFIG_COCOA)
display_type = DT_SDL;
#else
- display_type = DT_VNC;
vnc_display = "localhost:0,to=99";
show_vnc_port = 1;
#endif
}
+ /* init local displays */
switch (display_type) {
case DT_NOGRAPHIC:
break;
@@ -2923,7 +2937,12 @@ int main(int argc, char **argv, char **envp)
cocoa_display_init(ds, full_screen);
break;
#endif
- case DT_VNC:
+ default:
+ break;
+ }
+
+ /* init remote displays */
+ if (vnc_display) {
vnc_display_init(ds);
if (vnc_display_open(ds, vnc_display) < 0)
exit(1);
@@ -2931,12 +2950,15 @@ int main(int argc, char **argv, char **envp)
if (show_vnc_port) {
printf("VNC server running on `%s'\n", vnc_display_local_addr(ds));
}
- break;
- default:
- break;
}
- dpy_resize(ds);
+#ifdef CONFIG_SPICE
+ if (using_spice) {
+ qemu_spice_display_init(ds);
+ }
+#endif
+ /* display setup */
+ dpy_resize(ds);
dcl = ds->listeners;
while (dcl != NULL) {
if (dcl->dpy_refresh != NULL) {
@@ -2946,12 +2968,10 @@ int main(int argc, char **argv, char **envp)
}
dcl = dcl->next;
}
-
- if (display_type == DT_NOGRAPHIC || display_type == DT_VNC) {
+ if (ds->gui_timer == NULL) {
nographic_timer = qemu_new_timer(rt_clock, nographic_update, NULL);
qemu_mod_timer(nographic_timer, qemu_get_clock(rt_clock));
}
-
text_consoles_set_display(ds);
if (gdbstub_dev && gdbserver_start(gdbstub_dev) < 0) {
diff --git a/vnc-encoding-tight.c b/vnc-encoding-tight.c
deleted file mode 100644
index faba4834cd..0000000000
--- a/vnc-encoding-tight.c
+++ /dev/null
@@ -1,959 +0,0 @@
-/*
- * QEMU VNC display driver: tight encoding
- *
- * From libvncserver/libvncserver/tight.c
- * Copyright (C) 2000, 2001 Const Kaplinsky. All Rights Reserved.
- * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
- *
- * Copyright (C) 2010 Corentin Chary <corentin.chary@gmail.com>
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-#include "qdict.h"
-#include "qint.h"
-#include "vnc.h"
-#include "vnc-encoding-tight.h"
-
-/* Compression level stuff. The following array contains various
- encoder parameters for each of 10 compression levels (0..9).
- Last three parameters correspond to JPEG quality levels (0..9). */
-
-static const struct {
- int max_rect_size, max_rect_width;
- int mono_min_rect_size, gradient_min_rect_size;
- int idx_zlib_level, mono_zlib_level, raw_zlib_level, gradient_zlib_level;
- int gradient_threshold, gradient_threshold24;
- int idx_max_colors_divisor;
- int jpeg_quality, jpeg_threshold, jpeg_threshold24;
-} tight_conf[] = {
- { 512, 32, 6, 65536, 0, 0, 0, 0, 0, 0, 4, 5, 10000, 23000 },
- { 2048, 128, 6, 65536, 1, 1, 1, 0, 0, 0, 8, 10, 8000, 18000 },
- { 6144, 256, 8, 65536, 3, 3, 2, 0, 0, 0, 24, 15, 6500, 15000 },
- { 10240, 1024, 12, 65536, 5, 5, 3, 0, 0, 0, 32, 25, 5000, 12000 },
- { 16384, 2048, 12, 65536, 6, 6, 4, 0, 0, 0, 32, 37, 4000, 10000 },
- { 32768, 2048, 12, 4096, 7, 7, 5, 4, 150, 380, 32, 50, 3000, 8000 },
- { 65536, 2048, 16, 4096, 7, 7, 6, 4, 170, 420, 48, 60, 2000, 5000 },
- { 65536, 2048, 16, 4096, 8, 8, 7, 5, 180, 450, 64, 70, 1000, 2500 },
- { 65536, 2048, 32, 8192, 9, 9, 8, 6, 190, 475, 64, 75, 500, 1200 },
- { 65536, 2048, 32, 8192, 9, 9, 9, 6, 200, 500, 96, 80, 200, 500 }
-};
-
-/*
- * Code to determine how many different colors used in rectangle.
- */
-
-static void tight_palette_rgb2buf(uint32_t rgb, int bpp, uint8_t buf[6])
-{
- memset(buf, 0, 6);
-
- if (bpp == 32) {
- buf[0] = ((rgb >> 24) & 0xFF);
- buf[1] = ((rgb >> 16) & 0xFF);
- buf[2] = ((rgb >> 8) & 0xFF);
- buf[3] = ((rgb >> 0) & 0xFF);
- buf[4] = ((buf[0] & 1) == 0) << 3 | ((buf[1] & 1) == 0) << 2;
- buf[4]|= ((buf[2] & 1) == 0) << 1 | ((buf[3] & 1) == 0) << 0;
- buf[0] |= 1;
- buf[1] |= 1;
- buf[2] |= 1;
- buf[3] |= 1;
- }
- if (bpp == 16) {
- buf[0] = ((rgb >> 8) & 0xFF);
- buf[1] = ((rgb >> 0) & 0xFF);
- buf[2] = ((buf[0] & 1) == 0) << 1 | ((buf[1] & 1) == 0) << 0;
- buf[0] |= 1;
- buf[1] |= 1;
- }
-}
-
-static uint32_t tight_palette_buf2rgb(int bpp, const uint8_t *buf)
-{
- uint32_t rgb = 0;
-
- if (bpp == 32) {
- rgb |= ((buf[0] & ~1) | !((buf[4] >> 3) & 1)) << 24;
- rgb |= ((buf[1] & ~1) | !((buf[4] >> 2) & 1)) << 16;
- rgb |= ((buf[2] & ~1) | !((buf[4] >> 1) & 1)) << 8;
- rgb |= ((buf[3] & ~1) | !((buf[4] >> 0) & 1)) << 0;
- }
- if (bpp == 16) {
- rgb |= ((buf[0] & ~1) | !((buf[2] >> 1) & 1)) << 8;
- rgb |= ((buf[1] & ~1) | !((buf[2] >> 0) & 1)) << 0;
- }
- return rgb;
-}
-
-
-static int tight_palette_insert(QDict *palette, uint32_t rgb, int bpp, int max)
-{
- uint8_t key[6];
- int idx = qdict_size(palette);
- bool present;
-
- tight_palette_rgb2buf(rgb, bpp, key);
- present = qdict_haskey(palette, (char *)key);
- if (idx >= max && !present) {
- return 0;
- }
- if (!present) {
- qdict_put(palette, (char *)key, qint_from_int(idx));
- }
- return qdict_size(palette);
-}
-
-#define DEFINE_FILL_PALETTE_FUNCTION(bpp) \
- \
- static int \
- tight_fill_palette##bpp(VncState *vs, int x, int y, \
- int max, size_t count, \
- uint32_t *bg, uint32_t *fg, \
- struct QDict **palette) { \
- uint##bpp##_t *data; \
- uint##bpp##_t c0, c1, ci; \
- int i, n0, n1; \
- \
- data = (uint##bpp##_t *)vs->tight.buffer; \
- \
- c0 = data[0]; \
- i = 1; \
- while (i < count && data[i] == c0) \
- i++; \
- if (i >= count) { \
- *bg = *fg = c0; \
- return 1; \
- } \
- \
- if (max < 2) { \
- return 0; \
- } \
- \
- n0 = i; \
- c1 = data[i]; \
- n1 = 0; \
- for (i++; i < count; i++) { \
- ci = data[i]; \
- if (ci == c0) { \
- n0++; \
- } else if (ci == c1) { \
- n1++; \
- } else \
- break; \
- } \
- if (i >= count) { \
- if (n0 > n1) { \
- *bg = (uint32_t)c0; \
- *fg = (uint32_t)c1; \
- } else { \
- *bg = (uint32_t)c1; \
- *fg = (uint32_t)c0; \
- } \
- return 2; \
- } \
- \
- if (max == 2) { \
- return 0; \
- } \
- \
- *palette = qdict_new(); \
- tight_palette_insert(*palette, c0, bpp, max); \
- tight_palette_insert(*palette, c1, bpp, max); \
- tight_palette_insert(*palette, ci, bpp, max); \
- \
- for (i++; i < count; i++) { \
- if (data[i] == ci) { \
- continue; \
- } else { \
- if (!tight_palette_insert(*palette, (uint32_t)ci, \
- bpp, max)) { \
- return 0; \
- } \
- ci = data[i]; \
- } \
- } \
- \
- return qdict_size(*palette); \
- }
-
-DEFINE_FILL_PALETTE_FUNCTION(8)
-DEFINE_FILL_PALETTE_FUNCTION(16)
-DEFINE_FILL_PALETTE_FUNCTION(32)
-
-static int tight_fill_palette(VncState *vs, int x, int y,
- size_t count, uint32_t *bg, uint32_t *fg,
- struct QDict **palette)
-{
- int max;
-
- max = count / tight_conf[vs->tight_compression].idx_max_colors_divisor;
- if (max < 2 &&
- count >= tight_conf[vs->tight_compression].mono_min_rect_size) {
- max = 2;
- }
- if (max >= 256) {
- max = 256;
- }
-
- switch(vs->clientds.pf.bytes_per_pixel) {
- case 4:
- return tight_fill_palette32(vs, x, y, max, count, bg, fg, palette);
- case 2:
- return tight_fill_palette16(vs, x, y, max, count, bg, fg, palette);
- default:
- max = 2;
- return tight_fill_palette8(vs, x, y, max, count, bg, fg, palette);
- }
- return 0;
-}
-
-/* Callback to dump a palette with qdict_iter
-static void print_palette(const char *key, QObject *obj, void *opaque)
-{
- uint8_t idx = qint_get_int(qobject_to_qint(obj));
- uint32_t rgb = tight_palette_buf2rgb(32, (uint8_t *)key);
-
- fprintf(stderr, "%.2x ", (unsigned char)*key);
- while (*key++)
- fprintf(stderr, "%.2x ", (unsigned char)*key);
-
- fprintf(stderr, ": idx: %x rgb: %x\n", idx, rgb);
-}
-*/
-
-/*
- * Converting truecolor samples into palette indices.
- */
-#define DEFINE_IDX_ENCODE_FUNCTION(bpp) \
- \
- static void \
- tight_encode_indexed_rect##bpp(uint8_t *buf, int count, \
- struct QDict *palette) { \
- uint##bpp##_t *src; \
- uint##bpp##_t rgb; \
- uint8_t key[6]; \
- int i, rep; \
- uint8_t idx; \
- \
- src = (uint##bpp##_t *) buf; \
- \
- for (i = 0; i < count; i++) { \
- rgb = *src++; \
- rep = 0; \
- while (i < count && *src == rgb) { \
- rep++, src++, i++; \
- } \
- tight_palette_rgb2buf(rgb, bpp, key); \
- if (!qdict_haskey(palette, (char *)key)) { \
- /* \
- * Should never happen, but don't break everything \
- * if it does, use the first color instead \
- */ \
- idx = 0; \
- } else { \
- idx = qdict_get_int(palette, (char *)key); \
- } \
- while (rep >= 0) { \
- *buf++ = idx; \
- rep--; \
- } \
- } \
- }
-
-DEFINE_IDX_ENCODE_FUNCTION(16)
-DEFINE_IDX_ENCODE_FUNCTION(32)
-
-#define DEFINE_MONO_ENCODE_FUNCTION(bpp) \
- \
- static void \
- tight_encode_mono_rect##bpp(uint8_t *buf, int w, int h, \
- uint##bpp##_t bg, uint##bpp##_t fg) { \
- uint##bpp##_t *ptr; \
- unsigned int value, mask; \
- int aligned_width; \
- int x, y, bg_bits; \
- \
- ptr = (uint##bpp##_t *) buf; \
- aligned_width = w - w % 8; \
- \
- for (y = 0; y < h; y++) { \
- for (x = 0; x < aligned_width; x += 8) { \
- for (bg_bits = 0; bg_bits < 8; bg_bits++) { \
- if (*ptr++ != bg) { \
- break; \
- } \
- } \
- if (bg_bits == 8) { \
- *buf++ = 0; \
- continue; \
- } \
- mask = 0x80 >> bg_bits; \
- value = mask; \
- for (bg_bits++; bg_bits < 8; bg_bits++) { \
- mask >>= 1; \
- if (*ptr++ != bg) { \
- value |= mask; \
- } \
- } \
- *buf++ = (uint8_t)value; \
- } \
- \
- mask = 0x80; \
- value = 0; \
- if (x >= w) { \
- continue; \
- } \
- \
- for (; x < w; x++) { \
- if (*ptr++ != bg) { \
- value |= mask; \
- } \
- mask >>= 1; \
- } \
- *buf++ = (uint8_t)value; \
- } \
- }
-
-DEFINE_MONO_ENCODE_FUNCTION(8)
-DEFINE_MONO_ENCODE_FUNCTION(16)
-DEFINE_MONO_ENCODE_FUNCTION(32)
-
-/*
- * Check if a rectangle is all of the same color. If needSameColor is
- * set to non-zero, then also check that its color equals to the
- * *colorPtr value. The result is 1 if the test is successfull, and in
- * that case new color will be stored in *colorPtr.
- */
-
-#define DEFINE_CHECK_SOLID_FUNCTION(bpp) \
- \
- static bool \
- check_solid_tile##bpp(VncState *vs, int x, int y, int w, int h, \
- uint32_t* color, bool samecolor) \
- { \
- VncDisplay *vd = vs->vd; \
- uint##bpp##_t *fbptr; \
- uint##bpp##_t c; \
- int dx, dy; \
- \
- fbptr = (uint##bpp##_t *) \
- (vd->server->data + y * ds_get_linesize(vs->ds) + \
- x * ds_get_bytes_per_pixel(vs->ds)); \
- \
- c = *fbptr; \
- if (samecolor && (uint32_t)c != *color) { \
- return false; \
- } \
- \
- for (dy = 0; dy < h; dy++) { \
- for (dx = 0; dx < w; dx++) { \
- if (c != fbptr[dx]) { \
- return false; \
- } \
- } \
- fbptr = (uint##bpp##_t *) \
- ((uint8_t *)fbptr + ds_get_linesize(vs->ds)); \
- } \
- \
- *color = (uint32_t)c; \
- return true; \
- }
-
-DEFINE_CHECK_SOLID_FUNCTION(32)
-DEFINE_CHECK_SOLID_FUNCTION(16)
-DEFINE_CHECK_SOLID_FUNCTION(8)
-
-static bool check_solid_tile(VncState *vs, int x, int y, int w, int h,
- uint32_t* color, bool samecolor)
-{
- VncDisplay *vd = vs->vd;
-
- switch(vd->server->pf.bytes_per_pixel) {
- case 4:
- return check_solid_tile32(vs, x, y, w, h, color, samecolor);
- case 2:
- return check_solid_tile16(vs, x, y, w, h, color, samecolor);
- default:
- return check_solid_tile8(vs, x, y, w, h, color, samecolor);
- }
-}
-
-static void find_best_solid_area(VncState *vs, int x, int y, int w, int h,
- uint32_t color, int *w_ptr, int *h_ptr)
-{
- int dx, dy, dw, dh;
- int w_prev;
- int w_best = 0, h_best = 0;
-
- w_prev = w;
-
- for (dy = y; dy < y + h; dy += VNC_TIGHT_MAX_SPLIT_TILE_SIZE) {
-
- dh = MIN(VNC_TIGHT_MAX_SPLIT_TILE_SIZE, y + h - dy);
- dw = MIN(VNC_TIGHT_MAX_SPLIT_TILE_SIZE, w_prev);
-
- if (!check_solid_tile(vs, x, dy, dw, dh, &color, true)) {
- break;
- }
-
- for (dx = x + dw; dx < x + w_prev;) {
- dw = MIN(VNC_TIGHT_MAX_SPLIT_TILE_SIZE, x + w_prev - dx);
-
- if (!check_solid_tile(vs, dx, dy, dw, dh, &color, true)) {
- break;
- }
- dx += dw;
- }
-
- w_prev = dx - x;
- if (w_prev * (dy + dh - y) > w_best * h_best) {
- w_best = w_prev;
- h_best = dy + dh - y;
- }
- }
-
- *w_ptr = w_best;
- *h_ptr = h_best;
-}
-
-static void extend_solid_area(VncState *vs, int x, int y, int w, int h,
- uint32_t color, int *x_ptr, int *y_ptr,
- int *w_ptr, int *h_ptr)
-{
- int cx, cy;
-
- /* Try to extend the area upwards. */
- for ( cy = *y_ptr - 1;
- cy >= y && check_solid_tile(vs, *x_ptr, cy, *w_ptr, 1, &color, true);
- cy-- );
- *h_ptr += *y_ptr - (cy + 1);
- *y_ptr = cy + 1;
-
- /* ... downwards. */
- for ( cy = *y_ptr + *h_ptr;
- cy < y + h &&
- check_solid_tile(vs, *x_ptr, cy, *w_ptr, 1, &color, true);
- cy++ );
- *h_ptr += cy - (*y_ptr + *h_ptr);
-
- /* ... to the left. */
- for ( cx = *x_ptr - 1;
- cx >= x && check_solid_tile(vs, cx, *y_ptr, 1, *h_ptr, &color, true);
- cx-- );
- *w_ptr += *x_ptr - (cx + 1);
- *x_ptr = cx + 1;
-
- /* ... to the right. */
- for ( cx = *x_ptr + *w_ptr;
- cx < x + w &&
- check_solid_tile(vs, cx, *y_ptr, 1, *h_ptr, &color, true);
- cx++ );
- *w_ptr += cx - (*x_ptr + *w_ptr);
-}
-
-static int tight_init_stream(VncState *vs, int stream_id,
- int level, int strategy)
-{
- z_streamp zstream = &vs->tight_stream[stream_id];
-
- if (zstream->opaque == NULL) {
- int err;
-
- VNC_DEBUG("VNC: TIGHT: initializing zlib stream %d\n", stream_id);
- VNC_DEBUG("VNC: TIGHT: opaque = %p | vs = %p\n", zstream->opaque, vs);
- zstream->zalloc = vnc_zlib_zalloc;
- zstream->zfree = vnc_zlib_zfree;
-
- err = deflateInit2(zstream, level, Z_DEFLATED, MAX_WBITS,
- MAX_MEM_LEVEL, strategy);
-
- if (err != Z_OK) {
- fprintf(stderr, "VNC: error initializing zlib\n");
- return -1;
- }
-
- vs->tight_levels[stream_id] = level;
- zstream->opaque = vs;
- }
-
- if (vs->tight_levels[stream_id] != level) {
- if (deflateParams(zstream, level, strategy) != Z_OK) {
- return -1;
- }
- vs->tight_levels[stream_id] = level;
- }
- return 0;
-}
-
-static void tight_send_compact_size(VncState *vs, size_t len)
-{
- int lpc = 0;
- int bytes = 0;
- char buf[3] = {0, 0, 0};
-
- buf[bytes++] = len & 0x7F;
- if (len > 0x7F) {
- buf[bytes-1] |= 0x80;
- buf[bytes++] = (len >> 7) & 0x7F;
- if (len > 0x3FFF) {
- buf[bytes-1] |= 0x80;
- buf[bytes++] = (len >> 14) & 0xFF;
- }
- }
- for (lpc = 0; lpc < bytes; lpc++) {
- vnc_write_u8(vs, buf[lpc]);
- }
-}
-
-static int tight_compress_data(VncState *vs, int stream_id, size_t bytes,
- int level, int strategy)
-{
- z_streamp zstream = &vs->tight_stream[stream_id];
- int previous_out;
-
- if (bytes < VNC_TIGHT_MIN_TO_COMPRESS) {
- vnc_write(vs, vs->tight.buffer, vs->tight.offset);
- return bytes;
- }
-
- if (tight_init_stream(vs, stream_id, level, strategy)) {
- return -1;
- }
-
- /* reserve memory in output buffer */
- buffer_reserve(&vs->tight_zlib, bytes + 64);
-
- /* set pointers */
- zstream->next_in = vs->tight.buffer;
- zstream->avail_in = vs->tight.offset;
- zstream->next_out = vs->tight_zlib.buffer + vs->tight_zlib.offset;
- zstream->avail_out = vs->tight_zlib.capacity - vs->tight_zlib.offset;
- zstream->data_type = Z_BINARY;
- previous_out = zstream->total_out;
-
- /* start encoding */
- if (deflate(zstream, Z_SYNC_FLUSH) != Z_OK) {
- fprintf(stderr, "VNC: error during tight compression\n");
- return -1;
- }
-
- vs->tight_zlib.offset = vs->tight_zlib.capacity - zstream->avail_out;
- bytes = zstream->total_out - previous_out;
-
- tight_send_compact_size(vs, bytes);
- vnc_write(vs, vs->tight_zlib.buffer, bytes);
-
- buffer_reset(&vs->tight_zlib);
-
- return bytes;
-}
-
-/*
- * Subencoding implementations.
- */
-static void tight_pack24(VncState *vs, uint8_t *buf, size_t count, size_t *ret)
-{
- uint32_t *buf32;
- uint32_t pix;
- int rshift, gshift, bshift;
-
- buf32 = (uint32_t *)buf;
-
- if ((vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG) ==
- (vs->ds->surface->flags & QEMU_BIG_ENDIAN_FLAG)) {
- rshift = vs->clientds.pf.rshift;
- gshift = vs->clientds.pf.gshift;
- bshift = vs->clientds.pf.bshift;
- } else {
- rshift = 24 - vs->clientds.pf.rshift;
- gshift = 24 - vs->clientds.pf.gshift;
- bshift = 24 - vs->clientds.pf.bshift;
- }
-
- if (ret) {
- *ret = count * 3;
- }
-
- while (count--) {
- pix = *buf32++;
- *buf++ = (char)(pix >> rshift);
- *buf++ = (char)(pix >> gshift);
- *buf++ = (char)(pix >> bshift);
- }
-}
-
-static int send_full_color_rect(VncState *vs, int w, int h)
-{
- int stream = 0;
- size_t bytes;
-
- vnc_write_u8(vs, stream << 4); /* no flushing, no filter */
-
- if (vs->tight_pixel24) {
- tight_pack24(vs, vs->tight.buffer, w * h, &vs->tight.offset);
- bytes = 3;
- } else {
- bytes = vs->clientds.pf.bytes_per_pixel;
- }
-
- bytes = tight_compress_data(vs, stream, w * h * bytes,
- tight_conf[vs->tight_compression].raw_zlib_level,
- Z_DEFAULT_STRATEGY);
-
- return (bytes >= 0);
-}
-
-static int send_solid_rect(VncState *vs)
-{
- size_t bytes;
-
- vnc_write_u8(vs, VNC_TIGHT_FILL << 4); /* no flushing, no filter */
-
- if (vs->tight_pixel24) {
- tight_pack24(vs, vs->tight.buffer, 1, &vs->tight.offset);
- bytes = 3;
- } else {
- bytes = vs->clientds.pf.bytes_per_pixel;
- }
-
- vnc_write(vs, vs->tight.buffer, bytes);
- return 1;
-}
-
-static int send_mono_rect(VncState *vs, int w, int h, uint32_t bg, uint32_t fg)
-{
- size_t bytes;
- int stream = 1;
- int level = tight_conf[vs->tight_compression].mono_zlib_level;
-
- bytes = ((w + 7) / 8) * h;
-
- vnc_write_u8(vs, (stream | VNC_TIGHT_EXPLICIT_FILTER) << 4);
- vnc_write_u8(vs, VNC_TIGHT_FILTER_PALETTE);
- vnc_write_u8(vs, 1);
-
- switch(vs->clientds.pf.bytes_per_pixel) {
- case 4:
- {
- uint32_t buf[2] = {bg, fg};
- size_t ret = sizeof (buf);
-
- if (vs->tight_pixel24) {
- tight_pack24(vs, (unsigned char*)buf, 2, &ret);
- }
- vnc_write(vs, buf, ret);
-
- tight_encode_mono_rect32(vs->tight.buffer, w, h, bg, fg);
- break;
- }
- case 2:
- vnc_write(vs, &bg, 2);
- vnc_write(vs, &fg, 2);
- tight_encode_mono_rect16(vs->tight.buffer, w, h, bg, fg);
- break;
- default:
- vnc_write_u8(vs, bg);
- vnc_write_u8(vs, fg);
- tight_encode_mono_rect8(vs->tight.buffer, w, h, bg, fg);
- break;
- }
- vs->tight.offset = bytes;
-
- bytes = tight_compress_data(vs, stream, bytes, level, Z_DEFAULT_STRATEGY);
- return (bytes >= 0);
-}
-
-struct palette_cb_priv {
- VncState *vs;
- uint8_t *header;
-};
-
-static void write_palette(const char *key, QObject *obj, void *opaque)
-{
- struct palette_cb_priv *priv = opaque;
- VncState *vs = priv->vs;
- uint32_t bytes = vs->clientds.pf.bytes_per_pixel;
- uint8_t idx = qint_get_int(qobject_to_qint(obj));
-
- if (bytes == 4) {
- uint32_t color = tight_palette_buf2rgb(32, (uint8_t *)key);
-
- ((uint32_t*)priv->header)[idx] = color;
- } else {
- uint16_t color = tight_palette_buf2rgb(16, (uint8_t *)key);
-
- ((uint16_t*)priv->header)[idx] = color;
- }
-}
-
-static int send_palette_rect(VncState *vs, int w, int h, struct QDict *palette)
-{
- int stream = 2;
- int level = tight_conf[vs->tight_compression].idx_zlib_level;
- int colors;
- size_t bytes;
-
- colors = qdict_size(palette);
-
- vnc_write_u8(vs, (stream | VNC_TIGHT_EXPLICIT_FILTER) << 4);
- vnc_write_u8(vs, VNC_TIGHT_FILTER_PALETTE);
- vnc_write_u8(vs, colors - 1);
-
- switch(vs->clientds.pf.bytes_per_pixel) {
- case 4:
- {
- size_t old_offset, offset;
- uint32_t header[qdict_size(palette)];
- struct palette_cb_priv priv = { vs, (uint8_t *)header };
-
- old_offset = vs->output.offset;
- qdict_iter(palette, write_palette, &priv);
- vnc_write(vs, header, sizeof(header));
-
- if (vs->tight_pixel24) {
- tight_pack24(vs, vs->output.buffer + old_offset, colors, &offset);
- vs->output.offset = old_offset + offset;
- }
-
- tight_encode_indexed_rect32(vs->tight.buffer, w * h, palette);
- break;
- }
- case 2:
- {
- uint16_t header[qdict_size(palette)];
- struct palette_cb_priv priv = { vs, (uint8_t *)header };
-
- qdict_iter(palette, write_palette, &priv);
- vnc_write(vs, header, sizeof(header));
- tight_encode_indexed_rect16(vs->tight.buffer, w * h, palette);
- break;
- }
- default:
- return -1; /* No palette for 8bits colors */
- break;
- }
- bytes = w * h;
- vs->tight.offset = bytes;
-
- bytes = tight_compress_data(vs, stream, bytes,
- level, Z_DEFAULT_STRATEGY);
- return (bytes >= 0);
-}
-
-static void vnc_tight_start(VncState *vs)
-{
- buffer_reset(&vs->tight);
-
- // make the output buffer be the zlib buffer, so we can compress it later
- vs->tight_tmp = vs->output;
- vs->output = vs->tight;
-}
-
-static void vnc_tight_stop(VncState *vs)
-{
- // switch back to normal output/zlib buffers
- vs->tight = vs->output;
- vs->output = vs->tight_tmp;
-}
-
-static int send_sub_rect(VncState *vs, int x, int y, int w, int h)
-{
- struct QDict *palette = NULL;
- uint32_t bg = 0, fg = 0;
- int colors;
- int ret = 0;
-
- vnc_framebuffer_update(vs, x, y, w, h, VNC_ENCODING_TIGHT);
-
- vnc_tight_start(vs);
- vnc_raw_send_framebuffer_update(vs, x, y, w, h);
- vnc_tight_stop(vs);
-
- colors = tight_fill_palette(vs, x, y, w * h, &fg, &bg, &palette);
-
- if (colors == 0) {
- ret = send_full_color_rect(vs, w, h);
- } else if (colors == 1) {
- ret = send_solid_rect(vs);
- } else if (colors == 2) {
- ret = send_mono_rect(vs, w, h, bg, fg);
- } else if (colors <= 256) {
- ret = send_palette_rect(vs, w, h, palette);
- }
- QDECREF(palette);
- return ret;
-}
-
-static int send_sub_rect_solid(VncState *vs, int x, int y, int w, int h)
-{
- vnc_framebuffer_update(vs, x, y, w, h, VNC_ENCODING_TIGHT);
-
- vnc_tight_start(vs);
- vnc_raw_send_framebuffer_update(vs, x, y, w, h);
- vnc_tight_stop(vs);
-
- return send_solid_rect(vs);
-}
-
-static int send_rect_simple(VncState *vs, int x, int y, int w, int h)
-{
- int max_size, max_width;
- int max_sub_width, max_sub_height;
- int dx, dy;
- int rw, rh;
- int n = 0;
-
- max_size = tight_conf[vs->tight_compression].max_rect_size;
- max_width = tight_conf[vs->tight_compression].max_rect_width;
-
- if (w > max_width || w * h > max_size) {
- max_sub_width = (w > max_width) ? max_width : w;
- max_sub_height = max_size / max_sub_width;
-
- for (dy = 0; dy < h; dy += max_sub_height) {
- for (dx = 0; dx < w; dx += max_width) {
- rw = MIN(max_sub_width, w - dx);
- rh = MIN(max_sub_height, h - dy);
- n += send_sub_rect(vs, x+dx, y+dy, rw, rh);
- }
- }
- } else {
- n += send_sub_rect(vs, x, y, w, h);
- }
-
- return n;
-}
-
-static int find_large_solid_color_rect(VncState *vs, int x, int y,
- int w, int h, int max_rows)
-{
- int dx, dy, dw, dh;
- int n = 0;
-
- /* Try to find large solid-color areas and send them separately. */
-
- for (dy = y; dy < y + h; dy += VNC_TIGHT_MAX_SPLIT_TILE_SIZE) {
-
- /* If a rectangle becomes too large, send its upper part now. */
-
- if (dy - y >= max_rows) {
- n += send_rect_simple(vs, x, y, w, max_rows);
- y += max_rows;
- h -= max_rows;
- }
-
- dh = MIN(VNC_TIGHT_MAX_SPLIT_TILE_SIZE, (y + h - dy));
-
- for (dx = x; dx < x + w; dx += VNC_TIGHT_MAX_SPLIT_TILE_SIZE) {
- uint32_t color_value;
- int x_best, y_best, w_best, h_best;
-
- dw = MIN(VNC_TIGHT_MAX_SPLIT_TILE_SIZE, (x + w - dx));
-
- if (!check_solid_tile(vs, dx, dy, dw, dh, &color_value, false)) {
- continue ;
- }
-
- /* Get dimensions of solid-color area. */
-
- find_best_solid_area(vs, dx, dy, w - (dx - x), h - (dy - y),
- color_value, &w_best, &h_best);
-
- /* Make sure a solid rectangle is large enough
- (or the whole rectangle is of the same color). */
-
- if (w_best * h_best != w * h &&
- w_best * h_best < VNC_TIGHT_MIN_SOLID_SUBRECT_SIZE) {
- continue;
- }
-
- /* Try to extend solid rectangle to maximum size. */
-
- x_best = dx; y_best = dy;
- extend_solid_area(vs, x, y, w, h, color_value,
- &x_best, &y_best, &w_best, &h_best);
-
- /* Send rectangles at top and left to solid-color area. */
-
- if (y_best != y) {
- n += send_rect_simple(vs, x, y, w, y_best-y);
- }
- if (x_best != x) {
- n += vnc_tight_send_framebuffer_update(vs, x, y_best,
- x_best-x, h_best);
- }
-
- /* Send solid-color rectangle. */
- n += send_sub_rect_solid(vs, x_best, y_best, w_best, h_best);
-
- /* Send remaining rectangles (at right and bottom). */
-
- if (x_best + w_best != x + w) {
- n += vnc_tight_send_framebuffer_update(vs, x_best+w_best,
- y_best,
- w-(x_best-x)-w_best,
- h_best);
- }
- if (y_best + h_best != y + h) {
- n += vnc_tight_send_framebuffer_update(vs, x, y_best+h_best,
- w, h-(y_best-y)-h_best);
- }
-
- /* Return after all recursive calls are done. */
- return n;
- }
- }
- return n + send_rect_simple(vs, x, y, w, h);
-}
-
-int vnc_tight_send_framebuffer_update(VncState *vs, int x, int y,
- int w, int h)
-{
- int max_rows;
-
- if (vs->clientds.pf.bytes_per_pixel == 4 && vs->clientds.pf.rmax == 0xFF &&
- vs->clientds.pf.bmax == 0xFF && vs->clientds.pf.gmax == 0xFF) {
- vs->tight_pixel24 = true;
- } else {
- vs->tight_pixel24 = false;
- }
-
- if (w * h < VNC_TIGHT_MIN_SPLIT_RECT_SIZE)
- return send_rect_simple(vs, x, y, w, h);
-
- /* Calculate maximum number of rows in one non-solid rectangle. */
-
- max_rows = tight_conf[vs->tight_compression].max_rect_size;
- max_rows /= MIN(tight_conf[vs->tight_compression].max_rect_width, w);
-
- return find_large_solid_color_rect(vs, x, y, w, h, max_rows);
-}
-
-void vnc_tight_clear(VncState *vs)
-{
- int i;
- for (i=0; i<ARRAY_SIZE(vs->tight_stream); i++) {
- if (vs->tight_stream[i].opaque) {
- deflateEnd(&vs->tight_stream[i]);
- }
- }
-
- buffer_free(&vs->tight);
- buffer_free(&vs->tight_zlib);
-}