diff options
-rw-r--r-- | .gitignore | 5 | ||||
-rw-r--r-- | CODING_STYLE | 3 | ||||
-rw-r--r-- | HACKING | 125 | ||||
-rw-r--r-- | MAINTAINERS | 8 | ||||
-rw-r--r-- | Makefile | 81 | ||||
-rw-r--r-- | Makefile.objs | 58 | ||||
-rw-r--r-- | Makefile.target | 30 | ||||
-rw-r--r-- | QMP/README | 73 | ||||
-rw-r--r-- | VERSION | 2 | ||||
-rw-r--r-- | arch_init.c | 24 | ||||
-rw-r--r-- | audio/alsaaudio.c | 27 | ||||
-rw-r--r-- | audio/audio.h | 8 | ||||
-rw-r--r-- | audio/audio_int.h | 8 | ||||
-rw-r--r-- | audio/audio_pt_int.c | 25 | ||||
-rw-r--r-- | audio/audio_template.h | 4 | ||||
-rw-r--r-- | audio/esdaudio.c | 58 | ||||
-rw-r--r-- | audio/paaudio.c | 2 | ||||
-rw-r--r-- | audio/sdlaudio.c | 22 | ||||
-rw-r--r-- | balloon.c | 2 | ||||
-rw-r--r-- | block-migration.c | 20 | ||||
-rw-r--r-- | block.c | 107 | ||||
-rw-r--r-- | block.h | 3 | ||||
-rw-r--r-- | block/blkdebug.c | 4 | ||||
-rw-r--r-- | block/blkverify.c | 382 | ||||
-rw-r--r-- | block/nbd.c | 64 | ||||
-rw-r--r-- | block/qcow2-cluster.c | 35 | ||||
-rw-r--r-- | block/qcow2-refcount.c | 14 | ||||
-rw-r--r-- | block/qcow2-snapshot.c | 2 | ||||
-rw-r--r-- | block/qcow2.c | 115 | ||||
-rw-r--r-- | block/qcow2.h | 4 | ||||
-rw-r--r-- | block/raw-posix.c | 119 | ||||
-rw-r--r-- | block/raw-win32.c | 6 | ||||
-rw-r--r-- | block/raw.c | 6 | ||||
-rw-r--r-- | block/sheepdog.c | 10 | ||||
-rw-r--r-- | block/vvfat.c | 29 | ||||
-rw-r--r-- | block_int.h | 12 | ||||
-rw-r--r-- | blockdev.c | 5 | ||||
-rw-r--r-- | blockdev.h | 23 | ||||
-rw-r--r-- | bsd-user/main.c | 6 | ||||
-rw-r--r-- | bsd-user/qemu.h | 2 | ||||
-rw-r--r-- | buffered_file.c | 12 | ||||
-rw-r--r-- | compatfd.c | 117 | ||||
-rw-r--r-- | compatfd.h | 43 | ||||
-rwxr-xr-x | configure | 312 | ||||
-rw-r--r-- | console.c | 6 | ||||
-rw-r--r-- | cpu-all.h | 4 | ||||
-rw-r--r-- | cpu-common.h | 5 | ||||
-rw-r--r-- | cpu-exec.c | 17 | ||||
-rw-r--r-- | cpus.c | 213 | ||||
-rw-r--r-- | cpus.h | 2 | ||||
-rw-r--r-- | cris-dis.c | 4 | ||||
-rw-r--r-- | cutils.c | 50 | ||||
-rw-r--r-- | darwin-user/qemu.h | 2 | ||||
-rw-r--r-- | default-configs/ppc-softmmu.mak | 2 | ||||
-rw-r--r-- | default-configs/ppc64-softmmu.mak | 2 | ||||
-rw-r--r-- | default-configs/ppcemb-softmmu.mak | 2 | ||||
-rw-r--r-- | disas.c | 3 | ||||
-rw-r--r-- | disas.h | 3 | ||||
-rw-r--r-- | docs/blkverify.txt | 69 | ||||
-rw-r--r-- | docs/migration.txt | 303 | ||||
-rw-r--r-- | docs/specs/ivshmem_device_spec.txt | 96 | ||||
-rw-r--r-- | docs/tracing.txt | 184 | ||||
-rw-r--r-- | dyngen-exec.h | 23 | ||||
-rw-r--r-- | elf.h | 44 | ||||
-rw-r--r-- | exec-all.h | 7 | ||||
-rw-r--r-- | exec.c | 140 | ||||
-rw-r--r-- | feature_to_c.sh | 1 | ||||
-rw-r--r-- | fpu/softfloat.c | 79 | ||||
-rw-r--r-- | fpu/softfloat.h | 3 | ||||
-rw-r--r-- | fsdev/qemu-fsdev.c | 57 | ||||
-rw-r--r-- | gdbstub.c | 1 | ||||
-rw-r--r-- | gdbstub.h | 3 | ||||
-rw-r--r-- | hmp-commands.hx | 1216 | ||||
-rw-r--r-- | hw/acpi.c | 28 | ||||
-rw-r--r-- | hw/acpi_piix4.c | 35 | ||||
-rw-r--r-- | hw/apic.c | 48 | ||||
-rw-r--r-- | hw/bonito.c | 1 | ||||
-rw-r--r-- | hw/cirrus_vga.c | 30 | ||||
-rw-r--r-- | hw/cirrus_vga_rop.h | 38 | ||||
-rw-r--r-- | hw/cirrus_vga_rop2.h | 12 | ||||
-rw-r--r-- | hw/device-hotplug.c | 1 | ||||
-rw-r--r-- | hw/e1000.c | 38 | ||||
-rw-r--r-- | hw/eepro100.c | 36 | ||||
-rw-r--r-- | hw/elf_ops.h | 5 | ||||
-rw-r--r-- | hw/escc.c | 56 | ||||
-rw-r--r-- | hw/esp.c | 53 | ||||
-rw-r--r-- | hw/esp.h | 3 | ||||
-rw-r--r-- | hw/etraxfs.c | 1 | ||||
-rw-r--r-- | hw/etraxfs_eth.c | 3 | ||||
-rw-r--r-- | hw/fdc.c | 1 | ||||
-rw-r--r-- | hw/fdc.h | 1 | ||||
-rw-r--r-- | hw/file-op-9p.h | 40 | ||||
-rw-r--r-- | hw/fmopl.c | 6 | ||||
-rw-r--r-- | hw/gumstix.c | 1 | ||||
-rw-r--r-- | hw/hw.h | 8 | ||||
-rw-r--r-- | hw/ide/core.c | 99 | ||||
-rw-r--r-- | hw/ide/pci.c | 61 | ||||
-rw-r--r-- | hw/ide/qdev.c | 1 | ||||
-rw-r--r-- | hw/ide/via.c | 1 | ||||
-rw-r--r-- | hw/ivshmem.c | 829 | ||||
-rw-r--r-- | hw/jazz_led.c | 21 | ||||
-rw-r--r-- | hw/loader.c | 5 | ||||
-rw-r--r-- | hw/lsi53c895a.c | 5 | ||||
-rw-r--r-- | hw/mainstone.c | 1 | ||||
-rw-r--r-- | hw/mips_fulong2e.c | 34 | ||||
-rw-r--r-- | hw/mips_int.c | 32 | ||||
-rw-r--r-- | hw/mips_jazz.c | 5 | ||||
-rw-r--r-- | hw/mips_malta.c | 22 | ||||
-rw-r--r-- | hw/mips_r4k.c | 4 | ||||
-rw-r--r-- | hw/mipsnet.c | 2 | ||||
-rw-r--r-- | hw/msix.c | 1 | ||||
-rw-r--r-- | hw/multiboot.c | 2 | ||||
-rw-r--r-- | hw/musicpal.c | 1 | ||||
-rw-r--r-- | hw/omap.h | 6 | ||||
-rw-r--r-- | hw/omap1.c | 18 | ||||
-rw-r--r-- | hw/omap2.c | 6 | ||||
-rw-r--r-- | hw/omap_clk.c | 1 | ||||
-rw-r--r-- | hw/omap_i2c.c | 5 | ||||
-rw-r--r-- | hw/omap_mmc.c | 5 | ||||
-rw-r--r-- | hw/omap_sx1.c | 1 | ||||
-rw-r--r-- | hw/omap_uart.c | 12 | ||||
-rw-r--r-- | hw/pc.c | 7 | ||||
-rw-r--r-- | hw/pc_piix.c | 8 | ||||
-rw-r--r-- | hw/pci-hotplug.c | 3 | ||||
-rw-r--r-- | hw/pci.c | 3 | ||||
-rw-r--r-- | hw/pci.h | 29 | ||||
-rw-r--r-- | hw/pckbd.c | 23 | ||||
-rw-r--r-- | hw/pcmcia.h | 1 | ||||
-rw-r--r-- | hw/petalogix_s3adsp1800_mmu.c | 14 | ||||
-rw-r--r-- | hw/piix_pci.c | 1 | ||||
-rw-r--r-- | hw/ppc.c | 27 | ||||
-rw-r--r-- | hw/ppc.h | 7 | ||||
-rw-r--r-- | hw/ppc405_boards.c | 29 | ||||
-rw-r--r-- | hw/ppc405_uc.c | 28 | ||||
-rw-r--r-- | hw/ppc440_bamboo.c | 41 | ||||
-rw-r--r-- | hw/ppc4xx_devs.c | 5 | ||||
-rw-r--r-- | hw/ppc_mac.h | 1 | ||||
-rw-r--r-- | hw/ppc_newworld.c | 55 | ||||
-rw-r--r-- | hw/ppc_oldworld.c | 121 | ||||
-rw-r--r-- | hw/ppc_prep.c | 7 | ||||
-rw-r--r-- | hw/ppce500_mpc8544ds.c | 13 | ||||
-rw-r--r-- | hw/pxa2xx.c | 16 | ||||
-rw-r--r-- | hw/qdev-properties.c | 3 | ||||
-rw-r--r-- | hw/qdev.c | 3 | ||||
-rw-r--r-- | hw/qdev.h | 1 | ||||
-rw-r--r-- | hw/r2d.c | 1 | ||||
-rw-r--r-- | hw/rc4030.c | 5 | ||||
-rw-r--r-- | hw/realview.c | 1 | ||||
-rw-r--r-- | hw/s390-virtio-bus.c | 8 | ||||
-rw-r--r-- | hw/s390-virtio-bus.h | 1 | ||||
-rw-r--r-- | hw/scsi-bus.c | 16 | ||||
-rw-r--r-- | hw/scsi-disk.c | 146 | ||||
-rw-r--r-- | hw/scsi-generic.c | 40 | ||||
-rw-r--r-- | hw/scsi.h | 1 | ||||
-rw-r--r-- | hw/sd.c | 3 | ||||
-rw-r--r-- | hw/serial.c | 26 | ||||
-rw-r--r-- | hw/sh_intc.c | 3 | ||||
-rw-r--r-- | hw/slavio_timer.c | 4 | ||||
-rw-r--r-- | hw/sm501.c | 5 | ||||
-rw-r--r-- | hw/soc_dma.c | 5 | ||||
-rw-r--r-- | hw/sparc32_dma.c | 23 | ||||
-rw-r--r-- | hw/spitz.c | 1 | ||||
-rw-r--r-- | hw/sun4m.c | 80 | ||||
-rw-r--r-- | hw/sun4u.c | 1 | ||||
-rw-r--r-- | hw/syborg_virtio.c | 8 | ||||
-rw-r--r-- | hw/sysbus.c | 3 | ||||
-rw-r--r-- | hw/sysbus.h | 5 | ||||
-rw-r--r-- | hw/tc6393xb_template.h | 2 | ||||
-rw-r--r-- | hw/tosa.c | 1 | ||||
-rw-r--r-- | hw/usb-msd.c | 3 | ||||
-rw-r--r-- | hw/usb-net.c | 2 | ||||
-rw-r--r-- | hw/usb-wacom.c | 13 | ||||
-rw-r--r-- | hw/versatilepb.c | 1 | ||||
-rw-r--r-- | hw/vga.c | 7 | ||||
-rw-r--r-- | hw/vhost.c | 58 | ||||
-rw-r--r-- | hw/vhost_net.c | 27 | ||||
-rw-r--r-- | hw/virtex_ml507.c | 276 | ||||
-rw-r--r-- | hw/virtio-9p-debug.c | 195 | ||||
-rw-r--r-- | hw/virtio-9p-debug.h | 1 | ||||
-rw-r--r-- | hw/virtio-9p-local.c | 142 | ||||
-rw-r--r-- | hw/virtio-9p-posix-acl.c | 140 | ||||
-rw-r--r-- | hw/virtio-9p-xattr-user.c | 109 | ||||
-rw-r--r-- | hw/virtio-9p-xattr.c | 156 | ||||
-rw-r--r-- | hw/virtio-9p-xattr.h | 103 | ||||
-rw-r--r-- | hw/virtio-9p.c | 1969 | ||||
-rw-r--r-- | hw/virtio-9p.h | 257 | ||||
-rw-r--r-- | hw/virtio-balloon.c | 12 | ||||
-rw-r--r-- | hw/virtio-blk.c | 32 | ||||
-rw-r--r-- | hw/virtio-net.c | 218 | ||||
-rw-r--r-- | hw/virtio-net.h | 14 | ||||
-rw-r--r-- | hw/virtio-pci.c | 54 | ||||
-rw-r--r-- | hw/virtio-serial-bus.c | 31 | ||||
-rw-r--r-- | hw/virtio.c | 59 | ||||
-rw-r--r-- | hw/virtio.h | 11 | ||||
-rw-r--r-- | hw/vmmouse.c | 31 | ||||
-rw-r--r-- | hw/vmware_vga.c | 80 | ||||
-rw-r--r-- | hw/vt82c686.c | 5 | ||||
-rw-r--r-- | hw/watchdog.c | 2 | ||||
-rw-r--r-- | hw/wm8750.c | 5 | ||||
-rw-r--r-- | hw/xen_backend.h | 4 | ||||
-rw-r--r-- | hw/xen_devconfig.c | 2 | ||||
-rw-r--r-- | hw/xen_disk.c | 1 | ||||
-rw-r--r-- | hw/xen_machine_pv.c | 1 | ||||
-rw-r--r-- | ioport.c | 7 | ||||
-rw-r--r-- | json-parser.c | 3 | ||||
-rw-r--r-- | kvm-all.c | 83 | ||||
-rw-r--r-- | kvm-stub.c | 10 | ||||
-rw-r--r-- | kvm.h | 10 | ||||
-rw-r--r-- | linux-user/elfload.c | 1799 | ||||
-rw-r--r-- | linux-user/flatload.c | 3 | ||||
-rw-r--r-- | linux-user/linuxload.c | 17 | ||||
-rw-r--r-- | linux-user/m68k-sim.c | 6 | ||||
-rw-r--r-- | linux-user/main.c | 39 | ||||
-rw-r--r-- | linux-user/mmap.c | 16 | ||||
-rw-r--r-- | linux-user/qemu.h | 9 | ||||
-rw-r--r-- | linux-user/signal.c | 27 | ||||
-rw-r--r-- | linux-user/syscall.c | 20 | ||||
-rw-r--r-- | linux-user/syscall_defs.h | 14 | ||||
-rw-r--r-- | migration-tcp.c | 5 | ||||
-rw-r--r-- | migration.c | 10 | ||||
-rw-r--r-- | monitor.c | 466 | ||||
-rw-r--r-- | monitor.h | 8 | ||||
-rw-r--r-- | nbd.c | 149 | ||||
-rw-r--r-- | nbd.h | 7 | ||||
-rw-r--r-- | net.c | 81 | ||||
-rw-r--r-- | net.h | 1 | ||||
-rw-r--r-- | net/tap-aix.c | 9 | ||||
-rw-r--r-- | net/tap-bsd.c | 13 | ||||
-rw-r--r-- | net/tap-haiku.c | 61 | ||||
-rw-r--r-- | net/tap-linux.c | 29 | ||||
-rw-r--r-- | net/tap-linux.h | 8 | ||||
-rw-r--r-- | net/tap-solaris.c | 10 | ||||
-rw-r--r-- | net/tap-win32.c | 14 | ||||
-rw-r--r-- | net/tap.c | 49 | ||||
-rw-r--r-- | net/tap.h | 4 | ||||
-rw-r--r-- | os-posix.c | 2 | ||||
-rw-r--r-- | os-win32.c | 21 | ||||
-rw-r--r-- | osdep.c | 47 | ||||
-rw-r--r-- | osdep.h | 37 | ||||
-rw-r--r-- | pc-bios/README | 6 | ||||
-rw-r--r-- | pc-bios/bios.bin | bin | 131072 -> 131072 bytes | |||
-rw-r--r-- | pc-bios/openbios-ppc | bin | 316128 -> 320328 bytes | |||
-rw-r--r-- | pc-bios/openbios-sparc32 | bin | 217664 -> 217660 bytes | |||
-rw-r--r-- | pc-bios/openbios-sparc64 | bin | 1065872 -> 1065864 bytes | |||
-rw-r--r-- | pc-bios/video.x | bin | 12192 -> 0 bytes | |||
-rw-r--r-- | pflib.c | 213 | ||||
-rw-r--r-- | pflib.h | 20 | ||||
-rw-r--r-- | posix-aio-compat.c | 7 | ||||
-rw-r--r-- | qemu-barrier.h | 3 | ||||
-rw-r--r-- | qemu-char.c | 9 | ||||
-rw-r--r-- | qemu-char.h | 6 | ||||
-rw-r--r-- | qemu-common.h | 38 | ||||
-rw-r--r-- | qemu-config.c | 91 | ||||
-rw-r--r-- | qemu-config.h | 13 | ||||
-rw-r--r-- | qemu-doc.texi | 64 | ||||
-rw-r--r-- | qemu-error.h | 9 | ||||
-rw-r--r-- | qemu-img-cmds.hx | 2 | ||||
-rw-r--r-- | qemu-img.c | 7 | ||||
-rw-r--r-- | qemu-io.c | 13 | ||||
-rw-r--r-- | qemu-malloc.c | 12 | ||||
-rw-r--r-- | qemu-nbd.c | 17 | ||||
-rw-r--r-- | qemu-options.hx | 61 | ||||
-rw-r--r-- | qemu-os-win32.h | 3 | ||||
-rw-r--r-- | qemu-thread.c | 22 | ||||
-rw-r--r-- | qemu-thread.h | 4 | ||||
-rw-r--r-- | qemu-timer-common.c | 62 | ||||
-rw-r--r-- | qemu-timer.c | 73 | ||||
-rw-r--r-- | qemu-timer.h | 74 | ||||
-rw-r--r-- | qemu-tool.c | 7 | ||||
-rw-r--r-- | qemu_socket.h | 1 | ||||
-rw-r--r-- | qerror.c | 10 | ||||
-rw-r--r-- | qerror.h | 8 | ||||
-rw-r--r-- | qjson.c | 55 | ||||
-rw-r--r-- | qjson.h | 8 | ||||
-rw-r--r-- | qmp-commands.hx (renamed from qemu-monitor.hx) | 1049 | ||||
-rw-r--r-- | range.h | 29 | ||||
m--------- | roms/seabios | 0 | ||||
-rw-r--r-- | rules.mak | 9 | ||||
-rw-r--r-- | savevm.c | 296 | ||||
-rw-r--r-- | simpletrace.c | 255 | ||||
-rw-r--r-- | simpletrace.h | 40 | ||||
-rwxr-xr-x | simpletrace.py | 93 | ||||
-rw-r--r-- | slirp/bootp.c | 2 | ||||
-rw-r--r-- | slirp/cksum.c | 20 | ||||
-rw-r--r-- | slirp/ip.h | 40 | ||||
-rw-r--r-- | slirp/ip_icmp.h | 2 | ||||
-rw-r--r-- | slirp/ip_input.c | 4 | ||||
-rw-r--r-- | slirp/ip_output.c | 22 | ||||
-rw-r--r-- | slirp/main.h | 2 | ||||
-rw-r--r-- | slirp/misc.h | 20 | ||||
-rw-r--r-- | slirp/slirp.c | 3 | ||||
-rw-r--r-- | slirp/slirp.h | 45 | ||||
-rw-r--r-- | slirp/slirp_config.h | 6 | ||||
-rw-r--r-- | slirp/socket.c | 2 | ||||
-rw-r--r-- | slirp/socket.h | 10 | ||||
-rw-r--r-- | slirp/tcp.h | 14 | ||||
-rw-r--r-- | slirp/tcp_input.c | 4 | ||||
-rw-r--r-- | slirp/tcp_output.c | 10 | ||||
-rw-r--r-- | slirp/tcp_subr.c | 12 | ||||
-rw-r--r-- | slirp/tcp_var.h | 14 | ||||
-rw-r--r-- | slirp/tftp.c | 8 | ||||
-rw-r--r-- | slirp/tftp.h | 14 | ||||
-rw-r--r-- | slirp/udp.c | 8 | ||||
-rw-r--r-- | slirp/udp.h | 10 | ||||
-rw-r--r-- | sysemu.h | 2 | ||||
-rw-r--r-- | target-cris/cpu.h | 5 | ||||
-rw-r--r-- | target-cris/helper.c | 10 | ||||
-rw-r--r-- | target-cris/mmu.c | 4 | ||||
-rw-r--r-- | target-cris/op_helper.c | 4 | ||||
-rw-r--r-- | target-cris/translate.c | 2 | ||||
-rw-r--r-- | target-cris/translate_v10.c | 5 | ||||
-rw-r--r-- | target-i386/cpu.h | 32 | ||||
-rw-r--r-- | target-i386/cpuid.c | 83 | ||||
-rw-r--r-- | target-i386/helper.c | 6 | ||||
-rw-r--r-- | target-i386/kvm.c | 415 | ||||
-rw-r--r-- | target-i386/kvm_x86.h | 22 | ||||
-rw-r--r-- | target-i386/op_helper.c | 6 | ||||
-rw-r--r-- | target-i386/translate.c | 60 | ||||
-rw-r--r-- | target-microblaze/cpu.h | 9 | ||||
-rw-r--r-- | target-microblaze/helper.h | 16 | ||||
-rw-r--r-- | target-microblaze/op_helper.c | 244 | ||||
-rw-r--r-- | target-microblaze/translate.c | 200 | ||||
-rw-r--r-- | target-mips/cpu.h | 25 | ||||
-rw-r--r-- | target-mips/helper.c | 33 | ||||
-rw-r--r-- | target-mips/helper.h | 1 | ||||
-rw-r--r-- | target-mips/op_helper.c | 34 | ||||
-rw-r--r-- | target-mips/translate.c | 44 | ||||
-rw-r--r-- | target-mips/translate_init.c | 4 | ||||
-rw-r--r-- | target-ppc/cpu.h | 8 | ||||
-rw-r--r-- | target-ppc/helper.c | 132 | ||||
-rw-r--r-- | target-ppc/helper.h | 1 | ||||
-rw-r--r-- | target-ppc/kvm.c | 69 | ||||
-rw-r--r-- | target-ppc/kvm_ppc.h | 14 | ||||
-rw-r--r-- | target-ppc/op_helper.c | 140 | ||||
-rw-r--r-- | target-ppc/translate.c | 6 | ||||
-rw-r--r-- | target-ppc/translate_init.c | 10 | ||||
-rw-r--r-- | tcg/ppc/tcg-target.h | 2 | ||||
-rw-r--r-- | tcg/ppc64/tcg-target.c | 2 | ||||
-rw-r--r-- | tcg/tcg.c | 2 | ||||
-rw-r--r-- | tests/Makefile | 123 | ||||
-rw-r--r-- | tests/cris/check_addo.c | 4 | ||||
-rw-r--r-- | tests/cris/check_addoq.c | 2 | ||||
-rw-r--r-- | tests/cris/check_settls1.c | 10 | ||||
-rw-r--r-- | tests/cris/check_swap.c | 2 | ||||
-rw-r--r-- | tests/runcom.c | 11 | ||||
-rw-r--r-- | tests/sha1.c | 24 | ||||
-rw-r--r-- | tests/test-i386.c | 5 | ||||
-rw-r--r-- | tests/test_path.c | 13 | ||||
-rw-r--r-- | trace-events | 83 | ||||
-rwxr-xr-x | tracetool | 377 | ||||
-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.h | 41 | ||||
-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.c | 189 | ||||
-rw-r--r-- | ui/spice-display.c | 411 | ||||
-rw-r--r-- | ui/spice-display.h | 69 | ||||
-rw-r--r-- | ui/spice-input.c | 217 | ||||
-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.c | 1717 | ||||
-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.c | 331 | ||||
-rw-r--r-- | ui/vnc-jobs-sync.c | 73 | ||||
-rw-r--r-- | ui/vnc-jobs.h | 87 | ||||
-rw-r--r-- | ui/vnc-palette.c | 136 | ||||
-rw-r--r-- | ui/vnc-palette.h | 63 | ||||
-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.rc | 28 | ||||
-rw-r--r-- | vl.c | 200 | ||||
-rw-r--r-- | vnc-encoding-tight.c | 959 |
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 @@ -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 @@ -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; } @@ -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"); @@ -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; } @@ -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 @@ -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`" @@ -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; @@ -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() */ @@ -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) @@ -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) @@ -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 @@ -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); @@ -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__) @@ -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 @@ -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); + @@ -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; @@ -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 @@ -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; } @@ -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; @@ -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; @@ -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); @@ -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. */ @@ -34,6 +34,7 @@ #include "isa.h" #include "sysbus.h" #include "qdev-addr.h" +#include "blockdev.h" /********************************************************/ /* debug Floppy devices */ @@ -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; @@ -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; @@ -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 @@ -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); @@ -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; } @@ -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) @@ -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. @@ -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; @@ -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); } @@ -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; } @@ -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" @@ -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, @@ -43,6 +43,7 @@ typedef struct SCSIRequest { enum SCSIXferMode mode; } cmd; BlockDriverAIOCB *aiocb; + bool enqueued; QTAILQ_ENTRY(SCSIRequest) next; } SCSIRequest; @@ -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) @@ -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. */ @@ -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, @@ -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); @@ -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; +} @@ -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; } @@ -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; @@ -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); @@ -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; @@ -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 @@ -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; } @@ -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) { } @@ -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); @@ -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 @@ -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]; @@ -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 Binary files differindex f27ea89f8d..d0d4b6aa43 100644 --- a/pc-bios/bios.bin +++ b/pc-bios/bios.bin diff --git a/pc-bios/openbios-ppc b/pc-bios/openbios-ppc Binary files differindex 9e660ed538..cb0af05575 100644 --- a/pc-bios/openbios-ppc +++ b/pc-bios/openbios-ppc diff --git a/pc-bios/openbios-sparc32 b/pc-bios/openbios-sparc32 Binary files differindex 1acaa31acf..aaff1f00c9 100644 --- a/pc-bios/openbios-sparc32 +++ b/pc-bios/openbios-sparc32 diff --git a/pc-bios/openbios-sparc64 b/pc-bios/openbios-sparc64 Binary files differindex 93c7269c15..a1b692e7e5 100644 --- a/pc-bios/openbios-sparc64 +++ b/pc-bios/openbios-sparc64 diff --git a/pc-bios/video.x b/pc-bios/video.x Binary files differdeleted file mode 100644 index 761aa0c9d4..0000000000 --- a/pc-bios/video.x +++ /dev/null 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); @@ -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> @@ -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; @@ -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 } }" @@ -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; } @@ -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 @@ -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 @@ -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, @@ -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); @@ -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/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/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 */ @@ -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 @@ -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; } } @@ -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 + } +} @@ -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); -} |