aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--CODING_STYLE4
-rw-r--r--MAINTAINERS5
-rw-r--r--Makefile53
-rw-r--r--Makefile.hw2
-rw-r--r--Makefile.objs30
-rw-r--r--Makefile.target23
-rw-r--r--VERSION2
-rw-r--r--arm-semi.c113
-rw-r--r--async.c98
-rw-r--r--balloon.c61
-rw-r--r--balloon.h12
-rw-r--r--block.c329
-rw-r--r--block.h8
-rw-r--r--block/qcow.c180
-rw-r--r--block/qcow2-cache.c12
-rw-r--r--block/qcow2-cluster.c26
-rw-r--r--block/qcow2-refcount.c38
-rw-r--r--block/qcow2.c240
-rw-r--r--block/qcow2.h7
-rw-r--r--block/qed-table.c14
-rw-r--r--block/qed.c4
-rw-r--r--block/raw-posix.c60
-rw-r--r--block/raw-win32.c64
-rw-r--r--block/raw.c7
-rw-r--r--block/sheepdog.c73
-rw-r--r--block/vmdk.c1297
-rw-r--r--block/vpc.c8
-rw-r--r--block_int.h12
-rw-r--r--blockdev.c21
-rw-r--r--bsd-user/main.c8
-rw-r--r--bswap.h474
-rw-r--r--compatfd.c11
-rw-r--r--compatfd.h1
-rw-r--r--compiler.h34
-rwxr-xr-xconfigure170
-rw-r--r--console.c8
-rw-r--r--console.h3
-rw-r--r--coroutine-gthread.c131
-rw-r--r--coroutine-ucontext.c230
-rw-r--r--coroutine-win32.c92
-rw-r--r--cpu-all.h446
-rw-r--r--cpu-common.h33
-rw-r--r--cpus.c24
-rw-r--r--darwin-user/main.c8
-rw-r--r--dma.h4
-rw-r--r--docs/ich9-ehci-uhci.cfg37
-rw-r--r--docs/memory.txt172
-rw-r--r--docs/qapi-code-gen.txt316
-rw-r--r--docs/usb2.txt33
-rw-r--r--dyngen-exec.h2
-rw-r--r--error.h1
-rw-r--r--exec-all.h2
-rw-r--r--exec-memory.h39
-rw-r--r--exec.c97
-rw-r--r--fpu/softfloat-specialize.h72
-rw-r--r--fpu/softfloat.h60
-rw-r--r--gdbstub.c2
-rw-r--r--hmp-commands.hx4
-rw-r--r--hw/a9mpcore.c2
-rw-r--r--hw/acpi.c298
-rw-r--r--hw/acpi_piix4.c22
-rw-r--r--hw/an5206.c2
-rw-r--r--hw/apb_pci.c2
-rw-r--r--hw/arm-misc.h2
-rw-r--r--hw/arm11mpcore.c2
-rw-r--r--hw/arm_boot.c2
-rw-r--r--hw/arm_gic.c2
-rw-r--r--hw/arm_pic.c2
-rw-r--r--hw/arm_sysctl.c2
-rw-r--r--hw/arm_timer.c2
-rw-r--r--hw/armv7m_nvic.c2
-rw-r--r--hw/axis_dev88.c8
-rw-r--r--hw/baum.c7
-rw-r--r--hw/baum.h2
-rw-r--r--hw/bitbang_i2c.c2
-rw-r--r--hw/bonito.c4
-rw-r--r--hw/bt-hid.c62
-rw-r--r--hw/ccid-card-emulated.c2
-rw-r--r--hw/ccid.h3
-rw-r--r--hw/dp8393x.c2
-rw-r--r--hw/ds1225y.c152
-rw-r--r--hw/ds1338.c2
-rw-r--r--hw/dummy_m68k.c2
-rw-r--r--hw/esp.c2
-rw-r--r--hw/etraxfs_eth.c2
-rw-r--r--hw/flash.h17
-rw-r--r--hw/fw_cfg.c142
-rw-r--r--hw/grackle_pci.c5
-rw-r--r--hw/gt64xxx.c4
-rw-r--r--hw/hid.c403
-rw-r--r--hw/hid.h58
-rw-r--r--hw/hw.h1
-rw-r--r--hw/i2c.c2
-rw-r--r--hw/i2c.h2
-rw-r--r--hw/ide.h1
-rw-r--r--hw/ide/ich.c6
-rw-r--r--hw/ide/piix.c44
-rw-r--r--hw/ide/qdev.c5
-rw-r--r--hw/integratorcp.c2
-rw-r--r--hw/lan9118.c2
-rw-r--r--hw/lm832x.c4
-rw-r--r--hw/lsi53c895a.c24
-rw-r--r--hw/marvell_88w8618_audio.c2
-rw-r--r--hw/mc146818rtc.c20
-rw-r--r--hw/mcf5206.c2
-rw-r--r--hw/mcf5208.c2
-rw-r--r--hw/mcf_fec.c4
-rw-r--r--hw/mcf_intc.c2
-rw-r--r--hw/mcf_uart.c2
-rw-r--r--hw/milkymist-softusb.c23
-rw-r--r--hw/mips.h4
-rw-r--r--hw/mips_jazz.c10
-rw-r--r--hw/mipsnet.c2
-rw-r--r--hw/mpcore.c2
-rw-r--r--hw/msmouse.c5
-rw-r--r--hw/msmouse.h2
-rw-r--r--hw/multiboot.c32
-rw-r--r--hw/musicpal.c2
-rw-r--r--hw/nand.c351
-rw-r--r--hw/nseries.c59
-rw-r--r--hw/omap.h22
-rw-r--r--hw/omap1.c10
-rw-r--r--hw/omap2.c34
-rw-r--r--hw/omap_clk.c6
-rw-r--r--hw/omap_gpio.c258
-rw-r--r--hw/omap_l4.c6
-rw-r--r--hw/onenand.c172
-rw-r--r--hw/palm.c26
-rw-r--r--hw/pc.c62
-rw-r--r--hw/pc.h9
-rw-r--r--hw/pc_piix.c26
-rw-r--r--hw/pci.c63
-rw-r--r--hw/pci.h15
-rw-r--r--hw/pci_host.h1
-rw-r--r--hw/pci_ids.h8
-rw-r--r--hw/pci_internals.h1
-rw-r--r--hw/piix_pci.c13
-rw-r--r--hw/pl011.c2
-rw-r--r--hw/pl022.c2
-rw-r--r--hw/pl050.c2
-rw-r--r--hw/pl061.c2
-rw-r--r--hw/pl080.c2
-rw-r--r--hw/pl110.c2
-rw-r--r--hw/pl110_template.h2
-rw-r--r--hw/pl181.c2
-rw-r--r--hw/pl190.c2
-rw-r--r--hw/ppc4xx_pci.c5
-rw-r--r--hw/ppc_mac.h9
-rw-r--r--hw/ppc_newworld.c5
-rw-r--r--hw/ppc_oldworld.c3
-rw-r--r--hw/ppc_prep.c3
-rw-r--r--hw/ppce500_pci.c6
-rw-r--r--hw/prep_pci.c5
-rw-r--r--hw/prep_pci.h3
-rw-r--r--hw/ptimer.c2
-rw-r--r--hw/pxa.h2
-rw-r--r--hw/pxa2xx.c2
-rw-r--r--hw/pxa2xx_dma.c2
-rw-r--r--hw/pxa2xx_pic.c2
-rw-r--r--hw/pxa2xx_timer.c2
-rw-r--r--hw/qdev.c5
-rw-r--r--hw/qxl-logger.c4
-rw-r--r--hw/qxl-render.c4
-rw-r--r--hw/qxl.c486
-rw-r--r--hw/qxl.h34
-rw-r--r--hw/realview.c2
-rw-r--r--hw/realview_gic.c2
-rw-r--r--hw/scsi-bus.c83
-rw-r--r--hw/scsi-defs.h62
-rw-r--r--hw/scsi-disk.c102
-rw-r--r--hw/scsi-generic.c9
-rw-r--r--hw/scsi.h10
-rw-r--r--hw/sd.c23
-rw-r--r--hw/sh_intc.c2
-rw-r--r--hw/sh_pci.c4
-rw-r--r--hw/sh_timer.c2
-rw-r--r--hw/smbus.c2
-rw-r--r--hw/smc91c111.c2
-rw-r--r--hw/spapr_hcall.c2
-rw-r--r--hw/spapr_vscsi.c28
-rw-r--r--hw/spitz.c6
-rw-r--r--hw/ssd0303.c2
-rw-r--r--hw/ssd0323.c2
-rw-r--r--hw/ssi-sd.c2
-rw-r--r--hw/ssi.c2
-rw-r--r--hw/stellaris.c4
-rw-r--r--hw/stellaris_enet.c2
-rw-r--r--hw/stellaris_input.c2
-rw-r--r--hw/sysbus.c27
-rw-r--r--hw/sysbus.h3
-rw-r--r--hw/tc6393xb.c7
-rw-r--r--hw/unin_pci.c10
-rw-r--r--hw/usb-bt.c61
-rw-r--r--hw/usb-bus.c46
-rw-r--r--hw/usb-ccid.c57
-rw-r--r--hw/usb-ehci.c439
-rw-r--r--hw/usb-hid.c517
-rw-r--r--hw/usb-hub.c98
-rw-r--r--hw/usb-libhw.c63
-rw-r--r--hw/usb-msd.c132
-rw-r--r--hw/usb-musb.c46
-rw-r--r--hw/usb-net.c71
-rw-r--r--hw/usb-ohci.c154
-rw-r--r--hw/usb-serial.c35
-rw-r--r--hw/usb-uhci.c152
-rw-r--r--hw/usb-wacom.c12
-rw-r--r--hw/usb.c99
-rw-r--r--hw/usb.h33
-rw-r--r--hw/versatile_pci.c4
-rw-r--r--hw/versatilepb.c2
-rw-r--r--hw/vga_int.h10
-rw-r--r--hw/virtio-balloon.c76
-rw-r--r--hw/virtio-console.c29
-rw-r--r--hw/virtio-net.c2
-rw-r--r--hw/virtio-pci.c14
-rw-r--r--hw/virtio-serial-bus.c29
-rw-r--r--hw/virtio.h1
-rw-r--r--hw/xen.h12
-rw-r--r--hw/xen_common.h12
-rw-r--r--hw/xen_console.c25
-rw-r--r--hw/xen_devconfig.c4
-rw-r--r--hw/xen_disk.c37
-rw-r--r--hw/xen_platform.c43
-rw-r--r--hw/xenfb.c19
-rw-r--r--hw/z2.c358
-rw-r--r--input.c2
-rw-r--r--ioport.c23
-rw-r--r--ioport.h2
-rw-r--r--iov.c103
-rw-r--r--iov.h14
-rw-r--r--libcacard/Makefile26
-rw-r--r--libcacard/libcacard.pc.in13
-rw-r--r--libcacard/vcard_emul_nss.c257
-rw-r--r--linux-aio.c43
-rw-r--r--linux-user/alpha/syscall_nr.h23
-rw-r--r--linux-user/arm/syscall_nr.h13
-rw-r--r--linux-user/cris/syscall_nr.h2
-rw-r--r--linux-user/i386/syscall_nr.h12
-rw-r--r--linux-user/ioctls.h13
-rw-r--r--linux-user/m68k/syscall_nr.h16
-rw-r--r--linux-user/main.c44
-rw-r--r--linux-user/microblaze/syscall_nr.h14
-rw-r--r--linux-user/mips/syscall_nr.h13
-rw-r--r--linux-user/mips64/syscall_nr.h13
-rw-r--r--linux-user/mipsn32/syscall_nr.h14
-rw-r--r--linux-user/ppc/syscall_nr.h30
-rw-r--r--linux-user/s390x/syscall_nr.h13
-rw-r--r--linux-user/sh4/syscall_nr.h34
-rw-r--r--linux-user/signal.c30
-rw-r--r--linux-user/sparc/syscall_nr.h12
-rw-r--r--linux-user/sparc64/syscall_nr.h12
-rw-r--r--linux-user/syscall.c155
-rw-r--r--linux-user/syscall_defs.h51
-rw-r--r--linux-user/syscall_types.h20
-rw-r--r--linux-user/x86_64/syscall_nr.h12
-rw-r--r--memory.c1141
-rw-r--r--memory.h469
-rw-r--r--migration.c12
-rw-r--r--module.h2
-rw-r--r--monitor.c34
-rw-r--r--nbd.c2
-rw-r--r--net.c65
-rw-r--r--net.h8
-rw-r--r--net/slirp.c23
-rw-r--r--net/socket.c4
-rw-r--r--notify.c4
-rw-r--r--notify.h4
-rw-r--r--oslib-posix.c5
-rw-r--r--pc-bios/README7
-rw-r--r--pc-bios/openbios-sparc32bin377388 -> 381484 bytes
-rw-r--r--pc-bios/openbios-sparc64bin1598328 -> 1598328 bytes
-rw-r--r--poison.h9
-rw-r--r--posix-aio-compat.c30
-rw-r--r--qapi-schema-guest.json217
-rw-r--r--qapi-schema-test.json22
-rw-r--r--qapi/qapi-dealloc-visitor.c147
-rw-r--r--qapi/qapi-dealloc-visitor.h26
-rw-r--r--qapi/qapi-types-core.h20
-rw-r--r--qapi/qapi-visit-core.c118
-rw-r--r--qapi/qapi-visit-core.h76
-rw-r--r--qapi/qmp-core.h41
-rw-r--r--qapi/qmp-dispatch.c124
-rw-r--r--qapi/qmp-input-visitor.c301
-rw-r--r--qapi/qmp-input-visitor.h27
-rw-r--r--qapi/qmp-output-visitor.c239
-rw-r--r--qapi/qmp-output-visitor.h28
-rw-r--r--qapi/qmp-registry.c40
-rw-r--r--qemu-char.c204
-rw-r--r--qemu-char.h2
-rw-r--r--qemu-common.h36
-rw-r--r--qemu-config.c38
-rw-r--r--qemu-coroutine-int.h49
-rw-r--r--qemu-coroutine-lock.c117
-rw-r--r--qemu-coroutine.c75
-rw-r--r--qemu-coroutine.h159
-rw-r--r--qemu-ga.c637
-rw-r--r--qemu-img-cmds.hx4
-rw-r--r--qemu-img.c31
-rw-r--r--qemu-img.texi6
-rw-r--r--qemu-io.c2653
-rw-r--r--qemu-options.hx70
-rw-r--r--qemu-timer.c77
-rw-r--r--qemu-timer.h5
-rw-r--r--qerror.c17
-rw-r--r--qerror.h12
-rw-r--r--qga/guest-agent-command-state.c73
-rw-r--r--qga/guest-agent-commands.c561
-rw-r--r--qga/guest-agent-core.h31
-rw-r--r--qlist.h11
-rw-r--r--qmp-commands.hx63
-rw-r--r--savevm.c3
-rwxr-xr-xscripts/checkpatch.pl5
-rw-r--r--scripts/ordereddict.py127
-rw-r--r--scripts/qapi-commands.py385
-rw-r--r--scripts/qapi-types.py270
-rw-r--r--scripts/qapi-visit.py246
-rw-r--r--scripts/qapi.py203
-rw-r--r--simpletrace.c2
-rw-r--r--slirp/arp_table.c95
-rw-r--r--slirp/bootp.c21
-rw-r--r--slirp/if.c28
-rw-r--r--slirp/ip_icmp.c95
-rw-r--r--slirp/ip_icmp.h3
-rw-r--r--slirp/ip_input.c34
-rw-r--r--slirp/ip_output.c4
-rw-r--r--slirp/main.h2
-rw-r--r--slirp/mbuf.c2
-rw-r--r--slirp/mbuf.h5
-rw-r--r--slirp/misc.c16
-rw-r--r--slirp/slirp.c174
-rw-r--r--slirp/slirp.h52
-rw-r--r--slirp/socket.c6
-rw-r--r--slirp/tcp_input.c10
-rw-r--r--slirp/tcp_subr.c2
-rw-r--r--slirp/udp.c23
-rw-r--r--softmmu-semi.h2
-rw-r--r--spice-qemu-char.c9
-rw-r--r--sysemu.h3
-rw-r--r--target-alpha/cpu.h5
-rw-r--r--target-alpha/exec.h39
-rw-r--r--target-alpha/op_helper.c13
-rw-r--r--target-arm/cpu.h8
-rw-r--r--target-arm/exec.h30
-rw-r--r--target-arm/helper.c193
-rw-r--r--target-arm/helper.h293
-rw-r--r--target-arm/iwmmxt_helper.c80
-rw-r--r--target-arm/machine.c12
-rw-r--r--target-arm/neon_helper.c203
-rw-r--r--target-arm/op_addsub.h2
-rw-r--r--target-arm/op_helper.c9
-rw-r--r--target-arm/translate.c457
-rw-r--r--target-cris/exec.h28
-rw-r--r--target-cris/op_helper.c4
-rw-r--r--target-i386/cpu.h3
-rw-r--r--target-i386/exec.h142
-rw-r--r--target-i386/helper.c2
-rw-r--r--target-i386/op_helper.c165
-rw-r--r--target-lm32/exec.h38
-rw-r--r--target-lm32/op_helper.c3
-rw-r--r--target-m68k/exec.h28
-rw-r--r--target-m68k/op_helper.c5
-rw-r--r--target-microblaze/cpu.h4
-rw-r--r--target-microblaze/exec.h27
-rw-r--r--target-microblaze/op_helper.c19
-rw-r--r--target-microblaze/translate.c1
-rw-r--r--target-mips/cpu.h6
-rw-r--r--target-mips/exec.h60
-rw-r--r--target-mips/op_helper.c62
-rw-r--r--target-ppc/exec.h34
-rw-r--r--target-ppc/op_helper.c7
-rw-r--r--target-s390x/exec.h37
-rw-r--r--target-s390x/op_helper.c4
-rw-r--r--target-sh4/exec.h33
-rw-r--r--target-sh4/op_helper.c4
-rw-r--r--target-sparc/cpu.h39
-rw-r--r--target-sparc/exec.h15
-rw-r--r--target-sparc/helper.c153
-rw-r--r--target-sparc/helper.h4
-rw-r--r--target-sparc/op_helper.c118
-rw-r--r--target-sparc/translate.c163
-rw-r--r--target-unicore32/exec.h43
-rw-r--r--target-unicore32/op_helper.c3
-rw-r--r--tcg/mips/tcg-target.c2
-rw-r--r--tcg/optimize.c675
-rw-r--r--tcg/tcg.c6
-rw-r--r--tcg/tcg.h3
-rw-r--r--test-coroutine.c192
-rw-r--r--test-qmp-commands.c113
-rw-r--r--test-visitor.c306
-rw-r--r--trace-events43
-rw-r--r--ui/qemu-spice.h2
-rw-r--r--ui/sdl.c2
-rw-r--r--ui/spice-core.c18
-rw-r--r--ui/spice-display.c98
-rw-r--r--ui/spice-display.h28
-rw-r--r--ui/spice-input.c4
-rw-r--r--ui/vnc-auth-sasl.c15
-rw-r--r--ui/vnc-auth-vencrypt.c18
-rw-r--r--ui/vnc-tls.c2
-rw-r--r--ui/vnc.c52
-rw-r--r--ui/vnc.h2
-rw-r--r--usb-bsd.c14
-rw-r--r--usb-linux.c50
-rw-r--r--usb-redir.c1225
-rw-r--r--user-exec.c49
-rw-r--r--vl.c88
-rw-r--r--xen-all.c129
-rw-r--r--xen-mapcache-stub.c36
-rw-r--r--xen-mapcache.c44
-rw-r--r--xen-mapcache.h46
-rw-r--r--xen-stub.c4
412 files changed, 21329 insertions, 7159 deletions
diff --git a/.gitignore b/.gitignore
index 08013fc..59c343c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,6 +15,7 @@ libdis*
libhw32
libhw64
libuser
+qapi-generated
qemu-doc.html
qemu-tech.html
qemu-doc.info
@@ -32,8 +33,10 @@ qemu-options.texi
qemu-img-cmds.texi
qemu-img-cmds.h
qemu-io
+qemu-ga
qemu-monitor.texi
QMP/qmp-commands.txt
+test-coroutine
.gdbinit
*.a
*.aux
diff --git a/CODING_STYLE b/CODING_STYLE
index 5ecfa22..6e61c49 100644
--- a/CODING_STYLE
+++ b/CODING_STYLE
@@ -68,6 +68,10 @@ keyword. Example:
printf("a was something else entirely.\n");
}
+Note that 'else if' is considered a single statement; otherwise a long if/
+else if/else if/.../else sequence would need an indent for every else
+statement.
+
An exception is the opening brace for a function; for reasons of tradition
and clarity it comes on a line by itself:
diff --git a/MAINTAINERS b/MAINTAINERS
index 6115e4e..7cbcd7e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -431,9 +431,10 @@ S: Maintained
F: net/
SLIRP
-M: qemu-devel@nongnu.org
-S: Orphan
+M: Jan Kiszka <jan.kiszka@siemens.com>
+S: Maintained
F: slirp/
+T: git://git.kiszka.org/qemu.git queues/slirp
Usermode Emulation
------------------
diff --git a/Makefile b/Makefile
index b3ffbe2..9e5871b 100644
--- a/Makefile
+++ b/Makefile
@@ -106,6 +106,8 @@ audio/audio.o audio/fmodaudio.o: QEMU_CFLAGS += $(FMOD_CFLAGS)
QEMU_CFLAGS+=$(CURL_CFLAGS)
+QEMU_CFLAGS+=$(GLIB_CFLAGS)
+
ui/cocoa.o: ui/cocoa.m
ui/sdl.o audio/sdlaudio.o ui/sdl_zoom.o baum.o: QEMU_CFLAGS += $(SDL_CFLAGS)
@@ -138,7 +140,7 @@ endif
######################################################################
qemu-img.o: qemu-img-cmds.h
-qemu-img.o qemu-tool.o qemu-nbd.o qemu-io.o cmd.o: $(GENERATED_HEADERS)
+qemu-img.o qemu-tool.o qemu-nbd.o qemu-io.o cmd.o qemu-ga.o: $(GENERATED_HEADERS)
qemu-img$(EXESUF): qemu-img.o qemu-tool.o qemu-error.o $(oslib-obj-y) $(trace-obj-y) $(block-obj-y) $(qobject-obj-y) $(version-obj-y) qemu-timer-common.o
@@ -149,7 +151,7 @@ qemu-io$(EXESUF): qemu-io.o cmd.o qemu-tool.o qemu-error.o $(oslib-obj-y) $(trac
qemu-img-cmds.h: $(SRC_PATH)/qemu-img-cmds.hx
$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@," GEN $@")
-check-qint.o check-qstring.o check-qdict.o check-qlist.o check-qfloat.o check-qjson.o: $(GENERATED_HEADERS)
+check-qint.o check-qstring.o check-qdict.o check-qlist.o check-qfloat.o check-qjson.o test-coroutine.o: $(GENERATED_HEADERS)
CHECK_PROG_DEPS = qemu-malloc.o $(oslib-obj-y) $(trace-obj-y) qemu-tool.o
@@ -159,6 +161,43 @@ check-qdict: check-qdict.o qdict.o qfloat.o qint.o qstring.o qbool.o qlist.o $(C
check-qlist: check-qlist.o qlist.o qint.o $(CHECK_PROG_DEPS)
check-qfloat: check-qfloat.o qfloat.o $(CHECK_PROG_DEPS)
check-qjson: check-qjson.o qfloat.o qint.o qdict.o qstring.o qlist.o qbool.o qjson.o json-streamer.o json-lexer.o json-parser.o error.o qerror.o qemu-error.o $(CHECK_PROG_DEPS)
+test-coroutine: test-coroutine.o qemu-timer-common.o async.o $(coroutine-obj-y) $(CHECK_PROG_DEPS)
+
+$(qapi-obj-y): $(GENERATED_HEADERS)
+qapi-dir := qapi-generated
+test-visitor.o test-qmp-commands.o qemu-ga$(EXESUF): QEMU_CFLAGS += -I $(qapi-dir)
+
+$(qapi-dir)/test-qapi-types.c: $(qapi-dir)/test-qapi-types.h
+$(qapi-dir)/test-qapi-types.h: $(SRC_PATH)/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-types.py
+ $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-types.py -o "$(qapi-dir)" -p "test-" < $<, " GEN $@")
+$(qapi-dir)/test-qapi-visit.c: $(qapi-dir)/test-qapi-visit.h
+$(qapi-dir)/test-qapi-visit.h: $(SRC_PATH)/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-visit.py
+ $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-visit.py -o "$(qapi-dir)" -p "test-" < $<, " GEN $@")
+$(qapi-dir)/test-qmp-commands.h: $(qapi-dir)/test-qmp-marshal.c
+$(qapi-dir)/test-qmp-marshal.c: $(SRC_PATH)/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-commands.py
+ $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-commands.py -o "$(qapi-dir)" -p "test-" < $<, " GEN $@")
+
+$(qapi-dir)/qga-qapi-types.c: $(qapi-dir)/qga-qapi-types.h
+$(qapi-dir)/qga-qapi-types.h: $(SRC_PATH)/qapi-schema-guest.json $(SRC_PATH)/scripts/qapi-types.py
+ $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-types.py -o "$(qapi-dir)" -p "qga-" < $<, " GEN $@")
+$(qapi-dir)/qga-qapi-visit.c: $(qapi-dir)/qga-qapi-visit.h
+$(qapi-dir)/qga-qapi-visit.h: $(SRC_PATH)/qapi-schema-guest.json $(SRC_PATH)/scripts/qapi-visit.py
+ $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-visit.py -o "$(qapi-dir)" -p "qga-" < $<, " GEN $@")
+$(qapi-dir)/qga-qmp-marshal.c: $(SRC_PATH)/qapi-schema-guest.json $(SRC_PATH)/scripts/qapi-commands.py
+ $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-commands.py -o "$(qapi-dir)" -p "qga-" < $<, " GEN $@")
+
+test-visitor.o: $(addprefix $(qapi-dir)/, test-qapi-types.c test-qapi-types.h test-qapi-visit.c test-qapi-visit.h) $(qapi-obj-y)
+test-visitor: test-visitor.o qfloat.o qint.o qdict.o qstring.o qlist.o qbool.o $(qapi-obj-y) error.o osdep.o qemu-malloc.o $(oslib-obj-y) qjson.o json-streamer.o json-lexer.o json-parser.o qerror.o qemu-error.o qemu-tool.o $(qapi-dir)/test-qapi-visit.o $(qapi-dir)/test-qapi-types.o
+
+test-qmp-commands.o: $(addprefix $(qapi-dir)/, test-qapi-types.c test-qapi-types.h test-qapi-visit.c test-qapi-visit.h test-qmp-marshal.c test-qmp-commands.h) $(qapi-obj-y)
+test-qmp-commands: test-qmp-commands.o qfloat.o qint.o qdict.o qstring.o qlist.o qbool.o $(qapi-obj-y) error.o osdep.o qemu-malloc.o $(oslib-obj-y) qjson.o json-streamer.o json-lexer.o json-parser.o qerror.o qemu-error.o qemu-tool.o $(qapi-dir)/test-qapi-visit.o $(qapi-dir)/test-qapi-types.o $(qapi-dir)/test-qmp-marshal.o module.o
+
+QGALIB=qga/guest-agent-command-state.o qga/guest-agent-commands.o
+QGALIB_GEN=$(addprefix $(qapi-dir)/, qga-qapi-types.c qga-qapi-types.h qga-qapi-visit.c qga-qmp-marshal.c)
+
+$(QGALIB_GEN): $(GENERATED_HEADERS)
+$(QGALIB) qemu-ga.o: $(QGALIB_GEN) $(qapi-obj-y)
+qemu-ga$(EXESUF): qemu-ga.o $(QGALIB) qemu-tool.o qemu-error.o error.o $(oslib-obj-y) $(trace-obj-y) $(block-obj-y) $(qobject-obj-y) $(version-obj-y) $(qapi-obj-y) qemu-timer-common.o qemu-sockets.o module.o qapi/qmp-dispatch.o qapi/qmp-registry.o $(qapi-dir)/qga-qapi-visit.o $(qapi-dir)/qga-qapi-types.o $(qapi-dir)/qga-qmp-marshal.o
QEMULIBS=libhw32 libhw64 libuser libdis libdis-user
@@ -166,13 +205,14 @@ 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 *.lo $(TOOLS) TAGS cscope.* *.pod *~ */*~
+ rm -f *.o *.d *.a *.lo $(TOOLS) qemu-ga TAGS cscope.* *.pod *~ */*~
rm -Rf .libs
- 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 slirp/*.o slirp/*.d audio/*.o audio/*.d block/*.o block/*.d net/*.o net/*.d fsdev/*.o fsdev/*.d ui/*.o ui/*.d qapi/*.o qapi/*.d qga/*.o qga/*.d
rm -f qemu-img-cmds.h
rm -f trace.c trace.h trace.c-timestamp trace.h-timestamp
rm -f trace-dtrace.dtrace trace-dtrace.dtrace-timestamp
rm -f trace-dtrace.h trace-dtrace.h-timestamp
+ rm -rf $(qapi-dir)
$(MAKE) -C tests clean
for d in $(ALL_SUBDIRS) $(QEMULIBS) libcacard; do \
if test -d $$d; then $(MAKE) -C $$d $@ || exit 1; fi; \
@@ -187,6 +227,7 @@ distclean: clean
rm -f qemu-doc.fn qemu-doc.fns qemu-doc.info qemu-doc.ky qemu-doc.kys
rm -f qemu-doc.log qemu-doc.pdf qemu-doc.pg qemu-doc.toc qemu-doc.tp
rm -f qemu-doc.vr
+ rm -f config.log
rm -f qemu-tech.info qemu-tech.aux qemu-tech.cp qemu-tech.dvi qemu-tech.fn qemu-tech.info qemu-tech.ky qemu-tech.log qemu-tech.pdf qemu-tech.pg qemu-tech.toc qemu-tech.tp qemu-tech.vr
for d in $(TARGET_DIRS) $(QEMULIBS); do \
rm -rf $$d || exit 1 ; \
@@ -254,7 +295,7 @@ TAGS:
cscope:
rm -f ./cscope.*
- find . -name "*.[ch]" -print | sed 's,^\./,,' > ./cscope.files
+ find "$(SRC_PATH)" -name "*.[chsS]" -print | sed 's,^\./,,' > ./cscope.files
cscope -b
# documentation
@@ -361,4 +402,4 @@ tarbin:
$(mandir)/man8/qemu-nbd.8
# Include automatically generated dependency files
--include $(wildcard *.d audio/*.d slirp/*.d block/*.d net/*.d ui/*.d)
+-include $(wildcard *.d audio/*.d slirp/*.d block/*.d net/*.d ui/*.d qapi/*.d qga/*.d)
diff --git a/Makefile.hw b/Makefile.hw
index b9181ab..659e441 100644
--- a/Makefile.hw
+++ b/Makefile.hw
@@ -9,7 +9,7 @@ include $(SRC_PATH)/rules.mak
$(call set-vpath, $(SRC_PATH):$(SRC_PATH)/hw)
-QEMU_CFLAGS+=-I.. -I$(SRC_PATH)/fpu
+QEMU_CFLAGS+=-I..
include $(SRC_PATH)/Makefile.objs
diff --git a/Makefile.objs b/Makefile.objs
index cea15e4..432b619 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -11,10 +11,21 @@ oslib-obj-$(CONFIG_WIN32) += oslib-win32.o qemu-thread-win32.o
oslib-obj-$(CONFIG_POSIX) += oslib-posix.o qemu-thread-posix.o
#######################################################################
+# coroutines
+coroutine-obj-y = qemu-coroutine.o qemu-coroutine-lock.o
+ifeq ($(CONFIG_UCONTEXT_COROUTINE),y)
+coroutine-obj-$(CONFIG_POSIX) += coroutine-ucontext.o
+else
+coroutine-obj-$(CONFIG_POSIX) += coroutine-gthread.o
+endif
+coroutine-obj-$(CONFIG_WIN32) += coroutine-win32.o
+
+#######################################################################
# block-obj-y is code used by both qemu system emulation and qemu-img
block-obj-y = cutils.o cache-utils.o qemu-malloc.o qemu-option.o module.o async.o
block-obj-y += nbd.o block.o aio.o aes.o qemu-config.o qemu-progress.o qemu-sockets.o
+block-obj-y += $(coroutine-obj-y)
block-obj-$(CONFIG_POSIX) += posix-aio-compat.o
block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o
@@ -89,6 +100,7 @@ common-obj-y += i2c.o smbus.o smbus_eeprom.o
common-obj-y += eeprom93xx.o
common-obj-y += scsi-disk.o cdrom.o
common-obj-y += scsi-generic.o scsi-bus.o
+common-obj-y += hid.o
common-obj-y += usb.o usb-hub.o usb-$(HOST_USB).o usb-hid.o usb-msd.o usb-wacom.o
common-obj-y += usb-serial.o usb-net.o usb-bus.o usb-desc.o
common-obj-$(CONFIG_SSI) += ssi.o
@@ -151,12 +163,12 @@ 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
-slirp-obj-y += tcp_subr.o tcp_timer.o udp.o bootp.o tftp.o
+slirp-obj-y += tcp_subr.o tcp_timer.o udp.o bootp.o tftp.o arp_table.o
common-obj-$(CONFIG_SLIRP) += $(addprefix slirp/, $(slirp-obj-y))
# xen backend driver support
-common-obj-$(CONFIG_XEN) += xen_backend.o xen_devconfig.o
-common-obj-$(CONFIG_XEN) += xen_console.o xenfb.o xen_disk.o xen_nic.o
+common-obj-$(CONFIG_XEN_BACKEND) += xen_backend.o xen_devconfig.o
+common-obj-$(CONFIG_XEN_BACKEND) += xen_console.o xenfb.o xen_disk.o xen_nic.o
######################################################################
# libuser
@@ -171,7 +183,8 @@ user-obj-y += cutils.o cache-utils.o
hw-obj-y =
hw-obj-y += vl.o loader.o
-hw-obj-$(CONFIG_VIRTIO) += virtio.o virtio-console.o
+hw-obj-$(CONFIG_VIRTIO) += virtio-console.o
+hw-obj-y += usb-libhw.o
hw-obj-$(CONFIG_VIRTIO_PCI) += virtio-pci.o
hw-obj-y += fw_cfg.o
hw-obj-$(CONFIG_PCI) += pci.o pci_bridge.o
@@ -205,6 +218,7 @@ hw-obj-$(CONFIG_HPET) += hpet.o
hw-obj-$(CONFIG_APPLESMC) += applesmc.o
hw-obj-$(CONFIG_SMARTCARD) += usb-ccid.o ccid-card-passthru.o
hw-obj-$(CONFIG_SMARTCARD_NSS) += ccid-card-emulated.o
+hw-obj-$(CONFIG_USB_REDIR) += usb-redir.o
# PPC devices
hw-obj-$(CONFIG_OPENPIC) += openpic.o
@@ -372,7 +386,15 @@ endif
libcacard-y = cac.o event.o vcard.o vreader.o vcard_emul_nss.o vcard_emul_type.o card_7816.o
+######################################################################
+# qapi
+
+qapi-nested-y = qapi-visit-core.o qmp-input-visitor.o qmp-output-visitor.o qapi-dealloc-visitor.o
+qapi-nested-y += qmp-registry.o qmp-dispatch.o
+qapi-obj-y = $(addprefix qapi/, $(qapi-nested-y))
+
vl.o: QEMU_CFLAGS+=$(GPROF_CFLAGS)
vl.o: QEMU_CFLAGS+=$(SDL_CFLAGS)
+vl.o: QEMU_CFLAGS+=$(GLIB_CFLAGS)
diff --git a/Makefile.target b/Makefile.target
index a53a2ff..4aacc67 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -3,6 +3,7 @@
GENERATED_HEADERS = config-target.h
CONFIG_NO_PCI = $(if $(subst n,,$(CONFIG_PCI)),n,y)
CONFIG_NO_KVM = $(if $(subst n,,$(CONFIG_KVM)),n,y)
+CONFIG_NO_XEN = $(if $(subst n,,$(CONFIG_XEN)),n,y)
include ../config-host.mak
include config-devices.mak
@@ -71,7 +72,7 @@ all: $(PROGS) stap
#########################################################
# cpu emulator library
libobj-y = exec.o translate-all.o cpu-exec.o translate.o
-libobj-y += tcg/tcg.o
+libobj-y += tcg/tcg.o tcg/optimize.o
libobj-y += fpu/softfloat.o
libobj-y += op_helper.o helper.o
ifeq ($(TARGET_BASE_ARCH), i386)
@@ -94,7 +95,7 @@ tcg/tcg.o: cpu.h
# HELPER_CFLAGS is used for all the code compiled with static register
# variables
-%_helper.o user-exec.o: QEMU_CFLAGS += $(HELPER_CFLAGS)
+op_helper.o user-exec.o: QEMU_CFLAGS += $(HELPER_CFLAGS)
# Note: this is a workaround. The real fix is to avoid compiling
# cpu_signal_handler() in user-exec.c.
@@ -190,34 +191,25 @@ obj-y = arch_init.o cpus.o monitor.o machine.o gdbstub.o balloon.o
# virtio has to be here due to weird dependency between PCI and virtio-net.
# need to fix this properly
obj-$(CONFIG_NO_PCI) += pci-stub.o
-obj-$(CONFIG_VIRTIO) += virtio-blk.o virtio-balloon.o virtio-net.o virtio-serial-bus.o
+obj-$(CONFIG_VIRTIO) += virtio.o virtio-blk.o virtio-balloon.o virtio-net.o virtio-serial-bus.o
obj-y += vhost_net.o
obj-$(CONFIG_VHOST_NET) += vhost.o
obj-$(CONFIG_REALLY_VIRTFS) += 9pfs/virtio-9p-device.o
obj-y += rwhandler.o
obj-$(CONFIG_KVM) += kvm.o kvm-all.o
obj-$(CONFIG_NO_KVM) += kvm-stub.o
+obj-y += memory.o
LIBS+=-lz
QEMU_CFLAGS += $(VNC_TLS_CFLAGS)
QEMU_CFLAGS += $(VNC_SASL_CFLAGS)
QEMU_CFLAGS += $(VNC_JPEG_CFLAGS)
QEMU_CFLAGS += $(VNC_PNG_CFLAGS)
+QEMU_CFLAGS += $(GLIB_CFLAGS)
-# xen backend driver support
-obj-i386-$(CONFIG_XEN) += xen_machine_pv.o xen_domainbuild.o
-
-ifeq ($(TARGET_BASE_ARCH), i386)
- CONFIG_NO_XEN = $(if $(subst n,,$(CONFIG_XEN)),n,y)
-else
- CONFIG_NO_XEN = y
-endif
# xen support
-CONFIG_NO_XEN_MAPCACHE = $(if $(subst n,,$(CONFIG_XEN_MAPCACHE)),n,y)
-obj-i386-$(CONFIG_XEN) += xen-all.o
+obj-$(CONFIG_XEN) += xen-all.o xen_machine_pv.o xen_domainbuild.o xen-mapcache.o
obj-$(CONFIG_NO_XEN) += xen-stub.o
-obj-i386-$(CONFIG_XEN_MAPCACHE) += xen-mapcache.o
-obj-$(CONFIG_NO_XEN_MAPCACHE) += xen-mapcache-stub.o
obj-i386-$(CONFIG_XEN) += xen_platform.o
@@ -360,6 +352,7 @@ obj-arm-y += omap2.o omap_dss.o soc_dma.o omap_gptimer.o omap_synctimer.o \
obj-arm-y += omap_sx1.o palm.o tsc210x.o
obj-arm-y += nseries.o blizzard.o onenand.o vga.o cbus.o tusb6010.o usb-musb.o
obj-arm-y += mst_fpga.o mainstone.o
+obj-arm-y += z2.o
obj-arm-y += musicpal.o bitbang_i2c.o marvell_88w8618_audio.o
obj-arm-y += framebuffer.o
obj-arm-y += syborg.o syborg_fb.o syborg_interrupt.o syborg_keyboard.o
diff --git a/VERSION b/VERSION
index d07c6d0..e653127 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-0.14.50
+0.15.50
diff --git a/arm-semi.c b/arm-semi.c
index 5a62d03..873518a 100644
--- a/arm-semi.c
+++ b/arm-semi.c
@@ -34,6 +34,7 @@
#else
#include "qemu-common.h"
#include "gdbstub.h"
+#include "hw/arm-misc.h"
#endif
#define SYS_OPEN 0x01
@@ -369,68 +370,88 @@ uint32_t do_arm_semihosting(CPUState *env)
return syscall_err;
#endif
case SYS_GET_CMDLINE:
-#ifdef CONFIG_USER_ONLY
- /* Build a commandline from the original argv. */
{
- char *arm_cmdline_buffer;
- const char *host_cmdline_buffer;
+ /* Build a command-line from the original argv.
+ *
+ * The inputs are:
+ * * ARG(0), pointer to a buffer of at least the size
+ * specified in ARG(1).
+ * * ARG(1), size of the buffer pointed to by ARG(0) in
+ * bytes.
+ *
+ * The outputs are:
+ * * ARG(0), pointer to null-terminated string of the
+ * command line.
+ * * ARG(1), length of the string pointed to by ARG(0).
+ */
- unsigned int i;
- unsigned int arm_cmdline_len = ARG(1);
- unsigned int host_cmdline_len =
- ts->info->arg_end-ts->info->arg_start;
+ char *output_buffer;
+ size_t input_size = ARG(1);
+ size_t output_size;
+ int status = 0;
- if (!arm_cmdline_len || host_cmdline_len > arm_cmdline_len) {
- return -1; /* not enough space to store command line */
- }
+ /* Compute the size of the output string. */
+#if !defined(CONFIG_USER_ONLY)
+ output_size = strlen(ts->boot_info->kernel_filename)
+ + 1 /* Separating space. */
+ + strlen(ts->boot_info->kernel_cmdline)
+ + 1; /* Terminating null byte. */
+#else
+ unsigned int i;
- if (!host_cmdline_len) {
+ output_size = ts->info->arg_end - ts->info->arg_start;
+ if (!output_size) {
/* We special-case the "empty command line" case (argc==0).
Just provide the terminating 0. */
- arm_cmdline_buffer = lock_user(VERIFY_WRITE, ARG(0), 1, 0);
- arm_cmdline_buffer[0] = 0;
- unlock_user(arm_cmdline_buffer, ARG(0), 1);
+ output_size = 1;
+ }
+#endif
- /* Adjust the commandline length argument. */
- SET_ARG(1, 0);
- return 0;
+ if (output_size > input_size) {
+ /* Not enough space to store command-line arguments. */
+ return -1;
}
- /* lock the buffers on the ARM side */
- arm_cmdline_buffer =
- lock_user(VERIFY_WRITE, ARG(0), host_cmdline_len, 0);
- host_cmdline_buffer =
- lock_user(VERIFY_READ, ts->info->arg_start,
- host_cmdline_len, 1);
+ /* Adjust the command-line length. */
+ SET_ARG(1, output_size - 1);
- if (arm_cmdline_buffer && host_cmdline_buffer)
- {
- /* the last argument is zero-terminated;
- no need for additional termination */
- memcpy(arm_cmdline_buffer, host_cmdline_buffer,
- host_cmdline_len);
+ /* Lock the buffer on the ARM side. */
+ output_buffer = lock_user(VERIFY_WRITE, ARG(0), output_size, 0);
+ if (!output_buffer) {
+ return -1;
+ }
- /* separate arguments by white spaces */
- for (i = 0; i < host_cmdline_len-1; i++) {
- if (arm_cmdline_buffer[i] == 0) {
- arm_cmdline_buffer[i] = ' ';
- }
- }
+ /* Copy the command-line arguments. */
+#if !defined(CONFIG_USER_ONLY)
+ pstrcpy(output_buffer, output_size, ts->boot_info->kernel_filename);
+ pstrcat(output_buffer, output_size, " ");
+ pstrcat(output_buffer, output_size, ts->boot_info->kernel_cmdline);
+#else
+ if (output_size == 1) {
+ /* Empty command-line. */
+ output_buffer[0] = '\0';
+ goto out;
+ }
- /* Adjust the commandline length argument. */
- SET_ARG(1, host_cmdline_len-1);
+ if (copy_from_user(output_buffer, ts->info->arg_start,
+ output_size)) {
+ status = -1;
+ goto out;
}
- /* Unlock the buffers on the ARM side. */
- unlock_user(arm_cmdline_buffer, ARG(0), host_cmdline_len);
- unlock_user((void*)host_cmdline_buffer, ts->info->arg_start, 0);
+ /* Separate arguments by white spaces. */
+ for (i = 0; i < output_size - 1; i++) {
+ if (output_buffer[i] == 0) {
+ output_buffer[i] = ' ';
+ }
+ }
+ out:
+#endif
+ /* Unlock the buffer on the ARM side. */
+ unlock_user(output_buffer, ARG(0), output_size);
- /* Return success if we could return a commandline. */
- return (arm_cmdline_buffer && host_cmdline_buffer) ? 0 : -1;
+ return status;
}
-#else
- return -1;
-#endif
case SYS_HEAPINFO:
{
uint32_t *ptr;
diff --git a/async.c b/async.c
index fd313df..3fe70b9 100644
--- a/async.c
+++ b/async.c
@@ -25,92 +25,8 @@
#include "qemu-common.h"
#include "qemu-aio.h"
-/*
- * An AsyncContext protects the callbacks of AIO requests and Bottom Halves
- * against interfering with each other. A typical example is qcow2 that accepts
- * asynchronous requests, but relies for manipulation of its metadata on
- * synchronous bdrv_read/write that doesn't trigger any callbacks.
- *
- * However, these functions are often emulated using AIO which means that AIO
- * callbacks must be run - but at the same time we must not run callbacks of
- * other requests as they might start to modify metadata and corrupt the
- * internal state of the caller of bdrv_read/write.
- *
- * To achieve the desired semantics we switch into a new AsyncContext.
- * Callbacks must only be run if they belong to the current AsyncContext.
- * Otherwise they need to be queued until their own context is active again.
- * This is how you can make qemu_aio_wait() wait only for your own callbacks.
- *
- * The AsyncContexts form a stack. When you leave a AsyncContexts, you always
- * return to the old ("parent") context.
- */
-struct AsyncContext {
- /* Consecutive number of the AsyncContext (position in the stack) */
- int id;
-
- /* Anchor of the list of Bottom Halves belonging to the context */
- struct QEMUBH *first_bh;
-
- /* Link to parent context */
- struct AsyncContext *parent;
-};
-
-/* The currently active AsyncContext */
-static struct AsyncContext *async_context = &(struct AsyncContext) { 0 };
-
-/*
- * Enter a new AsyncContext. Already scheduled Bottom Halves and AIO callbacks
- * won't be called until this context is left again.
- */
-void async_context_push(void)
-{
- struct AsyncContext *new = qemu_mallocz(sizeof(*new));
- new->parent = async_context;
- new->id = async_context->id + 1;
- async_context = new;
-}
-
-/* Run queued AIO completions and destroy Bottom Half */
-static void bh_run_aio_completions(void *opaque)
-{
- QEMUBH **bh = opaque;
- qemu_bh_delete(*bh);
- qemu_free(bh);
- qemu_aio_process_queue();
-}
-/*
- * Leave the currently active AsyncContext. All Bottom Halves belonging to the
- * old context are executed before changing the context.
- */
-void async_context_pop(void)
-{
- struct AsyncContext *old = async_context;
- QEMUBH **bh;
-
- /* Flush the bottom halves, we don't want to lose them */
- while (qemu_bh_poll());
-
- /* Switch back to the parent context */
- async_context = async_context->parent;
- qemu_free(old);
-
- if (async_context == NULL) {
- abort();
- }
-
- /* Schedule BH to run any queued AIO completions as soon as possible */
- bh = qemu_malloc(sizeof(*bh));
- *bh = qemu_bh_new(bh_run_aio_completions, bh);
- qemu_bh_schedule(*bh);
-}
-
-/*
- * Returns the ID of the currently active AsyncContext
- */
-int get_async_context_id(void)
-{
- return async_context->id;
-}
+/* Anchor of the list of Bottom Halves belonging to the context */
+static struct QEMUBH *first_bh;
/***********************************************************/
/* bottom halves (can be seen as timers which expire ASAP) */
@@ -130,8 +46,8 @@ QEMUBH *qemu_bh_new(QEMUBHFunc *cb, void *opaque)
bh = qemu_mallocz(sizeof(QEMUBH));
bh->cb = cb;
bh->opaque = opaque;
- bh->next = async_context->first_bh;
- async_context->first_bh = bh;
+ bh->next = first_bh;
+ first_bh = bh;
return bh;
}
@@ -141,7 +57,7 @@ int qemu_bh_poll(void)
int ret;
ret = 0;
- for (bh = async_context->first_bh; bh; bh = next) {
+ for (bh = first_bh; bh; bh = next) {
next = bh->next;
if (!bh->deleted && bh->scheduled) {
bh->scheduled = 0;
@@ -153,7 +69,7 @@ int qemu_bh_poll(void)
}
/* remove deleted bhs */
- bhp = &async_context->first_bh;
+ bhp = &first_bh;
while (*bhp) {
bh = *bhp;
if (bh->deleted) {
@@ -199,7 +115,7 @@ void qemu_bh_update_timeout(int *timeout)
{
QEMUBH *bh;
- for (bh = async_context->first_bh; bh; bh = bh->next) {
+ for (bh = first_bh; bh; bh = bh->next) {
if (!bh->deleted && bh->scheduled) {
if (bh->idle) {
/* idle bottom halves will be polled at least
diff --git a/balloon.c b/balloon.c
index 248c1b5..f56fdc1 100644
--- a/balloon.c
+++ b/balloon.c
@@ -1,7 +1,9 @@
/*
- * QEMU System Emulator
+ * Generic Balloon handlers and management
*
* Copyright (c) 2003-2008 Fabrice Bellard
+ * Copyright (C) 2011 Red Hat, Inc.
+ * Copyright (C) 2011 Amit Shah <amit.shah@redhat.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
@@ -30,44 +32,53 @@
#include "balloon.h"
#include "trace.h"
+static QEMUBalloonEvent *balloon_event_fn;
+static QEMUBalloonStatus *balloon_stat_fn;
+static void *balloon_opaque;
-static QEMUBalloonEvent *qemu_balloon_event;
-void *qemu_balloon_event_opaque;
-
-void qemu_add_balloon_handler(QEMUBalloonEvent *func, void *opaque)
+int qemu_add_balloon_handler(QEMUBalloonEvent *event_func,
+ QEMUBalloonStatus *stat_func, void *opaque)
{
- qemu_balloon_event = func;
- qemu_balloon_event_opaque = opaque;
+ if (balloon_event_fn || balloon_stat_fn || balloon_opaque) {
+ /* We're already registered one balloon handler. How many can
+ * a guest really have?
+ */
+ error_report("Another balloon device already registered");
+ return -1;
+ }
+ balloon_event_fn = event_func;
+ balloon_stat_fn = stat_func;
+ balloon_opaque = opaque;
+ return 0;
}
-int qemu_balloon(ram_addr_t target, MonitorCompletion cb, void *opaque)
+static int qemu_balloon(ram_addr_t target)
{
- 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 {
+ if (!balloon_event_fn) {
return 0;
}
+ trace_balloon_event(balloon_opaque, target);
+ balloon_event_fn(balloon_opaque, target);
+ return 1;
}
-int qemu_balloon_status(MonitorCompletion cb, void *opaque)
+static int qemu_balloon_status(MonitorCompletion cb, void *opaque)
{
- if (qemu_balloon_event) {
- qemu_balloon_event(qemu_balloon_event_opaque, 0, cb, opaque);
- return 1;
- } else {
+ if (!balloon_stat_fn) {
return 0;
}
+ balloon_stat_fn(balloon_opaque, cb, opaque);
+ return 1;
}
static void print_balloon_stat(const char *key, QObject *obj, void *opaque)
{
Monitor *mon = opaque;
- if (strcmp(key, "actual"))
+ if (strcmp(key, "actual")) {
monitor_printf(mon, ",%s=%" PRId64, key,
qint_get_int(qobject_to_qint(obj)));
+ }
}
void monitor_print_balloon(Monitor *mon, const QObject *data)
@@ -75,9 +86,9 @@ void monitor_print_balloon(Monitor *mon, const QObject *data)
QDict *qdict;
qdict = qobject_to_qdict(data);
- if (!qdict_haskey(qdict, "actual"))
+ if (!qdict_haskey(qdict, "actual")) {
return;
-
+ }
monitor_printf(mon, "balloon: actual=%" PRId64,
qdict_get_int(qdict, "actual") >> 20);
qdict_iter(qdict, print_balloon_stat, mon);
@@ -129,6 +140,7 @@ int do_info_balloon(Monitor *mon, MonitorCompletion cb, void *opaque)
int do_balloon(Monitor *mon, const QDict *params,
MonitorCompletion cb, void *opaque)
{
+ int64_t target;
int ret;
if (kvm_enabled() && !kvm_has_sync_mmu()) {
@@ -136,7 +148,12 @@ int do_balloon(Monitor *mon, const QDict *params,
return -1;
}
- ret = qemu_balloon(qdict_get_int(params, "value"), cb, opaque);
+ target = qdict_get_int(params, "value");
+ if (target <= 0) {
+ qerror_report(QERR_INVALID_PARAMETER_VALUE, "target", "a size");
+ return -1;
+ }
+ ret = qemu_balloon(target);
if (ret == 0) {
qerror_report(QERR_DEVICE_NOT_ACTIVE, "balloon");
return -1;
diff --git a/balloon.h b/balloon.h
index d478e28..3df14e6 100644
--- a/balloon.h
+++ b/balloon.h
@@ -16,14 +16,12 @@
#include "monitor.h"
-typedef void (QEMUBalloonEvent)(void *opaque, ram_addr_t target,
- MonitorCompletion cb, void *cb_data);
+typedef void (QEMUBalloonEvent)(void *opaque, ram_addr_t target);
+typedef void (QEMUBalloonStatus)(void *opaque, MonitorCompletion cb,
+ void *cb_data);
-void qemu_add_balloon_handler(QEMUBalloonEvent *func, void *opaque);
-
-int qemu_balloon(ram_addr_t target, MonitorCompletion cb, void *opaque);
-
-int qemu_balloon_status(MonitorCompletion cb, void *opaque);
+int qemu_add_balloon_handler(QEMUBalloonEvent *event_func,
+ QEMUBalloonStatus *stat_func, void *opaque);
void monitor_print_balloon(Monitor *mon, const QObject *data);
int do_info_balloon(Monitor *mon, MonitorCompletion cb, void *opaque);
diff --git a/block.c b/block.c
index 24a25d5..26910ca 100644
--- a/block.c
+++ b/block.c
@@ -28,6 +28,7 @@
#include "block_int.h"
#include "module.h"
#include "qemu-objects.h"
+#include "qemu-coroutine.h"
#ifdef CONFIG_BSD
#include <sys/types.h>
@@ -57,6 +58,19 @@ static int bdrv_read_em(BlockDriverState *bs, int64_t sector_num,
uint8_t *buf, int nb_sectors);
static int bdrv_write_em(BlockDriverState *bs, int64_t sector_num,
const uint8_t *buf, int nb_sectors);
+static BlockDriverAIOCB *bdrv_co_aio_readv_em(BlockDriverState *bs,
+ int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
+ BlockDriverCompletionFunc *cb, void *opaque);
+static BlockDriverAIOCB *bdrv_co_aio_writev_em(BlockDriverState *bs,
+ int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
+ BlockDriverCompletionFunc *cb, void *opaque);
+static int coroutine_fn bdrv_co_readv_em(BlockDriverState *bs,
+ int64_t sector_num, int nb_sectors,
+ QEMUIOVector *iov);
+static int coroutine_fn bdrv_co_writev_em(BlockDriverState *bs,
+ int64_t sector_num, int nb_sectors,
+ QEMUIOVector *iov);
+static int coroutine_fn bdrv_co_flush_em(BlockDriverState *bs);
static QTAILQ_HEAD(, BlockDriverState) bdrv_states =
QTAILQ_HEAD_INITIALIZER(bdrv_states);
@@ -169,14 +183,25 @@ void path_combine(char *dest, int dest_size,
void bdrv_register(BlockDriver *bdrv)
{
- if (!bdrv->bdrv_aio_readv) {
- /* add AIO emulation layer */
- bdrv->bdrv_aio_readv = bdrv_aio_readv_em;
- bdrv->bdrv_aio_writev = bdrv_aio_writev_em;
- } else if (!bdrv->bdrv_read) {
- /* add synchronous IO emulation layer */
+ if (bdrv->bdrv_co_readv) {
+ /* Emulate AIO by coroutines, and sync by AIO */
+ bdrv->bdrv_aio_readv = bdrv_co_aio_readv_em;
+ bdrv->bdrv_aio_writev = bdrv_co_aio_writev_em;
bdrv->bdrv_read = bdrv_read_em;
bdrv->bdrv_write = bdrv_write_em;
+ } else {
+ bdrv->bdrv_co_readv = bdrv_co_readv_em;
+ bdrv->bdrv_co_writev = bdrv_co_writev_em;
+
+ if (!bdrv->bdrv_aio_readv) {
+ /* add AIO emulation layer */
+ bdrv->bdrv_aio_readv = bdrv_aio_readv_em;
+ bdrv->bdrv_aio_writev = bdrv_aio_writev_em;
+ } else if (!bdrv->bdrv_read) {
+ /* add synchronous IO emulation layer */
+ bdrv->bdrv_read = bdrv_read_em;
+ bdrv->bdrv_write = bdrv_write_em;
+ }
}
if (!bdrv->bdrv_aio_flush)
@@ -730,6 +755,8 @@ void bdrv_detach(BlockDriverState *bs, DeviceState *qdev)
{
assert(bs->peer == qdev);
bs->peer = NULL;
+ bs->change_cb = NULL;
+ bs->change_opaque = NULL;
}
DeviceState *bdrv_get_attached(BlockDriverState *bs)
@@ -920,6 +947,17 @@ static int bdrv_check_request(BlockDriverState *bs, int64_t sector_num,
nb_sectors * BDRV_SECTOR_SIZE);
}
+static inline bool bdrv_has_async_rw(BlockDriver *drv)
+{
+ return drv->bdrv_co_readv != bdrv_co_readv_em
+ || drv->bdrv_aio_readv != bdrv_aio_readv_em;
+}
+
+static inline bool bdrv_has_async_flush(BlockDriver *drv)
+{
+ return drv->bdrv_aio_flush != bdrv_aio_flush_em;
+}
+
/* return < 0 if error. See bdrv_write() for the return codes */
int bdrv_read(BlockDriverState *bs, int64_t sector_num,
uint8_t *buf, int nb_sectors)
@@ -928,6 +966,18 @@ int bdrv_read(BlockDriverState *bs, int64_t sector_num,
if (!drv)
return -ENOMEDIUM;
+
+ if (bdrv_has_async_rw(drv) && qemu_in_coroutine()) {
+ QEMUIOVector qiov;
+ struct iovec iov = {
+ .iov_base = (void *)buf,
+ .iov_len = nb_sectors * BDRV_SECTOR_SIZE,
+ };
+
+ qemu_iovec_init_external(&qiov, &iov, 1);
+ return bdrv_co_readv(bs, sector_num, nb_sectors, &qiov);
+ }
+
if (bdrv_check_request(bs, sector_num, nb_sectors))
return -EIO;
@@ -972,8 +1022,21 @@ int bdrv_write(BlockDriverState *bs, int64_t sector_num,
const uint8_t *buf, int nb_sectors)
{
BlockDriver *drv = bs->drv;
+
if (!bs->drv)
return -ENOMEDIUM;
+
+ if (bdrv_has_async_rw(drv) && qemu_in_coroutine()) {
+ QEMUIOVector qiov;
+ struct iovec iov = {
+ .iov_base = (void *)buf,
+ .iov_len = nb_sectors * BDRV_SECTOR_SIZE,
+ };
+
+ qemu_iovec_init_external(&qiov, &iov, 1);
+ return bdrv_co_writev(bs, sector_num, nb_sectors, &qiov);
+ }
+
if (bs->read_only)
return -EACCES;
if (bdrv_check_request(bs, sector_num, nb_sectors))
@@ -1108,17 +1171,49 @@ int bdrv_pwrite_sync(BlockDriverState *bs, int64_t offset,
return 0;
}
-/*
- * Writes to the file and ensures that no writes are reordered across this
- * request (acts as a barrier)
- *
- * Returns 0 on success, -errno in error cases.
- */
-int bdrv_write_sync(BlockDriverState *bs, int64_t sector_num,
- const uint8_t *buf, int nb_sectors)
+int coroutine_fn bdrv_co_readv(BlockDriverState *bs, int64_t sector_num,
+ int nb_sectors, QEMUIOVector *qiov)
{
- return bdrv_pwrite_sync(bs, BDRV_SECTOR_SIZE * sector_num,
- buf, BDRV_SECTOR_SIZE * nb_sectors);
+ BlockDriver *drv = bs->drv;
+
+ trace_bdrv_co_readv(bs, sector_num, nb_sectors);
+
+ if (!drv) {
+ return -ENOMEDIUM;
+ }
+ if (bdrv_check_request(bs, sector_num, nb_sectors)) {
+ return -EIO;
+ }
+
+ return drv->bdrv_co_readv(bs, sector_num, nb_sectors, qiov);
+}
+
+int coroutine_fn bdrv_co_writev(BlockDriverState *bs, int64_t sector_num,
+ int nb_sectors, QEMUIOVector *qiov)
+{
+ BlockDriver *drv = bs->drv;
+
+ trace_bdrv_co_writev(bs, sector_num, nb_sectors);
+
+ if (!bs->drv) {
+ return -ENOMEDIUM;
+ }
+ if (bs->read_only) {
+ return -EACCES;
+ }
+ if (bdrv_check_request(bs, sector_num, nb_sectors)) {
+ return -EIO;
+ }
+
+ if (bs->dirty_bitmap) {
+ set_dirty_bitmap(bs, sector_num, nb_sectors, 1);
+ }
+
+ if (bs->wr_highest_sector < sector_num + nb_sectors - 1) {
+ bs->wr_highest_sector = sector_num + nb_sectors - 1;
+ }
+
+ return drv->bdrv_co_writev(bs, sector_num, nb_sectors, qiov);
}
/**
@@ -1147,6 +1242,25 @@ int bdrv_truncate(BlockDriverState *bs, int64_t offset)
}
/**
+ * Length of a allocated file in bytes. Sparse files are counted by actual
+ * allocated space. Return < 0 if error or unknown.
+ */
+int64_t bdrv_get_allocated_file_size(BlockDriverState *bs)
+{
+ BlockDriver *drv = bs->drv;
+ if (!drv) {
+ return -ENOMEDIUM;
+ }
+ if (drv->bdrv_get_allocated_file_size) {
+ return drv->bdrv_get_allocated_file_size(bs);
+ }
+ if (bs->file) {
+ return bdrv_get_allocated_file_size(bs->file);
+ }
+ return -ENOTSUP;
+}
+
+/**
* Length of a file in bytes. Return < 0 if error or unknown.
*/
int64_t bdrv_getlength(BlockDriverState *bs)
@@ -1572,6 +1686,10 @@ int bdrv_flush(BlockDriverState *bs)
return 0;
}
+ if (bs->drv && bdrv_has_async_flush(bs->drv) && qemu_in_coroutine()) {
+ return bdrv_co_flush_em(bs);
+ }
+
if (bs->drv && bs->drv->bdrv_flush) {
return bs->drv->bdrv_flush(bs);
}
@@ -2561,6 +2679,89 @@ static BlockDriverAIOCB *bdrv_aio_writev_em(BlockDriverState *bs,
return bdrv_aio_rw_vector(bs, sector_num, qiov, nb_sectors, cb, opaque, 1);
}
+
+typedef struct BlockDriverAIOCBCoroutine {
+ BlockDriverAIOCB common;
+ BlockRequest req;
+ bool is_write;
+ QEMUBH* bh;
+} BlockDriverAIOCBCoroutine;
+
+static void bdrv_aio_co_cancel_em(BlockDriverAIOCB *blockacb)
+{
+ qemu_aio_flush();
+}
+
+static AIOPool bdrv_em_co_aio_pool = {
+ .aiocb_size = sizeof(BlockDriverAIOCBCoroutine),
+ .cancel = bdrv_aio_co_cancel_em,
+};
+
+static void bdrv_co_rw_bh(void *opaque)
+{
+ BlockDriverAIOCBCoroutine *acb = opaque;
+
+ acb->common.cb(acb->common.opaque, acb->req.error);
+ qemu_bh_delete(acb->bh);
+ qemu_aio_release(acb);
+}
+
+static void coroutine_fn bdrv_co_rw(void *opaque)
+{
+ BlockDriverAIOCBCoroutine *acb = opaque;
+ BlockDriverState *bs = acb->common.bs;
+
+ if (!acb->is_write) {
+ acb->req.error = bs->drv->bdrv_co_readv(bs, acb->req.sector,
+ acb->req.nb_sectors, acb->req.qiov);
+ } else {
+ acb->req.error = bs->drv->bdrv_co_writev(bs, acb->req.sector,
+ acb->req.nb_sectors, acb->req.qiov);
+ }
+
+ acb->bh = qemu_bh_new(bdrv_co_rw_bh, acb);
+ qemu_bh_schedule(acb->bh);
+}
+
+static BlockDriverAIOCB *bdrv_co_aio_rw_vector(BlockDriverState *bs,
+ int64_t sector_num,
+ QEMUIOVector *qiov,
+ int nb_sectors,
+ BlockDriverCompletionFunc *cb,
+ void *opaque,
+ bool is_write)
+{
+ Coroutine *co;
+ BlockDriverAIOCBCoroutine *acb;
+
+ acb = qemu_aio_get(&bdrv_em_co_aio_pool, bs, cb, opaque);
+ acb->req.sector = sector_num;
+ acb->req.nb_sectors = nb_sectors;
+ acb->req.qiov = qiov;
+ acb->is_write = is_write;
+
+ co = qemu_coroutine_create(bdrv_co_rw);
+ qemu_coroutine_enter(co, acb);
+
+ return &acb->common;
+}
+
+static BlockDriverAIOCB *bdrv_co_aio_readv_em(BlockDriverState *bs,
+ int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
+ BlockDriverCompletionFunc *cb, void *opaque)
+{
+ return bdrv_co_aio_rw_vector(bs, sector_num, qiov, nb_sectors, cb, opaque,
+ false);
+}
+
+static BlockDriverAIOCB *bdrv_co_aio_writev_em(BlockDriverState *bs,
+ int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
+ BlockDriverCompletionFunc *cb, void *opaque)
+{
+ return bdrv_co_aio_rw_vector(bs, sector_num, qiov, nb_sectors, cb, opaque,
+ true);
+}
+
static BlockDriverAIOCB *bdrv_aio_flush_em(BlockDriverState *bs,
BlockDriverCompletionFunc *cb, void *opaque)
{
@@ -2617,8 +2818,6 @@ static int bdrv_read_em(BlockDriverState *bs, int64_t sector_num,
struct iovec iov;
QEMUIOVector qiov;
- async_context_push();
-
async_ret = NOT_DONE;
iov.iov_base = (void *)buf;
iov.iov_len = nb_sectors * BDRV_SECTOR_SIZE;
@@ -2636,7 +2835,6 @@ static int bdrv_read_em(BlockDriverState *bs, int64_t sector_num,
fail:
- async_context_pop();
return async_ret;
}
@@ -2648,8 +2846,6 @@ static int bdrv_write_em(BlockDriverState *bs, int64_t sector_num,
struct iovec iov;
QEMUIOVector qiov;
- async_context_push();
-
async_ret = NOT_DONE;
iov.iov_base = (void *)buf;
iov.iov_len = nb_sectors * BDRV_SECTOR_SIZE;
@@ -2665,7 +2861,6 @@ static int bdrv_write_em(BlockDriverState *bs, int64_t sector_num,
}
fail:
- async_context_pop();
return async_ret;
}
@@ -2707,6 +2902,77 @@ void qemu_aio_release(void *p)
}
/**************************************************************/
+/* Coroutine block device emulation */
+
+typedef struct CoroutineIOCompletion {
+ Coroutine *coroutine;
+ int ret;
+} CoroutineIOCompletion;
+
+static void bdrv_co_io_em_complete(void *opaque, int ret)
+{
+ CoroutineIOCompletion *co = opaque;
+
+ co->ret = ret;
+ qemu_coroutine_enter(co->coroutine, NULL);
+}
+
+static int coroutine_fn bdrv_co_io_em(BlockDriverState *bs, int64_t sector_num,
+ int nb_sectors, QEMUIOVector *iov,
+ bool is_write)
+{
+ CoroutineIOCompletion co = {
+ .coroutine = qemu_coroutine_self(),
+ };
+ BlockDriverAIOCB *acb;
+
+ if (is_write) {
+ acb = bdrv_aio_writev(bs, sector_num, iov, nb_sectors,
+ bdrv_co_io_em_complete, &co);
+ } else {
+ acb = bdrv_aio_readv(bs, sector_num, iov, nb_sectors,
+ bdrv_co_io_em_complete, &co);
+ }
+
+ trace_bdrv_co_io(is_write, acb);
+ if (!acb) {
+ return -EIO;
+ }
+ qemu_coroutine_yield();
+
+ return co.ret;
+}
+
+static int coroutine_fn bdrv_co_readv_em(BlockDriverState *bs,
+ int64_t sector_num, int nb_sectors,
+ QEMUIOVector *iov)
+{
+ return bdrv_co_io_em(bs, sector_num, nb_sectors, iov, false);
+}
+
+static int coroutine_fn bdrv_co_writev_em(BlockDriverState *bs,
+ int64_t sector_num, int nb_sectors,
+ QEMUIOVector *iov)
+{
+ return bdrv_co_io_em(bs, sector_num, nb_sectors, iov, true);
+}
+
+static int coroutine_fn bdrv_co_flush_em(BlockDriverState *bs)
+{
+ CoroutineIOCompletion co = {
+ .coroutine = qemu_coroutine_self(),
+ };
+ BlockDriverAIOCB *acb;
+
+ acb = bdrv_aio_flush(bs, bdrv_co_io_em_complete, &co);
+ if (!acb) {
+ return -EIO;
+ }
+ qemu_coroutine_yield();
+ return co.ret;
+}
+
+/**************************************************************/
/* removable device support */
/**
@@ -2749,25 +3015,16 @@ int bdrv_media_changed(BlockDriverState *bs)
int bdrv_eject(BlockDriverState *bs, int eject_flag)
{
BlockDriver *drv = bs->drv;
- int ret;
- if (bs->locked) {
+ if (eject_flag && bs->locked) {
return -EBUSY;
}
- if (!drv || !drv->bdrv_eject) {
- ret = -ENOTSUP;
- } else {
- ret = drv->bdrv_eject(bs, eject_flag);
- }
- if (ret == -ENOTSUP) {
- ret = 0;
+ if (drv && drv->bdrv_eject) {
+ drv->bdrv_eject(bs, eject_flag);
}
- if (ret >= 0) {
- bs->tray_open = eject_flag;
- }
-
- return ret;
+ bs->tray_open = eject_flag;
+ return 0;
}
int bdrv_is_locked(BlockDriverState *bs)
diff --git a/block.h b/block.h
index 859d1d9..a3bfaaf 100644
--- a/block.h
+++ b/block.h
@@ -4,6 +4,7 @@
#include "qemu-aio.h"
#include "qemu-common.h"
#include "qemu-option.h"
+#include "qemu-coroutine.h"
#include "qobject.h"
/* block.c */
@@ -85,10 +86,13 @@ int bdrv_pwrite(BlockDriverState *bs, int64_t offset,
const void *buf, int count);
int bdrv_pwrite_sync(BlockDriverState *bs, int64_t offset,
const void *buf, int count);
-int bdrv_write_sync(BlockDriverState *bs, int64_t sector_num,
- const uint8_t *buf, int nb_sectors);
+int coroutine_fn bdrv_co_readv(BlockDriverState *bs, int64_t sector_num,
+ int nb_sectors, QEMUIOVector *qiov);
+int coroutine_fn bdrv_co_writev(BlockDriverState *bs, int64_t sector_num,
+ int nb_sectors, QEMUIOVector *qiov);
int bdrv_truncate(BlockDriverState *bs, int64_t offset);
int64_t bdrv_getlength(BlockDriverState *bs);
+int64_t bdrv_get_allocated_file_size(BlockDriverState *bs);
void bdrv_get_geometry(BlockDriverState *bs, uint64_t *nb_sectors_ptr);
void bdrv_guess_geometry(BlockDriverState *bs, int *pcyls, int *pheads, int *psecs);
int bdrv_commit(BlockDriverState *bs);
diff --git a/block/qcow.c b/block/qcow.c
index 227b104..6447c2a 100644
--- a/block/qcow.c
+++ b/block/qcow.c
@@ -73,6 +73,7 @@ typedef struct BDRVQcowState {
uint32_t crypt_method_header;
AES_KEY aes_encrypt_key;
AES_KEY aes_decrypt_key;
+ CoMutex lock;
} BDRVQcowState;
static int decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset);
@@ -517,11 +518,11 @@ static AIOPool qcow_aio_pool = {
static QCowAIOCB *qcow_aio_setup(BlockDriverState *bs,
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
- BlockDriverCompletionFunc *cb, void *opaque, int is_write)
+ int is_write)
{
QCowAIOCB *acb;
- acb = qemu_aio_get(&qcow_aio_pool, bs, cb, opaque);
+ acb = qemu_aio_get(&qcow_aio_pool, bs, NULL, NULL);
if (!acb)
return NULL;
acb->hd_aiocb = NULL;
@@ -542,48 +543,15 @@ static QCowAIOCB *qcow_aio_setup(BlockDriverState *bs,
return acb;
}
-static void qcow_aio_read_cb(void *opaque, int ret);
-static void qcow_aio_write_cb(void *opaque, int ret);
-
-static void qcow_aio_rw_bh(void *opaque)
-{
- QCowAIOCB *acb = opaque;
- qemu_bh_delete(acb->bh);
- acb->bh = NULL;
-
- if (acb->is_write) {
- qcow_aio_write_cb(opaque, 0);
- } else {
- qcow_aio_read_cb(opaque, 0);
- }
-}
-
-static int qcow_schedule_bh(QEMUBHFunc *cb, QCowAIOCB *acb)
-{
- if (acb->bh) {
- return -EIO;
- }
-
- acb->bh = qemu_bh_new(cb, acb);
- if (!acb->bh) {
- return -EIO;
- }
-
- qemu_bh_schedule(acb->bh);
-
- return 0;
-}
-
-static void qcow_aio_read_cb(void *opaque, int ret)
+static int qcow_aio_read_cb(void *opaque)
{
QCowAIOCB *acb = opaque;
BlockDriverState *bs = acb->common.bs;
BDRVQcowState *s = bs->opaque;
int index_in_cluster;
+ int ret;
acb->hd_aiocb = NULL;
- if (ret < 0)
- goto done;
redo:
/* post process the read buffer */
@@ -605,8 +573,7 @@ static void qcow_aio_read_cb(void *opaque, int ret)
if (acb->nb_sectors == 0) {
/* request completed */
- ret = 0;
- goto done;
+ return 0;
}
/* prepare next AIO request */
@@ -623,11 +590,12 @@ static void qcow_aio_read_cb(void *opaque, int ret)
acb->hd_iov.iov_base = (void *)acb->buf;
acb->hd_iov.iov_len = acb->n * 512;
qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1);
- acb->hd_aiocb = bdrv_aio_readv(bs->backing_hd, acb->sector_num,
- &acb->hd_qiov, acb->n, qcow_aio_read_cb, acb);
- if (acb->hd_aiocb == NULL) {
- ret = -EIO;
- goto done;
+ qemu_co_mutex_unlock(&s->lock);
+ ret = bdrv_co_readv(bs->backing_hd, acb->sector_num,
+ acb->n, &acb->hd_qiov);
+ qemu_co_mutex_lock(&s->lock);
+ if (ret < 0) {
+ return -EIO;
}
} else {
/* Note: in this case, no need to wait */
@@ -637,64 +605,56 @@ static void qcow_aio_read_cb(void *opaque, int ret)
} else if (acb->cluster_offset & QCOW_OFLAG_COMPRESSED) {
/* add AIO support for compressed blocks ? */
if (decompress_cluster(bs, acb->cluster_offset) < 0) {
- ret = -EIO;
- goto done;
+ return -EIO;
}
memcpy(acb->buf,
s->cluster_cache + index_in_cluster * 512, 512 * acb->n);
goto redo;
} else {
if ((acb->cluster_offset & 511) != 0) {
- ret = -EIO;
- goto done;
+ return -EIO;
}
acb->hd_iov.iov_base = (void *)acb->buf;
acb->hd_iov.iov_len = acb->n * 512;
qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1);
- acb->hd_aiocb = bdrv_aio_readv(bs->file,
+ qemu_co_mutex_unlock(&s->lock);
+ ret = bdrv_co_readv(bs->file,
(acb->cluster_offset >> 9) + index_in_cluster,
- &acb->hd_qiov, acb->n, qcow_aio_read_cb, acb);
- if (acb->hd_aiocb == NULL) {
- ret = -EIO;
- goto done;
+ acb->n, &acb->hd_qiov);
+ qemu_co_mutex_lock(&s->lock);
+ if (ret < 0) {
+ return 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_aio_release(acb);
+ return 1;
}
-static BlockDriverAIOCB *qcow_aio_readv(BlockDriverState *bs,
- int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
- BlockDriverCompletionFunc *cb, void *opaque)
+static int qcow_co_readv(BlockDriverState *bs, int64_t sector_num,
+ int nb_sectors, QEMUIOVector *qiov)
{
+ BDRVQcowState *s = bs->opaque;
QCowAIOCB *acb;
int ret;
- acb = qcow_aio_setup(bs, sector_num, qiov, nb_sectors, cb, opaque, 0);
- if (!acb)
- return NULL;
+ acb = qcow_aio_setup(bs, sector_num, qiov, nb_sectors, 0);
- ret = qcow_schedule_bh(qcow_aio_rw_bh, acb);
- if (ret < 0) {
- if (acb->qiov->niov > 1) {
- qemu_vfree(acb->orig_buf);
- }
- qemu_aio_release(acb);
- return NULL;
+ qemu_co_mutex_lock(&s->lock);
+ do {
+ ret = qcow_aio_read_cb(acb);
+ } while (ret > 0);
+ qemu_co_mutex_unlock(&s->lock);
+
+ if (acb->qiov->niov > 1) {
+ qemu_iovec_from_buffer(acb->qiov, acb->orig_buf, acb->qiov->size);
+ qemu_vfree(acb->orig_buf);
}
+ qemu_aio_release(acb);
- return &acb->common;
+ return ret;
}
-static void qcow_aio_write_cb(void *opaque, int ret)
+static int qcow_aio_write_cb(void *opaque)
{
QCowAIOCB *acb = opaque;
BlockDriverState *bs = acb->common.bs;
@@ -702,20 +662,17 @@ static void qcow_aio_write_cb(void *opaque, int ret)
int index_in_cluster;
uint64_t cluster_offset;
const uint8_t *src_buf;
+ int ret;
acb->hd_aiocb = NULL;
- if (ret < 0)
- goto done;
-
acb->nb_sectors -= acb->n;
acb->sector_num += acb->n;
acb->buf += acb->n * 512;
if (acb->nb_sectors == 0) {
/* request completed */
- ret = 0;
- goto done;
+ return 0;
}
index_in_cluster = acb->sector_num & (s->cluster_sectors - 1);
@@ -726,16 +683,11 @@ static void qcow_aio_write_cb(void *opaque, int ret)
index_in_cluster,
index_in_cluster + acb->n);
if (!cluster_offset || (cluster_offset & 511) != 0) {
- ret = -EIO;
- goto done;
+ return -EIO;
}
if (s->crypt_method) {
if (!acb->cluster_data) {
acb->cluster_data = qemu_mallocz(s->cluster_size);
- if (!acb->cluster_data) {
- ret = -ENOMEM;
- goto done;
- }
}
encrypt_sectors(s, acb->sector_num, acb->cluster_data, acb->buf,
acb->n, 1, &s->aes_encrypt_key);
@@ -747,26 +699,19 @@ static void qcow_aio_write_cb(void *opaque, int ret)
acb->hd_iov.iov_base = (void *)src_buf;
acb->hd_iov.iov_len = acb->n * 512;
qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1);
- acb->hd_aiocb = bdrv_aio_writev(bs->file,
- (cluster_offset >> 9) + index_in_cluster,
- &acb->hd_qiov, acb->n,
- qcow_aio_write_cb, acb);
- if (acb->hd_aiocb == NULL) {
- ret = -EIO;
- goto done;
+ qemu_co_mutex_unlock(&s->lock);
+ ret = bdrv_co_writev(bs->file,
+ (cluster_offset >> 9) + index_in_cluster,
+ acb->n, &acb->hd_qiov);
+ qemu_co_mutex_lock(&s->lock);
+ if (ret < 0) {
+ return ret;
}
- return;
-
-done:
- if (acb->qiov->niov > 1)
- qemu_vfree(acb->orig_buf);
- acb->common.cb(acb->common.opaque, ret);
- qemu_aio_release(acb);
+ return 1;
}
-static BlockDriverAIOCB *qcow_aio_writev(BlockDriverState *bs,
- int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
- BlockDriverCompletionFunc *cb, void *opaque)
+static int qcow_co_writev(BlockDriverState *bs, int64_t sector_num,
+ int nb_sectors, QEMUIOVector *qiov)
{
BDRVQcowState *s = bs->opaque;
QCowAIOCB *acb;
@@ -774,21 +719,20 @@ static BlockDriverAIOCB *qcow_aio_writev(BlockDriverState *bs,
s->cluster_cache_offset = -1; /* disable compressed cache */
- acb = qcow_aio_setup(bs, sector_num, qiov, nb_sectors, cb, opaque, 1);
- if (!acb)
- return NULL;
+ acb = qcow_aio_setup(bs, sector_num, qiov, nb_sectors, 1);
+ qemu_co_mutex_lock(&s->lock);
+ do {
+ ret = qcow_aio_write_cb(acb);
+ } while (ret > 0);
+ qemu_co_mutex_unlock(&s->lock);
- ret = qcow_schedule_bh(qcow_aio_rw_bh, acb);
- if (ret < 0) {
- if (acb->qiov->niov > 1) {
- qemu_vfree(acb->orig_buf);
- }
- qemu_aio_release(acb);
- return NULL;
+ if (acb->qiov->niov > 1) {
+ qemu_vfree(acb->orig_buf);
}
+ qemu_aio_release(acb);
- return &acb->common;
+ return ret;
}
static void qcow_close(BlockDriverState *bs)
@@ -1020,8 +964,8 @@ static BlockDriver bdrv_qcow = {
.bdrv_is_allocated = qcow_is_allocated,
.bdrv_set_key = qcow_set_key,
.bdrv_make_empty = qcow_make_empty,
- .bdrv_aio_readv = qcow_aio_readv,
- .bdrv_aio_writev = qcow_aio_writev,
+ .bdrv_co_readv = qcow_co_readv,
+ .bdrv_co_writev = qcow_co_writev,
.bdrv_aio_flush = qcow_aio_flush,
.bdrv_write_compressed = qcow_write_compressed,
.bdrv_get_info = qcow_get_info,
diff --git a/block/qcow2-cache.c b/block/qcow2-cache.c
index 3824739..8408847 100644
--- a/block/qcow2-cache.c
+++ b/block/qcow2-cache.c
@@ -312,3 +312,15 @@ found:
c->entries[i].dirty = true;
}
+bool qcow2_cache_set_writethrough(BlockDriverState *bs, Qcow2Cache *c,
+ bool enable)
+{
+ bool old = c->writethrough;
+
+ if (!old && enable) {
+ qcow2_cache_flush(bs, c);
+ }
+
+ c->writethrough = enable;
+ return old;
+}
diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c
index 882f50a..81cf77d 100644
--- a/block/qcow2-cluster.c
+++ b/block/qcow2-cluster.c
@@ -697,12 +697,12 @@ err:
* m->depends_on is set to NULL and the other fields in m are meaningless.
*
* If the cluster is newly allocated, m->nb_clusters is set to the number of
- * contiguous clusters that have been allocated. This may be 0 if the request
- * conflict with another write request in flight; in this case, m->depends_on
- * is set and the remaining fields of m are meaningless.
+ * contiguous clusters that have been allocated. In this case, the other
+ * fields of m are valid and contain information about the first allocated
+ * cluster.
*
- * If m->nb_clusters is non-zero, the other fields of m are valid and contain
- * information about the first allocated cluster.
+ * If the request conflicts with another write request in flight, the coroutine
+ * is queued and will be reentered when the dependency has completed.
*
* Return 0 on success and -errno in error cases
*/
@@ -721,6 +721,7 @@ int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset,
return ret;
}
+again:
nb_clusters = size_to_clusters(s, n_end << 9);
nb_clusters = MIN(nb_clusters, s->l2_size - l2_index);
@@ -792,12 +793,12 @@ int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset,
}
if (nb_clusters == 0) {
- /* Set dependency and wait for a callback */
- m->depends_on = old_alloc;
- m->nb_clusters = 0;
- *num = 0;
-
- goto out_wait_dependency;
+ /* Wait for the dependency to complete. We need to recheck
+ * the free/allocated clusters when we continue. */
+ qemu_co_mutex_unlock(&s->lock);
+ qemu_co_queue_wait(&old_alloc->dependent_requests);
+ qemu_co_mutex_lock(&s->lock);
+ goto again;
}
}
}
@@ -834,9 +835,6 @@ out:
return 0;
-out_wait_dependency:
- return qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table);
-
fail:
qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table);
fail_put:
diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
index ac95b88..14b2f67 100644
--- a/block/qcow2-refcount.c
+++ b/block/qcow2-refcount.c
@@ -705,8 +705,15 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
BDRVQcowState *s = bs->opaque;
uint64_t *l1_table, *l2_table, l2_offset, offset, l1_size2, l1_allocated;
int64_t old_offset, old_l2_offset;
- int i, j, l1_modified, nb_csectors, refcount;
+ int i, j, l1_modified = 0, nb_csectors, refcount;
int ret;
+ bool old_l2_writethrough, old_refcount_writethrough;
+
+ /* Switch caches to writeback mode during update */
+ old_l2_writethrough =
+ qcow2_cache_set_writethrough(bs, s->l2_table_cache, false);
+ old_refcount_writethrough =
+ qcow2_cache_set_writethrough(bs, s->refcount_block_cache, false);
l2_table = NULL;
l1_table = NULL;
@@ -720,7 +727,11 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
l1_allocated = 1;
if (bdrv_pread(bs->file, l1_table_offset,
l1_table, l1_size2) != l1_size2)
+ {
+ ret = -EIO;
goto fail;
+ }
+
for(i = 0;i < l1_size; i++)
be64_to_cpus(&l1_table[i]);
} else {
@@ -729,7 +740,6 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
l1_allocated = 0;
}
- l1_modified = 0;
for(i = 0; i < l1_size; i++) {
l2_offset = l1_table[i];
if (l2_offset) {
@@ -773,6 +783,7 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
}
if (refcount < 0) {
+ ret = -EIO;
goto fail;
}
}
@@ -803,6 +814,7 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
refcount = get_refcount(bs, l2_offset >> s->cluster_bits);
}
if (refcount < 0) {
+ ret = -EIO;
goto fail;
} else if (refcount == 1) {
l2_offset |= QCOW_OFLAG_COPIED;
@@ -813,6 +825,18 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
}
}
}
+
+ ret = 0;
+fail:
+ if (l2_table) {
+ qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table);
+ }
+
+ /* Enable writethrough cache mode again */
+ qcow2_cache_set_writethrough(bs, s->l2_table_cache, old_l2_writethrough);
+ qcow2_cache_set_writethrough(bs, s->refcount_block_cache,
+ old_refcount_writethrough);
+
if (l1_modified) {
for(i = 0; i < l1_size; i++)
cpu_to_be64s(&l1_table[i]);
@@ -824,15 +848,7 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
}
if (l1_allocated)
qemu_free(l1_table);
- return 0;
- fail:
- if (l2_table) {
- qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table);
- }
-
- if (l1_allocated)
- qemu_free(l1_table);
- return -EIO;
+ return ret;
}
diff --git a/block/qcow2.c b/block/qcow2.c
index 48e1b95..f07d550 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -276,6 +276,9 @@ static int qcow2_open(BlockDriverState *bs, int flags)
goto fail;
}
+ /* Initialise locks */
+ qemu_co_mutex_init(&s->lock);
+
#ifdef DEBUG_ALLOC
qcow2_check_refcounts(bs);
#endif
@@ -379,7 +382,6 @@ typedef struct QCowAIOCB {
uint64_t cluster_offset;
uint8_t *cluster_data;
bool is_write;
- BlockDriverAIOCB *hd_aiocb;
QEMUIOVector hd_qiov;
QEMUBH *bh;
QCowL2Meta l2meta;
@@ -389,8 +391,6 @@ typedef struct QCowAIOCB {
static void qcow2_aio_cancel(BlockDriverAIOCB *blockacb)
{
QCowAIOCB *acb = container_of(blockacb, QCowAIOCB, common);
- if (acb->hd_aiocb)
- bdrv_aio_cancel(acb->hd_aiocb);
qemu_aio_release(acb);
}
@@ -399,46 +399,16 @@ static AIOPool qcow2_aio_pool = {
.cancel = qcow2_aio_cancel,
};
-static void qcow2_aio_read_cb(void *opaque, int ret);
-static void qcow2_aio_write_cb(void *opaque, int ret);
-
-static void qcow2_aio_rw_bh(void *opaque)
-{
- QCowAIOCB *acb = opaque;
- qemu_bh_delete(acb->bh);
- acb->bh = NULL;
-
- if (acb->is_write) {
- qcow2_aio_write_cb(opaque, 0);
- } else {
- qcow2_aio_read_cb(opaque, 0);
- }
-}
-
-static int qcow2_schedule_bh(QEMUBHFunc *cb, QCowAIOCB *acb)
-{
- if (acb->bh)
- return -EIO;
-
- acb->bh = qemu_bh_new(cb, acb);
- if (!acb->bh)
- return -EIO;
-
- qemu_bh_schedule(acb->bh);
-
- return 0;
-}
-
-static void qcow2_aio_read_cb(void *opaque, int ret)
+/*
+ * Returns 0 when the request is completed successfully, 1 when there is still
+ * a part left to do and -errno in error cases.
+ */
+static int qcow2_aio_read_cb(QCowAIOCB *acb)
{
- QCowAIOCB *acb = opaque;
BlockDriverState *bs = acb->common.bs;
BDRVQcowState *s = bs->opaque;
int index_in_cluster, n1;
-
- acb->hd_aiocb = NULL;
- if (ret < 0)
- goto done;
+ int ret;
/* post process the read buffer */
if (!acb->cluster_offset) {
@@ -463,8 +433,7 @@ static void qcow2_aio_read_cb(void *opaque, int ret)
if (acb->remaining_sectors == 0) {
/* request completed */
- ret = 0;
- goto done;
+ return 0;
}
/* prepare next AIO request */
@@ -477,7 +446,7 @@ static void qcow2_aio_read_cb(void *opaque, int ret)
ret = qcow2_get_cluster_offset(bs, acb->sector_num << 9,
&acb->cur_nr_sectors, &acb->cluster_offset);
if (ret < 0) {
- goto done;
+ return ret;
}
index_in_cluster = acb->sector_num & (s->cluster_sectors - 1);
@@ -494,42 +463,35 @@ static void qcow2_aio_read_cb(void *opaque, int ret)
acb->sector_num, acb->cur_nr_sectors);
if (n1 > 0) {
BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING_AIO);
- acb->hd_aiocb = bdrv_aio_readv(bs->backing_hd, acb->sector_num,
- &acb->hd_qiov, n1, qcow2_aio_read_cb, acb);
- if (acb->hd_aiocb == NULL) {
- ret = -EIO;
- goto done;
+ qemu_co_mutex_unlock(&s->lock);
+ ret = bdrv_co_readv(bs->backing_hd, acb->sector_num,
+ n1, &acb->hd_qiov);
+ qemu_co_mutex_lock(&s->lock);
+ if (ret < 0) {
+ return ret;
}
- } else {
- ret = qcow2_schedule_bh(qcow2_aio_rw_bh, acb);
- if (ret < 0)
- goto done;
}
+ return 1;
} else {
/* Note: in this case, no need to wait */
qemu_iovec_memset(&acb->hd_qiov, 0, 512 * acb->cur_nr_sectors);
- ret = qcow2_schedule_bh(qcow2_aio_rw_bh, acb);
- if (ret < 0)
- goto done;
+ return 1;
}
} else if (acb->cluster_offset & QCOW_OFLAG_COMPRESSED) {
/* add AIO support for compressed blocks ? */
ret = qcow2_decompress_cluster(bs, acb->cluster_offset);
if (ret < 0) {
- goto done;
+ return ret;
}
qemu_iovec_from_buffer(&acb->hd_qiov,
s->cluster_cache + index_in_cluster * 512,
512 * acb->cur_nr_sectors);
- ret = qcow2_schedule_bh(qcow2_aio_rw_bh, acb);
- if (ret < 0)
- goto done;
+ return 1;
} else {
if ((acb->cluster_offset & 511) != 0) {
- ret = -EIO;
- goto done;
+ return -EIO;
}
if (s->crypt_method) {
@@ -550,21 +512,17 @@ static void qcow2_aio_read_cb(void *opaque, int ret)
}
BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO);
- acb->hd_aiocb = bdrv_aio_readv(bs->file,
+ qemu_co_mutex_unlock(&s->lock);
+ ret = bdrv_co_readv(bs->file,
(acb->cluster_offset >> 9) + index_in_cluster,
- &acb->hd_qiov, acb->cur_nr_sectors,
- qcow2_aio_read_cb, acb);
- if (acb->hd_aiocb == NULL) {
- ret = -EIO;
- goto done;
+ acb->cur_nr_sectors, &acb->hd_qiov);
+ qemu_co_mutex_lock(&s->lock);
+ if (ret < 0) {
+ return ret;
}
}
- return;
-done:
- acb->common.cb(acb->common.opaque, ret);
- qemu_iovec_destroy(&acb->hd_qiov);
- qemu_aio_release(acb);
+ return 1;
}
static QCowAIOCB *qcow2_aio_setup(BlockDriverState *bs, int64_t sector_num,
@@ -577,7 +535,6 @@ static QCowAIOCB *qcow2_aio_setup(BlockDriverState *bs, int64_t sector_num,
acb = qemu_aio_get(&qcow2_aio_pool, bs, cb, opaque);
if (!acb)
return NULL;
- acb->hd_aiocb = NULL;
acb->sector_num = sector_num;
acb->qiov = qiov;
acb->is_write = is_write;
@@ -589,70 +546,65 @@ static QCowAIOCB *qcow2_aio_setup(BlockDriverState *bs, int64_t sector_num,
acb->cur_nr_sectors = 0;
acb->cluster_offset = 0;
acb->l2meta.nb_clusters = 0;
- QLIST_INIT(&acb->l2meta.dependent_requests);
+ qemu_co_queue_init(&acb->l2meta.dependent_requests);
return acb;
}
-static BlockDriverAIOCB *qcow2_aio_readv(BlockDriverState *bs,
- int64_t sector_num,
- QEMUIOVector *qiov, int nb_sectors,
- BlockDriverCompletionFunc *cb,
- void *opaque)
+static int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num,
+ int nb_sectors, QEMUIOVector *qiov)
{
+ BDRVQcowState *s = bs->opaque;
QCowAIOCB *acb;
int ret;
- acb = qcow2_aio_setup(bs, sector_num, qiov, nb_sectors, cb, opaque, 0);
- if (!acb)
- return NULL;
+ acb = qcow2_aio_setup(bs, sector_num, qiov, nb_sectors, NULL, NULL, 0);
- ret = qcow2_schedule_bh(qcow2_aio_rw_bh, acb);
- if (ret < 0) {
- qemu_iovec_destroy(&acb->hd_qiov);
- qemu_aio_release(acb);
- return NULL;
- }
+ qemu_co_mutex_lock(&s->lock);
+ do {
+ ret = qcow2_aio_read_cb(acb);
+ } while (ret > 0);
+ qemu_co_mutex_unlock(&s->lock);
- return &acb->common;
+ qemu_iovec_destroy(&acb->hd_qiov);
+ qemu_aio_release(acb);
+
+ return ret;
}
-static void run_dependent_requests(QCowL2Meta *m)
+static void run_dependent_requests(BDRVQcowState *s, QCowL2Meta *m)
{
- QCowAIOCB *req;
- QCowAIOCB *next;
-
/* Take the request off the list of running requests */
if (m->nb_clusters != 0) {
QLIST_REMOVE(m, next_in_flight);
}
/* Restart all dependent requests */
- QLIST_FOREACH_SAFE(req, &m->dependent_requests, next_depend, next) {
- qcow2_aio_write_cb(req, 0);
+ if (!qemu_co_queue_empty(&m->dependent_requests)) {
+ qemu_co_mutex_unlock(&s->lock);
+ while(qemu_co_queue_next(&m->dependent_requests));
+ qemu_co_mutex_lock(&s->lock);
}
-
- /* Empty the list for the next part of the request */
- QLIST_INIT(&m->dependent_requests);
}
-static void qcow2_aio_write_cb(void *opaque, int ret)
+/*
+ * Returns 0 when the request is completed successfully, 1 when there is still
+ * a part left to do and -errno in error cases.
+ */
+static int qcow2_aio_write_cb(QCowAIOCB *acb)
{
- QCowAIOCB *acb = opaque;
BlockDriverState *bs = acb->common.bs;
BDRVQcowState *s = bs->opaque;
int index_in_cluster;
int n_end;
+ int ret;
- acb->hd_aiocb = NULL;
-
- if (ret >= 0) {
- ret = qcow2_alloc_cluster_link_l2(bs, &acb->l2meta);
- }
+ ret = qcow2_alloc_cluster_link_l2(bs, &acb->l2meta);
- run_dependent_requests(&acb->l2meta);
+ run_dependent_requests(s, &acb->l2meta);
- if (ret < 0)
- goto done;
+ if (ret < 0) {
+ return ret;
+ }
acb->remaining_sectors -= acb->cur_nr_sectors;
acb->sector_num += acb->cur_nr_sectors;
@@ -660,8 +612,7 @@ static void qcow2_aio_write_cb(void *opaque, int ret)
if (acb->remaining_sectors == 0) {
/* request completed */
- ret = 0;
- goto done;
+ return 0;
}
index_in_cluster = acb->sector_num & (s->cluster_sectors - 1);
@@ -673,18 +624,10 @@ static void qcow2_aio_write_cb(void *opaque, int ret)
ret = qcow2_alloc_cluster_offset(bs, acb->sector_num << 9,
index_in_cluster, n_end, &acb->cur_nr_sectors, &acb->l2meta);
if (ret < 0) {
- goto done;
+ return ret;
}
acb->cluster_offset = acb->l2meta.cluster_offset;
-
- /* Need to wait for another request? If so, we are done for now. */
- if (acb->l2meta.nb_clusters == 0 && acb->l2meta.depends_on != NULL) {
- QLIST_INSERT_HEAD(&acb->l2meta.depends_on->dependent_requests,
- acb, next_depend);
- return;
- }
-
assert((acb->cluster_offset & 511) == 0);
qemu_iovec_reset(&acb->hd_qiov);
@@ -709,51 +652,40 @@ static void qcow2_aio_write_cb(void *opaque, int ret)
}
BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO);
- acb->hd_aiocb = bdrv_aio_writev(bs->file,
- (acb->cluster_offset >> 9) + index_in_cluster,
- &acb->hd_qiov, acb->cur_nr_sectors,
- qcow2_aio_write_cb, acb);
- if (acb->hd_aiocb == NULL) {
- ret = -EIO;
- goto fail;
+ qemu_co_mutex_unlock(&s->lock);
+ ret = bdrv_co_writev(bs->file,
+ (acb->cluster_offset >> 9) + index_in_cluster,
+ acb->cur_nr_sectors, &acb->hd_qiov);
+ qemu_co_mutex_lock(&s->lock);
+ if (ret < 0) {
+ return ret;
}
- return;
-
-fail:
- if (acb->l2meta.nb_clusters != 0) {
- QLIST_REMOVE(&acb->l2meta, next_in_flight);
- }
-done:
- acb->common.cb(acb->common.opaque, ret);
- qemu_iovec_destroy(&acb->hd_qiov);
- qemu_aio_release(acb);
+ return 1;
}
-static BlockDriverAIOCB *qcow2_aio_writev(BlockDriverState *bs,
- int64_t sector_num,
- QEMUIOVector *qiov, int nb_sectors,
- BlockDriverCompletionFunc *cb,
- void *opaque)
+static int qcow2_co_writev(BlockDriverState *bs,
+ int64_t sector_num,
+ int nb_sectors,
+ QEMUIOVector *qiov)
{
BDRVQcowState *s = bs->opaque;
QCowAIOCB *acb;
int ret;
+ acb = qcow2_aio_setup(bs, sector_num, qiov, nb_sectors, NULL, NULL, 1);
s->cluster_cache_offset = -1; /* disable compressed cache */
- acb = qcow2_aio_setup(bs, sector_num, qiov, nb_sectors, cb, opaque, 1);
- if (!acb)
- return NULL;
+ qemu_co_mutex_lock(&s->lock);
+ do {
+ ret = qcow2_aio_write_cb(acb);
+ } while (ret > 0);
+ qemu_co_mutex_unlock(&s->lock);
- ret = qcow2_schedule_bh(qcow2_aio_rw_bh, acb);
- if (ret < 0) {
- qemu_iovec_destroy(&acb->hd_qiov);
- qemu_aio_release(acb);
- return NULL;
- }
+ qemu_iovec_destroy(&acb->hd_qiov);
+ qemu_aio_release(acb);
- return &acb->common;
+ return ret;
}
static void qcow2_close(BlockDriverState *bs)
@@ -881,7 +813,7 @@ static int preallocate(BlockDriverState *bs)
nb_sectors = bdrv_getlength(bs) >> 9;
offset = 0;
- QLIST_INIT(&meta.dependent_requests);
+ qemu_co_queue_init(&meta.dependent_requests);
meta.cluster_offset = 0;
while (nb_sectors) {
@@ -899,7 +831,7 @@ static int preallocate(BlockDriverState *bs)
/* There are no dependent requests, but we need to remove our request
* from the list of in-flight requests */
- run_dependent_requests(&meta);
+ run_dependent_requests(bs->opaque, &meta);
/* TODO Preallocate data if requested */
@@ -1387,8 +1319,8 @@ static BlockDriver bdrv_qcow2 = {
.bdrv_set_key = qcow2_set_key,
.bdrv_make_empty = qcow2_make_empty,
- .bdrv_aio_readv = qcow2_aio_readv,
- .bdrv_aio_writev = qcow2_aio_writev,
+ .bdrv_co_readv = qcow2_co_readv,
+ .bdrv_co_writev = qcow2_co_writev,
.bdrv_aio_flush = qcow2_aio_flush,
.bdrv_discard = qcow2_discard,
diff --git a/block/qcow2.h b/block/qcow2.h
index e1ae3e8..de23abe 100644
--- a/block/qcow2.h
+++ b/block/qcow2.h
@@ -26,6 +26,7 @@
#define BLOCK_QCOW2_H
#include "aes.h"
+#include "qemu-coroutine.h"
//#define DEBUG_ALLOC
//#define DEBUG_ALLOC2
@@ -114,6 +115,8 @@ typedef struct BDRVQcowState {
int64_t free_cluster_index;
int64_t free_byte_offset;
+ CoMutex lock;
+
uint32_t crypt_method; /* current crypt method, 0 if no key yet */
uint32_t crypt_method_header;
AES_KEY aes_encrypt_key;
@@ -146,7 +149,7 @@ typedef struct QCowL2Meta
int nb_available;
int nb_clusters;
struct QCowL2Meta *depends_on;
- QLIST_HEAD(QCowAioDependencies, QCowAIOCB) dependent_requests;
+ CoQueue dependent_requests;
QLIST_ENTRY(QCowL2Meta) next_in_flight;
} QCowL2Meta;
@@ -228,6 +231,8 @@ int qcow2_read_snapshots(BlockDriverState *bs);
Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables,
bool writethrough);
int qcow2_cache_destroy(BlockDriverState* bs, Qcow2Cache *c);
+bool qcow2_cache_set_writethrough(BlockDriverState *bs, Qcow2Cache *c,
+ bool enable);
void qcow2_cache_entry_mark_dirty(Qcow2Cache *c, void *table);
int qcow2_cache_flush(BlockDriverState *bs, Qcow2Cache *c);
diff --git a/block/qed-table.c b/block/qed-table.c
index d38c673..d96afa8 100644
--- a/block/qed-table.c
+++ b/block/qed-table.c
@@ -179,16 +179,12 @@ int qed_read_l1_table_sync(BDRVQEDState *s)
{
int ret = -EINPROGRESS;
- async_context_push();
-
qed_read_table(s, s->header.l1_table_offset,
s->l1_table, qed_sync_cb, &ret);
while (ret == -EINPROGRESS) {
qemu_aio_wait();
}
- async_context_pop();
-
return ret;
}
@@ -205,15 +201,11 @@ int qed_write_l1_table_sync(BDRVQEDState *s, unsigned int index,
{
int ret = -EINPROGRESS;
- async_context_push();
-
qed_write_l1_table(s, index, n, qed_sync_cb, &ret);
while (ret == -EINPROGRESS) {
qemu_aio_wait();
}
- async_context_pop();
-
return ret;
}
@@ -282,14 +274,11 @@ int qed_read_l2_table_sync(BDRVQEDState *s, QEDRequest *request, uint64_t offset
{
int ret = -EINPROGRESS;
- async_context_push();
-
qed_read_l2_table(s, request, offset, qed_sync_cb, &ret);
while (ret == -EINPROGRESS) {
qemu_aio_wait();
}
- async_context_pop();
return ret;
}
@@ -307,13 +296,10 @@ int qed_write_l2_table_sync(BDRVQEDState *s, QEDRequest *request,
{
int ret = -EINPROGRESS;
- async_context_push();
-
qed_write_l2_table(s, request, index, n, flush, qed_sync_cb, &ret);
while (ret == -EINPROGRESS) {
qemu_aio_wait();
}
- async_context_pop();
return ret;
}
diff --git a/block/qed.c b/block/qed.c
index 3970379..333f067 100644
--- a/block/qed.c
+++ b/block/qed.c
@@ -680,16 +680,12 @@ static int bdrv_qed_is_allocated(BlockDriverState *bs, int64_t sector_num,
};
QEDRequest request = { .l2_table = NULL };
- async_context_push();
-
qed_find_cluster(s, &request, pos, len, qed_is_allocated_cb, &cb);
while (cb.is_allocated == -1) {
qemu_aio_wait();
}
- async_context_pop();
-
qed_unref_l2_cache_entry(request.l2_table);
return cb.is_allocated;
diff --git a/block/raw-posix.c b/block/raw-posix.c
index 34b64aa..c5c9944 100644
--- a/block/raw-posix.c
+++ b/block/raw-posix.c
@@ -230,13 +230,15 @@ static int raw_open_common(BlockDriverState *bs, const char *filename,
}
}
+ /* We're falling back to POSIX AIO in some cases so init always */
+ if (paio_init() < 0) {
+ goto out_free_buf;
+ }
+
#ifdef CONFIG_LINUX_AIO
if ((bdrv_flags & (BDRV_O_NOCACHE|BDRV_O_NATIVE_AIO)) ==
(BDRV_O_NOCACHE|BDRV_O_NATIVE_AIO)) {
- /* We're falling back to POSIX AIO in some cases */
- paio_init();
-
s->aio_ctx = laio_init();
if (!s->aio_ctx) {
goto out_free_buf;
@@ -245,9 +247,6 @@ static int raw_open_common(BlockDriverState *bs, const char *filename,
} else
#endif
{
- if (paio_init() < 0) {
- goto out_free_buf;
- }
#ifdef CONFIG_LINUX_AIO
s->use_aio = 0;
#endif
@@ -587,7 +586,7 @@ static BlockDriverAIOCB *raw_aio_submit(BlockDriverState *bs,
/*
* If O_DIRECT is used the buffer needs to be aligned on a sector
- * boundary. Check if this is the case or telll the low-level
+ * boundary. Check if this is the case or tell the low-level
* driver that it needs to copy the buffer.
*/
if (s->aligned_buf) {
@@ -793,6 +792,17 @@ static int64_t raw_getlength(BlockDriverState *bs)
}
#endif
+static int64_t raw_get_allocated_file_size(BlockDriverState *bs)
+{
+ struct stat st;
+ BDRVRawState *s = bs->opaque;
+
+ if (fstat(s->fd, &st) < 0) {
+ return -errno;
+ }
+ return (int64_t)st.st_blocks * 512;
+}
+
static int raw_create(const char *filename, QEMUOptionParameter *options)
{
int fd;
@@ -888,6 +898,8 @@ static BlockDriver bdrv_file = {
.bdrv_truncate = raw_truncate,
.bdrv_getlength = raw_getlength,
+ .bdrv_get_allocated_file_size
+ = raw_get_allocated_file_size,
.create_options = raw_create_options,
};
@@ -1156,6 +1168,8 @@ static BlockDriver bdrv_host_device = {
.bdrv_read = raw_read,
.bdrv_write = raw_write,
.bdrv_getlength = raw_getlength,
+ .bdrv_get_allocated_file_size
+ = raw_get_allocated_file_size,
/* generic scsi device */
#ifdef __linux__
@@ -1239,7 +1253,7 @@ static int floppy_media_changed(BlockDriverState *bs)
return ret;
}
-static int floppy_eject(BlockDriverState *bs, int eject_flag)
+static void floppy_eject(BlockDriverState *bs, int eject_flag)
{
BDRVRawState *s = bs->opaque;
int fd;
@@ -1254,8 +1268,6 @@ static int floppy_eject(BlockDriverState *bs, int eject_flag)
perror("FDEJECT");
close(fd);
}
-
- return 0;
}
static BlockDriver bdrv_host_floppy = {
@@ -1277,6 +1289,8 @@ static BlockDriver bdrv_host_floppy = {
.bdrv_read = raw_read,
.bdrv_write = raw_write,
.bdrv_getlength = raw_getlength,
+ .bdrv_get_allocated_file_size
+ = raw_get_allocated_file_size,
/* removable device support */
.bdrv_is_inserted = floppy_is_inserted,
@@ -1331,7 +1345,7 @@ static int cdrom_is_inserted(BlockDriverState *bs)
return 0;
}
-static int cdrom_eject(BlockDriverState *bs, int eject_flag)
+static void cdrom_eject(BlockDriverState *bs, int eject_flag)
{
BDRVRawState *s = bs->opaque;
@@ -1342,11 +1356,9 @@ static int cdrom_eject(BlockDriverState *bs, int eject_flag)
if (ioctl(s->fd, CDROMCLOSETRAY, NULL) < 0)
perror("CDROMEJECT");
}
-
- return 0;
}
-static int cdrom_set_locked(BlockDriverState *bs, int locked)
+static void cdrom_set_locked(BlockDriverState *bs, int locked)
{
BDRVRawState *s = bs->opaque;
@@ -1357,8 +1369,6 @@ static int cdrom_set_locked(BlockDriverState *bs, int locked)
*/
/* perror("CDROM_LOCKDOOR"); */
}
-
- return 0;
}
static BlockDriver bdrv_host_cdrom = {
@@ -1380,6 +1390,8 @@ static BlockDriver bdrv_host_cdrom = {
.bdrv_read = raw_read,
.bdrv_write = raw_write,
.bdrv_getlength = raw_getlength,
+ .bdrv_get_allocated_file_size
+ = raw_get_allocated_file_size,
/* removable device support */
.bdrv_is_inserted = cdrom_is_inserted,
@@ -1445,12 +1457,12 @@ static int cdrom_is_inserted(BlockDriverState *bs)
return raw_getlength(bs) > 0;
}
-static int cdrom_eject(BlockDriverState *bs, int eject_flag)
+static void cdrom_eject(BlockDriverState *bs, int eject_flag)
{
BDRVRawState *s = bs->opaque;
if (s->fd < 0)
- return -ENOTSUP;
+ return;
(void) ioctl(s->fd, CDIOCALLOW);
@@ -1462,17 +1474,15 @@ static int cdrom_eject(BlockDriverState *bs, int eject_flag)
perror("CDIOCCLOSE");
}
- if (cdrom_reopen(bs) < 0)
- return -ENOTSUP;
- return 0;
+ cdrom_reopen(bs);
}
-static int cdrom_set_locked(BlockDriverState *bs, int locked)
+static void cdrom_set_locked(BlockDriverState *bs, int locked)
{
BDRVRawState *s = bs->opaque;
if (s->fd < 0)
- return -ENOTSUP;
+ return;
if (ioctl(s->fd, (locked ? CDIOCPREVENT : CDIOCALLOW)) < 0) {
/*
* Note: an error can happen if the distribution automatically
@@ -1480,8 +1490,6 @@ static int cdrom_set_locked(BlockDriverState *bs, int locked)
*/
/* perror("CDROM_LOCKDOOR"); */
}
-
- return 0;
}
static BlockDriver bdrv_host_cdrom = {
@@ -1503,6 +1511,8 @@ static BlockDriver bdrv_host_cdrom = {
.bdrv_read = raw_read,
.bdrv_write = raw_write,
.bdrv_getlength = raw_getlength,
+ .bdrv_get_allocated_file_size
+ = raw_get_allocated_file_size,
/* removable device support */
.bdrv_is_inserted = cdrom_is_inserted,
diff --git a/block/raw-win32.c b/block/raw-win32.c
index 56bd719..e47cfe0 100644
--- a/block/raw-win32.c
+++ b/block/raw-win32.c
@@ -213,6 +213,31 @@ static int64_t raw_getlength(BlockDriverState *bs)
return l.QuadPart;
}
+static int64_t raw_get_allocated_file_size(BlockDriverState *bs)
+{
+ typedef DWORD (WINAPI * get_compressed_t)(const char *filename,
+ DWORD * high);
+ get_compressed_t get_compressed;
+ struct _stati64 st;
+ const char *filename = bs->filename;
+ /* WinNT support GetCompressedFileSize to determine allocate size */
+ get_compressed =
+ (get_compressed_t) GetProcAddress(GetModuleHandle("kernel32"),
+ "GetCompressedFileSizeA");
+ if (get_compressed) {
+ DWORD high, low;
+ low = get_compressed(filename, &high);
+ if (low != 0xFFFFFFFFlu || GetLastError() == NO_ERROR) {
+ return (((int64_t) high) << 32) + low;
+ }
+ }
+
+ if (_stati64(filename, &st) < 0) {
+ return -1;
+ }
+ return st.st_size;
+}
+
static int raw_create(const char *filename, QEMUOptionParameter *options)
{
int fd;
@@ -257,6 +282,8 @@ static BlockDriver bdrv_file = {
.bdrv_write = raw_write,
.bdrv_truncate = raw_truncate,
.bdrv_getlength = raw_getlength,
+ .bdrv_get_allocated_file_size
+ = raw_get_allocated_file_size,
.create_options = raw_create_options,
};
@@ -366,41 +393,6 @@ static int hdev_open(BlockDriverState *bs, const char *filename, int flags)
return 0;
}
-#if 0
-/***********************************************/
-/* removable device additional commands */
-
-static int raw_is_inserted(BlockDriverState *bs)
-{
- return 1;
-}
-
-static int raw_media_changed(BlockDriverState *bs)
-{
- return -ENOTSUP;
-}
-
-static int raw_eject(BlockDriverState *bs, int eject_flag)
-{
- DWORD ret_count;
-
- if (s->type == FTYPE_FILE)
- return -ENOTSUP;
- if (eject_flag) {
- DeviceIoControl(s->hfile, IOCTL_STORAGE_EJECT_MEDIA,
- NULL, 0, NULL, 0, &lpBytesReturned, NULL);
- } else {
- DeviceIoControl(s->hfile, IOCTL_STORAGE_LOAD_MEDIA,
- NULL, 0, NULL, 0, &lpBytesReturned, NULL);
- }
-}
-
-static int raw_set_locked(BlockDriverState *bs, int locked)
-{
- return -ENOTSUP;
-}
-#endif
-
static int hdev_has_zero_init(BlockDriverState *bs)
{
return 0;
@@ -419,6 +411,8 @@ static BlockDriver bdrv_host_device = {
.bdrv_read = raw_read,
.bdrv_write = raw_write,
.bdrv_getlength = raw_getlength,
+ .bdrv_get_allocated_file_size
+ = raw_get_allocated_file_size,
};
static void bdrv_file_init(void)
diff --git a/block/raw.c b/block/raw.c
index b0f72d6..cb6203e 100644
--- a/block/raw.c
+++ b/block/raw.c
@@ -75,15 +75,14 @@ static int raw_is_inserted(BlockDriverState *bs)
return bdrv_is_inserted(bs->file);
}
-static int raw_eject(BlockDriverState *bs, int eject_flag)
+static void raw_eject(BlockDriverState *bs, int eject_flag)
{
- return bdrv_eject(bs->file, eject_flag);
+ bdrv_eject(bs->file, eject_flag);
}
-static int raw_set_locked(BlockDriverState *bs, int locked)
+static void raw_set_locked(BlockDriverState *bs, int locked)
{
bdrv_set_locked(bs->file, locked);
- return 0;
}
static int raw_ioctl(BlockDriverState *bs, unsigned long int req, void *buf)
diff --git a/block/sheepdog.c b/block/sheepdog.c
index 80d106c..e150ac0 100644
--- a/block/sheepdog.c
+++ b/block/sheepdog.c
@@ -496,7 +496,7 @@ static ssize_t recvmsg(int s, struct msghdr *msg, int flags)
}
buf = qemu_malloc(size);
- ret = recv(s, buf, size, flags);
+ ret = qemu_recv(s, buf, size, flags);
if (ret < 0) {
goto out;
}
@@ -1286,6 +1286,49 @@ static int do_sd_create(char *filename, int64_t vdi_size,
return 0;
}
+static int sd_prealloc(const char *filename)
+{
+ BlockDriverState *bs = NULL;
+ uint32_t idx, max_idx;
+ int64_t vdi_size;
+ void *buf = qemu_mallocz(SD_DATA_OBJ_SIZE);
+ int ret;
+
+ ret = bdrv_file_open(&bs, filename, BDRV_O_RDWR);
+ if (ret < 0) {
+ goto out;
+ }
+
+ vdi_size = bdrv_getlength(bs);
+ if (vdi_size < 0) {
+ ret = vdi_size;
+ goto out;
+ }
+ max_idx = DIV_ROUND_UP(vdi_size, SD_DATA_OBJ_SIZE);
+
+ for (idx = 0; idx < max_idx; idx++) {
+ /*
+ * The created image can be a cloned image, so we need to read
+ * a data from the source image.
+ */
+ ret = bdrv_pread(bs, idx * SD_DATA_OBJ_SIZE, buf, SD_DATA_OBJ_SIZE);
+ if (ret < 0) {
+ goto out;
+ }
+ ret = bdrv_pwrite(bs, idx * SD_DATA_OBJ_SIZE, buf, SD_DATA_OBJ_SIZE);
+ if (ret < 0) {
+ goto out;
+ }
+ }
+out:
+ if (bs) {
+ bdrv_delete(bs);
+ }
+ qemu_free(buf);
+
+ return ret;
+}
+
static int sd_create(const char *filename, QEMUOptionParameter *options)
{
int ret;
@@ -1295,13 +1338,15 @@ static int sd_create(const char *filename, QEMUOptionParameter *options)
BDRVSheepdogState s;
char vdi[SD_MAX_VDI_LEN], tag[SD_MAX_VDI_TAG_LEN];
uint32_t snapid;
+ int prealloc = 0;
+ const char *vdiname;
- strstart(filename, "sheepdog:", (const char **)&filename);
+ strstart(filename, "sheepdog:", &vdiname);
memset(&s, 0, sizeof(s));
memset(vdi, 0, sizeof(vdi));
memset(tag, 0, sizeof(tag));
- if (parse_vdiname(&s, filename, vdi, &snapid, tag) < 0) {
+ if (parse_vdiname(&s, vdiname, vdi, &snapid, tag) < 0) {
error_report("invalid filename");
return -EINVAL;
}
@@ -1311,6 +1356,16 @@ static int sd_create(const char *filename, QEMUOptionParameter *options)
vdi_size = options->value.n;
} else if (!strcmp(options->name, BLOCK_OPT_BACKING_FILE)) {
backing_file = options->value.s;
+ } else if (!strcmp(options->name, BLOCK_OPT_PREALLOC)) {
+ if (!options->value.s || !strcmp(options->value.s, "off")) {
+ prealloc = 0;
+ } else if (!strcmp(options->value.s, "full")) {
+ prealloc = 1;
+ } else {
+ error_report("Invalid preallocation mode: '%s'",
+ options->value.s);
+ return -EINVAL;
+ }
}
options++;
}
@@ -1348,7 +1403,12 @@ static int sd_create(const char *filename, QEMUOptionParameter *options)
bdrv_delete(bs);
}
- return do_sd_create((char *)vdi, vdi_size, base_vid, &vid, 0, s.addr, s.port);
+ ret = do_sd_create(vdi, vdi_size, base_vid, &vid, 0, s.addr, s.port);
+ if (!prealloc || ret) {
+ return ret;
+ }
+
+ return sd_prealloc(filename);
}
static void sd_close(BlockDriverState *bs)
@@ -1984,6 +2044,11 @@ static QEMUOptionParameter sd_create_options[] = {
.type = OPT_STRING,
.help = "File name of a base image"
},
+ {
+ .name = BLOCK_OPT_PREALLOC,
+ .type = OPT_STRING,
+ .help = "Preallocation mode (allowed values: off, full)"
+ },
{ NULL }
};
diff --git a/block/vmdk.c b/block/vmdk.c
index 922b23d..37478d2 100644
--- a/block/vmdk.c
+++ b/block/vmdk.c
@@ -60,7 +60,12 @@ typedef struct {
#define L2_CACHE_SIZE 16
-typedef struct BDRVVmdkState {
+typedef struct VmdkExtent {
+ BlockDriverState *file;
+ bool flat;
+ int64_t sectors;
+ int64_t end_sector;
+ int64_t flat_start_offset;
int64_t l1_table_offset;
int64_t l1_backup_table_offset;
uint32_t *l1_table;
@@ -74,7 +79,15 @@ typedef struct BDRVVmdkState {
uint32_t l2_cache_counts[L2_CACHE_SIZE];
unsigned int cluster_sectors;
+} VmdkExtent;
+
+typedef struct BDRVVmdkState {
+ int desc_offset;
+ bool cid_updated;
uint32_t parent_cid;
+ int num_extents;
+ /* Extent array with num_extents entries, ascend ordered by address */
+ VmdkExtent *extents;
} BDRVVmdkState;
typedef struct VmdkMetaData {
@@ -89,21 +102,77 @@ static int vmdk_probe(const uint8_t *buf, int buf_size, const char *filename)
{
uint32_t magic;
- if (buf_size < 4)
+ if (buf_size < 4) {
return 0;
+ }
magic = be32_to_cpu(*(uint32_t *)buf);
if (magic == VMDK3_MAGIC ||
- magic == VMDK4_MAGIC)
+ magic == VMDK4_MAGIC) {
return 100;
- else
+ } else {
+ const char *p = (const char *)buf;
+ const char *end = p + buf_size;
+ while (p < end) {
+ if (*p == '#') {
+ /* skip comment line */
+ while (p < end && *p != '\n') {
+ p++;
+ }
+ p++;
+ continue;
+ }
+ if (*p == ' ') {
+ while (p < end && *p == ' ') {
+ p++;
+ }
+ /* skip '\r' if windows line endings used. */
+ if (p < end && *p == '\r') {
+ p++;
+ }
+ /* only accept blank lines before 'version=' line */
+ if (p == end || *p != '\n') {
+ return 0;
+ }
+ p++;
+ continue;
+ }
+ if (end - p >= strlen("version=X\n")) {
+ if (strncmp("version=1\n", p, strlen("version=1\n")) == 0 ||
+ strncmp("version=2\n", p, strlen("version=2\n")) == 0) {
+ return 100;
+ }
+ }
+ if (end - p >= strlen("version=X\r\n")) {
+ if (strncmp("version=1\r\n", p, strlen("version=1\r\n")) == 0 ||
+ strncmp("version=2\r\n", p, strlen("version=2\r\n")) == 0) {
+ return 100;
+ }
+ }
+ return 0;
+ }
return 0;
+ }
}
#define CHECK_CID 1
#define SECTOR_SIZE 512
-#define DESC_SIZE 20*SECTOR_SIZE // 20 sectors of 512 bytes each
-#define HEADER_SIZE 512 // first sector of 512 bytes
+#define DESC_SIZE (20 * SECTOR_SIZE) /* 20 sectors of 512 bytes each */
+#define BUF_SIZE 4096
+#define HEADER_SIZE 512 /* first sector of 512 bytes */
+
+static void vmdk_free_extents(BlockDriverState *bs)
+{
+ int i;
+ BDRVVmdkState *s = bs->opaque;
+
+ for (i = 0; i < s->num_extents; i++) {
+ qemu_free(s->extents[i].l1_table);
+ qemu_free(s->extents[i].l2_cache);
+ qemu_free(s->extents[i].l1_backup_table);
+ }
+ qemu_free(s->extents);
+}
static uint32_t vmdk_read_cid(BlockDriverState *bs, int parent)
{
@@ -111,10 +180,11 @@ static uint32_t vmdk_read_cid(BlockDriverState *bs, int parent)
uint32_t cid;
const char *p_name, *cid_str;
size_t cid_str_size;
+ BDRVVmdkState *s = bs->opaque;
- /* the descriptor offset = 0x200 */
- if (bdrv_pread(bs->file, 0x200, desc, DESC_SIZE) != DESC_SIZE)
+ if (bdrv_pread(bs->file, s->desc_offset, desc, DESC_SIZE) != DESC_SIZE) {
return 0;
+ }
if (parent) {
cid_str = "parentCID";
@@ -124,9 +194,10 @@ static uint32_t vmdk_read_cid(BlockDriverState *bs, int parent)
cid_str_size = sizeof("CID");
}
- if ((p_name = strstr(desc,cid_str)) != NULL) {
+ p_name = strstr(desc, cid_str);
+ if (p_name != NULL) {
p_name += cid_str_size;
- sscanf(p_name,"%x",&cid);
+ sscanf(p_name, "%x", &cid);
}
return cid;
@@ -136,21 +207,25 @@ static int vmdk_write_cid(BlockDriverState *bs, uint32_t cid)
{
char desc[DESC_SIZE], tmp_desc[DESC_SIZE];
char *p_name, *tmp_str;
+ BDRVVmdkState *s = bs->opaque;
- /* the descriptor offset = 0x200 */
- if (bdrv_pread(bs->file, 0x200, desc, DESC_SIZE) != DESC_SIZE)
- return -1;
+ memset(desc, 0, sizeof(desc));
+ if (bdrv_pread(bs->file, s->desc_offset, desc, DESC_SIZE) != DESC_SIZE) {
+ return -EIO;
+ }
- tmp_str = strstr(desc,"parentCID");
+ tmp_str = strstr(desc, "parentCID");
pstrcpy(tmp_desc, sizeof(tmp_desc), tmp_str);
- if ((p_name = strstr(desc,"CID")) != NULL) {
+ p_name = strstr(desc, "CID");
+ if (p_name != NULL) {
p_name += sizeof("CID");
snprintf(p_name, sizeof(desc) - (p_name - desc), "%x\n", cid);
pstrcat(desc, sizeof(desc), tmp_desc);
}
- if (bdrv_pwrite_sync(bs->file, 0x200, desc, DESC_SIZE) < 0)
- return -1;
+ if (bdrv_pwrite_sync(bs->file, s->desc_offset, desc, DESC_SIZE) < 0) {
+ return -EIO;
+ }
return 0;
}
@@ -162,302 +237,387 @@ static int vmdk_is_cid_valid(BlockDriverState *bs)
uint32_t cur_pcid;
if (p_bs) {
- cur_pcid = vmdk_read_cid(p_bs,0);
- if (s->parent_cid != cur_pcid)
- // CID not valid
+ cur_pcid = vmdk_read_cid(p_bs, 0);
+ if (s->parent_cid != cur_pcid) {
+ /* CID not valid */
return 0;
+ }
}
#endif
- // CID valid
+ /* CID valid */
return 1;
}
-static int vmdk_snapshot_create(const char *filename, const char *backing_file)
+static int vmdk_parent_open(BlockDriverState *bs)
{
- int snp_fd, p_fd;
- int ret;
- uint32_t p_cid;
- char *p_name, *gd_buf, *rgd_buf;
- const char *real_filename, *temp_str;
- VMDK4Header header;
- uint32_t gde_entries, gd_size;
- int64_t gd_offset, rgd_offset, capacity, gt_size;
- char p_desc[DESC_SIZE], s_desc[DESC_SIZE], hdr[HEADER_SIZE];
- static const char desc_template[] =
- "# Disk DescriptorFile\n"
- "version=1\n"
- "CID=%x\n"
- "parentCID=%x\n"
- "createType=\"monolithicSparse\"\n"
- "parentFileNameHint=\"%s\"\n"
- "\n"
- "# Extent description\n"
- "RW %u SPARSE \"%s\"\n"
- "\n"
- "# The Disk Data Base \n"
- "#DDB\n"
- "\n";
-
- snp_fd = open(filename, O_RDWR | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE, 0644);
- if (snp_fd < 0)
- return -errno;
- p_fd = open(backing_file, O_RDONLY | O_BINARY | O_LARGEFILE);
- if (p_fd < 0) {
- close(snp_fd);
- return -errno;
- }
+ char *p_name;
+ char desc[DESC_SIZE + 1];
+ BDRVVmdkState *s = bs->opaque;
- /* read the header */
- if (lseek(p_fd, 0x0, SEEK_SET) == -1) {
- ret = -errno;
- goto fail;
- }
- if (read(p_fd, hdr, HEADER_SIZE) != HEADER_SIZE) {
- ret = -errno;
- goto fail;
+ desc[DESC_SIZE] = '\0';
+ if (bdrv_pread(bs->file, s->desc_offset, desc, DESC_SIZE) != DESC_SIZE) {
+ return -1;
}
- /* write the header */
- if (lseek(snp_fd, 0x0, SEEK_SET) == -1) {
- ret = -errno;
- goto fail;
- }
- if (write(snp_fd, hdr, HEADER_SIZE) == -1) {
- ret = -errno;
- goto fail;
+ p_name = strstr(desc, "parentFileNameHint");
+ if (p_name != NULL) {
+ char *end_name;
+
+ p_name += sizeof("parentFileNameHint") + 1;
+ end_name = strchr(p_name, '\"');
+ if (end_name == NULL) {
+ return -1;
+ }
+ if ((end_name - p_name) > sizeof(bs->backing_file) - 1) {
+ return -1;
+ }
+
+ pstrcpy(bs->backing_file, end_name - p_name + 1, p_name);
}
- memset(&header, 0, sizeof(header));
- memcpy(&header,&hdr[4], sizeof(header)); // skip the VMDK4_MAGIC
+ return 0;
+}
- if (ftruncate(snp_fd, header.grain_offset << 9)) {
- ret = -errno;
- goto fail;
+/* Create and append extent to the extent array. Return the added VmdkExtent
+ * address. return NULL if allocation failed. */
+static VmdkExtent *vmdk_add_extent(BlockDriverState *bs,
+ BlockDriverState *file, bool flat, int64_t sectors,
+ int64_t l1_offset, int64_t l1_backup_offset,
+ uint32_t l1_size,
+ int l2_size, unsigned int cluster_sectors)
+{
+ VmdkExtent *extent;
+ BDRVVmdkState *s = bs->opaque;
+
+ s->extents = qemu_realloc(s->extents,
+ (s->num_extents + 1) * sizeof(VmdkExtent));
+ extent = &s->extents[s->num_extents];
+ s->num_extents++;
+
+ memset(extent, 0, sizeof(VmdkExtent));
+ extent->file = file;
+ extent->flat = flat;
+ extent->sectors = sectors;
+ extent->l1_table_offset = l1_offset;
+ extent->l1_backup_table_offset = l1_backup_offset;
+ extent->l1_size = l1_size;
+ extent->l1_entry_sectors = l2_size * cluster_sectors;
+ extent->l2_size = l2_size;
+ extent->cluster_sectors = cluster_sectors;
+
+ if (s->num_extents > 1) {
+ extent->end_sector = (*(extent - 1)).end_sector + extent->sectors;
+ } else {
+ extent->end_sector = extent->sectors;
}
- /* the descriptor offset = 0x200 */
- if (lseek(p_fd, 0x200, SEEK_SET) == -1) {
- ret = -errno;
- goto fail;
+ bs->total_sectors = extent->end_sector;
+ return extent;
+}
+
+static int vmdk_init_tables(BlockDriverState *bs, VmdkExtent *extent)
+{
+ int ret;
+ int l1_size, i;
+
+ /* read the L1 table */
+ l1_size = extent->l1_size * sizeof(uint32_t);
+ extent->l1_table = qemu_malloc(l1_size);
+ ret = bdrv_pread(extent->file,
+ extent->l1_table_offset,
+ extent->l1_table,
+ l1_size);
+ if (ret < 0) {
+ goto fail_l1;
}
- if (read(p_fd, p_desc, DESC_SIZE) != DESC_SIZE) {
- ret = -errno;
- goto fail;
+ for (i = 0; i < extent->l1_size; i++) {
+ le32_to_cpus(&extent->l1_table[i]);
}
- if ((p_name = strstr(p_desc,"CID")) != NULL) {
- p_name += sizeof("CID");
- sscanf(p_name,"%x",&p_cid);
+ if (extent->l1_backup_table_offset) {
+ extent->l1_backup_table = qemu_malloc(l1_size);
+ ret = bdrv_pread(extent->file,
+ extent->l1_backup_table_offset,
+ extent->l1_backup_table,
+ l1_size);
+ if (ret < 0) {
+ goto fail_l1b;
+ }
+ for (i = 0; i < extent->l1_size; i++) {
+ le32_to_cpus(&extent->l1_backup_table[i]);
+ }
}
- real_filename = filename;
- if ((temp_str = strrchr(real_filename, '\\')) != NULL)
- real_filename = temp_str + 1;
- if ((temp_str = strrchr(real_filename, '/')) != NULL)
- real_filename = temp_str + 1;
- if ((temp_str = strrchr(real_filename, ':')) != NULL)
- real_filename = temp_str + 1;
+ extent->l2_cache =
+ qemu_malloc(extent->l2_size * L2_CACHE_SIZE * sizeof(uint32_t));
+ return 0;
+ fail_l1b:
+ qemu_free(extent->l1_backup_table);
+ fail_l1:
+ qemu_free(extent->l1_table);
+ return ret;
+}
- snprintf(s_desc, sizeof(s_desc), desc_template, p_cid, p_cid, backing_file,
- (uint32_t)header.capacity, real_filename);
+static int vmdk_open_vmdk3(BlockDriverState *bs, int flags)
+{
+ int ret;
+ uint32_t magic;
+ VMDK3Header header;
+ BDRVVmdkState *s = bs->opaque;
+ VmdkExtent *extent;
- /* write the descriptor */
- if (lseek(snp_fd, 0x200, SEEK_SET) == -1) {
- ret = -errno;
+ s->desc_offset = 0x200;
+ ret = bdrv_pread(bs->file, sizeof(magic), &header, sizeof(header));
+ if (ret < 0) {
goto fail;
}
- if (write(snp_fd, s_desc, strlen(s_desc)) == -1) {
- ret = -errno;
+ extent = vmdk_add_extent(bs,
+ bs->file, false,
+ le32_to_cpu(header.disk_sectors),
+ le32_to_cpu(header.l1dir_offset) << 9,
+ 0, 1 << 6, 1 << 9,
+ le32_to_cpu(header.granularity));
+ ret = vmdk_init_tables(bs, extent);
+ if (ret) {
+ /* vmdk_init_tables cleans up on fail, so only free allocation of
+ * vmdk_add_extent here. */
goto fail;
}
+ return 0;
+ fail:
+ vmdk_free_extents(bs);
+ return ret;
+}
- gd_offset = header.gd_offset * SECTOR_SIZE; // offset of GD table
- rgd_offset = header.rgd_offset * SECTOR_SIZE; // offset of RGD table
- capacity = header.capacity * SECTOR_SIZE; // Extent size
- /*
- * Each GDE span 32M disk, means:
- * 512 GTE per GT, each GTE points to grain
- */
- gt_size = (int64_t)header.num_gtes_per_gte * header.granularity * SECTOR_SIZE;
- if (!gt_size) {
- ret = -EINVAL;
- goto fail;
- }
- gde_entries = (uint32_t)(capacity / gt_size); // number of gde/rgde
- gd_size = gde_entries * sizeof(uint32_t);
+static int vmdk_open_vmdk4(BlockDriverState *bs, int flags)
+{
+ int ret;
+ uint32_t magic;
+ uint32_t l1_size, l1_entry_sectors;
+ VMDK4Header header;
+ BDRVVmdkState *s = bs->opaque;
+ VmdkExtent *extent;
- /* write RGD */
- rgd_buf = qemu_malloc(gd_size);
- if (lseek(p_fd, rgd_offset, SEEK_SET) == -1) {
- ret = -errno;
- goto fail_rgd;
+ s->desc_offset = 0x200;
+ ret = bdrv_pread(bs->file, sizeof(magic), &header, sizeof(header));
+ if (ret < 0) {
+ goto fail;
}
- if (read(p_fd, rgd_buf, gd_size) != gd_size) {
- ret = -errno;
- goto fail_rgd;
+ l1_entry_sectors = le32_to_cpu(header.num_gtes_per_gte)
+ * le64_to_cpu(header.granularity);
+ l1_size = (le64_to_cpu(header.capacity) + l1_entry_sectors - 1)
+ / l1_entry_sectors;
+ extent = vmdk_add_extent(bs, bs->file, false,
+ le64_to_cpu(header.capacity),
+ le64_to_cpu(header.gd_offset) << 9,
+ le64_to_cpu(header.rgd_offset) << 9,
+ l1_size,
+ le32_to_cpu(header.num_gtes_per_gte),
+ le64_to_cpu(header.granularity));
+ if (extent->l1_entry_sectors <= 0) {
+ ret = -EINVAL;
+ goto fail;
}
- if (lseek(snp_fd, rgd_offset, SEEK_SET) == -1) {
- ret = -errno;
- goto fail_rgd;
+ /* try to open parent images, if exist */
+ ret = vmdk_parent_open(bs);
+ if (ret) {
+ goto fail;
}
- if (write(snp_fd, rgd_buf, gd_size) == -1) {
- ret = -errno;
- goto fail_rgd;
+ s->parent_cid = vmdk_read_cid(bs, 1);
+ ret = vmdk_init_tables(bs, extent);
+ if (ret) {
+ goto fail;
}
+ return 0;
+ fail:
+ vmdk_free_extents(bs);
+ return ret;
+}
- /* write GD */
- gd_buf = qemu_malloc(gd_size);
- if (lseek(p_fd, gd_offset, SEEK_SET) == -1) {
- ret = -errno;
- goto fail_gd;
+/* find an option value out of descriptor file */
+static int vmdk_parse_description(const char *desc, const char *opt_name,
+ char *buf, int buf_size)
+{
+ char *opt_pos, *opt_end;
+ const char *end = desc + strlen(desc);
+
+ opt_pos = strstr(desc, opt_name);
+ if (!opt_pos) {
+ return -1;
}
- if (read(p_fd, gd_buf, gd_size) != gd_size) {
- ret = -errno;
- goto fail_gd;
+ /* Skip "=\"" following opt_name */
+ opt_pos += strlen(opt_name) + 2;
+ if (opt_pos >= end) {
+ return -1;
}
- if (lseek(snp_fd, gd_offset, SEEK_SET) == -1) {
- ret = -errno;
- goto fail_gd;
+ opt_end = opt_pos;
+ while (opt_end < end && *opt_end != '"') {
+ opt_end++;
}
- if (write(snp_fd, gd_buf, gd_size) == -1) {
- ret = -errno;
- goto fail_gd;
+ if (opt_end == end || buf_size < opt_end - opt_pos + 1) {
+ return -1;
}
- ret = 0;
-
-fail_gd:
- qemu_free(gd_buf);
-fail_rgd:
- qemu_free(rgd_buf);
-fail:
- close(p_fd);
- close(snp_fd);
- return ret;
+ pstrcpy(buf, opt_end - opt_pos + 1, opt_pos);
+ return 0;
}
-static int vmdk_parent_open(BlockDriverState *bs)
+static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
+ const char *desc_file_path)
{
- char *p_name;
- char desc[DESC_SIZE];
+ int ret;
+ char access[11];
+ char type[11];
+ char fname[512];
+ const char *p = desc;
+ int64_t sectors = 0;
+ int64_t flat_offset;
+
+ while (*p) {
+ /* parse extent line:
+ * RW [size in sectors] FLAT "file-name.vmdk" OFFSET
+ * or
+ * RW [size in sectors] SPARSE "file-name.vmdk"
+ */
+ flat_offset = -1;
+ ret = sscanf(p, "%10s %" SCNd64 " %10s %511s %" SCNd64,
+ access, &sectors, type, fname, &flat_offset);
+ if (ret < 4 || strcmp(access, "RW")) {
+ goto next_line;
+ } else if (!strcmp(type, "FLAT")) {
+ if (ret != 5 || flat_offset < 0) {
+ return -EINVAL;
+ }
+ } else if (ret != 4) {
+ return -EINVAL;
+ }
- /* the descriptor offset = 0x200 */
- if (bdrv_pread(bs->file, 0x200, desc, DESC_SIZE) != DESC_SIZE)
- return -1;
+ /* trim the quotation marks around */
+ if (fname[0] == '"') {
+ memmove(fname, fname + 1, strlen(fname));
+ if (strlen(fname) <= 1 || fname[strlen(fname) - 1] != '"') {
+ return -EINVAL;
+ }
+ fname[strlen(fname) - 1] = '\0';
+ }
+ if (sectors <= 0 ||
+ (strcmp(type, "FLAT") && strcmp(type, "SPARSE")) ||
+ (strcmp(access, "RW"))) {
+ goto next_line;
+ }
- if ((p_name = strstr(desc,"parentFileNameHint")) != NULL) {
- char *end_name;
+ /* save to extents array */
+ if (!strcmp(type, "FLAT")) {
+ /* FLAT extent */
+ char extent_path[PATH_MAX];
+ BlockDriverState *extent_file;
+ VmdkExtent *extent;
+
+ path_combine(extent_path, sizeof(extent_path),
+ desc_file_path, fname);
+ ret = bdrv_file_open(&extent_file, extent_path, bs->open_flags);
+ if (ret) {
+ return ret;
+ }
+ extent = vmdk_add_extent(bs, extent_file, true, sectors,
+ 0, 0, 0, 0, sectors);
+ extent->flat_start_offset = flat_offset;
+ } else {
+ /* SPARSE extent, not supported for now */
+ fprintf(stderr,
+ "VMDK: Not supported extent type \"%s\""".\n", type);
+ return -ENOTSUP;
+ }
+next_line:
+ /* move to next line */
+ while (*p && *p != '\n') {
+ p++;
+ }
+ p++;
+ }
+ return 0;
+}
- p_name += sizeof("parentFileNameHint") + 1;
- if ((end_name = strchr(p_name,'\"')) == NULL)
- return -1;
- if ((end_name - p_name) > sizeof (bs->backing_file) - 1)
- return -1;
+static int vmdk_open_desc_file(BlockDriverState *bs, int flags)
+{
+ int ret;
+ char buf[2048];
+ char ct[128];
+ BDRVVmdkState *s = bs->opaque;
- pstrcpy(bs->backing_file, end_name - p_name + 1, p_name);
+ ret = bdrv_pread(bs->file, 0, buf, sizeof(buf));
+ if (ret < 0) {
+ return ret;
+ }
+ buf[2047] = '\0';
+ if (vmdk_parse_description(buf, "createType", ct, sizeof(ct))) {
+ return -EINVAL;
+ }
+ if (strcmp(ct, "monolithicFlat")) {
+ fprintf(stderr,
+ "VMDK: Not supported image type \"%s\""".\n", ct);
+ return -ENOTSUP;
+ }
+ s->desc_offset = 0;
+ ret = vmdk_parse_extents(buf, bs, bs->file->filename);
+ if (ret) {
+ return ret;
}
+ /* try to open parent images, if exist */
+ if (vmdk_parent_open(bs)) {
+ qemu_free(s->extents);
+ return -EINVAL;
+ }
+ s->parent_cid = vmdk_read_cid(bs, 1);
return 0;
}
static int vmdk_open(BlockDriverState *bs, int flags)
{
- BDRVVmdkState *s = bs->opaque;
uint32_t magic;
- int l1_size, i;
- if (bdrv_pread(bs->file, 0, &magic, sizeof(magic)) != sizeof(magic))
- goto fail;
+ if (bdrv_pread(bs->file, 0, &magic, sizeof(magic)) != sizeof(magic)) {
+ return -EIO;
+ }
magic = be32_to_cpu(magic);
if (magic == VMDK3_MAGIC) {
- VMDK3Header header;
-
- if (bdrv_pread(bs->file, sizeof(magic), &header, sizeof(header)) != sizeof(header))
- goto fail;
- s->cluster_sectors = le32_to_cpu(header.granularity);
- s->l2_size = 1 << 9;
- s->l1_size = 1 << 6;
- bs->total_sectors = le32_to_cpu(header.disk_sectors);
- s->l1_table_offset = le32_to_cpu(header.l1dir_offset) << 9;
- s->l1_backup_table_offset = 0;
- s->l1_entry_sectors = s->l2_size * s->cluster_sectors;
+ return vmdk_open_vmdk3(bs, flags);
} else if (magic == VMDK4_MAGIC) {
- VMDK4Header header;
-
- if (bdrv_pread(bs->file, sizeof(magic), &header, sizeof(header)) != sizeof(header))
- goto fail;
- bs->total_sectors = le64_to_cpu(header.capacity);
- s->cluster_sectors = le64_to_cpu(header.granularity);
- s->l2_size = le32_to_cpu(header.num_gtes_per_gte);
- s->l1_entry_sectors = s->l2_size * s->cluster_sectors;
- if (s->l1_entry_sectors <= 0)
- goto fail;
- s->l1_size = (bs->total_sectors + s->l1_entry_sectors - 1)
- / s->l1_entry_sectors;
- s->l1_table_offset = le64_to_cpu(header.rgd_offset) << 9;
- s->l1_backup_table_offset = le64_to_cpu(header.gd_offset) << 9;
-
- // try to open parent images, if exist
- if (vmdk_parent_open(bs) != 0)
- goto fail;
- // write the CID once after the image creation
- s->parent_cid = vmdk_read_cid(bs,1);
+ return vmdk_open_vmdk4(bs, flags);
} else {
- goto fail;
- }
-
- /* read the L1 table */
- l1_size = s->l1_size * sizeof(uint32_t);
- s->l1_table = qemu_malloc(l1_size);
- if (bdrv_pread(bs->file, s->l1_table_offset, s->l1_table, l1_size) != l1_size)
- goto fail;
- for(i = 0; i < s->l1_size; i++) {
- le32_to_cpus(&s->l1_table[i]);
- }
-
- if (s->l1_backup_table_offset) {
- s->l1_backup_table = qemu_malloc(l1_size);
- if (bdrv_pread(bs->file, s->l1_backup_table_offset, s->l1_backup_table, l1_size) != l1_size)
- goto fail;
- for(i = 0; i < s->l1_size; i++) {
- le32_to_cpus(&s->l1_backup_table[i]);
- }
+ return vmdk_open_desc_file(bs, flags);
}
-
- s->l2_cache = qemu_malloc(s->l2_size * L2_CACHE_SIZE * sizeof(uint32_t));
- return 0;
- fail:
- qemu_free(s->l1_backup_table);
- qemu_free(s->l1_table);
- qemu_free(s->l2_cache);
- return -1;
}
-static uint64_t get_cluster_offset(BlockDriverState *bs, VmdkMetaData *m_data,
- uint64_t offset, int allocate);
-
-static int get_whole_cluster(BlockDriverState *bs, uint64_t cluster_offset,
- uint64_t offset, int allocate)
+static int get_whole_cluster(BlockDriverState *bs,
+ VmdkExtent *extent,
+ uint64_t cluster_offset,
+ uint64_t offset,
+ bool allocate)
{
- BDRVVmdkState *s = bs->opaque;
- uint8_t whole_grain[s->cluster_sectors*512]; // 128 sectors * 512 bytes each = grain size 64KB
+ /* 128 sectors * 512 bytes each = grain size 64KB */
+ uint8_t whole_grain[extent->cluster_sectors * 512];
- // we will be here if it's first write on non-exist grain(cluster).
- // try to read from parent image, if exist
+ /* we will be here if it's first write on non-exist grain(cluster).
+ * try to read from parent image, if exist */
if (bs->backing_hd) {
int ret;
- if (!vmdk_is_cid_valid(bs))
+ if (!vmdk_is_cid_valid(bs)) {
return -1;
+ }
+ /* floor offset to cluster */
+ offset -= offset % (extent->cluster_sectors * 512);
ret = bdrv_read(bs->backing_hd, offset >> 9, whole_grain,
- s->cluster_sectors);
+ extent->cluster_sectors);
if (ret < 0) {
return -1;
}
- //Write grain only into the active image
- ret = bdrv_write(bs->file, cluster_offset, whole_grain,
- s->cluster_sectors);
+ /* Write grain only into the active image */
+ ret = bdrv_write(extent->file, cluster_offset, whole_grain,
+ extent->cluster_sectors);
if (ret < 0) {
return -1;
}
@@ -465,85 +625,112 @@ static int get_whole_cluster(BlockDriverState *bs, uint64_t cluster_offset,
return 0;
}
-static int vmdk_L2update(BlockDriverState *bs, VmdkMetaData *m_data)
+static int vmdk_L2update(VmdkExtent *extent, VmdkMetaData *m_data)
{
- BDRVVmdkState *s = bs->opaque;
-
/* update L2 table */
- if (bdrv_pwrite_sync(bs->file, ((int64_t)m_data->l2_offset * 512) + (m_data->l2_index * sizeof(m_data->offset)),
- &(m_data->offset), sizeof(m_data->offset)) < 0)
+ if (bdrv_pwrite_sync(
+ extent->file,
+ ((int64_t)m_data->l2_offset * 512)
+ + (m_data->l2_index * sizeof(m_data->offset)),
+ &(m_data->offset),
+ sizeof(m_data->offset)
+ ) < 0) {
return -1;
+ }
/* update backup L2 table */
- if (s->l1_backup_table_offset != 0) {
- m_data->l2_offset = s->l1_backup_table[m_data->l1_index];
- if (bdrv_pwrite_sync(bs->file, ((int64_t)m_data->l2_offset * 512) + (m_data->l2_index * sizeof(m_data->offset)),
- &(m_data->offset), sizeof(m_data->offset)) < 0)
+ if (extent->l1_backup_table_offset != 0) {
+ m_data->l2_offset = extent->l1_backup_table[m_data->l1_index];
+ if (bdrv_pwrite_sync(
+ extent->file,
+ ((int64_t)m_data->l2_offset * 512)
+ + (m_data->l2_index * sizeof(m_data->offset)),
+ &(m_data->offset), sizeof(m_data->offset)
+ ) < 0) {
return -1;
+ }
}
return 0;
}
-static uint64_t get_cluster_offset(BlockDriverState *bs, VmdkMetaData *m_data,
- uint64_t offset, int allocate)
+static int get_cluster_offset(BlockDriverState *bs,
+ VmdkExtent *extent,
+ VmdkMetaData *m_data,
+ uint64_t offset,
+ int allocate,
+ uint64_t *cluster_offset)
{
- BDRVVmdkState *s = bs->opaque;
unsigned int l1_index, l2_offset, l2_index;
int min_index, i, j;
uint32_t min_count, *l2_table, tmp = 0;
- uint64_t cluster_offset;
- if (m_data)
+ if (m_data) {
m_data->valid = 0;
-
- l1_index = (offset >> 9) / s->l1_entry_sectors;
- if (l1_index >= s->l1_size)
- return 0;
- l2_offset = s->l1_table[l1_index];
- if (!l2_offset)
+ }
+ if (extent->flat) {
+ *cluster_offset = extent->flat_start_offset;
return 0;
- for(i = 0; i < L2_CACHE_SIZE; i++) {
- if (l2_offset == s->l2_cache_offsets[i]) {
+ }
+
+ l1_index = (offset >> 9) / extent->l1_entry_sectors;
+ if (l1_index >= extent->l1_size) {
+ return -1;
+ }
+ l2_offset = extent->l1_table[l1_index];
+ if (!l2_offset) {
+ return -1;
+ }
+ for (i = 0; i < L2_CACHE_SIZE; i++) {
+ if (l2_offset == extent->l2_cache_offsets[i]) {
/* increment the hit count */
- if (++s->l2_cache_counts[i] == 0xffffffff) {
- for(j = 0; j < L2_CACHE_SIZE; j++) {
- s->l2_cache_counts[j] >>= 1;
+ if (++extent->l2_cache_counts[i] == 0xffffffff) {
+ for (j = 0; j < L2_CACHE_SIZE; j++) {
+ extent->l2_cache_counts[j] >>= 1;
}
}
- l2_table = s->l2_cache + (i * s->l2_size);
+ l2_table = extent->l2_cache + (i * extent->l2_size);
goto found;
}
}
/* not found: load a new entry in the least used one */
min_index = 0;
min_count = 0xffffffff;
- for(i = 0; i < L2_CACHE_SIZE; i++) {
- if (s->l2_cache_counts[i] < min_count) {
- min_count = s->l2_cache_counts[i];
+ for (i = 0; i < L2_CACHE_SIZE; i++) {
+ if (extent->l2_cache_counts[i] < min_count) {
+ min_count = extent->l2_cache_counts[i];
min_index = i;
}
}
- l2_table = s->l2_cache + (min_index * s->l2_size);
- if (bdrv_pread(bs->file, (int64_t)l2_offset * 512, l2_table, s->l2_size * sizeof(uint32_t)) !=
- s->l2_size * sizeof(uint32_t))
- return 0;
+ l2_table = extent->l2_cache + (min_index * extent->l2_size);
+ if (bdrv_pread(
+ extent->file,
+ (int64_t)l2_offset * 512,
+ l2_table,
+ extent->l2_size * sizeof(uint32_t)
+ ) != extent->l2_size * sizeof(uint32_t)) {
+ return -1;
+ }
- s->l2_cache_offsets[min_index] = l2_offset;
- s->l2_cache_counts[min_index] = 1;
+ extent->l2_cache_offsets[min_index] = l2_offset;
+ extent->l2_cache_counts[min_index] = 1;
found:
- l2_index = ((offset >> 9) / s->cluster_sectors) % s->l2_size;
- cluster_offset = le32_to_cpu(l2_table[l2_index]);
+ l2_index = ((offset >> 9) / extent->cluster_sectors) % extent->l2_size;
+ *cluster_offset = le32_to_cpu(l2_table[l2_index]);
- if (!cluster_offset) {
- if (!allocate)
- return 0;
+ if (!*cluster_offset) {
+ if (!allocate) {
+ return -1;
+ }
- // Avoid the L2 tables update for the images that have snapshots.
- cluster_offset = bdrv_getlength(bs->file);
- bdrv_truncate(bs->file, cluster_offset + (s->cluster_sectors << 9));
+ /* Avoid the L2 tables update for the images that have snapshots. */
+ *cluster_offset = bdrv_getlength(extent->file);
+ bdrv_truncate(
+ extent->file,
+ *cluster_offset + (extent->cluster_sectors << 9)
+ );
- cluster_offset >>= 9;
- tmp = cpu_to_le32(cluster_offset);
+ *cluster_offset >>= 9;
+ tmp = cpu_to_le32(*cluster_offset);
l2_table[l2_index] = tmp;
/* First of all we write grain itself, to avoid race condition
@@ -551,8 +738,10 @@ static uint64_t get_cluster_offset(BlockDriverState *bs, VmdkMetaData *m_data,
* This problem may occur because of insufficient space on host disk
* or inappropriate VM shutdown.
*/
- if (get_whole_cluster(bs, cluster_offset, offset, allocate) == -1)
- return 0;
+ if (get_whole_cluster(
+ bs, extent, *cluster_offset, offset, allocate) == -1) {
+ return -1;
+ }
if (m_data) {
m_data->offset = tmp;
@@ -562,53 +751,95 @@ static uint64_t get_cluster_offset(BlockDriverState *bs, VmdkMetaData *m_data,
m_data->valid = 1;
}
}
- cluster_offset <<= 9;
- return cluster_offset;
+ *cluster_offset <<= 9;
+ return 0;
+}
+
+static VmdkExtent *find_extent(BDRVVmdkState *s,
+ int64_t sector_num, VmdkExtent *start_hint)
+{
+ VmdkExtent *extent = start_hint;
+
+ if (!extent) {
+ extent = &s->extents[0];
+ }
+ while (extent < &s->extents[s->num_extents]) {
+ if (sector_num < extent->end_sector) {
+ return extent;
+ }
+ extent++;
+ }
+ return NULL;
}
static int vmdk_is_allocated(BlockDriverState *bs, int64_t sector_num,
int nb_sectors, int *pnum)
{
BDRVVmdkState *s = bs->opaque;
- int index_in_cluster, n;
- uint64_t cluster_offset;
+ int64_t index_in_cluster, n, ret;
+ uint64_t offset;
+ VmdkExtent *extent;
- cluster_offset = get_cluster_offset(bs, NULL, sector_num << 9, 0);
- index_in_cluster = sector_num % s->cluster_sectors;
- n = s->cluster_sectors - index_in_cluster;
- if (n > nb_sectors)
+ extent = find_extent(s, sector_num, NULL);
+ if (!extent) {
+ return 0;
+ }
+ ret = get_cluster_offset(bs, extent, NULL,
+ sector_num * 512, 0, &offset);
+ /* get_cluster_offset returning 0 means success */
+ ret = !ret;
+
+ index_in_cluster = sector_num % extent->cluster_sectors;
+ n = extent->cluster_sectors - index_in_cluster;
+ if (n > nb_sectors) {
n = nb_sectors;
+ }
*pnum = n;
- return (cluster_offset != 0);
+ return ret;
}
static int vmdk_read(BlockDriverState *bs, int64_t sector_num,
uint8_t *buf, int nb_sectors)
{
BDRVVmdkState *s = bs->opaque;
- int index_in_cluster, n, ret;
+ int ret;
+ uint64_t n, index_in_cluster;
+ VmdkExtent *extent = NULL;
uint64_t cluster_offset;
while (nb_sectors > 0) {
- cluster_offset = get_cluster_offset(bs, NULL, sector_num << 9, 0);
- index_in_cluster = sector_num % s->cluster_sectors;
- n = s->cluster_sectors - index_in_cluster;
- if (n > nb_sectors)
+ extent = find_extent(s, sector_num, extent);
+ if (!extent) {
+ return -EIO;
+ }
+ ret = get_cluster_offset(
+ bs, extent, NULL,
+ sector_num << 9, 0, &cluster_offset);
+ index_in_cluster = sector_num % extent->cluster_sectors;
+ n = extent->cluster_sectors - index_in_cluster;
+ if (n > nb_sectors) {
n = nb_sectors;
- if (!cluster_offset) {
- // try to read from parent image, if exist
+ }
+ if (ret) {
+ /* if not allocated, try to read from parent image, if exist */
if (bs->backing_hd) {
- if (!vmdk_is_cid_valid(bs))
- return -1;
+ if (!vmdk_is_cid_valid(bs)) {
+ return -EINVAL;
+ }
ret = bdrv_read(bs->backing_hd, sector_num, buf, n);
- if (ret < 0)
- return -1;
+ if (ret < 0) {
+ return ret;
+ }
} else {
memset(buf, 0, 512 * n);
}
} else {
- if(bdrv_pread(bs->file, cluster_offset + index_in_cluster * 512, buf, n * 512) != n * 512)
- return -1;
+ ret = bdrv_pread(extent->file,
+ cluster_offset + index_in_cluster * 512,
+ buf, n * 512);
+ if (ret < 0) {
+ return ret;
+ }
}
nb_sectors -= n;
sector_num += n;
@@ -621,110 +852,101 @@ static int vmdk_write(BlockDriverState *bs, int64_t sector_num,
const uint8_t *buf, int nb_sectors)
{
BDRVVmdkState *s = bs->opaque;
- VmdkMetaData m_data;
- int index_in_cluster, n;
+ VmdkExtent *extent = NULL;
+ int n, ret;
+ int64_t index_in_cluster;
uint64_t cluster_offset;
- static int cid_update = 0;
+ VmdkMetaData m_data;
if (sector_num > bs->total_sectors) {
fprintf(stderr,
"(VMDK) Wrong offset: sector_num=0x%" PRIx64
" total_sectors=0x%" PRIx64 "\n",
sector_num, bs->total_sectors);
- return -1;
+ return -EIO;
}
while (nb_sectors > 0) {
- index_in_cluster = sector_num & (s->cluster_sectors - 1);
- n = s->cluster_sectors - index_in_cluster;
- if (n > nb_sectors)
+ extent = find_extent(s, sector_num, extent);
+ if (!extent) {
+ return -EIO;
+ }
+ ret = get_cluster_offset(
+ bs,
+ extent,
+ &m_data,
+ sector_num << 9, 1,
+ &cluster_offset);
+ if (ret) {
+ return -EINVAL;
+ }
+ index_in_cluster = sector_num % extent->cluster_sectors;
+ n = extent->cluster_sectors - index_in_cluster;
+ if (n > nb_sectors) {
n = nb_sectors;
- cluster_offset = get_cluster_offset(bs, &m_data, sector_num << 9, 1);
- if (!cluster_offset)
- return -1;
+ }
- if (bdrv_pwrite(bs->file, cluster_offset + index_in_cluster * 512, buf, n * 512) != n * 512)
- return -1;
+ ret = bdrv_pwrite(extent->file,
+ cluster_offset + index_in_cluster * 512,
+ buf,
+ n * 512);
+ if (ret < 0) {
+ return ret;
+ }
if (m_data.valid) {
/* update L2 tables */
- if (vmdk_L2update(bs, &m_data) == -1)
- return -1;
+ if (vmdk_L2update(extent, &m_data) == -1) {
+ return -EIO;
+ }
}
nb_sectors -= n;
sector_num += n;
buf += n * 512;
- // update CID on the first write every time the virtual disk is opened
- if (!cid_update) {
+ /* update CID on the first write every time the virtual disk is
+ * opened */
+ if (!s->cid_updated) {
vmdk_write_cid(bs, time(NULL));
- cid_update++;
+ s->cid_updated = true;
}
}
return 0;
}
-static int vmdk_create(const char *filename, QEMUOptionParameter *options)
+
+static int vmdk_create_extent(const char *filename, int64_t filesize, bool flat)
{
- int fd, i;
+ int ret, i;
+ int fd = 0;
VMDK4Header header;
uint32_t tmp, magic, grains, gd_size, gt_size, gt_count;
- static const char desc_template[] =
- "# Disk DescriptorFile\n"
- "version=1\n"
- "CID=%x\n"
- "parentCID=ffffffff\n"
- "createType=\"monolithicSparse\"\n"
- "\n"
- "# Extent description\n"
- "RW %" PRId64 " SPARSE \"%s\"\n"
- "\n"
- "# The Disk Data Base \n"
- "#DDB\n"
- "\n"
- "ddb.virtualHWVersion = \"%d\"\n"
- "ddb.geometry.cylinders = \"%" PRId64 "\"\n"
- "ddb.geometry.heads = \"16\"\n"
- "ddb.geometry.sectors = \"63\"\n"
- "ddb.adapterType = \"ide\"\n";
- char desc[1024];
- const char *real_filename, *temp_str;
- int64_t total_size = 0;
- const char *backing_file = NULL;
- int flags = 0;
- int ret;
- // Read out options
- while (options && options->name) {
- if (!strcmp(options->name, BLOCK_OPT_SIZE)) {
- total_size = options->value.n / 512;
- } else if (!strcmp(options->name, BLOCK_OPT_BACKING_FILE)) {
- backing_file = options->value.s;
- } else if (!strcmp(options->name, BLOCK_OPT_COMPAT6)) {
- flags |= options->value.n ? BLOCK_FLAG_COMPAT6: 0;
- }
- options++;
+ fd = open(
+ filename,
+ O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE,
+ 0644);
+ if (fd < 0) {
+ return -errno;
}
-
- /* XXX: add support for backing file */
- if (backing_file) {
- return vmdk_snapshot_create(filename, backing_file);
+ if (flat) {
+ ret = ftruncate(fd, filesize);
+ if (ret < 0) {
+ ret = -errno;
+ }
+ goto exit;
}
-
- fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE,
- 0644);
- if (fd < 0)
- return -errno;
magic = cpu_to_be32(VMDK4_MAGIC);
memset(&header, 0, sizeof(header));
header.version = 1;
header.flags = 3; /* ?? */
- header.capacity = total_size;
+ header.capacity = filesize / 512;
header.granularity = 128;
header.num_gtes_per_gte = 512;
- grains = (total_size + header.granularity - 1) / header.granularity;
+ grains = (filesize / 512 + header.granularity - 1) / header.granularity;
gt_size = ((header.num_gtes_per_gte * sizeof(uint32_t)) + 511) >> 9;
- gt_count = (grains + header.num_gtes_per_gte - 1) / header.num_gtes_per_gte;
+ gt_count =
+ (grains + header.num_gtes_per_gte - 1) / header.num_gtes_per_gte;
gd_size = (gt_count * sizeof(uint32_t) + 511) >> 9;
header.desc_offset = 1;
@@ -735,7 +957,6 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options)
((header.gd_offset + gd_size + (gt_size * gt_count) +
header.granularity - 1) / header.granularity) *
header.granularity;
-
/* swap endianness for all header fields */
header.version = cpu_to_le32(header.version);
header.flags = cpu_to_le32(header.flags);
@@ -793,27 +1014,255 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options)
}
}
- /* compose the descriptor */
- real_filename = filename;
- if ((temp_str = strrchr(real_filename, '\\')) != NULL)
- real_filename = temp_str + 1;
- if ((temp_str = strrchr(real_filename, '/')) != NULL)
- real_filename = temp_str + 1;
- if ((temp_str = strrchr(real_filename, ':')) != NULL)
- real_filename = temp_str + 1;
- snprintf(desc, sizeof(desc), desc_template, (unsigned int)time(NULL),
- total_size, real_filename,
- (flags & BLOCK_FLAG_COMPAT6 ? 6 : 4),
- total_size / (int64_t)(63 * 16));
+ ret = 0;
+ exit:
+ close(fd);
+ return ret;
+}
+
+static int filename_decompose(const char *filename, char *path, char *prefix,
+ char *postfix, size_t buf_len)
+{
+ const char *p, *q;
+
+ if (filename == NULL || !strlen(filename)) {
+ fprintf(stderr, "Vmdk: no filename provided.\n");
+ return -1;
+ }
+ p = strrchr(filename, '/');
+ if (p == NULL) {
+ p = strrchr(filename, '\\');
+ }
+ if (p == NULL) {
+ p = strrchr(filename, ':');
+ }
+ if (p != NULL) {
+ p++;
+ if (p - filename >= buf_len) {
+ return -1;
+ }
+ pstrcpy(path, p - filename + 1, filename);
+ } else {
+ p = filename;
+ path[0] = '\0';
+ }
+ q = strrchr(p, '.');
+ if (q == NULL) {
+ pstrcpy(prefix, buf_len, p);
+ postfix[0] = '\0';
+ } else {
+ if (q - p >= buf_len) {
+ return -1;
+ }
+ pstrcpy(prefix, q - p + 1, p);
+ pstrcpy(postfix, buf_len, q);
+ }
+ return 0;
+}
+
+static int relative_path(char *dest, int dest_size,
+ const char *base, const char *target)
+{
+ int i = 0;
+ int n = 0;
+ const char *p, *q;
+#ifdef _WIN32
+ const char *sep = "\\";
+#else
+ const char *sep = "/";
+#endif
+
+ if (!(dest && base && target)) {
+ return -1;
+ }
+ if (path_is_absolute(target)) {
+ dest[dest_size - 1] = '\0';
+ strncpy(dest, target, dest_size - 1);
+ return 0;
+ }
+ while (base[i] == target[i]) {
+ i++;
+ }
+ p = &base[i];
+ q = &target[i];
+ while (*p) {
+ if (*p == *sep) {
+ n++;
+ }
+ p++;
+ }
+ dest[0] = '\0';
+ for (; n; n--) {
+ pstrcat(dest, dest_size, "..");
+ pstrcat(dest, dest_size, sep);
+ }
+ pstrcat(dest, dest_size, q);
+ return 0;
+}
+
+static int vmdk_create(const char *filename, QEMUOptionParameter *options)
+{
+ int fd, idx = 0;
+ char desc[BUF_SIZE];
+ int64_t total_size = 0, filesize;
+ const char *backing_file = NULL;
+ const char *fmt = NULL;
+ int flags = 0;
+ int ret = 0;
+ bool flat, split;
+ char ext_desc_lines[BUF_SIZE] = "";
+ char path[PATH_MAX], prefix[PATH_MAX], postfix[PATH_MAX];
+ const int64_t split_size = 0x80000000; /* VMDK has constant split size */
+ const char *desc_extent_line;
+ char parent_desc_line[BUF_SIZE] = "";
+ uint32_t parent_cid = 0xffffffff;
+ const char desc_template[] =
+ "# Disk DescriptorFile\n"
+ "version=1\n"
+ "CID=%x\n"
+ "parentCID=%x\n"
+ "createType=\"%s\"\n"
+ "%s"
+ "\n"
+ "# Extent description\n"
+ "%s"
+ "\n"
+ "# The Disk Data Base\n"
+ "#DDB\n"
+ "\n"
+ "ddb.virtualHWVersion = \"%d\"\n"
+ "ddb.geometry.cylinders = \"%" PRId64 "\"\n"
+ "ddb.geometry.heads = \"16\"\n"
+ "ddb.geometry.sectors = \"63\"\n"
+ "ddb.adapterType = \"ide\"\n";
+
+ if (filename_decompose(filename, path, prefix, postfix, PATH_MAX)) {
+ return -EINVAL;
+ }
+ /* Read out options */
+ while (options && options->name) {
+ if (!strcmp(options->name, BLOCK_OPT_SIZE)) {
+ total_size = options->value.n;
+ } else if (!strcmp(options->name, BLOCK_OPT_BACKING_FILE)) {
+ backing_file = options->value.s;
+ } else if (!strcmp(options->name, BLOCK_OPT_COMPAT6)) {
+ flags |= options->value.n ? BLOCK_FLAG_COMPAT6 : 0;
+ } else if (!strcmp(options->name, BLOCK_OPT_SUBFMT)) {
+ fmt = options->value.s;
+ }
+ options++;
+ }
+ if (!fmt) {
+ /* Default format to monolithicSparse */
+ fmt = "monolithicSparse";
+ } else if (strcmp(fmt, "monolithicFlat") &&
+ strcmp(fmt, "monolithicSparse") &&
+ strcmp(fmt, "twoGbMaxExtentSparse") &&
+ strcmp(fmt, "twoGbMaxExtentFlat")) {
+ fprintf(stderr, "VMDK: Unknown subformat: %s\n", fmt);
+ return -EINVAL;
+ }
+ split = !(strcmp(fmt, "twoGbMaxExtentFlat") &&
+ strcmp(fmt, "twoGbMaxExtentSparse"));
+ flat = !(strcmp(fmt, "monolithicFlat") &&
+ strcmp(fmt, "twoGbMaxExtentFlat"));
+ if (flat) {
+ desc_extent_line = "RW %lld FLAT \"%s\" 0\n";
+ } else {
+ desc_extent_line = "RW %lld SPARSE \"%s\"\n";
+ }
+ if (flat && backing_file) {
+ /* not supporting backing file for flat image */
+ return -ENOTSUP;
+ }
+ if (backing_file) {
+ char parent_filename[PATH_MAX];
+ BlockDriverState *bs = bdrv_new("");
+ ret = bdrv_open(bs, backing_file, 0, NULL);
+ if (ret != 0) {
+ bdrv_delete(bs);
+ return ret;
+ }
+ if (strcmp(bs->drv->format_name, "vmdk")) {
+ bdrv_delete(bs);
+ return -EINVAL;
+ }
+ filesize = bdrv_getlength(bs);
+ parent_cid = vmdk_read_cid(bs, 0);
+ bdrv_delete(bs);
+ relative_path(parent_filename, sizeof(parent_filename),
+ filename, backing_file);
+ snprintf(parent_desc_line, sizeof(parent_desc_line),
+ "parentFileNameHint=\"%s\"", parent_filename);
+ }
+
+ /* Create extents */
+ filesize = total_size;
+ while (filesize > 0) {
+ char desc_line[BUF_SIZE];
+ char ext_filename[PATH_MAX];
+ char desc_filename[PATH_MAX];
+ int64_t size = filesize;
- /* write the descriptor */
- lseek(fd, le64_to_cpu(header.desc_offset) << 9, SEEK_SET);
+ if (split && size > split_size) {
+ size = split_size;
+ }
+ if (split) {
+ snprintf(desc_filename, sizeof(desc_filename), "%s-%c%03d%s",
+ prefix, flat ? 'f' : 's', ++idx, postfix);
+ } else if (flat) {
+ snprintf(desc_filename, sizeof(desc_filename), "%s-flat%s",
+ prefix, postfix);
+ } else {
+ snprintf(desc_filename, sizeof(desc_filename), "%s%s",
+ prefix, postfix);
+ }
+ snprintf(ext_filename, sizeof(ext_filename), "%s%s",
+ path, desc_filename);
+
+ if (vmdk_create_extent(ext_filename, size, flat)) {
+ return -EINVAL;
+ }
+ filesize -= size;
+
+ /* Format description line */
+ snprintf(desc_line, sizeof(desc_line),
+ desc_extent_line, size / 512, desc_filename);
+ pstrcat(ext_desc_lines, sizeof(ext_desc_lines), desc_line);
+ }
+ /* generate descriptor file */
+ snprintf(desc, sizeof(desc), desc_template,
+ (unsigned int)time(NULL),
+ parent_cid,
+ fmt,
+ parent_desc_line,
+ ext_desc_lines,
+ (flags & BLOCK_FLAG_COMPAT6 ? 6 : 4),
+ total_size / (int64_t)(63 * 16 * 512));
+ if (split || flat) {
+ fd = open(
+ filename,
+ O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE,
+ 0644);
+ } else {
+ fd = open(
+ filename,
+ O_WRONLY | O_BINARY | O_LARGEFILE,
+ 0644);
+ }
+ if (fd < 0) {
+ return -errno;
+ }
+ /* the descriptor offset = 0x200 */
+ if (!split && !flat && 0x200 != lseek(fd, 0x200, SEEK_SET)) {
+ ret = -errno;
+ goto exit;
+ }
ret = qemu_write_full(fd, desc, strlen(desc));
if (ret != strlen(desc)) {
ret = -errno;
goto exit;
}
-
ret = 0;
exit:
close(fd);
@@ -822,17 +1271,47 @@ exit:
static void vmdk_close(BlockDriverState *bs)
{
- BDRVVmdkState *s = bs->opaque;
-
- qemu_free(s->l1_table);
- qemu_free(s->l2_cache);
+ vmdk_free_extents(bs);
}
static int vmdk_flush(BlockDriverState *bs)
{
- return bdrv_flush(bs->file);
+ int i, ret, err;
+ BDRVVmdkState *s = bs->opaque;
+
+ ret = bdrv_flush(bs->file);
+ for (i = 0; i < s->num_extents; i++) {
+ err = bdrv_flush(s->extents[i].file);
+ if (err < 0) {
+ ret = err;
+ }
+ }
+ return ret;
}
+static int64_t vmdk_get_allocated_file_size(BlockDriverState *bs)
+{
+ int i;
+ int64_t ret = 0;
+ int64_t r;
+ BDRVVmdkState *s = bs->opaque;
+
+ ret = bdrv_get_allocated_file_size(bs->file);
+ if (ret < 0) {
+ return ret;
+ }
+ for (i = 0; i < s->num_extents; i++) {
+ if (s->extents[i].file == bs->file) {
+ continue;
+ }
+ r = bdrv_get_allocated_file_size(s->extents[i].file);
+ if (r < 0) {
+ return r;
+ }
+ ret += r;
+ }
+ return ret;
+}
static QEMUOptionParameter vmdk_create_options[] = {
{
@@ -850,20 +1329,28 @@ static QEMUOptionParameter vmdk_create_options[] = {
.type = OPT_FLAG,
.help = "VMDK version 6 image"
},
+ {
+ .name = BLOCK_OPT_SUBFMT,
+ .type = OPT_STRING,
+ .help =
+ "VMDK flat extent format, can be one of "
+ "{monolithicSparse (default) | monolithicFlat | twoGbMaxExtentSparse | twoGbMaxExtentFlat} "
+ },
{ NULL }
};
static BlockDriver bdrv_vmdk = {
- .format_name = "vmdk",
- .instance_size = sizeof(BDRVVmdkState),
- .bdrv_probe = vmdk_probe,
+ .format_name = "vmdk",
+ .instance_size = sizeof(BDRVVmdkState),
+ .bdrv_probe = vmdk_probe,
.bdrv_open = vmdk_open,
- .bdrv_read = vmdk_read,
- .bdrv_write = vmdk_write,
- .bdrv_close = vmdk_close,
- .bdrv_create = vmdk_create,
- .bdrv_flush = vmdk_flush,
- .bdrv_is_allocated = vmdk_is_allocated,
+ .bdrv_read = vmdk_read,
+ .bdrv_write = vmdk_write,
+ .bdrv_close = vmdk_close,
+ .bdrv_create = vmdk_create,
+ .bdrv_flush = vmdk_flush,
+ .bdrv_is_allocated = vmdk_is_allocated,
+ .bdrv_get_allocated_file_size = vmdk_get_allocated_file_size,
.create_options = vmdk_create_options,
};
diff --git a/block/vpc.c b/block/vpc.c
index 56865da..fdd5236 100644
--- a/block/vpc.c
+++ b/block/vpc.c
@@ -156,6 +156,7 @@ static int vpc_open(BlockDriverState *bs, int flags)
struct vhd_dyndisk_header* dyndisk_header;
uint8_t buf[HEADER_SIZE];
uint32_t checksum;
+ int err = -1;
if (bdrv_pread(bs->file, 0, s->footer_buf, HEADER_SIZE) != HEADER_SIZE)
goto fail;
@@ -176,6 +177,11 @@ static int vpc_open(BlockDriverState *bs, int flags)
bs->total_sectors = (int64_t)
be16_to_cpu(footer->cyls) * footer->heads * footer->secs_per_cyl;
+ if (bs->total_sectors >= 65535 * 16 * 255) {
+ err = -EFBIG;
+ goto fail;
+ }
+
if (bdrv_pread(bs->file, be64_to_cpu(footer->data_offset), buf, HEADER_SIZE)
!= HEADER_SIZE)
goto fail;
@@ -222,7 +228,7 @@ static int vpc_open(BlockDriverState *bs, int flags)
return 0;
fail:
- return -1;
+ return err;
}
/*
diff --git a/block_int.h b/block_int.h
index 1e265d2..f6d02b3 100644
--- a/block_int.h
+++ b/block_int.h
@@ -27,6 +27,7 @@
#include "block.h"
#include "qemu-option.h"
#include "qemu-queue.h"
+#include "qemu-coroutine.h"
#define BLOCK_FLAG_ENCRYPT 1
#define BLOCK_FLAG_COMPAT6 4
@@ -39,6 +40,7 @@
#define BLOCK_OPT_CLUSTER_SIZE "cluster_size"
#define BLOCK_OPT_TABLE_SIZE "table_size"
#define BLOCK_OPT_PREALLOC "preallocation"
+#define BLOCK_OPT_SUBFMT "subformat"
typedef struct AIOPool {
void (*cancel)(BlockDriverAIOCB *acb);
@@ -76,6 +78,11 @@ struct BlockDriver {
int (*bdrv_discard)(BlockDriverState *bs, int64_t sector_num,
int nb_sectors);
+ int coroutine_fn (*bdrv_co_readv)(BlockDriverState *bs,
+ int64_t sector_num, int nb_sectors, QEMUIOVector *qiov);
+ int coroutine_fn (*bdrv_co_writev)(BlockDriverState *bs,
+ int64_t sector_num, int nb_sectors, QEMUIOVector *qiov);
+
int (*bdrv_aio_multiwrite)(BlockDriverState *bs, BlockRequest *reqs,
int num_reqs);
int (*bdrv_merge_requests)(BlockDriverState *bs, BlockRequest* a,
@@ -85,6 +92,7 @@ struct BlockDriver {
const char *protocol_name;
int (*bdrv_truncate)(BlockDriverState *bs, int64_t offset);
int64_t (*bdrv_getlength)(BlockDriverState *bs);
+ int64_t (*bdrv_get_allocated_file_size)(BlockDriverState *bs);
int (*bdrv_write_compressed)(BlockDriverState *bs, int64_t sector_num,
const uint8_t *buf, int nb_sectors);
@@ -110,8 +118,8 @@ struct BlockDriver {
/* removable device specific */
int (*bdrv_is_inserted)(BlockDriverState *bs);
int (*bdrv_media_changed)(BlockDriverState *bs);
- int (*bdrv_eject)(BlockDriverState *bs, int eject_flag);
- int (*bdrv_set_locked)(BlockDriverState *bs, int locked);
+ void (*bdrv_eject)(BlockDriverState *bs, int eject_flag);
+ void (*bdrv_set_locked)(BlockDriverState *bs, int locked);
/* to control generic scsi devices */
int (*bdrv_ioctl)(BlockDriverState *bs, unsigned long int req, void *buf);
diff --git a/blockdev.c b/blockdev.c
index a97a801..a25367a 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -568,7 +568,7 @@ void do_commit(Monitor *mon, const QDict *qdict)
int do_snapshot_blkdev(Monitor *mon, const QDict *qdict, QObject **ret_data)
{
const char *device = qdict_get_str(qdict, "device");
- const char *filename = qdict_get_try_str(qdict, "snapshot_file");
+ const char *filename = qdict_get_try_str(qdict, "snapshot-file");
const char *format = qdict_get_try_str(qdict, "format");
BlockDriverState *bs;
BlockDriver *drv, *old_drv, *proto_drv;
@@ -577,7 +577,7 @@ int do_snapshot_blkdev(Monitor *mon, const QDict *qdict, QObject **ret_data)
char old_filename[1024];
if (!filename) {
- qerror_report(QERR_MISSING_PARAMETER, "snapshot_file");
+ qerror_report(QERR_MISSING_PARAMETER, "snapshot-file");
ret = -1;
goto out;
}
@@ -646,16 +646,13 @@ out:
static int eject_device(Monitor *mon, BlockDriverState *bs, int force)
{
- if (!force) {
- if (!bdrv_is_removable(bs)) {
- qerror_report(QERR_DEVICE_NOT_REMOVABLE,
- bdrv_get_device_name(bs));
- return -1;
- }
- if (bdrv_is_locked(bs)) {
- qerror_report(QERR_DEVICE_LOCKED, bdrv_get_device_name(bs));
- return -1;
- }
+ if (!bdrv_is_removable(bs)) {
+ qerror_report(QERR_DEVICE_NOT_REMOVABLE, bdrv_get_device_name(bs));
+ return -1;
+ }
+ if (!force && bdrv_is_locked(bs)) {
+ qerror_report(QERR_DEVICE_LOCKED, bdrv_get_device_name(bs));
+ return -1;
}
bdrv_close(bs);
return 0;
diff --git a/bsd-user/main.c b/bsd-user/main.c
index 6018a41..a63b877 100644
--- a/bsd-user/main.c
+++ b/bsd-user/main.c
@@ -856,9 +856,6 @@ int main(int argc, char **argv)
usage();
}
}
- if (optind >= argc)
- usage();
- filename = argv[optind];
/* init debug */
cpu_set_log_filename(log_file);
@@ -877,6 +874,11 @@ int main(int argc, char **argv)
cpu_set_log(mask);
}
+ if (optind >= argc) {
+ usage();
+ }
+ filename = argv[optind];
+
/* Zero out regs */
memset(regs, 0, sizeof(struct target_pt_regs));
diff --git a/bswap.h b/bswap.h
index 82a7951..f41bebe 100644
--- a/bswap.h
+++ b/bswap.h
@@ -11,6 +11,8 @@
#include <machine/bswap.h>
#else
+#include "softfloat.h"
+
#ifdef CONFIG_BYTESWAP_H
#include <byteswap.h>
#else
@@ -237,4 +239,476 @@ static inline uint32_t qemu_bswap_len(uint32_t value, int len)
return bswap32(value) >> (32 - 8 * len);
}
+typedef union {
+ float32 f;
+ uint32_t l;
+} CPU_FloatU;
+
+typedef union {
+ float64 d;
+#if defined(HOST_WORDS_BIGENDIAN)
+ struct {
+ uint32_t upper;
+ uint32_t lower;
+ } l;
+#else
+ struct {
+ uint32_t lower;
+ uint32_t upper;
+ } l;
+#endif
+ uint64_t ll;
+} CPU_DoubleU;
+
+typedef union {
+ floatx80 d;
+ struct {
+ uint64_t lower;
+ uint16_t upper;
+ } l;
+} CPU_LDoubleU;
+
+typedef union {
+ float128 q;
+#if defined(HOST_WORDS_BIGENDIAN)
+ struct {
+ uint32_t upmost;
+ uint32_t upper;
+ uint32_t lower;
+ uint32_t lowest;
+ } l;
+ struct {
+ uint64_t upper;
+ uint64_t lower;
+ } ll;
+#else
+ struct {
+ uint32_t lowest;
+ uint32_t lower;
+ uint32_t upper;
+ uint32_t upmost;
+ } l;
+ struct {
+ uint64_t lower;
+ uint64_t upper;
+ } ll;
+#endif
+} CPU_QuadU;
+
+/* unaligned/endian-independent pointer access */
+
+/*
+ * the generic syntax is:
+ *
+ * load: ld{type}{sign}{size}{endian}_p(ptr)
+ *
+ * store: st{type}{size}{endian}_p(ptr, val)
+ *
+ * Note there are small differences with the softmmu access API!
+ *
+ * type is:
+ * (empty): integer access
+ * f : float access
+ *
+ * sign is:
+ * (empty): for floats or 32 bit size
+ * u : unsigned
+ * s : signed
+ *
+ * size is:
+ * b: 8 bits
+ * w: 16 bits
+ * l: 32 bits
+ * q: 64 bits
+ *
+ * endian is:
+ * (empty): 8 bit access
+ * be : big endian
+ * le : little endian
+ */
+static inline int ldub_p(const void *ptr)
+{
+ return *(uint8_t *)ptr;
+}
+
+static inline int ldsb_p(const void *ptr)
+{
+ return *(int8_t *)ptr;
+}
+
+static inline void stb_p(void *ptr, int v)
+{
+ *(uint8_t *)ptr = v;
+}
+
+/* NOTE: on arm, putting 2 in /proc/sys/debug/alignment so that the
+ kernel handles unaligned load/stores may give better results, but
+ it is a system wide setting : bad */
+#if defined(HOST_WORDS_BIGENDIAN) || defined(WORDS_ALIGNED)
+
+/* conservative code for little endian unaligned accesses */
+static inline int lduw_le_p(const void *ptr)
+{
+#ifdef _ARCH_PPC
+ int val;
+ __asm__ __volatile__ ("lhbrx %0,0,%1" : "=r" (val) : "r" (ptr));
+ return val;
+#else
+ const uint8_t *p = ptr;
+ return p[0] | (p[1] << 8);
+#endif
+}
+
+static inline int ldsw_le_p(const void *ptr)
+{
+#ifdef _ARCH_PPC
+ int val;
+ __asm__ __volatile__ ("lhbrx %0,0,%1" : "=r" (val) : "r" (ptr));
+ return (int16_t)val;
+#else
+ const uint8_t *p = ptr;
+ return (int16_t)(p[0] | (p[1] << 8));
+#endif
+}
+
+static inline int ldl_le_p(const void *ptr)
+{
+#ifdef _ARCH_PPC
+ int val;
+ __asm__ __volatile__ ("lwbrx %0,0,%1" : "=r" (val) : "r" (ptr));
+ return val;
+#else
+ const uint8_t *p = ptr;
+ return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
+#endif
+}
+
+static inline uint64_t ldq_le_p(const void *ptr)
+{
+ const uint8_t *p = ptr;
+ uint32_t v1, v2;
+ v1 = ldl_le_p(p);
+ v2 = ldl_le_p(p + 4);
+ return v1 | ((uint64_t)v2 << 32);
+}
+
+static inline void stw_le_p(void *ptr, int v)
+{
+#ifdef _ARCH_PPC
+ __asm__ __volatile__ ("sthbrx %1,0,%2" : "=m" (*(uint16_t *)ptr) : "r" (v), "r" (ptr));
+#else
+ uint8_t *p = ptr;
+ p[0] = v;
+ p[1] = v >> 8;
+#endif
+}
+
+static inline void stl_le_p(void *ptr, int v)
+{
+#ifdef _ARCH_PPC
+ __asm__ __volatile__ ("stwbrx %1,0,%2" : "=m" (*(uint32_t *)ptr) : "r" (v), "r" (ptr));
+#else
+ uint8_t *p = ptr;
+ p[0] = v;
+ p[1] = v >> 8;
+ p[2] = v >> 16;
+ p[3] = v >> 24;
+#endif
+}
+
+static inline void stq_le_p(void *ptr, uint64_t v)
+{
+ uint8_t *p = ptr;
+ stl_le_p(p, (uint32_t)v);
+ stl_le_p(p + 4, v >> 32);
+}
+
+/* float access */
+
+static inline float32 ldfl_le_p(const void *ptr)
+{
+ union {
+ float32 f;
+ uint32_t i;
+ } u;
+ u.i = ldl_le_p(ptr);
+ return u.f;
+}
+
+static inline void stfl_le_p(void *ptr, float32 v)
+{
+ union {
+ float32 f;
+ uint32_t i;
+ } u;
+ u.f = v;
+ stl_le_p(ptr, u.i);
+}
+
+static inline float64 ldfq_le_p(const void *ptr)
+{
+ CPU_DoubleU u;
+ u.l.lower = ldl_le_p(ptr);
+ u.l.upper = ldl_le_p(ptr + 4);
+ return u.d;
+}
+
+static inline void stfq_le_p(void *ptr, float64 v)
+{
+ CPU_DoubleU u;
+ u.d = v;
+ stl_le_p(ptr, u.l.lower);
+ stl_le_p(ptr + 4, u.l.upper);
+}
+
+#else
+
+static inline int lduw_le_p(const void *ptr)
+{
+ return *(uint16_t *)ptr;
+}
+
+static inline int ldsw_le_p(const void *ptr)
+{
+ return *(int16_t *)ptr;
+}
+
+static inline int ldl_le_p(const void *ptr)
+{
+ return *(uint32_t *)ptr;
+}
+
+static inline uint64_t ldq_le_p(const void *ptr)
+{
+ return *(uint64_t *)ptr;
+}
+
+static inline void stw_le_p(void *ptr, int v)
+{
+ *(uint16_t *)ptr = v;
+}
+
+static inline void stl_le_p(void *ptr, int v)
+{
+ *(uint32_t *)ptr = v;
+}
+
+static inline void stq_le_p(void *ptr, uint64_t v)
+{
+ *(uint64_t *)ptr = v;
+}
+
+/* float access */
+
+static inline float32 ldfl_le_p(const void *ptr)
+{
+ return *(float32 *)ptr;
+}
+
+static inline float64 ldfq_le_p(const void *ptr)
+{
+ return *(float64 *)ptr;
+}
+
+static inline void stfl_le_p(void *ptr, float32 v)
+{
+ *(float32 *)ptr = v;
+}
+
+static inline void stfq_le_p(void *ptr, float64 v)
+{
+ *(float64 *)ptr = v;
+}
+#endif
+
+#if !defined(HOST_WORDS_BIGENDIAN) || defined(WORDS_ALIGNED)
+
+static inline int lduw_be_p(const void *ptr)
+{
+#if defined(__i386__)
+ int val;
+ asm volatile ("movzwl %1, %0\n"
+ "xchgb %b0, %h0\n"
+ : "=q" (val)
+ : "m" (*(uint16_t *)ptr));
+ return val;
+#else
+ const uint8_t *b = ptr;
+ return ((b[0] << 8) | b[1]);
+#endif
+}
+
+static inline int ldsw_be_p(const void *ptr)
+{
+#if defined(__i386__)
+ int val;
+ asm volatile ("movzwl %1, %0\n"
+ "xchgb %b0, %h0\n"
+ : "=q" (val)
+ : "m" (*(uint16_t *)ptr));
+ return (int16_t)val;
+#else
+ const uint8_t *b = ptr;
+ return (int16_t)((b[0] << 8) | b[1]);
+#endif
+}
+
+static inline int ldl_be_p(const void *ptr)
+{
+#if defined(__i386__) || defined(__x86_64__)
+ int val;
+ asm volatile ("movl %1, %0\n"
+ "bswap %0\n"
+ : "=r" (val)
+ : "m" (*(uint32_t *)ptr));
+ return val;
+#else
+ const uint8_t *b = ptr;
+ return (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3];
+#endif
+}
+
+static inline uint64_t ldq_be_p(const void *ptr)
+{
+ uint32_t a,b;
+ a = ldl_be_p(ptr);
+ b = ldl_be_p((uint8_t *)ptr + 4);
+ return (((uint64_t)a<<32)|b);
+}
+
+static inline void stw_be_p(void *ptr, int v)
+{
+#if defined(__i386__)
+ asm volatile ("xchgb %b0, %h0\n"
+ "movw %w0, %1\n"
+ : "=q" (v)
+ : "m" (*(uint16_t *)ptr), "0" (v));
+#else
+ uint8_t *d = (uint8_t *) ptr;
+ d[0] = v >> 8;
+ d[1] = v;
+#endif
+}
+
+static inline void stl_be_p(void *ptr, int v)
+{
+#if defined(__i386__) || defined(__x86_64__)
+ asm volatile ("bswap %0\n"
+ "movl %0, %1\n"
+ : "=r" (v)
+ : "m" (*(uint32_t *)ptr), "0" (v));
+#else
+ uint8_t *d = (uint8_t *) ptr;
+ d[0] = v >> 24;
+ d[1] = v >> 16;
+ d[2] = v >> 8;
+ d[3] = v;
+#endif
+}
+
+static inline void stq_be_p(void *ptr, uint64_t v)
+{
+ stl_be_p(ptr, v >> 32);
+ stl_be_p((uint8_t *)ptr + 4, v);
+}
+
+/* float access */
+
+static inline float32 ldfl_be_p(const void *ptr)
+{
+ union {
+ float32 f;
+ uint32_t i;
+ } u;
+ u.i = ldl_be_p(ptr);
+ return u.f;
+}
+
+static inline void stfl_be_p(void *ptr, float32 v)
+{
+ union {
+ float32 f;
+ uint32_t i;
+ } u;
+ u.f = v;
+ stl_be_p(ptr, u.i);
+}
+
+static inline float64 ldfq_be_p(const void *ptr)
+{
+ CPU_DoubleU u;
+ u.l.upper = ldl_be_p(ptr);
+ u.l.lower = ldl_be_p((uint8_t *)ptr + 4);
+ return u.d;
+}
+
+static inline void stfq_be_p(void *ptr, float64 v)
+{
+ CPU_DoubleU u;
+ u.d = v;
+ stl_be_p(ptr, u.l.upper);
+ stl_be_p((uint8_t *)ptr + 4, u.l.lower);
+}
+
+#else
+
+static inline int lduw_be_p(const void *ptr)
+{
+ return *(uint16_t *)ptr;
+}
+
+static inline int ldsw_be_p(const void *ptr)
+{
+ return *(int16_t *)ptr;
+}
+
+static inline int ldl_be_p(const void *ptr)
+{
+ return *(uint32_t *)ptr;
+}
+
+static inline uint64_t ldq_be_p(const void *ptr)
+{
+ return *(uint64_t *)ptr;
+}
+
+static inline void stw_be_p(void *ptr, int v)
+{
+ *(uint16_t *)ptr = v;
+}
+
+static inline void stl_be_p(void *ptr, int v)
+{
+ *(uint32_t *)ptr = v;
+}
+
+static inline void stq_be_p(void *ptr, uint64_t v)
+{
+ *(uint64_t *)ptr = v;
+}
+
+/* float access */
+
+static inline float32 ldfl_be_p(const void *ptr)
+{
+ return *(float32 *)ptr;
+}
+
+static inline float64 ldfq_be_p(const void *ptr)
+{
+ return *(float64 *)ptr;
+}
+
+static inline void stfl_be_p(void *ptr, float32 v)
+{
+ *(float32 *)ptr = v;
+}
+
+static inline void stfq_be_p(void *ptr, float64 v)
+{
+ *(float64 *)ptr = v;
+}
+
+#endif
+
#endif /* BSWAP_H */
diff --git a/compatfd.c b/compatfd.c
index 41586ce..31654c6 100644
--- a/compatfd.c
+++ b/compatfd.c
@@ -115,3 +115,14 @@ int qemu_signalfd(const sigset_t *mask)
return qemu_signalfd_compat(mask);
}
+
+bool qemu_signalfd_available(void)
+{
+#ifdef CONFIG_SIGNALFD
+ errno = 0;
+ syscall(SYS_signalfd, -1, NULL, _NSIG / 8);
+ return errno != ENOSYS;
+#else
+ return false;
+#endif
+}
diff --git a/compatfd.h b/compatfd.h
index fc37915..6b04877 100644
--- a/compatfd.h
+++ b/compatfd.h
@@ -39,5 +39,6 @@ struct qemu_signalfd_siginfo {
};
int qemu_signalfd(const sigset_t *mask);
+bool qemu_signalfd_available(void);
#endif
diff --git a/compiler.h b/compiler.h
new file mode 100644
index 0000000..9af5dc6
--- /dev/null
+++ b/compiler.h
@@ -0,0 +1,34 @@
+/* public domain */
+
+#ifndef COMPILER_H
+#define COMPILER_H
+
+#include "config-host.h"
+
+#define QEMU_NORETURN __attribute__ ((__noreturn__))
+#ifdef CONFIG_GCC_ATTRIBUTE_WARN_UNUSED_RESULT
+#define QEMU_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
+#else
+#define QEMU_WARN_UNUSED_RESULT
+#endif
+
+#define QEMU_BUILD_BUG_ON(x) \
+ typedef char qemu_build_bug_on__##__LINE__[(x)?-1:1];
+
+#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
+
+#endif /* COMPILER_H */
diff --git a/configure b/configure
index 88159ac..408d454 100755
--- a/configure
+++ b/configure
@@ -113,7 +113,6 @@ curl=""
curses=""
docs=""
fdt=""
-kvm=""
nptl=""
sdl=""
vnc="yes"
@@ -129,9 +128,10 @@ xen=""
xen_ctrl_version=""
linux_aio=""
attr=""
-vhost_net=""
xfs=""
+vhost_net="no"
+kvm="no"
gprof="no"
debug_tcg="no"
debug_mon="no"
@@ -146,6 +146,7 @@ datadir="\${prefix}/share/qemu"
docdir="\${prefix}/share/doc/qemu"
bindir="\${prefix}/bin"
libdir="\${prefix}/lib"
+includedir="\${prefix}/include"
sysconfdir="\${prefix}/etc"
confsuffix="/qemu"
slirp="yes"
@@ -177,7 +178,10 @@ spice=""
rbd=""
smartcard=""
smartcard_nss=""
+usb_redir=""
opengl=""
+zlib="yes"
+guest_agent="yes"
# parse CC options first
for opt do
@@ -232,7 +236,7 @@ QEMU_CFLAGS="-Wall -Wundef -Wwrite-strings -Wmissing-prototypes $QEMU_CFLAGS"
QEMU_CFLAGS="-Wstrict-prototypes -Wredundant-decls $QEMU_CFLAGS"
QEMU_CFLAGS="-D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE $QEMU_CFLAGS"
QEMU_CFLAGS="-D_FORTIFY_SOURCE=2 $QEMU_CFLAGS"
-QEMU_INCLUDES="-I. -I\$(SRC_PATH)"
+QEMU_INCLUDES="-I. -I\$(SRC_PATH) -I\$(SRC_PATH)/fpu"
LDFLAGS="-g $LDFLAGS"
# make source path absolute
@@ -474,6 +478,7 @@ fi
: ${make=${MAKE-make}}
: ${install=${INSTALL-install}}
+: ${python=${PYTHON-python}}
if test "$mingw32" = "yes" ; then
EXESUF=".exe"
@@ -515,6 +520,8 @@ for opt do
;;
--install=*) install="$optarg"
;;
+ --python=*) python="$optarg"
+ ;;
--extra-cflags=*)
;;
--extra-ldflags=*)
@@ -539,6 +546,8 @@ for opt do
;;
--libdir=*) libdir="$optarg"
;;
+ --includedir=*) includedir="$optarg"
+ ;;
--datadir=*) datadir="$optarg"
;;
--docdir=*) docdir="$optarg"
@@ -743,6 +752,16 @@ for opt do
;;
--enable-smartcard-nss) smartcard_nss="yes"
;;
+ --disable-usb-redir) usb_redir="no"
+ ;;
+ --enable-usb-redir) usb_redir="yes"
+ ;;
+ --disable-zlib-test) zlib="no"
+ ;;
+ --enable-guest-agent) guest_agent="yes"
+ ;;
+ --disable-guest-agent) guest_agent="no"
+ ;;
*) echo "ERROR: unknown option $opt"; show_help="yes"
;;
esac
@@ -832,7 +851,6 @@ if [ "$softmmu" = "yes" ] ; then
default_target_list="\
i386-softmmu \
x86_64-softmmu \
-alpha-softmmu \
arm-softmmu \
cris-softmmu \
lm32-softmmu \
@@ -919,6 +937,7 @@ echo " --extra-cflags=CFLAGS append extra C compiler flags QEMU_CFLAGS"
echo " --extra-ldflags=LDFLAGS append extra linker flags LDFLAGS"
echo " --make=MAKE use specified make [$make]"
echo " --install=INSTALL use specified install [$install]"
+echo " --python=PYTHON use specified python [$python]"
echo " --static enable static build [$static]"
echo " --mandir=PATH install man pages in PATH"
echo " --datadir=PATH install firmware in PATH"
@@ -1018,6 +1037,10 @@ echo " --disable-smartcard disable smartcard support"
echo " --enable-smartcard enable smartcard support"
echo " --disable-smartcard-nss disable smartcard nss support"
echo " --enable-smartcard-nss enable smartcard nss support"
+echo " --disable-usb-redir disable usb network redirection support"
+echo " --enable-usb-redir enable usb network redirection support"
+echo " --disable-guest-agent disable building of the QEMU Guest Agent"
+echo " --enable-guest-agent enable building of the QEMU Guest Agent"
echo ""
echo "NOTE: The object files are built at the place where configure is launched"
exit 1
@@ -1077,6 +1100,15 @@ if test "$solaris" = "yes" ; then
fi
fi
+if test "$guest_agent" != "no" ; then
+ if has $python; then
+ :
+ else
+ echo "Python not found. Use --python=/path/to/python"
+ exit 1
+ fi
+fi
+
if test -z "$target_list" ; then
target_list="$default_target_list"
else
@@ -1172,18 +1204,20 @@ fi
##########################################
# zlib check
-cat > $TMPC << EOF
+if test "$zlib" != "no" ; then
+ cat > $TMPC << EOF
#include <zlib.h>
int main(void) { zlibVersion(); return 0; }
EOF
-if compile_prog "" "-lz" ; then
- :
-else
- echo
- echo "Error: zlib check failed"
- echo "Make sure to have the zlib libs and headers installed."
- echo
- exit 1
+ if compile_prog "" "-lz" ; then
+ :
+ else
+ echo
+ echo "Error: zlib check failed"
+ echo "Make sure to have the zlib libs and headers installed."
+ echo
+ exit 1
+ fi
fi
##########################################
@@ -1487,11 +1521,17 @@ int main(void) {
return 0;
}
EOF
+ if $pkg_config libpng --modversion >/dev/null 2>&1; then
+ vnc_png_cflags=`$pkg_config libpng --cflags 2> /dev/null`
+ vnc_png_libs=`$pkg_config libpng --libs 2> /dev/null`
+ else
vnc_png_cflags=""
vnc_png_libs="-lpng"
+ fi
if compile_prog "$vnc_png_cflags" "$vnc_png_libs" ; then
vnc_png=yes
libs_softmmu="$vnc_png_libs $libs_softmmu"
+ QEMU_CFLAGS="$QEMU_CFLAGS $vnc_png_cflags"
else
if test "$vnc_png" = "yes" ; then
feature_not_found "vnc-png"
@@ -1803,6 +1843,20 @@ EOF
fi
##########################################
+# glib support probe
+if test "$guest_agent" != "no" ; then
+ if $pkg_config --modversion glib-2.0 > /dev/null 2>&1 ; then
+ glib_cflags=`$pkg_config --cflags glib-2.0 2>/dev/null`
+ glib_libs=`$pkg_config --libs glib-2.0 2>/dev/null`
+ libs_softmmu="$glib_libs $libs_softmmu"
+ libs_tools="$glib_libs $libs_tools"
+ else
+ echo "glib-2.0 required to compile QEMU"
+ exit 1
+ fi
+fi
+
+##########################################
# pthread probe
PTHREADLIBS_LIST="-lpthread -lpthreadGC2"
@@ -2371,6 +2425,22 @@ if test "$smartcard" = "no" ; then
smartcard_nss="no"
fi
+# check for usbredirparser for usb network redirection support
+if test "$usb_redir" != "no" ; then
+ if $pkg_config libusbredirparser >/dev/null 2>&1 ; then
+ usb_redir="yes"
+ usb_redir_cflags=$($pkg_config --cflags libusbredirparser 2>/dev/null)
+ usb_redir_libs=$($pkg_config --libs libusbredirparser 2>/dev/null)
+ QEMU_CFLAGS="$QEMU_CFLAGS $usb_redir_cflags"
+ LIBS="$LIBS $usb_redir_libs"
+ else
+ if test "$usb_redir" = "yes"; then
+ feature_not_found "usb-redir"
+ fi
+ usb_redir="no"
+ fi
+fi
+
##########################################
##########################################
@@ -2464,6 +2534,43 @@ if test "$trace_backend" = "dtrace"; then
fi
##########################################
+# __sync_fetch_and_and requires at least -march=i486. Many toolchains
+# use i686 as default anyway, but for those that don't, an explicit
+# specification is necessary
+if test "$vhost_net" = "yes" && test "$cpu" = "i386"; then
+ cat > $TMPC << EOF
+int sfaa(unsigned *ptr)
+{
+ return __sync_fetch_and_and(ptr, 0);
+}
+
+int main(int argc, char **argv)
+{
+ int val = 42;
+ sfaa(&val);
+ return val;
+}
+EOF
+ if ! compile_prog "" "" ; then
+ CFLAGS+="-march=i486"
+ fi
+fi
+
+##########################################
+# check if we have makecontext
+
+ucontext_coroutine=no
+if test "$darwin" != "yes"; then
+ cat > $TMPC << EOF
+#include <ucontext.h>
+int main(void) { makecontext(0, 0, 0); }
+EOF
+ if compile_prog "" "" ; then
+ ucontext_coroutine=yes
+ fi
+fi
+
+##########################################
# End of CC checks
# After here, no more $cc or $ld runs
@@ -2520,6 +2627,9 @@ if test "$softmmu" = yes ; then
tools="qemu-img\$(EXESUF) qemu-io\$(EXESUF) $tools"
if [ "$linux" = "yes" -o "$bsd" = "yes" -o "$solaris" = "yes" ] ; then
tools="qemu-nbd\$(EXESUF) $tools"
+ if [ "$guest_agent" = "yes" ]; then
+ tools="qemu-ga\$(EXESUF) $tools"
+ fi
if [ "$check_utests" = "yes" ]; then
tools="check-qint check-qstring check-qdict check-qlist $tools"
tools="check-qfloat check-qjson $tools"
@@ -2542,6 +2652,7 @@ echo "Install prefix $prefix"
echo "BIOS directory `eval echo $datadir`"
echo "binary directory `eval echo $bindir`"
echo "library directory `eval echo $libdir`"
+echo "include directory `eval echo $includedir`"
echo "config directory `eval echo $sysconfdir`"
if test "$mingw32" = "no" ; then
echo "Manual directory `eval echo $mandir`"
@@ -2555,6 +2666,7 @@ echo "QEMU_CFLAGS $QEMU_CFLAGS"
echo "LDFLAGS $LDFLAGS"
echo "make $make"
echo "install $install"
+echo "python $python"
echo "host CPU $cpu"
echo "host big endian $bigendian"
echo "target list $target_list"
@@ -2617,9 +2729,11 @@ echo "spice support $spice"
echo "rbd support $rbd"
echo "xfsctl support $xfs"
echo "nss used $smartcard_nss"
+echo "usb net redir $usb_redir"
echo "OpenGL support $opengl"
+echo "build guest agent $guest_agent"
-if test $sdl_too_old = "yes"; then
+if test "$sdl_too_old" = "yes"; then
echo "-> Your SDL version is too old - please upgrade to have SDL support"
fi
@@ -2635,6 +2749,7 @@ echo all: >> $config_host_mak
echo "prefix=$prefix" >> $config_host_mak
echo "bindir=$bindir" >> $config_host_mak
echo "libdir=$libdir" >> $config_host_mak
+echo "includedir=$includedir" >> $config_host_mak
echo "mandir=$mandir" >> $config_host_mak
echo "datadir=$datadir" >> $config_host_mak
echo "sysconfdir=$sysconfdir" >> $config_host_mak
@@ -2706,7 +2821,7 @@ fi
if test "$static" = "yes" ; then
echo "CONFIG_STATIC=y" >> $config_host_mak
fi
-if test $profiler = "yes" ; then
+if test "$profiler" = "yes" ; then
echo "CONFIG_PROFILER=y" >> $config_host_mak
fi
if test "$slirp" = "yes" ; then
@@ -2849,8 +2964,9 @@ if test "$bluez" = "yes" ; then
echo "CONFIG_BLUEZ=y" >> $config_host_mak
echo "BLUEZ_CFLAGS=$bluez_cflags" >> $config_host_mak
fi
+echo "GLIB_CFLAGS=$glib_cflags" >> $config_host_mak
if test "$xen" = "yes" ; then
- echo "CONFIG_XEN=y" >> $config_host_mak
+ echo "CONFIG_XEN_BACKEND=y" >> $config_host_mak
echo "CONFIG_XEN_CTRL_INTERFACE_VERSION=$xen_ctrl_version" >> $config_host_mak
fi
if test "$io_thread" = "yes" ; then
@@ -2910,6 +3026,10 @@ if test "$smartcard_nss" = "yes" ; then
echo "CONFIG_SMARTCARD_NSS=y" >> $config_host_mak
fi
+if test "$usb_redir" = "yes" ; then
+ echo "CONFIG_USB_REDIR=y" >> $config_host_mak
+fi
+
if test "$opengl" = "yes" ; then
echo "CONFIG_OPENGL=y" >> $config_host_mak
fi
@@ -2928,6 +3048,10 @@ if test "$rbd" = "yes" ; then
echo "CONFIG_RBD=y" >> $config_host_mak
fi
+if test "$ucontext_coroutine" = "yes" ; then
+ echo "CONFIG_UCONTEXT_COROUTINE=y" >> $config_host_mak
+fi
+
# USB host support
case "$usb" in
linux)
@@ -2961,6 +3085,7 @@ echo "INSTALL=$install" >> $config_host_mak
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 "PYTHON=$python" >> $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
@@ -3235,10 +3360,12 @@ case "$target_arch2" in
if test "$xen" = "yes" -a "$target_softmmu" = "yes" ; then
target_phys_bits=64
echo "CONFIG_XEN=y" >> $config_target_mak
- if test "$cpu" = "i386" -o "$cpu" = "x86_64"; then
- echo "CONFIG_XEN_MAPCACHE=y" >> $config_target_mak
- fi
+ else
+ echo "CONFIG_NO_XEN=y" >> $config_target_mak
fi
+ ;;
+ *)
+ echo "CONFIG_NO_XEN=y" >> $config_target_mak
esac
case "$target_arch2" in
i386|x86_64|ppcemb|ppc|ppc64|s390x)
@@ -3252,7 +3379,7 @@ case "$target_arch2" in
\( "$target_arch2" = "x86_64" -a "$cpu" = "i386" \) -o \
\( "$target_arch2" = "i386" -a "$cpu" = "x86_64" \) \) ; then
echo "CONFIG_KVM=y" >> $config_target_mak
- if test $vhost_net = "yes" ; then
+ if test "$vhost_net" = "yes" ; then
echo "CONFIG_VHOST_NET=y" >> $config_target_mak
fi
fi
@@ -3319,7 +3446,6 @@ else
includes="-I\$(SRC_PATH)/tcg/\$(ARCH) $includes"
fi
includes="-I\$(SRC_PATH)/tcg $includes"
-includes="-I\$(SRC_PATH)/fpu $includes"
if test "$target_user_only" = "yes" ; then
libdis_config_mak=libdis-user/config.mak
@@ -3473,6 +3599,8 @@ DIRS="tests tests/cris slirp audio block net pc-bios/optionrom"
DIRS="$DIRS pc-bios/spapr-rtas"
DIRS="$DIRS roms/seabios roms/vgabios"
DIRS="$DIRS fsdev ui"
+DIRS="$DIRS qapi"
+DIRS="$DIRS qga"
FILES="Makefile tests/Makefile"
FILES="$FILES tests/cris/Makefile tests/cris/.gdbinit"
FILES="$FILES pc-bios/optionrom/Makefile pc-bios/keymaps"
diff --git a/console.c b/console.c
index acd8ca1..242086cf 100644
--- a/console.c
+++ b/console.c
@@ -1514,7 +1514,7 @@ static void text_console_do_init(CharDriverState *chr, DisplayState *ds)
chr->init(chr);
}
-CharDriverState *text_console_init(QemuOpts *opts)
+int text_console_init(QemuOpts *opts, CharDriverState **_chr)
{
CharDriverState *chr;
TextConsole *s;
@@ -1546,7 +1546,7 @@ CharDriverState *text_console_init(QemuOpts *opts)
if (!s) {
free(chr);
- return NULL;
+ return -EBUSY;
}
s->chr = chr;
@@ -1554,7 +1554,9 @@ CharDriverState *text_console_init(QemuOpts *opts)
s->g_height = height;
chr->opaque = s;
chr->chr_set_echo = text_console_set_echo;
- return chr;
+
+ *_chr = chr;
+ return 0;
}
void text_consoles_set_display(DisplayState *ds)
diff --git a/console.h b/console.h
index 64d1f09..67d1373 100644
--- a/console.h
+++ b/console.h
@@ -354,7 +354,7 @@ void vga_hw_text_update(console_ch_t *chardata);
int is_graphic_console(void);
int is_fixedsize_console(void);
-CharDriverState *text_console_init(QemuOpts *opts);
+int text_console_init(QemuOpts *opts, CharDriverState **_chr);
void text_consoles_set_display(DisplayState *ds);
void console_select(unsigned int index);
void console_color_init(DisplayState *ds);
@@ -372,6 +372,7 @@ void cocoa_display_init(DisplayState *ds, int full_screen);
void vnc_display_init(DisplayState *ds);
void vnc_display_close(DisplayState *ds);
int vnc_display_open(DisplayState *ds, const char *display);
+void vnc_display_add_client(DisplayState *ds, int csock, int skipauth);
int vnc_display_disable_login(DisplayState *ds);
char *vnc_display_local_addr(DisplayState *ds);
#ifdef CONFIG_VNC
diff --git a/coroutine-gthread.c b/coroutine-gthread.c
new file mode 100644
index 0000000..f09877e
--- /dev/null
+++ b/coroutine-gthread.c
@@ -0,0 +1,131 @@
+/*
+ * GThread coroutine initialization code
+ *
+ * Copyright (C) 2006 Anthony Liguori <anthony@codemonkey.ws>
+ * Copyright (C) 2011 Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.0 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib.h>
+#include "qemu-common.h"
+#include "qemu-coroutine-int.h"
+
+typedef struct {
+ Coroutine base;
+ GThread *thread;
+ bool runnable;
+ CoroutineAction action;
+} CoroutineGThread;
+
+static GCond *coroutine_cond;
+static GStaticMutex coroutine_lock = G_STATIC_MUTEX_INIT;
+static GStaticPrivate coroutine_key = G_STATIC_PRIVATE_INIT;
+
+static void __attribute__((constructor)) coroutine_init(void)
+{
+ if (!g_thread_supported()) {
+ g_thread_init(NULL);
+ }
+
+ coroutine_cond = g_cond_new();
+}
+
+static void coroutine_wait_runnable_locked(CoroutineGThread *co)
+{
+ while (!co->runnable) {
+ g_cond_wait(coroutine_cond, g_static_mutex_get_mutex(&coroutine_lock));
+ }
+}
+
+static void coroutine_wait_runnable(CoroutineGThread *co)
+{
+ g_static_mutex_lock(&coroutine_lock);
+ coroutine_wait_runnable_locked(co);
+ g_static_mutex_unlock(&coroutine_lock);
+}
+
+static gpointer coroutine_thread(gpointer opaque)
+{
+ CoroutineGThread *co = opaque;
+
+ g_static_private_set(&coroutine_key, co, NULL);
+ coroutine_wait_runnable(co);
+ co->base.entry(co->base.entry_arg);
+ qemu_coroutine_switch(&co->base, co->base.caller, COROUTINE_TERMINATE);
+ return NULL;
+}
+
+Coroutine *qemu_coroutine_new(void)
+{
+ CoroutineGThread *co;
+
+ co = qemu_mallocz(sizeof(*co));
+ co->thread = g_thread_create_full(coroutine_thread, co, 0, TRUE, TRUE,
+ G_THREAD_PRIORITY_NORMAL, NULL);
+ if (!co->thread) {
+ qemu_free(co);
+ return NULL;
+ }
+ return &co->base;
+}
+
+void qemu_coroutine_delete(Coroutine *co_)
+{
+ CoroutineGThread *co = DO_UPCAST(CoroutineGThread, base, co_);
+
+ g_thread_join(co->thread);
+ qemu_free(co);
+}
+
+CoroutineAction qemu_coroutine_switch(Coroutine *from_,
+ Coroutine *to_,
+ CoroutineAction action)
+{
+ CoroutineGThread *from = DO_UPCAST(CoroutineGThread, base, from_);
+ CoroutineGThread *to = DO_UPCAST(CoroutineGThread, base, to_);
+
+ g_static_mutex_lock(&coroutine_lock);
+ from->runnable = false;
+ from->action = action;
+ to->runnable = true;
+ to->action = action;
+ g_cond_broadcast(coroutine_cond);
+
+ if (action != COROUTINE_TERMINATE) {
+ coroutine_wait_runnable_locked(from);
+ }
+ g_static_mutex_unlock(&coroutine_lock);
+ return from->action;
+}
+
+Coroutine *qemu_coroutine_self(void)
+{
+ CoroutineGThread *co = g_static_private_get(&coroutine_key);
+
+ if (!co) {
+ co = qemu_mallocz(sizeof(*co));
+ co->runnable = true;
+ g_static_private_set(&coroutine_key, co, (GDestroyNotify)qemu_free);
+ }
+
+ return &co->base;
+}
+
+bool qemu_in_coroutine(void)
+{
+ CoroutineGThread *co = g_static_private_get(&coroutine_key);
+
+ return co && co->base.caller;
+}
diff --git a/coroutine-ucontext.c b/coroutine-ucontext.c
new file mode 100644
index 0000000..41c2379
--- /dev/null
+++ b/coroutine-ucontext.c
@@ -0,0 +1,230 @@
+/*
+ * ucontext coroutine initialization code
+ *
+ * Copyright (C) 2006 Anthony Liguori <anthony@codemonkey.ws>
+ * Copyright (C) 2011 Kevin Wolf <kwolf@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.0 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* XXX Is there a nicer way to disable glibc's stack check for longjmp? */
+#ifdef _FORTIFY_SOURCE
+#undef _FORTIFY_SOURCE
+#endif
+#include <stdlib.h>
+#include <setjmp.h>
+#include <stdint.h>
+#include <pthread.h>
+#include <ucontext.h>
+#include "qemu-common.h"
+#include "qemu-coroutine-int.h"
+
+enum {
+ /* Maximum free pool size prevents holding too many freed coroutines */
+ POOL_MAX_SIZE = 64,
+};
+
+typedef struct {
+ Coroutine base;
+ void *stack;
+ jmp_buf env;
+} CoroutineUContext;
+
+/**
+ * Per-thread coroutine bookkeeping
+ */
+typedef struct {
+ /** Currently executing coroutine */
+ Coroutine *current;
+
+ /** Free list to speed up creation */
+ QLIST_HEAD(, Coroutine) pool;
+ unsigned int pool_size;
+
+ /** The default coroutine */
+ CoroutineUContext leader;
+} CoroutineThreadState;
+
+static pthread_key_t thread_state_key;
+
+/*
+ * va_args to makecontext() must be type 'int', so passing
+ * the pointer we need may require several int args. This
+ * union is a quick hack to let us do that
+ */
+union cc_arg {
+ void *p;
+ int i[2];
+};
+
+static CoroutineThreadState *coroutine_get_thread_state(void)
+{
+ CoroutineThreadState *s = pthread_getspecific(thread_state_key);
+
+ if (!s) {
+ s = qemu_mallocz(sizeof(*s));
+ s->current = &s->leader.base;
+ QLIST_INIT(&s->pool);
+ pthread_setspecific(thread_state_key, s);
+ }
+ return s;
+}
+
+static void qemu_coroutine_thread_cleanup(void *opaque)
+{
+ CoroutineThreadState *s = opaque;
+ Coroutine *co;
+ Coroutine *tmp;
+
+ QLIST_FOREACH_SAFE(co, &s->pool, pool_next, tmp) {
+ qemu_free(DO_UPCAST(CoroutineUContext, base, co)->stack);
+ qemu_free(co);
+ }
+ qemu_free(s);
+}
+
+static void __attribute__((constructor)) coroutine_init(void)
+{
+ int ret;
+
+ ret = pthread_key_create(&thread_state_key, qemu_coroutine_thread_cleanup);
+ if (ret != 0) {
+ fprintf(stderr, "unable to create leader key: %s\n", strerror(errno));
+ abort();
+ }
+}
+
+static void coroutine_trampoline(int i0, int i1)
+{
+ union cc_arg arg;
+ CoroutineUContext *self;
+ Coroutine *co;
+
+ arg.i[0] = i0;
+ arg.i[1] = i1;
+ self = arg.p;
+ co = &self->base;
+
+ /* Initialize longjmp environment and switch back the caller */
+ if (!setjmp(self->env)) {
+ longjmp(*(jmp_buf *)co->entry_arg, 1);
+ }
+
+ while (true) {
+ co->entry(co->entry_arg);
+ qemu_coroutine_switch(co, co->caller, COROUTINE_TERMINATE);
+ }
+}
+
+static Coroutine *coroutine_new(void)
+{
+ const size_t stack_size = 1 << 20;
+ CoroutineUContext *co;
+ ucontext_t old_uc, uc;
+ jmp_buf old_env;
+ union cc_arg arg;
+
+ /* The ucontext functions preserve signal masks which incurs a system call
+ * overhead. setjmp()/longjmp() does not preserve signal masks but only
+ * works on the current stack. Since we need a way to create and switch to
+ * a new stack, use the ucontext functions for that but setjmp()/longjmp()
+ * for everything else.
+ */
+
+ if (getcontext(&uc) == -1) {
+ abort();
+ }
+
+ co = qemu_mallocz(sizeof(*co));
+ co->stack = qemu_malloc(stack_size);
+ co->base.entry_arg = &old_env; /* stash away our jmp_buf */
+
+ uc.uc_link = &old_uc;
+ uc.uc_stack.ss_sp = co->stack;
+ uc.uc_stack.ss_size = stack_size;
+ uc.uc_stack.ss_flags = 0;
+
+ arg.p = co;
+
+ makecontext(&uc, (void (*)(void))coroutine_trampoline,
+ 2, arg.i[0], arg.i[1]);
+
+ /* swapcontext() in, longjmp() back out */
+ if (!setjmp(old_env)) {
+ swapcontext(&old_uc, &uc);
+ }
+ return &co->base;
+}
+
+Coroutine *qemu_coroutine_new(void)
+{
+ CoroutineThreadState *s = coroutine_get_thread_state();
+ Coroutine *co;
+
+ co = QLIST_FIRST(&s->pool);
+ if (co) {
+ QLIST_REMOVE(co, pool_next);
+ s->pool_size--;
+ } else {
+ co = coroutine_new();
+ }
+ return co;
+}
+
+void qemu_coroutine_delete(Coroutine *co_)
+{
+ CoroutineThreadState *s = coroutine_get_thread_state();
+ CoroutineUContext *co = DO_UPCAST(CoroutineUContext, base, co_);
+
+ if (s->pool_size < POOL_MAX_SIZE) {
+ QLIST_INSERT_HEAD(&s->pool, &co->base, pool_next);
+ co->base.caller = NULL;
+ s->pool_size++;
+ return;
+ }
+
+ qemu_free(co->stack);
+ qemu_free(co);
+}
+
+CoroutineAction qemu_coroutine_switch(Coroutine *from_, Coroutine *to_,
+ CoroutineAction action)
+{
+ CoroutineUContext *from = DO_UPCAST(CoroutineUContext, base, from_);
+ CoroutineUContext *to = DO_UPCAST(CoroutineUContext, base, to_);
+ CoroutineThreadState *s = coroutine_get_thread_state();
+ int ret;
+
+ s->current = to_;
+
+ ret = setjmp(from->env);
+ if (ret == 0) {
+ longjmp(to->env, action);
+ }
+ return ret;
+}
+
+Coroutine *qemu_coroutine_self(void)
+{
+ CoroutineThreadState *s = coroutine_get_thread_state();
+
+ return s->current;
+}
+
+bool qemu_in_coroutine(void)
+{
+ CoroutineThreadState *s = pthread_getspecific(thread_state_key);
+
+ return s && s->current->caller;
+}
diff --git a/coroutine-win32.c b/coroutine-win32.c
new file mode 100644
index 0000000..0e29448
--- /dev/null
+++ b/coroutine-win32.c
@@ -0,0 +1,92 @@
+/*
+ * Win32 coroutine initialization code
+ *
+ * Copyright (c) 2011 Kevin Wolf <kwolf@redhat.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 "qemu-common.h"
+#include "qemu-coroutine-int.h"
+
+typedef struct
+{
+ Coroutine base;
+
+ LPVOID fiber;
+ CoroutineAction action;
+} CoroutineWin32;
+
+static __thread CoroutineWin32 leader;
+static __thread Coroutine *current;
+
+CoroutineAction qemu_coroutine_switch(Coroutine *from_, Coroutine *to_,
+ CoroutineAction action)
+{
+ CoroutineWin32 *from = DO_UPCAST(CoroutineWin32, base, from_);
+ CoroutineWin32 *to = DO_UPCAST(CoroutineWin32, base, to_);
+
+ current = to_;
+
+ to->action = action;
+ SwitchToFiber(to->fiber);
+ return from->action;
+}
+
+static void CALLBACK coroutine_trampoline(void *co_)
+{
+ Coroutine *co = co_;
+
+ while (true) {
+ co->entry(co->entry_arg);
+ qemu_coroutine_switch(co, co->caller, COROUTINE_TERMINATE);
+ }
+}
+
+Coroutine *qemu_coroutine_new(void)
+{
+ const size_t stack_size = 1 << 20;
+ CoroutineWin32 *co;
+
+ co = qemu_mallocz(sizeof(*co));
+ co->fiber = CreateFiber(stack_size, coroutine_trampoline, &co->base);
+ return &co->base;
+}
+
+void qemu_coroutine_delete(Coroutine *co_)
+{
+ CoroutineWin32 *co = DO_UPCAST(CoroutineWin32, base, co_);
+
+ DeleteFiber(co->fiber);
+ qemu_free(co);
+}
+
+Coroutine *qemu_coroutine_self(void)
+{
+ if (!current) {
+ current = &leader.base;
+ leader.fiber = ConvertThreadToFiber(NULL);
+ }
+ return current;
+}
+
+bool qemu_in_coroutine(void)
+{
+ return current && current->caller;
+}
diff --git a/cpu-all.h b/cpu-all.h
index e839100..fa0205c 100644
--- a/cpu-all.h
+++ b/cpu-all.h
@@ -35,8 +35,6 @@
* TARGET_WORDS_BIGENDIAN : same for target cpu
*/
-#include "softfloat.h"
-
#if defined(HOST_WORDS_BIGENDIAN) != defined(TARGET_WORDS_BIGENDIAN)
#define BSWAP_NEEDED
#endif
@@ -114,64 +112,6 @@ static inline void tswap64s(uint64_t *s)
#define bswaptls(s) bswap64s(s)
#endif
-typedef union {
- float32 f;
- uint32_t l;
-} CPU_FloatU;
-
-/* NOTE: arm FPA is horrible as double 32 bit words are stored in big
- endian ! */
-typedef union {
- float64 d;
-#if defined(HOST_WORDS_BIGENDIAN)
- struct {
- uint32_t upper;
- uint32_t lower;
- } l;
-#else
- struct {
- uint32_t lower;
- uint32_t upper;
- } l;
-#endif
- uint64_t ll;
-} CPU_DoubleU;
-
-typedef union {
- floatx80 d;
- struct {
- uint64_t lower;
- uint16_t upper;
- } l;
-} CPU_LDoubleU;
-
-typedef union {
- float128 q;
-#if defined(HOST_WORDS_BIGENDIAN)
- struct {
- uint32_t upmost;
- uint32_t upper;
- uint32_t lower;
- uint32_t lowest;
- } l;
- struct {
- uint64_t upper;
- uint64_t lower;
- } ll;
-#else
- struct {
- uint32_t lowest;
- uint32_t lower;
- uint32_t upper;
- uint32_t upmost;
- } l;
- struct {
- uint64_t lower;
- uint64_t upper;
- } ll;
-#endif
-} CPU_QuadU;
-
/* CPU memory access without any memory or io remapping */
/*
@@ -207,392 +147,8 @@ typedef union {
* user : user mode access using soft MMU
* kernel : kernel mode access using soft MMU
*/
-static inline int ldub_p(const void *ptr)
-{
- return *(uint8_t *)ptr;
-}
-
-static inline int ldsb_p(const void *ptr)
-{
- return *(int8_t *)ptr;
-}
-
-static inline void stb_p(void *ptr, int v)
-{
- *(uint8_t *)ptr = v;
-}
-
-/* NOTE: on arm, putting 2 in /proc/sys/debug/alignment so that the
- kernel handles unaligned load/stores may give better results, but
- it is a system wide setting : bad */
-#if defined(HOST_WORDS_BIGENDIAN) || defined(WORDS_ALIGNED)
-
-/* conservative code for little endian unaligned accesses */
-static inline int lduw_le_p(const void *ptr)
-{
-#ifdef _ARCH_PPC
- int val;
- __asm__ __volatile__ ("lhbrx %0,0,%1" : "=r" (val) : "r" (ptr));
- return val;
-#else
- const uint8_t *p = ptr;
- return p[0] | (p[1] << 8);
-#endif
-}
-
-static inline int ldsw_le_p(const void *ptr)
-{
-#ifdef _ARCH_PPC
- int val;
- __asm__ __volatile__ ("lhbrx %0,0,%1" : "=r" (val) : "r" (ptr));
- return (int16_t)val;
-#else
- const uint8_t *p = ptr;
- return (int16_t)(p[0] | (p[1] << 8));
-#endif
-}
-
-static inline int ldl_le_p(const void *ptr)
-{
-#ifdef _ARCH_PPC
- int val;
- __asm__ __volatile__ ("lwbrx %0,0,%1" : "=r" (val) : "r" (ptr));
- return val;
-#else
- const uint8_t *p = ptr;
- return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
-#endif
-}
-
-static inline uint64_t ldq_le_p(const void *ptr)
-{
- const uint8_t *p = ptr;
- uint32_t v1, v2;
- v1 = ldl_le_p(p);
- v2 = ldl_le_p(p + 4);
- return v1 | ((uint64_t)v2 << 32);
-}
-
-static inline void stw_le_p(void *ptr, int v)
-{
-#ifdef _ARCH_PPC
- __asm__ __volatile__ ("sthbrx %1,0,%2" : "=m" (*(uint16_t *)ptr) : "r" (v), "r" (ptr));
-#else
- uint8_t *p = ptr;
- p[0] = v;
- p[1] = v >> 8;
-#endif
-}
-
-static inline void stl_le_p(void *ptr, int v)
-{
-#ifdef _ARCH_PPC
- __asm__ __volatile__ ("stwbrx %1,0,%2" : "=m" (*(uint32_t *)ptr) : "r" (v), "r" (ptr));
-#else
- uint8_t *p = ptr;
- p[0] = v;
- p[1] = v >> 8;
- p[2] = v >> 16;
- p[3] = v >> 24;
-#endif
-}
-
-static inline void stq_le_p(void *ptr, uint64_t v)
-{
- uint8_t *p = ptr;
- stl_le_p(p, (uint32_t)v);
- stl_le_p(p + 4, v >> 32);
-}
-
-/* float access */
-
-static inline float32 ldfl_le_p(const void *ptr)
-{
- union {
- float32 f;
- uint32_t i;
- } u;
- u.i = ldl_le_p(ptr);
- return u.f;
-}
-
-static inline void stfl_le_p(void *ptr, float32 v)
-{
- union {
- float32 f;
- uint32_t i;
- } u;
- u.f = v;
- stl_le_p(ptr, u.i);
-}
-
-static inline float64 ldfq_le_p(const void *ptr)
-{
- CPU_DoubleU u;
- u.l.lower = ldl_le_p(ptr);
- u.l.upper = ldl_le_p(ptr + 4);
- return u.d;
-}
-
-static inline void stfq_le_p(void *ptr, float64 v)
-{
- CPU_DoubleU u;
- u.d = v;
- stl_le_p(ptr, u.l.lower);
- stl_le_p(ptr + 4, u.l.upper);
-}
-
-#else
-
-static inline int lduw_le_p(const void *ptr)
-{
- return *(uint16_t *)ptr;
-}
-
-static inline int ldsw_le_p(const void *ptr)
-{
- return *(int16_t *)ptr;
-}
-
-static inline int ldl_le_p(const void *ptr)
-{
- return *(uint32_t *)ptr;
-}
-
-static inline uint64_t ldq_le_p(const void *ptr)
-{
- return *(uint64_t *)ptr;
-}
-
-static inline void stw_le_p(void *ptr, int v)
-{
- *(uint16_t *)ptr = v;
-}
-
-static inline void stl_le_p(void *ptr, int v)
-{
- *(uint32_t *)ptr = v;
-}
-
-static inline void stq_le_p(void *ptr, uint64_t v)
-{
- *(uint64_t *)ptr = v;
-}
-
-/* float access */
-
-static inline float32 ldfl_le_p(const void *ptr)
-{
- return *(float32 *)ptr;
-}
-
-static inline float64 ldfq_le_p(const void *ptr)
-{
- return *(float64 *)ptr;
-}
-
-static inline void stfl_le_p(void *ptr, float32 v)
-{
- *(float32 *)ptr = v;
-}
-
-static inline void stfq_le_p(void *ptr, float64 v)
-{
- *(float64 *)ptr = v;
-}
-#endif
-
-#if !defined(HOST_WORDS_BIGENDIAN) || defined(WORDS_ALIGNED)
-
-static inline int lduw_be_p(const void *ptr)
-{
-#if defined(__i386__)
- int val;
- asm volatile ("movzwl %1, %0\n"
- "xchgb %b0, %h0\n"
- : "=q" (val)
- : "m" (*(uint16_t *)ptr));
- return val;
-#else
- const uint8_t *b = ptr;
- return ((b[0] << 8) | b[1]);
-#endif
-}
-
-static inline int ldsw_be_p(const void *ptr)
-{
-#if defined(__i386__)
- int val;
- asm volatile ("movzwl %1, %0\n"
- "xchgb %b0, %h0\n"
- : "=q" (val)
- : "m" (*(uint16_t *)ptr));
- return (int16_t)val;
-#else
- const uint8_t *b = ptr;
- return (int16_t)((b[0] << 8) | b[1]);
-#endif
-}
-
-static inline int ldl_be_p(const void *ptr)
-{
-#if defined(__i386__) || defined(__x86_64__)
- int val;
- asm volatile ("movl %1, %0\n"
- "bswap %0\n"
- : "=r" (val)
- : "m" (*(uint32_t *)ptr));
- return val;
-#else
- const uint8_t *b = ptr;
- return (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3];
-#endif
-}
-
-static inline uint64_t ldq_be_p(const void *ptr)
-{
- uint32_t a,b;
- a = ldl_be_p(ptr);
- b = ldl_be_p((uint8_t *)ptr + 4);
- return (((uint64_t)a<<32)|b);
-}
-
-static inline void stw_be_p(void *ptr, int v)
-{
-#if defined(__i386__)
- asm volatile ("xchgb %b0, %h0\n"
- "movw %w0, %1\n"
- : "=q" (v)
- : "m" (*(uint16_t *)ptr), "0" (v));
-#else
- uint8_t *d = (uint8_t *) ptr;
- d[0] = v >> 8;
- d[1] = v;
-#endif
-}
-
-static inline void stl_be_p(void *ptr, int v)
-{
-#if defined(__i386__) || defined(__x86_64__)
- asm volatile ("bswap %0\n"
- "movl %0, %1\n"
- : "=r" (v)
- : "m" (*(uint32_t *)ptr), "0" (v));
-#else
- uint8_t *d = (uint8_t *) ptr;
- d[0] = v >> 24;
- d[1] = v >> 16;
- d[2] = v >> 8;
- d[3] = v;
-#endif
-}
-
-static inline void stq_be_p(void *ptr, uint64_t v)
-{
- stl_be_p(ptr, v >> 32);
- stl_be_p((uint8_t *)ptr + 4, v);
-}
-
-/* float access */
-
-static inline float32 ldfl_be_p(const void *ptr)
-{
- union {
- float32 f;
- uint32_t i;
- } u;
- u.i = ldl_be_p(ptr);
- return u.f;
-}
-
-static inline void stfl_be_p(void *ptr, float32 v)
-{
- union {
- float32 f;
- uint32_t i;
- } u;
- u.f = v;
- stl_be_p(ptr, u.i);
-}
-
-static inline float64 ldfq_be_p(const void *ptr)
-{
- CPU_DoubleU u;
- u.l.upper = ldl_be_p(ptr);
- u.l.lower = ldl_be_p((uint8_t *)ptr + 4);
- return u.d;
-}
-
-static inline void stfq_be_p(void *ptr, float64 v)
-{
- CPU_DoubleU u;
- u.d = v;
- stl_be_p(ptr, u.l.upper);
- stl_be_p((uint8_t *)ptr + 4, u.l.lower);
-}
-
-#else
-
-static inline int lduw_be_p(const void *ptr)
-{
- return *(uint16_t *)ptr;
-}
-
-static inline int ldsw_be_p(const void *ptr)
-{
- return *(int16_t *)ptr;
-}
-
-static inline int ldl_be_p(const void *ptr)
-{
- return *(uint32_t *)ptr;
-}
-
-static inline uint64_t ldq_be_p(const void *ptr)
-{
- return *(uint64_t *)ptr;
-}
-
-static inline void stw_be_p(void *ptr, int v)
-{
- *(uint16_t *)ptr = v;
-}
-
-static inline void stl_be_p(void *ptr, int v)
-{
- *(uint32_t *)ptr = v;
-}
-
-static inline void stq_be_p(void *ptr, uint64_t v)
-{
- *(uint64_t *)ptr = v;
-}
-
-/* float access */
-
-static inline float32 ldfl_be_p(const void *ptr)
-{
- return *(float32 *)ptr;
-}
-
-static inline float64 ldfq_be_p(const void *ptr)
-{
- return *(float64 *)ptr;
-}
-
-static inline void stfl_be_p(void *ptr, float32 v)
-{
- *(float32 *)ptr = v;
-}
-
-static inline void stfq_be_p(void *ptr, float64 v)
-{
- *(float64 *)ptr = v;
-}
-
-#endif
-/* target CPU memory access functions */
+/* target-endianness CPU memory access functions */
#if defined(TARGET_WORDS_BIGENDIAN)
#define lduw_p(p) lduw_be_p(p)
#define ldsw_p(p) ldsw_be_p(p)
diff --git a/cpu-common.h b/cpu-common.h
index c6a2b5f..c9878ba 100644
--- a/cpu-common.h
+++ b/cpu-common.h
@@ -3,10 +3,6 @@
/* CPU interfaces that are target indpendent. */
-#if defined(__arm__) || defined(__sparc__) || defined(__mips__) || defined(__hppa__) || defined(__ia64__)
-#define WORDS_ALIGNED
-#endif
-
#ifdef TARGET_PHYS_ADDR_BITS
#include "targphys.h"
#endif
@@ -27,7 +23,15 @@ enum device_endian {
};
/* address in the RAM (different from a physical address) */
+#if defined(CONFIG_XEN_BACKEND) && TARGET_PHYS_ADDR_BITS == 64
+typedef uint64_t ram_addr_t;
+# define RAM_ADDR_MAX UINT64_MAX
+# define RAM_ADDR_FMT "%" PRIx64
+#else
typedef unsigned long ram_addr_t;
+# define RAM_ADDR_MAX ULONG_MAX
+# define RAM_ADDR_FMT "%lx"
+#endif
/* memory API */
@@ -65,7 +69,7 @@ void qemu_ram_free_from_ptr(ram_addr_t addr);
void qemu_ram_remap(ram_addr_t addr, ram_addr_t length);
/* This should only be used for ram local to a device. */
void *qemu_get_ram_ptr(ram_addr_t addr);
-void *qemu_ram_ptr_length(target_phys_addr_t addr, target_phys_addr_t *size);
+void *qemu_ram_ptr_length(ram_addr_t addr, ram_addr_t *size);
/* Same but slower, to use for migration, where the order of
* RAMBlocks must not change. */
void *qemu_safe_ram_ptr(ram_addr_t addr);
@@ -134,28 +138,31 @@ void qemu_unregister_coalesced_mmio(target_phys_addr_t addr, ram_addr_t size);
void qemu_flush_coalesced_mmio_buffer(void);
uint32_t ldub_phys(target_phys_addr_t addr);
-uint32_t lduw_phys(target_phys_addr_t addr);
uint32_t lduw_le_phys(target_phys_addr_t addr);
uint32_t lduw_be_phys(target_phys_addr_t addr);
-uint32_t ldl_phys(target_phys_addr_t addr);
uint32_t ldl_le_phys(target_phys_addr_t addr);
uint32_t ldl_be_phys(target_phys_addr_t addr);
-uint64_t ldq_phys(target_phys_addr_t addr);
uint64_t ldq_le_phys(target_phys_addr_t addr);
uint64_t ldq_be_phys(target_phys_addr_t addr);
-void stl_phys_notdirty(target_phys_addr_t addr, uint32_t val);
-void stq_phys_notdirty(target_phys_addr_t addr, uint64_t val);
void stb_phys(target_phys_addr_t addr, uint32_t val);
-void stw_phys(target_phys_addr_t addr, uint32_t val);
void stw_le_phys(target_phys_addr_t addr, uint32_t val);
void stw_be_phys(target_phys_addr_t addr, uint32_t val);
-void stl_phys(target_phys_addr_t addr, uint32_t val);
void stl_le_phys(target_phys_addr_t addr, uint32_t val);
void stl_be_phys(target_phys_addr_t addr, uint32_t val);
-void stq_phys(target_phys_addr_t addr, uint64_t val);
void stq_le_phys(target_phys_addr_t addr, uint64_t val);
void stq_be_phys(target_phys_addr_t addr, uint64_t val);
+#ifdef NEED_CPU_H
+uint32_t lduw_phys(target_phys_addr_t addr);
+uint32_t ldl_phys(target_phys_addr_t addr);
+uint64_t ldq_phys(target_phys_addr_t addr);
+void stl_phys_notdirty(target_phys_addr_t addr, uint32_t val);
+void stq_phys_notdirty(target_phys_addr_t addr, uint64_t val);
+void stw_phys(target_phys_addr_t addr, uint32_t val);
+void stl_phys(target_phys_addr_t addr, uint32_t val);
+void stq_phys(target_phys_addr_t addr, uint64_t val);
+#endif
+
void cpu_physical_memory_write_rom(target_phys_addr_t addr,
const uint8_t *buf, int len);
diff --git a/cpus.c b/cpus.c
index 3035314..6bf4e3f 100644
--- a/cpus.c
+++ b/cpus.c
@@ -636,7 +636,8 @@ void vm_stop(int reason)
#else /* CONFIG_IOTHREAD */
QemuMutex qemu_global_mutex;
-static QemuMutex qemu_fair_mutex;
+static QemuCond qemu_io_proceeded_cond;
+static bool iothread_requesting_mutex;
static QemuThread io_thread;
@@ -672,7 +673,7 @@ int qemu_init_main_loop(void)
qemu_cond_init(&qemu_system_cond);
qemu_cond_init(&qemu_pause_cond);
qemu_cond_init(&qemu_work_cond);
- qemu_mutex_init(&qemu_fair_mutex);
+ qemu_cond_init(&qemu_io_proceeded_cond);
qemu_mutex_init(&qemu_global_mutex);
qemu_mutex_lock(&qemu_global_mutex);
@@ -755,17 +756,9 @@ static void qemu_tcg_wait_io_event(void)
qemu_cond_wait(tcg_halt_cond, &qemu_global_mutex);
}
- qemu_mutex_unlock(&qemu_global_mutex);
-
- /*
- * Users of qemu_global_mutex can be starved, having no chance
- * to acquire it since this path will get to it first.
- * So use another lock to provide fairness.
- */
- qemu_mutex_lock(&qemu_fair_mutex);
- qemu_mutex_unlock(&qemu_fair_mutex);
-
- qemu_mutex_lock(&qemu_global_mutex);
+ while (iothread_requesting_mutex) {
+ qemu_cond_wait(&qemu_io_proceeded_cond, &qemu_global_mutex);
+ }
for (env = first_cpu; env != NULL; env = env->next_cpu) {
qemu_wait_io_event_common(env);
@@ -908,12 +901,13 @@ void qemu_mutex_lock_iothread(void)
if (kvm_enabled()) {
qemu_mutex_lock(&qemu_global_mutex);
} else {
- qemu_mutex_lock(&qemu_fair_mutex);
+ iothread_requesting_mutex = true;
if (qemu_mutex_trylock(&qemu_global_mutex)) {
qemu_cpu_kick_thread(first_cpu);
qemu_mutex_lock(&qemu_global_mutex);
}
- qemu_mutex_unlock(&qemu_fair_mutex);
+ iothread_requesting_mutex = false;
+ qemu_cond_broadcast(&qemu_io_proceeded_cond);
}
}
diff --git a/darwin-user/main.c b/darwin-user/main.c
index 35196a1..72307ad 100644
--- a/darwin-user/main.c
+++ b/darwin-user/main.c
@@ -809,9 +809,6 @@ int main(int argc, char **argv)
usage();
}
}
- if (optind >= argc)
- usage();
- filename = argv[optind];
/* init debug */
cpu_set_log_filename(log_file);
@@ -830,6 +827,11 @@ int main(int argc, char **argv)
cpu_set_log(mask);
}
+ if (optind >= argc) {
+ usage();
+ }
+ filename = argv[optind];
+
/* Zero out regs */
memset(regs, 0, sizeof(struct target_pt_regs));
diff --git a/dma.h b/dma.h
index 3d8324b..a6db5ba 100644
--- a/dma.h
+++ b/dma.h
@@ -20,12 +20,12 @@ typedef struct {
target_phys_addr_t len;
} ScatterGatherEntry;
-typedef struct {
+struct QEMUSGList {
ScatterGatherEntry *sg;
int nsg;
int nalloc;
target_phys_addr_t size;
-} QEMUSGList;
+};
void qemu_sglist_init(QEMUSGList *qsg, int alloc_hint);
void qemu_sglist_add(QEMUSGList *qsg, target_phys_addr_t base,
diff --git a/docs/ich9-ehci-uhci.cfg b/docs/ich9-ehci-uhci.cfg
new file mode 100644
index 0000000..a0e9b96
--- /dev/null
+++ b/docs/ich9-ehci-uhci.cfg
@@ -0,0 +1,37 @@
+###########################################################################
+#
+# You can pass this file directly to qemu using the -readconfig
+# command line switch.
+#
+# This config file creates a EHCI adapter with companion UHCI
+# controllers as multifunction device in PCI slot "1d".
+#
+# Specify "bus=ehci.0" when creating usb devices to hook them up
+# there.
+#
+
+[device "ehci"]
+ driver = "ich9-usb-ehci1"
+ addr = "1d.7"
+ multifunction = "on"
+
+[device "uhci-1"]
+ driver = "ich9-usb-uhci1"
+ addr = "1d.0"
+ multifunction = "on"
+ masterbus = "ehci.0"
+ firstport = "0"
+
+[device "uhci-2"]
+ driver = "ich9-usb-uhci2"
+ addr = "1d.1"
+ multifunction = "on"
+ masterbus = "ehci.0"
+ firstport = "2"
+
+[device "uhci-3"]
+ driver = "ich9-usb-uhci3"
+ addr = "1d.2"
+ multifunction = "on"
+ masterbus = "ehci.0"
+ firstport = "4"
diff --git a/docs/memory.txt b/docs/memory.txt
new file mode 100644
index 0000000..4460c06
--- /dev/null
+++ b/docs/memory.txt
@@ -0,0 +1,172 @@
+The memory API
+==============
+
+The memory API models the memory and I/O buses and controllers of a QEMU
+machine. It attempts to allow modelling of:
+
+ - ordinary RAM
+ - memory-mapped I/O (MMIO)
+ - memory controllers that can dynamically reroute physical memory regions
+ to different destinations
+
+The memory model provides support for
+
+ - tracking RAM changes by the guest
+ - setting up coalesced memory for kvm
+ - setting up ioeventfd regions for kvm
+
+Memory is modelled as an tree (really acyclic graph) of MemoryRegion objects.
+The root of the tree is memory as seen from the CPU's viewpoint (the system
+bus). Nodes in the tree represent other buses, memory controllers, and
+memory regions that have been rerouted. Leaves are RAM and MMIO regions.
+
+Types of regions
+----------------
+
+There are four types of memory regions (all represented by a single C type
+MemoryRegion):
+
+- RAM: a RAM region is simply a range of host memory that can be made available
+ to the guest.
+
+- MMIO: a range of guest memory that is implemented by host callbacks;
+ each read or write causes a callback to be called on the host.
+
+- container: a container simply includes other memory regions, each at
+ a different offset. Containers are useful for grouping several regions
+ into one unit. For example, a PCI BAR may be composed of a RAM region
+ and an MMIO region.
+
+ A container's subregions are usually non-overlapping. In some cases it is
+ useful to have overlapping regions; for example a memory controller that
+ can overlay a subregion of RAM with MMIO or ROM, or a PCI controller
+ that does not prevent card from claiming overlapping BARs.
+
+- alias: a subsection of another region. Aliases allow a region to be
+ split apart into discontiguous regions. Examples of uses are memory banks
+ used when the guest address space is smaller than the amount of RAM
+ addressed, or a memory controller that splits main memory to expose a "PCI
+ hole". Aliases may point to any type of region, including other aliases,
+ but an alias may not point back to itself, directly or indirectly.
+
+
+Region names
+------------
+
+Regions are assigned names by the constructor. For most regions these are
+only used for debugging purposes, but RAM regions also use the name to identify
+live migration sections. This means that RAM region names need to have ABI
+stability.
+
+Region lifecycle
+----------------
+
+A region is created by one of the constructor functions (memory_region_init*())
+and destroyed by the destructor (memory_region_destroy()). In between,
+a region can be added to an address space by using memory_region_add_subregion()
+and removed using memory_region_del_subregion(). Region attributes may be
+changed at any point; they take effect once the region becomes exposed to the
+guest.
+
+Overlapping regions and priority
+--------------------------------
+Usually, regions may not overlap each other; a memory address decodes into
+exactly one target. In some cases it is useful to allow regions to overlap,
+and sometimes to control which of an overlapping regions is visible to the
+guest. This is done with memory_region_add_subregion_overlap(), which
+allows the region to overlap any other region in the same container, and
+specifies a priority that allows the core to decide which of two regions at
+the same address are visible (highest wins).
+
+Visibility
+----------
+The memory core uses the following rules to select a memory region when the
+guest accesses an address:
+
+- all direct subregions of the root region are matched against the address, in
+ descending priority order
+ - if the address lies outside the region offset/size, the subregion is
+ discarded
+ - if the subregion is a leaf (RAM or MMIO), the seach terminates
+ - if the subregion is a container, the same algorithm is used within the
+ subregion (after the address is adjusted by the subregion offset)
+ - if the subregion is an alias, the search is continues at the alias target
+ (after the address is adjusted by the subregion offset and alias offset)
+
+Example memory map
+------------------
+
+system_memory: container@0-2^48-1
+ |
+ +---- lomem: alias@0-0xdfffffff ---> #ram (0-0xdfffffff)
+ |
+ +---- himem: alias@0x100000000-0x11fffffff ---> #ram (0xe0000000-0xffffffff)
+ |
+ +---- vga-window: alias@0xa0000-0xbfffff ---> #pci (0xa0000-0xbffff)
+ | (prio 1)
+ |
+ +---- pci-hole: alias@0xe0000000-0xffffffff ---> #pci (0xe0000000-0xffffffff)
+
+pci (0-2^32-1)
+ |
+ +--- vga-area: container@0xa0000-0xbffff
+ | |
+ | +--- alias@0x00000-0x7fff ---> #vram (0x010000-0x017fff)
+ | |
+ | +--- alias@0x08000-0xffff ---> #vram (0x020000-0x027fff)
+ |
+ +---- vram: ram@0xe1000000-0xe1ffffff
+ |
+ +---- vga-mmio: mmio@0xe2000000-0xe200ffff
+
+ram: ram@0x00000000-0xffffffff
+
+The is a (simplified) PC memory map. The 4GB RAM block is mapped into the
+system address space via two aliases: "lomem" is a 1:1 mapping of the first
+3.5GB; "himem" maps the last 0.5GB at address 4GB. This leaves 0.5GB for the
+so-called PCI hole, that allows a 32-bit PCI bus to exist in a system with
+4GB of memory.
+
+The memory controller diverts addresses in the range 640K-768K to the PCI
+address space. This is modeled using the "vga-window" alias, mapped at a
+higher priority so it obscures the RAM at the same addresses. The vga window
+can be removed by programming the memory controller; this is modelled by
+removing the alias and exposing the RAM underneath.
+
+The pci address space is not a direct child of the system address space, since
+we only want parts of it to be visible (we accomplish this using aliases).
+It has two subregions: vga-area models the legacy vga window and is occupied
+by two 32K memory banks pointing at two sections of the framebuffer.
+In addition the vram is mapped as a BAR at address e1000000, and an additional
+BAR containing MMIO registers is mapped after it.
+
+Note that if the guest maps a BAR outside the PCI hole, it would not be
+visible as the pci-hole alias clips it to a 0.5GB range.
+
+Attributes
+----------
+
+Various region attributes (read-only, dirty logging, coalesced mmio, ioeventfd)
+can be changed during the region lifecycle. They take effect once the region
+is made visible (which can be immediately, later, or never).
+
+MMIO Operations
+---------------
+
+MMIO regions are provided with ->read() and ->write() callbacks; in addition
+various constraints can be supplied to control how these callbacks are called:
+
+ - .valid.min_access_size, .valid.max_access_size define the access sizes
+ (in bytes) which the device accepts; accesses outside this range will
+ have device and bus specific behaviour (ignored, or machine check)
+ - .valid.aligned specifies that the device only accepts naturally aligned
+ accesses. Unaligned accesses invoke device and bus specific behaviour.
+ - .impl.min_access_size, .impl.max_access_size define the access sizes
+ (in bytes) supported by the *implementation*; other access sizes will be
+ emulated using the ones available. For example a 4-byte write will be
+ emulated using four 1-byte write, is .impl.max_access_size = 1.
+ - .impl.valid specifies that the *implementation* only supports unaligned
+ accesses; unaligned accesses will be emulated by two aligned accesses.
+ - .old_portio and .old_mmio can be used to ease porting from code using
+ cpu_register_io_memory() and register_ioport(). They should not be used
+ in new code.
diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt
new file mode 100644
index 0000000..b7befb5
--- /dev/null
+++ b/docs/qapi-code-gen.txt
@@ -0,0 +1,316 @@
+= How to use the QAPI code generator =
+
+* Note: as of this writing, QMP does not use QAPI. Eventually QMP
+commands will be converted to use QAPI internally. The following
+information describes QMP/QAPI as it will exist after the
+conversion.
+
+QAPI is a native C API within QEMU which provides management-level
+functionality to internal/external users. For external
+users/processes, this interface is made available by a JSON-based
+QEMU Monitor protocol that is provided by the QMP server.
+
+To map QMP-defined interfaces to the native C QAPI implementations,
+a JSON-based schema is used to define types and function
+signatures, and a set of scripts is used to generate types/signatures,
+and marshaling/dispatch code. The QEMU Guest Agent also uses these
+scripts, paired with a seperate schema, to generate
+marshaling/dispatch code for the guest agent server running in the
+guest.
+
+This document will describe how the schemas, scripts, and resulting
+code is used.
+
+
+== QMP/Guest agent schema ==
+
+This file defines the types, commands, and events used by QMP. It should
+fully describe the interface used by QMP.
+
+This file is designed to be loosely based on JSON although it's technically
+executable Python. While dictionaries are used, they are parsed as
+OrderedDicts so that ordering is preserved.
+
+There are two basic syntaxes used, type definitions and command definitions.
+
+The first syntax defines a type and is represented by a dictionary. There are
+two kinds of types that are supported: complex user-defined types, and enums.
+
+A complex type is a dictionary containing a single key who's value is a
+dictionary. This corresponds to a struct in C or an Object in JSON. An
+example of a complex type is:
+
+ { 'type': 'MyType',
+ 'data' { 'member1': 'str', 'member2': 'int', '*member3': 'str } }
+
+The use of '*' as a prefix to the name means the member is optional. Optional
+members should always be added to the end of the dictionary to preserve
+backwards compatibility.
+
+An enumeration type is a dictionary containing a single key who's value is a
+list of strings. An example enumeration is:
+
+ { 'enum': 'MyEnum', 'data': [ 'value1', 'value2', 'value3' ] }
+
+Generally speaking, complex types and enums should always use CamelCase for
+the type names.
+
+Commands are defined by using a list containing three members. The first
+member is the command name, the second member is a dictionary containing
+arguments, and the third member is the return type.
+
+An example command is:
+
+ { 'command': 'my-command',
+ 'data': { 'arg1': 'str', '*arg2': 'str' },
+ 'returns': 'str' ]
+
+Command names should be all lower case with words separated by a hyphen.
+
+
+== Code generation ==
+
+Schemas are fed into 3 scripts to generate all the code/files that, paired
+with the core QAPI libraries, comprise everything required to take JSON
+commands read in by a QMP/guest agent server, unmarshal the arguments into
+the underlying C types, call into the corresponding C function, and map the
+response back to a QMP/guest agent response to be returned to the user.
+
+As an example, we'll use the following schema, which describes a single
+complex user-defined type (which will produce a C struct, along with a list
+node structure that can be used to chain together a list of such types in
+case we want to accept/return a list of this type with a command), and a
+command which takes that type as a parameter and returns the same type:
+
+ mdroth@illuin:~/w/qemu2.git$ cat example-schema.json
+ { 'type': 'UserDefOne',
+ 'data': { 'integer': 'int', 'string': 'str' } }
+
+ { 'command': 'my-command',
+ 'data': {'arg1': 'UserDefOne'},
+ 'returns': 'UserDefOne' }
+ mdroth@illuin:~/w/qemu2.git$
+
+=== scripts/qapi-types.py ===
+
+Used to generate the C types defined by a schema. The following files are
+created:
+
+$(prefix)qapi-types.h - C types corresponding to types defined in
+ the schema you pass in
+$(prefix)qapi-types.c - Cleanup functions for the above C types
+
+The $(prefix) is an optional parameter used as a namespace to keep the
+generated code from one schema/code-generation separated from others so code
+can be generated/used from multiple schemas without clobbering previously
+created code.
+
+Example:
+
+ mdroth@illuin:~/w/qemu2.git$ python scripts/qapi-types.py \
+ --output-dir="qapi-generated" --prefix="example-" < example-schema.json
+ mdroth@illuin:~/w/qemu2.git$ cat qapi-generated/example-qapi-types.c
+ /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
+
+ #include "qapi/qapi-dealloc-visitor.h"
+ #include "example-qapi-types.h"
+ #include "example-qapi-visit.h"
+
+ void qapi_free_UserDefOne(UserDefOne * obj)
+ {
+ QapiDeallocVisitor *md;
+ Visitor *v;
+
+ if (!obj) {
+ return;
+ }
+
+ md = qapi_dealloc_visitor_new();
+ v = qapi_dealloc_get_visitor(md);
+ visit_type_UserDefOne(v, &obj, NULL, NULL);
+ qapi_dealloc_visitor_cleanup(md);
+ }
+
+ mdroth@illuin:~/w/qemu2.git$ cat qapi-generated/example-qapi-types.h
+ /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
+ #ifndef QAPI_GENERATED_EXAMPLE_QAPI_TYPES
+ #define QAPI_GENERATED_EXAMPLE_QAPI_TYPES
+
+ #include "qapi/qapi-types-core.h"
+
+ typedef struct UserDefOne UserDefOne;
+
+ typedef struct UserDefOneList
+ {
+ UserDefOne *value;
+ struct UserDefOneList *next;
+ } UserDefOneList;
+
+ struct UserDefOne
+ {
+ int64_t integer;
+ char * string;
+ };
+
+ void qapi_free_UserDefOne(UserDefOne * obj);
+
+ #endif
+
+
+=== scripts/qapi-visit.py ===
+
+Used to generate the visitor functions used to walk through and convert
+a QObject (as provided by QMP) to a native C data structure and
+vice-versa, as well as the visitor function used to dealloc a complex
+schema-defined C type.
+
+The following files are generated:
+
+$(prefix)qapi-visit.c: visitor function for a particular C type, used
+ to automagically convert QObjects into the
+ corresponding C type and vice-versa, as well
+ as for deallocating memory for an existing C
+ type
+
+$(prefix)qapi-visit.h: declarations for previously mentioned visitor
+ functions
+
+Example:
+
+ mdroth@illuin:~/w/qemu2.git$ python scripts/qapi-visit.py \
+ --output-dir="qapi-generated" --prefix="example-" < example-schema.json
+ mdroth@illuin:~/w/qemu2.git$ cat qapi-generated/example-qapi-visit.c
+ /* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */
+
+ #include "example-qapi-visit.h"
+
+ void visit_type_UserDefOne(Visitor *m, UserDefOne ** obj, const char *name, Error **errp)
+ {
+ visit_start_struct(m, (void **)obj, "UserDefOne", name, sizeof(UserDefOne), errp);
+ visit_type_int(m, (obj && *obj) ? &(*obj)->integer : NULL, "integer", errp);
+ visit_type_str(m, (obj && *obj) ? &(*obj)->string : NULL, "string", errp);
+ visit_end_struct(m, errp);
+ }
+
+ void visit_type_UserDefOneList(Visitor *m, UserDefOneList ** obj, const char *name, Error **errp)
+ {
+ GenericList *i;
+
+ visit_start_list(m, name, errp);
+
+ for (i = visit_next_list(m, (GenericList **)obj, errp); i; i = visit_next_list(m, &i, errp)) {
+ UserDefOneList *native_i = (UserDefOneList *)i;
+ visit_type_UserDefOne(m, &native_i->value, NULL, errp);
+ }
+
+ visit_end_list(m, errp);
+ }
+ mdroth@illuin:~/w/qemu2.git$ cat qapi-generated/example-qapi-visit.h
+ /* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */
+
+ #ifndef QAPI_GENERATED_EXAMPLE_QAPI_VISIT
+ #define QAPI_GENERATED_EXAMPLE_QAPI_VISIT
+
+ #include "qapi/qapi-visit-core.h"
+ #include "example-qapi-types.h"
+
+ void visit_type_UserDefOne(Visitor *m, UserDefOne ** obj, const char *name, Error **errp);
+ void visit_type_UserDefOneList(Visitor *m, UserDefOneList ** obj, const char *name, Error **errp);
+
+ #endif
+ mdroth@illuin:~/w/qemu2.git$
+
+
+=== scripts/qapi-commands.py ===
+
+Used to generate the marshaling/dispatch functions for the commands defined
+in the schema. The following files are generated:
+
+$(prefix)qmp-marshal.c: command marshal/dispatch functions for each
+ QMP command defined in the schema. Functions
+ generated by qapi-visit.py are used to
+ convert QObjects recieved from the wire into
+ function parameters, and uses the same
+ visitor functions to convert native C return
+ values to QObjects from transmission back
+ over the wire.
+
+$(prefix)qmp-commands.h: Function prototypes for the QMP commands
+ specified in the schema.
+
+Example:
+
+ mdroth@illuin:~/w/qemu2.git$ cat qapi-generated/example-qmp-marshal.c
+ /* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */
+
+ #include "qemu-objects.h"
+ #include "qapi/qmp-core.h"
+ #include "qapi/qapi-visit-core.h"
+ #include "qapi/qmp-output-visitor.h"
+ #include "qapi/qmp-input-visitor.h"
+ #include "qapi/qapi-dealloc-visitor.h"
+ #include "example-qapi-types.h"
+ #include "example-qapi-visit.h"
+
+ #include "example-qmp-commands.h"
+ static void qmp_marshal_output_my_command(UserDefOne * ret_in, QObject **ret_out, Error **errp)
+ {
+ QapiDeallocVisitor *md = qapi_dealloc_visitor_new();
+ QmpOutputVisitor *mo = qmp_output_visitor_new();
+ Visitor *v;
+
+ v = qmp_output_get_visitor(mo);
+ visit_type_UserDefOne(v, &ret_in, "unused", errp);
+ v = qapi_dealloc_get_visitor(md);
+ visit_type_UserDefOne(v, &ret_in, "unused", errp);
+ qapi_dealloc_visitor_cleanup(md);
+
+
+ *ret_out = qmp_output_get_qobject(mo);
+ }
+
+ static void qmp_marshal_input_my_command(QmpState *qmp__sess, QDict *args, QObject **ret, Error **errp)
+ {
+ UserDefOne * retval = NULL;
+ QmpInputVisitor *mi;
+ QapiDeallocVisitor *md;
+ Visitor *v;
+ UserDefOne * arg1 = NULL;
+
+ mi = qmp_input_visitor_new(QOBJECT(args));
+ v = qmp_input_get_visitor(mi);
+ visit_type_UserDefOne(v, &arg1, "arg1", errp);
+
+ if (error_is_set(errp)) {
+ goto out;
+ }
+ retval = qmp_my_command(arg1, errp);
+ qmp_marshal_output_my_command(retval, ret, errp);
+
+ out:
+ md = qapi_dealloc_visitor_new();
+ v = qapi_dealloc_get_visitor(md);
+ visit_type_UserDefOne(v, &arg1, "arg1", errp);
+ qapi_dealloc_visitor_cleanup(md);
+ return;
+ }
+
+ static void qmp_init_marshal(void)
+ {
+ qmp_register_command("my-command", qmp_marshal_input_my_command);
+ }
+
+ qapi_init(qmp_init_marshal);
+ mdroth@illuin:~/w/qemu2.git$ cat qapi-generated/example-qmp-commands.h
+ /* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */
+
+ #ifndef QAPI_GENERATED_EXAMPLE_QMP_COMMANDS
+ #define QAPI_GENERATED_EXAMPLE_QMP_COMMANDS
+
+ #include "example-qapi-types.h"
+ #include "error.h"
+
+ UserDefOne * qmp_my_command(UserDefOne * arg1, Error **errp);
+
+ #endif
+ mdroth@illuin:~/w/qemu2.git$
diff --git a/docs/usb2.txt b/docs/usb2.txt
index 5950c71..228aa33 100644
--- a/docs/usb2.txt
+++ b/docs/usb2.txt
@@ -2,11 +2,13 @@
USB 2.0 Quick Start
===================
-The QEMU EHCI Adapter does *not* support companion controllers. That
-implies there are two completely separate USB busses: One USB 1.1 bus
-driven by the UHCI controller and one USB 2.0 bus driven by the EHCI
-controller. Devices must be attached to the correct controller
-manually.
+The QEMU EHCI Adapter can be used with and without companion
+controllers. See below for the companion controller mode.
+
+When not running in companion controller mode there are two completely
+separate USB busses: One USB 1.1 bus driven by the UHCI controller and
+one USB 2.0 bus driven by the EHCI controller. Devices must be
+attached to the correct controller manually.
The '-usb' switch will make qemu create the UHCI controller as part of
the PIIX3 chipset. The USB 1.1 bus will carry the name "usb.0".
@@ -32,6 +34,27 @@ This attaches a usb tablet to the UHCI adapter and a usb mass storage
device to the EHCI adapter.
+Companion controller support
+----------------------------
+
+Companion controller support has been added recently. The operational
+model described above with two completely separate busses still works
+fine. Additionally the UHCI and OHCI controllers got the ability to
+attach to a usb bus created by EHCI as companion controllers. This is
+done by specifying the masterbus and firstport properties. masterbus
+specifies the bus name the controller should attach to. firstport
+specifies the first port the controller should attach to, which is
+needed as usually one ehci controller with six ports has three uhci
+companion controllers with two ports each.
+
+There is a config file in docs which will do all this for you, just
+try ...
+
+ qemu -readconfig docs/ich9-ehci-uhci.cfg
+
+... then use "bus=ehci.0" to assign your usb devices to that bus.
+
+
More USB tips & tricks
======================
diff --git a/dyngen-exec.h b/dyngen-exec.h
index db00fba..cc1e4fb 100644
--- a/dyngen-exec.h
+++ b/dyngen-exec.h
@@ -64,6 +64,8 @@ typedef void * host_reg_t;
#error unsupported CPU
#endif
+register CPUState *env asm(AREG0);
+
#define xglue(x, y) x ## y
#define glue(x, y) xglue(x, y)
#define stringify(s) tostring(s)
diff --git a/error.h b/error.h
index 0f92a6f..6361f40 100644
--- a/error.h
+++ b/error.h
@@ -12,6 +12,7 @@
#ifndef ERROR_H
#define ERROR_H
+#include "compiler.h"
#include <stdbool.h>
/**
diff --git a/exec-all.h b/exec-all.h
index 69acf3b..9b8d62c 100644
--- a/exec-all.h
+++ b/exec-all.h
@@ -323,7 +323,7 @@ static inline tb_page_addr_t get_page_addr_code(CPUState *env1, target_ulong add
pd = env1->tlb_table[mmu_idx][page_index].addr_code & ~TARGET_PAGE_MASK;
if (pd > IO_MEM_ROM && !(pd & IO_MEM_ROMD)) {
#if defined(TARGET_ALPHA) || defined(TARGET_MIPS) || defined(TARGET_SPARC)
- do_unassigned_access(addr, 0, 1, 0, 4);
+ cpu_unassigned_access(env1, addr, 0, 1, 0, 4);
#else
cpu_abort(env1, "Trying to execute code outside RAM or ROM at 0x" TARGET_FMT_lx "\n", addr);
#endif
diff --git a/exec-memory.h b/exec-memory.h
new file mode 100644
index 0000000..c439aba
--- /dev/null
+++ b/exec-memory.h
@@ -0,0 +1,39 @@
+/*
+ * Internal memory managment interfaces
+ *
+ * Copyright 2011 Red Hat, Inc. and/or its affiliates
+ *
+ * Authors:
+ * Avi Kivity <avi@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#ifndef EXEC_MEMORY_H
+#define EXEC_MEMORY_H
+
+/*
+ * Internal interfaces between memory.c/exec.c/vl.c. Do not #include unless
+ * you're one of them.
+ */
+
+#include "memory.h"
+
+#ifndef CONFIG_USER_ONLY
+
+/* Get the root memory region. This interface should only be used temporarily
+ * until a proper bus interface is available.
+ */
+MemoryRegion *get_system_memory(void);
+
+/* Set the root memory region. This region is the system memory map. */
+void set_system_memory_map(MemoryRegion *mr);
+
+/* Set the I/O memory region. This region is the I/O memory map. */
+void set_system_io_map(MemoryRegion *mr);
+
+#endif
+
+#endif
diff --git a/exec.c b/exec.c
index f1777e6..476b507 100644
--- a/exec.c
+++ b/exec.c
@@ -33,6 +33,8 @@
#include "kvm.h"
#include "hw/xen.h"
#include "qemu-timer.h"
+#include "memory.h"
+#include "exec-memory.h"
#if defined(CONFIG_USER_ONLY)
#include <qemu.h>
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
@@ -109,6 +111,9 @@ int phys_ram_fd;
static int in_migration;
RAMList ram_list = { .blocks = QLIST_HEAD_INITIALIZER(ram_list) };
+
+static MemoryRegion *system_memory;
+
#endif
CPUState *first_cpu;
@@ -197,6 +202,7 @@ typedef struct PhysPageDesc {
static void *l1_phys_map[P_L1_SIZE];
static void io_mem_init(void);
+static void memory_map_init(void);
/* io memory support */
CPUWriteMemoryFunc *io_mem_write[IO_MEM_NB_ENTRIES][4];
@@ -571,6 +577,7 @@ void cpu_exec_init_all(unsigned long tb_size)
code_gen_ptr = code_gen_buffer;
page_init();
#if !defined(CONFIG_USER_ONLY)
+ memory_map_init();
io_mem_init();
#endif
#if !defined(CONFIG_USER_ONLY) || !defined(CONFIG_USE_GUEST_BASE)
@@ -2863,13 +2870,13 @@ static void *file_ram_alloc(RAMBlock *block,
static ram_addr_t find_ram_offset(ram_addr_t size)
{
RAMBlock *block, *next_block;
- ram_addr_t offset = 0, mingap = ULONG_MAX;
+ ram_addr_t offset = 0, mingap = RAM_ADDR_MAX;
if (QLIST_EMPTY(&ram_list.blocks))
return 0;
QLIST_FOREACH(block, &ram_list.blocks, next) {
- ram_addr_t end, next = ULONG_MAX;
+ ram_addr_t end, next = RAM_ADDR_MAX;
end = block->offset + block->length;
@@ -2953,7 +2960,7 @@ ram_addr_t qemu_ram_alloc_from_ptr(DeviceState *dev, const char *name,
abort();
}
#else
- if (xen_mapcache_enabled()) {
+ if (xen_enabled()) {
xen_ram_alloc(new_block->offset, size);
} else {
new_block->host = qemu_vmalloc(size);
@@ -3019,8 +3026,8 @@ void qemu_ram_free(ram_addr_t addr)
#if defined(TARGET_S390X) && defined(CONFIG_KVM)
munmap(block->host, block->length);
#else
- if (xen_mapcache_enabled()) {
- qemu_invalidate_entry(block->host);
+ if (xen_enabled()) {
+ xen_invalidate_map_cache_entry(block->host);
} else {
qemu_vfree(block->host);
}
@@ -3081,7 +3088,8 @@ void qemu_ram_remap(ram_addr_t addr, ram_addr_t length)
#endif
}
if (area != vaddr) {
- fprintf(stderr, "Could not remap addr: %lx@%lx\n",
+ fprintf(stderr, "Could not remap addr: "
+ RAM_ADDR_FMT "@" RAM_ADDR_FMT "\n",
length, addr);
exit(1);
}
@@ -3112,15 +3120,16 @@ void *qemu_get_ram_ptr(ram_addr_t addr)
QLIST_REMOVE(block, next);
QLIST_INSERT_HEAD(&ram_list.blocks, block, next);
}
- if (xen_mapcache_enabled()) {
+ if (xen_enabled()) {
/* We need to check if the requested address is in the RAM
* because we don't want to map the entire memory in QEMU.
* In that case just map until the end of the page.
*/
if (block->offset == 0) {
- return qemu_map_cache(addr, 0, 0);
+ return xen_map_cache(addr, 0, 0);
} else if (block->host == NULL) {
- block->host = qemu_map_cache(block->offset, block->length, 1);
+ block->host =
+ xen_map_cache(block->offset, block->length, 1);
}
}
return block->host + (addr - block->offset);
@@ -3142,15 +3151,16 @@ void *qemu_safe_ram_ptr(ram_addr_t addr)
QLIST_FOREACH(block, &ram_list.blocks, next) {
if (addr - block->offset < block->length) {
- if (xen_mapcache_enabled()) {
+ if (xen_enabled()) {
/* We need to check if the requested address is in the RAM
* because we don't want to map the entire memory in QEMU.
* In that case just map until the end of the page.
*/
if (block->offset == 0) {
- return qemu_map_cache(addr, 0, 0);
+ return xen_map_cache(addr, 0, 0);
} else if (block->host == NULL) {
- block->host = qemu_map_cache(block->offset, block->length, 1);
+ block->host =
+ xen_map_cache(block->offset, block->length, 1);
}
}
return block->host + (addr - block->offset);
@@ -3165,11 +3175,14 @@ void *qemu_safe_ram_ptr(ram_addr_t addr)
/* Return a host pointer to guest's ram. Similar to qemu_get_ram_ptr
* but takes a size argument */
-void *qemu_ram_ptr_length(target_phys_addr_t addr, target_phys_addr_t *size)
+void *qemu_ram_ptr_length(ram_addr_t addr, ram_addr_t *size)
{
- if (xen_mapcache_enabled())
- return qemu_map_cache(addr, *size, 1);
- else {
+ if (*size == 0) {
+ return NULL;
+ }
+ if (xen_enabled()) {
+ return xen_map_cache(addr, *size, 1);
+ } else {
RAMBlock *block;
QLIST_FOREACH(block, &ram_list.blocks, next) {
@@ -3182,9 +3195,6 @@ void *qemu_ram_ptr_length(target_phys_addr_t addr, target_phys_addr_t *size)
fprintf(stderr, "Bad ram offset %" PRIx64 "\n", (uint64_t)addr);
abort();
-
- *size = 0;
- return NULL;
}
}
@@ -3198,8 +3208,8 @@ int qemu_ram_addr_from_host(void *ptr, ram_addr_t *ram_addr)
RAMBlock *block;
uint8_t *host = ptr;
- if (xen_mapcache_enabled()) {
- *ram_addr = qemu_ram_addr_from_mapcache(ptr);
+ if (xen_enabled()) {
+ *ram_addr = xen_ram_addr_from_mapcache(ptr);
return 0;
}
@@ -3236,7 +3246,7 @@ static uint32_t unassigned_mem_readb(void *opaque, target_phys_addr_t addr)
printf("Unassigned mem read " TARGET_FMT_plx "\n", addr);
#endif
#if defined(TARGET_ALPHA) || defined(TARGET_SPARC) || defined(TARGET_MICROBLAZE)
- do_unassigned_access(addr, 0, 0, 0, 1);
+ cpu_unassigned_access(cpu_single_env, addr, 0, 0, 0, 1);
#endif
return 0;
}
@@ -3247,7 +3257,7 @@ static uint32_t unassigned_mem_readw(void *opaque, target_phys_addr_t addr)
printf("Unassigned mem read " TARGET_FMT_plx "\n", addr);
#endif
#if defined(TARGET_ALPHA) || defined(TARGET_SPARC) || defined(TARGET_MICROBLAZE)
- do_unassigned_access(addr, 0, 0, 0, 2);
+ cpu_unassigned_access(cpu_single_env, addr, 0, 0, 0, 2);
#endif
return 0;
}
@@ -3258,7 +3268,7 @@ static uint32_t unassigned_mem_readl(void *opaque, target_phys_addr_t addr)
printf("Unassigned mem read " TARGET_FMT_plx "\n", addr);
#endif
#if defined(TARGET_ALPHA) || defined(TARGET_SPARC) || defined(TARGET_MICROBLAZE)
- do_unassigned_access(addr, 0, 0, 0, 4);
+ cpu_unassigned_access(cpu_single_env, addr, 0, 0, 0, 4);
#endif
return 0;
}
@@ -3269,7 +3279,7 @@ static void unassigned_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_
printf("Unassigned mem write " TARGET_FMT_plx " = 0x%x\n", addr, val);
#endif
#if defined(TARGET_ALPHA) || defined(TARGET_SPARC) || defined(TARGET_MICROBLAZE)
- do_unassigned_access(addr, 1, 0, 0, 1);
+ cpu_unassigned_access(cpu_single_env, addr, 1, 0, 0, 1);
#endif
}
@@ -3279,7 +3289,7 @@ static void unassigned_mem_writew(void *opaque, target_phys_addr_t addr, uint32_
printf("Unassigned mem write " TARGET_FMT_plx " = 0x%x\n", addr, val);
#endif
#if defined(TARGET_ALPHA) || defined(TARGET_SPARC) || defined(TARGET_MICROBLAZE)
- do_unassigned_access(addr, 1, 0, 0, 2);
+ cpu_unassigned_access(cpu_single_env, addr, 1, 0, 0, 2);
#endif
}
@@ -3289,7 +3299,7 @@ static void unassigned_mem_writel(void *opaque, target_phys_addr_t addr, uint32_
printf("Unassigned mem write " TARGET_FMT_plx " = 0x%x\n", addr, val);
#endif
#if defined(TARGET_ALPHA) || defined(TARGET_SPARC) || defined(TARGET_MICROBLAZE)
- do_unassigned_access(addr, 1, 0, 0, 4);
+ cpu_unassigned_access(cpu_single_env, addr, 1, 0, 0, 4);
#endif
}
@@ -3805,6 +3815,18 @@ static void io_mem_init(void)
DEVICE_NATIVE_ENDIAN);
}
+static void memory_map_init(void)
+{
+ system_memory = qemu_malloc(sizeof(*system_memory));
+ memory_region_init(system_memory, "system", UINT64_MAX);
+ set_system_memory_map(system_memory);
+}
+
+MemoryRegion *get_system_memory(void)
+{
+ return system_memory;
+}
+
#endif /* !defined(CONFIG_USER_ONLY) */
/* physical memory access (slow version, mainly for debug) */
@@ -3856,7 +3878,7 @@ void cpu_physical_memory_rw(target_phys_addr_t addr, uint8_t *buf,
uint8_t *ptr;
uint32_t val;
target_phys_addr_t page;
- unsigned long pd;
+ ram_addr_t pd;
PhysPageDesc *p;
while (len > 0) {
@@ -3896,7 +3918,7 @@ void cpu_physical_memory_rw(target_phys_addr_t addr, uint8_t *buf,
l = 1;
}
} else {
- unsigned long addr1;
+ ram_addr_t addr1;
addr1 = (pd & TARGET_PAGE_MASK) + (addr & ~TARGET_PAGE_MASK);
/* RAM case */
ptr = qemu_get_ram_ptr(addr1);
@@ -4050,7 +4072,9 @@ void *cpu_physical_memory_map(target_phys_addr_t addr,
target_phys_addr_t page;
unsigned long pd;
PhysPageDesc *p;
- target_phys_addr_t addr1 = addr;
+ ram_addr_t raddr = RAM_ADDR_MAX;
+ ram_addr_t rlen;
+ void *ret;
while (len > 0) {
page = addr & TARGET_PAGE_MASK;
@@ -4078,13 +4102,18 @@ void *cpu_physical_memory_map(target_phys_addr_t addr,
*plen = l;
return bounce.buffer;
}
+ if (!todo) {
+ raddr = (pd & TARGET_PAGE_MASK) + (addr & ~TARGET_PAGE_MASK);
+ }
len -= l;
addr += l;
todo += l;
}
- *plen = todo;
- return qemu_ram_ptr_length(addr1, plen);
+ rlen = todo;
+ ret = qemu_ram_ptr_length(raddr, &rlen);
+ *plen = rlen;
+ return ret;
}
/* Unmaps a memory region previously mapped by cpu_physical_memory_map().
@@ -4113,8 +4142,8 @@ void cpu_physical_memory_unmap(void *buffer, target_phys_addr_t len,
access_len -= l;
}
}
- if (xen_mapcache_enabled()) {
- qemu_invalidate_entry(buffer);
+ if (xen_enabled()) {
+ xen_invalidate_map_cache_entry(buffer);
}
return;
}
diff --git a/fpu/softfloat-specialize.h b/fpu/softfloat-specialize.h
index c7d35a1..c165205 100644
--- a/fpu/softfloat-specialize.h
+++ b/fpu/softfloat-specialize.h
@@ -35,6 +35,78 @@ these four paragraphs for those parts of this code that are retained.
=============================================================================*/
+#if defined(TARGET_MIPS) || defined(TARGET_SH4) || defined(TARGET_UNICORE32)
+#define SNAN_BIT_IS_ONE 1
+#else
+#define SNAN_BIT_IS_ONE 0
+#endif
+
+/*----------------------------------------------------------------------------
+| The pattern for a default generated half-precision NaN.
+*----------------------------------------------------------------------------*/
+#if defined(TARGET_ARM)
+const float16 float16_default_nan = const_float16(0x7E00);
+#elif SNAN_BIT_IS_ONE
+const float16 float16_default_nan = const_float16(0x7DFF);
+#else
+const float16 float16_default_nan = const_float16(0xFE00);
+#endif
+
+/*----------------------------------------------------------------------------
+| The pattern for a default generated single-precision NaN.
+*----------------------------------------------------------------------------*/
+#if defined(TARGET_SPARC)
+const float32 float32_default_nan = const_float32(0x7FFFFFFF);
+#elif defined(TARGET_PPC) || defined(TARGET_ARM) || defined(TARGET_ALPHA)
+const float32 float32_default_nan = const_float32(0x7FC00000);
+#elif SNAN_BIT_IS_ONE
+const float32 float32_default_nan = const_float32(0x7FBFFFFF);
+#else
+const float32 float32_default_nan = const_float32(0xFFC00000);
+#endif
+
+/*----------------------------------------------------------------------------
+| The pattern for a default generated double-precision NaN.
+*----------------------------------------------------------------------------*/
+#if defined(TARGET_SPARC)
+const float64 float64_default_nan = const_float64(LIT64( 0x7FFFFFFFFFFFFFFF ));
+#elif defined(TARGET_PPC) || defined(TARGET_ARM) || defined(TARGET_ALPHA)
+const float64 float64_default_nan = const_float64(LIT64( 0x7FF8000000000000 ));
+#elif SNAN_BIT_IS_ONE
+const float64 float64_default_nan = const_float64(LIT64( 0x7FF7FFFFFFFFFFFF ));
+#else
+const float64 float64_default_nan = const_float64(LIT64( 0xFFF8000000000000 ));
+#endif
+
+/*----------------------------------------------------------------------------
+| The pattern for a default generated extended double-precision NaN.
+*----------------------------------------------------------------------------*/
+#if SNAN_BIT_IS_ONE
+#define floatx80_default_nan_high 0x7FFF
+#define floatx80_default_nan_low LIT64( 0xBFFFFFFFFFFFFFFF )
+#else
+#define floatx80_default_nan_high 0xFFFF
+#define floatx80_default_nan_low LIT64( 0xC000000000000000 )
+#endif
+
+const floatx80 floatx80_default_nan = make_floatx80(floatx80_default_nan_high,
+ floatx80_default_nan_low);
+
+/*----------------------------------------------------------------------------
+| The pattern for a default generated quadruple-precision NaN. The `high' and
+| `low' values hold the most- and least-significant bits, respectively.
+*----------------------------------------------------------------------------*/
+#if SNAN_BIT_IS_ONE
+#define float128_default_nan_high LIT64( 0x7FFF7FFFFFFFFFFF )
+#define float128_default_nan_low LIT64( 0xFFFFFFFFFFFFFFFF )
+#else
+#define float128_default_nan_high LIT64( 0xFFFF800000000000 )
+#define float128_default_nan_low LIT64( 0x0000000000000000 )
+#endif
+
+const float128 float128_default_nan = make_float128(float128_default_nan_high,
+ float128_default_nan_low);
+
/*----------------------------------------------------------------------------
| Raises the exceptions specified by `flags'. Floating-point traps can be
| defined here if desired. It is currently not possible for such a trap
diff --git a/fpu/softfloat.h b/fpu/softfloat.h
index bde2500..3bb7d8f 100644
--- a/fpu/softfloat.h
+++ b/fpu/softfloat.h
@@ -43,7 +43,7 @@ these four paragraphs for those parts of this code that are retained.
#endif
#include <inttypes.h>
-#include "config.h"
+#include "config-host.h"
/*----------------------------------------------------------------------------
| Each of the following `typedef's defines the most convenient type that holds
@@ -68,12 +68,6 @@ typedef int64_t int64;
#define LIT64( a ) a##LL
#define INLINE static inline
-#if defined(TARGET_MIPS) || defined(TARGET_SH4) || defined(TARGET_UNICORE32)
-#define SNAN_BIT_IS_ONE 1
-#else
-#define SNAN_BIT_IS_ONE 0
-#endif
-
#define STATUS_PARAM , float_status *status
#define STATUS(field) status->field
#define STATUS_VAR , status
@@ -142,6 +136,7 @@ typedef struct {
uint64_t low, high;
#endif
} float128;
+#define make_float128(high_, low_) ((float128) { .high = high_, .low = low_ })
/*----------------------------------------------------------------------------
| Software IEC/IEEE floating-point underflow tininess-detection mode.
@@ -248,13 +243,7 @@ float16 float16_maybe_silence_nan( float16 );
/*----------------------------------------------------------------------------
| The pattern for a default generated half-precision NaN.
*----------------------------------------------------------------------------*/
-#if defined(TARGET_ARM)
-#define float16_default_nan make_float16(0x7E00)
-#elif SNAN_BIT_IS_ONE
-#define float16_default_nan make_float16(0x7DFF)
-#else
-#define float16_default_nan make_float16(0xFE00)
-#endif
+extern const float16 float16_default_nan;
/*----------------------------------------------------------------------------
| Software IEC/IEEE single-precision conversion routines.
@@ -357,15 +346,7 @@ INLINE float32 float32_set_sign(float32 a, int sign)
/*----------------------------------------------------------------------------
| The pattern for a default generated single-precision NaN.
*----------------------------------------------------------------------------*/
-#if defined(TARGET_SPARC)
-#define float32_default_nan make_float32(0x7FFFFFFF)
-#elif defined(TARGET_PPC) || defined(TARGET_ARM) || defined(TARGET_ALPHA)
-#define float32_default_nan make_float32(0x7FC00000)
-#elif SNAN_BIT_IS_ONE
-#define float32_default_nan make_float32(0x7FBFFFFF)
-#else
-#define float32_default_nan make_float32(0xFFC00000)
-#endif
+extern const float32 float32_default_nan;
/*----------------------------------------------------------------------------
| Software IEC/IEEE double-precision conversion routines.
@@ -470,15 +451,7 @@ INLINE float64 float64_set_sign(float64 a, int sign)
/*----------------------------------------------------------------------------
| The pattern for a default generated double-precision NaN.
*----------------------------------------------------------------------------*/
-#if defined(TARGET_SPARC)
-#define float64_default_nan make_float64(LIT64( 0x7FFFFFFFFFFFFFFF ))
-#elif defined(TARGET_PPC) || defined(TARGET_ARM) || defined(TARGET_ALPHA)
-#define float64_default_nan make_float64(LIT64( 0x7FF8000000000000 ))
-#elif SNAN_BIT_IS_ONE
-#define float64_default_nan make_float64(LIT64( 0x7FF7FFFFFFFFFFFF ))
-#else
-#define float64_default_nan make_float64(LIT64( 0xFFF8000000000000 ))
-#endif
+extern const float64 float64_default_nan;
/*----------------------------------------------------------------------------
| Software IEC/IEEE extended double-precision conversion routines.
@@ -561,17 +534,9 @@ INLINE int floatx80_is_any_nan(floatx80 a)
#define floatx80_infinity make_floatx80(0x7fff, 0x8000000000000000LL)
/*----------------------------------------------------------------------------
-| The pattern for a default generated extended double-precision NaN. The
-| `high' and `low' values hold the most- and least-significant bits,
-| respectively.
+| The pattern for a default generated extended double-precision NaN.
*----------------------------------------------------------------------------*/
-#if SNAN_BIT_IS_ONE
-#define floatx80_default_nan_high 0x7FFF
-#define floatx80_default_nan_low LIT64( 0xBFFFFFFFFFFFFFFF )
-#else
-#define floatx80_default_nan_high 0xFFFF
-#define floatx80_default_nan_low LIT64( 0xC000000000000000 )
-#endif
+extern const floatx80 floatx80_default_nan;
/*----------------------------------------------------------------------------
| Software IEC/IEEE quadruple-precision conversion routines.
@@ -648,15 +613,8 @@ INLINE int float128_is_any_nan(float128 a)
}
/*----------------------------------------------------------------------------
-| The pattern for a default generated quadruple-precision NaN. The `high' and
-| `low' values hold the most- and least-significant bits, respectively.
+| The pattern for a default generated quadruple-precision NaN.
*----------------------------------------------------------------------------*/
-#if SNAN_BIT_IS_ONE
-#define float128_default_nan_high LIT64( 0x7FFF7FFFFFFFFFFF )
-#define float128_default_nan_low LIT64( 0xFFFFFFFFFFFFFFFF )
-#else
-#define float128_default_nan_high LIT64( 0xFFFF800000000000 )
-#define float128_default_nan_low LIT64( 0x0000000000000000 )
-#endif
+extern const float128 float128_default_nan;
#endif /* !SOFTFLOAT_H */
diff --git a/gdbstub.c b/gdbstub.c
index c085a5a..27b0cfa 100644
--- a/gdbstub.c
+++ b/gdbstub.c
@@ -319,7 +319,7 @@ static int get_char(GDBState *s)
int ret;
for(;;) {
- ret = recv(s->fd, &ch, 1, 0);
+ ret = qemu_recv(s->fd, &ch, 1, 0);
if (ret < 0) {
if (errno == ECONNRESET)
s->fd = -1;
diff --git a/hmp-commands.hx b/hmp-commands.hx
index 6ad8806..0ccfb28 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -840,7 +840,7 @@ ETEXI
{
.name = "snapshot_blkdev",
- .args_type = "device:B,snapshot_file:s?,format:s?",
+ .args_type = "device:B,snapshot-file:s?,format:s?",
.params = "device [new-image-file] [format]",
.help = "initiates a live snapshot\n\t\t\t"
"of device. If a new image file is specified, the\n\t\t\t"
@@ -1311,8 +1311,6 @@ show virtual to physical memory mappings (i386, SH4 and SPARC only)
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
diff --git a/hw/a9mpcore.c b/hw/a9mpcore.c
index b5e5328..6f108f4 100644
--- a/hw/a9mpcore.c
+++ b/hw/a9mpcore.c
@@ -4,7 +4,7 @@
* Copyright (c) 2009 CodeSourcery.
* Written by Paul Brook
*
- * This code is licenced under the GPL.
+ * This code is licensed under the GPL.
*/
/* 64 external IRQ lines. */
diff --git a/hw/acpi.c b/hw/acpi.c
index ad40fb4..79ec66c 100644
--- a/hw/acpi.c
+++ b/hw/acpi.c
@@ -20,19 +20,30 @@
#include "pc.h"
#include "acpi.h"
-struct acpi_table_header
-{
- char signature [4]; /* ACPI signature (4 ASCII characters) */
+struct acpi_table_header {
+ uint16_t _length; /* our length, not actual part of the hdr */
+ /* XXX why we have 2 length fields here? */
+ char sig[4]; /* ACPI signature (4 ASCII characters) */
uint32_t length; /* Length of table, in bytes, including header */
uint8_t revision; /* ACPI Specification minor version # */
uint8_t checksum; /* To make sum of entire table == 0 */
- char oem_id [6]; /* OEM identification */
- char oem_table_id [8]; /* OEM table identification */
+ char oem_id[6]; /* OEM identification */
+ char oem_table_id[8]; /* OEM table identification */
uint32_t oem_revision; /* OEM revision number */
- char asl_compiler_id [4]; /* ASL compiler vendor ID */
+ char asl_compiler_id[4]; /* ASL compiler vendor ID */
uint32_t asl_compiler_revision; /* ASL compiler revision number */
} __attribute__((packed));
+#define ACPI_TABLE_HDR_SIZE sizeof(struct acpi_table_header)
+#define ACPI_TABLE_PFX_SIZE sizeof(uint16_t) /* size of the extra prefix */
+
+static const char dfl_hdr[ACPI_TABLE_HDR_SIZE] =
+ "\0\0" /* fake _length (2) */
+ "QEMU\0\0\0\0\1\0" /* sig (4), len(4), revno (1), csum (1) */
+ "QEMUQEQEMUQEMU\1\0\0\0" /* OEM id (6), table (8), revno (4) */
+ "QEMU\1\0\0\0" /* ASL compiler ID (4), version (4) */
+ ;
+
char *acpi_tables;
size_t acpi_tables_len;
@@ -40,163 +51,198 @@ static int acpi_checksum(const uint8_t *data, int len)
{
int sum, i;
sum = 0;
- for(i = 0; i < len; i++)
+ for (i = 0; i < len; i++) {
sum += data[i];
+ }
return (-sum) & 0xff;
}
+/* like strncpy() but zero-fills the tail of destination */
+static void strzcpy(char *dst, const char *src, size_t size)
+{
+ size_t len = strlen(src);
+ if (len >= size) {
+ len = size;
+ } else {
+ memset(dst + len, 0, size - len);
+ }
+ memcpy(dst, src, len);
+}
+
+/* XXX fixme: this function uses obsolete argument parsing interface */
int acpi_table_add(const char *t)
{
- static const char *dfl_id = "QEMUQEMU";
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;
+ size_t len, start, allen;
+ bool has_header;
+ int changed;
+ int r;
+ struct acpi_table_header hdr;
+
+ r = 0;
+ r |= get_param_value(buf, sizeof(buf), "data", t) ? 1 : 0;
+ r |= get_param_value(buf, sizeof(buf), "file", t) ? 2 : 0;
+ switch (r) {
+ case 0:
+ buf[0] = '\0';
+ /* fallthrough for default behavior */
+ case 1:
+ has_header = false;
+ break;
+ case 2:
+ has_header = true;
+ break;
+ default:
+ fprintf(stderr, "acpitable: both data and file are specified\n");
+ return -1;
+ }
- memset(&acpi_hdr, 0, sizeof(acpi_hdr));
-
- if (get_param_value(buf, sizeof(buf), "sig", t)) {
- strncpy(acpi_hdr.signature, buf, 4);
+ if (!acpi_tables) {
+ allen = sizeof(uint16_t);
+ acpi_tables = qemu_mallocz(allen);
} else {
- strncpy(acpi_hdr.signature, dfl_id, 4);
+ allen = acpi_tables_len;
}
+
+ start = allen;
+ acpi_tables = qemu_realloc(acpi_tables, start + ACPI_TABLE_HDR_SIZE);
+ allen += has_header ? ACPI_TABLE_PFX_SIZE : ACPI_TABLE_HDR_SIZE;
+
+ /* now read in the data files, reallocating buffer as needed */
+
+ for (f = strtok(buf, ":"); f; f = strtok(NULL, ":")) {
+ int fd = open(f, O_RDONLY);
+
+ if (fd < 0) {
+ fprintf(stderr, "can't open file %s: %s\n", f, strerror(errno));
+ return -1;
+ }
+
+ for (;;) {
+ char data[8192];
+ r = read(fd, data, sizeof(data));
+ if (r == 0) {
+ break;
+ } else if (r > 0) {
+ acpi_tables = qemu_realloc(acpi_tables, allen + r);
+ memcpy(acpi_tables + allen, data, r);
+ allen += r;
+ } else if (errno != EINTR) {
+ fprintf(stderr, "can't read file %s: %s\n",
+ f, strerror(errno));
+ close(fd);
+ return -1;
+ }
+ }
+
+ close(fd);
+ }
+
+ /* now fill in the header fields */
+
+ f = acpi_tables + start; /* start of the table */
+ changed = 0;
+
+ /* copy the header to temp place to align the fields */
+ memcpy(&hdr, has_header ? f : dfl_hdr, ACPI_TABLE_HDR_SIZE);
+
+ /* length of the table minus our prefix */
+ len = allen - start - ACPI_TABLE_PFX_SIZE;
+
+ hdr._length = cpu_to_le16(len);
+
+ if (get_param_value(buf, sizeof(buf), "sig", t)) {
+ strzcpy(hdr.sig, buf, sizeof(hdr.sig));
+ ++changed;
+ }
+
+ /* length of the table including header, in bytes */
+ if (has_header) {
+ /* check if actual length is correct */
+ val = le32_to_cpu(hdr.length);
+ if (val != len) {
+ fprintf(stderr,
+ "warning: acpitable has wrong length,"
+ " header says %lu, actual size %zu bytes\n",
+ val, len);
+ ++changed;
+ }
+ }
+ /* we may avoid putting length here if has_header is true */
+ hdr.length = cpu_to_le32(len);
+
if (get_param_value(buf, sizeof(buf), "rev", t)) {
- val = strtoul(buf, &p, 10);
- if (val > 255 || *p != '\0')
- goto out;
- } else {
- val = 1;
+ val = strtoul(buf, &p, 0);
+ if (val > 255 || *p) {
+ fprintf(stderr, "acpitable: \"rev=%s\" is invalid\n", buf);
+ return -1;
+ }
+ hdr.revision = (uint8_t)val;
+ ++changed;
}
- acpi_hdr.revision = (int8_t)val;
if (get_param_value(buf, sizeof(buf), "oem_id", t)) {
- strncpy(acpi_hdr.oem_id, buf, 6);
- } else {
- strncpy(acpi_hdr.oem_id, dfl_id, 6);
+ strzcpy(hdr.oem_id, buf, sizeof(hdr.oem_id));
+ ++changed;
}
if (get_param_value(buf, sizeof(buf), "oem_table_id", t)) {
- strncpy(acpi_hdr.oem_table_id, buf, 8);
- } else {
- strncpy(acpi_hdr.oem_table_id, dfl_id, 8);
+ strzcpy(hdr.oem_table_id, buf, sizeof(hdr.oem_table_id));
+ ++changed;
}
if (get_param_value(buf, sizeof(buf), "oem_rev", t)) {
- val = strtol(buf, &p, 10);
- if(*p != '\0')
- goto out;
- } else {
- val = 1;
+ val = strtol(buf, &p, 0);
+ if (*p) {
+ fprintf(stderr, "acpitable: \"oem_rev=%s\" is invalid\n", buf);
+ return -1;
+ }
+ hdr.oem_revision = cpu_to_le32(val);
+ ++changed;
}
- acpi_hdr.oem_revision = cpu_to_le32(val);
if (get_param_value(buf, sizeof(buf), "asl_compiler_id", t)) {
- strncpy(acpi_hdr.asl_compiler_id, buf, 4);
- } else {
- strncpy(acpi_hdr.asl_compiler_id, dfl_id, 4);
+ strzcpy(hdr.asl_compiler_id, buf, sizeof(hdr.asl_compiler_id));
+ ++changed;
}
if (get_param_value(buf, sizeof(buf), "asl_compiler_rev", t)) {
- val = strtol(buf, &p, 10);
- if(*p != '\0')
- goto out;
- } else {
- val = 1;
- }
- acpi_hdr.asl_compiler_revision = cpu_to_le32(val);
-
- if (!get_param_value(buf, sizeof(buf), "data", t)) {
- buf[0] = '\0';
- }
-
- length = sizeof(acpi_hdr);
-
- f = buf;
- while (buf[0]) {
- struct stat s;
- char *n = strchr(f, ':');
- if (n)
- *n = '\0';
- if(stat(f, &s) < 0) {
- fprintf(stderr, "Can't stat file '%s': %s\n", f, strerror(errno));
- goto out;
+ val = strtol(buf, &p, 0);
+ if (*p) {
+ fprintf(stderr, "acpitable: \"%s=%s\" is invalid\n",
+ "asl_compiler_rev", buf);
+ return -1;
}
- length += s.st_size;
- if (!n)
- break;
- *n = ':';
- f = n + 1;
+ hdr.asl_compiler_revision = cpu_to_le32(val);
+ ++changed;
}
- if (!acpi_tables) {
- acpi_tables_len = sizeof(uint16_t);
- acpi_tables = qemu_mallocz(acpi_tables_len);
+ if (!has_header && !changed) {
+ fprintf(stderr, "warning: acpitable: no table headers are specified\n");
}
- 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) + length;
-
- *(uint16_t*)p = cpu_to_le32(length);
- p += sizeof(uint16_t);
- memcpy(p, &acpi_hdr, sizeof(acpi_hdr));
- off = sizeof(acpi_hdr);
-
- f = buf;
- while (buf[0]) {
- struct stat s;
- int fd;
- char *n = strchr(f, ':');
- if (n)
- *n = '\0';
- fd = open(f, O_RDONLY);
-
- if(fd < 0)
- goto out;
- if(fstat(fd, &s) < 0) {
- close(fd);
- goto out;
- }
- /* 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) {
- off += r;
- s.st_size -= r;
- } else if ((r < 0 && errno != EINTR) || r == 0) {
- close(fd);
- goto out;
- }
- }
- 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);
+ /* now calculate checksum of the table, complete with the header */
+ /* we may as well leave checksum intact if has_header is true */
+ /* alternatively there may be a way to set cksum to a given value */
+ hdr.checksum = 0; /* for checksum calculation */
+
+ /* put header back */
+ memcpy(f, &hdr, sizeof(hdr));
+
+ if (changed || !has_header || 1) {
+ ((struct acpi_table_header *)f)->checksum =
+ acpi_checksum((uint8_t *)f + ACPI_TABLE_PFX_SIZE, len);
}
- 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);
+ (*(uint16_t *)acpi_tables) =
+ cpu_to_le32(le32_to_cpu(*(uint16_t *)acpi_tables) + 1);
+
+ acpi_tables_len = allen;
return 0;
-out:
- if (acpi_tables) {
- qemu_free(acpi_tables);
- acpi_tables = NULL;
- }
- return -1;
+
}
/* ACPI PM1a EVT */
diff --git a/hw/acpi_piix4.c b/hw/acpi_piix4.c
index 350558b..29f0f76 100644
--- a/hw/acpi_piix4.c
+++ b/hw/acpi_piix4.c
@@ -23,6 +23,7 @@
#include "acpi.h"
#include "sysemu.h"
#include "range.h"
+#include "ioport.h"
//#define DEBUG
@@ -63,6 +64,7 @@ typedef struct PIIX4PMState {
qemu_irq irq;
qemu_irq smi_irq;
int kvm_enabled;
+ Notifier machine_ready;
/* for pci hotplug */
ACPIGPE gpe;
@@ -311,6 +313,19 @@ static void piix4_powerdown(void *opaque, int irq, int power_failing)
acpi_pm1_evt_power_down(pm1a, tmr);
}
+static void piix4_pm_machine_ready(Notifier *n, void *opaque)
+{
+ PIIX4PMState *s = container_of(n, PIIX4PMState, machine_ready);
+ uint8_t *pci_conf;
+
+ pci_conf = s->dev.config;
+ pci_conf[0x5f] = (isa_is_ioport_assigned(0x378) ? 0x80 : 0) | 0x10;
+ pci_conf[0x63] = 0x60;
+ pci_conf[0x67] = (isa_is_ioport_assigned(0x3f8) ? 0x08 : 0) |
+ (isa_is_ioport_assigned(0x2f8) ? 0x90 : 0);
+
+}
+
static int piix4_pm_initfn(PCIDevice *dev)
{
PIIX4PMState *s = DO_UPCAST(PIIX4PMState, dev, dev);
@@ -337,11 +352,6 @@ static int piix4_pm_initfn(PCIDevice *dev)
/* XXX: which specification is used ? The i82731AB has different
mappings */
- pci_conf[0x5f] = (parallel_hds[0] != NULL ? 0x80 : 0) | 0x10;
- pci_conf[0x63] = 0x60;
- pci_conf[0x67] = (serial_hds[0] != NULL ? 0x08 : 0) |
- (serial_hds[1] != NULL ? 0x90 : 0);
-
pci_conf[0x90] = s->smb_io_base | 1;
pci_conf[0x91] = s->smb_io_base >> 8;
pci_conf[0xd2] = 0x09;
@@ -354,6 +364,8 @@ static int piix4_pm_initfn(PCIDevice *dev)
qemu_system_powerdown = *qemu_allocate_irqs(piix4_powerdown, s, 1);
pm_smbus_init(&s->dev.qdev, &s->smb);
+ s->machine_ready.notify = piix4_pm_machine_ready;
+ qemu_add_machine_init_done_notifier(&s->machine_ready);
qemu_register_reset(piix4_reset, s);
piix4_acpi_system_hot_add_init(dev->bus, s);
diff --git a/hw/an5206.c b/hw/an5206.c
index 42a0163..04ca420 100644
--- a/hw/an5206.c
+++ b/hw/an5206.c
@@ -3,7 +3,7 @@
*
* Copyright (c) 2007 CodeSourcery.
*
- * This code is licenced under the GPL
+ * This code is licensed under the GPL
*/
#include "hw.h"
diff --git a/hw/apb_pci.c b/hw/apb_pci.c
index 974c87a..8b9939c 100644
--- a/hw/apb_pci.c
+++ b/hw/apb_pci.c
@@ -34,6 +34,7 @@
#include "rwhandler.h"
#include "apb_pci.h"
#include "sysemu.h"
+#include "exec-memory.h"
/* debug APB */
//#define DEBUG_APB
@@ -346,6 +347,7 @@ PCIBus *pci_apb_init(target_phys_addr_t special_base,
d->bus = pci_register_bus(&d->busdev.qdev, "pci",
pci_apb_set_irq, pci_pbm_map_irq, d,
+ get_system_memory(),
0, 32);
pci_bus_set_mem_base(d->bus, mem_base);
diff --git a/hw/arm-misc.h b/hw/arm-misc.h
index 9aeeaea..f8a7472 100644
--- a/hw/arm-misc.h
+++ b/hw/arm-misc.h
@@ -4,7 +4,7 @@
* Copyright (c) 2006 CodeSourcery.
* Written by Paul Brook
*
- * This code is licenced under the LGPL.
+ * This code is licensed under the LGPL.
*
*/
diff --git a/hw/arm11mpcore.c b/hw/arm11mpcore.c
index 3bbd885..b47707f 100644
--- a/hw/arm11mpcore.c
+++ b/hw/arm11mpcore.c
@@ -4,7 +4,7 @@
* Copyright (c) 2006-2007 CodeSourcery.
* Written by Paul Brook
*
- * This code is licenced under the GPL.
+ * This code is licensed under the GPL.
*/
/* ??? The MPCore TRM says the on-chip controller has 224 external IRQ lines
diff --git a/hw/arm_boot.c b/hw/arm_boot.c
index e021576..215d5de 100644
--- a/hw/arm_boot.c
+++ b/hw/arm_boot.c
@@ -4,7 +4,7 @@
* Copyright (c) 2006-2007 CodeSourcery.
* Written by Paul Brook
*
- * This code is licenced under the GPL.
+ * This code is licensed under the GPL.
*/
#include "hw.h"
diff --git a/hw/arm_gic.c b/hw/arm_gic.c
index 0e934ec..fb07314 100644
--- a/hw/arm_gic.c
+++ b/hw/arm_gic.c
@@ -4,7 +4,7 @@
* Copyright (c) 2006-2007 CodeSourcery.
* Written by Paul Brook
*
- * This code is licenced under the GPL.
+ * This code is licensed under the GPL.
*/
/* This file contains implementation code for the RealView EB interrupt
diff --git a/hw/arm_pic.c b/hw/arm_pic.c
index f44568c..985148a 100644
--- a/hw/arm_pic.c
+++ b/hw/arm_pic.c
@@ -4,7 +4,7 @@
* Copyright (c) 2006 CodeSourcery.
* Written by Paul Brook
*
- * This code is licenced under the LGPL
+ * This code is licensed under the LGPL
*/
#include "hw.h"
diff --git a/hw/arm_sysctl.c b/hw/arm_sysctl.c
index 9225b58..fd0c8bc 100644
--- a/hw/arm_sysctl.c
+++ b/hw/arm_sysctl.c
@@ -4,7 +4,7 @@
* Copyright (c) 2006-2007 CodeSourcery.
* Written by Paul Brook
*
- * This code is licenced under the GPL.
+ * This code is licensed under the GPL.
*/
#include "hw.h"
diff --git a/hw/arm_timer.c b/hw/arm_timer.c
index dac9e70..fd9448f 100644
--- a/hw/arm_timer.c
+++ b/hw/arm_timer.c
@@ -4,7 +4,7 @@
* Copyright (c) 2005-2006 CodeSourcery.
* Written by Paul Brook
*
- * This code is licenced under the GPL.
+ * This code is licensed under the GPL.
*/
#include "sysbus.h"
diff --git a/hw/armv7m_nvic.c b/hw/armv7m_nvic.c
index d06eec9..1df8d4d 100644
--- a/hw/armv7m_nvic.c
+++ b/hw/armv7m_nvic.c
@@ -4,7 +4,7 @@
* Copyright (c) 2006-2007 CodeSourcery.
* Written by Paul Brook
*
- * This code is licenced under the GPL.
+ * This code is licensed under the GPL.
*
* The ARMv7M System controller is fairly tightly tied in with the
* NVIC. Much of that is also implemented here.
diff --git a/hw/axis_dev88.c b/hw/axis_dev88.c
index 0e2135a..e0a8c14 100644
--- a/hw/axis_dev88.c
+++ b/hw/axis_dev88.c
@@ -30,13 +30,14 @@
#include "loader.h"
#include "elf.h"
#include "cris-boot.h"
+#include "blockdev.h"
#define D(x)
#define DNAND(x)
struct nand_state_t
{
- NANDFlashState *nand;
+ DeviceState *nand;
unsigned int rdy:1;
unsigned int ale:1;
unsigned int cle:1;
@@ -251,6 +252,7 @@ void axisdev88_init (ram_addr_t ram_size,
CPUState *env;
DeviceState *dev;
SysBusDevice *s;
+ DriveInfo *nand;
qemu_irq irq[30], nmi[2], *cpu_irq;
void *etraxfs_dmac;
struct etraxfs_dma_client *eth[2] = {NULL, NULL};
@@ -278,7 +280,9 @@ void axisdev88_init (ram_addr_t ram_size,
/* Attach a NAND flash to CS1. */
- nand_state.nand = nand_init(NAND_MFR_STMICRO, 0x39);
+ nand = drive_get(IF_MTD, 0, 0);
+ nand_state.nand = nand_init(nand ? nand->bdrv : NULL,
+ NAND_MFR_STMICRO, 0x39);
nand_regs = cpu_register_io_memory(nand_read, nand_write, &nand_state,
DEVICE_NATIVE_ENDIAN);
cpu_register_physical_memory(0x10000000, 0x05000000, nand_regs);
diff --git a/hw/baum.c b/hw/baum.c
index 2aaf5ff..33a22a7 100644
--- a/hw/baum.c
+++ b/hw/baum.c
@@ -576,7 +576,7 @@ static void baum_close(struct CharDriverState *chr)
qemu_free(baum);
}
-CharDriverState *chr_baum_init(QemuOpts *opts)
+int chr_baum_init(QemuOpts *opts, CharDriverState **_chr)
{
BaumDriverState *baum;
CharDriverState *chr;
@@ -629,7 +629,8 @@ CharDriverState *chr_baum_init(QemuOpts *opts)
qemu_chr_generic_open(chr);
- return chr;
+ *_chr = chr;
+ return 0;
fail:
qemu_free_timer(baum->cellCount_timer);
@@ -638,5 +639,5 @@ fail_handle:
qemu_free(handle);
qemu_free(chr);
qemu_free(baum);
- return NULL;
+ return -EIO;
}
diff --git a/hw/baum.h b/hw/baum.h
index 8af710f..3f28cc3 100644
--- a/hw/baum.h
+++ b/hw/baum.h
@@ -23,4 +23,4 @@
*/
/* char device */
-CharDriverState *chr_baum_init(QemuOpts *opts);
+int chr_baum_init(QemuOpts *opts, CharDriverState **_chr);
diff --git a/hw/bitbang_i2c.c b/hw/bitbang_i2c.c
index 2937b5c..53e9c5c 100644
--- a/hw/bitbang_i2c.c
+++ b/hw/bitbang_i2c.c
@@ -4,7 +4,7 @@
*
* Copyright (c) 2008 Jan Kiszka
*
- * This code is licenced under the GNU GPL v2.
+ * This code is licensed under the GNU GPL v2.
*/
#include "hw.h"
#include "bitbang_i2c.h"
diff --git a/hw/bonito.c b/hw/bonito.c
index e8c57a3..5f62dda 100644
--- a/hw/bonito.c
+++ b/hw/bonito.c
@@ -42,6 +42,7 @@
#include "mips.h"
#include "pci_host.h"
#include "sysemu.h"
+#include "exec-memory.h"
//#define DEBUG_BONITO
@@ -773,7 +774,8 @@ PCIBus *bonito_init(qemu_irq *pic)
dev = qdev_create(NULL, "Bonito-pcihost");
pcihost = FROM_SYSBUS(BonitoState, sysbus_from_qdev(dev));
b = pci_register_bus(&pcihost->busdev.qdev, "pci", pci_bonito_set_irq,
- pci_bonito_map_irq, pic, 0x28, 32);
+ pci_bonito_map_irq, pic, get_system_memory(),
+ 0x28, 32);
pcihost->bus = b;
qdev_init_nofail(dev);
diff --git a/hw/bt-hid.c b/hw/bt-hid.c
index 09120af..5f1afe3 100644
--- a/hw/bt-hid.c
+++ b/hw/bt-hid.c
@@ -19,7 +19,9 @@
*/
#include "qemu-common.h"
-#include "usb.h"
+#include "qemu-timer.h"
+#include "console.h"
+#include "hid.h"
#include "bt.h"
enum hid_transaction_req {
@@ -86,7 +88,7 @@ struct bt_hid_device_s {
struct bt_l2cap_device_s btdev;
struct bt_l2cap_conn_params_s *control;
struct bt_l2cap_conn_params_s *interrupt;
- USBDevice *usbdev;
+ HIDState hid;
int proto;
int connected;
@@ -111,7 +113,7 @@ static void bt_hid_reset(struct bt_hid_device_s *s)
bt_l2cap_device_done(&s->btdev);
bt_l2cap_device_init(&s->btdev, net);
- s->usbdev->info->handle_reset(s->usbdev);
+ hid_reset(&s->hid);
s->proto = BT_HID_PROTO_REPORT;
s->state = bt_state_ready;
s->dataother.len = 0;
@@ -124,23 +126,16 @@ static void bt_hid_reset(struct bt_hid_device_s *s)
static int bt_hid_out(struct bt_hid_device_s *s)
{
- USBPacket p;
-
if (s->data_type == BT_DATA_OUTPUT) {
- p.pid = USB_TOKEN_OUT;
- p.devep = 1;
- p.data = s->dataout.buffer;
- p.len = s->dataout.len;
- s->dataout.len = s->usbdev->info->handle_data(s->usbdev, &p);
-
- return s->dataout.len;
+ /* nothing */
+ ;
}
if (s->data_type == BT_DATA_FEATURE) {
/* XXX:
* does this send a USB_REQ_CLEAR_FEATURE/USB_REQ_SET_FEATURE
* or a SET_REPORT? */
- p.devep = 0;
+ ;
}
return -1;
@@ -148,14 +143,8 @@ static int bt_hid_out(struct bt_hid_device_s *s)
static int bt_hid_in(struct bt_hid_device_s *s)
{
- USBPacket p;
-
- p.pid = USB_TOKEN_IN;
- p.devep = 1;
- p.data = s->datain.buffer;
- p.len = sizeof(s->datain.buffer);
- s->datain.len = s->usbdev->info->handle_data(s->usbdev, &p);
-
+ s->datain.len = hid_keyboard_poll(&s->hid, s->datain.buffer,
+ sizeof(s->datain.buffer));
return s->datain.len;
}
@@ -323,8 +312,7 @@ static void bt_hid_control_transaction(struct bt_hid_device_s *s,
break;
}
s->proto = parameter;
- s->usbdev->info->handle_control(s->usbdev, NULL, SET_PROTOCOL, s->proto, 0, 0,
- NULL);
+ s->hid.protocol = parameter;
ret = BT_HS_SUCCESSFUL;
break;
@@ -333,8 +321,7 @@ static void bt_hid_control_transaction(struct bt_hid_device_s *s,
ret = BT_HS_ERR_INVALID_PARAMETER;
break;
}
- s->usbdev->info->handle_control(s->usbdev, NULL, GET_IDLE, 0, 0, 1,
- s->control->sdu_out(s->control, 1));
+ *s->control->sdu_out(s->control, 1) = s->hid.idle;
s->control->sdu_submit(s->control);
break;
@@ -344,11 +331,7 @@ static void bt_hid_control_transaction(struct bt_hid_device_s *s,
break;
}
- /* We don't need to know about the Idle Rate here really,
- * so just pass it on to the device. */
- ret = s->usbdev->info->handle_control(s->usbdev, NULL,
- SET_IDLE, data[1], 0, 0, NULL) ?
- BT_HS_SUCCESSFUL : BT_HS_ERR_INVALID_PARAMETER;
+ s->hid.idle = data[1];
/* XXX: Does this generate a handshake? */
break;
@@ -385,9 +368,10 @@ static void bt_hid_control_sdu(void *opaque, const uint8_t *data, int len)
bt_hid_control_transaction(hid, data, len);
}
-static void bt_hid_datain(void *opaque)
+static void bt_hid_datain(HIDState *hs)
{
- struct bt_hid_device_s *hid = opaque;
+ struct bt_hid_device_s *hid =
+ container_of(hs, struct bt_hid_device_s, hid);
/* If suspended, wake-up and send a wake-up event first. We might
* want to also inspect the input report and ignore event like
@@ -450,7 +434,7 @@ static void bt_hid_connected_update(struct bt_hid_device_s *hid)
hid->btdev.device.inquiry_scan = !hid->connected;
if (hid->connected && !prev) {
- hid->usbdev->info->handle_reset(hid->usbdev);
+ hid_reset(&hid->hid);
hid->proto = BT_HID_PROTO_REPORT;
}
@@ -518,7 +502,7 @@ static void bt_hid_destroy(struct bt_device_s *dev)
bt_hid_send_control(hid, BT_HC_VIRTUAL_CABLE_UNPLUG);
bt_l2cap_device_done(&hid->btdev);
- hid->usbdev->info->handle_destroy(hid->usbdev);
+ hid_free(&hid->hid);
qemu_free(hid);
}
@@ -531,7 +515,7 @@ enum peripheral_minor_class {
};
static struct bt_device_s *bt_hid_init(struct bt_scatternet_s *net,
- USBDevice *dev, enum peripheral_minor_class minor)
+ enum peripheral_minor_class minor)
{
struct bt_hid_device_s *s = qemu_mallocz(sizeof(*s));
uint32_t class =
@@ -551,9 +535,8 @@ static struct bt_device_s *bt_hid_init(struct bt_scatternet_s *net,
bt_l2cap_psm_register(&s->btdev, BT_PSM_HID_INTR,
BT_HID_MTU, bt_hid_new_interrupt_ch);
- s->usbdev = dev;
- s->btdev.device.lmp_name = s->usbdev->product_desc;
- usb_hid_datain_cb(s->usbdev, s, bt_hid_datain);
+ hid_init(&s->hid, HID_KEYBOARD, bt_hid_datain);
+ s->btdev.device.lmp_name = "BT Keyboard";
s->btdev.device.handle_destroy = bt_hid_destroy;
@@ -566,6 +549,5 @@ static struct bt_device_s *bt_hid_init(struct bt_scatternet_s *net,
struct bt_device_s *bt_keyboard_init(struct bt_scatternet_s *net)
{
- USBDevice *dev = usb_create_simple(NULL /* FIXME */, "usb-kbd");
- return bt_hid_init(net, dev, class_keyboard);
+ return bt_hid_init(net, class_keyboard);
}
diff --git a/hw/ccid-card-emulated.c b/hw/ccid-card-emulated.c
index 0b07184..4762e85 100644
--- a/hw/ccid-card-emulated.c
+++ b/hw/ccid-card-emulated.c
@@ -4,7 +4,7 @@
* Copyright (c) 2011 Red Hat.
* Written by Alon Levy.
*
- * This code is licenced under the GNU LGPL, version 2 or later.
+ * This code is licensed under the GNU LGPL, version 2 or later.
*/
/*
diff --git a/hw/ccid.h b/hw/ccid.h
index dbfc13c..9e3abe1 100644
--- a/hw/ccid.h
+++ b/hw/ccid.h
@@ -4,7 +4,7 @@
* Copyright (c) 2011 Red Hat.
* Written by Alon Levy.
*
- * This code is licenced under the GNU LGPL, version 2 or later.
+ * This code is licensed under the GNU LGPL, version 2 or later.
*/
#ifndef CCID_H
@@ -29,7 +29,6 @@ struct CCIDCardState {
*/
struct CCIDCardInfo {
DeviceInfo qdev;
- void (*print)(Monitor *mon, CCIDCardState *card, int indent);
const uint8_t *(*get_atr)(CCIDCardState *card, uint32_t *len);
void (*apdu_from_guest)(CCIDCardState *card,
const uint8_t *apdu,
diff --git a/hw/dp8393x.c b/hw/dp8393x.c
index c332dd5..1bcd8ee 100644
--- a/hw/dp8393x.c
+++ b/hw/dp8393x.c
@@ -898,7 +898,7 @@ void dp83932_init(NICInfo *nd, target_phys_addr_t base, int it_shift,
s->watchdog = qemu_new_timer_ns(vm_clock, dp8393x_watchdog, s);
s->regs[SONIC_SR] = 0x0004; /* only revision recognized by Linux */
- memcpy(s->conf.macaddr.a, nd->macaddr, sizeof(s->conf.macaddr));
+ s->conf.macaddr = nd->macaddr;
s->conf.vlan = nd->vlan;
s->conf.peer = nd->netdev;
diff --git a/hw/ds1225y.c b/hw/ds1225y.c
index b1c5232..662d7b5 100644
--- a/hw/ds1225y.c
+++ b/hw/ds1225y.c
@@ -22,31 +22,24 @@
* THE SOFTWARE.
*/
-#include "hw.h"
-#include "mips.h"
-#include "nvram.h"
+#include "sysbus.h"
+#include "trace.h"
-//#define DEBUG_NVRAM
-
-typedef struct ds1225y_t
-{
+typedef struct {
+ DeviceState qdev;
uint32_t chip_size;
+ char *filename;
QEMUFile *file;
uint8_t *contents;
- uint8_t protection;
-} ds1225y_t;
-
+} NvRamState;
static uint32_t nvram_readb (void *opaque, target_phys_addr_t addr)
{
- ds1225y_t *s = opaque;
+ NvRamState *s = opaque;
uint32_t val;
val = s->contents[addr];
-
-#ifdef DEBUG_NVRAM
- printf("nvram: read 0x%x at " TARGET_FMT_lx "\n", val, addr);
-#endif
+ trace_nvram_read(addr, val);
return val;
}
@@ -70,13 +63,12 @@ static uint32_t nvram_readl (void *opaque, target_phys_addr_t addr)
static void nvram_writeb (void *opaque, target_phys_addr_t addr, uint32_t val)
{
- ds1225y_t *s = opaque;
+ NvRamState *s = opaque;
-#ifdef DEBUG_NVRAM
- printf("nvram: write 0x%x at " TARGET_FMT_lx "\n", val, addr);
-#endif
+ val &= 0xff;
+ trace_nvram_write(addr, s->contents[addr], val);
- s->contents[addr] = val & 0xff;
+ s->contents[addr] = val;
if (s->file) {
qemu_fseek(s->file, addr, SEEK_SET);
qemu_put_byte(s->file, (int)val);
@@ -98,34 +90,6 @@ static void nvram_writel (void *opaque, target_phys_addr_t addr, uint32_t val)
nvram_writeb(opaque, addr + 3, (val >> 24) & 0xff);
}
-static void nvram_writeb_protected (void *opaque, target_phys_addr_t addr, uint32_t val)
-{
- ds1225y_t *s = opaque;
-
- if (s->protection != 7) {
-#ifdef DEBUG_NVRAM
- printf("nvram: prevent write of 0x%x at " TARGET_FMT_lx "\n", val, addr);
-#endif
- return;
- }
-
- nvram_writeb(opaque, addr, val);
-}
-
-static void nvram_writew_protected (void *opaque, target_phys_addr_t addr, uint32_t val)
-{
- nvram_writeb_protected(opaque, addr, val & 0xff);
- nvram_writeb_protected(opaque, addr + 1, (val >> 8) & 0xff);
-}
-
-static void nvram_writel_protected (void *opaque, target_phys_addr_t addr, uint32_t val)
-{
- nvram_writeb_protected(opaque, addr, val & 0xff);
- nvram_writeb_protected(opaque, addr + 1, (val >> 8) & 0xff);
- nvram_writeb_protected(opaque, addr + 2, (val >> 16) & 0xff);
- nvram_writeb_protected(opaque, addr + 3, (val >> 24) & 0xff);
-}
-
static CPUReadMemoryFunc * const nvram_read[] = {
&nvram_readb,
&nvram_readw,
@@ -138,45 +102,83 @@ static CPUWriteMemoryFunc * const nvram_write[] = {
&nvram_writel,
};
-static CPUWriteMemoryFunc * const nvram_write_protected[] = {
- &nvram_writeb_protected,
- &nvram_writew_protected,
- &nvram_writel_protected,
+static int nvram_post_load(void *opaque, int version_id)
+{
+ NvRamState *s = opaque;
+
+ /* Close file, as filename may has changed in load/store process */
+ if (s->file) {
+ qemu_fclose(s->file);
+ }
+
+ /* Write back nvram contents */
+ s->file = qemu_fopen(s->filename, "wb");
+ if (s->file) {
+ /* Write back contents, as 'wb' mode cleaned the file */
+ qemu_put_buffer(s->file, s->contents, s->chip_size);
+ qemu_fflush(s->file);
+ }
+
+ return 0;
+}
+
+static const VMStateDescription vmstate_nvram = {
+ .name = "nvram",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .post_load = nvram_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_VARRAY_UINT32(contents, NvRamState, chip_size, 0,
+ vmstate_info_uint8, uint8_t),
+ VMSTATE_END_OF_LIST()
+ }
};
-/* Initialisation routine */
-void *ds1225y_init(target_phys_addr_t mem_base, const char *filename)
+typedef struct {
+ SysBusDevice busdev;
+ NvRamState nvram;
+} SysBusNvRamState;
+
+static int nvram_sysbus_initfn(SysBusDevice *dev)
{
- ds1225y_t *s;
- int mem_indexRW, mem_indexRP;
+ NvRamState *s = &FROM_SYSBUS(SysBusNvRamState, dev)->nvram;
QEMUFile *file;
+ int s_io;
- s = qemu_mallocz(sizeof(ds1225y_t));
- s->chip_size = 0x2000; /* Fixed for ds1225y chip: 8 KiB */
s->contents = qemu_mallocz(s->chip_size);
- s->protection = 7;
+
+ s_io = cpu_register_io_memory(nvram_read, nvram_write, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, s->chip_size, s_io);
/* Read current file */
- file = qemu_fopen(filename, "rb");
+ file = qemu_fopen(s->filename, "rb");
if (file) {
/* Read nvram contents */
qemu_get_buffer(file, s->contents, s->chip_size);
qemu_fclose(file);
}
- s->file = qemu_fopen(filename, "wb");
- if (s->file) {
- /* Write back contents, as 'wb' mode cleaned the file */
- qemu_put_buffer(s->file, s->contents, s->chip_size);
- qemu_fflush(s->file);
- }
+ nvram_post_load(s, 0);
+
+ return 0;
+}
+
+static SysBusDeviceInfo nvram_sysbus_info = {
+ .qdev.name = "ds1225y",
+ .qdev.size = sizeof(SysBusNvRamState),
+ .qdev.vmsd = &vmstate_nvram,
+ .init = nvram_sysbus_initfn,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_UINT32("size", SysBusNvRamState, nvram.chip_size, 0x2000),
+ DEFINE_PROP_STRING("filename", SysBusNvRamState, nvram.filename),
+ DEFINE_PROP_END_OF_LIST(),
+ },
+};
- /* Read/write memory */
- mem_indexRW = cpu_register_io_memory(nvram_read, nvram_write, s,
- DEVICE_NATIVE_ENDIAN);
- cpu_register_physical_memory(mem_base, s->chip_size, mem_indexRW);
- /* Read/write protected memory */
- mem_indexRP = cpu_register_io_memory(nvram_read, nvram_write_protected, s,
- DEVICE_NATIVE_ENDIAN);
- cpu_register_physical_memory(mem_base + s->chip_size, s->chip_size, mem_indexRP);
- return s;
+static void nvram_register(void)
+{
+ sysbus_register_withprop(&nvram_sysbus_info);
}
+
+device_init(nvram_register)
diff --git a/hw/ds1338.c b/hw/ds1338.c
index 6f5ae5e..3522af5 100644
--- a/hw/ds1338.c
+++ b/hw/ds1338.c
@@ -4,7 +4,7 @@
* Copyright (c) 2009 CodeSourcery.
* Written by Paul Brook
*
- * This code is licenced under the GNU GPL v2.
+ * This code is licensed under the GNU GPL v2.
*/
#include "i2c.h"
diff --git a/hw/dummy_m68k.c b/hw/dummy_m68k.c
index cec1cc8..eed9e38 100644
--- a/hw/dummy_m68k.c
+++ b/hw/dummy_m68k.c
@@ -3,7 +3,7 @@
*
* Copyright (c) 2007 CodeSourcery.
*
- * This code is licenced under the GPL
+ * This code is licensed under the GPL
*/
#include "hw.h"
diff --git a/hw/esp.c b/hw/esp.c
index aa50800..9ddd637 100644
--- a/hw/esp.c
+++ b/hw/esp.c
@@ -244,7 +244,7 @@ static void do_busid_cmd(ESPState *s, uint8_t *buf, uint8_t busid)
DPRINTF("do_busid_cmd: busid 0x%x\n", busid);
lun = busid & 7;
- s->current_req = scsi_req_new(s->current_dev, 0, lun);
+ s->current_req = scsi_req_new(s->current_dev, 0, lun, NULL);
datalen = scsi_req_enqueue(s->current_req, buf);
s->ti_size = datalen;
if (datalen != 0) {
diff --git a/hw/etraxfs_eth.c b/hw/etraxfs_eth.c
index 6aa4007..dff5f55 100644
--- a/hw/etraxfs_eth.c
+++ b/hw/etraxfs_eth.c
@@ -602,7 +602,7 @@ void *etraxfs_eth_init(NICInfo *nd, target_phys_addr_t base, int phyaddr)
DEVICE_NATIVE_ENDIAN);
cpu_register_physical_memory (base, 0x5c, eth->ethregs);
- memcpy(eth->conf.macaddr.a, nd->macaddr, sizeof(nd->macaddr));
+ eth->conf.macaddr = nd->macaddr;
eth->conf.vlan = nd->vlan;
eth->conf.peer = nd->netdev;
diff --git a/hw/flash.h b/hw/flash.h
index c22e1a9..140ae39 100644
--- a/hw/flash.h
+++ b/hw/flash.h
@@ -18,14 +18,13 @@ pflash_t *pflash_cfi02_register(target_phys_addr_t base, ram_addr_t off,
int be);
/* nand.c */
-typedef struct NANDFlashState NANDFlashState;
-NANDFlashState *nand_init(int manf_id, int chip_id);
-void nand_done(NANDFlashState *s);
-void nand_setpins(NANDFlashState *s, uint8_t cle, uint8_t ale,
+DeviceState *nand_init(BlockDriverState *bdrv, int manf_id, int chip_id);
+void nand_setpins(DeviceState *dev, uint8_t cle, uint8_t ale,
uint8_t ce, uint8_t wp, uint8_t gnd);
-void nand_getpins(NANDFlashState *s, int *rb);
-void nand_setio(NANDFlashState *s, uint8_t value);
-uint8_t nand_getio(NANDFlashState *s);
+void nand_getpins(DeviceState *dev, int *rb);
+void nand_setio(DeviceState *dev, uint32_t value);
+uint32_t nand_getio(DeviceState *dev);
+uint32_t nand_getbuswidth(DeviceState *dev);
#define NAND_MFR_TOSHIBA 0x98
#define NAND_MFR_SAMSUNG 0xec
@@ -39,7 +38,9 @@ uint8_t nand_getio(NANDFlashState *s);
/* onenand.c */
void onenand_base_update(void *opaque, target_phys_addr_t new);
void onenand_base_unmap(void *opaque);
-void *onenand_init(uint32_t id, int regshift, qemu_irq irq);
+void *onenand_init(BlockDriverState *bdrv,
+ uint16_t man_id, uint16_t dev_id, uint16_t ver_id,
+ int regshift, qemu_irq irq);
void *onenand_raw_otp(void *opaque);
/* ecc.c */
diff --git a/hw/fw_cfg.c b/hw/fw_cfg.c
index 85c8c3c..a29db90 100644
--- a/hw/fw_cfg.c
+++ b/hw/fw_cfg.c
@@ -26,6 +26,7 @@
#include "isa.h"
#include "fw_cfg.h"
#include "sysbus.h"
+#include "qemu-error.h"
/* debug firmware config */
//#define DEBUG_FW_CFG
@@ -56,6 +57,143 @@ struct FWCfgState {
Notifier machine_ready;
};
+#define JPG_FILE 0
+#define BMP_FILE 1
+
+static FILE *probe_splashfile(char *filename, int *file_sizep, int *file_typep)
+{
+ FILE *fp = NULL;
+ int fop_ret;
+ int file_size;
+ int file_type = -1;
+ unsigned char buf[2] = {0, 0};
+ unsigned int filehead_value = 0;
+ int bmp_bpp;
+
+ fp = fopen(filename, "rb");
+ if (fp == NULL) {
+ error_report("failed to open file '%s'.", filename);
+ return fp;
+ }
+ /* check file size */
+ fseek(fp, 0L, SEEK_END);
+ file_size = ftell(fp);
+ if (file_size < 2) {
+ error_report("file size is less than 2 bytes '%s'.", filename);
+ fclose(fp);
+ fp = NULL;
+ return fp;
+ }
+ /* check magic ID */
+ fseek(fp, 0L, SEEK_SET);
+ fop_ret = fread(buf, 1, 2, fp);
+ filehead_value = (buf[0] + (buf[1] << 8)) & 0xffff;
+ if (filehead_value == 0xd8ff) {
+ file_type = JPG_FILE;
+ } else {
+ if (filehead_value == 0x4d42) {
+ file_type = BMP_FILE;
+ }
+ }
+ if (file_type < 0) {
+ error_report("'%s' not jpg/bmp file,head:0x%x.",
+ filename, filehead_value);
+ fclose(fp);
+ fp = NULL;
+ return fp;
+ }
+ /* check BMP bpp */
+ if (file_type == BMP_FILE) {
+ fseek(fp, 28, SEEK_SET);
+ fop_ret = fread(buf, 1, 2, fp);
+ bmp_bpp = (buf[0] + (buf[1] << 8)) & 0xffff;
+ if (bmp_bpp != 24) {
+ error_report("only 24bpp bmp file is supported.");
+ fclose(fp);
+ fp = NULL;
+ return fp;
+ }
+ }
+ /* return values */
+ *file_sizep = file_size;
+ *file_typep = file_type;
+ return fp;
+}
+
+static void fw_cfg_bootsplash(FWCfgState *s)
+{
+ int boot_splash_time = -1;
+ const char *boot_splash_filename = NULL;
+ char *p;
+ char *filename;
+ FILE *fp;
+ int fop_ret;
+ int file_size;
+ int file_type = -1;
+ const char *temp;
+
+ /* get user configuration */
+ QemuOptsList *plist = qemu_find_opts("boot-opts");
+ QemuOpts *opts = QTAILQ_FIRST(&plist->head);
+ if (opts != NULL) {
+ temp = qemu_opt_get(opts, "splash");
+ if (temp != NULL) {
+ boot_splash_filename = temp;
+ }
+ temp = qemu_opt_get(opts, "splash-time");
+ if (temp != NULL) {
+ p = (char *)temp;
+ boot_splash_time = strtol(p, (char **)&p, 10);
+ }
+ }
+
+ /* insert splash time if user configurated */
+ if (boot_splash_time >= 0) {
+ /* validate the input */
+ if (boot_splash_time > 0xffff) {
+ error_report("splash time is big than 65535, force it to 65535.");
+ boot_splash_time = 0xffff;
+ }
+ /* use little endian format */
+ qemu_extra_params_fw[0] = (uint8_t)(boot_splash_time & 0xff);
+ qemu_extra_params_fw[1] = (uint8_t)((boot_splash_time >> 8) & 0xff);
+ fw_cfg_add_file(s, "etc/boot-menu-wait", qemu_extra_params_fw, 2);
+ }
+
+ /* insert splash file if user configurated */
+ if (boot_splash_filename != NULL) {
+ filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, boot_splash_filename);
+ if (filename == NULL) {
+ error_report("failed to find file '%s'.", boot_splash_filename);
+ return;
+ }
+ /* probing the file */
+ fp = probe_splashfile(filename, &file_size, &file_type);
+ if (fp == NULL) {
+ qemu_free(filename);
+ return;
+ }
+ /* loading file data */
+ if (boot_splash_filedata != NULL) {
+ qemu_free(boot_splash_filedata);
+ }
+ boot_splash_filedata = qemu_malloc(file_size);
+ boot_splash_filedata_size = file_size;
+ fseek(fp, 0L, SEEK_SET);
+ fop_ret = fread(boot_splash_filedata, 1, file_size, fp);
+ fclose(fp);
+ /* insert data */
+ if (file_type == JPG_FILE) {
+ fw_cfg_add_file(s, "bootsplash.jpg",
+ boot_splash_filedata, boot_splash_filedata_size);
+ } else {
+ fw_cfg_add_file(s, "bootsplash.bmp",
+ boot_splash_filedata, boot_splash_filedata_size);
+ }
+ qemu_free(filename);
+ }
+}
+
static void fw_cfg_write(FWCfgState *s, uint8_t value)
{
int arch = !!(s->cur_entry & FW_CFG_ARCH_LOCAL);
@@ -316,7 +454,7 @@ int fw_cfg_add_file(FWCfgState *s, const char *filename, uint8_t *data,
return 1;
}
-static void fw_cfg_machine_ready(struct Notifier* n)
+static void fw_cfg_machine_ready(struct Notifier *n, void *data)
{
uint32_t len;
FWCfgState *s = container_of(n, FWCfgState, machine_ready);
@@ -352,7 +490,7 @@ FWCfgState *fw_cfg_init(uint32_t ctl_port, uint32_t data_port,
fw_cfg_add_i16(s, FW_CFG_NB_CPUS, (uint16_t)smp_cpus);
fw_cfg_add_i16(s, FW_CFG_MAX_CPUS, (uint16_t)max_cpus);
fw_cfg_add_i16(s, FW_CFG_BOOT_MENU, (uint16_t)boot_menu);
-
+ fw_cfg_bootsplash(s);
s->machine_ready.notify = fw_cfg_machine_ready;
qemu_add_machine_init_done_notifier(&s->machine_ready);
diff --git a/hw/grackle_pci.c b/hw/grackle_pci.c
index cee07e0..da67cf9 100644
--- a/hw/grackle_pci.c
+++ b/hw/grackle_pci.c
@@ -61,7 +61,8 @@ static void pci_grackle_reset(void *opaque)
{
}
-PCIBus *pci_grackle_init(uint32_t base, qemu_irq *pic)
+PCIBus *pci_grackle_init(uint32_t base, qemu_irq *pic,
+ MemoryRegion *address_space)
{
DeviceState *dev;
SysBusDevice *s;
@@ -74,7 +75,7 @@ PCIBus *pci_grackle_init(uint32_t base, qemu_irq *pic)
d->host_state.bus = pci_register_bus(&d->busdev.qdev, "pci",
pci_grackle_set_irq,
pci_grackle_map_irq,
- pic, 0, 4);
+ pic, address_space, 0, 4);
pci_create_simple(d->host_state.bus, 0, "grackle");
diff --git a/hw/gt64xxx.c b/hw/gt64xxx.c
index 8e1f6a0..65e63dd 100644
--- a/hw/gt64xxx.c
+++ b/hw/gt64xxx.c
@@ -27,6 +27,7 @@
#include "pci.h"
#include "pci_host.h"
#include "pc.h"
+#include "exec-memory.h"
//#define DEBUG
@@ -1092,7 +1093,8 @@ PCIBus *gt64120_register(qemu_irq *pic)
d = FROM_SYSBUS(GT64120State, s);
d->pci.bus = pci_register_bus(&d->busdev.qdev, "pci",
gt64120_pci_set_irq, gt64120_pci_map_irq,
- pic, PCI_DEVFN(18, 0), 4);
+ pic, get_system_memory(),
+ PCI_DEVFN(18, 0), 4);
d->ISD_handle = cpu_register_io_memory(gt64120_read, gt64120_write, d,
DEVICE_NATIVE_ENDIAN);
diff --git a/hw/hid.c b/hw/hid.c
new file mode 100644
index 0000000..7b5ef5f
--- /dev/null
+++ b/hw/hid.c
@@ -0,0 +1,403 @@
+/*
+ * QEMU HID devices
+ *
+ * Copyright (c) 2005 Fabrice Bellard
+ * Copyright (c) 2007 OpenMoko, Inc. (andrew@openedhand.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 "hw.h"
+#include "console.h"
+#include "qemu-timer.h"
+#include "hid.h"
+
+#define HID_USAGE_ERROR_ROLLOVER 0x01
+#define HID_USAGE_POSTFAIL 0x02
+#define HID_USAGE_ERROR_UNDEFINED 0x03
+
+/* Indices are QEMU keycodes, values are from HID Usage Table. Indices
+ * above 0x80 are for keys that come after 0xe0 or 0xe1+0x1d or 0xe1+0x9d. */
+static const uint8_t hid_usage_keys[0x100] = {
+ 0x00, 0x29, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23,
+ 0x24, 0x25, 0x26, 0x27, 0x2d, 0x2e, 0x2a, 0x2b,
+ 0x14, 0x1a, 0x08, 0x15, 0x17, 0x1c, 0x18, 0x0c,
+ 0x12, 0x13, 0x2f, 0x30, 0x28, 0xe0, 0x04, 0x16,
+ 0x07, 0x09, 0x0a, 0x0b, 0x0d, 0x0e, 0x0f, 0x33,
+ 0x34, 0x35, 0xe1, 0x31, 0x1d, 0x1b, 0x06, 0x19,
+ 0x05, 0x11, 0x10, 0x36, 0x37, 0x38, 0xe5, 0x55,
+ 0xe2, 0x2c, 0x32, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e,
+ 0x3f, 0x40, 0x41, 0x42, 0x43, 0x53, 0x47, 0x5f,
+ 0x60, 0x61, 0x56, 0x5c, 0x5d, 0x5e, 0x57, 0x59,
+ 0x5a, 0x5b, 0x62, 0x63, 0x00, 0x00, 0x00, 0x44,
+ 0x45, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e,
+ 0xe8, 0xe9, 0x71, 0x72, 0x73, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0xe7, 0x65,
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x58, 0xe4, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x00, 0x46,
+ 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x4a,
+ 0x52, 0x4b, 0x00, 0x50, 0x00, 0x4f, 0x00, 0x4d,
+ 0x51, 0x4e, 0x49, 0x4c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xe3, 0xe7, 0x65, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+bool hid_has_events(HIDState *hs)
+{
+ return hs->n > 0;
+}
+
+void hid_set_next_idle(HIDState *hs, int64_t curtime)
+{
+ hs->next_idle_clock = curtime + (get_ticks_per_sec() * hs->idle * 4) / 1000;
+}
+
+static void hid_pointer_event_clear(HIDPointerEvent *e, int buttons)
+{
+ e->xdx = e->ydy = e->dz = 0;
+ e->buttons_state = buttons;
+}
+
+static void hid_pointer_event_combine(HIDPointerEvent *e, int xyrel,
+ int x1, int y1, int z1) {
+ if (xyrel) {
+ e->xdx += x1;
+ e->ydy += y1;
+ } else {
+ e->xdx = x1;
+ e->ydy = y1;
+ /* Windows drivers do not like the 0/0 position and ignore such
+ * events. */
+ if (!(x1 | y1)) {
+ x1 = 1;
+ }
+ }
+ e->dz += z1;
+}
+
+static void hid_pointer_event(void *opaque,
+ int x1, int y1, int z1, int buttons_state)
+{
+ HIDState *hs = opaque;
+ unsigned use_slot = (hs->head + hs->n - 1) & QUEUE_MASK;
+ unsigned previous_slot = (use_slot - 1) & QUEUE_MASK;
+
+ /* We combine events where feasible to keep the queue small. We shouldn't
+ * combine anything with the first event of a particular button state, as
+ * that would change the location of the button state change. When the
+ * queue is empty, a second event is needed because we don't know if
+ * the first event changed the button state. */
+ if (hs->n == QUEUE_LENGTH) {
+ /* Queue full. Discard old button state, combine motion normally. */
+ hs->ptr.queue[use_slot].buttons_state = buttons_state;
+ } else if (hs->n < 2 ||
+ hs->ptr.queue[use_slot].buttons_state != buttons_state ||
+ hs->ptr.queue[previous_slot].buttons_state !=
+ hs->ptr.queue[use_slot].buttons_state) {
+ /* Cannot or should not combine, so add an empty item to the queue. */
+ QUEUE_INCR(use_slot);
+ hs->n++;
+ hid_pointer_event_clear(&hs->ptr.queue[use_slot], buttons_state);
+ }
+ hid_pointer_event_combine(&hs->ptr.queue[use_slot],
+ hs->kind == HID_MOUSE,
+ x1, y1, z1);
+ hs->event(hs);
+}
+
+static void hid_keyboard_event(void *opaque, int keycode)
+{
+ HIDState *hs = opaque;
+ int slot;
+
+ if (hs->n == QUEUE_LENGTH) {
+ fprintf(stderr, "usb-kbd: warning: key event queue full\n");
+ return;
+ }
+ slot = (hs->head + hs->n) & QUEUE_MASK; hs->n++;
+ hs->kbd.keycodes[slot] = keycode;
+ hs->event(hs);
+}
+
+static void hid_keyboard_process_keycode(HIDState *hs)
+{
+ uint8_t hid_code, key;
+ int i, keycode, slot;
+
+ if (hs->n == 0) {
+ return;
+ }
+ slot = hs->head & QUEUE_MASK; QUEUE_INCR(hs->head); hs->n--;
+ keycode = hs->kbd.keycodes[slot];
+
+ key = keycode & 0x7f;
+ hid_code = hid_usage_keys[key | ((hs->kbd.modifiers >> 1) & (1 << 7))];
+ hs->kbd.modifiers &= ~(1 << 8);
+
+ switch (hid_code) {
+ case 0x00:
+ return;
+
+ case 0xe0:
+ if (hs->kbd.modifiers & (1 << 9)) {
+ hs->kbd.modifiers ^= 3 << 8;
+ return;
+ }
+ case 0xe1 ... 0xe7:
+ if (keycode & (1 << 7)) {
+ hs->kbd.modifiers &= ~(1 << (hid_code & 0x0f));
+ return;
+ }
+ case 0xe8 ... 0xef:
+ hs->kbd.modifiers |= 1 << (hid_code & 0x0f);
+ return;
+ }
+
+ if (keycode & (1 << 7)) {
+ for (i = hs->kbd.keys - 1; i >= 0; i--) {
+ if (hs->kbd.key[i] == hid_code) {
+ hs->kbd.key[i] = hs->kbd.key[-- hs->kbd.keys];
+ hs->kbd.key[hs->kbd.keys] = 0x00;
+ break;
+ }
+ }
+ if (i < 0) {
+ return;
+ }
+ } else {
+ for (i = hs->kbd.keys - 1; i >= 0; i--) {
+ if (hs->kbd.key[i] == hid_code) {
+ break;
+ }
+ }
+ if (i < 0) {
+ if (hs->kbd.keys < sizeof(hs->kbd.key)) {
+ hs->kbd.key[hs->kbd.keys++] = hid_code;
+ }
+ } else {
+ return;
+ }
+ }
+}
+
+static inline int int_clamp(int val, int vmin, int vmax)
+{
+ if (val < vmin) {
+ return vmin;
+ } else if (val > vmax) {
+ return vmax;
+ } else {
+ return val;
+ }
+}
+
+int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len)
+{
+ int dx, dy, dz, b, l;
+ int index;
+ HIDPointerEvent *e;
+
+ if (!hs->ptr.mouse_grabbed) {
+ qemu_activate_mouse_event_handler(hs->ptr.eh_entry);
+ hs->ptr.mouse_grabbed = 1;
+ }
+
+ /* When the buffer is empty, return the last event. Relative
+ movements will all be zero. */
+ index = (hs->n ? hs->head : hs->head - 1);
+ e = &hs->ptr.queue[index & QUEUE_MASK];
+
+ if (hs->kind == HID_MOUSE) {
+ dx = int_clamp(e->xdx, -127, 127);
+ dy = int_clamp(e->ydy, -127, 127);
+ e->xdx -= dx;
+ e->ydy -= dy;
+ } else {
+ dx = e->xdx;
+ dy = e->ydy;
+ }
+ dz = int_clamp(e->dz, -127, 127);
+ e->dz -= dz;
+
+ b = 0;
+ if (e->buttons_state & MOUSE_EVENT_LBUTTON) {
+ b |= 0x01;
+ }
+ if (e->buttons_state & MOUSE_EVENT_RBUTTON) {
+ b |= 0x02;
+ }
+ if (e->buttons_state & MOUSE_EVENT_MBUTTON) {
+ b |= 0x04;
+ }
+
+ if (hs->n &&
+ !e->dz &&
+ (hs->kind == HID_TABLET || (!e->xdx && !e->ydy))) {
+ /* that deals with this event */
+ QUEUE_INCR(hs->head);
+ hs->n--;
+ }
+
+ /* Appears we have to invert the wheel direction */
+ dz = 0 - dz;
+ l = 0;
+ switch (hs->kind) {
+ case HID_MOUSE:
+ if (len > l) {
+ buf[l++] = b;
+ }
+ if (len > l) {
+ buf[l++] = dx;
+ }
+ if (len > l) {
+ buf[l++] = dy;
+ }
+ if (len > l) {
+ buf[l++] = dz;
+ }
+ break;
+
+ case HID_TABLET:
+ if (len > l) {
+ buf[l++] = b;
+ }
+ if (len > l) {
+ buf[l++] = dx & 0xff;
+ }
+ if (len > l) {
+ buf[l++] = dx >> 8;
+ }
+ if (len > l) {
+ buf[l++] = dy & 0xff;
+ }
+ if (len > l) {
+ buf[l++] = dy >> 8;
+ }
+ if (len > l) {
+ buf[l++] = dz;
+ }
+ break;
+
+ default:
+ abort();
+ }
+
+ return l;
+}
+
+int hid_keyboard_poll(HIDState *hs, uint8_t *buf, int len)
+{
+ if (len < 2) {
+ return 0;
+ }
+
+ hid_keyboard_process_keycode(hs);
+
+ buf[0] = hs->kbd.modifiers & 0xff;
+ buf[1] = 0;
+ if (hs->kbd.keys > 6) {
+ memset(buf + 2, HID_USAGE_ERROR_ROLLOVER, MIN(8, len) - 2);
+ } else {
+ memcpy(buf + 2, hs->kbd.key, MIN(8, len) - 2);
+ }
+
+ return MIN(8, len);
+}
+
+int hid_keyboard_write(HIDState *hs, uint8_t *buf, int len)
+{
+ if (len > 0) {
+ int ledstate = 0;
+ /* 0x01: Num Lock LED
+ * 0x02: Caps Lock LED
+ * 0x04: Scroll Lock LED
+ * 0x08: Compose LED
+ * 0x10: Kana LED */
+ hs->kbd.leds = buf[0];
+ if (hs->kbd.leds & 0x04) {
+ ledstate |= QEMU_SCROLL_LOCK_LED;
+ }
+ if (hs->kbd.leds & 0x01) {
+ ledstate |= QEMU_NUM_LOCK_LED;
+ }
+ if (hs->kbd.leds & 0x02) {
+ ledstate |= QEMU_CAPS_LOCK_LED;
+ }
+ kbd_put_ledstate(ledstate);
+ }
+ return 0;
+}
+
+void hid_reset(HIDState *hs)
+{
+ switch (hs->kind) {
+ case HID_KEYBOARD:
+ qemu_add_kbd_event_handler(hid_keyboard_event, hs);
+ memset(hs->kbd.keycodes, 0, sizeof(hs->kbd.keycodes));
+ memset(hs->kbd.key, 0, sizeof(hs->kbd.key));
+ hs->kbd.keys = 0;
+ break;
+ case HID_MOUSE:
+ case HID_TABLET:
+ memset(hs->ptr.queue, 0, sizeof(hs->ptr.queue));
+ break;
+ }
+ hs->head = 0;
+ hs->n = 0;
+ hs->protocol = 1;
+ hs->idle = 0;
+}
+
+void hid_free(HIDState *hs)
+{
+ switch (hs->kind) {
+ case HID_KEYBOARD:
+ qemu_remove_kbd_event_handler();
+ break;
+ case HID_MOUSE:
+ case HID_TABLET:
+ qemu_remove_mouse_event_handler(hs->ptr.eh_entry);
+ break;
+ }
+}
+
+void hid_init(HIDState *hs, int kind, HIDEventFunc event)
+{
+ hs->kind = kind;
+ hs->event = event;
+
+ if (hs->kind == HID_MOUSE) {
+ hs->ptr.eh_entry = qemu_add_mouse_event_handler(hid_pointer_event, hs,
+ 0, "QEMU HID Mouse");
+ } else if (hs->kind == HID_TABLET) {
+ hs->ptr.eh_entry = qemu_add_mouse_event_handler(hid_pointer_event, hs,
+ 1, "QEMU HID Tablet");
+ }
+}
diff --git a/hw/hid.h b/hw/hid.h
new file mode 100644
index 0000000..4a8fa5b
--- /dev/null
+++ b/hw/hid.h
@@ -0,0 +1,58 @@
+#ifndef QEMU_HID_H
+#define QEMU_HID_H
+
+#define HID_MOUSE 1
+#define HID_TABLET 2
+#define HID_KEYBOARD 3
+
+typedef struct HIDPointerEvent {
+ int32_t xdx, ydy; /* relative iff it's a mouse, otherwise absolute */
+ int32_t dz, buttons_state;
+} HIDPointerEvent;
+
+#define QUEUE_LENGTH 16 /* should be enough for a triple-click */
+#define QUEUE_MASK (QUEUE_LENGTH-1u)
+#define QUEUE_INCR(v) ((v)++, (v) &= QUEUE_MASK)
+
+typedef struct HIDState HIDState;
+typedef void (*HIDEventFunc)(HIDState *s);
+
+typedef struct HIDMouseState {
+ HIDPointerEvent queue[QUEUE_LENGTH];
+ int mouse_grabbed;
+ QEMUPutMouseEntry *eh_entry;
+} HIDMouseState;
+
+typedef struct HIDKeyboardState {
+ uint32_t keycodes[QUEUE_LENGTH];
+ uint16_t modifiers;
+ uint8_t leds;
+ uint8_t key[16];
+ int32_t keys;
+} HIDKeyboardState;
+
+struct HIDState {
+ union {
+ HIDMouseState ptr;
+ HIDKeyboardState kbd;
+ };
+ uint32_t head; /* index into circular queue */
+ uint32_t n;
+ int kind;
+ int32_t protocol;
+ uint8_t idle;
+ int64_t next_idle_clock;
+ HIDEventFunc event;
+};
+
+void hid_init(HIDState *hs, int kind, HIDEventFunc event);
+void hid_reset(HIDState *hs);
+void hid_free(HIDState *hs);
+
+bool hid_has_events(HIDState *hs);
+void hid_set_next_idle(HIDState *hs, int64_t curtime);
+int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len);
+int hid_keyboard_poll(HIDState *hs, uint8_t *buf, int len);
+int hid_keyboard_write(HIDState *hs, uint8_t *buf, int len);
+
+#endif /* QEMU_HID_H */
diff --git a/hw/hw.h b/hw/hw.h
index 9dd7096..df6ca65 100644
--- a/hw/hw.h
+++ b/hw/hw.h
@@ -324,6 +324,7 @@ typedef struct VMStateSubsection {
struct VMStateDescription {
const char *name;
+ int unmigratable;
int version_id;
int minimum_version_id;
int minimum_version_id_old;
diff --git a/hw/i2c.c b/hw/i2c.c
index f80d12d..49b9ecb 100644
--- a/hw/i2c.c
+++ b/hw/i2c.c
@@ -4,7 +4,7 @@
* Copyright (c) 2007 CodeSourcery.
* Written by Paul Brook
*
- * This code is licenced under the LGPL.
+ * This code is licensed under the LGPL.
*/
#include "i2c.h"
diff --git a/hw/i2c.h b/hw/i2c.h
index 5514402..9381d01 100644
--- a/hw/i2c.h
+++ b/hw/i2c.h
@@ -72,6 +72,6 @@ void wm8750_set_bclk_in(void *opaque, int new_hz);
void tmp105_set(i2c_slave *i2c, int temp);
/* lm832x.c */
-void lm832x_key_event(i2c_slave *i2c, int key, int state);
+void lm832x_key_event(DeviceState *dev, int key, int state);
#endif
diff --git a/hw/ide.h b/hw/ide.h
index 34d9394..a490cbb 100644
--- a/hw/ide.h
+++ b/hw/ide.h
@@ -13,6 +13,7 @@ ISADevice *isa_ide_init(int iobase, int iobase2, int isairq,
/* ide-pci.c */
void pci_cmd646_ide_init(PCIBus *bus, DriveInfo **hd_table,
int secondary_ide_enabled);
+PCIDevice *pci_piix3_xen_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn);
PCIDevice *pci_piix3_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn);
PCIDevice *pci_piix4_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn);
void vt82c686b_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn);
diff --git a/hw/ide/ich.c b/hw/ide/ich.c
index 054e073..d241ea8 100644
--- a/hw/ide/ich.c
+++ b/hw/ide/ich.c
@@ -72,6 +72,11 @@
#include <hw/ide/pci.h>
#include <hw/ide/ahci.h>
+static const VMStateDescription vmstate_ahci = {
+ .name = "ahci",
+ .unmigratable = 1,
+};
+
static int pci_ich9_ahci_init(PCIDevice *dev)
{
struct AHCIPCIState *d;
@@ -123,6 +128,7 @@ static PCIDeviceInfo ich_ahci_info[] = {
.qdev.name = "ich9-ahci",
.qdev.alias = "ahci",
.qdev.size = sizeof(AHCIPCIState),
+ .qdev.vmsd = &vmstate_ahci,
.init = pci_ich9_ahci_init,
.exit = pci_ich9_uninit,
.config_write = pci_ich9_write_config,
diff --git a/hw/ide/piix.c b/hw/ide/piix.c
index 84f72b0..f527dbd 100644
--- a/hw/ide/piix.c
+++ b/hw/ide/piix.c
@@ -149,6 +149,42 @@ static int pci_piix_ide_initfn(PCIDevice *dev)
return 0;
}
+static int pci_piix3_xen_ide_unplug(DeviceState *dev)
+{
+ PCIDevice *pci_dev;
+ PCIIDEState *pci_ide;
+ DriveInfo *di;
+ int i = 0;
+
+ pci_dev = DO_UPCAST(PCIDevice, qdev, dev);
+ pci_ide = DO_UPCAST(PCIIDEState, dev, pci_dev);
+
+ for (; i < 3; i++) {
+ di = drive_get_by_index(IF_IDE, i);
+ if (di != NULL && di->bdrv != NULL && !di->bdrv->removable) {
+ DeviceState *ds = bdrv_get_attached(di->bdrv);
+ if (ds) {
+ bdrv_detach(di->bdrv, ds);
+ }
+ bdrv_close(di->bdrv);
+ pci_ide->bus[di->bus].ifs[di->unit].bs = NULL;
+ drive_put_ref(di);
+ }
+ }
+ qdev_reset_all(&(pci_ide->dev.qdev));
+ return 0;
+}
+
+PCIDevice *pci_piix3_xen_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn)
+{
+ PCIDevice *dev;
+
+ dev = pci_create_simple(bus, devfn, "piix3-ide-xen");
+ dev->qdev.info->unplug = pci_piix3_xen_ide_unplug;
+ pci_ide_create_devs(dev, hd_table);
+ return dev;
+}
+
/* hd_table must contain 4 block drivers */
/* NOTE: for the PIIX3, the IRQs and IOports are hardcoded */
PCIDevice *pci_piix3_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn)
@@ -182,6 +218,14 @@ static PCIDeviceInfo piix_ide_info[] = {
.device_id = PCI_DEVICE_ID_INTEL_82371SB_1,
.class_id = PCI_CLASS_STORAGE_IDE,
},{
+ .qdev.name = "piix3-ide-xen",
+ .qdev.size = sizeof(PCIIDEState),
+ .qdev.no_user = 1,
+ .init = pci_piix_ide_initfn,
+ .vendor_id = PCI_VENDOR_ID_INTEL,
+ .device_id = PCI_DEVICE_ID_INTEL_82371SB_1,
+ .class_id = PCI_CLASS_STORAGE_IDE,
+ },{
.qdev.name = "piix4-ide",
.qdev.size = sizeof(PCIIDEState),
.qdev.no_user = 1,
diff --git a/hw/ide/qdev.c b/hw/ide/qdev.c
index d9b8f24..6bd8d20 100644
--- a/hw/ide/qdev.c
+++ b/hw/ide/qdev.c
@@ -31,6 +31,10 @@ static struct BusInfo ide_bus_info = {
.name = "IDE",
.size = sizeof(IDEBus),
.get_fw_dev_path = idebus_get_fw_dev_path,
+ .props = (Property[]) {
+ DEFINE_PROP_UINT32("unit", IDEDevice, unit, -1),
+ DEFINE_PROP_END_OF_LIST(),
+ },
};
void ide_bus_new(IDEBus *idebus, DeviceState *dev, int bus_id)
@@ -174,7 +178,6 @@ static int ide_drive_initfn(IDEDevice *dev)
}
#define DEFINE_IDE_DEV_PROPERTIES() \
- DEFINE_PROP_UINT32("unit", IDEDrive, dev.unit, -1), \
DEFINE_BLOCK_PROPERTIES(IDEDrive, dev.conf), \
DEFINE_PROP_STRING("ver", IDEDrive, dev.version), \
DEFINE_PROP_STRING("serial", IDEDrive, dev.serial)
diff --git a/hw/integratorcp.c b/hw/integratorcp.c
index a6c27be..2814108 100644
--- a/hw/integratorcp.c
+++ b/hw/integratorcp.c
@@ -4,7 +4,7 @@
* Copyright (c) 2005-2007 CodeSourcery.
* Written by Paul Brook
*
- * This code is licenced under the GPL
+ * This code is licensed under the GPL
*/
#include "sysbus.h"
diff --git a/hw/lan9118.c b/hw/lan9118.c
index 3f3c05d..73a8661 100644
--- a/hw/lan9118.c
+++ b/hw/lan9118.c
@@ -4,7 +4,7 @@
* Copyright (c) 2009 CodeSourcery, LLC.
* Written by Paul Brook
*
- * This code is licenced under the GNU GPL v2
+ * This code is licensed under the GNU GPL v2
*/
#include "sysbus.h"
diff --git a/hw/lm832x.c b/hw/lm832x.c
index 590a4cc..992ce49 100644
--- a/hw/lm832x.c
+++ b/hw/lm832x.c
@@ -474,9 +474,9 @@ static int lm8323_init(i2c_slave *i2c)
return 0;
}
-void lm832x_key_event(struct i2c_slave *i2c, int key, int state)
+void lm832x_key_event(DeviceState *dev, int key, int state)
{
- LM823KbdState *s = (LM823KbdState *) i2c;
+ LM823KbdState *s = FROM_I2C_SLAVE(LM823KbdState, I2C_SLAVE_FROM_QDEV(dev));
if ((s->status & INT_ERROR) && (s->error & ERR_FIFOOVR))
return;
diff --git a/hw/lsi53c895a.c b/hw/lsi53c895a.c
index 940b43a..e9904c4 100644
--- a/hw/lsi53c895a.c
+++ b/hw/lsi53c895a.c
@@ -4,7 +4,7 @@
* Copyright (c) 2006 CodeSourcery.
* Written by Paul Brook
*
- * This code is licenced under the LGPL.
+ * This code is licensed under the LGPL.
*/
/* ??? Need to check if the {read,write}[wl] routines work properly on
@@ -661,7 +661,7 @@ static lsi_request *lsi_find_by_tag(LSIState *s, uint32_t tag)
static void lsi_request_cancelled(SCSIRequest *req)
{
LSIState *s = DO_UPCAST(LSIState, dev.qdev, req->bus->qbus.parent);
- lsi_request *p;
+ lsi_request *p = req->hba_private;
if (s->current && req == s->current->req) {
scsi_req_unref(req);
@@ -670,7 +670,6 @@ static void lsi_request_cancelled(SCSIRequest *req)
return;
}
- p = lsi_find_by_tag(s, req->tag);
if (p) {
QTAILQ_REMOVE(&s->queue, p, next);
scsi_req_unref(req);
@@ -680,18 +679,12 @@ static void lsi_request_cancelled(SCSIRequest *req)
/* Record that data is available for a queued command. Returns zero if
the device was reselected, nonzero if the IO is deferred. */
-static int lsi_queue_tag(LSIState *s, uint32_t tag, uint32_t len)
+static int lsi_queue_req(LSIState *s, SCSIRequest *req, uint32_t len)
{
- lsi_request *p;
-
- p = lsi_find_by_tag(s, tag);
- if (!p) {
- BADF("IO with unknown tag %d\n", tag);
- return 1;
- }
+ lsi_request *p = req->hba_private;
if (p->pending) {
- BADF("Multiple IO pending for tag %d\n", tag);
+ BADF("Multiple IO pending for request %p\n", p);
}
p->pending = len;
/* Reselect if waiting for it, or if reselection triggers an IRQ
@@ -743,9 +736,9 @@ static void lsi_transfer_data(SCSIRequest *req, uint32_t len)
LSIState *s = DO_UPCAST(LSIState, dev.qdev, req->bus->qbus.parent);
int out;
- if (s->waiting == 1 || !s->current || req->tag != s->current->tag ||
+ if (s->waiting == 1 || !s->current || req->hba_private != s->current ||
(lsi_irq_on_rsl(s) && !(s->scntl1 & LSI_SCNTL1_CON))) {
- if (lsi_queue_tag(s, req->tag, len)) {
+ if (lsi_queue_req(s, req, len)) {
return;
}
}
@@ -789,7 +782,8 @@ static void lsi_do_command(LSIState *s)
assert(s->current == NULL);
s->current = qemu_mallocz(sizeof(lsi_request));
s->current->tag = s->select_tag;
- s->current->req = scsi_req_new(dev, s->current->tag, s->current_lun);
+ s->current->req = scsi_req_new(dev, s->current->tag, s->current_lun,
+ s->current);
n = scsi_req_enqueue(s->current->req, buf);
if (n) {
diff --git a/hw/marvell_88w8618_audio.c b/hw/marvell_88w8618_audio.c
index 3eff925..f8c5242 100644
--- a/hw/marvell_88w8618_audio.c
+++ b/hw/marvell_88w8618_audio.c
@@ -4,7 +4,7 @@
*
* Copyright (c) 2008 Jan Kiszka
*
- * This code is licenced under the GNU GPL v2.
+ * This code is licensed under the GNU GPL v2.
*/
#include "sysbus.h"
#include "hw.h"
diff --git a/hw/mc146818rtc.c b/hw/mc146818rtc.c
index 1c9a706..feb3b25 100644
--- a/hw/mc146818rtc.c
+++ b/hw/mc146818rtc.c
@@ -99,6 +99,7 @@ typedef struct RTCState {
QEMUTimer *coalesced_timer;
QEMUTimer *second_timer;
QEMUTimer *second_timer2;
+ Notifier clock_reset_notifier;
} RTCState;
static void rtc_set_time(RTCState *s);
@@ -572,6 +573,22 @@ static const VMStateDescription vmstate_rtc = {
}
};
+static void rtc_notify_clock_reset(Notifier *notifier, void *data)
+{
+ RTCState *s = container_of(notifier, RTCState, clock_reset_notifier);
+ int64_t now = *(int64_t *)data;
+
+ rtc_set_date_from_host(&s->dev);
+ s->next_second_time = now + (get_ticks_per_sec() * 99) / 100;
+ qemu_mod_timer(s->second_timer2, s->next_second_time);
+ rtc_timer_update(s, now);
+#ifdef TARGET_I386
+ if (rtc_td_hack) {
+ rtc_coalesced_timer_update(s);
+ }
+#endif
+}
+
static void rtc_reset(void *opaque)
{
RTCState *s = opaque;
@@ -608,6 +625,9 @@ static int rtc_initfn(ISADevice *dev)
s->second_timer = qemu_new_timer_ns(rtc_clock, rtc_update_second, s);
s->second_timer2 = qemu_new_timer_ns(rtc_clock, rtc_update_second2, s);
+ s->clock_reset_notifier.notify = rtc_notify_clock_reset;
+ qemu_register_clock_reset_notifier(rtc_clock, &s->clock_reset_notifier);
+
s->next_second_time =
qemu_get_clock_ns(rtc_clock) + (get_ticks_per_sec() * 99) / 100;
qemu_mod_timer(s->second_timer2, s->next_second_time);
diff --git a/hw/mcf5206.c b/hw/mcf5206.c
index 2a618d4..fce282d 100644
--- a/hw/mcf5206.c
+++ b/hw/mcf5206.c
@@ -3,7 +3,7 @@
*
* Copyright (c) 2007 CodeSourcery.
*
- * This code is licenced under the GPL
+ * This code is licensed under the GPL
*/
#include "hw.h"
#include "mcf.h"
diff --git a/hw/mcf5208.c b/hw/mcf5208.c
index 17a692d..78fbc5f 100644
--- a/hw/mcf5208.c
+++ b/hw/mcf5208.c
@@ -3,7 +3,7 @@
*
* Copyright (c) 2007 CodeSourcery.
*
- * This code is licenced under the GPL
+ * This code is licensed under the GPL
*/
#include "hw.h"
#include "mcf.h"
diff --git a/hw/mcf_fec.c b/hw/mcf_fec.c
index 21035da..748eb59 100644
--- a/hw/mcf_fec.c
+++ b/hw/mcf_fec.c
@@ -3,7 +3,7 @@
*
* Copyright (c) 2007 CodeSourcery.
*
- * This code is licenced under the GPL
+ * This code is licensed under the GPL
*/
#include "hw.h"
#include "net.h"
@@ -471,7 +471,7 @@ void mcf_fec_init(NICInfo *nd, target_phys_addr_t base, qemu_irq *irq)
DEVICE_NATIVE_ENDIAN);
cpu_register_physical_memory(base, 0x400, s->mmio_index);
- memcpy(s->conf.macaddr.a, nd->macaddr, sizeof(nd->macaddr));
+ s->conf.macaddr = nd->macaddr;
s->conf.vlan = nd->vlan;
s->conf.peer = nd->netdev;
diff --git a/hw/mcf_intc.c b/hw/mcf_intc.c
index ac04295..6cb0a09 100644
--- a/hw/mcf_intc.c
+++ b/hw/mcf_intc.c
@@ -3,7 +3,7 @@
*
* Copyright (c) 2007 CodeSourcery.
*
- * This code is licenced under the GPL
+ * This code is licensed under the GPL
*/
#include "hw.h"
#include "mcf.h"
diff --git a/hw/mcf_uart.c b/hw/mcf_uart.c
index db57096..905e116 100644
--- a/hw/mcf_uart.c
+++ b/hw/mcf_uart.c
@@ -3,7 +3,7 @@
*
* Copyright (c) 2007 CodeSourcery.
*
- * This code is licenced under the GPL
+ * This code is licensed under the GPL
*/
#include "hw.h"
#include "mcf.h"
diff --git a/hw/milkymist-softusb.c b/hw/milkymist-softusb.c
index 5ab35c3..75c85ae 100644
--- a/hw/milkymist-softusb.c
+++ b/hw/milkymist-softusb.c
@@ -234,11 +234,11 @@ static void softusb_usbdev_datain(void *opaque)
USBPacket p;
- p.pid = USB_TOKEN_IN;
- p.devep = 1;
- p.data = s->kbd_usb_buffer;
- p.len = sizeof(s->kbd_usb_buffer);
+ usb_packet_init(&p);
+ usb_packet_setup(&p, USB_TOKEN_IN, 0, 1);
+ usb_packet_addbuf(&p, s->kbd_usb_buffer, sizeof(s->kbd_usb_buffer));
s->usbdev->info->handle_data(s->usbdev, &p);
+ usb_packet_cleanup(&p);
softusb_kbd_changed(s);
}
@@ -247,16 +247,21 @@ static void softusb_attach(USBPort *port)
{
}
-static void softusb_device_destroy(USBBus *bus, USBDevice *dev)
+static void softusb_detach(USBPort *port)
+{
+}
+
+static void softusb_child_detach(USBPort *port, USBDevice *child)
{
}
static USBPortOps softusb_ops = {
.attach = softusb_attach,
+ .detach = softusb_detach,
+ .child_detach = softusb_child_detach,
};
static USBBusOps softusb_bus_ops = {
- .device_destroy = softusb_device_destroy,
};
static void milkymist_softusb_reset(DeviceState *d)
@@ -305,10 +310,12 @@ static int milkymist_softusb_init(SysBusDevice *dev)
usb_bus_new(&s->usbbus, &softusb_bus_ops, NULL);
/* our two ports */
+ /* FIXME: claim to support full speed devices. qemu mouse and keyboard
+ * report themselves as full speed devices. */
usb_register_port(&s->usbbus, &s->usbport[0], NULL, 0, &softusb_ops,
- USB_SPEED_MASK_LOW);
+ USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL);
usb_register_port(&s->usbbus, &s->usbport[1], NULL, 1, &softusb_ops,
- USB_SPEED_MASK_LOW);
+ USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL);
/* and finally create an usb keyboard */
s->usbdev = usb_create_simple(&s->usbbus, "usb-kbd");
diff --git a/hw/mips.h b/hw/mips.h
index 73aa8f8..cae5f4c 100644
--- a/hw/mips.h
+++ b/hw/mips.h
@@ -8,10 +8,6 @@ PCIBus *gt64120_register(qemu_irq *pic);
/* bonito.c */
PCIBus *bonito_init(qemu_irq *pic);
-/* ds1225y.c */
-void *ds1225y_init(target_phys_addr_t mem_base, const char *filename);
-void ds1225y_set_protection(void *opaque, int protection);
-
/* g364fb.c */
int g364fb_mm_init(target_phys_addr_t vram_base,
target_phys_addr_t ctrl_base, int it_shift,
diff --git a/hw/mips_jazz.c b/hw/mips_jazz.c
index a100394..f6ab6dc 100644
--- a/hw/mips_jazz.c
+++ b/hw/mips_jazz.c
@@ -37,6 +37,7 @@
#include "loader.h"
#include "mc146818rtc.h"
#include "blockdev.h"
+#include "sysbus.h"
enum jazz_model_e
{
@@ -115,6 +116,8 @@ void mips_jazz_init (ram_addr_t ram_size,
void* rc4030_opaque;
int s_rtc, s_dma_dummy;
NICInfo *nd;
+ DeviceState *dev;
+ SysBusDevice *sysbus;
ISADevice *pit;
DriveInfo *fds[MAX_FD];
qemu_irq esp_reset, dma_enable;
@@ -266,8 +269,11 @@ void mips_jazz_init (ram_addr_t ram_size,
/* FIXME: missing Jazz sound at 0x8000c000, rc4030[2] */
audio_init(i8259, NULL);
- /* NVRAM: Unprotected at 0x9000, Protected at 0xa000, Read only at 0xb000 */
- ds1225y_init(0x80009000, "nvram");
+ /* NVRAM */
+ dev = qdev_create(NULL, "ds1225y");
+ qdev_init_nofail(dev);
+ sysbus = sysbus_from_qdev(dev);
+ sysbus_mmio_map(sysbus, 0, 0x80009000);
/* LED indicator */
jazz_led_init(0x8000f000);
diff --git a/hw/mipsnet.c b/hw/mipsnet.c
index 26aad51..0db3ba7 100644
--- a/hw/mipsnet.c
+++ b/hw/mipsnet.c
@@ -258,7 +258,7 @@ void mipsnet_init (int base, qemu_irq irq, NICInfo *nd)
s->irq = irq;
if (nd) {
- memcpy(s->conf.macaddr.a, nd->macaddr, sizeof(nd->macaddr));
+ s->conf.macaddr = nd->macaddr;
s->conf.vlan = nd->vlan;
s->conf.peer = nd->netdev;
diff --git a/hw/mpcore.c b/hw/mpcore.c
index 379065a..d778507 100644
--- a/hw/mpcore.c
+++ b/hw/mpcore.c
@@ -4,7 +4,7 @@
* Copyright (c) 2006-2007 CodeSourcery.
* Written by Paul Brook
*
- * This code is licenced under the GPL.
+ * This code is licensed under the GPL.
*/
#include "sysbus.h"
diff --git a/hw/msmouse.c b/hw/msmouse.c
index 05f893c..67c6cd4 100644
--- a/hw/msmouse.c
+++ b/hw/msmouse.c
@@ -64,7 +64,7 @@ static void msmouse_chr_close (struct CharDriverState *chr)
qemu_free (chr);
}
-CharDriverState *qemu_chr_open_msmouse(QemuOpts *opts)
+int qemu_chr_open_msmouse(QemuOpts *opts, CharDriverState **_chr)
{
CharDriverState *chr;
@@ -74,5 +74,6 @@ CharDriverState *qemu_chr_open_msmouse(QemuOpts *opts)
qemu_add_mouse_event_handler(msmouse_event, chr, 0, "QEMU Microsoft Mouse");
- return chr;
+ *_chr = chr;
+ return 0;
}
diff --git a/hw/msmouse.h b/hw/msmouse.h
index 456cb21..8b853b3 100644
--- a/hw/msmouse.h
+++ b/hw/msmouse.h
@@ -1,2 +1,2 @@
/* msmouse.c */
-CharDriverState *qemu_chr_open_msmouse(QemuOpts *opts);
+int qemu_chr_open_msmouse(QemuOpts *opts, CharDriverState **_chr);
diff --git a/hw/multiboot.c b/hw/multiboot.c
index 6e6cfb9..a1d3f41 100644
--- a/hw/multiboot.c
+++ b/hw/multiboot.c
@@ -97,11 +97,11 @@ typedef struct {
static uint32_t mb_add_cmdline(MultibootState *s, const char *cmdline)
{
- int len = strlen(cmdline) + 1;
target_phys_addr_t p = s->offset_cmdlines;
+ char *b = (char *)s->mb_buf + p;
- pstrcpy((char *)s->mb_buf + p, len, cmdline);
- s->offset_cmdlines += len;
+ get_opt_value(b, strlen(cmdline) + 1, cmdline);
+ s->offset_cmdlines += strlen(b) + 1;
return s->mb_buf_phys + p;
}
@@ -198,11 +198,14 @@ int load_multiboot(void *fw_cfg,
} else {
/* Valid if mh_flags sets MULTIBOOT_HEADER_HAS_ADDR. */
uint32_t mh_header_addr = ldl_p(header+i+12);
+ uint32_t mh_load_end_addr = ldl_p(header+i+20);
+ uint32_t mh_bss_end_addr = ldl_p(header+i+24);
mh_load_addr = ldl_p(header+i+16);
uint32_t mb_kernel_text_offset = i - (mh_header_addr - mh_load_addr);
+ uint32_t mb_load_size = mh_load_end_addr - mh_load_addr;
mh_entry_addr = ldl_p(header+i+28);
- mb_kernel_size = kernel_file_size - mb_kernel_text_offset;
+ mb_kernel_size = mh_bss_end_addr - mh_load_addr;
/* Valid if mh_flags sets MULTIBOOT_HEADER_HAS_VBE.
uint32_t mh_mode_type = ldl_p(header+i+32);
@@ -212,17 +215,18 @@ int load_multiboot(void *fw_cfg,
mb_debug("multiboot: mh_header_addr = %#x\n", mh_header_addr);
mb_debug("multiboot: mh_load_addr = %#x\n", mh_load_addr);
- mb_debug("multiboot: mh_load_end_addr = %#x\n", ldl_p(header+i+20));
- mb_debug("multiboot: mh_bss_end_addr = %#x\n", ldl_p(header+i+24));
+ mb_debug("multiboot: mh_load_end_addr = %#x\n", mh_load_end_addr);
+ mb_debug("multiboot: mh_bss_end_addr = %#x\n", mh_bss_end_addr);
mb_debug("qemu: loading multiboot kernel (%#x bytes) at %#x\n",
- mb_kernel_size, mh_load_addr);
+ mb_load_size, mh_load_addr);
mbs.mb_buf = qemu_malloc(mb_kernel_size);
fseek(f, mb_kernel_text_offset, SEEK_SET);
- if (fread(mbs.mb_buf, 1, mb_kernel_size, f) != mb_kernel_size) {
+ if (fread(mbs.mb_buf, 1, mb_load_size, f) != mb_load_size) {
fprintf(stderr, "fread() failed\n");
exit(1);
}
+ memset(mbs.mb_buf + mb_load_size, 0, mb_kernel_size - mb_load_size);
fclose(f);
}
@@ -238,7 +242,7 @@ int load_multiboot(void *fw_cfg,
const char *r = initrd_filename;
mbs.mb_buf_size += strlen(r) + 1;
mbs.mb_mods_avail = 1;
- while ((r = strchr(r, ','))) {
+ while (*(r = get_opt_value(NULL, 0, r))) {
mbs.mb_mods_avail++;
r++;
}
@@ -252,7 +256,7 @@ int load_multiboot(void *fw_cfg,
mbs.offset_cmdlines = mbs.offset_mbinfo + mbs.mb_mods_avail * MB_MOD_SIZE;
if (initrd_filename) {
- char *next_initrd;
+ char *next_initrd, not_last;
mbs.offset_mods = mbs.mb_buf_size;
@@ -261,9 +265,9 @@ int load_multiboot(void *fw_cfg,
int mb_mod_length;
uint32_t offs = mbs.mb_buf_size;
- next_initrd = strchr(initrd_filename, ',');
- if (next_initrd)
- *next_initrd = '\0';
+ next_initrd = (char *)get_opt_value(NULL, 0, initrd_filename);
+ not_last = *next_initrd;
+ *next_initrd = '\0';
/* if a space comes after the module filename, treat everything
after that as parameters */
target_phys_addr_t c = mb_add_cmdline(&mbs, initrd_filename);
@@ -287,7 +291,7 @@ int load_multiboot(void *fw_cfg,
(char *)mbs.mb_buf + offs,
(char *)mbs.mb_buf + offs + mb_mod_length, c);
initrd_filename = next_initrd+1;
- } while (next_initrd);
+ } while (not_last);
}
/* Commandline support */
diff --git a/hw/musicpal.c b/hw/musicpal.c
index 52b2931..63dd391 100644
--- a/hw/musicpal.c
+++ b/hw/musicpal.c
@@ -3,7 +3,7 @@
*
* Copyright (c) 2008 Jan Kiszka
*
- * This code is licenced under the GNU GPL v2.
+ * This code is licensed under the GNU GPL v2.
*/
#include "sysbus.h"
diff --git a/hw/nand.c b/hw/nand.c
index 37e51d7..28d9f0b 100644
--- a/hw/nand.c
+++ b/hw/nand.c
@@ -6,6 +6,10 @@
* Copyright (c) 2006 Openedhand Ltd.
* Written by Andrzej Zaborowski <balrog@zabor.org>
*
+ * Support for additional features based on "MT29F2G16ABCWP 2Gx16"
+ * datasheet from Micron Technology and "NAND02G-B2C" datasheet
+ * from ST Microelectronics.
+ *
* This code is licensed under the GNU GPL v2.
*/
@@ -14,7 +18,7 @@
# include "hw.h"
# include "flash.h"
# include "blockdev.h"
-/* FIXME: Pass block device as an argument. */
+# include "sysbus.h"
# define NAND_CMD_READ0 0x00
# define NAND_CMD_READ1 0x01
@@ -44,8 +48,11 @@
# define MAX_PAGE 0x800
# define MAX_OOB 0x40
+typedef struct NANDFlashState NANDFlashState;
struct NANDFlashState {
+ SysBusDevice busdev;
uint8_t manf_id, chip_id;
+ uint8_t buswidth; /* in BYTES */
int size, pages;
int page_shift, oob_shift, erase_shift, addr_shift;
uint8_t *storage;
@@ -58,18 +65,28 @@ struct NANDFlashState {
uint8_t *ioaddr;
int iolen;
- uint32_t cmd, addr;
+ uint32_t cmd;
+ uint64_t addr;
int addrlen;
int status;
int offset;
void (*blk_write)(NANDFlashState *s);
void (*blk_erase)(NANDFlashState *s);
- void (*blk_load)(NANDFlashState *s, uint32_t addr, int offset);
+ void (*blk_load)(NANDFlashState *s, uint64_t addr, int offset);
uint32_t ioaddr_vmstate;
};
+static void mem_and(uint8_t *dest, const uint8_t *src, size_t n)
+{
+ /* Like memcpy() but we logical-AND the data into the destination */
+ int i;
+ for (i = 0; i < n; i++) {
+ dest[i] &= src[i];
+ }
+}
+
# define NAND_NO_AUTOINCR 0x00000001
# define NAND_BUSWIDTH_16 0x00000002
# define NAND_NO_PADDING 0x00000004
@@ -201,8 +218,9 @@ static const struct {
[0xc5] = { 2048, 16, 0, 0, LP_OPTIONS16 },
};
-static void nand_reset(NANDFlashState *s)
+static void nand_reset(DeviceState *dev)
{
+ NANDFlashState *s = FROM_SYSBUS(NANDFlashState, sysbus_from_qdev(dev));
s->cmd = NAND_CMD_READ0;
s->addr = 0;
s->addrlen = 0;
@@ -211,6 +229,14 @@ static void nand_reset(NANDFlashState *s)
s->status &= NAND_IOSTATUS_UNPROTCT;
}
+static inline void nand_pushio_byte(NANDFlashState *s, uint8_t value)
+{
+ s->ioaddr[s->iolen++] = value;
+ for (value = s->buswidth; --value;) {
+ s->ioaddr[s->iolen++] = 0;
+ }
+}
+
static void nand_command(NANDFlashState *s)
{
unsigned int offset;
@@ -220,15 +246,19 @@ static void nand_command(NANDFlashState *s)
break;
case NAND_CMD_READID:
- s->io[0] = s->manf_id;
- s->io[1] = s->chip_id;
- s->io[2] = 'Q'; /* Don't-care byte (often 0xa5) */
- if (nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP)
- s->io[3] = 0x15; /* Page Size, Block Size, Spare Size.. */
- else
- s->io[3] = 0xc0; /* Multi-plane */
s->ioaddr = s->io;
- s->iolen = 4;
+ s->iolen = 0;
+ nand_pushio_byte(s, s->manf_id);
+ nand_pushio_byte(s, s->chip_id);
+ nand_pushio_byte(s, 'Q'); /* Don't-care byte (often 0xa5) */
+ if (nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) {
+ /* Page Size, Block Size, Spare Size; bit 6 indicates
+ * 8 vs 16 bit width NAND.
+ */
+ nand_pushio_byte(s, (s->buswidth == 2) ? 0x55 : 0x15);
+ } else {
+ nand_pushio_byte(s, 0xc0); /* Multi-plane */
+ }
break;
case NAND_CMD_RANDOMREAD2:
@@ -244,7 +274,7 @@ static void nand_command(NANDFlashState *s)
break;
case NAND_CMD_RESET:
- nand_reset(s);
+ nand_reset(&s->busdev.qdev);
break;
case NAND_CMD_PAGEPROGRAM1:
@@ -273,9 +303,9 @@ static void nand_command(NANDFlashState *s)
break;
case NAND_CMD_READSTATUS:
- s->io[0] = s->status;
s->ioaddr = s->io;
- s->iolen = 1;
+ s->iolen = 0;
+ nand_pushio_byte(s, s->status);
break;
default:
@@ -304,9 +334,9 @@ static int nand_post_load(void *opaque, int version_id)
static const VMStateDescription vmstate_nand = {
.name = "nand",
- .version_id = 0,
- .minimum_version_id = 0,
- .minimum_version_id_old = 0,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
.pre_save = nand_pre_save,
.post_load = nand_post_load,
.fields = (VMStateField[]) {
@@ -319,7 +349,7 @@ static const VMStateDescription vmstate_nand = {
VMSTATE_UINT32(ioaddr_vmstate, NANDFlashState),
VMSTATE_INT32(iolen, NANDFlashState),
VMSTATE_UINT32(cmd, NANDFlashState),
- VMSTATE_UINT32(addr, NANDFlashState),
+ VMSTATE_UINT64(addr, NANDFlashState),
VMSTATE_INT32(addrlen, NANDFlashState),
VMSTATE_INT32(status, NANDFlashState),
VMSTATE_INT32(offset, NANDFlashState),
@@ -328,15 +358,85 @@ static const VMStateDescription vmstate_nand = {
}
};
+static int nand_device_init(SysBusDevice *dev)
+{
+ int pagesize;
+ NANDFlashState *s = FROM_SYSBUS(NANDFlashState, dev);
+
+ s->buswidth = nand_flash_ids[s->chip_id].width >> 3;
+ s->size = nand_flash_ids[s->chip_id].size << 20;
+ if (nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) {
+ s->page_shift = 11;
+ s->erase_shift = 6;
+ } else {
+ s->page_shift = nand_flash_ids[s->chip_id].page_shift;
+ s->erase_shift = nand_flash_ids[s->chip_id].erase_shift;
+ }
+
+ switch (1 << s->page_shift) {
+ case 256:
+ nand_init_256(s);
+ break;
+ case 512:
+ nand_init_512(s);
+ break;
+ case 2048:
+ nand_init_2048(s);
+ break;
+ default:
+ hw_error("%s: Unsupported NAND block size.\n", __func__);
+ }
+
+ pagesize = 1 << s->oob_shift;
+ s->mem_oob = 1;
+ if (s->bdrv && bdrv_getlength(s->bdrv) >=
+ (s->pages << s->page_shift) + (s->pages << s->oob_shift)) {
+ pagesize = 0;
+ s->mem_oob = 0;
+ }
+
+ if (!s->bdrv) {
+ pagesize += 1 << s->page_shift;
+ }
+ if (pagesize) {
+ s->storage = (uint8_t *) memset(qemu_malloc(s->pages * pagesize),
+ 0xff, s->pages * pagesize);
+ }
+ /* Give s->ioaddr a sane value in case we save state before it is used. */
+ s->ioaddr = s->io;
+
+ return 0;
+}
+
+static SysBusDeviceInfo nand_info = {
+ .init = nand_device_init,
+ .qdev.name = "nand",
+ .qdev.size = sizeof(NANDFlashState),
+ .qdev.reset = nand_reset,
+ .qdev.vmsd = &vmstate_nand,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_UINT8("manufacturer_id", NANDFlashState, manf_id, 0),
+ DEFINE_PROP_UINT8("chip_id", NANDFlashState, chip_id, 0),
+ DEFINE_PROP_DRIVE("drive", NANDFlashState, bdrv),
+ DEFINE_PROP_END_OF_LIST()
+ }
+};
+
+static void nand_create_device(void)
+{
+ sysbus_register_withprop(&nand_info);
+}
+
/*
* Chip inputs are CLE, ALE, CE, WP, GND and eight I/O pins. Chip
* outputs are R/B and eight I/O pins.
*
* CE, WP and R/B are active low.
*/
-void nand_setpins(NANDFlashState *s, uint8_t cle, uint8_t ale,
+void nand_setpins(DeviceState *dev, uint8_t cle, uint8_t ale,
uint8_t ce, uint8_t wp, uint8_t gnd)
{
+ NANDFlashState *s = (NANDFlashState *) dev;
s->cle = cle;
s->ale = ale;
s->ce = ce;
@@ -348,13 +448,15 @@ void nand_setpins(NANDFlashState *s, uint8_t cle, uint8_t ale,
s->status &= ~NAND_IOSTATUS_UNPROTCT;
}
-void nand_getpins(NANDFlashState *s, int *rb)
+void nand_getpins(DeviceState *dev, int *rb)
{
*rb = 1;
}
-void nand_setio(NANDFlashState *s, uint8_t value)
+void nand_setio(DeviceState *dev, uint32_t value)
{
+ int i;
+ NANDFlashState *s = (NANDFlashState *) dev;
if (!s->ce && s->cle) {
if (nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) {
if (s->cmd == NAND_CMD_READ0 && value == NAND_CMD_LPREAD2)
@@ -400,40 +502,69 @@ void nand_setio(NANDFlashState *s, uint8_t value)
s->addr = (s->addr & mask) | v;
s->addrlen ++;
- if (s->addrlen == 1 && s->cmd == NAND_CMD_READID)
- nand_command(s);
-
- if (!(nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) &&
- s->addrlen == 3 && (
- s->cmd == NAND_CMD_READ0 ||
- s->cmd == NAND_CMD_PAGEPROGRAM1))
- nand_command(s);
- if ((nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) &&
- s->addrlen == 4 && (
- s->cmd == NAND_CMD_READ0 ||
- s->cmd == NAND_CMD_PAGEPROGRAM1))
- nand_command(s);
+ switch (s->addrlen) {
+ case 1:
+ if (s->cmd == NAND_CMD_READID) {
+ nand_command(s);
+ }
+ break;
+ case 2: /* fix cache address as a byte address */
+ s->addr <<= (s->buswidth - 1);
+ break;
+ case 3:
+ if (!(nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) &&
+ (s->cmd == NAND_CMD_READ0 ||
+ s->cmd == NAND_CMD_PAGEPROGRAM1)) {
+ nand_command(s);
+ }
+ break;
+ case 4:
+ if ((nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) &&
+ nand_flash_ids[s->chip_id].size < 256 && /* 1Gb or less */
+ (s->cmd == NAND_CMD_READ0 ||
+ s->cmd == NAND_CMD_PAGEPROGRAM1)) {
+ nand_command(s);
+ }
+ break;
+ case 5:
+ if ((nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) &&
+ nand_flash_ids[s->chip_id].size >= 256 && /* 2Gb or more */
+ (s->cmd == NAND_CMD_READ0 ||
+ s->cmd == NAND_CMD_PAGEPROGRAM1)) {
+ nand_command(s);
+ }
+ break;
+ default:
+ break;
+ }
}
if (!s->cle && !s->ale && s->cmd == NAND_CMD_PAGEPROGRAM1) {
- if (s->iolen < (1 << s->page_shift) + (1 << s->oob_shift))
- s->io[s->iolen ++] = value;
+ if (s->iolen < (1 << s->page_shift) + (1 << s->oob_shift)) {
+ for (i = s->buswidth; i--; value >>= 8) {
+ s->io[s->iolen ++] = (uint8_t) (value & 0xff);
+ }
+ }
} else if (!s->cle && !s->ale && s->cmd == NAND_CMD_COPYBACKPRG1) {
if ((s->addr & ((1 << s->addr_shift) - 1)) <
(1 << s->page_shift) + (1 << s->oob_shift)) {
- s->io[s->iolen + (s->addr & ((1 << s->addr_shift) - 1))] = value;
- s->addr ++;
+ for (i = s->buswidth; i--; s->addr++, value >>= 8) {
+ s->io[s->iolen + (s->addr & ((1 << s->addr_shift) - 1))] =
+ (uint8_t) (value & 0xff);
+ }
}
}
}
-uint8_t nand_getio(NANDFlashState *s)
+uint32_t nand_getio(DeviceState *dev)
{
int offset;
+ uint32_t x = 0;
+ NANDFlashState *s = (NANDFlashState *) dev;
/* Allow sequential reading */
if (!s->iolen && s->cmd == NAND_CMD_READ0) {
- offset = (s->addr & ((1 << s->addr_shift) - 1)) + s->offset;
+ offset = (int) (s->addr & ((1 << s->addr_shift) - 1)) + s->offset;
s->offset = 0;
s->blk_load(s, s->addr, offset);
@@ -446,129 +577,90 @@ uint8_t nand_getio(NANDFlashState *s)
if (s->ce || s->iolen <= 0)
return 0;
- s->iolen --;
- s->addr++;
- return *(s->ioaddr ++);
+ for (offset = s->buswidth; offset--;) {
+ x |= s->ioaddr[offset] << (offset << 3);
+ }
+ /* after receiving READ STATUS command all subsequent reads will
+ * return the status register value until another command is issued
+ */
+ if (s->cmd != NAND_CMD_READSTATUS) {
+ s->addr += s->buswidth;
+ s->ioaddr += s->buswidth;
+ s->iolen -= s->buswidth;
+ }
+ return x;
+}
+
+uint32_t nand_getbuswidth(DeviceState *dev)
+{
+ NANDFlashState *s = (NANDFlashState *) dev;
+ return s->buswidth << 3;
}
-NANDFlashState *nand_init(int manf_id, int chip_id)
+DeviceState *nand_init(BlockDriverState *bdrv, int manf_id, int chip_id)
{
- int pagesize;
- NANDFlashState *s;
- DriveInfo *dinfo;
+ DeviceState *dev;
if (nand_flash_ids[chip_id].size == 0) {
hw_error("%s: Unsupported NAND chip ID.\n", __FUNCTION__);
}
-
- s = (NANDFlashState *) qemu_mallocz(sizeof(NANDFlashState));
- dinfo = drive_get(IF_MTD, 0, 0);
- if (dinfo)
- s->bdrv = dinfo->bdrv;
- s->manf_id = manf_id;
- s->chip_id = chip_id;
- s->size = nand_flash_ids[s->chip_id].size << 20;
- if (nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) {
- s->page_shift = 11;
- s->erase_shift = 6;
- } else {
- s->page_shift = nand_flash_ids[s->chip_id].page_shift;
- s->erase_shift = nand_flash_ids[s->chip_id].erase_shift;
+ dev = qdev_create(NULL, "nand");
+ qdev_prop_set_uint8(dev, "manufacturer_id", manf_id);
+ qdev_prop_set_uint8(dev, "chip_id", chip_id);
+ if (bdrv) {
+ qdev_prop_set_drive_nofail(dev, "drive", bdrv);
}
- switch (1 << s->page_shift) {
- case 256:
- nand_init_256(s);
- break;
- case 512:
- nand_init_512(s);
- break;
- case 2048:
- nand_init_2048(s);
- break;
- default:
- hw_error("%s: Unsupported NAND block size.\n", __FUNCTION__);
- }
-
- pagesize = 1 << s->oob_shift;
- s->mem_oob = 1;
- if (s->bdrv && bdrv_getlength(s->bdrv) >=
- (s->pages << s->page_shift) + (s->pages << s->oob_shift)) {
- pagesize = 0;
- s->mem_oob = 0;
- }
-
- if (!s->bdrv)
- pagesize += 1 << s->page_shift;
- if (pagesize)
- s->storage = (uint8_t *) memset(qemu_malloc(s->pages * pagesize),
- 0xff, s->pages * pagesize);
- /* Give s->ioaddr a sane value in case we save state before it
- is used. */
- s->ioaddr = s->io;
-
- vmstate_register(NULL, -1, &vmstate_nand, s);
-
- return s;
+ qdev_init_nofail(dev);
+ return dev;
}
-void nand_done(NANDFlashState *s)
-{
- if (s->bdrv) {
- bdrv_close(s->bdrv);
- bdrv_delete(s->bdrv);
- }
-
- if (!s->bdrv || s->mem_oob)
- qemu_free(s->storage);
-
- qemu_free(s);
-}
+device_init(nand_create_device)
#else
/* Program a single page */
static void glue(nand_blk_write_, PAGE_SIZE)(NANDFlashState *s)
{
- uint32_t off, page, sector, soff;
+ uint64_t off, page, sector, soff;
uint8_t iobuf[(PAGE_SECTORS + 2) * 0x200];
if (PAGE(s->addr) >= s->pages)
return;
if (!s->bdrv) {
- memcpy(s->storage + PAGE_START(s->addr) + (s->addr & PAGE_MASK) +
+ mem_and(s->storage + PAGE_START(s->addr) + (s->addr & PAGE_MASK) +
s->offset, s->io, s->iolen);
} else if (s->mem_oob) {
sector = SECTOR(s->addr);
off = (s->addr & PAGE_MASK) + s->offset;
soff = SECTOR_OFFSET(s->addr);
if (bdrv_read(s->bdrv, sector, iobuf, PAGE_SECTORS) == -1) {
- printf("%s: read error in sector %i\n", __FUNCTION__, sector);
+ printf("%s: read error in sector %" PRIu64 "\n", __func__, sector);
return;
}
- memcpy(iobuf + (soff | off), s->io, MIN(s->iolen, PAGE_SIZE - off));
+ mem_and(iobuf + (soff | off), s->io, MIN(s->iolen, PAGE_SIZE - off));
if (off + s->iolen > PAGE_SIZE) {
page = PAGE(s->addr);
- memcpy(s->storage + (page << OOB_SHIFT), s->io + PAGE_SIZE - off,
+ mem_and(s->storage + (page << OOB_SHIFT), s->io + PAGE_SIZE - off,
MIN(OOB_SIZE, off + s->iolen - PAGE_SIZE));
}
if (bdrv_write(s->bdrv, sector, iobuf, PAGE_SECTORS) == -1)
- printf("%s: write error in sector %i\n", __FUNCTION__, sector);
+ printf("%s: write error in sector %" PRIu64 "\n", __func__, sector);
} else {
off = PAGE_START(s->addr) + (s->addr & PAGE_MASK) + s->offset;
sector = off >> 9;
soff = off & 0x1ff;
if (bdrv_read(s->bdrv, sector, iobuf, PAGE_SECTORS + 2) == -1) {
- printf("%s: read error in sector %i\n", __FUNCTION__, sector);
+ printf("%s: read error in sector %" PRIu64 "\n", __func__, sector);
return;
}
- memcpy(iobuf + soff, s->io, s->iolen);
+ mem_and(iobuf + soff, s->io, s->iolen);
if (bdrv_write(s->bdrv, sector, iobuf, PAGE_SECTORS + 2) == -1)
- printf("%s: write error in sector %i\n", __FUNCTION__, sector);
+ printf("%s: write error in sector %" PRIu64 "\n", __func__, sector);
}
s->offset = 0;
}
@@ -576,7 +668,7 @@ static void glue(nand_blk_write_, PAGE_SIZE)(NANDFlashState *s)
/* Erase a single block */
static void glue(nand_blk_erase_, PAGE_SIZE)(NANDFlashState *s)
{
- uint32_t i, page, addr;
+ uint64_t i, page, addr;
uint8_t iobuf[0x200] = { [0 ... 0x1ff] = 0xff, };
addr = s->addr & ~((1 << (ADDR_SHIFT + s->erase_shift)) - 1);
@@ -593,34 +685,35 @@ static void glue(nand_blk_erase_, PAGE_SIZE)(NANDFlashState *s)
page = SECTOR(addr + (ADDR_SHIFT + s->erase_shift));
for (; i < page; i ++)
if (bdrv_write(s->bdrv, i, iobuf, 1) == -1)
- printf("%s: write error in sector %i\n", __FUNCTION__, i);
+ printf("%s: write error in sector %" PRIu64 "\n", __func__, i);
} else {
addr = PAGE_START(addr);
page = addr >> 9;
if (bdrv_read(s->bdrv, page, iobuf, 1) == -1)
- printf("%s: read error in sector %i\n", __FUNCTION__, page);
+ printf("%s: read error in sector %" PRIu64 "\n", __func__, page);
memset(iobuf + (addr & 0x1ff), 0xff, (~addr & 0x1ff) + 1);
if (bdrv_write(s->bdrv, page, iobuf, 1) == -1)
- printf("%s: write error in sector %i\n", __FUNCTION__, page);
+ printf("%s: write error in sector %" PRIu64 "\n", __func__, page);
memset(iobuf, 0xff, 0x200);
i = (addr & ~0x1ff) + 0x200;
for (addr += ((PAGE_SIZE + OOB_SIZE) << s->erase_shift) - 0x200;
i < addr; i += 0x200)
if (bdrv_write(s->bdrv, i >> 9, iobuf, 1) == -1)
- printf("%s: write error in sector %i\n", __FUNCTION__, i >> 9);
+ printf("%s: write error in sector %" PRIu64 "\n",
+ __func__, i >> 9);
page = i >> 9;
if (bdrv_read(s->bdrv, page, iobuf, 1) == -1)
- printf("%s: read error in sector %i\n", __FUNCTION__, page);
+ printf("%s: read error in sector %" PRIu64 "\n", __func__, page);
memset(iobuf, 0xff, ((addr - 1) & 0x1ff) + 1);
if (bdrv_write(s->bdrv, page, iobuf, 1) == -1)
- printf("%s: write error in sector %i\n", __FUNCTION__, page);
+ printf("%s: write error in sector %" PRIu64 "\n", __func__, page);
}
}
static void glue(nand_blk_load_, PAGE_SIZE)(NANDFlashState *s,
- uint32_t addr, int offset)
+ uint64_t addr, int offset)
{
if (PAGE(addr) >= s->pages)
return;
@@ -628,8 +721,8 @@ static void glue(nand_blk_load_, PAGE_SIZE)(NANDFlashState *s,
if (s->bdrv) {
if (s->mem_oob) {
if (bdrv_read(s->bdrv, SECTOR(addr), s->io, PAGE_SECTORS) == -1)
- printf("%s: read error in sector %i\n",
- __FUNCTION__, SECTOR(addr));
+ printf("%s: read error in sector %" PRIu64 "\n",
+ __func__, SECTOR(addr));
memcpy(s->io + SECTOR_OFFSET(s->addr) + PAGE_SIZE,
s->storage + (PAGE(s->addr) << OOB_SHIFT),
OOB_SIZE);
@@ -637,8 +730,8 @@ static void glue(nand_blk_load_, PAGE_SIZE)(NANDFlashState *s,
} else {
if (bdrv_read(s->bdrv, PAGE_START(addr) >> 9,
s->io, (PAGE_SECTORS + 2)) == -1)
- printf("%s: read error in sector %i\n",
- __FUNCTION__, PAGE_START(addr) >> 9);
+ printf("%s: read error in sector %" PRIu64 "\n",
+ __func__, PAGE_START(addr) >> 9);
s->ioaddr = s->io + (PAGE_START(addr) & 0x1ff) + offset;
}
} else {
diff --git a/hw/nseries.c b/hw/nseries.c
index 2f84f53..6a5575e 100644
--- a/hw/nseries.c
+++ b/hw/nseries.c
@@ -31,6 +31,7 @@
#include "hw.h"
#include "bt.h"
#include "loader.h"
+#include "blockdev.h"
/* Nokia N8x0 support */
struct n800_s {
@@ -45,7 +46,7 @@ struct n800_s {
i2c_bus *i2c;
int keymap[0x80];
- i2c_slave *kbd;
+ DeviceState *kbd;
TUSBState *usb;
void *retu;
@@ -134,9 +135,9 @@ static void n800_mmc_cs_cb(void *opaque, int line, int level)
static void n8x0_gpio_setup(struct n800_s *s)
{
qemu_irq *mmc_cs = qemu_allocate_irqs(n800_mmc_cs_cb, s->cpu->mmc, 1);
- omap2_gpio_out_set(s->cpu->gpif, N8X0_MMC_CS_GPIO, mmc_cs[0]);
+ qdev_connect_gpio_out(s->cpu->gpio, N8X0_MMC_CS_GPIO, mmc_cs[0]);
- qemu_irq_lower(omap2_gpio_in_get(s->cpu->gpif, N800_BAT_COVER_GPIO)[0]);
+ qemu_irq_lower(qdev_get_gpio_in(s->cpu->gpio, N800_BAT_COVER_GPIO));
}
#define MAEMO_CAL_HEADER(...) \
@@ -163,13 +164,15 @@ static const uint8_t n8x0_cal_bt_id[] = {
static void n8x0_nand_setup(struct n800_s *s)
{
char *otp_region;
+ DriveInfo *dinfo;
- /* Either ec40xx or ec48xx are OK for the ID */
+ dinfo = drive_get(IF_MTD, 0, 0);
+ /* Either 0x40 or 0x48 are OK for the device ID */
+ s->nand = onenand_init(dinfo ? dinfo->bdrv : 0,
+ NAND_MFR_SAMSUNG, 0x48, 0, 1,
+ qdev_get_gpio_in(s->cpu->gpio, N8X0_ONENAND_GPIO));
omap_gpmc_attach(s->cpu->gpmc, N8X0_ONENAND_CS, 0, onenand_base_update,
- onenand_base_unmap,
- (s->nand = onenand_init(0xec4800, 1,
- omap2_gpio_in_get(s->cpu->gpif,
- N8X0_ONENAND_GPIO)[0])));
+ onenand_base_unmap, s->nand);
otp_region = onenand_raw_otp(s->nand);
memcpy(otp_region + 0x000, n8x0_cal_wlan_mac, sizeof(n8x0_cal_wlan_mac));
@@ -180,7 +183,7 @@ static void n8x0_nand_setup(struct n800_s *s)
static void n8x0_i2c_setup(struct n800_s *s)
{
DeviceState *dev;
- qemu_irq tmp_irq = omap2_gpio_in_get(s->cpu->gpif, N8X0_TMP105_GPIO)[0];
+ qemu_irq tmp_irq = qdev_get_gpio_in(s->cpu->gpio, N8X0_TMP105_GPIO);
/* Attach the CPU on one end of our I2C bus. */
s->i2c = omap_i2c_bus(s->cpu->i2c[0]);
@@ -249,8 +252,8 @@ static void n800_tsc_kbd_setup(struct n800_s *s)
/* XXX: are the three pins inverted inside the chip between the
* tsc and the cpu (N4111)? */
qemu_irq penirq = NULL; /* NC */
- qemu_irq kbirq = omap2_gpio_in_get(s->cpu->gpif, N800_TSC_KP_IRQ_GPIO)[0];
- qemu_irq dav = omap2_gpio_in_get(s->cpu->gpif, N800_TSC_TS_GPIO)[0];
+ qemu_irq kbirq = qdev_get_gpio_in(s->cpu->gpio, N800_TSC_KP_IRQ_GPIO);
+ qemu_irq dav = qdev_get_gpio_in(s->cpu->gpio, N800_TSC_TS_GPIO);
s->ts.chip = tsc2301_init(penirq, kbirq, dav);
s->ts.opaque = s->ts.chip->opaque;
@@ -269,7 +272,7 @@ static void n800_tsc_kbd_setup(struct n800_s *s)
static void n810_tsc_setup(struct n800_s *s)
{
- qemu_irq pintdav = omap2_gpio_in_get(s->cpu->gpif, N810_TSC_TS_GPIO)[0];
+ qemu_irq pintdav = qdev_get_gpio_in(s->cpu->gpio, N810_TSC_TS_GPIO);
s->ts.opaque = tsc2005_init(pintdav);
s->ts.txrx = tsc2005_txrx;
@@ -361,8 +364,7 @@ static int n810_keys[0x80] = {
static void n810_kbd_setup(struct n800_s *s)
{
- qemu_irq kbd_irq = omap2_gpio_in_get(s->cpu->gpif, N810_KEYBOARD_GPIO)[0];
- DeviceState *dev;
+ qemu_irq kbd_irq = qdev_get_gpio_in(s->cpu->gpio, N810_KEYBOARD_GPIO);
int i;
for (i = 0; i < 0x80; i ++)
@@ -375,8 +377,8 @@ static void n810_kbd_setup(struct n800_s *s)
/* Attach the LM8322 keyboard to the I2C bus,
* should happen in n8x0_i2c_setup and s->kbd be initialised here. */
- dev = i2c_create_slave(s->i2c, "lm8323", N810_LM8323_ADDR);
- qdev_connect_gpio_out(dev, 0, kbd_irq);
+ s->kbd = i2c_create_slave(s->i2c, "lm8323", N810_LM8323_ADDR);
+ qdev_connect_gpio_out(s->kbd, 0, kbd_irq);
}
/* LCD MIPI DBI-C controller (URAL) */
@@ -726,15 +728,15 @@ static void n8x0_dss_setup(struct n800_s *s)
static void n8x0_cbus_setup(struct n800_s *s)
{
- qemu_irq dat_out = omap2_gpio_in_get(s->cpu->gpif, N8X0_CBUS_DAT_GPIO)[0];
- qemu_irq retu_irq = omap2_gpio_in_get(s->cpu->gpif, N8X0_RETU_GPIO)[0];
- qemu_irq tahvo_irq = omap2_gpio_in_get(s->cpu->gpif, N8X0_TAHVO_GPIO)[0];
+ qemu_irq dat_out = qdev_get_gpio_in(s->cpu->gpio, N8X0_CBUS_DAT_GPIO);
+ qemu_irq retu_irq = qdev_get_gpio_in(s->cpu->gpio, N8X0_RETU_GPIO);
+ qemu_irq tahvo_irq = qdev_get_gpio_in(s->cpu->gpio, N8X0_TAHVO_GPIO);
CBus *cbus = cbus_init(dat_out);
- omap2_gpio_out_set(s->cpu->gpif, N8X0_CBUS_CLK_GPIO, cbus->clk);
- omap2_gpio_out_set(s->cpu->gpif, N8X0_CBUS_DAT_GPIO, cbus->dat);
- omap2_gpio_out_set(s->cpu->gpif, N8X0_CBUS_SEL_GPIO, cbus->sel);
+ qdev_connect_gpio_out(s->cpu->gpio, N8X0_CBUS_CLK_GPIO, cbus->clk);
+ qdev_connect_gpio_out(s->cpu->gpio, N8X0_CBUS_DAT_GPIO, cbus->dat);
+ qdev_connect_gpio_out(s->cpu->gpio, N8X0_CBUS_SEL_GPIO, cbus->sel);
cbus_attach(cbus, s->retu = retu_init(retu_irq, 1));
cbus_attach(cbus, s->tahvo = tahvo_init(tahvo_irq, 1));
@@ -743,12 +745,11 @@ static void n8x0_cbus_setup(struct n800_s *s)
static void n8x0_uart_setup(struct n800_s *s)
{
CharDriverState *radio = uart_hci_init(
- omap2_gpio_in_get(s->cpu->gpif,
- N8X0_BT_HOST_WKUP_GPIO)[0]);
+ qdev_get_gpio_in(s->cpu->gpio, N8X0_BT_HOST_WKUP_GPIO));
- omap2_gpio_out_set(s->cpu->gpif, N8X0_BT_RESET_GPIO,
+ qdev_connect_gpio_out(s->cpu->gpio, N8X0_BT_RESET_GPIO,
csrhci_pins_get(radio)[csrhci_pin_reset]);
- omap2_gpio_out_set(s->cpu->gpif, N8X0_BT_WKUP_GPIO,
+ qdev_connect_gpio_out(s->cpu->gpio, N8X0_BT_WKUP_GPIO,
csrhci_pins_get(radio)[csrhci_pin_wakeup]);
omap_uart_attach(s->cpu->uart[BT_UART], radio);
@@ -763,7 +764,7 @@ static void n8x0_usb_power_cb(void *opaque, int line, int level)
static void n8x0_usb_setup(struct n800_s *s)
{
- qemu_irq tusb_irq = omap2_gpio_in_get(s->cpu->gpif, N8X0_TUSB_INT_GPIO)[0];
+ qemu_irq tusb_irq = qdev_get_gpio_in(s->cpu->gpio, N8X0_TUSB_INT_GPIO);
qemu_irq tusb_pwr = qemu_allocate_irqs(n8x0_usb_power_cb, s, 1)[0];
TUSBState *tusb = tusb6010_init(tusb_irq);
@@ -774,7 +775,7 @@ static void n8x0_usb_setup(struct n800_s *s)
tusb6010_sync_io(tusb), NULL, NULL, tusb);
s->usb = tusb;
- omap2_gpio_out_set(s->cpu->gpif, N8X0_TUSB_ENABLE_GPIO, tusb_pwr);
+ qdev_connect_gpio_out(s->cpu->gpio, N8X0_TUSB_ENABLE_GPIO, tusb_pwr);
}
/* Setup done before the main bootloader starts by some early setup code
@@ -1020,7 +1021,7 @@ static void n8x0_boot_init(void *opaque)
/* If the machine has a slided keyboard, open it */
if (s->kbd)
- qemu_irq_raise(omap2_gpio_in_get(s->cpu->gpif, N810_SLIDE_GPIO)[0]);
+ qemu_irq_raise(qdev_get_gpio_in(s->cpu->gpio, N810_SLIDE_GPIO));
}
#define OMAP_TAG_NOKIA_BT 0x4e01
diff --git a/hw/omap.h b/hw/omap.h
index c227a82..a064353 100644
--- a/hw/omap.h
+++ b/hw/omap.h
@@ -93,6 +93,8 @@ struct omap_target_agent_s *omap_l4ta_get(
int cs);
target_phys_addr_t omap_l4_attach(struct omap_target_agent_s *ta, int region,
int iotype);
+target_phys_addr_t omap_l4_region_base(struct omap_target_agent_s *ta,
+ int region);
int l4_register_io_memory(CPUReadMemoryFunc * const *mem_read,
CPUWriteMemoryFunc * const *mem_write, void *opaque);
@@ -681,22 +683,6 @@ qemu_irq *omap_mpuio_in_get(struct omap_mpuio_s *s);
void omap_mpuio_out_set(struct omap_mpuio_s *s, int line, qemu_irq handler);
void omap_mpuio_key(struct omap_mpuio_s *s, int row, int col, int down);
-/* omap1 gpio module interface */
-struct omap_gpio_s;
-struct omap_gpio_s *omap_gpio_init(target_phys_addr_t base,
- qemu_irq irq, omap_clk clk);
-void omap_gpio_reset(struct omap_gpio_s *s);
-qemu_irq *omap_gpio_in_get(struct omap_gpio_s *s);
-void omap_gpio_out_set(struct omap_gpio_s *s, int line, qemu_irq handler);
-
-/* omap2 gpio interface */
-struct omap_gpif_s;
-struct omap_gpif_s *omap2_gpio_init(struct omap_target_agent_s *ta,
- qemu_irq *irq, omap_clk *fclk, omap_clk iclk, int modules);
-void omap_gpif_reset(struct omap_gpif_s *s);
-qemu_irq *omap2_gpio_in_get(struct omap_gpif_s *s, int start);
-void omap2_gpio_out_set(struct omap_gpif_s *s, int line, qemu_irq handler);
-
struct uWireSlave {
uint16_t (*receive)(void *opaque);
void (*send)(void *opaque, uint16_t data);
@@ -850,7 +836,7 @@ struct omap_mpu_state_s {
/* MPUI-TIPB peripherals */
struct omap_uart_s *uart[3];
- struct omap_gpio_s *gpio;
+ DeviceState *gpio;
struct omap_mcbsp_s *mcbsp1;
struct omap_mcbsp_s *mcbsp3;
@@ -948,8 +934,6 @@ struct omap_mpu_state_s {
struct omap_gpmc_s *gpmc;
struct omap_sysctl_s *sysc;
- struct omap_gpif_s *gpif;
-
struct omap_mcspi_s *mcspi[2];
struct omap_dss_s *dss;
diff --git a/hw/omap1.c b/hw/omap1.c
index 364c26f..400de47 100644
--- a/hw/omap1.c
+++ b/hw/omap1.c
@@ -27,6 +27,7 @@
#include "pc.h"
#include "blockdev.h"
#include "range.h"
+#include "sysbus.h"
/* Should signal the TCMI/GPMC */
uint32_t omap_badwidth_read8(void *opaque, target_phys_addr_t addr)
@@ -3585,7 +3586,6 @@ static void omap1_mpu_reset(void *opaque)
omap_uart_reset(mpu->uart[2]);
omap_mmc_reset(mpu->mmc);
omap_mpuio_reset(mpu->mpuio);
- omap_gpio_reset(mpu->gpio);
omap_uwire_reset(mpu->microwire);
omap_pwl_reset(mpu);
omap_pwt_reset(mpu);
@@ -3845,8 +3845,12 @@ struct omap_mpu_state_s *omap310_mpu_init(unsigned long sdram_size,
s->irq[1][OMAP_INT_KEYBOARD], s->irq[1][OMAP_INT_MPUIO],
s->wakeup, omap_findclk(s, "clk32-kHz"));
- s->gpio = omap_gpio_init(0xfffce000, s->irq[0][OMAP_INT_GPIO_BANK1],
- omap_findclk(s, "arm_gpio_ck"));
+ s->gpio = qdev_create(NULL, "omap-gpio");
+ qdev_prop_set_int32(s->gpio, "mpu_model", s->mpu_model);
+ qdev_init_nofail(s->gpio);
+ sysbus_connect_irq(sysbus_from_qdev(s->gpio), 0,
+ s->irq[0][OMAP_INT_GPIO_BANK1]);
+ sysbus_mmio_map(sysbus_from_qdev(s->gpio), 0, 0xfffce000);
s->microwire = omap_uwire_init(0xfffb3000, &s->irq[1][OMAP_INT_uWireTX],
s->drq[OMAP_DMA_UWIRE_TX], omap_findclk(s, "mpuper_ck"));
diff --git a/hw/omap2.c b/hw/omap2.c
index 0f13272..c9b3540 100644
--- a/hw/omap2.c
+++ b/hw/omap2.c
@@ -27,6 +27,7 @@
#include "qemu-char.h"
#include "flash.h"
#include "soc_dma.h"
+#include "sysbus.h"
#include "audio/audio.h"
/* Enhanced Audio Controller (CODEC only) */
@@ -2203,7 +2204,6 @@ static void omap2_mpu_reset(void *opaque)
omap_uart_reset(mpu->uart[1]);
omap_uart_reset(mpu->uart[2]);
omap_mmc_reset(mpu->mmc);
- omap_gpif_reset(mpu->gpif);
omap_mcspi_reset(mpu->mcspi[0]);
omap_mcspi_reset(mpu->mcspi[1]);
omap_i2c_reset(mpu->i2c[0]);
@@ -2232,9 +2232,10 @@ struct omap_mpu_state_s *omap2420_mpu_init(unsigned long sdram_size,
ram_addr_t sram_base, q2_base;
qemu_irq *cpu_irq;
qemu_irq dma_irqs[4];
- omap_clk gpio_clks[4];
DriveInfo *dinfo;
int i;
+ SysBusDevice *busdev;
+ struct omap_target_agent_s *ta;
/* Core */
s->mpu_model = omap2420;
@@ -2377,13 +2378,28 @@ struct omap_mpu_state_s *omap2420_mpu_init(unsigned long sdram_size,
omap_findclk(s, "i2c2.fclk"),
omap_findclk(s, "i2c2.iclk"));
- gpio_clks[0] = omap_findclk(s, "gpio1_dbclk");
- gpio_clks[1] = omap_findclk(s, "gpio2_dbclk");
- gpio_clks[2] = omap_findclk(s, "gpio3_dbclk");
- gpio_clks[3] = omap_findclk(s, "gpio4_dbclk");
- s->gpif = omap2_gpio_init(omap_l4ta(s->l4, 3),
- &s->irq[0][OMAP_INT_24XX_GPIO_BANK1],
- gpio_clks, omap_findclk(s, "gpio_iclk"), 4);
+ s->gpio = qdev_create(NULL, "omap2-gpio");
+ qdev_prop_set_int32(s->gpio, "mpu_model", s->mpu_model);
+ qdev_prop_set_ptr(s->gpio, "iclk", omap_findclk(s, "gpio_iclk"));
+ qdev_prop_set_ptr(s->gpio, "fclk0", omap_findclk(s, "gpio1_dbclk"));
+ qdev_prop_set_ptr(s->gpio, "fclk1", omap_findclk(s, "gpio2_dbclk"));
+ qdev_prop_set_ptr(s->gpio, "fclk2", omap_findclk(s, "gpio3_dbclk"));
+ qdev_prop_set_ptr(s->gpio, "fclk3", omap_findclk(s, "gpio4_dbclk"));
+ if (s->mpu_model == omap2430) {
+ qdev_prop_set_ptr(s->gpio, "fclk4", omap_findclk(s, "gpio5_dbclk"));
+ }
+ qdev_init_nofail(s->gpio);
+ busdev = sysbus_from_qdev(s->gpio);
+ sysbus_connect_irq(busdev, 0, s->irq[0][OMAP_INT_24XX_GPIO_BANK1]);
+ sysbus_connect_irq(busdev, 3, s->irq[0][OMAP_INT_24XX_GPIO_BANK2]);
+ sysbus_connect_irq(busdev, 6, s->irq[0][OMAP_INT_24XX_GPIO_BANK3]);
+ sysbus_connect_irq(busdev, 9, s->irq[0][OMAP_INT_24XX_GPIO_BANK4]);
+ ta = omap_l4ta(s->l4, 3);
+ sysbus_mmio_map(busdev, 0, omap_l4_region_base(ta, 1));
+ sysbus_mmio_map(busdev, 1, omap_l4_region_base(ta, 0));
+ sysbus_mmio_map(busdev, 2, omap_l4_region_base(ta, 2));
+ sysbus_mmio_map(busdev, 3, omap_l4_region_base(ta, 4));
+ sysbus_mmio_map(busdev, 4, omap_l4_region_base(ta, 5));
s->sdrc = omap_sdrc_init(0x68009000);
s->gpmc = omap_gpmc_init(0x6800a000, s->irq[0][OMAP_INT_24XX_GPMC_IRQ]);
diff --git a/hw/omap_clk.c b/hw/omap_clk.c
index 6bcabef..577b326 100644
--- a/hw/omap_clk.c
+++ b/hw/omap_clk.c
@@ -836,7 +836,7 @@ static struct clk i2c2_iclk = {
.parent = &core_l4_iclk,
};
-static struct clk gpio_dbclk[4] = {
+static struct clk gpio_dbclk[5] = {
{
.name = "gpio1_dbclk",
.flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
@@ -853,6 +853,10 @@ static struct clk gpio_dbclk[4] = {
.name = "gpio4_dbclk",
.flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
.parent = &wu_32k_clk,
+ }, {
+ .name = "gpio5_dbclk",
+ .flags = CLOCK_IN_OMAP243X,
+ .parent = &wu_32k_clk,
},
};
diff --git a/hw/omap_gpio.c b/hw/omap_gpio.c
index 478f7d9..c23964c 100644
--- a/hw/omap_gpio.c
+++ b/hw/omap_gpio.c
@@ -20,10 +20,10 @@
#include "hw.h"
#include "omap.h"
-/* General-Purpose I/O */
+#include "sysbus.h"
+
struct omap_gpio_s {
qemu_irq irq;
- qemu_irq *in;
qemu_irq handler[16];
uint16_t inputs;
@@ -35,9 +35,17 @@ struct omap_gpio_s {
uint16_t pins;
};
+struct omap_gpif_s {
+ SysBusDevice busdev;
+ int mpu_model;
+ void *clk;
+ struct omap_gpio_s omap1;
+};
+
+/* General-Purpose I/O of OMAP1 */
static void omap_gpio_set(void *opaque, int line, int level)
{
- struct omap_gpio_s *s = (struct omap_gpio_s *) opaque;
+ struct omap_gpio_s *s = &((struct omap_gpif_s *) opaque)->omap1;
uint16_t prev = s->inputs;
if (level)
@@ -160,7 +168,7 @@ static CPUWriteMemoryFunc * const omap_gpio_writefn[] = {
omap_badwidth_write16,
};
-void omap_gpio_reset(struct omap_gpio_s *s)
+static void omap_gpio_reset(struct omap_gpio_s *s)
{
s->inputs = 0;
s->outputs = ~0;
@@ -171,43 +179,12 @@ void omap_gpio_reset(struct omap_gpio_s *s)
s->pins = ~0;
}
-struct omap_gpio_s *omap_gpio_init(target_phys_addr_t base,
- qemu_irq irq, omap_clk clk)
-{
- int iomemtype;
- struct omap_gpio_s *s = (struct omap_gpio_s *)
- qemu_mallocz(sizeof(struct omap_gpio_s));
-
- s->irq = irq;
- s->in = qemu_allocate_irqs(omap_gpio_set, s, 16);
- omap_gpio_reset(s);
-
- iomemtype = cpu_register_io_memory(omap_gpio_readfn,
- omap_gpio_writefn, s, DEVICE_NATIVE_ENDIAN);
- cpu_register_physical_memory(base, 0x1000, iomemtype);
-
- return s;
-}
-
-qemu_irq *omap_gpio_in_get(struct omap_gpio_s *s)
-{
- return s->in;
-}
-
-void omap_gpio_out_set(struct omap_gpio_s *s, int line, qemu_irq handler)
-{
- if (line >= 16 || line < 0)
- hw_error("%s: No GPIO line %i\n", __FUNCTION__, line);
- s->handler[line] = handler;
-}
-
-/* General-Purpose Interface of OMAP2 */
struct omap2_gpio_s {
qemu_irq irq[2];
qemu_irq wkup;
- qemu_irq *in;
- qemu_irq handler[32];
+ qemu_irq *handler;
+ uint8_t revision;
uint8_t config[2];
uint32_t inputs;
uint32_t outputs;
@@ -221,8 +198,21 @@ struct omap2_gpio_s {
uint8_t delay;
};
+struct omap2_gpif_s {
+ SysBusDevice busdev;
+ int mpu_model;
+ void *iclk;
+ void *fclk[6];
+ int modulecount;
+ struct omap2_gpio_s *modules;
+ qemu_irq *handler;
+ int autoidle;
+ int gpo;
+};
+
+/* General-Purpose Interface of OMAP2/3 */
static inline void omap2_gpio_module_int_update(struct omap2_gpio_s *s,
- int line)
+ int line)
{
qemu_set_irq(s->irq[line], s->ints[line] & s->mask[line]);
}
@@ -269,10 +259,12 @@ static inline void omap2_gpio_module_int(struct omap2_gpio_s *s, int line)
omap2_gpio_module_wake(s, line);
}
-static void omap2_gpio_module_set(void *opaque, int line, int level)
+static void omap2_gpio_set(void *opaque, int line, int level)
{
- struct omap2_gpio_s *s = (struct omap2_gpio_s *) opaque;
+ struct omap2_gpif_s *p = opaque;
+ struct omap2_gpio_s *s = &p->modules[line >> 5];
+ line &= 31;
if (level) {
if (s->dir & (1 << line) & ((~s->inputs & s->edge[0]) | s->level[1]))
omap2_gpio_module_int(s, line);
@@ -308,7 +300,7 @@ static uint32_t omap2_gpio_module_read(void *opaque, target_phys_addr_t addr)
switch (addr) {
case 0x00: /* GPIO_REVISION */
- return 0x18;
+ return s->revision;
case 0x10: /* GPIO_SYSCONFIG */
return s->config[0];
@@ -583,45 +575,28 @@ static CPUWriteMemoryFunc * const omap2_gpio_module_writefn[] = {
omap2_gpio_module_write,
};
-static void omap2_gpio_module_init(struct omap2_gpio_s *s,
- struct omap_target_agent_s *ta, int region,
- qemu_irq mpu, qemu_irq dsp, qemu_irq wkup,
- omap_clk fclk, omap_clk iclk)
+static void omap_gpif_reset(DeviceState *dev)
{
- int iomemtype;
-
- s->irq[0] = mpu;
- s->irq[1] = dsp;
- s->wkup = wkup;
- s->in = qemu_allocate_irqs(omap2_gpio_module_set, s, 32);
-
- iomemtype = l4_register_io_memory(omap2_gpio_module_readfn,
- omap2_gpio_module_writefn, s);
- omap_l4_attach(ta, region, iomemtype);
+ struct omap_gpif_s *s = FROM_SYSBUS(struct omap_gpif_s,
+ sysbus_from_qdev(dev));
+ omap_gpio_reset(&s->omap1);
}
-struct omap_gpif_s {
- struct omap2_gpio_s module[5];
- int modules;
-
- int autoidle;
- int gpo;
-};
-
-void omap_gpif_reset(struct omap_gpif_s *s)
+static void omap2_gpif_reset(DeviceState *dev)
{
int i;
-
- for (i = 0; i < s->modules; i ++)
- omap2_gpio_module_reset(s->module + i);
-
+ struct omap2_gpif_s *s = FROM_SYSBUS(struct omap2_gpif_s,
+ sysbus_from_qdev(dev));
+ for (i = 0; i < s->modulecount; i++) {
+ omap2_gpio_module_reset(&s->modules[i]);
+ }
s->autoidle = 0;
s->gpo = 0;
}
-static uint32_t omap_gpif_top_read(void *opaque, target_phys_addr_t addr)
+static uint32_t omap2_gpif_top_read(void *opaque, target_phys_addr_t addr)
{
- struct omap_gpif_s *s = (struct omap_gpif_s *) opaque;
+ struct omap2_gpif_s *s = (struct omap2_gpif_s *) opaque;
switch (addr) {
case 0x00: /* IPGENERICOCPSPL_REVISION */
@@ -647,10 +622,10 @@ static uint32_t omap_gpif_top_read(void *opaque, target_phys_addr_t addr)
return 0;
}
-static void omap_gpif_top_write(void *opaque, target_phys_addr_t addr,
+static void omap2_gpif_top_write(void *opaque, target_phys_addr_t addr,
uint32_t value)
{
- struct omap_gpif_s *s = (struct omap_gpif_s *) opaque;
+ struct omap2_gpif_s *s = (struct omap2_gpif_s *) opaque;
switch (addr) {
case 0x00: /* IPGENERICOCPSPL_REVISION */
@@ -662,7 +637,7 @@ static void omap_gpif_top_write(void *opaque, target_phys_addr_t addr,
case 0x10: /* IPGENERICOCPSPL_SYSCONFIG */
if (value & (1 << 1)) /* SOFTRESET */
- omap_gpif_reset(s);
+ omap2_gpif_reset(&s->busdev.qdev);
s->autoidle = value & 1;
break;
@@ -676,50 +651,119 @@ static void omap_gpif_top_write(void *opaque, target_phys_addr_t addr,
}
}
-static CPUReadMemoryFunc * const omap_gpif_top_readfn[] = {
- omap_gpif_top_read,
- omap_gpif_top_read,
- omap_gpif_top_read,
+static CPUReadMemoryFunc * const omap2_gpif_top_readfn[] = {
+ omap2_gpif_top_read,
+ omap2_gpif_top_read,
+ omap2_gpif_top_read,
};
-static CPUWriteMemoryFunc * const omap_gpif_top_writefn[] = {
- omap_gpif_top_write,
- omap_gpif_top_write,
- omap_gpif_top_write,
+static CPUWriteMemoryFunc * const omap2_gpif_top_writefn[] = {
+ omap2_gpif_top_write,
+ omap2_gpif_top_write,
+ omap2_gpif_top_write,
};
-struct omap_gpif_s *omap2_gpio_init(struct omap_target_agent_s *ta,
- qemu_irq *irq, omap_clk *fclk, omap_clk iclk, int modules)
+static int omap_gpio_init(SysBusDevice *dev)
{
- int iomemtype, i;
- struct omap_gpif_s *s = (struct omap_gpif_s *)
- qemu_mallocz(sizeof(struct omap_gpif_s));
- int region[4] = { 0, 2, 4, 5 };
+ struct omap_gpif_s *s = FROM_SYSBUS(struct omap_gpif_s, dev);
+ if (!s->clk) {
+ hw_error("omap-gpio: clk not connected\n");
+ }
+ qdev_init_gpio_in(&dev->qdev, omap_gpio_set, 16);
+ qdev_init_gpio_out(&dev->qdev, s->omap1.handler, 16);
+ sysbus_init_irq(dev, &s->omap1.irq);
+ sysbus_init_mmio(dev, 0x1000,
+ cpu_register_io_memory(omap_gpio_readfn,
+ omap_gpio_writefn,
+ &s->omap1,
+ DEVICE_NATIVE_ENDIAN));
+ return 0;
+}
- s->modules = modules;
- for (i = 0; i < modules; i ++)
- omap2_gpio_module_init(s->module + i, ta, region[i],
- irq[i], NULL, NULL, fclk[i], iclk);
+static int omap2_gpio_init(SysBusDevice *dev)
+{
+ int i;
+ struct omap2_gpif_s *s = FROM_SYSBUS(struct omap2_gpif_s, dev);
+ if (!s->iclk) {
+ hw_error("omap2-gpio: iclk not connected\n");
+ }
+ if (s->mpu_model < omap3430) {
+ s->modulecount = (s->mpu_model < omap2430) ? 4 : 5;
+ sysbus_init_mmio(dev, 0x1000,
+ cpu_register_io_memory(omap2_gpif_top_readfn,
+ omap2_gpif_top_writefn, s,
+ DEVICE_NATIVE_ENDIAN));
+ } else {
+ s->modulecount = 6;
+ }
+ s->modules = qemu_mallocz(s->modulecount * sizeof(struct omap2_gpio_s));
+ s->handler = qemu_mallocz(s->modulecount * 32 * sizeof(qemu_irq));
+ qdev_init_gpio_in(&dev->qdev, omap2_gpio_set, s->modulecount * 32);
+ qdev_init_gpio_out(&dev->qdev, s->handler, s->modulecount * 32);
+ for (i = 0; i < s->modulecount; i++) {
+ struct omap2_gpio_s *m = &s->modules[i];
+ if (!s->fclk[i]) {
+ hw_error("omap2-gpio: fclk%d not connected\n", i);
+ }
+ m->revision = (s->mpu_model < omap3430) ? 0x18 : 0x25;
+ m->handler = &s->handler[i * 32];
+ sysbus_init_irq(dev, &m->irq[0]); /* mpu irq */
+ sysbus_init_irq(dev, &m->irq[1]); /* dsp irq */
+ sysbus_init_irq(dev, &m->wkup);
+ sysbus_init_mmio(dev, 0x1000,
+ cpu_register_io_memory(omap2_gpio_module_readfn,
+ omap2_gpio_module_writefn,
+ m, DEVICE_NATIVE_ENDIAN));
+ }
+ return 0;
+}
- omap_gpif_reset(s);
+/* Using qdev pointer properties for the clocks is not ideal.
+ * qdev should support a generic means of defining a 'port' with
+ * an arbitrary interface for connecting two devices. Then we
+ * could reframe the omap clock API in terms of clock ports,
+ * and get some type safety. For now the best qdev provides is
+ * passing an arbitrary pointer.
+ * (It's not possible to pass in the string which is the clock
+ * name, because this device does not have the necessary information
+ * (ie the struct omap_mpu_state_s*) to do the clockname to pointer
+ * translation.)
+ */
- iomemtype = l4_register_io_memory(omap_gpif_top_readfn,
- omap_gpif_top_writefn, s);
- omap_l4_attach(ta, 1, iomemtype);
+static SysBusDeviceInfo omap_gpio_info = {
+ .init = omap_gpio_init,
+ .qdev.name = "omap-gpio",
+ .qdev.size = sizeof(struct omap_gpif_s),
+ .qdev.reset = omap_gpif_reset,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_INT32("mpu_model", struct omap_gpif_s, mpu_model, 0),
+ DEFINE_PROP_PTR("clk", struct omap_gpif_s, clk),
+ DEFINE_PROP_END_OF_LIST()
+ }
+};
- return s;
-}
+static SysBusDeviceInfo omap2_gpio_info = {
+ .init = omap2_gpio_init,
+ .qdev.name = "omap2-gpio",
+ .qdev.size = sizeof(struct omap2_gpif_s),
+ .qdev.reset = omap2_gpif_reset,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_INT32("mpu_model", struct omap2_gpif_s, mpu_model, 0),
+ DEFINE_PROP_PTR("iclk", struct omap2_gpif_s, iclk),
+ DEFINE_PROP_PTR("fclk0", struct omap2_gpif_s, fclk[0]),
+ DEFINE_PROP_PTR("fclk1", struct omap2_gpif_s, fclk[1]),
+ DEFINE_PROP_PTR("fclk2", struct omap2_gpif_s, fclk[2]),
+ DEFINE_PROP_PTR("fclk3", struct omap2_gpif_s, fclk[3]),
+ DEFINE_PROP_PTR("fclk4", struct omap2_gpif_s, fclk[4]),
+ DEFINE_PROP_PTR("fclk5", struct omap2_gpif_s, fclk[5]),
+ DEFINE_PROP_END_OF_LIST()
+ }
+};
-qemu_irq *omap2_gpio_in_get(struct omap_gpif_s *s, int start)
+static void omap_gpio_register_device(void)
{
- if (start >= s->modules * 32 || start < 0)
- hw_error("%s: No GPIO line %i\n", __FUNCTION__, start);
- return s->module[start >> 5].in + (start & 31);
+ sysbus_register_withprop(&omap_gpio_info);
+ sysbus_register_withprop(&omap2_gpio_info);
}
-void omap2_gpio_out_set(struct omap_gpif_s *s, int line, qemu_irq handler)
-{
- if (line >= s->modules * 32 || line < 0)
- hw_error("%s: No GPIO line %i\n", __FUNCTION__, line);
- s->module[line >> 5].handler[line & 31] = handler;
-}
+device_init(omap_gpio_register_device)
diff --git a/hw/omap_l4.c b/hw/omap_l4.c
index 4af0ca8..59c84b1 100644
--- a/hw/omap_l4.c
+++ b/hw/omap_l4.c
@@ -146,6 +146,12 @@ struct omap_l4_s *omap_l4_init(target_phys_addr_t base, int ta_num)
return bus;
}
+target_phys_addr_t omap_l4_region_base(struct omap_target_agent_s *ta,
+ int region)
+{
+ return ta->bus->base + ta->start[region].offset;
+}
+
static uint32_t omap_l4ta_read(void *opaque, target_phys_addr_t addr)
{
struct omap_target_agent_s *s = (struct omap_target_agent_s *) opaque;
diff --git a/hw/onenand.c b/hw/onenand.c
index 71c1ab4..b0cbebc 100644
--- a/hw/onenand.c
+++ b/hw/onenand.c
@@ -31,7 +31,11 @@
#define BLOCK_SHIFT (PAGE_SHIFT + 6)
typedef struct {
- uint32_t id;
+ struct {
+ uint16_t man;
+ uint16_t dev;
+ uint16_t ver;
+ } id;
int shift;
target_phys_addr_t base;
qemu_irq intr;
@@ -175,14 +179,39 @@ static inline int onenand_load_main(OneNANDState *s, int sec, int secn,
static inline int onenand_prog_main(OneNANDState *s, int sec, int secn,
void *src)
{
- if (s->bdrv_cur)
- return bdrv_write(s->bdrv_cur, sec, src, secn) < 0;
- else if (sec + secn > s->secs_cur)
- return 1;
-
- memcpy(s->current + (sec << 9), src, secn << 9);
+ int result = 0;
+
+ if (secn > 0) {
+ uint32_t size = (uint32_t) secn * 512;
+ const uint8_t *sp = (const uint8_t *) src;
+ uint8_t *dp = 0;
+ if (s->bdrv_cur) {
+ dp = qemu_malloc(size);
+ if (!dp || bdrv_read(s->bdrv_cur, sec, dp, secn) < 0) {
+ result = 1;
+ }
+ } else {
+ if (sec + secn > s->secs_cur) {
+ result = 1;
+ } else {
+ dp = (uint8_t *) s->current + (sec << 9);
+ }
+ }
+ if (!result) {
+ uint32_t i;
+ for (i = 0; i < size; i++) {
+ dp[i] &= sp[i];
+ }
+ if (s->bdrv_cur) {
+ result = bdrv_write(s->bdrv_cur, sec, dp, secn) < 0;
+ }
+ }
+ if (dp && s->bdrv_cur) {
+ qemu_free(dp);
+ }
+ }
- return 0;
+ return result;
}
static inline int onenand_load_spare(OneNANDState *s, int sec, int secn,
@@ -205,35 +234,87 @@ static inline int onenand_load_spare(OneNANDState *s, int sec, int secn,
static inline int onenand_prog_spare(OneNANDState *s, int sec, int secn,
void *src)
{
- uint8_t buf[512];
-
- if (s->bdrv_cur) {
- if (bdrv_read(s->bdrv_cur, s->secs_cur + (sec >> 5), buf, 1) < 0)
- return 1;
- memcpy(buf + ((sec & 31) << 4), src, secn << 4);
- return bdrv_write(s->bdrv_cur, s->secs_cur + (sec >> 5), buf, 1) < 0;
- } else if (sec + secn > s->secs_cur)
- return 1;
-
- memcpy(s->current + (s->secs_cur << 9) + (sec << 4), src, secn << 4);
-
- return 0;
+ int result = 0;
+ if (secn > 0) {
+ const uint8_t *sp = (const uint8_t *) src;
+ uint8_t *dp = 0, *dpp = 0;
+ if (s->bdrv_cur) {
+ dp = qemu_malloc(512);
+ if (!dp || bdrv_read(s->bdrv_cur,
+ s->secs_cur + (sec >> 5),
+ dp, 1) < 0) {
+ result = 1;
+ } else {
+ dpp = dp + ((sec & 31) << 4);
+ }
+ } else {
+ if (sec + secn > s->secs_cur) {
+ result = 1;
+ } else {
+ dpp = s->current + (s->secs_cur << 9) + (sec << 4);
+ }
+ }
+ if (!result) {
+ uint32_t i;
+ for (i = 0; i < (secn << 4); i++) {
+ dpp[i] &= sp[i];
+ }
+ if (s->bdrv_cur) {
+ result = bdrv_write(s->bdrv_cur, s->secs_cur + (sec >> 5),
+ dp, 1) < 0;
+ }
+ }
+ if (dp) {
+ qemu_free(dp);
+ }
+ }
+ return result;
}
static inline int onenand_erase(OneNANDState *s, int sec, int num)
{
- /* TODO: optimise */
- uint8_t buf[512];
-
- memset(buf, 0xff, sizeof(buf));
- for (; num > 0; num --, sec ++) {
- if (onenand_prog_main(s, sec, 1, buf))
- return 1;
- if (onenand_prog_spare(s, sec, 1, buf))
- return 1;
+ uint8_t *blankbuf, *tmpbuf;
+ blankbuf = qemu_malloc(512);
+ if (!blankbuf) {
+ return 1;
+ }
+ tmpbuf = qemu_malloc(512);
+ if (!tmpbuf) {
+ qemu_free(blankbuf);
+ return 1;
+ }
+ memset(blankbuf, 0xff, 512);
+ for (; num > 0; num--, sec++) {
+ if (s->bdrv_cur) {
+ int erasesec = s->secs_cur + (sec >> 5);
+ if (bdrv_write(s->bdrv_cur, sec, blankbuf, 1)) {
+ goto fail;
+ }
+ if (bdrv_read(s->bdrv_cur, erasesec, tmpbuf, 1) < 0) {
+ goto fail;
+ }
+ memcpy(tmpbuf + ((sec & 31) << 4), blankbuf, 1 << 4);
+ if (bdrv_write(s->bdrv_cur, erasesec, tmpbuf, 1) < 0) {
+ goto fail;
+ }
+ } else {
+ if (sec + 1 > s->secs_cur) {
+ goto fail;
+ }
+ memcpy(s->current + (sec << 9), blankbuf, 512);
+ memcpy(s->current + (s->secs_cur << 9) + (sec << 4),
+ blankbuf, 1 << 4);
+ }
}
+ qemu_free(tmpbuf);
+ qemu_free(blankbuf);
return 0;
+
+fail:
+ qemu_free(tmpbuf);
+ qemu_free(blankbuf);
+ return 1;
}
static void onenand_command(OneNANDState *s, int cmd)
@@ -453,12 +534,12 @@ static uint32_t onenand_read(void *opaque, target_phys_addr_t addr)
return lduw_le_p(s->boot[0] + addr);
case 0xf000: /* Manufacturer ID */
- return (s->id >> 16) & 0xff;
+ return s->id.man;
case 0xf001: /* Device ID */
- return (s->id >> 8) & 0xff;
- /* TODO: get the following values from a real chip! */
+ return s->id.dev;
case 0xf002: /* Version ID */
- return (s->id >> 0) & 0xff;
+ return s->id.ver;
+ /* TODO: get the following values from a real chip! */
case 0xf003: /* Data Buffer size */
return 1 << PAGE_SHIFT;
case 0xf004: /* Boot Buffer size */
@@ -541,8 +622,8 @@ static void onenand_write(void *opaque, target_phys_addr_t addr,
case 0x0090: /* Read Identification Data */
memset(s->boot[0], 0, 3 << s->shift);
- s->boot[0][0 << s->shift] = (s->id >> 16) & 0xff;
- s->boot[0][1 << s->shift] = (s->id >> 8) & 0xff;
+ s->boot[0][0 << s->shift] = s->id.man & 0xff;
+ s->boot[0][1 << s->shift] = s->id.dev & 0xff;
s->boot[0][2 << s->shift] = s->wpstatus & 0xff;
break;
@@ -615,28 +696,31 @@ static CPUWriteMemoryFunc * const onenand_writefn[] = {
onenand_write,
};
-void *onenand_init(uint32_t id, int regshift, qemu_irq irq)
+void *onenand_init(BlockDriverState *bdrv,
+ uint16_t man_id, uint16_t dev_id, uint16_t ver_id,
+ int regshift, qemu_irq irq)
{
OneNANDState *s = (OneNANDState *) qemu_mallocz(sizeof(*s));
- DriveInfo *dinfo = drive_get(IF_MTD, 0, 0);
- uint32_t size = 1 << (24 + ((id >> 12) & 7));
+ uint32_t size = 1 << (24 + ((dev_id >> 4) & 7));
void *ram;
s->shift = regshift;
s->intr = irq;
s->rdy = NULL;
- s->id = id;
+ s->id.man = man_id;
+ s->id.dev = dev_id;
+ s->id.ver = ver_id;
s->blocks = size >> BLOCK_SHIFT;
s->secs = size >> 9;
s->blockwp = qemu_malloc(s->blocks);
- s->density_mask = (id & (1 << 11)) ? (1 << (6 + ((id >> 12) & 7))) : 0;
+ s->density_mask = (dev_id & 0x08) ? (1 << (6 + ((dev_id >> 4) & 7))) : 0;
s->iomemtype = cpu_register_io_memory(onenand_readfn,
onenand_writefn, s, DEVICE_NATIVE_ENDIAN);
- if (!dinfo)
+ s->bdrv = bdrv;
+ if (!s->bdrv) {
s->image = memset(qemu_malloc(size + (size >> 5)),
0xff, size + (size >> 5));
- else
- s->bdrv = dinfo->bdrv;
+ }
s->otp = memset(qemu_malloc((64 + 2) << PAGE_SHIFT),
0xff, (64 + 2) << PAGE_SHIFT);
s->ram = qemu_ram_alloc(NULL, "onenand.ram", 0xc000 << s->shift);
diff --git a/hw/palm.c b/hw/palm.c
index f22d777..4c67e75 100644
--- a/hw/palm.c
+++ b/hw/palm.c
@@ -94,7 +94,7 @@ static void palmte_microwire_setup(struct omap_mpu_state_s *cpu)
{
uWireSlave *tsc;
- tsc = tsc2102_init(omap_gpio_in_get(cpu->gpio)[PALMTE_PINTDAV_GPIO]);
+ tsc = tsc2102_init(qdev_get_gpio_in(cpu->gpio, PALMTE_PINTDAV_GPIO));
omap_uwire_attach(cpu->microwire, tsc, 0);
omap_mcbsp_i2s_attach(cpu->mcbsp1, tsc210x_codec(tsc));
@@ -163,24 +163,24 @@ static void palmte_gpio_setup(struct omap_mpu_state_s *cpu)
qemu_irq *misc_gpio;
omap_mmc_handlers(cpu->mmc,
- omap_gpio_in_get(cpu->gpio)[PALMTE_MMC_WP_GPIO],
+ qdev_get_gpio_in(cpu->gpio, PALMTE_MMC_WP_GPIO),
qemu_irq_invert(omap_mpuio_in_get(cpu->mpuio)
[PALMTE_MMC_SWITCH_GPIO]));
misc_gpio = qemu_allocate_irqs(palmte_onoff_gpios, cpu, 7);
- omap_gpio_out_set(cpu->gpio, PALMTE_MMC_POWER_GPIO, misc_gpio[0]);
- omap_gpio_out_set(cpu->gpio, PALMTE_SPEAKER_GPIO, misc_gpio[1]);
- omap_gpio_out_set(cpu->gpio, 11, misc_gpio[2]);
- omap_gpio_out_set(cpu->gpio, 12, misc_gpio[3]);
- omap_gpio_out_set(cpu->gpio, 13, misc_gpio[4]);
- omap_mpuio_out_set(cpu->mpuio, 1, misc_gpio[5]);
- omap_mpuio_out_set(cpu->mpuio, 3, misc_gpio[6]);
+ qdev_connect_gpio_out(cpu->gpio, PALMTE_MMC_POWER_GPIO, misc_gpio[0]);
+ qdev_connect_gpio_out(cpu->gpio, PALMTE_SPEAKER_GPIO, misc_gpio[1]);
+ qdev_connect_gpio_out(cpu->gpio, 11, misc_gpio[2]);
+ qdev_connect_gpio_out(cpu->gpio, 12, misc_gpio[3]);
+ qdev_connect_gpio_out(cpu->gpio, 13, misc_gpio[4]);
+ omap_mpuio_out_set(cpu->mpuio, 1, misc_gpio[5]);
+ omap_mpuio_out_set(cpu->mpuio, 3, misc_gpio[6]);
/* Reset some inputs to initial state. */
- qemu_irq_lower(omap_gpio_in_get(cpu->gpio)[PALMTE_USBDETECT_GPIO]);
- qemu_irq_lower(omap_gpio_in_get(cpu->gpio)[PALMTE_USB_OR_DC_GPIO]);
- qemu_irq_lower(omap_gpio_in_get(cpu->gpio)[4]);
- qemu_irq_lower(omap_gpio_in_get(cpu->gpio)[PALMTE_HEADPHONES_GPIO]);
+ qemu_irq_lower(qdev_get_gpio_in(cpu->gpio, PALMTE_USBDETECT_GPIO));
+ qemu_irq_lower(qdev_get_gpio_in(cpu->gpio, PALMTE_USB_OR_DC_GPIO));
+ qemu_irq_lower(qdev_get_gpio_in(cpu->gpio, 4));
+ qemu_irq_lower(qdev_get_gpio_in(cpu->gpio, PALMTE_HEADPHONES_GPIO));
qemu_irq_lower(omap_mpuio_in_get(cpu->mpuio)[PALMTE_DC_GPIO]);
qemu_irq_raise(omap_mpuio_in_get(cpu->mpuio)[6]);
qemu_irq_raise(omap_mpuio_in_get(cpu->mpuio)[7]);
diff --git a/hw/pc.c b/hw/pc.c
index a3e8539..1c9d89a 100644
--- a/hw/pc.c
+++ b/hw/pc.c
@@ -41,6 +41,7 @@
#include "sysemu.h"
#include "blockdev.h"
#include "ui/qemu-spice.h"
+#include "memory.h"
/* output Bochs bios info messages */
//#define DEBUG_BIOS
@@ -957,7 +958,8 @@ void pc_cpus_init(const char *cpu_model)
}
}
-void pc_memory_init(const char *kernel_filename,
+void pc_memory_init(MemoryRegion *system_memory,
+ const char *kernel_filename,
const char *kernel_cmdline,
const char *initrd_filename,
ram_addr_t below_4g_mem_size,
@@ -965,22 +967,30 @@ void pc_memory_init(const char *kernel_filename,
{
char *filename;
int ret, linux_boot, i;
- ram_addr_t ram_addr, bios_offset, option_rom_offset;
+ MemoryRegion *ram, *bios, *isa_bios, *option_rom_mr;
+ MemoryRegion *ram_below_4g, *ram_above_4g;
int bios_size, isa_bios_size;
void *fw_cfg;
linux_boot = (kernel_filename != NULL);
- /* allocate RAM */
- ram_addr = qemu_ram_alloc(NULL, "pc.ram",
- below_4g_mem_size + above_4g_mem_size);
- cpu_register_physical_memory(0, 0xa0000, ram_addr);
- cpu_register_physical_memory(0x100000,
- below_4g_mem_size - 0x100000,
- ram_addr + 0x100000);
+ /* Allocate RAM. We allocate it as a single memory region and use
+ * aliases to address portions of it, mostly for backwards compatiblity
+ * with older qemus that used qemu_ram_alloc().
+ */
+ ram = qemu_malloc(sizeof(*ram));
+ memory_region_init_ram(ram, NULL, "pc.ram",
+ below_4g_mem_size + above_4g_mem_size);
+ ram_below_4g = qemu_malloc(sizeof(*ram_below_4g));
+ memory_region_init_alias(ram_below_4g, "ram-below-4g", ram,
+ 0, below_4g_mem_size);
+ memory_region_add_subregion(system_memory, 0, ram_below_4g);
if (above_4g_mem_size > 0) {
- cpu_register_physical_memory(0x100000000ULL, above_4g_mem_size,
- ram_addr + below_4g_mem_size);
+ ram_above_4g = qemu_malloc(sizeof(*ram_above_4g));
+ memory_region_init_alias(ram_above_4g, "ram-above-4g", ram,
+ below_4g_mem_size, above_4g_mem_size);
+ memory_region_add_subregion(system_memory, 0x100000000ULL,
+ ram_above_4g);
}
/* BIOS load */
@@ -996,7 +1006,9 @@ void pc_memory_init(const char *kernel_filename,
(bios_size % 65536) != 0) {
goto bios_error;
}
- bios_offset = qemu_ram_alloc(NULL, "pc.bios", bios_size);
+ bios = qemu_malloc(sizeof(*bios));
+ memory_region_init_ram(bios, NULL, "pc.bios", bios_size);
+ memory_region_set_readonly(bios, true);
ret = rom_add_file_fixed(bios_name, (uint32_t)(-bios_size), -1);
if (ret != 0) {
bios_error:
@@ -1010,16 +1022,26 @@ void pc_memory_init(const char *kernel_filename,
isa_bios_size = bios_size;
if (isa_bios_size > (128 * 1024))
isa_bios_size = 128 * 1024;
- cpu_register_physical_memory(0x100000 - isa_bios_size,
- isa_bios_size,
- (bios_offset + bios_size - isa_bios_size) | IO_MEM_ROM);
-
- option_rom_offset = qemu_ram_alloc(NULL, "pc.rom", PC_ROM_SIZE);
- cpu_register_physical_memory(PC_ROM_MIN_VGA, PC_ROM_SIZE, option_rom_offset);
+ isa_bios = qemu_malloc(sizeof(*isa_bios));
+ memory_region_init_alias(isa_bios, "isa-bios", bios,
+ bios_size - isa_bios_size, isa_bios_size);
+ memory_region_add_subregion_overlap(system_memory,
+ 0x100000 - isa_bios_size,
+ isa_bios,
+ 1);
+ memory_region_set_readonly(isa_bios, true);
+
+ option_rom_mr = qemu_malloc(sizeof(*option_rom_mr));
+ memory_region_init_ram(option_rom_mr, NULL, "pc.rom", PC_ROM_SIZE);
+ memory_region_add_subregion_overlap(system_memory,
+ PC_ROM_MIN_VGA,
+ option_rom_mr,
+ 1);
/* map all the bios at the top of memory */
- cpu_register_physical_memory((uint32_t)(-bios_size),
- bios_size, bios_offset | IO_MEM_ROM);
+ memory_region_add_subregion(system_memory,
+ (uint32_t)(-bios_size),
+ bios);
fw_cfg = bochs_bios_init();
rom_set_fw(fw_cfg);
diff --git a/hw/pc.h b/hw/pc.h
index 6d5730b..a2de0fe 100644
--- a/hw/pc.h
+++ b/hw/pc.h
@@ -2,10 +2,12 @@
#define HW_PC_H
#include "qemu-common.h"
+#include "memory.h"
#include "ioport.h"
#include "isa.h"
#include "fdc.h"
#include "net.h"
+#include "memory.h"
/* PC-style peripherals (also used by other machines). */
@@ -129,7 +131,8 @@ void pc_cmos_set_s3_resume(void *opaque, int irq, int level);
void pc_acpi_smi_interrupt(void *opaque, int irq, int level);
void pc_cpus_init(const char *cpu_model);
-void pc_memory_init(const char *kernel_filename,
+void pc_memory_init(MemoryRegion *system_memory,
+ const char *kernel_filename,
const char *kernel_cmdline,
const char *initrd_filename,
ram_addr_t below_4g_mem_size,
@@ -175,7 +178,9 @@ int pcspk_audio_init(qemu_irq *pic);
struct PCII440FXState;
typedef struct PCII440FXState PCII440FXState;
-PCIBus *i440fx_init(PCII440FXState **pi440fx_state, int *piix_devfn, qemu_irq *pic, ram_addr_t ram_size);
+PCIBus *i440fx_init(PCII440FXState **pi440fx_state, int *piix_devfn,
+ qemu_irq *pic, MemoryRegion *address_space,
+ ram_addr_t ram_size);
void i440fx_init_memory_mappings(PCII440FXState *d);
/* piix4.c */
diff --git a/hw/pc_piix.c b/hw/pc_piix.c
index c5c16b4..c0a2abe 100644
--- a/hw/pc_piix.c
+++ b/hw/pc_piix.c
@@ -39,6 +39,8 @@
#include "blockdev.h"
#include "smbus.h"
#include "xen.h"
+#include "memory.h"
+#include "exec-memory.h"
#ifdef CONFIG_XEN
# include <xen/hvm/hvm_info_table.h>
#endif
@@ -66,7 +68,8 @@ static void ioapic_init(IsaIrqState *isa_irq_state)
}
/* PC hardware initialisation */
-static void pc_init1(ram_addr_t ram_size,
+static void pc_init1(MemoryRegion *system_memory,
+ ram_addr_t ram_size,
const char *boot_device,
const char *kernel_filename,
const char *kernel_cmdline,
@@ -106,7 +109,8 @@ static void pc_init1(ram_addr_t ram_size,
/* allocate ram and load rom/bios */
if (!xen_enabled()) {
- pc_memory_init(kernel_filename, kernel_cmdline, initrd_filename,
+ pc_memory_init(system_memory,
+ kernel_filename, kernel_cmdline, initrd_filename,
below_4g_mem_size, above_4g_mem_size);
}
@@ -124,7 +128,8 @@ static void pc_init1(ram_addr_t ram_size,
isa_irq = qemu_allocate_irqs(isa_irq_handler, isa_irq_state, 24);
if (pci_enabled) {
- pci_bus = i440fx_init(&i440fx_state, &piix3_devfn, isa_irq, ram_size);
+ pci_bus = i440fx_init(&i440fx_state, &piix3_devfn, isa_irq,
+ system_memory, ram_size);
} else {
pci_bus = NULL;
i440fx_state = NULL;
@@ -155,7 +160,11 @@ static void pc_init1(ram_addr_t ram_size,
ide_drive_get(hd, MAX_IDE_BUS);
if (pci_enabled) {
PCIDevice *dev;
- dev = pci_piix3_ide_init(pci_bus, hd, piix3_devfn + 1);
+ if (xen_enabled()) {
+ dev = pci_piix3_xen_ide_init(pci_bus, hd, piix3_devfn + 1);
+ } else {
+ dev = pci_piix3_ide_init(pci_bus, hd, piix3_devfn + 1);
+ }
idebus[0] = qdev_get_child_bus(&dev->qdev, "ide.0");
idebus[1] = qdev_get_child_bus(&dev->qdev, "ide.1");
} else {
@@ -208,7 +217,8 @@ static void pc_init_pci(ram_addr_t ram_size,
const char *initrd_filename,
const char *cpu_model)
{
- pc_init1(ram_size, boot_device,
+ pc_init1(get_system_memory(),
+ ram_size, boot_device,
kernel_filename, kernel_cmdline,
initrd_filename, cpu_model, 1, 1);
}
@@ -220,7 +230,8 @@ static void pc_init_pci_no_kvmclock(ram_addr_t ram_size,
const char *initrd_filename,
const char *cpu_model)
{
- pc_init1(ram_size, boot_device,
+ pc_init1(get_system_memory(),
+ ram_size, boot_device,
kernel_filename, kernel_cmdline,
initrd_filename, cpu_model, 1, 0);
}
@@ -234,7 +245,8 @@ static void pc_init_isa(ram_addr_t ram_size,
{
if (cpu_model == NULL)
cpu_model = "486";
- pc_init1(ram_size, boot_device,
+ pc_init1(get_system_memory(),
+ ram_size, boot_device,
kernel_filename, kernel_cmdline,
initrd_filename, cpu_model, 0, 1);
}
diff --git a/hw/pci.c b/hw/pci.c
index ef94739..8621d3d 100644
--- a/hw/pci.c
+++ b/hw/pci.c
@@ -263,11 +263,14 @@ int pci_find_domain(const PCIBus *bus)
}
void pci_bus_new_inplace(PCIBus *bus, DeviceState *parent,
- const char *name, uint8_t devfn_min)
+ const char *name,
+ MemoryRegion *address_space,
+ uint8_t devfn_min)
{
qbus_create_inplace(&bus->qbus, &pci_bus_info, parent, name);
assert(PCI_FUNC(devfn_min) == 0);
bus->devfn_min = devfn_min;
+ bus->address_space = address_space;
/* host bridge */
QLIST_INIT(&bus->child);
@@ -276,13 +279,14 @@ void pci_bus_new_inplace(PCIBus *bus, DeviceState *parent,
vmstate_register(NULL, -1, &vmstate_pcibus, bus);
}
-PCIBus *pci_bus_new(DeviceState *parent, const char *name, uint8_t devfn_min)
+PCIBus *pci_bus_new(DeviceState *parent, const char *name,
+ MemoryRegion *address_space, uint8_t devfn_min)
{
PCIBus *bus;
bus = qemu_mallocz(sizeof(*bus));
bus->qbus.qdev_allocated = 1;
- pci_bus_new_inplace(bus, parent, name, devfn_min);
+ pci_bus_new_inplace(bus, parent, name, address_space, devfn_min);
return bus;
}
@@ -310,11 +314,13 @@ void pci_bus_set_mem_base(PCIBus *bus, target_phys_addr_t base)
PCIBus *pci_register_bus(DeviceState *parent, const char *name,
pci_set_irq_fn set_irq, pci_map_irq_fn map_irq,
- void *irq_opaque, uint8_t devfn_min, int nirq)
+ void *irq_opaque,
+ MemoryRegion *address_space,
+ uint8_t devfn_min, int nirq)
{
PCIBus *bus;
- bus = pci_bus_new(parent, name, devfn_min);
+ bus = pci_bus_new(parent, name, address_space, devfn_min);
pci_bus_irqs(bus, set_irq, map_irq, irq_opaque, nirq);
return bus;
}
@@ -838,10 +844,15 @@ static void pci_unregister_io_regions(PCIDevice *pci_dev)
if (r->type == PCI_BASE_ADDRESS_SPACE_IO) {
isa_unassign_ioport(r->addr, r->filtered_size);
} else {
- cpu_register_physical_memory(pci_to_cpu_addr(pci_dev->bus,
- r->addr),
- r->filtered_size,
- IO_MEM_UNASSIGNED);
+ if (r->memory) {
+ memory_region_del_subregion(pci_dev->bus->address_space,
+ r->memory);
+ } else {
+ cpu_register_physical_memory(pci_to_cpu_addr(pci_dev->bus,
+ r->addr),
+ r->filtered_size,
+ IO_MEM_UNASSIGNED);
+ }
}
}
}
@@ -887,6 +898,7 @@ void pci_register_bar(PCIDevice *pci_dev, int region_num,
r->type = type;
r->map_func = map_func;
r->ram_addr = IO_MEM_UNASSIGNED;
+ r->memory = NULL;
wmask = ~(size - 1);
addr = pci_bar(pci_dev, region_num);
@@ -912,6 +924,16 @@ static void pci_simple_bar_mapfunc(PCIDevice *pci_dev, int region_num,
pci_dev->io_regions[region_num].ram_addr);
}
+static void pci_simple_bar_mapfunc_region(PCIDevice *pci_dev, int region_num,
+ pcibus_t addr, pcibus_t size,
+ int type)
+{
+ memory_region_add_subregion_overlap(pci_dev->bus->address_space,
+ addr,
+ pci_dev->io_regions[region_num].memory,
+ 1);
+}
+
void pci_register_bar_simple(PCIDevice *pci_dev, int region_num,
pcibus_t size, uint8_t attr, ram_addr_t ram_addr)
{
@@ -921,6 +943,15 @@ void pci_register_bar_simple(PCIDevice *pci_dev, int region_num,
pci_dev->io_regions[region_num].ram_addr = ram_addr;
}
+void pci_register_bar_region(PCIDevice *pci_dev, int region_num,
+ uint8_t attr, MemoryRegion *memory)
+{
+ pci_register_bar(pci_dev, region_num, memory_region_size(memory),
+ PCI_BASE_ADDRESS_SPACE_MEMORY | attr,
+ pci_simple_bar_mapfunc_region);
+ pci_dev->io_regions[region_num].memory = memory;
+}
+
static void pci_bridge_filter(PCIDevice *d, pcibus_t *addr, pcibus_t *size,
uint8_t type)
{
@@ -1059,10 +1090,16 @@ static void pci_update_mappings(PCIDevice *d)
isa_unassign_ioport(r->addr, r->filtered_size);
}
} else {
- cpu_register_physical_memory(pci_to_cpu_addr(d->bus, r->addr),
- r->filtered_size,
- IO_MEM_UNASSIGNED);
- qemu_unregister_coalesced_mmio(r->addr, r->filtered_size);
+ if (r->memory) {
+ memory_region_del_subregion(d->bus->address_space,
+ r->memory);
+ } else {
+ cpu_register_physical_memory(pci_to_cpu_addr(d->bus,
+ r->addr),
+ r->filtered_size,
+ IO_MEM_UNASSIGNED);
+ qemu_unregister_coalesced_mmio(r->addr, r->filtered_size);
+ }
}
}
r->addr = new_addr;
diff --git a/hw/pci.h b/hw/pci.h
index c220745..c51156d 100644
--- a/hw/pci.h
+++ b/hw/pci.h
@@ -5,6 +5,7 @@
#include "qobject.h"
#include "qdev.h"
+#include "memory.h"
/* PCI includes legacy ISA access. */
#include "isa.h"
@@ -93,6 +94,7 @@ typedef struct PCIIORegion {
uint8_t type;
PCIMapIORegionFunc *map_func;
ram_addr_t ram_addr;
+ MemoryRegion *memory;
} PCIIORegion;
#define PCI_ROM_SLOT 6
@@ -203,6 +205,8 @@ void pci_register_bar(PCIDevice *pci_dev, int region_num,
PCIMapIORegionFunc *map_func);
void pci_register_bar_simple(PCIDevice *pci_dev, int region_num,
pcibus_t size, uint8_t attr, ram_addr_t ram_addr);
+void pci_register_bar_region(PCIDevice *pci_dev, int region_num,
+ uint8_t attr, MemoryRegion *memory);
int pci_add_capability(PCIDevice *pdev, uint8_t cap_id,
uint8_t offset, uint8_t size);
@@ -233,15 +237,20 @@ typedef enum {
typedef int (*pci_hotplug_fn)(DeviceState *qdev, PCIDevice *pci_dev,
PCIHotplugState state);
void pci_bus_new_inplace(PCIBus *bus, DeviceState *parent,
- const char *name, uint8_t devfn_min);
-PCIBus *pci_bus_new(DeviceState *parent, const char *name, uint8_t devfn_min);
+ const char *name,
+ MemoryRegion *address_space,
+ uint8_t devfn_min);
+PCIBus *pci_bus_new(DeviceState *parent, const char *name,
+ MemoryRegion *address_space, uint8_t devfn_min);
void pci_bus_irqs(PCIBus *bus, pci_set_irq_fn set_irq, pci_map_irq_fn map_irq,
void *irq_opaque, int nirq);
int pci_bus_get_irq_level(PCIBus *bus, int irq_num);
void pci_bus_hotplug(PCIBus *bus, pci_hotplug_fn hotplug, DeviceState *dev);
PCIBus *pci_register_bus(DeviceState *parent, const char *name,
pci_set_irq_fn set_irq, pci_map_irq_fn map_irq,
- void *irq_opaque, uint8_t devfn_min, int nirq);
+ void *irq_opaque,
+ MemoryRegion *address_space,
+ uint8_t devfn_min, int nirq);
void pci_device_reset(PCIDevice *dev);
void pci_bus_reset(PCIBus *bus);
diff --git a/hw/pci_host.h b/hw/pci_host.h
index c8390ee..7f55114 100644
--- a/hw/pci_host.h
+++ b/hw/pci_host.h
@@ -35,6 +35,7 @@ struct PCIHostState {
SysBusDevice busdev;
ReadWriteHandler conf_handler;
ReadWriteHandler data_handler;
+ MemoryRegion *address_space;
uint32_t config_reg;
PCIBus *bus;
};
diff --git a/hw/pci_ids.h b/hw/pci_ids.h
index b49c602..83f3893 100644
--- a/hw/pci_ids.h
+++ b/hw/pci_ids.h
@@ -109,6 +109,14 @@
#define PCI_DEVICE_ID_INTEL_82371AB 0x7111
#define PCI_DEVICE_ID_INTEL_82371AB_2 0x7112
#define PCI_DEVICE_ID_INTEL_82371AB_3 0x7113
+#define PCI_DEVICE_ID_INTEL_82801I_UHCI1 0x2934
+#define PCI_DEVICE_ID_INTEL_82801I_UHCI2 0x2935
+#define PCI_DEVICE_ID_INTEL_82801I_UHCI3 0x2936
+#define PCI_DEVICE_ID_INTEL_82801I_UHCI4 0x2937
+#define PCI_DEVICE_ID_INTEL_82801I_UHCI5 0x2938
+#define PCI_DEVICE_ID_INTEL_82801I_UHCI6 0x2939
+#define PCI_DEVICE_ID_INTEL_82801I_EHCI1 0x293a
+#define PCI_DEVICE_ID_INTEL_82801I_EHCI2 0x293c
#define PCI_VENDOR_ID_XEN 0x5853
#define PCI_DEVICE_ID_XEN_PLATFORM 0x0001
diff --git a/hw/pci_internals.h b/hw/pci_internals.h
index fbe1866..c3a463a 100644
--- a/hw/pci_internals.h
+++ b/hw/pci_internals.h
@@ -25,6 +25,7 @@ struct PCIBus {
PCIDevice *devices[PCI_SLOT_MAX * PCI_FUNC_MAX];
PCIDevice *parent_dev;
target_phys_addr_t mem_base;
+ MemoryRegion *address_space;
QLIST_HEAD(, PCIBus) child; /* this will be replaced by qdev later */
QLIST_ENTRY(PCIBus) sibling;/* this will be replaced by qdev later */
diff --git a/hw/piix_pci.c b/hw/piix_pci.c
index d08b31a..80d6665 100644
--- a/hw/piix_pci.c
+++ b/hw/piix_pci.c
@@ -241,7 +241,9 @@ static int i440fx_initfn(PCIDevice *dev)
static PCIBus *i440fx_common_init(const char *device_name,
PCII440FXState **pi440fx_state,
int *piix3_devfn,
- qemu_irq *pic, ram_addr_t ram_size)
+ qemu_irq *pic,
+ MemoryRegion *address_space,
+ ram_addr_t ram_size)
{
DeviceState *dev;
PCIBus *b;
@@ -251,7 +253,8 @@ static PCIBus *i440fx_common_init(const char *device_name,
dev = qdev_create(NULL, "i440FX-pcihost");
s = FROM_SYSBUS(I440FXState, sysbus_from_qdev(dev));
- b = pci_bus_new(&s->busdev.qdev, NULL, 0);
+ s->address_space = address_space;
+ b = pci_bus_new(&s->busdev.qdev, NULL, s->address_space, 0);
s->bus = b;
qdev_init_nofail(dev);
@@ -288,11 +291,13 @@ static PCIBus *i440fx_common_init(const char *device_name,
}
PCIBus *i440fx_init(PCII440FXState **pi440fx_state, int *piix3_devfn,
- qemu_irq *pic, ram_addr_t ram_size)
+ qemu_irq *pic, MemoryRegion *address_space,
+ ram_addr_t ram_size)
{
PCIBus *b;
- b = i440fx_common_init("i440FX", pi440fx_state, piix3_devfn, pic, ram_size);
+ b = i440fx_common_init("i440FX", pi440fx_state, piix3_devfn, pic,
+ address_space, ram_size);
return b;
}
diff --git a/hw/pl011.c b/hw/pl011.c
index 3b94b14..997ce84 100644
--- a/hw/pl011.c
+++ b/hw/pl011.c
@@ -4,7 +4,7 @@
* Copyright (c) 2006 CodeSourcery.
* Written by Paul Brook
*
- * This code is licenced under the GPL.
+ * This code is licensed under the GPL.
*/
#include "sysbus.h"
diff --git a/hw/pl022.c b/hw/pl022.c
index 00e494a..9a1cb71 100644
--- a/hw/pl022.c
+++ b/hw/pl022.c
@@ -4,7 +4,7 @@
* Copyright (c) 2007 CodeSourcery.
* Written by Paul Brook
*
- * This code is licenced under the GPL.
+ * This code is licensed under the GPL.
*/
#include "sysbus.h"
diff --git a/hw/pl050.c b/hw/pl050.c
index b155cc0..f7fa2e2 100644
--- a/hw/pl050.c
+++ b/hw/pl050.c
@@ -4,7 +4,7 @@
* Copyright (c) 2006-2007 CodeSourcery.
* Written by Paul Brook
*
- * This code is licenced under the GPL.
+ * This code is licensed under the GPL.
*/
#include "sysbus.h"
diff --git a/hw/pl061.c b/hw/pl061.c
index 372dfc2..79e5c53 100644
--- a/hw/pl061.c
+++ b/hw/pl061.c
@@ -5,7 +5,7 @@
* Copyright (c) 2007 CodeSourcery.
* Written by Paul Brook
*
- * This code is licenced under the GPL.
+ * This code is licensed under the GPL.
*/
#include "sysbus.h"
diff --git a/hw/pl080.c b/hw/pl080.c
index dd8139b..5ba3b08 100644
--- a/hw/pl080.c
+++ b/hw/pl080.c
@@ -4,7 +4,7 @@
* Copyright (c) 2006 CodeSourcery.
* Written by Paul Brook
*
- * This code is licenced under the GPL.
+ * This code is licensed under the GPL.
*/
#include "sysbus.h"
diff --git a/hw/pl110.c b/hw/pl110.c
index 06d2dfa..62aba17 100644
--- a/hw/pl110.c
+++ b/hw/pl110.c
@@ -4,7 +4,7 @@
* Copyright (c) 2005-2009 CodeSourcery.
* Written by Paul Brook
*
- * This code is licenced under the GNU LGPL
+ * This code is licensed under the GNU LGPL
*/
#include "sysbus.h"
diff --git a/hw/pl110_template.h b/hw/pl110_template.h
index b3c9077..d303336 100644
--- a/hw/pl110_template.h
+++ b/hw/pl110_template.h
@@ -4,7 +4,7 @@
* Copyright (c) 2005 CodeSourcery, LLC.
* Written by Paul Brook
*
- * This code is licenced under the GNU LGPL
+ * This code is licensed under the GNU LGPL
*
* Framebuffer format conversion routines.
*/
diff --git a/hw/pl181.c b/hw/pl181.c
index 6bc79f5..0943c09 100644
--- a/hw/pl181.c
+++ b/hw/pl181.c
@@ -4,7 +4,7 @@
* Copyright (c) 2007 CodeSourcery.
* Written by Paul Brook
*
- * This code is licenced under the GPL.
+ * This code is licensed under the GPL.
*/
#include "blockdev.h"
diff --git a/hw/pl190.c b/hw/pl190.c
index 75f2ba1..8dc7e42 100644
--- a/hw/pl190.c
+++ b/hw/pl190.c
@@ -4,7 +4,7 @@
* Copyright (c) 2006 CodeSourcery.
* Written by Paul Brook
*
- * This code is licenced under the GPL.
+ * This code is licensed under the GPL.
*/
#include "sysbus.h"
diff --git a/hw/ppc4xx_pci.c b/hw/ppc4xx_pci.c
index 299473c..15c24f6 100644
--- a/hw/ppc4xx_pci.c
+++ b/hw/ppc4xx_pci.c
@@ -24,6 +24,7 @@
#include "ppc4xx.h"
#include "pci.h"
#include "pci_host.h"
+#include "exec-memory.h"
#undef DEBUG
#ifdef DEBUG
@@ -345,7 +346,9 @@ PCIBus *ppc4xx_pci_init(CPUState *env, qemu_irq pci_irqs[4],
controller->pci_state.bus = pci_register_bus(NULL, "pci",
ppc4xx_pci_set_irq,
ppc4xx_pci_map_irq,
- pci_irqs, 0, 4);
+ pci_irqs,
+ get_system_memory(),
+ 0, 4);
controller->pci_dev = pci_register_device(controller->pci_state.bus,
"host bridge", sizeof(PCIDevice),
diff --git a/hw/ppc_mac.h b/hw/ppc_mac.h
index 68dade7..6fad20a 100644
--- a/hw/ppc_mac.h
+++ b/hw/ppc_mac.h
@@ -25,6 +25,8 @@
#if !defined(__PPC_MAC_H__)
#define __PPC_MAC_H__
+#include "memory.h"
+
/* SMP is not enabled, for now */
#define MAX_CPUS 1
@@ -52,11 +54,12 @@ qemu_irq *heathrow_pic_init(int *pmem_index,
int nb_cpus, qemu_irq **irqs);
/* Grackle PCI */
-PCIBus *pci_grackle_init(uint32_t base, qemu_irq *pic);
+PCIBus *pci_grackle_init(uint32_t base, qemu_irq *pic,
+ MemoryRegion *address_space);
/* UniNorth PCI */
-PCIBus *pci_pmac_init(qemu_irq *pic);
-PCIBus *pci_pmac_u3_init(qemu_irq *pic);
+PCIBus *pci_pmac_init(qemu_irq *pic, MemoryRegion *address_space);
+PCIBus *pci_pmac_u3_init(qemu_irq *pic, MemoryRegion *address_space);
/* Mac NVRAM */
typedef struct MacIONVRAMState MacIONVRAMState;
diff --git a/hw/ppc_newworld.c b/hw/ppc_newworld.c
index 5bce709..2c0fae8 100644
--- a/hw/ppc_newworld.c
+++ b/hw/ppc_newworld.c
@@ -67,6 +67,7 @@
#include "kvm_ppc.h"
#include "hw/usb.h"
#include "blockdev.h"
+#include "exec-memory.h"
#define MAX_IDE_BUS 2
#define CFG_ADDR 0xf0000510
@@ -317,10 +318,10 @@ static void ppc_core99_init (ram_addr_t ram_size,
pic = openpic_init(NULL, &pic_mem_index, smp_cpus, openpic_irqs, NULL);
if (PPC_INPUT(env) == PPC_FLAGS_INPUT_970) {
/* 970 gets a U3 bus */
- pci_bus = pci_pmac_u3_init(pic);
+ pci_bus = pci_pmac_u3_init(pic, get_system_memory());
machine_arch = ARCH_MAC99_U3;
} else {
- pci_bus = pci_pmac_init(pic);
+ pci_bus = pci_pmac_init(pic, get_system_memory());
machine_arch = ARCH_MAC99;
}
/* init basic PC hardware */
diff --git a/hw/ppc_oldworld.c b/hw/ppc_oldworld.c
index 20cd8e1..585afd6 100644
--- a/hw/ppc_oldworld.c
+++ b/hw/ppc_oldworld.c
@@ -43,6 +43,7 @@
#include "kvm.h"
#include "kvm_ppc.h"
#include "blockdev.h"
+#include "exec-memory.h"
#define MAX_IDE_BUS 2
#define CFG_ADDR 0xf0000510
@@ -233,7 +234,7 @@ static void ppc_heathrow_init (ram_addr_t ram_size,
hw_error("Only 6xx bus is supported on heathrow machine\n");
}
pic = heathrow_pic_init(&pic_mem_index, 1, heathrow_irqs);
- pci_bus = pci_grackle_init(0xfec00000, pic);
+ pci_bus = pci_grackle_init(0xfec00000, pic, get_system_memory());
pci_vga_init(pci_bus);
escc_mem_index = escc_init(0x80013000, pic[0x0f], pic[0x10], serial_hds[0],
diff --git a/hw/ppc_prep.c b/hw/ppc_prep.c
index 0e9cfc2..91ebe07 100644
--- a/hw/ppc_prep.c
+++ b/hw/ppc_prep.c
@@ -38,6 +38,7 @@
#include "loader.h"
#include "mc146818rtc.h"
#include "blockdev.h"
+#include "exec-memory.h"
//#define HARD_DEBUG_PPC_IO
//#define DEBUG_PPC_IO
@@ -648,7 +649,7 @@ static void ppc_prep_init (ram_addr_t ram_size,
hw_error("Only 6xx bus is supported on PREP machine\n");
}
i8259 = i8259_init(first_cpu->irq_inputs[PPC6xx_INPUT_INT]);
- pci_bus = pci_prep_init(i8259);
+ pci_bus = pci_prep_init(i8259, get_system_memory());
/* Hmm, prep has no pci-isa bridge ??? */
isa_bus_new(NULL);
isa_bus_irqs(i8259);
diff --git a/hw/ppce500_pci.c b/hw/ppce500_pci.c
index fc11af4..1344539 100644
--- a/hw/ppce500_pci.c
+++ b/hw/ppce500_pci.c
@@ -274,12 +274,15 @@ static void e500_pci_map(SysBusDevice *dev, target_phys_addr_t base)
s->reg);
}
+#include "exec-memory.h"
+
static int e500_pcihost_initfn(SysBusDevice *dev)
{
PCIHostState *h;
PPCE500PCIState *s;
PCIBus *b;
int i;
+ MemoryRegion *address_space = get_system_memory();
h = FROM_SYSBUS(PCIHostState, sysbus_from_qdev(dev));
s = DO_UPCAST(PPCE500PCIState, pci_state, h);
@@ -289,7 +292,8 @@ static int e500_pcihost_initfn(SysBusDevice *dev)
}
b = pci_register_bus(&s->pci_state.busdev.qdev, NULL, mpc85xx_pci_set_irq,
- mpc85xx_pci_map_irq, s->irq, PCI_DEVFN(0x11, 0), 4);
+ mpc85xx_pci_map_irq, s->irq, address_space,
+ PCI_DEVFN(0x11, 0), 4);
s->pci_state.bus = b;
pci_create_simple(b, 0, "e500-host-bridge");
diff --git a/hw/prep_pci.c b/hw/prep_pci.c
index f88b825..da02f0e 100644
--- a/hw/prep_pci.c
+++ b/hw/prep_pci.c
@@ -110,7 +110,7 @@ static void prep_set_irq(void *opaque, int irq_num, int level)
qemu_set_irq(pic[(irq_num & 1) ? 11 : 9] , level);
}
-PCIBus *pci_prep_init(qemu_irq *pic)
+PCIBus *pci_prep_init(qemu_irq *pic, MemoryRegion *address_space)
{
PREPPCIState *s;
PCIDevice *d;
@@ -118,7 +118,8 @@ PCIBus *pci_prep_init(qemu_irq *pic)
s = qemu_mallocz(sizeof(PREPPCIState));
s->bus = pci_register_bus(NULL, "pci",
- prep_set_irq, prep_map_irq, pic, 0, 4);
+ prep_set_irq, prep_map_irq, pic,
+ address_space, 0, 4);
pci_host_conf_register_ioport(0xcf8, s);
diff --git a/hw/prep_pci.h b/hw/prep_pci.h
index cd68512..a27368b 100644
--- a/hw/prep_pci.h
+++ b/hw/prep_pci.h
@@ -2,7 +2,8 @@
#define QEMU_PREP_PCI_H
#include "qemu-common.h"
+#include "memory.h"
-PCIBus *pci_prep_init(qemu_irq *pic);
+PCIBus *pci_prep_init(qemu_irq *pic, MemoryRegion *address_space);
#endif
diff --git a/hw/ptimer.c b/hw/ptimer.c
index 47964a6..6f13ce9 100644
--- a/hw/ptimer.c
+++ b/hw/ptimer.c
@@ -3,7 +3,7 @@
*
* Copyright (c) 2007 CodeSourcery.
*
- * This code is licenced under the GNU LGPL.
+ * This code is licensed under the GNU LGPL.
*/
#include "hw.h"
#include "qemu-timer.h"
diff --git a/hw/pxa.h b/hw/pxa.h
index d982f00..859fc67 100644
--- a/hw/pxa.h
+++ b/hw/pxa.h
@@ -4,7 +4,7 @@
* Copyright (c) 2006 Openedhand Ltd.
* Written by Andrzej Zaborowski <balrog@zabor.org>
*
- * This code is licenced under the GNU GPL v2.
+ * This code is licensed under the GNU GPL v2.
*/
#ifndef PXA_H
# define PXA_H "pxa.h"
diff --git a/hw/pxa2xx.c b/hw/pxa2xx.c
index ac5d95d..cf93110 100644
--- a/hw/pxa2xx.c
+++ b/hw/pxa2xx.c
@@ -4,7 +4,7 @@
* Copyright (c) 2006 Openedhand Ltd.
* Written by Andrzej Zaborowski <balrog@zabor.org>
*
- * This code is licenced under the GPL.
+ * This code is licensed under the GPL.
*/
#include "sysbus.h"
diff --git a/hw/pxa2xx_dma.c b/hw/pxa2xx_dma.c
index a67498b..599581e 100644
--- a/hw/pxa2xx_dma.c
+++ b/hw/pxa2xx_dma.c
@@ -5,7 +5,7 @@
* Copyright (c) 2006 Thorsten Zitterell
* Written by Andrzej Zaborowski <balrog@zabor.org>
*
- * This code is licenced under the GPL.
+ * This code is licensed under the GPL.
*/
#include "hw.h"
diff --git a/hw/pxa2xx_pic.c b/hw/pxa2xx_pic.c
index e9a5361..bdd82e6 100644
--- a/hw/pxa2xx_pic.c
+++ b/hw/pxa2xx_pic.c
@@ -5,7 +5,7 @@
* Copyright (c) 2006 Thorsten Zitterell
* Written by Andrzej Zaborowski <balrog@zabor.org>
*
- * This code is licenced under the GPL.
+ * This code is licensed under the GPL.
*/
#include "hw.h"
diff --git a/hw/pxa2xx_timer.c b/hw/pxa2xx_timer.c
index f777a21..4235e42 100644
--- a/hw/pxa2xx_timer.c
+++ b/hw/pxa2xx_timer.c
@@ -4,7 +4,7 @@
* Copyright (c) 2006 Openedhand Ltd.
* Copyright (c) 2006 Thorsten Zitterell
*
- * This code is licenced under the GPL.
+ * This code is licensed under the GPL.
*/
#include "hw.h"
diff --git a/hw/qdev.c b/hw/qdev.c
index 292b52f..b4ea8e1 100644
--- a/hw/qdev.c
+++ b/hw/qdev.c
@@ -289,6 +289,9 @@ int qdev_init(DeviceState *dev)
dev->alias_required_for_version);
}
dev->state = DEV_STATE_INITIALIZED;
+ if (dev->hotplugged && dev->info->reset) {
+ dev->info->reset(dev);
+ }
return 0;
}
@@ -459,7 +462,7 @@ void qdev_connect_gpio_out(DeviceState * dev, int n, qemu_irq pin)
void qdev_set_nic_properties(DeviceState *dev, NICInfo *nd)
{
- qdev_prop_set_macaddr(dev, "mac", nd->macaddr);
+ qdev_prop_set_macaddr(dev, "mac", nd->macaddr.a);
if (nd->vlan)
qdev_prop_set_vlan(dev, "vlan", nd->vlan);
if (nd->netdev)
diff --git a/hw/qxl-logger.c b/hw/qxl-logger.c
index 76f43e6..74cadba 100644
--- a/hw/qxl-logger.c
+++ b/hw/qxl-logger.c
@@ -19,6 +19,7 @@
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu-timer.h"
#include "qxl.h"
static const char *qxl_type[] = {
@@ -223,7 +224,8 @@ void qxl_log_command(PCIQXLDevice *qxl, const char *ring, QXLCommandExt *ext)
if (!qxl->cmdlog) {
return;
}
- fprintf(stderr, "qxl-%d/%s:", qxl->id, ring);
+ fprintf(stderr, "%ld qxl-%d/%s:", qemu_get_clock_ns(vm_clock),
+ qxl->id, ring);
fprintf(stderr, " cmd @ 0x%" PRIx64 " %s%s", ext->cmd.data,
qxl_name(qxl_type, ext->cmd.type),
compat ? "(compat)" : "");
diff --git a/hw/qxl-render.c b/hw/qxl-render.c
index 1316066..643ff2d 100644
--- a/hw/qxl-render.c
+++ b/hw/qxl-render.c
@@ -124,8 +124,8 @@ void qxl_render_update(PCIQXLDevice *qxl)
update.bottom = qxl->guest_primary.surface.height;
memset(dirty, 0, sizeof(dirty));
- qxl->ssd.worker->update_area(qxl->ssd.worker, 0, &update,
- dirty, ARRAY_SIZE(dirty), 1);
+ qxl_spice_update_area(qxl, 0, &update,
+ dirty, ARRAY_SIZE(dirty), 1, QXL_SYNC);
for (i = 0; i < ARRAY_SIZE(dirty); i++) {
if (qemu_spice_rect_is_empty(dirty+i)) {
diff --git a/hw/qxl.c b/hw/qxl.c
index 16316f2..b684608 100644
--- a/hw/qxl.c
+++ b/hw/qxl.c
@@ -120,11 +120,127 @@ static QXLMode qxl_modes[] = {
static PCIQXLDevice *qxl0;
static void qxl_send_events(PCIQXLDevice *d, uint32_t events);
-static void qxl_destroy_primary(PCIQXLDevice *d);
+static int qxl_destroy_primary(PCIQXLDevice *d, qxl_async_io async);
static void qxl_reset_memslots(PCIQXLDevice *d);
static void qxl_reset_surfaces(PCIQXLDevice *d);
static void qxl_ring_set_dirty(PCIQXLDevice *qxl);
+void qxl_guest_bug(PCIQXLDevice *qxl, const char *msg, ...)
+{
+#if SPICE_INTERFACE_QXL_MINOR >= 1
+ qxl_send_events(qxl, QXL_INTERRUPT_ERROR);
+#endif
+ if (qxl->guestdebug) {
+ va_list ap;
+ va_start(ap, msg);
+ fprintf(stderr, "qxl-%d: guest bug: ", qxl->id);
+ vfprintf(stderr, msg, ap);
+ fprintf(stderr, "\n");
+ va_end(ap);
+ }
+}
+
+
+void qxl_spice_update_area(PCIQXLDevice *qxl, uint32_t surface_id,
+ struct QXLRect *area, struct QXLRect *dirty_rects,
+ uint32_t num_dirty_rects,
+ uint32_t clear_dirty_region,
+ qxl_async_io async)
+{
+ if (async == QXL_SYNC) {
+ qxl->ssd.worker->update_area(qxl->ssd.worker, surface_id, area,
+ dirty_rects, num_dirty_rects, clear_dirty_region);
+ } else {
+#if SPICE_INTERFACE_QXL_MINOR >= 1
+ spice_qxl_update_area_async(&qxl->ssd.qxl, surface_id, area,
+ clear_dirty_region, 0);
+#else
+ abort();
+#endif
+ }
+}
+
+static void qxl_spice_destroy_surface_wait_complete(PCIQXLDevice *qxl,
+ uint32_t id)
+{
+ qemu_mutex_lock(&qxl->track_lock);
+ qxl->guest_surfaces.cmds[id] = 0;
+ qxl->guest_surfaces.count--;
+ qemu_mutex_unlock(&qxl->track_lock);
+}
+
+static void qxl_spice_destroy_surface_wait(PCIQXLDevice *qxl, uint32_t id,
+ qxl_async_io async)
+{
+ if (async) {
+#if SPICE_INTERFACE_QXL_MINOR < 1
+ abort();
+#else
+ spice_qxl_destroy_surface_async(&qxl->ssd.qxl, id,
+ (uint64_t)id);
+#endif
+ } else {
+ qxl->ssd.worker->destroy_surface_wait(qxl->ssd.worker, id);
+ qxl_spice_destroy_surface_wait_complete(qxl, id);
+ }
+}
+
+#if SPICE_INTERFACE_QXL_MINOR >= 1
+static void qxl_spice_flush_surfaces_async(PCIQXLDevice *qxl)
+{
+ spice_qxl_flush_surfaces_async(&qxl->ssd.qxl, 0);
+}
+#endif
+
+void qxl_spice_loadvm_commands(PCIQXLDevice *qxl, struct QXLCommandExt *ext,
+ uint32_t count)
+{
+ qxl->ssd.worker->loadvm_commands(qxl->ssd.worker, ext, count);
+}
+
+void qxl_spice_oom(PCIQXLDevice *qxl)
+{
+ qxl->ssd.worker->oom(qxl->ssd.worker);
+}
+
+void qxl_spice_reset_memslots(PCIQXLDevice *qxl)
+{
+ qxl->ssd.worker->reset_memslots(qxl->ssd.worker);
+}
+
+static void qxl_spice_destroy_surfaces_complete(PCIQXLDevice *qxl)
+{
+ qemu_mutex_lock(&qxl->track_lock);
+ memset(&qxl->guest_surfaces.cmds, 0, sizeof(qxl->guest_surfaces.cmds));
+ qxl->guest_surfaces.count = 0;
+ qemu_mutex_unlock(&qxl->track_lock);
+}
+
+static void qxl_spice_destroy_surfaces(PCIQXLDevice *qxl, qxl_async_io async)
+{
+ if (async) {
+#if SPICE_INTERFACE_QXL_MINOR < 1
+ abort();
+#else
+ spice_qxl_destroy_surfaces_async(&qxl->ssd.qxl, 0);
+#endif
+ } else {
+ qxl->ssd.worker->destroy_surfaces(qxl->ssd.worker);
+ qxl_spice_destroy_surfaces_complete(qxl);
+ }
+}
+
+void qxl_spice_reset_image_cache(PCIQXLDevice *qxl)
+{
+ qxl->ssd.worker->reset_image_cache(qxl->ssd.worker);
+}
+
+void qxl_spice_reset_cursor(PCIQXLDevice *qxl)
+{
+ qxl->ssd.worker->reset_cursor(qxl->ssd.worker);
+}
+
+
static inline uint32_t msb_mask(uint32_t val)
{
uint32_t mask;
@@ -270,6 +386,7 @@ static void qxl_track_command(PCIQXLDevice *qxl, struct QXLCommandExt *ext)
QXLSurfaceCmd *cmd = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id);
uint32_t id = le32_to_cpu(cmd->surface_id);
PANIC_ON(id >= NUM_SURFACES);
+ qemu_mutex_lock(&qxl->track_lock);
if (cmd->type == QXL_SURFACE_CMD_CREATE) {
qxl->guest_surfaces.cmds[id] = ext->cmd.data;
qxl->guest_surfaces.count++;
@@ -280,6 +397,7 @@ static void qxl_track_command(PCIQXLDevice *qxl, struct QXLCommandExt *ext)
qxl->guest_surfaces.cmds[id] = 0;
qxl->guest_surfaces.count--;
}
+ qemu_mutex_unlock(&qxl->track_lock);
break;
}
case QXL_CMD_CURSOR:
@@ -336,6 +454,58 @@ static void interface_get_init_info(QXLInstance *sin, QXLDevInitInfo *info)
info->n_surfaces = NUM_SURFACES;
}
+static const char *qxl_mode_to_string(int mode)
+{
+ switch (mode) {
+ case QXL_MODE_COMPAT:
+ return "compat";
+ case QXL_MODE_NATIVE:
+ return "native";
+ case QXL_MODE_UNDEFINED:
+ return "undefined";
+ case QXL_MODE_VGA:
+ return "vga";
+ }
+ return "INVALID";
+}
+
+static const char *io_port_to_string(uint32_t io_port)
+{
+ if (io_port >= QXL_IO_RANGE_SIZE) {
+ return "out of range";
+ }
+ static const char *io_port_to_string[QXL_IO_RANGE_SIZE + 1] = {
+ [QXL_IO_NOTIFY_CMD] = "QXL_IO_NOTIFY_CMD",
+ [QXL_IO_NOTIFY_CURSOR] = "QXL_IO_NOTIFY_CURSOR",
+ [QXL_IO_UPDATE_AREA] = "QXL_IO_UPDATE_AREA",
+ [QXL_IO_UPDATE_IRQ] = "QXL_IO_UPDATE_IRQ",
+ [QXL_IO_NOTIFY_OOM] = "QXL_IO_NOTIFY_OOM",
+ [QXL_IO_RESET] = "QXL_IO_RESET",
+ [QXL_IO_SET_MODE] = "QXL_IO_SET_MODE",
+ [QXL_IO_LOG] = "QXL_IO_LOG",
+ [QXL_IO_MEMSLOT_ADD] = "QXL_IO_MEMSLOT_ADD",
+ [QXL_IO_MEMSLOT_DEL] = "QXL_IO_MEMSLOT_DEL",
+ [QXL_IO_DETACH_PRIMARY] = "QXL_IO_DETACH_PRIMARY",
+ [QXL_IO_ATTACH_PRIMARY] = "QXL_IO_ATTACH_PRIMARY",
+ [QXL_IO_CREATE_PRIMARY] = "QXL_IO_CREATE_PRIMARY",
+ [QXL_IO_DESTROY_PRIMARY] = "QXL_IO_DESTROY_PRIMARY",
+ [QXL_IO_DESTROY_SURFACE_WAIT] = "QXL_IO_DESTROY_SURFACE_WAIT",
+ [QXL_IO_DESTROY_ALL_SURFACES] = "QXL_IO_DESTROY_ALL_SURFACES",
+#if SPICE_INTERFACE_QXL_MINOR >= 1
+ [QXL_IO_UPDATE_AREA_ASYNC] = "QXL_IO_UPDATE_AREA_ASYNC",
+ [QXL_IO_MEMSLOT_ADD_ASYNC] = "QXL_IO_MEMSLOT_ADD_ASYNC",
+ [QXL_IO_CREATE_PRIMARY_ASYNC] = "QXL_IO_CREATE_PRIMARY_ASYNC",
+ [QXL_IO_DESTROY_PRIMARY_ASYNC] = "QXL_IO_DESTROY_PRIMARY_ASYNC",
+ [QXL_IO_DESTROY_SURFACE_ASYNC] = "QXL_IO_DESTROY_SURFACE_ASYNC",
+ [QXL_IO_DESTROY_ALL_SURFACES_ASYNC]
+ = "QXL_IO_DESTROY_ALL_SURFACES_ASYNC",
+ [QXL_IO_FLUSH_SURFACES_ASYNC] = "QXL_IO_FLUSH_SURFACES_ASYNC",
+ [QXL_IO_FLUSH_RELEASE] = "QXL_IO_FLUSH_RELEASE",
+#endif
+ };
+ return io_port_to_string[io_port];
+}
+
/* called from spice server thread context only */
static int interface_get_command(QXLInstance *sin, struct QXLCommandExt *ext)
{
@@ -358,18 +528,19 @@ static int interface_get_command(QXLInstance *sin, struct QXLCommandExt *ext)
}
qemu_mutex_unlock(&qxl->ssd.lock);
if (ret) {
+ dprint(qxl, 2, "%s %s\n", __FUNCTION__, qxl_mode_to_string(qxl->mode));
qxl_log_command(qxl, "vga", ext);
}
return ret;
case QXL_MODE_COMPAT:
case QXL_MODE_NATIVE:
case QXL_MODE_UNDEFINED:
- dprint(qxl, 2, "%s: %s\n", __FUNCTION__,
- qxl->cmdflags ? "compat" : "native");
+ dprint(qxl, 4, "%s: %s\n", __FUNCTION__, qxl_mode_to_string(qxl->mode));
ring = &qxl->ram->cmd_ring;
if (SPICE_RING_IS_EMPTY(ring)) {
return false;
}
+ dprint(qxl, 2, "%s: %s\n", __FUNCTION__, qxl_mode_to_string(qxl->mode));
SPICE_RING_CONS_ITEM(ring, cmd);
ext->cmd = *cmd;
ext->group_id = MEMSLOT_GROUP_GUEST;
@@ -563,6 +734,38 @@ static int interface_flush_resources(QXLInstance *sin)
return ret;
}
+static void qxl_create_guest_primary_complete(PCIQXLDevice *d);
+
+#if SPICE_INTERFACE_QXL_MINOR >= 1
+
+/* called from spice server thread context only */
+static void interface_async_complete(QXLInstance *sin, uint64_t cookie)
+{
+ PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
+ uint32_t current_async;
+
+ qemu_mutex_lock(&qxl->async_lock);
+ current_async = qxl->current_async;
+ qxl->current_async = QXL_UNDEFINED_IO;
+ qemu_mutex_unlock(&qxl->async_lock);
+
+ dprint(qxl, 2, "async_complete: %d (%ld) done\n", current_async, cookie);
+ switch (current_async) {
+ case QXL_IO_CREATE_PRIMARY_ASYNC:
+ qxl_create_guest_primary_complete(qxl);
+ break;
+ case QXL_IO_DESTROY_ALL_SURFACES_ASYNC:
+ qxl_spice_destroy_surfaces_complete(qxl);
+ break;
+ case QXL_IO_DESTROY_SURFACE_ASYNC:
+ qxl_spice_destroy_surface_wait_complete(qxl, (uint32_t)cookie);
+ break;
+ }
+ qxl_send_events(qxl, QXL_INTERRUPT_IO_CMD);
+}
+
+#endif
+
static const QXLInterface qxl_interface = {
.base.type = SPICE_INTERFACE_QXL,
.base.description = "qxl gpu",
@@ -582,6 +785,9 @@ static const QXLInterface qxl_interface = {
.req_cursor_notification = interface_req_cursor_notification,
.notify_update = interface_notify_update,
.flush_resources = interface_flush_resources,
+#if SPICE_INTERFACE_QXL_MINOR >= 1
+ .async_complete = interface_async_complete,
+#endif
};
static void qxl_enter_vga_mode(PCIQXLDevice *d)
@@ -601,7 +807,7 @@ static void qxl_exit_vga_mode(PCIQXLDevice *d)
return;
}
dprint(d, 1, "%s\n", __FUNCTION__);
- qxl_destroy_primary(d);
+ qxl_destroy_primary(d, QXL_SYNC);
}
static void qxl_set_irq(PCIQXLDevice *d)
@@ -640,8 +846,8 @@ static void qxl_reset_state(PCIQXLDevice *d)
QXLRam *ram = d->ram;
QXLRom *rom = d->rom;
- assert(SPICE_RING_IS_EMPTY(&ram->cmd_ring));
- assert(SPICE_RING_IS_EMPTY(&ram->cursor_ring));
+ assert(!d->ssd.running || SPICE_RING_IS_EMPTY(&ram->cmd_ring));
+ assert(!d->ssd.running || SPICE_RING_IS_EMPTY(&ram->cursor_ring));
d->shadow_rom.update_id = cpu_to_le32(0);
*rom = d->shadow_rom;
qxl_rom_set_dirty(d);
@@ -668,8 +874,8 @@ static void qxl_hard_reset(PCIQXLDevice *d, int loadvm)
dprint(d, 1, "%s: start%s\n", __FUNCTION__,
loadvm ? " (loadvm)" : "");
- d->ssd.worker->reset_cursor(d->ssd.worker);
- d->ssd.worker->reset_image_cache(d->ssd.worker);
+ qxl_spice_reset_cursor(d);
+ qxl_spice_reset_image_cache(d);
qxl_reset_surfaces(d);
qxl_reset_memslots(d);
@@ -698,13 +904,14 @@ static void qxl_vga_ioport_write(void *opaque, uint32_t addr, uint32_t val)
if (qxl->mode != QXL_MODE_VGA) {
dprint(qxl, 1, "%s\n", __FUNCTION__);
- qxl_destroy_primary(qxl);
+ qxl_destroy_primary(qxl, QXL_SYNC);
qxl_soft_reset(qxl);
}
vga_ioport_write(opaque, addr, val);
}
-static void qxl_add_memslot(PCIQXLDevice *d, uint32_t slot_id, uint64_t delta)
+static void qxl_add_memslot(PCIQXLDevice *d, uint32_t slot_id, uint64_t delta,
+ qxl_async_io async)
{
static const int regions[] = {
QXL_RAM_RANGE_INDEX,
@@ -774,7 +981,7 @@ static void qxl_add_memslot(PCIQXLDevice *d, uint32_t slot_id, uint64_t delta)
__FUNCTION__, memslot.slot_id,
memslot.virt_start, memslot.virt_end);
- d->ssd.worker->add_memslot(d->ssd.worker, &memslot);
+ qemu_spice_add_memslot(&d->ssd, &memslot, async);
d->guest_slots[slot_id].ptr = (void*)memslot.virt_start;
d->guest_slots[slot_id].size = memslot.virt_end - memslot.virt_start;
d->guest_slots[slot_id].delta = delta;
@@ -784,14 +991,14 @@ static void qxl_add_memslot(PCIQXLDevice *d, uint32_t slot_id, uint64_t delta)
static void qxl_del_memslot(PCIQXLDevice *d, uint32_t slot_id)
{
dprint(d, 1, "%s: slot %d\n", __FUNCTION__, slot_id);
- d->ssd.worker->del_memslot(d->ssd.worker, MEMSLOT_GROUP_HOST, slot_id);
+ qemu_spice_del_memslot(&d->ssd, MEMSLOT_GROUP_HOST, slot_id);
d->guest_slots[slot_id].active = 0;
}
static void qxl_reset_memslots(PCIQXLDevice *d)
{
dprint(d, 1, "%s:\n", __FUNCTION__);
- d->ssd.worker->reset_memslots(d->ssd.worker);
+ qxl_spice_reset_memslots(d);
memset(&d->guest_slots, 0, sizeof(d->guest_slots));
}
@@ -799,8 +1006,7 @@ static void qxl_reset_surfaces(PCIQXLDevice *d)
{
dprint(d, 1, "%s:\n", __FUNCTION__);
d->mode = QXL_MODE_UNDEFINED;
- d->ssd.worker->destroy_surfaces(d->ssd.worker);
- memset(&d->guest_surfaces.cmds, 0, sizeof(d->guest_surfaces.cmds));
+ qxl_spice_destroy_surfaces(d, QXL_SYNC);
}
/* called from spice server thread context only */
@@ -825,7 +1031,14 @@ void *qxl_phys2virt(PCIQXLDevice *qxl, QXLPHYSICAL pqxl, int group_id)
}
}
-static void qxl_create_guest_primary(PCIQXLDevice *qxl, int loadvm)
+static void qxl_create_guest_primary_complete(PCIQXLDevice *qxl)
+{
+ /* for local rendering */
+ qxl_render_resize(qxl);
+}
+
+static void qxl_create_guest_primary(PCIQXLDevice *qxl, int loadvm,
+ qxl_async_io async)
{
QXLDevSurfaceCreate surface;
QXLSurfaceCreate *sc = &qxl->guest_primary.surface;
@@ -853,22 +1066,26 @@ static void qxl_create_guest_primary(PCIQXLDevice *qxl, int loadvm)
qxl->mode = QXL_MODE_NATIVE;
qxl->cmdflags = 0;
- qxl->ssd.worker->create_primary_surface(qxl->ssd.worker, 0, &surface);
+ qemu_spice_create_primary_surface(&qxl->ssd, 0, &surface, async);
- /* for local rendering */
- qxl_render_resize(qxl);
+ if (async == QXL_SYNC) {
+ qxl_create_guest_primary_complete(qxl);
+ }
}
-static void qxl_destroy_primary(PCIQXLDevice *d)
+/* return 1 if surface destoy was initiated (in QXL_ASYNC case) or
+ * done (in QXL_SYNC case), 0 otherwise. */
+static int qxl_destroy_primary(PCIQXLDevice *d, qxl_async_io async)
{
if (d->mode == QXL_MODE_UNDEFINED) {
- return;
+ return 0;
}
dprint(d, 1, "%s\n", __FUNCTION__);
d->mode = QXL_MODE_UNDEFINED;
- d->ssd.worker->destroy_primary_surface(d->ssd.worker, 0);
+ qemu_spice_destroy_primary_surface(&d->ssd, 0, async);
+ return 1;
}
static void qxl_set_mode(PCIQXLDevice *d, int modenr, int loadvm)
@@ -898,10 +1115,10 @@ static void qxl_set_mode(PCIQXLDevice *d, int modenr, int loadvm)
}
d->guest_slots[0].slot = slot;
- qxl_add_memslot(d, 0, devmem);
+ qxl_add_memslot(d, 0, devmem, QXL_SYNC);
d->guest_primary.surface = surface;
- qxl_create_guest_primary(d, 0);
+ qxl_create_guest_primary(d, 0, QXL_SYNC);
d->mode = QXL_MODE_COMPAT;
d->cmdflags = QXL_COMMAND_FLAG_COMPAT;
@@ -919,6 +1136,10 @@ static void ioport_write(void *opaque, uint32_t addr, uint32_t val)
{
PCIQXLDevice *d = opaque;
uint32_t io_port = addr - d->io_base;
+ qxl_async_io async = QXL_SYNC;
+#if SPICE_INTERFACE_QXL_MINOR >= 1
+ uint32_t orig_io_port = io_port;
+#endif
switch (io_port) {
case QXL_IO_RESET:
@@ -926,27 +1147,83 @@ static void ioport_write(void *opaque, uint32_t addr, uint32_t val)
case QXL_IO_MEMSLOT_ADD:
case QXL_IO_MEMSLOT_DEL:
case QXL_IO_CREATE_PRIMARY:
+ case QXL_IO_UPDATE_IRQ:
+ case QXL_IO_LOG:
+#if SPICE_INTERFACE_QXL_MINOR >= 1
+ case QXL_IO_MEMSLOT_ADD_ASYNC:
+ case QXL_IO_CREATE_PRIMARY_ASYNC:
+#endif
break;
default:
- if (d->mode == QXL_MODE_NATIVE || d->mode == QXL_MODE_COMPAT)
+ if (d->mode != QXL_MODE_VGA) {
break;
- dprint(d, 1, "%s: unexpected port 0x%x in vga mode\n", __FUNCTION__, io_port);
+ }
+ dprint(d, 1, "%s: unexpected port 0x%x (%s) in vga mode\n",
+ __func__, io_port, io_port_to_string(io_port));
+#if SPICE_INTERFACE_QXL_MINOR >= 1
+ /* be nice to buggy guest drivers */
+ if (io_port >= QXL_IO_UPDATE_AREA_ASYNC &&
+ io_port <= QXL_IO_DESTROY_ALL_SURFACES_ASYNC) {
+ qxl_send_events(d, QXL_INTERRUPT_IO_CMD);
+ }
+#endif
return;
}
+#if SPICE_INTERFACE_QXL_MINOR >= 1
+ /* we change the io_port to avoid ifdeffery in the main switch */
+ orig_io_port = io_port;
+ switch (io_port) {
+ case QXL_IO_UPDATE_AREA_ASYNC:
+ io_port = QXL_IO_UPDATE_AREA;
+ goto async_common;
+ case QXL_IO_MEMSLOT_ADD_ASYNC:
+ io_port = QXL_IO_MEMSLOT_ADD;
+ goto async_common;
+ case QXL_IO_CREATE_PRIMARY_ASYNC:
+ io_port = QXL_IO_CREATE_PRIMARY;
+ goto async_common;
+ case QXL_IO_DESTROY_PRIMARY_ASYNC:
+ io_port = QXL_IO_DESTROY_PRIMARY;
+ goto async_common;
+ case QXL_IO_DESTROY_SURFACE_ASYNC:
+ io_port = QXL_IO_DESTROY_SURFACE_WAIT;
+ goto async_common;
+ case QXL_IO_DESTROY_ALL_SURFACES_ASYNC:
+ io_port = QXL_IO_DESTROY_ALL_SURFACES;
+ goto async_common;
+ case QXL_IO_FLUSH_SURFACES_ASYNC:
+async_common:
+ async = QXL_ASYNC;
+ qemu_mutex_lock(&d->async_lock);
+ if (d->current_async != QXL_UNDEFINED_IO) {
+ qxl_guest_bug(d, "%d async started before last (%d) complete",
+ io_port, d->current_async);
+ qemu_mutex_unlock(&d->async_lock);
+ return;
+ }
+ d->current_async = orig_io_port;
+ qemu_mutex_unlock(&d->async_lock);
+ dprint(d, 2, "start async %d (%d)\n", io_port, val);
+ break;
+ default:
+ break;
+ }
+#endif
+
switch (io_port) {
case QXL_IO_UPDATE_AREA:
{
QXLRect update = d->ram->update_area;
- d->ssd.worker->update_area(d->ssd.worker, d->ram->update_surface,
- &update, NULL, 0, 0);
+ qxl_spice_update_area(d, d->ram->update_surface,
+ &update, NULL, 0, 0, async);
break;
}
case QXL_IO_NOTIFY_CMD:
- d->ssd.worker->wakeup(d->ssd.worker);
+ qemu_spice_wakeup(&d->ssd);
break;
case QXL_IO_NOTIFY_CURSOR:
- d->ssd.worker->wakeup(d->ssd.worker);
+ qemu_spice_wakeup(&d->ssd);
break;
case QXL_IO_UPDATE_IRQ:
qxl_set_irq(d);
@@ -960,7 +1237,7 @@ static void ioport_write(void *opaque, uint32_t addr, uint32_t val)
break;
}
d->oom_running = 1;
- d->ssd.worker->oom(d->ssd.worker);
+ qxl_spice_oom(d);
d->oom_running = 0;
break;
case QXL_IO_SET_MODE:
@@ -969,7 +1246,8 @@ static void ioport_write(void *opaque, uint32_t addr, uint32_t val)
break;
case QXL_IO_LOG:
if (d->guestdebug) {
- fprintf(stderr, "qxl/guest: %s", d->ram->log_buf);
+ fprintf(stderr, "qxl/guest-%d: %ld: %s", d->id,
+ qemu_get_clock_ns(vm_clock), d->ram->log_buf);
}
break;
case QXL_IO_RESET:
@@ -977,35 +1255,97 @@ static void ioport_write(void *opaque, uint32_t addr, uint32_t val)
qxl_hard_reset(d, 0);
break;
case QXL_IO_MEMSLOT_ADD:
- PANIC_ON(val >= NUM_MEMSLOTS);
- PANIC_ON(d->guest_slots[val].active);
+ if (val >= NUM_MEMSLOTS) {
+ qxl_guest_bug(d, "QXL_IO_MEMSLOT_ADD: val out of range");
+ break;
+ }
+ if (d->guest_slots[val].active) {
+ qxl_guest_bug(d, "QXL_IO_MEMSLOT_ADD: memory slot already active");
+ break;
+ }
d->guest_slots[val].slot = d->ram->mem_slot;
- qxl_add_memslot(d, val, 0);
+ qxl_add_memslot(d, val, 0, async);
break;
case QXL_IO_MEMSLOT_DEL:
+ if (val >= NUM_MEMSLOTS) {
+ qxl_guest_bug(d, "QXL_IO_MEMSLOT_DEL: val out of range");
+ break;
+ }
qxl_del_memslot(d, val);
break;
case QXL_IO_CREATE_PRIMARY:
- PANIC_ON(val != 0);
- dprint(d, 1, "QXL_IO_CREATE_PRIMARY\n");
+ if (val != 0) {
+ qxl_guest_bug(d, "QXL_IO_CREATE_PRIMARY (async=%d): val != 0",
+ async);
+ goto cancel_async;
+ }
+ dprint(d, 1, "QXL_IO_CREATE_PRIMARY async=%d\n", async);
d->guest_primary.surface = d->ram->create_surface;
- qxl_create_guest_primary(d, 0);
+ qxl_create_guest_primary(d, 0, async);
break;
case QXL_IO_DESTROY_PRIMARY:
- PANIC_ON(val != 0);
- dprint(d, 1, "QXL_IO_DESTROY_PRIMARY\n");
- qxl_destroy_primary(d);
+ if (val != 0) {
+ qxl_guest_bug(d, "QXL_IO_DESTROY_PRIMARY (async=%d): val != 0",
+ async);
+ goto cancel_async;
+ }
+ dprint(d, 1, "QXL_IO_DESTROY_PRIMARY (async=%d) (%s)\n", async,
+ qxl_mode_to_string(d->mode));
+ if (!qxl_destroy_primary(d, async)) {
+ dprint(d, 1, "QXL_IO_DESTROY_PRIMARY_ASYNC in %s, ignored\n",
+ qxl_mode_to_string(d->mode));
+ goto cancel_async;
+ }
break;
case QXL_IO_DESTROY_SURFACE_WAIT:
- d->ssd.worker->destroy_surface_wait(d->ssd.worker, val);
+ if (val >= NUM_SURFACES) {
+ qxl_guest_bug(d, "QXL_IO_DESTROY_SURFACE (async=%d):"
+ "%d >= NUM_SURFACES", async, val);
+ goto cancel_async;
+ }
+ qxl_spice_destroy_surface_wait(d, val, async);
+ break;
+#if SPICE_INTERFACE_QXL_MINOR >= 1
+ case QXL_IO_FLUSH_RELEASE: {
+ QXLReleaseRing *ring = &d->ram->release_ring;
+ if (ring->prod - ring->cons + 1 == ring->num_items) {
+ fprintf(stderr,
+ "ERROR: no flush, full release ring [p%d,%dc]\n",
+ ring->prod, ring->cons);
+ }
+ qxl_push_free_res(d, 1 /* flush */);
+ dprint(d, 1, "QXL_IO_FLUSH_RELEASE exit (%s, s#=%d, res#=%d,%p)\n",
+ qxl_mode_to_string(d->mode), d->guest_surfaces.count,
+ d->num_free_res, d->last_release);
+ break;
+ }
+ case QXL_IO_FLUSH_SURFACES_ASYNC:
+ dprint(d, 1, "QXL_IO_FLUSH_SURFACES_ASYNC (%d) (%s, s#=%d, res#=%d)\n",
+ val, qxl_mode_to_string(d->mode), d->guest_surfaces.count,
+ d->num_free_res);
+ qxl_spice_flush_surfaces_async(d);
break;
+#endif
case QXL_IO_DESTROY_ALL_SURFACES:
- d->ssd.worker->destroy_surfaces(d->ssd.worker);
+ d->mode = QXL_MODE_UNDEFINED;
+ qxl_spice_destroy_surfaces(d, async);
break;
default:
fprintf(stderr, "%s: ioport=0x%x, abort()\n", __FUNCTION__, io_port);
abort();
}
+ return;
+cancel_async:
+#if SPICE_INTERFACE_QXL_MINOR >= 1
+ if (async) {
+ qxl_send_events(d, QXL_INTERRUPT_IO_CMD);
+ qemu_mutex_lock(&d->async_lock);
+ d->current_async = QXL_UNDEFINED_IO;
+ qemu_mutex_unlock(&d->async_lock);
+ }
+#else
+ return;
+#endif
}
static uint32_t ioport_read(void *opaque, uint32_t addr)
@@ -1167,11 +1507,14 @@ static void qxl_vm_change_state_handler(void *opaque, int running, int reason)
qemu_spice_vm_change_state_handler(&qxl->ssd, running, reason);
if (!running && qxl->mode == QXL_MODE_NATIVE) {
- /* dirty all vram (which holds surfaces) to make sure it is saved */
+ /* dirty all vram (which holds surfaces) and devram (primary surface)
+ * to make sure they are saved */
/* FIXME #1: should go out during "live" stage */
/* FIXME #2: we only need to save the areas which are actually used */
- ram_addr_t addr = qxl->vram_offset;
- qxl_set_dirty(addr, addr + qxl->vram_size);
+ ram_addr_t vram_addr = qxl->vram_offset;
+ ram_addr_t surface0_addr = qxl->vga.vram_offset + qxl->shadow_rom.draw_area_offset;
+ qxl_set_dirty(vram_addr, vram_addr + qxl->vram_size);
+ qxl_set_dirty(surface0_addr, surface0_addr + qxl->shadow_rom.surface0_area_size);
}
}
@@ -1207,7 +1550,6 @@ static DisplayChangeListener display_listener = {
static int qxl_init_common(PCIQXLDevice *qxl)
{
uint8_t* config = qxl->pci.config;
- uint32_t pci_device_id;
uint32_t pci_device_rev;
uint32_t io_size;
@@ -1215,23 +1557,25 @@ static int qxl_init_common(PCIQXLDevice *qxl)
qxl->generation = 1;
qxl->num_memslots = NUM_MEMSLOTS;
qxl->num_surfaces = NUM_SURFACES;
+ qemu_mutex_init(&qxl->track_lock);
+ qemu_mutex_init(&qxl->async_lock);
+ qxl->current_async = QXL_UNDEFINED_IO;
switch (qxl->revision) {
case 1: /* spice 0.4 -- qxl-1 */
- pci_device_id = QXL_DEVICE_ID_STABLE;
pci_device_rev = QXL_REVISION_STABLE_V04;
break;
case 2: /* spice 0.6 -- qxl-2 */
- pci_device_id = QXL_DEVICE_ID_STABLE;
pci_device_rev = QXL_REVISION_STABLE_V06;
break;
- default: /* experimental */
- pci_device_id = QXL_DEVICE_ID_DEVEL;
- pci_device_rev = 1;
+#if SPICE_INTERFACE_QXL_MINOR >= 1
+ case 3: /* qxl-3 */
+#endif
+ default:
+ pci_device_rev = QXL_DEFAULT_REVISION;
break;
}
- pci_config_set_device_id(config, pci_device_id);
pci_set_byte(&config[PCI_REVISION_ID], pci_device_rev);
pci_set_byte(&config[PCI_INTERRUPT_PIN], 1);
@@ -1300,12 +1644,7 @@ static int qxl_init_primary(PCIDevice *dev)
vga->ds = graphic_console_init(qxl_hw_update, qxl_hw_invalidate,
qxl_hw_screen_dump, qxl_hw_text_update, qxl);
- qxl->ssd.ds = vga->ds;
- qemu_mutex_init(&qxl->ssd.lock);
- qxl->ssd.mouse_x = -1;
- qxl->ssd.mouse_y = -1;
- qxl->ssd.bufsize = (16 * 1024 * 1024);
- qxl->ssd.buf = qemu_malloc(qxl->ssd.bufsize);
+ qemu_spice_display_init_common(&qxl->ssd, vga->ds);
qxl0 = qxl;
register_displaychangelistener(vga->ds, &display_listener);
@@ -1375,7 +1714,8 @@ static int qxl_post_load(void *opaque, int version)
d->modes = (QXLModes*)((uint8_t*)d->rom + d->rom->modes_offset);
- dprint(d, 1, "%s: restore mode\n", __FUNCTION__);
+ dprint(d, 1, "%s: restore mode (%s)\n", __FUNCTION__,
+ qxl_mode_to_string(d->mode));
newmode = d->mode;
d->mode = QXL_MODE_UNDEFINED;
switch (newmode) {
@@ -1389,9 +1729,9 @@ static int qxl_post_load(void *opaque, int version)
if (!d->guest_slots[i].active) {
continue;
}
- qxl_add_memslot(d, i, 0);
+ qxl_add_memslot(d, i, 0, QXL_SYNC);
}
- qxl_create_guest_primary(d, 1);
+ qxl_create_guest_primary(d, 1, QXL_SYNC);
/* replay surface-create and cursor-set commands */
cmds = qemu_mallocz(sizeof(QXLCommandExt) * (NUM_SURFACES + 1));
@@ -1408,7 +1748,7 @@ static int qxl_post_load(void *opaque, int version)
cmds[out].cmd.type = QXL_CMD_CURSOR;
cmds[out].group_id = MEMSLOT_GROUP_GUEST;
out++;
- d->ssd.worker->loadvm_commands(d->ssd.worker, cmds, out);
+ qxl_spice_loadvm_commands(d, cmds, out);
qemu_free(cmds);
break;
@@ -1492,11 +1832,15 @@ static PCIDeviceInfo qxl_info_primary = {
.config_write = qxl_write_config,
.romfile = "vgabios-qxl.bin",
.vendor_id = REDHAT_PCI_VENDOR_ID,
+ .device_id = QXL_DEVICE_ID_STABLE,
.class_id = PCI_CLASS_DISPLAY_VGA,
.qdev.props = (Property[]) {
- DEFINE_PROP_UINT32("ram_size", PCIQXLDevice, vga.vram_size, 64 * 1024 * 1024),
- DEFINE_PROP_UINT32("vram_size", PCIQXLDevice, vram_size, 64 * 1024 * 1024),
- DEFINE_PROP_UINT32("revision", PCIQXLDevice, revision, 2),
+ DEFINE_PROP_UINT32("ram_size", PCIQXLDevice, vga.vram_size,
+ 64 * 1024 * 1024),
+ DEFINE_PROP_UINT32("vram_size", PCIQXLDevice, vram_size,
+ 64 * 1024 * 1024),
+ DEFINE_PROP_UINT32("revision", PCIQXLDevice, revision,
+ QXL_DEFAULT_REVISION),
DEFINE_PROP_UINT32("debug", PCIQXLDevice, debug, 0),
DEFINE_PROP_UINT32("guestdebug", PCIQXLDevice, guestdebug, 0),
DEFINE_PROP_UINT32("cmdlog", PCIQXLDevice, cmdlog, 0),
@@ -1512,11 +1856,15 @@ static PCIDeviceInfo qxl_info_secondary = {
.qdev.vmsd = &qxl_vmstate,
.init = qxl_init_secondary,
.vendor_id = REDHAT_PCI_VENDOR_ID,
+ .device_id = QXL_DEVICE_ID_STABLE,
.class_id = PCI_CLASS_DISPLAY_OTHER,
.qdev.props = (Property[]) {
- DEFINE_PROP_UINT32("ram_size", PCIQXLDevice, vga.vram_size, 64 * 1024 * 1024),
- DEFINE_PROP_UINT32("vram_size", PCIQXLDevice, vram_size, 64 * 1024 * 1024),
- DEFINE_PROP_UINT32("revision", PCIQXLDevice, revision, 2),
+ DEFINE_PROP_UINT32("ram_size", PCIQXLDevice, vga.vram_size,
+ 64 * 1024 * 1024),
+ DEFINE_PROP_UINT32("vram_size", PCIQXLDevice, vram_size,
+ 64 * 1024 * 1024),
+ DEFINE_PROP_UINT32("revision", PCIQXLDevice, revision,
+ QXL_DEFAULT_REVISION),
DEFINE_PROP_UINT32("debug", PCIQXLDevice, debug, 0),
DEFINE_PROP_UINT32("guestdebug", PCIQXLDevice, guestdebug, 0),
DEFINE_PROP_UINT32("cmdlog", PCIQXLDevice, cmdlog, 0),
diff --git a/hw/qxl.h b/hw/qxl.h
index f6c450d..4bcf7e1 100644
--- a/hw/qxl.h
+++ b/hw/qxl.h
@@ -15,6 +15,8 @@ enum qxl_mode {
QXL_MODE_NATIVE,
};
+#define QXL_UNDEFINED_IO UINT32_MAX
+
typedef struct PCIQXLDevice {
PCIDevice pci;
SimpleSpiceDisplay ssd;
@@ -30,6 +32,9 @@ typedef struct PCIQXLDevice {
int32_t num_memslots;
int32_t num_surfaces;
+ uint32_t current_async;
+ QemuMutex async_lock;
+
struct guest_slots {
QXLMemSlot slot;
void *ptr;
@@ -55,6 +60,8 @@ typedef struct PCIQXLDevice {
} guest_surfaces;
QXLPHYSICAL guest_cursor;
+ QemuMutex track_lock;
+
/* thread signaling */
pthread_t main;
int pipe[2];
@@ -84,7 +91,7 @@ typedef struct PCIQXLDevice {
#define PANIC_ON(x) if ((x)) { \
printf("%s: PANIC %s failed\n", __FUNCTION__, #x); \
- exit(-1); \
+ abort(); \
}
#define dprint(_qxl, _level, _fmt, ...) \
@@ -95,8 +102,27 @@ typedef struct PCIQXLDevice {
} \
} while (0)
+#if SPICE_INTERFACE_QXL_MINOR >= 1
+#define QXL_DEFAULT_REVISION QXL_REVISION_STABLE_V10
+#else
+#define QXL_DEFAULT_REVISION QXL_REVISION_STABLE_V06
+#endif
+
/* qxl.c */
void *qxl_phys2virt(PCIQXLDevice *qxl, QXLPHYSICAL phys, int group_id);
+void qxl_guest_bug(PCIQXLDevice *qxl, const char *msg, ...);
+
+void qxl_spice_update_area(PCIQXLDevice *qxl, uint32_t surface_id,
+ struct QXLRect *area, struct QXLRect *dirty_rects,
+ uint32_t num_dirty_rects,
+ uint32_t clear_dirty_region,
+ qxl_async_io async);
+void qxl_spice_loadvm_commands(PCIQXLDevice *qxl, struct QXLCommandExt *ext,
+ uint32_t count);
+void qxl_spice_oom(PCIQXLDevice *qxl);
+void qxl_spice_reset_memslots(PCIQXLDevice *qxl);
+void qxl_spice_reset_image_cache(PCIQXLDevice *qxl);
+void qxl_spice_reset_cursor(PCIQXLDevice *qxl);
/* qxl-logger.c */
void qxl_log_cmd_cursor(PCIQXLDevice *qxl, QXLCursorCmd *cmd, int group_id);
@@ -106,3 +132,9 @@ void qxl_log_command(PCIQXLDevice *qxl, const char *ring, QXLCommandExt *ext);
void qxl_render_resize(PCIQXLDevice *qxl);
void qxl_render_update(PCIQXLDevice *qxl);
void qxl_render_cursor(PCIQXLDevice *qxl, QXLCommandExt *ext);
+#if SPICE_INTERFACE_QXL_MINOR >= 1
+void qxl_spice_update_area_async(PCIQXLDevice *qxl, uint32_t surface_id,
+ struct QXLRect *area,
+ uint32_t clear_dirty_region,
+ int is_vga);
+#endif
diff --git a/hw/realview.c b/hw/realview.c
index 82f3d82..94ab900 100644
--- a/hw/realview.c
+++ b/hw/realview.c
@@ -4,7 +4,7 @@
* Copyright (c) 2006-2007 CodeSourcery.
* Written by Paul Brook
*
- * This code is licenced under the GPL.
+ * This code is licensed under the GPL.
*/
#include "sysbus.h"
diff --git a/hw/realview_gic.c b/hw/realview_gic.c
index db908b6..43a2a0d 100644
--- a/hw/realview_gic.c
+++ b/hw/realview_gic.c
@@ -4,7 +4,7 @@
* Copyright (c) 2006-2007 CodeSourcery.
* Written by Paul Brook
*
- * This code is licenced under the GPL.
+ * This code is licensed under the GPL.
*/
#include "sysbus.h"
diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c
index ad6a730..0b0344c 100644
--- a/hw/scsi-bus.c
+++ b/hw/scsi-bus.c
@@ -131,7 +131,8 @@ int scsi_bus_legacy_handle_cmdline(SCSIBus *bus)
return res;
}
-SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, uint32_t tag, uint32_t lun)
+SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, uint32_t tag,
+ uint32_t lun, void *hba_private)
{
SCSIRequest *req;
@@ -141,14 +142,16 @@ SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, uint32_t tag, uint32_t l
req->dev = d;
req->tag = tag;
req->lun = lun;
+ req->hba_private = hba_private;
req->status = -1;
trace_scsi_req_alloc(req->dev->id, req->lun, req->tag);
return req;
}
-SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag, uint32_t lun)
+SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag, uint32_t lun,
+ void *hba_private)
{
- return d->info->alloc_req(d, tag, lun);
+ return d->info->alloc_req(d, tag, lun, hba_private);
}
uint8_t *scsi_req_get_buf(SCSIRequest *req)
@@ -220,7 +223,7 @@ static int scsi_req_length(SCSIRequest *req, uint8_t *cmd)
switch(cmd[0]) {
case TEST_UNIT_READY:
- case REZERO_UNIT:
+ case REWIND:
case START_STOP:
case SEEK_6:
case WRITE_FILEMARKS:
@@ -229,24 +232,24 @@ static int scsi_req_length(SCSIRequest *req, uint8_t *cmd)
case RELEASE:
case ERASE:
case ALLOW_MEDIUM_REMOVAL:
- case VERIFY:
+ case VERIFY_10:
case SEEK_10:
case SYNCHRONIZE_CACHE:
case LOCK_UNLOCK_CACHE:
case LOAD_UNLOAD:
case SET_CD_SPEED:
case SET_LIMITS:
- case WRITE_LONG:
+ case WRITE_LONG_10:
case MOVE_MEDIUM:
case UPDATE_BLOCK:
req->cmd.xfer = 0;
break;
case MODE_SENSE:
break;
- case WRITE_SAME:
+ case WRITE_SAME_10:
req->cmd.xfer = 1;
break;
- case READ_CAPACITY:
+ case READ_CAPACITY_10:
req->cmd.xfer = 8;
break;
case READ_BLOCK_LIMITS:
@@ -262,7 +265,7 @@ static int scsi_req_length(SCSIRequest *req, uint8_t *cmd)
req->cmd.xfer *= 8;
break;
case WRITE_10:
- case WRITE_VERIFY:
+ case WRITE_VERIFY_10:
case WRITE_6:
case WRITE_12:
case WRITE_VERIFY_12:
@@ -322,7 +325,7 @@ static void scsi_req_xfer_mode(SCSIRequest *req)
switch (req->cmd.buf[0]) {
case WRITE_6:
case WRITE_10:
- case WRITE_VERIFY:
+ case WRITE_VERIFY_10:
case WRITE_12:
case WRITE_VERIFY_12:
case WRITE_16:
@@ -342,15 +345,13 @@ static void scsi_req_xfer_mode(SCSIRequest *req)
case SEARCH_HIGH:
case SEARCH_LOW:
case UPDATE_BLOCK:
- case WRITE_LONG:
- case WRITE_SAME:
+ case WRITE_LONG_10:
+ case WRITE_SAME_10:
case SEARCH_HIGH_12:
case SEARCH_EQUAL_12:
case SEARCH_LOW_12:
- case SET_WINDOW:
case MEDIUM_SCAN:
case SEND_VOLUME_TAG:
- case WRITE_LONG_2:
case PERSISTENT_RESERVE_OUT:
case MAINTENANCE_OUT:
req->cmd.mode = SCSI_XFER_TO_DEV;
@@ -514,8 +515,7 @@ static const char *scsi_command_name(uint8_t cmd)
{
static const char *names[] = {
[ TEST_UNIT_READY ] = "TEST_UNIT_READY",
- [ REZERO_UNIT ] = "REZERO_UNIT",
- /* REWIND and REZERO_UNIT use the same operation code */
+ [ REWIND ] = "REWIND",
[ REQUEST_SENSE ] = "REQUEST_SENSE",
[ FORMAT_UNIT ] = "FORMAT_UNIT",
[ READ_BLOCK_LIMITS ] = "READ_BLOCK_LIMITS",
@@ -540,14 +540,12 @@ static const char *scsi_command_name(uint8_t cmd)
[ RECEIVE_DIAGNOSTIC ] = "RECEIVE_DIAGNOSTIC",
[ SEND_DIAGNOSTIC ] = "SEND_DIAGNOSTIC",
[ ALLOW_MEDIUM_REMOVAL ] = "ALLOW_MEDIUM_REMOVAL",
-
- [ SET_WINDOW ] = "SET_WINDOW",
- [ READ_CAPACITY ] = "READ_CAPACITY",
+ [ READ_CAPACITY_10 ] = "READ_CAPACITY_10",
[ READ_10 ] = "READ_10",
[ WRITE_10 ] = "WRITE_10",
[ SEEK_10 ] = "SEEK_10",
- [ WRITE_VERIFY ] = "WRITE_VERIFY",
- [ VERIFY ] = "VERIFY",
+ [ WRITE_VERIFY_10 ] = "WRITE_VERIFY_10",
+ [ VERIFY_10 ] = "VERIFY_10",
[ SEARCH_HIGH ] = "SEARCH_HIGH",
[ SEARCH_EQUAL ] = "SEARCH_EQUAL",
[ SEARCH_LOW ] = "SEARCH_LOW",
@@ -563,11 +561,14 @@ static const char *scsi_command_name(uint8_t cmd)
[ WRITE_BUFFER ] = "WRITE_BUFFER",
[ READ_BUFFER ] = "READ_BUFFER",
[ UPDATE_BLOCK ] = "UPDATE_BLOCK",
- [ READ_LONG ] = "READ_LONG",
- [ WRITE_LONG ] = "WRITE_LONG",
+ [ READ_LONG_10 ] = "READ_LONG_10",
+ [ WRITE_LONG_10 ] = "WRITE_LONG_10",
[ CHANGE_DEFINITION ] = "CHANGE_DEFINITION",
- [ WRITE_SAME ] = "WRITE_SAME",
+ [ WRITE_SAME_10 ] = "WRITE_SAME_10",
+ [ UNMAP ] = "UNMAP",
[ READ_TOC ] = "READ_TOC",
+ [ REPORT_DENSITY_SUPPORT ] = "REPORT_DENSITY_SUPPORT",
+ [ GET_CONFIGURATION ] = "GET_CONFIGURATION",
[ LOG_SELECT ] = "LOG_SELECT",
[ LOG_SENSE ] = "LOG_SENSE",
[ MODE_SELECT_10 ] = "MODE_SELECT_10",
@@ -576,27 +577,39 @@ static const char *scsi_command_name(uint8_t cmd)
[ MODE_SENSE_10 ] = "MODE_SENSE_10",
[ PERSISTENT_RESERVE_IN ] = "PERSISTENT_RESERVE_IN",
[ PERSISTENT_RESERVE_OUT ] = "PERSISTENT_RESERVE_OUT",
+ [ WRITE_FILEMARKS_16 ] = "WRITE_FILEMARKS_16",
+ [ EXTENDED_COPY ] = "EXTENDED_COPY",
+ [ ATA_PASSTHROUGH ] = "ATA_PASSTHROUGH",
+ [ ACCESS_CONTROL_IN ] = "ACCESS_CONTROL_IN",
+ [ ACCESS_CONTROL_OUT ] = "ACCESS_CONTROL_OUT",
+ [ READ_16 ] = "READ_16",
+ [ COMPARE_AND_WRITE ] = "COMPARE_AND_WRITE",
+ [ WRITE_16 ] = "WRITE_16",
+ [ WRITE_VERIFY_16 ] = "WRITE_VERIFY_16",
+ [ VERIFY_16 ] = "VERIFY_16",
+ [ SYNCHRONIZE_CACHE_16 ] = "SYNCHRONIZE_CACHE_16",
+ [ LOCATE_16 ] = "LOCATE_16",
+ [ WRITE_SAME_16 ] = "WRITE_SAME_16",
+ [ ERASE_16 ] = "ERASE_16",
+ [ SERVICE_ACTION_IN ] = "SERVICE_ACTION_IN",
+ [ WRITE_LONG_16 ] = "WRITE_LONG_16",
+ [ REPORT_LUNS ] = "REPORT_LUNS",
+ [ BLANK ] = "BLANK",
+ [ MAINTENANCE_IN ] = "MAINTENANCE_IN",
+ [ MAINTENANCE_OUT ] = "MAINTENANCE_OUT",
[ MOVE_MEDIUM ] = "MOVE_MEDIUM",
+ [ LOAD_UNLOAD ] = "LOAD_UNLOAD",
[ READ_12 ] = "READ_12",
[ WRITE_12 ] = "WRITE_12",
[ WRITE_VERIFY_12 ] = "WRITE_VERIFY_12",
+ [ VERIFY_12 ] = "VERIFY_12",
[ SEARCH_HIGH_12 ] = "SEARCH_HIGH_12",
[ SEARCH_EQUAL_12 ] = "SEARCH_EQUAL_12",
[ SEARCH_LOW_12 ] = "SEARCH_LOW_12",
[ READ_ELEMENT_STATUS ] = "READ_ELEMENT_STATUS",
[ SEND_VOLUME_TAG ] = "SEND_VOLUME_TAG",
- [ WRITE_LONG_2 ] = "WRITE_LONG_2",
-
- [ REPORT_DENSITY_SUPPORT ] = "REPORT_DENSITY_SUPPORT",
- [ GET_CONFIGURATION ] = "GET_CONFIGURATION",
- [ READ_16 ] = "READ_16",
- [ WRITE_16 ] = "WRITE_16",
- [ WRITE_VERIFY_16 ] = "WRITE_VERIFY_16",
- [ SERVICE_ACTION_IN ] = "SERVICE_ACTION_IN",
- [ REPORT_LUNS ] = "REPORT_LUNS",
- [ LOAD_UNLOAD ] = "LOAD_UNLOAD",
+ [ READ_DEFECT_DATA_12 ] = "READ_DEFECT_DATA_12",
[ SET_CD_SPEED ] = "SET_CD_SPEED",
- [ BLANK ] = "BLANK",
};
if (cmd >= ARRAY_SIZE(names) || names[cmd] == NULL)
diff --git a/hw/scsi-defs.h b/hw/scsi-defs.h
index 413cce0..27010b7 100644
--- a/hw/scsi-defs.h
+++ b/hw/scsi-defs.h
@@ -25,7 +25,7 @@
*/
#define TEST_UNIT_READY 0x00
-#define REZERO_UNIT 0x01
+#define REWIND 0x01
#define REQUEST_SENSE 0x03
#define FORMAT_UNIT 0x04
#define READ_BLOCK_LIMITS 0x05
@@ -48,14 +48,13 @@
#define RECEIVE_DIAGNOSTIC 0x1c
#define SEND_DIAGNOSTIC 0x1d
#define ALLOW_MEDIUM_REMOVAL 0x1e
-
-#define SET_WINDOW 0x24
-#define READ_CAPACITY 0x25
+#define READ_CAPACITY_10 0x25
#define READ_10 0x28
#define WRITE_10 0x2a
#define SEEK_10 0x2b
-#define WRITE_VERIFY 0x2e
-#define VERIFY 0x2f
+#define LOCATE_10 0x2b
+#define WRITE_VERIFY_10 0x2e
+#define VERIFY_10 0x2f
#define SEARCH_HIGH 0x30
#define SEARCH_EQUAL 0x31
#define SEARCH_LOW 0x32
@@ -71,11 +70,14 @@
#define WRITE_BUFFER 0x3b
#define READ_BUFFER 0x3c
#define UPDATE_BLOCK 0x3d
-#define READ_LONG 0x3e
-#define WRITE_LONG 0x3f
+#define READ_LONG_10 0x3e
+#define WRITE_LONG_10 0x3f
#define CHANGE_DEFINITION 0x40
-#define WRITE_SAME 0x41
+#define WRITE_SAME_10 0x41
+#define UNMAP 0x42
#define READ_TOC 0x43
+#define REPORT_DENSITY_SUPPORT 0x44
+#define GET_CONFIGURATION 0x46
#define LOG_SELECT 0x4c
#define LOG_SENSE 0x4d
#define MODE_SELECT_10 0x55
@@ -84,32 +86,40 @@
#define MODE_SENSE_10 0x5a
#define PERSISTENT_RESERVE_IN 0x5e
#define PERSISTENT_RESERVE_OUT 0x5f
+#define VARLENGTH_CDB 0x7f
+#define WRITE_FILEMARKS_16 0x80
+#define EXTENDED_COPY 0x83
+#define ATA_PASSTHROUGH 0x85
+#define ACCESS_CONTROL_IN 0x86
+#define ACCESS_CONTROL_OUT 0x87
+#define READ_16 0x88
+#define COMPARE_AND_WRITE 0x89
+#define WRITE_16 0x8a
+#define WRITE_VERIFY_16 0x8e
+#define VERIFY_16 0x8f
+#define SYNCHRONIZE_CACHE_16 0x91
+#define LOCATE_16 0x92
#define WRITE_SAME_16 0x93
+#define ERASE_16 0x93
+#define SERVICE_ACTION_IN 0x9e
+#define WRITE_LONG_16 0x9f
+#define REPORT_LUNS 0xa0
+#define BLANK 0xa1
#define MAINTENANCE_IN 0xa3
#define MAINTENANCE_OUT 0xa4
#define MOVE_MEDIUM 0xa5
+#define LOAD_UNLOAD 0xa6
#define READ_12 0xa8
#define WRITE_12 0xaa
#define WRITE_VERIFY_12 0xae
+#define VERIFY_12 0xaf
#define SEARCH_HIGH_12 0xb0
#define SEARCH_EQUAL_12 0xb1
#define SEARCH_LOW_12 0xb2
#define READ_ELEMENT_STATUS 0xb8
#define SEND_VOLUME_TAG 0xb6
-#define WRITE_LONG_2 0xea
-
-/* from hw/scsi-generic.c */
-#define REWIND 0x01
-#define REPORT_DENSITY_SUPPORT 0x44
-#define GET_CONFIGURATION 0x46
-#define READ_16 0x88
-#define WRITE_16 0x8a
-#define WRITE_VERIFY_16 0x8e
-#define SERVICE_ACTION_IN 0x9e
-#define REPORT_LUNS 0xa0
-#define LOAD_UNLOAD 0xa6
-#define SET_CD_SPEED 0xbb
-#define BLANK 0xa1
+#define READ_DEFECT_DATA_12 0xb7
+#define SET_CD_SPEED 0xbb
/*
* SAM Status codes
@@ -154,6 +164,7 @@
#define TYPE_DISK 0x00
#define TYPE_TAPE 0x01
+#define TYPE_PRINTER 0x02
#define TYPE_PROCESSOR 0x03 /* HP scanners use this */
#define TYPE_WORM 0x04 /* Treated as ROM by our system */
#define TYPE_ROM 0x05
@@ -161,6 +172,9 @@
#define TYPE_MOD 0x07 /* Magneto-optical disk -
* - treated as TYPE_DISK */
#define TYPE_MEDIUM_CHANGER 0x08
-#define TYPE_ENCLOSURE 0x0d /* Enclosure Services Device */
+#define TYPE_STORAGE_ARRAY 0x0c /* Storage array device */
+#define TYPE_ENCLOSURE 0x0d /* Enclosure Services Device */
+#define TYPE_RBC 0x0e /* Simplified Direct-Access Device */
+#define TYPE_OSD 0x11 /* Object-storage Device */
#define TYPE_NO_LUN 0x7f
diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c
index a8c7372..fa198f9 100644
--- a/hw/scsi-disk.c
+++ b/hw/scsi-disk.c
@@ -12,7 +12,7 @@
* 2009-Oct-13 Artyom Tarasenko : implemented the block descriptor in the
* MODE SENSE response.
*
- * This code is licenced under the LGPL.
+ * This code is licensed under the LGPL.
*
* Note that this file only handles the SCSI architecture model and device
* commands. Emulation of interface/link layer protocols is handled by
@@ -59,8 +59,6 @@ typedef struct SCSIDiskReq {
uint32_t status;
} SCSIDiskReq;
-typedef enum { SCSI_HD, SCSI_CD } SCSIDriveKind;
-
struct SCSIDiskState
{
SCSIDevice qdev;
@@ -74,20 +72,19 @@ struct SCSIDiskState
char *version;
char *serial;
SCSISense sense;
- SCSIDriveKind drive_kind;
};
static int scsi_handle_rw_error(SCSIDiskReq *r, int error, int type);
static int scsi_disk_emulate_command(SCSIDiskReq *r, uint8_t *outbuf);
static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag,
- uint32_t lun)
+ uint32_t lun, void *hba_private)
{
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d);
SCSIRequest *req;
SCSIDiskReq *r;
- req = scsi_req_alloc(sizeof(SCSIDiskReq), &s->qdev, tag, lun);
+ req = scsi_req_alloc(sizeof(SCSIDiskReq), &s->qdev, tag, lun, hba_private);
r = DO_UPCAST(SCSIDiskReq, req, req);
r->iov.iov_base = qemu_blockalign(s->bs, SCSI_DMA_BUF_SIZE);
return req;
@@ -382,7 +379,7 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
return -1;
}
- if (s->drive_kind == SCSI_CD) {
+ if (s->qdev.type == TYPE_ROM) {
outbuf[buflen++] = 5;
} else {
outbuf[buflen++] = 0;
@@ -398,9 +395,10 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
"buffer size %zd\n", req->cmd.xfer);
pages = buflen++;
outbuf[buflen++] = 0x00; // list of supported pages (this page)
- outbuf[buflen++] = 0x80; // unit serial number
+ if (s->serial)
+ outbuf[buflen++] = 0x80; // unit serial number
outbuf[buflen++] = 0x83; // device identification
- if (s->drive_kind == SCSI_HD) {
+ if (s->qdev.type == TYPE_DISK) {
outbuf[buflen++] = 0xb0; // block limits
outbuf[buflen++] = 0xb2; // thin provisioning
}
@@ -409,8 +407,14 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
}
case 0x80: /* Device serial number, optional */
{
- int l = strlen(s->serial);
+ int l;
+ if (!s->serial) {
+ DPRINTF("Inquiry (EVPD[Serial number] not supported\n");
+ return -1;
+ }
+
+ l = strlen(s->serial);
if (l > req->cmd.xfer)
l = req->cmd.xfer;
if (l > 20)
@@ -453,7 +457,7 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
unsigned int opt_io_size =
s->qdev.conf.opt_io_size / s->qdev.blocksize;
- if (s->drive_kind == SCSI_CD) {
+ if (s->qdev.type == TYPE_ROM) {
DPRINTF("Inquiry (EVPD[%02X] not supported for CDROM\n",
page_code);
return -1;
@@ -519,16 +523,15 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
memset(outbuf, 0, buflen);
if (req->lun) {
- outbuf[0] = 0x7f; /* LUN not supported */
+ outbuf[0] = 0x7f; /* LUN not supported */
return buflen;
}
- if (s->drive_kind == SCSI_CD) {
- outbuf[0] = 5;
+ outbuf[0] = s->qdev.type & 0x1f;
+ if (s->qdev.type == TYPE_ROM) {
outbuf[1] = 0x80;
memcpy(&outbuf[16], "QEMU CD-ROM ", 16);
} else {
- outbuf[0] = 0;
outbuf[1] = s->removable ? 0x80 : 0;
memcpy(&outbuf[16], "QEMU HARDDISK ", 16);
}
@@ -654,7 +657,7 @@ static int mode_sense_page(SCSIRequest *req, int page, uint8_t *p,
return p[1] + 2;
case 0x2a: /* CD Capabilities and Mechanical Status page. */
- if (s->drive_kind != SCSI_CD)
+ if (s->qdev.type != TYPE_ROM)
return 0;
p[0] = 0x2a;
p[1] = 0x14;
@@ -829,7 +832,7 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r, uint8_t *outbuf)
case TEST_UNIT_READY:
if (!bdrv_is_inserted(s->bs))
goto not_ready;
- break;
+ break;
case REQUEST_SENSE:
if (req->cmd.xfer < 4)
goto illegal_request;
@@ -841,7 +844,7 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r, uint8_t *outbuf)
buflen = scsi_disk_emulate_inquiry(req, outbuf);
if (buflen < 0)
goto illegal_request;
- break;
+ break;
case MODE_SENSE:
case MODE_SENSE_10:
buflen = scsi_disk_emulate_mode_sense(req, outbuf);
@@ -870,18 +873,18 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r, uint8_t *outbuf)
goto illegal_request;
break;
case START_STOP:
- if (s->drive_kind == SCSI_CD && (req->cmd.buf[4] & 2)) {
+ if (s->qdev.type == TYPE_ROM && (req->cmd.buf[4] & 2)) {
/* load/eject medium */
bdrv_eject(s->bs, !(req->cmd.buf[4] & 1));
}
- break;
+ break;
case ALLOW_MEDIUM_REMOVAL:
bdrv_set_locked(s->bs, req->cmd.buf[4] & 1);
- break;
- case READ_CAPACITY:
+ break;
+ case READ_CAPACITY_10:
/* The normal LEN field for this command is zero. */
- memset(outbuf, 0, 8);
- bdrv_get_geometry(s->bs, &nb_sectors);
+ memset(outbuf, 0, 8);
+ bdrv_get_geometry(s->bs, &nb_sectors);
if (!nb_sectors)
goto not_ready;
nb_sectors /= s->cluster_size;
@@ -901,7 +904,7 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r, uint8_t *outbuf)
outbuf[6] = s->cluster_size * 2;
outbuf[7] = 0;
buflen = 8;
- break;
+ break;
case SYNCHRONIZE_CACHE:
ret = bdrv_flush(s->bs);
if (ret < 0) {
@@ -963,13 +966,7 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r, uint8_t *outbuf)
outbuf[3] = 8;
buflen = 16;
break;
- case VERIFY:
- break;
- case REZERO_UNIT:
- DPRINTF("Rezero Unit\n");
- if (!bdrv_is_inserted(s->bs)) {
- goto not_ready;
- }
+ case VERIFY_10:
break;
default:
scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(INVALID_OPCODE));
@@ -1007,7 +1004,7 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf)
command = buf[0];
outbuf = (uint8_t *)r->iov.iov_base;
- DPRINTF("Command: lun=%d tag=0x%x data=0x%02x", lun, tag, buf[0]);
+ DPRINTF("Command: lun=%d tag=0x%x data=0x%02x", req->lun, req->tag, buf[0]);
if (scsi_req_parse(&r->req, buf) != 0) {
BADF("Unsupported command length, command %x\n", command);
@@ -1045,14 +1042,13 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf)
case RELEASE_10:
case START_STOP:
case ALLOW_MEDIUM_REMOVAL:
- case READ_CAPACITY:
+ case READ_CAPACITY_10:
case SYNCHRONIZE_CACHE:
case READ_TOC:
case GET_CONFIGURATION:
case SERVICE_ACTION_IN:
case REPORT_LUNS:
- case VERIFY:
- case REZERO_UNIT:
+ case VERIFY_10:
rc = scsi_disk_emulate_command(r, outbuf);
if (rc < 0) {
return 0;
@@ -1075,7 +1071,7 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf)
case WRITE_10:
case WRITE_12:
case WRITE_16:
- case WRITE_VERIFY:
+ case WRITE_VERIFY_10:
case WRITE_VERIFY_12:
case WRITE_VERIFY_16:
len = r->req.cmd.xfer / s->qdev.blocksize;
@@ -1183,7 +1179,7 @@ static void scsi_destroy(SCSIDevice *dev)
blockdev_mark_auto_del(s->qdev.conf.bs);
}
-static int scsi_initfn(SCSIDevice *dev, SCSIDriveKind kind)
+static int scsi_initfn(SCSIDevice *dev, uint8_t scsi_type)
{
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev);
DriveInfo *dinfo;
@@ -1193,9 +1189,8 @@ static int scsi_initfn(SCSIDevice *dev, SCSIDriveKind kind)
return -1;
}
s->bs = s->qdev.conf.bs;
- s->drive_kind = kind;
- if (kind == SCSI_HD && !bdrv_is_inserted(s->bs)) {
+ if (scsi_type == TYPE_DISK && !bdrv_is_inserted(s->bs)) {
error_report("Device needs media, but drive is empty");
return -1;
}
@@ -1203,7 +1198,9 @@ static int scsi_initfn(SCSIDevice *dev, SCSIDriveKind kind)
if (!s->serial) {
/* try to fall back to value set with legacy -drive serial=... */
dinfo = drive_get_by_blockdev(s->bs);
- s->serial = qemu_strdup(*dinfo->serial ? dinfo->serial : "0");
+ if (*dinfo->serial) {
+ s->serial = qemu_strdup(dinfo->serial);
+ }
}
if (!s->version) {
@@ -1215,44 +1212,47 @@ static int scsi_initfn(SCSIDevice *dev, SCSIDriveKind kind)
return -1;
}
- if (kind == SCSI_CD) {
+ if (scsi_type == TYPE_ROM) {
s->qdev.blocksize = 2048;
- } else {
+ } else if (scsi_type == TYPE_DISK) {
s->qdev.blocksize = s->qdev.conf.logical_block_size;
+ } else {
+ error_report("scsi-disk: Unhandled SCSI type %02x", scsi_type);
+ return -1;
}
s->cluster_size = s->qdev.blocksize / 512;
s->bs->buffer_alignment = s->qdev.blocksize;
- s->qdev.type = TYPE_DISK;
+ s->qdev.type = scsi_type;
qemu_add_vm_change_state_handler(scsi_dma_restart_cb, s);
- bdrv_set_removable(s->bs, kind == SCSI_CD);
+ bdrv_set_removable(s->bs, scsi_type == TYPE_ROM);
add_boot_device_path(s->qdev.conf.bootindex, &dev->qdev, ",0");
return 0;
}
static int scsi_hd_initfn(SCSIDevice *dev)
{
- return scsi_initfn(dev, SCSI_HD);
+ return scsi_initfn(dev, TYPE_DISK);
}
static int scsi_cd_initfn(SCSIDevice *dev)
{
- return scsi_initfn(dev, SCSI_CD);
+ return scsi_initfn(dev, TYPE_ROM);
}
static int scsi_disk_initfn(SCSIDevice *dev)
{
- SCSIDriveKind kind;
DriveInfo *dinfo;
+ uint8_t scsi_type;
if (!dev->conf.bs) {
- kind = SCSI_HD; /* will die in scsi_initfn() */
+ scsi_type = TYPE_DISK; /* will die in scsi_initfn() */
} else {
dinfo = drive_get_by_blockdev(dev->conf.bs);
- kind = dinfo->media_cd ? SCSI_CD : SCSI_HD;
+ scsi_type = dinfo->media_cd ? TYPE_ROM : TYPE_DISK;
}
- return scsi_initfn(dev, kind);
+ return scsi_initfn(dev, scsi_type);
}
#define DEFINE_SCSI_DISK_PROPERTIES() \
diff --git a/hw/scsi-generic.c b/hw/scsi-generic.c
index 8e59c7e..7b0026e 100644
--- a/hw/scsi-generic.c
+++ b/hw/scsi-generic.c
@@ -7,7 +7,7 @@
*
* Written by Laurent Vivier <Laurent.Vivier@bull.net>
*
- * This code is licenced under the LGPL.
+ * This code is licensed under the LGPL.
*
*/
@@ -96,11 +96,12 @@ static int scsi_get_sense(SCSIRequest *req, uint8_t *outbuf, int len)
return size;
}
-static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun)
+static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun,
+ void *hba_private)
{
SCSIRequest *req;
- req = scsi_req_alloc(sizeof(SCSIGenericReq), d, tag, lun);
+ req = scsi_req_alloc(sizeof(SCSIGenericReq), d, tag, lun, hba_private);
return req;
}
@@ -405,7 +406,7 @@ static int get_blocksize(BlockDriverState *bdrv)
memset(cmd, 0, sizeof(cmd));
memset(buf, 0, sizeof(buf));
- cmd[0] = READ_CAPACITY;
+ cmd[0] = READ_CAPACITY_10;
memset(&io_header, 0, sizeof(io_header));
io_header.interface_id = 'S';
diff --git a/hw/scsi.h b/hw/scsi.h
index c1dca35..6b15bbc 100644
--- a/hw/scsi.h
+++ b/hw/scsi.h
@@ -43,6 +43,7 @@ struct SCSIRequest {
} cmd;
BlockDriverAIOCB *aiocb;
bool enqueued;
+ void *hba_private;
QTAILQ_ENTRY(SCSIRequest) next;
};
@@ -67,7 +68,8 @@ struct SCSIDeviceInfo {
DeviceInfo qdev;
scsi_qdev_initfn init;
void (*destroy)(SCSIDevice *s);
- SCSIRequest *(*alloc_req)(SCSIDevice *s, uint32_t tag, uint32_t lun);
+ SCSIRequest *(*alloc_req)(SCSIDevice *s, uint32_t tag, uint32_t lun,
+ void *hba_private);
void (*free_req)(SCSIRequest *req);
int32_t (*send_command)(SCSIRequest *req, uint8_t *buf);
void (*read_data)(SCSIRequest *req);
@@ -138,8 +140,10 @@ extern const struct SCSISense sense_code_LUN_FAILURE;
int scsi_build_sense(SCSISense sense, uint8_t *buf, int len, int fixed);
int scsi_sense_valid(SCSISense sense);
-SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, uint32_t tag, uint32_t lun);
-SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag, uint32_t lun);
+SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, uint32_t tag,
+ uint32_t lun, void *hba_private);
+SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag, uint32_t lun,
+ void *hba_private);
int32_t scsi_req_enqueue(SCSIRequest *req, uint8_t *buf);
void scsi_req_free(SCSIRequest *req);
SCSIRequest *scsi_req_ref(SCSIRequest *req);
diff --git a/hw/sd.c b/hw/sd.c
index cedfb20..c2c80ab 100644
--- a/hw/sd.c
+++ b/hw/sd.c
@@ -393,9 +393,7 @@ static void sd_reset(SDState *sd, BlockDriverState *bdrv)
} else {
sect = 0;
}
- sect <<= 9;
-
- size = sect + 1;
+ size = sect << 9;
sect = (size >> (HWBLOCK_SHIFT + SECTOR_SHIFT + WPGROUP_SHIFT)) + 1;
@@ -1450,14 +1448,8 @@ void sd_write_data(SDState *sd, uint8_t value)
break;
case 25: /* CMD25: WRITE_MULTIPLE_BLOCK */
- sd->data[sd->data_offset ++] = value;
- if (sd->data_offset >= sd->blk_len) {
- /* TODO: Check CRC before committing */
- sd->state = sd_programming_state;
- BLK_WRITE_BLOCK(sd->data_start, sd->data_offset);
- sd->blk_written ++;
- sd->data_start += sd->blk_len;
- sd->data_offset = 0;
+ if (sd->data_offset == 0) {
+ /* Start of the block - lets check the address is valid */
if (sd->data_start + sd->blk_len > sd->size) {
sd->card_status |= ADDRESS_ERROR;
break;
@@ -1466,6 +1458,15 @@ void sd_write_data(SDState *sd, uint8_t value)
sd->card_status |= WP_VIOLATION;
break;
}
+ }
+ sd->data[sd->data_offset++] = value;
+ if (sd->data_offset >= sd->blk_len) {
+ /* TODO: Check CRC before committing */
+ sd->state = sd_programming_state;
+ BLK_WRITE_BLOCK(sd->data_start, sd->data_offset);
+ sd->blk_written++;
+ sd->data_start += sd->blk_len;
+ sd->data_offset = 0;
sd->csd[14] |= 0x40;
/* Bzzzzzzztt .... Operation complete. */
diff --git a/hw/sh_intc.c b/hw/sh_intc.c
index 0734da9..c43b99f 100644
--- a/hw/sh_intc.c
+++ b/hw/sh_intc.c
@@ -5,7 +5,7 @@
* Based on sh_timer.c and arm_timer.c by Paul Brook
* Copyright (c) 2005-2006 CodeSourcery.
*
- * This code is licenced under the GPL.
+ * This code is licensed under the GPL.
*/
#include "sh_intc.h"
diff --git a/hw/sh_pci.c b/hw/sh_pci.c
index a076cf2..0ef93a0 100644
--- a/hw/sh_pci.c
+++ b/hw/sh_pci.c
@@ -26,6 +26,7 @@
#include "pci.h"
#include "pci_host.h"
#include "bswap.h"
+#include "exec-memory.h"
typedef struct SHPCIState {
SysBusDevice busdev;
@@ -127,7 +128,8 @@ static int sh_pci_init_device(SysBusDevice *dev)
}
s->bus = pci_register_bus(&s->busdev.qdev, "pci",
sh_pci_set_irq, sh_pci_map_irq,
- s->irq, PCI_DEVFN(0, 0), 4);
+ s->irq, get_system_memory(),
+ PCI_DEVFN(0, 0), 4);
s->memconfig = cpu_register_io_memory(sh_pci_reg.r, sh_pci_reg.w,
s, DEVICE_NATIVE_ENDIAN);
sysbus_init_mmio_cb(dev, 0x224, sh_pci_map);
diff --git a/hw/sh_timer.c b/hw/sh_timer.c
index 5eec6b7..5df7fb6 100644
--- a/hw/sh_timer.c
+++ b/hw/sh_timer.c
@@ -5,7 +5,7 @@
* Based on arm_timer.c by Paul Brook
* Copyright (c) 2005-2006 CodeSourcery.
*
- * This code is licenced under the GPL.
+ * This code is licensed under the GPL.
*/
#include "hw.h"
diff --git a/hw/smbus.c b/hw/smbus.c
index e464539..ff027c8 100644
--- a/hw/smbus.c
+++ b/hw/smbus.c
@@ -4,7 +4,7 @@
* Copyright (c) 2007 CodeSourcery.
* Written by Paul Brook
*
- * This code is licenced under the LGPL.
+ * This code is licensed under the LGPL.
*/
/* TODO: Implement PEC. */
diff --git a/hw/smc91c111.c b/hw/smc91c111.c
index 701baaf..3a8a85c 100644
--- a/hw/smc91c111.c
+++ b/hw/smc91c111.c
@@ -4,7 +4,7 @@
* Copyright (c) 2005 CodeSourcery, LLC.
* Written by Paul Brook
*
- * This code is licenced under the GPL
+ * This code is licensed under the GPL
*/
#include "sysbus.h"
diff --git a/hw/spapr_hcall.c b/hw/spapr_hcall.c
index 5cd8d8f..f7ead04 100644
--- a/hw/spapr_hcall.c
+++ b/hw/spapr_hcall.c
@@ -1,9 +1,9 @@
#include "sysemu.h"
#include "cpu.h"
+#include "dyngen-exec.h"
#include "qemu-char.h"
#include "sysemu.h"
#include "qemu-char.h"
-#include "exec.h"
#include "helper_regs.h"
#include "hw/spapr.h"
diff --git a/hw/spapr_vscsi.c b/hw/spapr_vscsi.c
index 1c901ef..646b1e3 100644
--- a/hw/spapr_vscsi.c
+++ b/hw/spapr_vscsi.c
@@ -121,7 +121,7 @@ static struct vscsi_req *vscsi_get_req(VSCSIState *s)
return NULL;
}
-static void vscsi_put_req(VSCSIState *s, vscsi_req *req)
+static void vscsi_put_req(vscsi_req *req)
{
if (req->sreq != NULL) {
scsi_req_unref(req->sreq);
@@ -130,15 +130,6 @@ static void vscsi_put_req(VSCSIState *s, vscsi_req *req)
req->active = 0;
}
-static vscsi_req *vscsi_find_req(VSCSIState *s, SCSIRequest *req)
-{
- uint32_t tag = req->tag;
- if (tag >= VSCSI_REQ_LIMIT || !s->reqs[tag].active) {
- return NULL;
- }
- return &s->reqs[tag];
-}
-
static void vscsi_decode_id_lun(uint64_t srp_lun, int *id, int *lun)
{
/* XXX Figure that one out properly ! This is crackpot */
@@ -454,7 +445,7 @@ static void vscsi_send_request_sense(VSCSIState *s, vscsi_req *req)
if (n) {
req->senselen = n;
vscsi_send_rsp(s, req, CHECK_CONDITION, 0, 0);
- vscsi_put_req(s, req);
+ vscsi_put_req(req);
return;
}
@@ -483,7 +474,7 @@ static void vscsi_send_request_sense(VSCSIState *s, vscsi_req *req)
static void vscsi_transfer_data(SCSIRequest *sreq, uint32_t len)
{
VSCSIState *s = DO_UPCAST(VSCSIState, vdev.qdev, sreq->bus->qbus.parent);
- vscsi_req *req = vscsi_find_req(s, sreq);
+ vscsi_req *req = sreq->hba_private;
uint8_t *buf;
int rc = 0;
@@ -531,7 +522,7 @@ static void vscsi_transfer_data(SCSIRequest *sreq, uint32_t len)
static void vscsi_command_complete(SCSIRequest *sreq, uint32_t status)
{
VSCSIState *s = DO_UPCAST(VSCSIState, vdev.qdev, sreq->bus->qbus.parent);
- vscsi_req *req = vscsi_find_req(s, sreq);
+ vscsi_req *req = sreq->hba_private;
int32_t res_in = 0, res_out = 0;
dprintf("VSCSI: SCSI cmd complete, r=0x%x tag=0x%x status=0x%x, req=%p\n",
@@ -563,15 +554,14 @@ static void vscsi_command_complete(SCSIRequest *sreq, uint32_t status)
}
}
vscsi_send_rsp(s, req, 0, res_in, res_out);
- vscsi_put_req(s, req);
+ vscsi_put_req(req);
}
static void vscsi_request_cancelled(SCSIRequest *sreq)
{
- VSCSIState *s = DO_UPCAST(VSCSIState, vdev.qdev, sreq->bus->qbus.parent);
- vscsi_req *req = vscsi_find_req(s, sreq);
+ vscsi_req *req = sreq->hba_private;
- vscsi_put_req(s, req);
+ vscsi_put_req(req);
}
static void vscsi_process_login(VSCSIState *s, vscsi_req *req)
@@ -659,7 +649,7 @@ static int vscsi_queue_cmd(VSCSIState *s, vscsi_req *req)
}
req->lun = lun;
- req->sreq = scsi_req_new(sdev, req->qtag, lun);
+ req->sreq = scsi_req_new(sdev, req->qtag, lun, req);
n = scsi_req_enqueue(req->sreq, srp->cmd.cdb);
dprintf("VSCSI: Queued command tag 0x%x CMD 0x%x ID %d LUN %d ret: %d\n",
@@ -858,7 +848,7 @@ static void vscsi_got_payload(VSCSIState *s, vscsi_crq *crq)
}
if (done) {
- vscsi_put_req(s, req);
+ vscsi_put_req(req);
}
}
diff --git a/hw/spitz.c b/hw/spitz.c
index 006f7a9..c05b5f7 100644
--- a/hw/spitz.c
+++ b/hw/spitz.c
@@ -48,7 +48,7 @@
typedef struct {
SysBusDevice busdev;
- NANDFlashState *nand;
+ DeviceState *nand;
uint8_t ctl;
uint8_t manf_id;
uint8_t chip_id;
@@ -169,11 +169,13 @@ static void sl_flash_register(PXA2xxState *cpu, int size)
static int sl_nand_init(SysBusDevice *dev) {
int iomemtype;
SLNANDState *s;
+ DriveInfo *nand;
s = FROM_SYSBUS(SLNANDState, dev);
s->ctl = 0;
- s->nand = nand_init(s->manf_id, s->chip_id);
+ nand = drive_get(IF_MTD, 0, 0);
+ s->nand = nand_init(nand ? nand->bdrv : NULL, s->manf_id, s->chip_id);
iomemtype = cpu_register_io_memory(sl_readfn,
sl_writefn, s, DEVICE_NATIVE_ENDIAN);
diff --git a/hw/ssd0303.c b/hw/ssd0303.c
index b39e259..401fdf5 100644
--- a/hw/ssd0303.c
+++ b/hw/ssd0303.c
@@ -4,7 +4,7 @@
* Copyright (c) 2006-2007 CodeSourcery.
* Written by Paul Brook
*
- * This code is licenced under the GPL.
+ * This code is licensed under the GPL.
*/
/* The controller can support a variety of different displays, but we only
diff --git a/hw/ssd0323.c b/hw/ssd0323.c
index 8643961..1eb3823 100644
--- a/hw/ssd0323.c
+++ b/hw/ssd0323.c
@@ -4,7 +4,7 @@
* Copyright (c) 2006-2007 CodeSourcery.
* Written by Paul Brook
*
- * This code is licenced under the GPL.
+ * This code is licensed under the GPL.
*/
/* The controller can support a variety of different displays, but we only
diff --git a/hw/ssi-sd.c b/hw/ssi-sd.c
index fb4b649..18dabd6 100644
--- a/hw/ssi-sd.c
+++ b/hw/ssi-sd.c
@@ -4,7 +4,7 @@
* Copyright (c) 2007-2009 CodeSourcery.
* Written by Paul Brook
*
- * This code is licenced under the GNU GPL v2.
+ * This code is licensed under the GNU GPL v2.
*/
#include "blockdev.h"
diff --git a/hw/ssi.c b/hw/ssi.c
index cfe7c07..3f4c5f9 100644
--- a/hw/ssi.c
+++ b/hw/ssi.c
@@ -4,7 +4,7 @@
* Copyright (c) 2009 CodeSourcery.
* Written by Paul Brook
*
- * This code is licenced under the GNU GPL v2.
+ * This code is licensed under the GNU GPL v2.
*/
#include "ssi.h"
diff --git a/hw/stellaris.c b/hw/stellaris.c
index ac9fcc1..a280930 100644
--- a/hw/stellaris.c
+++ b/hw/stellaris.c
@@ -4,7 +4,7 @@
* Copyright (c) 2006 CodeSourcery.
* Written by Paul Brook
*
- * This code is licenced under the GPL.
+ * This code is licensed under the GPL.
*/
#include "sysbus.h"
@@ -1230,7 +1230,7 @@ static void stellaris_init(const char *kernel_filename, const char *cpu_model,
}
}
- stellaris_sys_init(0x400fe000, pic[28], board, nd_table[0].macaddr);
+ stellaris_sys_init(0x400fe000, pic[28], board, nd_table[0].macaddr.a);
for (i = 0; i < 7; i++) {
if (board->dc4 & (1 << i)) {
diff --git a/hw/stellaris_enet.c b/hw/stellaris_enet.c
index 6a0583a..1291931 100644
--- a/hw/stellaris_enet.c
+++ b/hw/stellaris_enet.c
@@ -4,7 +4,7 @@
* Copyright (c) 2007 CodeSourcery.
* Written by Paul Brook
*
- * This code is licenced under the GPL.
+ * This code is licensed under the GPL.
*/
#include "sysbus.h"
#include "net.h"
diff --git a/hw/stellaris_input.c b/hw/stellaris_input.c
index 06c5f9d..95604ec 100644
--- a/hw/stellaris_input.c
+++ b/hw/stellaris_input.c
@@ -4,7 +4,7 @@
* Copyright (c) 2007 CodeSourcery.
* Written by Paul Brook
*
- * This code is licenced under the GPL.
+ * This code is licensed under the GPL.
*/
#include "hw.h"
#include "devices.h"
diff --git a/hw/sysbus.c b/hw/sysbus.c
index 2e22be7..ea442ac 100644
--- a/hw/sysbus.c
+++ b/hw/sysbus.c
@@ -19,6 +19,7 @@
#include "sysbus.h"
#include "monitor.h"
+#include "exec-memory.h"
static void sysbus_dev_print(Monitor *mon, DeviceState *dev, int indent);
static char *sysbus_get_fw_dev_path(DeviceState *dev);
@@ -49,11 +50,20 @@ void sysbus_mmio_map(SysBusDevice *dev, int n, target_phys_addr_t addr)
}
if (dev->mmio[n].addr != (target_phys_addr_t)-1) {
/* Unregister previous mapping. */
- cpu_register_physical_memory(dev->mmio[n].addr, dev->mmio[n].size,
- IO_MEM_UNASSIGNED);
+ if (dev->mmio[n].memory) {
+ memory_region_del_subregion(get_system_memory(),
+ dev->mmio[n].memory);
+ } else {
+ cpu_register_physical_memory(dev->mmio[n].addr, dev->mmio[n].size,
+ IO_MEM_UNASSIGNED);
+ }
}
dev->mmio[n].addr = addr;
- if (dev->mmio[n].cb) {
+ if (dev->mmio[n].memory) {
+ memory_region_add_subregion(get_system_memory(),
+ addr,
+ dev->mmio[n].memory);
+ } else if (dev->mmio[n].cb) {
dev->mmio[n].cb(dev, addr);
} else {
cpu_register_physical_memory(addr, dev->mmio[n].size,
@@ -107,6 +117,17 @@ void sysbus_init_mmio_cb(SysBusDevice *dev, target_phys_addr_t size,
dev->mmio[n].cb = cb;
}
+void sysbus_init_mmio_region(SysBusDevice *dev, MemoryRegion *memory)
+{
+ int n;
+
+ assert(dev->num_mmio < QDEV_MAX_MMIO);
+ n = dev->num_mmio++;
+ dev->mmio[n].addr = -1;
+ dev->mmio[n].size = memory_region_size(memory);
+ dev->mmio[n].memory = memory;
+}
+
void sysbus_init_ioports(SysBusDevice *dev, pio_addr_t ioport, pio_addr_t size)
{
pio_addr_t i;
diff --git a/hw/sysbus.h b/hw/sysbus.h
index 4e8cb16..5f62e2d 100644
--- a/hw/sysbus.h
+++ b/hw/sysbus.h
@@ -4,6 +4,7 @@
/* Devices attached directly to the main system bus. */
#include "qdev.h"
+#include "memory.h"
#define QDEV_MAX_MMIO 32
#define QDEV_MAX_PIO 32
@@ -23,6 +24,7 @@ struct SysBusDevice {
target_phys_addr_t size;
mmio_mapfunc cb;
ram_addr_t iofunc;
+ MemoryRegion *memory;
} mmio[QDEV_MAX_MMIO];
int num_pio;
pio_addr_t pio[QDEV_MAX_PIO];
@@ -46,6 +48,7 @@ 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_mmio_region(SysBusDevice *dev, MemoryRegion *memory);
void sysbus_init_irq(SysBusDevice *dev, qemu_irq *p);
void sysbus_pass_irq(SysBusDevice *dev, SysBusDevice *target);
void sysbus_init_ioports(SysBusDevice *dev, pio_addr_t ioport, pio_addr_t size);
diff --git a/hw/tc6393xb.c b/hw/tc6393xb.c
index ed49e94..a1c48bf 100644
--- a/hw/tc6393xb.c
+++ b/hw/tc6393xb.c
@@ -12,6 +12,7 @@
#include "flash.h"
#include "console.h"
#include "pixel_ops.h"
+#include "blockdev.h"
#define IRQ_TC6393_NAND 0
#define IRQ_TC6393_MMC 1
@@ -117,7 +118,7 @@ struct TC6393xbState {
} nand;
int nand_enable;
uint32_t nand_phys;
- NANDFlashState *flash;
+ DeviceState *flash;
ECCState ecc;
DisplayState *ds;
@@ -566,6 +567,7 @@ TC6393xbState *tc6393xb_init(uint32_t base, qemu_irq irq)
{
int iomemtype;
TC6393xbState *s;
+ DriveInfo *nand;
CPUReadMemoryFunc * const tc6393xb_readfn[] = {
tc6393xb_readb,
tc6393xb_readw,
@@ -586,7 +588,8 @@ TC6393xbState *tc6393xb_init(uint32_t base, qemu_irq irq)
s->sub_irqs = qemu_allocate_irqs(tc6393xb_sub_irq, s, TC6393XB_NR_IRQS);
- s->flash = nand_init(NAND_MFR_TOSHIBA, 0x76);
+ nand = drive_get(IF_MTD, 0, 0);
+ s->flash = nand_init(nand ? nand->bdrv : NULL, NAND_MFR_TOSHIBA, 0x76);
iomemtype = cpu_register_io_memory(tc6393xb_readfn,
tc6393xb_writefn, s, DEVICE_NATIVE_ENDIAN);
diff --git a/hw/unin_pci.c b/hw/unin_pci.c
index d364daa..b499523 100644
--- a/hw/unin_pci.c
+++ b/hw/unin_pci.c
@@ -201,7 +201,7 @@ static int pci_unin_internal_init_device(SysBusDevice *dev)
return 0;
}
-PCIBus *pci_pmac_init(qemu_irq *pic)
+PCIBus *pci_pmac_init(qemu_irq *pic, MemoryRegion *address_space)
{
DeviceState *dev;
SysBusDevice *s;
@@ -215,7 +215,8 @@ PCIBus *pci_pmac_init(qemu_irq *pic)
d = FROM_SYSBUS(UNINState, s);
d->host_state.bus = pci_register_bus(&d->busdev.qdev, "pci",
pci_unin_set_irq, pci_unin_map_irq,
- pic, PCI_DEVFN(11, 0), 4);
+ pic, address_space,
+ PCI_DEVFN(11, 0), 4);
#if 0
pci_create_simple(d->host_state.bus, PCI_DEVFN(11, 0), "uni-north");
@@ -252,7 +253,7 @@ PCIBus *pci_pmac_init(qemu_irq *pic)
return d->host_state.bus;
}
-PCIBus *pci_pmac_u3_init(qemu_irq *pic)
+PCIBus *pci_pmac_u3_init(qemu_irq *pic, MemoryRegion *address_space)
{
DeviceState *dev;
SysBusDevice *s;
@@ -267,7 +268,8 @@ PCIBus *pci_pmac_u3_init(qemu_irq *pic)
d->host_state.bus = pci_register_bus(&d->busdev.qdev, "pci",
pci_unin_set_irq, pci_unin_map_irq,
- pic, PCI_DEVFN(11, 0), 4);
+ pic, address_space,
+ PCI_DEVFN(11, 0), 4);
sysbus_mmio_map(s, 0, 0xf0800000);
sysbus_mmio_map(s, 1, 0xf0c00000);
diff --git a/hw/usb-bt.c b/hw/usb-bt.c
index baae487..529fa33 100644
--- a/hw/usb-bt.c
+++ b/hw/usb-bt.c
@@ -99,13 +99,13 @@ static const USBDescIface desc_iface_bluetooth[] = {
.eps = (USBDescEndpoint[]) {
{
.bEndpointAddress = USB_DIR_OUT | USB_SCO_EP,
- .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .bmAttributes = USB_ENDPOINT_XFER_ISOC,
.wMaxPacketSize = 0,
.bInterval = 0x01,
},
{
.bEndpointAddress = USB_DIR_IN | USB_SCO_EP,
- .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .bmAttributes = USB_ENDPOINT_XFER_ISOC,
.wMaxPacketSize = 0,
.bInterval = 0x01,
},
@@ -120,13 +120,13 @@ static const USBDescIface desc_iface_bluetooth[] = {
.eps = (USBDescEndpoint[]) {
{
.bEndpointAddress = USB_DIR_OUT | USB_SCO_EP,
- .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .bmAttributes = USB_ENDPOINT_XFER_ISOC,
.wMaxPacketSize = 0x09,
.bInterval = 0x01,
},
{
.bEndpointAddress = USB_DIR_IN | USB_SCO_EP,
- .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .bmAttributes = USB_ENDPOINT_XFER_ISOC,
.wMaxPacketSize = 0x09,
.bInterval = 0x01,
},
@@ -141,13 +141,13 @@ static const USBDescIface desc_iface_bluetooth[] = {
.eps = (USBDescEndpoint[]) {
{
.bEndpointAddress = USB_DIR_OUT | USB_SCO_EP,
- .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .bmAttributes = USB_ENDPOINT_XFER_ISOC,
.wMaxPacketSize = 0x11,
.bInterval = 0x01,
},
{
.bEndpointAddress = USB_DIR_IN | USB_SCO_EP,
- .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .bmAttributes = USB_ENDPOINT_XFER_ISOC,
.wMaxPacketSize = 0x11,
.bInterval = 0x01,
},
@@ -162,13 +162,13 @@ static const USBDescIface desc_iface_bluetooth[] = {
.eps = (USBDescEndpoint[]) {
{
.bEndpointAddress = USB_DIR_OUT | USB_SCO_EP,
- .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .bmAttributes = USB_ENDPOINT_XFER_ISOC,
.wMaxPacketSize = 0x19,
.bInterval = 0x01,
},
{
.bEndpointAddress = USB_DIR_IN | USB_SCO_EP,
- .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .bmAttributes = USB_ENDPOINT_XFER_ISOC,
.wMaxPacketSize = 0x19,
.bInterval = 0x01,
},
@@ -183,13 +183,13 @@ static const USBDescIface desc_iface_bluetooth[] = {
.eps = (USBDescEndpoint[]) {
{
.bEndpointAddress = USB_DIR_OUT | USB_SCO_EP,
- .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .bmAttributes = USB_ENDPOINT_XFER_ISOC,
.wMaxPacketSize = 0x21,
.bInterval = 0x01,
},
{
.bEndpointAddress = USB_DIR_IN | USB_SCO_EP,
- .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .bmAttributes = USB_ENDPOINT_XFER_ISOC,
.wMaxPacketSize = 0x21,
.bInterval = 0x01,
},
@@ -204,13 +204,13 @@ static const USBDescIface desc_iface_bluetooth[] = {
.eps = (USBDescEndpoint[]) {
{
.bEndpointAddress = USB_DIR_OUT | USB_SCO_EP,
- .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .bmAttributes = USB_ENDPOINT_XFER_ISOC,
.wMaxPacketSize = 0x31,
.bInterval = 0x01,
},
{
.bEndpointAddress = USB_DIR_IN | USB_SCO_EP,
- .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .bmAttributes = USB_ENDPOINT_XFER_ISOC,
.wMaxPacketSize = 0x31,
.bInterval = 0x01,
},
@@ -294,9 +294,9 @@ static inline int usb_bt_fifo_dequeue(struct usb_hci_in_fifo_s *fifo,
if (likely(!fifo->len))
return USB_RET_STALL;
- len = MIN(p->len, fifo->fifo[fifo->start].len);
- memcpy(p->data, fifo->fifo[fifo->start].data, len);
- if (len == p->len) {
+ len = MIN(p->iov.size, fifo->fifo[fifo->start].len);
+ usb_packet_copy(p, fifo->fifo[fifo->start].data, len);
+ if (len == p->iov.size) {
fifo->fifo[fifo->start].len -= len;
fifo->fifo[fifo->start].data += len;
} else {
@@ -319,20 +319,13 @@ static inline void usb_bt_fifo_out_enqueue(struct USBBtState *s,
struct usb_hci_out_fifo_s *fifo,
void (*send)(struct HCIInfo *, const uint8_t *, int),
int (*complete)(const uint8_t *, int),
- const uint8_t *data, int len)
+ USBPacket *p)
{
- if (fifo->len) {
- memcpy(fifo->data + fifo->len, data, len);
- fifo->len += len;
- if (complete(fifo->data, fifo->len)) {
- send(s->hci, fifo->data, fifo->len);
- fifo->len = 0;
- }
- } else if (complete(data, len))
- send(s->hci, data, len);
- else {
- memcpy(fifo->data, data, len);
- fifo->len = len;
+ usb_packet_copy(p, fifo->data + fifo->len, p->iov.size);
+ fifo->len += p->iov.size;
+ if (complete(fifo->data, fifo->len)) {
+ send(s->hci, fifo->data, fifo->len);
+ fifo->len = 0;
}
/* TODO: do we need to loop? */
@@ -432,7 +425,7 @@ static int usb_bt_handle_control(USBDevice *dev, USBPacket *p,
case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_DEVICE) << 8):
if (s->config)
usb_bt_fifo_out_enqueue(s, &s->outcmd, s->hci->cmd_send,
- usb_bt_hci_cmd_complete, data, length);
+ usb_bt_hci_cmd_complete, p);
break;
default:
fail:
@@ -474,12 +467,12 @@ static int usb_bt_handle_data(USBDevice *dev, USBPacket *p)
switch (p->devep & 0xf) {
case USB_ACL_EP:
usb_bt_fifo_out_enqueue(s, &s->outacl, s->hci->acl_send,
- usb_bt_hci_acl_complete, p->data, p->len);
+ usb_bt_hci_acl_complete, p);
break;
case USB_SCO_EP:
usb_bt_fifo_out_enqueue(s, &s->outsco, s->hci->sco_send,
- usb_bt_hci_sco_complete, p->data, p->len);
+ usb_bt_hci_sco_complete, p);
break;
default:
@@ -548,10 +541,16 @@ USBDevice *usb_bt_init(HCIInfo *hci)
return dev;
}
+static const VMStateDescription vmstate_usb_bt = {
+ .name = "usb-bt",
+ .unmigratable = 1,
+};
+
static struct USBDeviceInfo bt_info = {
.product_desc = "QEMU BT dongle",
.qdev.name = "usb-bt-dongle",
.qdev.size = sizeof(struct USBBtState),
+ .qdev.vmsd = &vmstate_usb_bt,
.usb_desc = &desc_bluetooth,
.init = usb_bt_initfn,
.handle_packet = usb_generic_handle_packet,
diff --git a/hw/usb-bus.c b/hw/usb-bus.c
index 2abce12..f1dd55e 100644
--- a/hw/usb-bus.c
+++ b/hw/usb-bus.c
@@ -82,12 +82,10 @@ static int usb_qdev_init(DeviceState *qdev, DeviceInfo *base)
static int usb_qdev_exit(DeviceState *qdev)
{
USBDevice *dev = DO_UPCAST(USBDevice, qdev, qdev);
- USBBus *bus = usb_bus_from_device(dev);
if (dev->attached) {
usb_device_detach(dev);
}
- bus->ops->device_destroy(bus, dev);
if (dev->info->handle_destroy) {
dev->info->handle_destroy(dev);
}
@@ -140,19 +138,55 @@ USBDevice *usb_create_simple(USBBus *bus, const char *name)
return dev;
}
-void usb_register_port(USBBus *bus, USBPort *port, void *opaque, int index,
- USBPortOps *ops, int speedmask)
+static void usb_fill_port(USBPort *port, void *opaque, int index,
+ USBPortOps *ops, int speedmask)
{
port->opaque = opaque;
port->index = index;
- port->opaque = opaque;
- port->index = index;
port->ops = ops;
port->speedmask = speedmask;
+ usb_port_location(port, NULL, index + 1);
+}
+
+void usb_register_port(USBBus *bus, USBPort *port, void *opaque, int index,
+ USBPortOps *ops, int speedmask)
+{
+ usb_fill_port(port, opaque, index, ops, speedmask);
QTAILQ_INSERT_TAIL(&bus->free, port, next);
bus->nfree++;
}
+int usb_register_companion(const char *masterbus, USBPort *ports[],
+ uint32_t portcount, uint32_t firstport,
+ void *opaque, USBPortOps *ops, int speedmask)
+{
+ USBBus *bus;
+ int i;
+
+ QTAILQ_FOREACH(bus, &busses, next) {
+ if (strcmp(bus->qbus.name, masterbus) == 0) {
+ break;
+ }
+ }
+
+ if (!bus || !bus->ops->register_companion) {
+ qerror_report(QERR_INVALID_PARAMETER_VALUE, "masterbus",
+ "an USB masterbus");
+ if (bus) {
+ error_printf_unless_qmp(
+ "USB bus '%s' does not allow companion controllers\n",
+ masterbus);
+ }
+ return -1;
+ }
+
+ for (i = 0; i < portcount; i++) {
+ usb_fill_port(ports[i], opaque, i, ops, speedmask);
+ }
+
+ return bus->ops->register_companion(bus, ports, portcount, firstport);
+}
+
void usb_port_location(USBPort *downstream, USBPort *upstream, int portnr)
{
if (upstream) {
diff --git a/hw/usb-ccid.c b/hw/usb-ccid.c
index d392299..66aeb21 100644
--- a/hw/usb-ccid.c
+++ b/hw/usb-ccid.c
@@ -934,16 +934,16 @@ static int ccid_handle_bulk_out(USBCCIDState *s, USBPacket *p)
{
CCID_Header *ccid_header;
- if (p->len + s->bulk_out_pos > BULK_OUT_DATA_SIZE) {
+ if (p->iov.size + s->bulk_out_pos > BULK_OUT_DATA_SIZE) {
return USB_RET_STALL;
}
ccid_header = (CCID_Header *)s->bulk_out_data;
- memcpy(s->bulk_out_data + s->bulk_out_pos, p->data, p->len);
- s->bulk_out_pos += p->len;
- if (p->len == CCID_MAX_PACKET_SIZE) {
+ usb_packet_copy(p, s->bulk_out_data + s->bulk_out_pos, p->iov.size);
+ s->bulk_out_pos += p->iov.size;
+ if (p->iov.size == CCID_MAX_PACKET_SIZE) {
DPRINTF(s, D_VERBOSE,
- "usb-ccid: bulk_in: expecting more packets (%d/%d)\n",
- p->len, ccid_header->dwLength);
+ "usb-ccid: bulk_in: expecting more packets (%zd/%d)\n",
+ p->iov.size, ccid_header->dwLength);
return 0;
}
if (s->bulk_out_pos < 10) {
@@ -1006,15 +1006,17 @@ static int ccid_handle_bulk_out(USBCCIDState *s, USBPacket *p)
return 0;
}
-static int ccid_bulk_in_copy_to_guest(USBCCIDState *s, uint8_t *data, int len)
+static int ccid_bulk_in_copy_to_guest(USBCCIDState *s, USBPacket *p)
{
int ret = 0;
- assert(len > 0);
+ assert(p->iov.size > 0);
ccid_bulk_in_get(s);
if (s->current_bulk_in != NULL) {
- ret = MIN(s->current_bulk_in->len - s->current_bulk_in->pos, len);
- memcpy(data, s->current_bulk_in->data + s->current_bulk_in->pos, ret);
+ ret = MIN(s->current_bulk_in->len - s->current_bulk_in->pos,
+ p->iov.size);
+ usb_packet_copy(p, s->current_bulk_in->data +
+ s->current_bulk_in->pos, ret);
s->current_bulk_in->pos += ret;
if (s->current_bulk_in->pos == s->current_bulk_in->len) {
ccid_bulk_in_release(s);
@@ -1025,11 +1027,13 @@ static int ccid_bulk_in_copy_to_guest(USBCCIDState *s, uint8_t *data, int len)
}
if (ret > 0) {
DPRINTF(s, D_MORE_INFO,
- "%s: %d/%d req/act to guest (BULK_IN)\n", __func__, len, ret);
+ "%s: %zd/%d req/act to guest (BULK_IN)\n",
+ __func__, p->iov.size, ret);
}
- if (ret != USB_RET_NAK && ret < len) {
+ if (ret != USB_RET_NAK && ret < p->iov.size) {
DPRINTF(s, 1,
- "%s: returning short (EREMOTEIO) %d < %d\n", __func__, ret, len);
+ "%s: returning short (EREMOTEIO) %d < %zd\n",
+ __func__, ret, p->iov.size);
}
return ret;
}
@@ -1038,8 +1042,7 @@ static int ccid_handle_data(USBDevice *dev, USBPacket *p)
{
USBCCIDState *s = DO_UPCAST(USBCCIDState, dev, dev);
int ret = 0;
- uint8_t *data = p->data;
- int len = p->len;
+ uint8_t buf[2];
switch (p->pid) {
case USB_TOKEN_OUT:
@@ -1049,24 +1052,25 @@ static int ccid_handle_data(USBDevice *dev, USBPacket *p)
case USB_TOKEN_IN:
switch (p->devep & 0xf) {
case CCID_BULK_IN_EP:
- if (!len) {
+ if (!p->iov.size) {
ret = USB_RET_NAK;
} else {
- ret = ccid_bulk_in_copy_to_guest(s, data, len);
+ ret = ccid_bulk_in_copy_to_guest(s, p);
}
break;
case CCID_INT_IN_EP:
if (s->notify_slot_change) {
/* page 56, RDR_to_PC_NotifySlotChange */
- data[0] = CCID_MESSAGE_TYPE_RDR_to_PC_NotifySlotChange;
- data[1] = s->bmSlotICCState;
+ buf[0] = CCID_MESSAGE_TYPE_RDR_to_PC_NotifySlotChange;
+ buf[1] = s->bmSlotICCState;
+ usb_packet_copy(p, buf, 2);
ret = 2;
s->notify_slot_change = false;
s->bmSlotICCState &= ~SLOT_0_CHANGED_MASK;
DPRINTF(s, D_INFO,
"handle_data: int_in: notify_slot_change %X, "
- "requested len %d\n",
- s->bmSlotICCState, len);
+ "requested len %zd\n",
+ s->bmSlotICCState, p->iov.size);
}
break;
default:
@@ -1104,20 +1108,9 @@ static Answer *ccid_peek_next_answer(USBCCIDState *s)
: &s->pending_answers[s->pending_answers_start % PENDING_ANSWERS_NUM];
}
-static void ccid_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent)
-{
- CCIDCardState *card = DO_UPCAST(CCIDCardState, qdev, qdev);
- CCIDCardInfo *info = DO_UPCAST(CCIDCardInfo, qdev, qdev->info);
-
- if (info->print) {
- info->print(mon, card, indent);
- }
-}
-
static struct BusInfo ccid_bus_info = {
.name = "ccid-bus",
.size = sizeof(CCIDBus),
- .print_dev = ccid_bus_dev_print,
.props = (Property[]) {
DEFINE_PROP_UINT32("slot", struct CCIDCardState, slot, 0),
DEFINE_PROP_END_OF_LIST(),
diff --git a/hw/usb-ehci.c b/hw/usb-ehci.c
index 91fb7de..2b43895 100644
--- a/hw/usb-ehci.c
+++ b/hw/usb-ehci.c
@@ -20,9 +20,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/>.
- *
- * TODO:
- * o Downstream port handoff
*/
#include "hw.h"
@@ -31,6 +28,7 @@
#include "pci.h"
#include "monitor.h"
#include "trace.h"
+#include "dma.h"
#define EHCI_DEBUG 0
@@ -103,10 +101,10 @@
#define PORTSC_BEGIN PORTSC
#define PORTSC_END (PORTSC + 4 * NB_PORTS)
/*
- * Bits that are reserverd or are read-only are masked out of values
+ * Bits that are reserved or are read-only are masked out of values
* written to us by software
*/
-#define PORTSC_RO_MASK 0x007021c5
+#define PORTSC_RO_MASK 0x007001c0
#define PORTSC_RWC_MASK 0x0000002a
#define PORTSC_WKOC_E (1 << 22) // Wake on Over Current Enable
#define PORTSC_WKDS_E (1 << 21) // Wake on Disconnect Enable
@@ -133,7 +131,7 @@
#define FRAME_TIMER_NS (1000000000 / FRAME_TIMER_FREQ)
#define NB_MAXINTRATE 8 // Max rate at which controller issues ints
-#define NB_PORTS 4 // Number of downstream ports
+#define NB_PORTS 6 // Number of downstream ports
#define BUFF_SIZE 5*4096 // Max bytes to transfer per transaction
#define MAX_ITERATIONS 20 // Max number of QH before we break the loop
#define MAX_QH 100 // Max allowable queue heads in a chain
@@ -272,6 +270,7 @@ typedef struct EHCIqtd {
uint32_t bufptr[5]; // Standard buffer pointer
#define QTD_BUFPTR_MASK 0xfffff000
+#define QTD_BUFPTR_SH 12
} EHCIqtd;
/* EHCI spec version 1.0 Section 3.6
@@ -360,7 +359,7 @@ struct EHCIQueue {
uint32_t qtdaddr; // address QTD read from
USBPacket packet;
- uint8_t buffer[BUFF_SIZE];
+ QEMUSGList sgl;
int pid;
uint32_t tbytes;
enum async_state async;
@@ -373,7 +372,7 @@ struct EHCIState {
qemu_irq irq;
target_phys_addr_t mem_base;
int mem;
- int num_ports;
+ int companion_count;
/* properties */
uint32_t freq;
@@ -409,6 +408,7 @@ struct EHCIState {
int astate; // Current state in asynchronous schedule
int pstate; // Current state in periodic schedule
USBPort ports[NB_PORTS];
+ USBPort *companion_ports[NB_PORTS];
uint32_t usbsts_pending;
QTAILQ_HEAD(, EHCIQueue) queues;
@@ -416,7 +416,7 @@ struct EHCIState {
uint32_t p_fetch_addr; // which address to look at next
USBPacket ipacket;
- uint8_t ibuffer[BUFF_SIZE];
+ QEMUSGList isgl;
int isoch_pause;
uint64_t last_run_ns;
@@ -731,17 +731,17 @@ static void ehci_attach(USBPort *port)
trace_usb_ehci_port_attach(port->index, port->dev->product_desc);
+ if (*portsc & PORTSC_POWNER) {
+ USBPort *companion = s->companion_ports[port->index];
+ companion->dev = port->dev;
+ companion->ops->attach(companion);
+ return;
+ }
+
*portsc |= PORTSC_CONNECT;
*portsc |= PORTSC_CSC;
- /*
- * If a high speed device is attached then we own this port(indicated
- * by zero in the PORTSC_POWNER bit field) so set the status bit
- * and set an interrupt if enabled.
- */
- if ( !(*portsc & PORTSC_POWNER)) {
- ehci_set_interrupt(s, USBSTS_PCD);
- }
+ ehci_set_interrupt(s, USBSTS_PCD);
}
static void ehci_detach(USBPort *port)
@@ -751,17 +751,88 @@ static void ehci_detach(USBPort *port)
trace_usb_ehci_port_detach(port->index);
- *portsc &= ~PORTSC_CONNECT;
+ if (*portsc & PORTSC_POWNER) {
+ USBPort *companion = s->companion_ports[port->index];
+ companion->ops->detach(companion);
+ companion->dev = NULL;
+ return;
+ }
+
+ ehci_queues_rip_device(s, port->dev);
+
+ *portsc &= ~(PORTSC_CONNECT|PORTSC_PED);
*portsc |= PORTSC_CSC;
- /*
- * If a high speed device is attached then we own this port(indicated
- * by zero in the PORTSC_POWNER bit field) so set the status bit
- * and set an interrupt if enabled.
- */
- if ( !(*portsc & PORTSC_POWNER)) {
- ehci_set_interrupt(s, USBSTS_PCD);
+ ehci_set_interrupt(s, USBSTS_PCD);
+}
+
+static void ehci_child_detach(USBPort *port, USBDevice *child)
+{
+ EHCIState *s = port->opaque;
+ uint32_t portsc = s->portsc[port->index];
+
+ if (portsc & PORTSC_POWNER) {
+ USBPort *companion = s->companion_ports[port->index];
+ companion->ops->child_detach(companion, child);
+ companion->dev = NULL;
+ return;
}
+
+ ehci_queues_rip_device(s, child);
+}
+
+static void ehci_wakeup(USBPort *port)
+{
+ EHCIState *s = port->opaque;
+ uint32_t portsc = s->portsc[port->index];
+
+ if (portsc & PORTSC_POWNER) {
+ USBPort *companion = s->companion_ports[port->index];
+ if (companion->ops->wakeup) {
+ companion->ops->wakeup(companion);
+ }
+ }
+}
+
+static int ehci_register_companion(USBBus *bus, USBPort *ports[],
+ uint32_t portcount, uint32_t firstport)
+{
+ EHCIState *s = container_of(bus, EHCIState, bus);
+ uint32_t i;
+
+ if (firstport + portcount > NB_PORTS) {
+ qerror_report(QERR_INVALID_PARAMETER_VALUE, "firstport",
+ "firstport on masterbus");
+ error_printf_unless_qmp(
+ "firstport value of %u makes companion take ports %u - %u, which "
+ "is outside of the valid range of 0 - %u\n", firstport, firstport,
+ firstport + portcount - 1, NB_PORTS - 1);
+ return -1;
+ }
+
+ for (i = 0; i < portcount; i++) {
+ if (s->companion_ports[firstport + i]) {
+ qerror_report(QERR_INVALID_PARAMETER_VALUE, "masterbus",
+ "an USB masterbus");
+ error_printf_unless_qmp(
+ "port %u on masterbus %s already has a companion assigned\n",
+ firstport + i, bus->qbus.name);
+ return -1;
+ }
+ }
+
+ for (i = 0; i < portcount; i++) {
+ s->companion_ports[firstport + i] = ports[i];
+ s->ports[firstport + i].speedmask |=
+ USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL;
+ /* Ensure devs attached before the initial reset go to the companion */
+ s->portsc[firstport + i] = PORTSC_POWNER;
+ }
+
+ s->companion_count++;
+ s->mmio[0x05] = (s->companion_count << 4) | portcount;
+
+ return 0;
}
/* 4.1 host controller initialization */
@@ -769,9 +840,21 @@ static void ehci_reset(void *opaque)
{
EHCIState *s = opaque;
int i;
+ USBDevice *devs[NB_PORTS];
trace_usb_ehci_reset();
+ /*
+ * Do the detach before touching portsc, so that it correctly gets send to
+ * us or to our companion based on PORTSC_POWNER before the reset.
+ */
+ for(i = 0; i < NB_PORTS; i++) {
+ devs[i] = s->ports[i].dev;
+ if (devs[i]) {
+ usb_attach(&s->ports[i], NULL);
+ }
+ }
+
memset(&s->mmio[OPREGBASE], 0x00, MMIO_SIZE - OPREGBASE);
s->usbcmd = NB_MAXINTRATE << USBCMD_ITC_SH;
@@ -783,10 +866,13 @@ static void ehci_reset(void *opaque)
s->attach_poll_counter = 0;
for(i = 0; i < NB_PORTS; i++) {
- s->portsc[i] = PORTSC_POWNER | PORTSC_PPOWER;
-
- if (s->ports[i].dev) {
- usb_attach(&s->ports[i], s->ports[i].dev);
+ if (s->companion_ports[i]) {
+ s->portsc[i] = PORTSC_POWNER | PORTSC_PPOWER;
+ } else {
+ s->portsc[i] = PORTSC_PPOWER;
+ }
+ if (devs[i]) {
+ usb_attach(&s->ports[i], devs[i]);
}
}
ehci_queues_rip_all(s);
@@ -836,43 +922,67 @@ static void ehci_mem_writew(void *ptr, target_phys_addr_t addr, uint32_t val)
exit(1);
}
+static void handle_port_owner_write(EHCIState *s, int port, uint32_t owner)
+{
+ USBDevice *dev = s->ports[port].dev;
+ uint32_t *portsc = &s->portsc[port];
+ uint32_t orig;
+
+ if (s->companion_ports[port] == NULL)
+ return;
+
+ owner = owner & PORTSC_POWNER;
+ orig = *portsc & PORTSC_POWNER;
+
+ if (!(owner ^ orig)) {
+ return;
+ }
+
+ if (dev) {
+ usb_attach(&s->ports[port], NULL);
+ }
+
+ *portsc &= ~PORTSC_POWNER;
+ *portsc |= owner;
+
+ if (dev) {
+ usb_attach(&s->ports[port], dev);
+ }
+}
+
static void handle_port_status_write(EHCIState *s, int port, uint32_t val)
{
uint32_t *portsc = &s->portsc[port];
- int rwc;
USBDevice *dev = s->ports[port].dev;
- rwc = val & PORTSC_RWC_MASK;
+ /* Clear rwc bits */
+ *portsc &= ~(val & PORTSC_RWC_MASK);
+ /* The guest may clear, but not set the PED bit */
+ *portsc &= val | ~PORTSC_PED;
+ /* POWNER is masked out by RO_MASK as it is RO when we've no companion */
+ handle_port_owner_write(s, port, val);
+ /* And finally apply RO_MASK */
val &= PORTSC_RO_MASK;
- // handle_read_write_clear(&val, portsc, PORTSC_PEDC | PORTSC_CSC);
-
- *portsc &= ~rwc;
-
if ((val & PORTSC_PRESET) && !(*portsc & PORTSC_PRESET)) {
trace_usb_ehci_port_reset(port, 1);
}
if (!(val & PORTSC_PRESET) &&(*portsc & PORTSC_PRESET)) {
trace_usb_ehci_port_reset(port, 0);
- usb_attach(&s->ports[port], dev);
-
- // TODO how to handle reset of ports with no device
if (dev) {
+ usb_attach(&s->ports[port], dev);
usb_send_msg(dev, USB_MSG_RESET);
- }
-
- if (s->ports[port].dev) {
*portsc &= ~PORTSC_CSC;
}
- /* Table 2.16 Set the enable bit(and enable bit change) to indicate
+ /*
+ * Table 2.16 Set the enable bit(and enable bit change) to indicate
* to SW that this port has a high speed device attached
- *
- * TODO - when to disable?
*/
- val |= PORTSC_PED;
- val |= PORTSC_PEDC;
+ if (dev && (dev->speedmask & USB_SPEED_MASK_HIGH)) {
+ val |= PORTSC_PED;
+ }
}
*portsc &= ~PORTSC_RO_MASK;
@@ -955,7 +1065,7 @@ static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val)
val &= 0x1;
if (val) {
for(i = 0; i < NB_PORTS; i++)
- s->portsc[i] &= ~PORTSC_POWNER;
+ handle_port_owner_write(s, i, 0);
}
break;
@@ -1057,68 +1167,75 @@ static int ehci_qh_do_overlay(EHCIQueue *q)
return 0;
}
-static int ehci_buffer_rw(EHCIQueue *q, int bytes, int rw)
+static int ehci_init_transfer(EHCIQueue *q)
{
- int bufpos = 0;
- int cpage, offset;
- uint32_t head;
- uint32_t tail;
-
-
- if (!bytes) {
- return 0;
- }
-
- cpage = get_field(q->qh.token, QTD_TOKEN_CPAGE);
- if (cpage > 4) {
- fprintf(stderr, "cpage out of range (%d)\n", cpage);
- return USB_RET_PROCERR;
- }
+ uint32_t cpage, offset, bytes, plen;
+ target_phys_addr_t page;
+ cpage = get_field(q->qh.token, QTD_TOKEN_CPAGE);
+ bytes = get_field(q->qh.token, QTD_TOKEN_TBYTES);
offset = q->qh.bufptr[0] & ~QTD_BUFPTR_MASK;
+ qemu_sglist_init(&q->sgl, 5);
- do {
- /* start and end of this page */
- head = q->qh.bufptr[cpage] & QTD_BUFPTR_MASK;
- tail = head + ~QTD_BUFPTR_MASK + 1;
- /* add offset into page */
- head |= offset;
-
- if (bytes <= (tail - head)) {
- tail = head + bytes;
+ while (bytes > 0) {
+ if (cpage > 4) {
+ fprintf(stderr, "cpage out of range (%d)\n", cpage);
+ return USB_RET_PROCERR;
}
- trace_usb_ehci_data(rw, cpage, offset, head, tail-head, bufpos);
- cpu_physical_memory_rw(head, q->buffer + bufpos, tail - head, rw);
-
- bufpos += (tail - head);
- offset += (tail - head);
- bytes -= (tail - head);
-
- if (bytes > 0) {
- cpage++;
+ page = q->qh.bufptr[cpage] & QTD_BUFPTR_MASK;
+ page += offset;
+ plen = bytes;
+ if (plen > 4096 - offset) {
+ plen = 4096 - offset;
offset = 0;
+ cpage++;
}
- } while (bytes > 0);
- /* save cpage */
- set_field(&q->qh.token, cpage, QTD_TOKEN_CPAGE);
+ qemu_sglist_add(&q->sgl, page, plen);
+ bytes -= plen;
+ }
+ return 0;
+}
- /* save offset into cpage */
- q->qh.bufptr[0] &= QTD_BUFPTR_MASK;
- q->qh.bufptr[0] |= offset;
+static void ehci_finish_transfer(EHCIQueue *q, int status)
+{
+ uint32_t cpage, offset;
- return 0;
+ qemu_sglist_destroy(&q->sgl);
+
+ if (status > 0) {
+ /* update cpage & offset */
+ cpage = get_field(q->qh.token, QTD_TOKEN_CPAGE);
+ offset = q->qh.bufptr[0] & ~QTD_BUFPTR_MASK;
+
+ offset += status;
+ cpage += offset >> QTD_BUFPTR_SH;
+ offset &= ~QTD_BUFPTR_MASK;
+
+ set_field(&q->qh.token, cpage, QTD_TOKEN_CPAGE);
+ q->qh.bufptr[0] &= QTD_BUFPTR_MASK;
+ q->qh.bufptr[0] |= offset;
+ }
}
-static void ehci_async_complete_packet(USBDevice *dev, USBPacket *packet)
+static void ehci_async_complete_packet(USBPort *port, USBPacket *packet)
{
- EHCIQueue *q = container_of(packet, EHCIQueue, packet);
+ EHCIQueue *q;
+ EHCIState *s = port->opaque;
+ uint32_t portsc = s->portsc[port->index];
+ if (portsc & PORTSC_POWNER) {
+ USBPort *companion = s->companion_ports[port->index];
+ companion->ops->complete(companion, packet);
+ return;
+ }
+
+ q = container_of(packet, EHCIQueue, packet);
trace_usb_ehci_queue_action(q, "wakeup");
assert(q->async == EHCI_ASYNC_INFLIGHT);
q->async = EHCI_ASYNC_FINISHED;
- q->usb_status = packet->len;
+ q->usb_status = packet->result;
}
static void ehci_execute_complete(EHCIQueue *q)
@@ -1178,10 +1295,6 @@ err:
}
if (q->tbytes && q->pid == USB_TOKEN_IN) {
- if (ehci_buffer_rw(q, q->usb_status, 1) != 0) {
- q->usb_status = USB_RET_PROCERR;
- return;
- }
q->tbytes -= q->usb_status;
} else {
q->tbytes = 0;
@@ -1190,6 +1303,8 @@ err:
DPRINTF("updating tbytes to %d\n", q->tbytes);
set_field(&q->qh.token, q->tbytes, QTD_TOKEN_TBYTES);
}
+ ehci_finish_transfer(q, q->usb_status);
+ usb_packet_unmap(&q->packet);
q->qh.token ^= QTD_TOKEN_DTOGGLE;
q->qh.token &= ~QTD_TOKEN_ACTIVE;
@@ -1229,8 +1344,7 @@ static int ehci_execute(EHCIQueue *q)
default: fprintf(stderr, "bad token\n"); break;
}
- if ((q->tbytes && q->pid != USB_TOKEN_IN) &&
- (ehci_buffer_rw(q, q->tbytes, 0) != 0)) {
+ if (ehci_init_transfer(q) != 0) {
return USB_RET_PROCERR;
}
@@ -1239,30 +1353,26 @@ static int ehci_execute(EHCIQueue *q)
ret = USB_RET_NODEV;
+ usb_packet_setup(&q->packet, q->pid, devadr, endp);
+ usb_packet_map(&q->packet, &q->sgl);
+
// TO-DO: associating device with ehci port
for(i = 0; i < NB_PORTS; i++) {
port = &q->ehci->ports[i];
dev = port->dev;
- // TODO sometime we will also need to check if we are the port owner
-
if (!(q->ehci->portsc[i] &(PORTSC_CONNECT))) {
DPRINTF("Port %d, no exec, not connected(%08X)\n",
i, q->ehci->portsc[i]);
continue;
}
- q->packet.pid = q->pid;
- q->packet.devaddr = devadr;
- q->packet.devep = endp;
- q->packet.data = q->buffer;
- q->packet.len = q->tbytes;
-
ret = usb_handle_packet(dev, &q->packet);
- DPRINTF("submit: qh %x next %x qtd %x pid %x len %d (total %d) endp %x ret %d\n",
+ DPRINTF("submit: qh %x next %x qtd %x pid %x len %zd "
+ "(total %d) endp %x ret %d\n",
q->qhaddr, q->qh.next, q->qtdaddr, q->pid,
- q->packet.len, q->tbytes, endp, ret);
+ q->packet.iov.size, q->tbytes, endp, ret);
if (ret != USB_RET_NODEV) {
break;
@@ -1286,7 +1396,7 @@ static int ehci_process_itd(EHCIState *ehci,
USBPort *port;
USBDevice *dev;
int ret;
- uint32_t i, j, len, len1, len2, pid, dir, devaddr, endp;
+ uint32_t i, j, len, pid, dir, devaddr, endp;
uint32_t pg, off, ptr1, ptr2, max, mult;
dir =(itd->bufptr[1] & ITD_BUFPTR_DIRECTION);
@@ -1311,45 +1421,31 @@ static int ehci_process_itd(EHCIState *ehci,
return USB_RET_PROCERR;
}
+ qemu_sglist_init(&ehci->isgl, 2);
if (off + len > 4096) {
/* transfer crosses page border */
- len2 = off + len - 4096;
- len1 = len - len2;
+ uint32_t len2 = off + len - 4096;
+ uint32_t len1 = len - len2;
+ qemu_sglist_add(&ehci->isgl, ptr1 + off, len1);
+ qemu_sglist_add(&ehci->isgl, ptr2, len2);
} else {
- len1 = len;
- len2 = 0;
+ qemu_sglist_add(&ehci->isgl, ptr1 + off, len);
}
- if (!dir) {
- pid = USB_TOKEN_OUT;
- trace_usb_ehci_data(0, pg, off, ptr1 + off, len1, 0);
- cpu_physical_memory_rw(ptr1 + off, &ehci->ibuffer[0], len1, 0);
- if (len2) {
- trace_usb_ehci_data(0, pg+1, 0, ptr2, len2, len1);
- cpu_physical_memory_rw(ptr2, &ehci->ibuffer[len1], len2, 0);
- }
- } else {
- pid = USB_TOKEN_IN;
- }
+ pid = dir ? USB_TOKEN_IN : USB_TOKEN_OUT;
- ret = USB_RET_NODEV;
+ usb_packet_setup(&ehci->ipacket, pid, devaddr, endp);
+ usb_packet_map(&ehci->ipacket, &ehci->isgl);
+ ret = USB_RET_NODEV;
for (j = 0; j < NB_PORTS; j++) {
port = &ehci->ports[j];
dev = port->dev;
- // TODO sometime we will also need to check if we are the port owner
-
if (!(ehci->portsc[j] &(PORTSC_CONNECT))) {
continue;
}
- ehci->ipacket.pid = pid;
- ehci->ipacket.devaddr = devaddr;
- ehci->ipacket.devep = endp;
- ehci->ipacket.data = ehci->ibuffer;
- ehci->ipacket.len = len;
-
ret = usb_handle_packet(dev, &ehci->ipacket);
if (ret != USB_RET_NODEV) {
@@ -1357,6 +1453,9 @@ static int ehci_process_itd(EHCIState *ehci,
}
}
+ usb_packet_unmap(&ehci->ipacket);
+ qemu_sglist_destroy(&ehci->isgl);
+
#if 0
/* In isoch, there is no facility to indicate a NAK so let's
* instead just complete a zero-byte transaction. Setting
@@ -1394,20 +1493,6 @@ static int ehci_process_itd(EHCIState *ehci,
set_field(&itd->transact[i], len - ret, ITD_XACT_LENGTH);
} else {
/* IN */
- if (len1 > ret) {
- len1 = ret;
- }
- if (len2 > ret - len1) {
- len2 = ret - len1;
- }
- if (len1) {
- trace_usb_ehci_data(1, pg, off, ptr1 + off, len1, 0);
- cpu_physical_memory_rw(ptr1 + off, &ehci->ibuffer[0], len1, 1);
- }
- if (len2) {
- trace_usb_ehci_data(1, pg+1, 0, ptr2, len2, len1);
- cpu_physical_memory_rw(ptr2, &ehci->ibuffer[len1], len2, 1);
- }
set_field(&itd->transact[i], ret, ITD_XACT_LENGTH);
}
@@ -2117,38 +2202,55 @@ static void ehci_map(PCIDevice *pci_dev, int region_num,
cpu_register_physical_memory(addr, size, s->mem);
}
-static void ehci_device_destroy(USBBus *bus, USBDevice *dev)
-{
- EHCIState *s = container_of(bus, EHCIState, bus);
-
- ehci_queues_rip_device(s, dev);
-}
-
static int usb_ehci_initfn(PCIDevice *dev);
static USBPortOps ehci_port_ops = {
.attach = ehci_attach,
.detach = ehci_detach,
+ .child_detach = ehci_child_detach,
+ .wakeup = ehci_wakeup,
.complete = ehci_async_complete_packet,
};
static USBBusOps ehci_bus_ops = {
- .device_destroy = ehci_device_destroy,
+ .register_companion = ehci_register_companion,
+};
+
+static const VMStateDescription vmstate_ehci = {
+ .name = "ehci",
+ .unmigratable = 1,
};
-static PCIDeviceInfo ehci_info = {
- .qdev.name = "usb-ehci",
- .qdev.size = sizeof(EHCIState),
- .init = usb_ehci_initfn,
- .vendor_id = PCI_VENDOR_ID_INTEL,
- .device_id = PCI_DEVICE_ID_INTEL_82801D,
- .revision = 0x10,
- .class_id = PCI_CLASS_SERIAL_USB,
- .qdev.props = (Property[]) {
- DEFINE_PROP_UINT32("freq", EHCIState, freq, FRAME_TIMER_FREQ),
- DEFINE_PROP_UINT32("maxframes", EHCIState, maxframes, 128),
- DEFINE_PROP_END_OF_LIST(),
- },
+static Property ehci_properties[] = {
+ DEFINE_PROP_UINT32("freq", EHCIState, freq, FRAME_TIMER_FREQ),
+ DEFINE_PROP_UINT32("maxframes", EHCIState, maxframes, 128),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static PCIDeviceInfo ehci_info[] = {
+ {
+ .qdev.name = "usb-ehci",
+ .qdev.size = sizeof(EHCIState),
+ .qdev.vmsd = &vmstate_ehci,
+ .init = usb_ehci_initfn,
+ .vendor_id = PCI_VENDOR_ID_INTEL,
+ .device_id = PCI_DEVICE_ID_INTEL_82801D, /* ich4 */
+ .revision = 0x10,
+ .class_id = PCI_CLASS_SERIAL_USB,
+ .qdev.props = ehci_properties,
+ },{
+ .qdev.name = "ich9-usb-ehci1",
+ .qdev.size = sizeof(EHCIState),
+ .qdev.vmsd = &vmstate_ehci,
+ .init = usb_ehci_initfn,
+ .vendor_id = PCI_VENDOR_ID_INTEL,
+ .device_id = PCI_DEVICE_ID_INTEL_82801I_EHCI1,
+ .revision = 0x03,
+ .class_id = PCI_CLASS_SERIAL_USB,
+ .qdev.props = ehci_properties,
+ },{
+ /* end of list */
+ }
};
static int usb_ehci_initfn(PCIDevice *dev)
@@ -2206,7 +2308,6 @@ static int usb_ehci_initfn(PCIDevice *dev)
for(i = 0; i < NB_PORTS; i++) {
usb_register_port(&s->bus, &s->ports[i], s, i, &ehci_port_ops,
USB_SPEED_MASK_HIGH);
- usb_port_location(&s->ports[i], NULL, i+1);
s->ports[i].dev = 0;
}
@@ -2228,7 +2329,7 @@ static int usb_ehci_initfn(PCIDevice *dev)
static void ehci_register(void)
{
- pci_qdev_register(&ehci_info);
+ pci_qdev_register_many(ehci_info);
}
device_init(ehci_register);
diff --git a/hw/usb-hid.c b/hw/usb-hid.c
index d711b5c..e5d57de 100644
--- a/hw/usb-hid.c
+++ b/hw/usb-hid.c
@@ -27,6 +27,7 @@
#include "usb.h"
#include "usb-desc.h"
#include "qemu-timer.h"
+#include "hid.h"
/* HID interface requests */
#define GET_REPORT 0xa101
@@ -41,46 +42,9 @@
#define USB_DT_REPORT 0x22
#define USB_DT_PHY 0x23
-#define USB_MOUSE 1
-#define USB_TABLET 2
-#define USB_KEYBOARD 3
-
-typedef struct USBPointerEvent {
- int32_t xdx, ydy; /* relative iff it's a mouse, otherwise absolute */
- int32_t dz, buttons_state;
-} USBPointerEvent;
-
-#define QUEUE_LENGTH 16 /* should be enough for a triple-click */
-#define QUEUE_MASK (QUEUE_LENGTH-1u)
-#define QUEUE_INCR(v) ((v)++, (v) &= QUEUE_MASK)
-
-typedef struct USBMouseState {
- USBPointerEvent queue[QUEUE_LENGTH];
- int mouse_grabbed;
- QEMUPutMouseEntry *eh_entry;
-} USBMouseState;
-
-typedef struct USBKeyboardState {
- uint32_t keycodes[QUEUE_LENGTH];
- uint16_t modifiers;
- uint8_t leds;
- uint8_t key[16];
- int32_t keys;
-} USBKeyboardState;
-
typedef struct USBHIDState {
USBDevice dev;
- union {
- USBMouseState ptr;
- USBKeyboardState kbd;
- };
- uint32_t head; /* index into circular queue */
- uint32_t n;
- int kind;
- int32_t protocol;
- uint8_t idle;
- int64_t next_idle_clock;
- int changed;
+ HIDState hid;
void *datain_opaque;
void (*datain)(void *);
} USBHIDState;
@@ -394,342 +358,29 @@ static const uint8_t qemu_keyboard_hid_report_descriptor[] = {
0xc0, /* End Collection */
};
-#define USB_HID_USAGE_ERROR_ROLLOVER 0x01
-#define USB_HID_USAGE_POSTFAIL 0x02
-#define USB_HID_USAGE_ERROR_UNDEFINED 0x03
-
-/* Indices are QEMU keycodes, values are from HID Usage Table. Indices
- * above 0x80 are for keys that come after 0xe0 or 0xe1+0x1d or 0xe1+0x9d. */
-static const uint8_t usb_hid_usage_keys[0x100] = {
- 0x00, 0x29, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23,
- 0x24, 0x25, 0x26, 0x27, 0x2d, 0x2e, 0x2a, 0x2b,
- 0x14, 0x1a, 0x08, 0x15, 0x17, 0x1c, 0x18, 0x0c,
- 0x12, 0x13, 0x2f, 0x30, 0x28, 0xe0, 0x04, 0x16,
- 0x07, 0x09, 0x0a, 0x0b, 0x0d, 0x0e, 0x0f, 0x33,
- 0x34, 0x35, 0xe1, 0x31, 0x1d, 0x1b, 0x06, 0x19,
- 0x05, 0x11, 0x10, 0x36, 0x37, 0x38, 0xe5, 0x55,
- 0xe2, 0x2c, 0x32, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e,
- 0x3f, 0x40, 0x41, 0x42, 0x43, 0x53, 0x47, 0x5f,
- 0x60, 0x61, 0x56, 0x5c, 0x5d, 0x5e, 0x57, 0x59,
- 0x5a, 0x5b, 0x62, 0x63, 0x00, 0x00, 0x00, 0x44,
- 0x45, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e,
- 0xe8, 0xe9, 0x71, 0x72, 0x73, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0xe7, 0x65,
-
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x58, 0xe4, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x00, 0x46,
- 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x4a,
- 0x52, 0x4b, 0x00, 0x50, 0x00, 0x4f, 0x00, 0x4d,
- 0x51, 0x4e, 0x49, 0x4c, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0xe3, 0xe7, 0x65, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-};
-
-static void usb_hid_changed(USBHIDState *hs)
-{
- hs->changed = 1;
-
- if (hs->datain)
- hs->datain(hs->datain_opaque);
-
- usb_wakeup(&hs->dev);
-}
-
-static void usb_pointer_event_clear(USBPointerEvent *e, int buttons) {
- e->xdx = e->ydy = e->dz = 0;
- e->buttons_state = buttons;
-}
-
-static void usb_pointer_event_combine(USBPointerEvent *e, int xyrel,
- int x1, int y1, int z1) {
- if (xyrel) {
- e->xdx += x1;
- e->ydy += y1;
- } else {
- e->xdx = x1;
- e->ydy = y1;
- }
- e->dz += z1;
-}
-
-static void usb_pointer_event(void *opaque,
- int x1, int y1, int z1, int buttons_state)
-{
- USBHIDState *hs = opaque;
- USBMouseState *s = &hs->ptr;
- unsigned use_slot = (hs->head + hs->n - 1) & QUEUE_MASK;
- unsigned previous_slot = (use_slot - 1) & QUEUE_MASK;
-
- /* We combine events where feasible to keep the queue small. We shouldn't
- * combine anything with the first event of a particular button state, as
- * that would change the location of the button state change. When the
- * queue is empty, a second event is needed because we don't know if
- * the first event changed the button state. */
- if (hs->n == QUEUE_LENGTH) {
- /* Queue full. Discard old button state, combine motion normally. */
- s->queue[use_slot].buttons_state = buttons_state;
- } else if (hs->n < 2 ||
- s->queue[use_slot].buttons_state != buttons_state ||
- s->queue[previous_slot].buttons_state != s->queue[use_slot].buttons_state) {
- /* Cannot or should not combine, so add an empty item to the queue. */
- QUEUE_INCR(use_slot);
- hs->n++;
- usb_pointer_event_clear(&s->queue[use_slot], buttons_state);
- }
- usb_pointer_event_combine(&s->queue[use_slot],
- hs->kind == USB_MOUSE,
- x1, y1, z1);
- usb_hid_changed(hs);
-}
-
-static void usb_keyboard_event(void *opaque, int keycode)
-{
- USBHIDState *hs = opaque;
- USBKeyboardState *s = &hs->kbd;
- int slot;
-
- if (hs->n == QUEUE_LENGTH) {
- fprintf(stderr, "usb-kbd: warning: key event queue full\n");
- return;
- }
- slot = (hs->head + hs->n) & QUEUE_MASK; hs->n++;
- s->keycodes[slot] = keycode;
- usb_hid_changed(hs);
-}
-
-static void usb_keyboard_process_keycode(USBHIDState *hs)
-{
- USBKeyboardState *s = &hs->kbd;
- uint8_t hid_code, key;
- int i, keycode, slot;
-
- if (hs->n == 0) {
- return;
- }
- slot = hs->head & QUEUE_MASK; QUEUE_INCR(hs->head); hs->n--;
- keycode = s->keycodes[slot];
-
- key = keycode & 0x7f;
- hid_code = usb_hid_usage_keys[key | ((s->modifiers >> 1) & (1 << 7))];
- s->modifiers &= ~(1 << 8);
-
- switch (hid_code) {
- case 0x00:
- return;
-
- case 0xe0:
- if (s->modifiers & (1 << 9)) {
- s->modifiers ^= 3 << 8;
- usb_hid_changed(hs);
- return;
- }
- case 0xe1 ... 0xe7:
- if (keycode & (1 << 7)) {
- s->modifiers &= ~(1 << (hid_code & 0x0f));
- usb_hid_changed(hs);
- return;
- }
- case 0xe8 ... 0xef:
- s->modifiers |= 1 << (hid_code & 0x0f);
- usb_hid_changed(hs);
- return;
- }
-
- if (keycode & (1 << 7)) {
- for (i = s->keys - 1; i >= 0; i --)
- if (s->key[i] == hid_code) {
- s->key[i] = s->key[-- s->keys];
- s->key[s->keys] = 0x00;
- break;
- }
- if (i < 0)
- return;
- } else {
- for (i = s->keys - 1; i >= 0; i --)
- if (s->key[i] == hid_code)
- break;
- if (i < 0) {
- if (s->keys < sizeof(s->key))
- s->key[s->keys ++] = hid_code;
- } else
- return;
- }
-}
-
-static inline int int_clamp(int val, int vmin, int vmax)
-{
- if (val < vmin)
- return vmin;
- else if (val > vmax)
- return vmax;
- else
- return val;
-}
-
-static int usb_pointer_poll(USBHIDState *hs, uint8_t *buf, int len)
-{
- int dx, dy, dz, b, l;
- int index;
- USBMouseState *s = &hs->ptr;
- USBPointerEvent *e;
-
- if (!s->mouse_grabbed) {
- qemu_activate_mouse_event_handler(s->eh_entry);
- s->mouse_grabbed = 1;
- }
-
- /* When the buffer is empty, return the last event. Relative
- movements will all be zero. */
- index = (hs->n ? hs->head : hs->head - 1);
- e = &s->queue[index & QUEUE_MASK];
-
- if (hs->kind == USB_MOUSE) {
- dx = int_clamp(e->xdx, -127, 127);
- dy = int_clamp(e->ydy, -127, 127);
- e->xdx -= dx;
- e->ydy -= dy;
- } else {
- dx = e->xdx;
- dy = e->ydy;
- }
- dz = int_clamp(e->dz, -127, 127);
- e->dz -= dz;
-
- b = 0;
- if (e->buttons_state & MOUSE_EVENT_LBUTTON)
- b |= 0x01;
- if (e->buttons_state & MOUSE_EVENT_RBUTTON)
- b |= 0x02;
- if (e->buttons_state & MOUSE_EVENT_MBUTTON)
- b |= 0x04;
-
- if (hs->n &&
- !e->dz &&
- (hs->kind == USB_TABLET || (!e->xdx && !e->ydy))) {
- /* that deals with this event */
- QUEUE_INCR(hs->head);
- hs->n--;
- }
-
- /* Appears we have to invert the wheel direction */
- dz = 0 - dz;
- l = 0;
- switch (hs->kind) {
- case USB_MOUSE:
- if (len > l)
- buf[l++] = b;
- if (len > l)
- buf[l++] = dx;
- if (len > l)
- buf[l++] = dy;
- if (len > l)
- buf[l++] = dz;
- break;
-
- case USB_TABLET:
- if (len > l)
- buf[l++] = b;
- if (len > l)
- buf[l++] = dx & 0xff;
- if (len > l)
- buf[l++] = dx >> 8;
- if (len > l)
- buf[l++] = dy & 0xff;
- if (len > l)
- buf[l++] = dy >> 8;
- if (len > l)
- buf[l++] = dz;
- break;
-
- default:
- abort();
- }
-
- return l;
-}
-
-static int usb_keyboard_poll(USBHIDState *hs, uint8_t *buf, int len)
+static void usb_hid_changed(HIDState *hs)
{
- USBKeyboardState *s = &hs->kbd;
- if (len < 2)
- return 0;
-
- usb_keyboard_process_keycode(hs);
-
- buf[0] = s->modifiers & 0xff;
- buf[1] = 0;
- if (s->keys > 6)
- memset(buf + 2, USB_HID_USAGE_ERROR_ROLLOVER, MIN(8, len) - 2);
- else
- memcpy(buf + 2, s->key, MIN(8, len) - 2);
-
- return MIN(8, len);
-}
+ USBHIDState *us = container_of(hs, USBHIDState, hid);
-static int usb_keyboard_write(USBKeyboardState *s, uint8_t *buf, int len)
-{
- if (len > 0) {
- int ledstate = 0;
- /* 0x01: Num Lock LED
- * 0x02: Caps Lock LED
- * 0x04: Scroll Lock LED
- * 0x08: Compose LED
- * 0x10: Kana LED */
- s->leds = buf[0];
- if (s->leds & 0x04)
- ledstate |= QEMU_SCROLL_LOCK_LED;
- if (s->leds & 0x01)
- ledstate |= QEMU_NUM_LOCK_LED;
- if (s->leds & 0x02)
- ledstate |= QEMU_CAPS_LOCK_LED;
- kbd_put_ledstate(ledstate);
+ if (us->datain) {
+ us->datain(us->datain_opaque);
}
- return 0;
-}
-static void usb_mouse_handle_reset(USBDevice *dev)
-{
- USBHIDState *s = (USBHIDState *)dev;
-
- memset(s->ptr.queue, 0, sizeof (s->ptr.queue));
- s->head = 0;
- s->n = 0;
- s->protocol = 1;
+ usb_wakeup(&us->dev);
}
-static void usb_keyboard_handle_reset(USBDevice *dev)
+static void usb_hid_handle_reset(USBDevice *dev)
{
- USBHIDState *s = (USBHIDState *)dev;
-
- qemu_add_kbd_event_handler(usb_keyboard_event, s);
- memset(s->kbd.keycodes, 0, sizeof (s->kbd.keycodes));
- s->head = 0;
- s->n = 0;
- memset(s->kbd.key, 0, sizeof (s->kbd.key));
- s->kbd.keys = 0;
- s->protocol = 1;
-}
+ USBHIDState *us = DO_UPCAST(USBHIDState, dev, dev);
-static void usb_hid_set_next_idle(USBHIDState *s, int64_t curtime)
-{
- s->next_idle_clock = curtime + (get_ticks_per_sec() * s->idle * 4) / 1000;
+ hid_reset(&us->hid);
}
static int usb_hid_handle_control(USBDevice *dev, USBPacket *p,
int request, int value, int index, int length, uint8_t *data)
{
- USBHIDState *s = (USBHIDState *)dev;
+ USBHIDState *us = DO_UPCAST(USBHIDState, dev, dev);
+ HIDState *hs = &us->hid;
int ret;
ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
@@ -738,7 +389,7 @@ static int usb_hid_handle_control(USBDevice *dev, USBPacket *p,
}
ret = 0;
- switch(request) {
+ switch (request) {
case DeviceRequest | USB_REQ_GET_INTERFACE:
data[0] = 0;
ret = 1;
@@ -748,17 +399,17 @@ static int usb_hid_handle_control(USBDevice *dev, USBPacket *p,
break;
/* hid specific requests */
case InterfaceRequest | USB_REQ_GET_DESCRIPTOR:
- switch(value >> 8) {
+ switch (value >> 8) {
case 0x22:
- if (s->kind == USB_MOUSE) {
+ if (hs->kind == HID_MOUSE) {
memcpy(data, qemu_mouse_hid_report_descriptor,
sizeof(qemu_mouse_hid_report_descriptor));
ret = sizeof(qemu_mouse_hid_report_descriptor);
- } else if (s->kind == USB_TABLET) {
- memcpy(data, qemu_tablet_hid_report_descriptor,
+ } else if (hs->kind == HID_TABLET) {
+ memcpy(data, qemu_tablet_hid_report_descriptor,
sizeof(qemu_tablet_hid_report_descriptor));
ret = sizeof(qemu_tablet_hid_report_descriptor);
- } else if (s->kind == USB_KEYBOARD) {
+ } else if (hs->kind == HID_KEYBOARD) {
memcpy(data, qemu_keyboard_hid_report_descriptor,
sizeof(qemu_keyboard_hid_report_descriptor));
ret = sizeof(qemu_keyboard_hid_report_descriptor);
@@ -769,36 +420,40 @@ static int usb_hid_handle_control(USBDevice *dev, USBPacket *p,
}
break;
case GET_REPORT:
- if (s->kind == USB_MOUSE || s->kind == USB_TABLET)
- ret = usb_pointer_poll(s, data, length);
- else if (s->kind == USB_KEYBOARD)
- ret = usb_keyboard_poll(s, data, length);
+ if (hs->kind == HID_MOUSE || hs->kind == HID_TABLET) {
+ ret = hid_pointer_poll(hs, data, length);
+ } else if (hs->kind == HID_KEYBOARD) {
+ ret = hid_keyboard_poll(hs, data, length);
+ }
break;
case SET_REPORT:
- if (s->kind == USB_KEYBOARD)
- ret = usb_keyboard_write(&s->kbd, data, length);
- else
+ if (hs->kind == HID_KEYBOARD) {
+ ret = hid_keyboard_write(hs, data, length);
+ } else {
goto fail;
+ }
break;
case GET_PROTOCOL:
- if (s->kind != USB_KEYBOARD && s->kind != USB_MOUSE)
+ if (hs->kind != HID_KEYBOARD && hs->kind != HID_MOUSE) {
goto fail;
+ }
ret = 1;
- data[0] = s->protocol;
+ data[0] = hs->protocol;
break;
case SET_PROTOCOL:
- if (s->kind != USB_KEYBOARD && s->kind != USB_MOUSE)
+ if (hs->kind != HID_KEYBOARD && hs->kind != HID_MOUSE) {
goto fail;
+ }
ret = 0;
- s->protocol = value;
+ hs->protocol = value;
break;
case GET_IDLE:
ret = 1;
- data[0] = s->idle;
+ data[0] = hs->idle;
break;
case SET_IDLE:
- s->idle = (uint8_t) (value >> 8);
- usb_hid_set_next_idle(s, qemu_get_clock_ns(vm_clock));
+ hs->idle = (uint8_t) (value >> 8);
+ hid_set_next_idle(hs, qemu_get_clock_ns(vm_clock));
ret = 0;
break;
default:
@@ -811,23 +466,26 @@ static int usb_hid_handle_control(USBDevice *dev, USBPacket *p,
static int usb_hid_handle_data(USBDevice *dev, USBPacket *p)
{
- USBHIDState *s = (USBHIDState *)dev;
+ USBHIDState *us = DO_UPCAST(USBHIDState, dev, dev);
+ HIDState *hs = &us->hid;
+ uint8_t buf[p->iov.size];
int ret = 0;
- switch(p->pid) {
+ switch (p->pid) {
case USB_TOKEN_IN:
if (p->devep == 1) {
int64_t curtime = qemu_get_clock_ns(vm_clock);
- if (!s->changed && (!s->idle || s->next_idle_clock - curtime > 0))
+ if (!hid_has_events(hs) &&
+ (!hs->idle || hs->next_idle_clock - curtime > 0)) {
return USB_RET_NAK;
- usb_hid_set_next_idle(s, curtime);
- if (s->kind == USB_MOUSE || s->kind == USB_TABLET) {
- ret = usb_pointer_poll(s, p->data, p->len);
}
- else if (s->kind == USB_KEYBOARD) {
- ret = usb_keyboard_poll(s, p->data, p->len);
+ hid_set_next_idle(hs, curtime);
+ if (hs->kind == HID_MOUSE || hs->kind == HID_TABLET) {
+ ret = hid_pointer_poll(hs, buf, p->iov.size);
+ } else if (hs->kind == HID_KEYBOARD) {
+ ret = hid_keyboard_poll(hs, buf, p->iov.size);
}
- s->changed = s->n > 0;
+ usb_packet_copy(p, buf, ret);
} else {
goto fail;
}
@@ -843,50 +501,33 @@ static int usb_hid_handle_data(USBDevice *dev, USBPacket *p)
static void usb_hid_handle_destroy(USBDevice *dev)
{
- USBHIDState *s = (USBHIDState *)dev;
+ USBHIDState *us = DO_UPCAST(USBHIDState, dev, dev);
- switch(s->kind) {
- case USB_KEYBOARD:
- qemu_remove_kbd_event_handler();
- break;
- default:
- qemu_remove_mouse_event_handler(s->ptr.eh_entry);
- }
+ hid_free(&us->hid);
}
static int usb_hid_initfn(USBDevice *dev, int kind)
{
- USBHIDState *s = DO_UPCAST(USBHIDState, dev, dev);
+ USBHIDState *us = DO_UPCAST(USBHIDState, dev, dev);
usb_desc_init(dev);
- s->kind = kind;
-
- if (s->kind == USB_MOUSE) {
- s->ptr.eh_entry = qemu_add_mouse_event_handler(usb_pointer_event, s,
- 0, "QEMU USB Mouse");
- } else if (s->kind == USB_TABLET) {
- s->ptr.eh_entry = qemu_add_mouse_event_handler(usb_pointer_event, s,
- 1, "QEMU USB Tablet");
- }
-
- /* Force poll routine to be run and grab input the first time. */
- s->changed = 1;
+ hid_init(&us->hid, kind, usb_hid_changed);
return 0;
}
static int usb_tablet_initfn(USBDevice *dev)
{
- return usb_hid_initfn(dev, USB_TABLET);
+ return usb_hid_initfn(dev, HID_TABLET);
}
static int usb_mouse_initfn(USBDevice *dev)
{
- return usb_hid_initfn(dev, USB_MOUSE);
+ return usb_hid_initfn(dev, HID_MOUSE);
}
static int usb_keyboard_initfn(USBDevice *dev)
{
- return usb_hid_initfn(dev, USB_KEYBOARD);
+ return usb_hid_initfn(dev, HID_KEYBOARD);
}
void usb_hid_datain_cb(USBDevice *dev, void *opaque, void (*datain)(void *))
@@ -901,8 +542,8 @@ static int usb_hid_post_load(void *opaque, int version_id)
{
USBHIDState *s = opaque;
- if (s->idle) {
- usb_hid_set_next_idle(s, qemu_get_clock_ns(vm_clock));
+ if (s->hid.idle) {
+ hid_set_next_idle(&s->hid, qemu_get_clock_ns(vm_clock));
}
return 0;
}
@@ -912,10 +553,10 @@ static const VMStateDescription vmstate_usb_ptr_queue = {
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField []) {
- VMSTATE_INT32(xdx, USBPointerEvent),
- VMSTATE_INT32(ydy, USBPointerEvent),
- VMSTATE_INT32(dz, USBPointerEvent),
- VMSTATE_INT32(buttons_state, USBPointerEvent),
+ VMSTATE_INT32(xdx, HIDPointerEvent),
+ VMSTATE_INT32(ydy, HIDPointerEvent),
+ VMSTATE_INT32(dz, HIDPointerEvent),
+ VMSTATE_INT32(buttons_state, HIDPointerEvent),
VMSTATE_END_OF_LIST()
}
};
@@ -926,12 +567,12 @@ static const VMStateDescription vmstate_usb_ptr = {
.post_load = usb_hid_post_load,
.fields = (VMStateField []) {
VMSTATE_USB_DEVICE(dev, USBHIDState),
- VMSTATE_STRUCT_ARRAY(ptr.queue, USBHIDState, QUEUE_LENGTH, 0,
- vmstate_usb_ptr_queue, USBPointerEvent),
- VMSTATE_UINT32(head, USBHIDState),
- VMSTATE_UINT32(n, USBHIDState),
- VMSTATE_INT32(protocol, USBHIDState),
- VMSTATE_UINT8(idle, USBHIDState),
+ VMSTATE_STRUCT_ARRAY(hid.ptr.queue, USBHIDState, QUEUE_LENGTH, 0,
+ vmstate_usb_ptr_queue, HIDPointerEvent),
+ VMSTATE_UINT32(hid.head, USBHIDState),
+ VMSTATE_UINT32(hid.n, USBHIDState),
+ VMSTATE_INT32(hid.protocol, USBHIDState),
+ VMSTATE_UINT8(hid.idle, USBHIDState),
VMSTATE_END_OF_LIST()
}
};
@@ -943,15 +584,15 @@ static const VMStateDescription vmstate_usb_kbd = {
.post_load = usb_hid_post_load,
.fields = (VMStateField []) {
VMSTATE_USB_DEVICE(dev, USBHIDState),
- VMSTATE_UINT32_ARRAY(kbd.keycodes, USBHIDState, QUEUE_LENGTH),
- VMSTATE_UINT32(head, USBHIDState),
- VMSTATE_UINT32(n, USBHIDState),
- VMSTATE_UINT16(kbd.modifiers, USBHIDState),
- VMSTATE_UINT8(kbd.leds, USBHIDState),
- VMSTATE_UINT8_ARRAY(kbd.key, USBHIDState, 16),
- VMSTATE_INT32(kbd.keys, USBHIDState),
- VMSTATE_INT32(protocol, USBHIDState),
- VMSTATE_UINT8(idle, USBHIDState),
+ VMSTATE_UINT32_ARRAY(hid.kbd.keycodes, USBHIDState, QUEUE_LENGTH),
+ VMSTATE_UINT32(hid.head, USBHIDState),
+ VMSTATE_UINT32(hid.n, USBHIDState),
+ VMSTATE_UINT16(hid.kbd.modifiers, USBHIDState),
+ VMSTATE_UINT8(hid.kbd.leds, USBHIDState),
+ VMSTATE_UINT8_ARRAY(hid.kbd.key, USBHIDState, 16),
+ VMSTATE_INT32(hid.kbd.keys, USBHIDState),
+ VMSTATE_INT32(hid.protocol, USBHIDState),
+ VMSTATE_UINT8(hid.idle, USBHIDState),
VMSTATE_END_OF_LIST()
}
};
@@ -966,7 +607,7 @@ static struct USBDeviceInfo hid_info[] = {
.usb_desc = &desc_tablet,
.init = usb_tablet_initfn,
.handle_packet = usb_generic_handle_packet,
- .handle_reset = usb_mouse_handle_reset,
+ .handle_reset = usb_hid_handle_reset,
.handle_control = usb_hid_handle_control,
.handle_data = usb_hid_handle_data,
.handle_destroy = usb_hid_handle_destroy,
@@ -979,7 +620,7 @@ static struct USBDeviceInfo hid_info[] = {
.usb_desc = &desc_mouse,
.init = usb_mouse_initfn,
.handle_packet = usb_generic_handle_packet,
- .handle_reset = usb_mouse_handle_reset,
+ .handle_reset = usb_hid_handle_reset,
.handle_control = usb_hid_handle_control,
.handle_data = usb_hid_handle_data,
.handle_destroy = usb_hid_handle_destroy,
@@ -992,7 +633,7 @@ static struct USBDeviceInfo hid_info[] = {
.usb_desc = &desc_keyboard,
.init = usb_keyboard_initfn,
.handle_packet = usb_generic_handle_packet,
- .handle_reset = usb_keyboard_handle_reset,
+ .handle_reset = usb_hid_handle_reset,
.handle_control = usb_hid_handle_control,
.handle_data = usb_hid_handle_data,
.handle_destroy = usb_hid_handle_destroy,
diff --git a/hw/usb-hub.c b/hw/usb-hub.c
index 6e2a358..c49c547 100644
--- a/hw/usb-hub.c
+++ b/hw/usb-hub.c
@@ -138,74 +138,6 @@ static const USBDesc desc_hub = {
.str = desc_strings,
};
-static const uint8_t qemu_hub_dev_descriptor[] = {
- 0x12, /* u8 bLength; */
- 0x01, /* u8 bDescriptorType; Device */
- 0x10, 0x01, /* u16 bcdUSB; v1.1 */
-
- 0x09, /* u8 bDeviceClass; HUB_CLASSCODE */
- 0x00, /* u8 bDeviceSubClass; */
- 0x00, /* u8 bDeviceProtocol; [ low/full speeds only ] */
- 0x08, /* u8 bMaxPacketSize0; 8 Bytes */
-
- 0x00, 0x00, /* u16 idVendor; */
- 0x00, 0x00, /* u16 idProduct; */
- 0x01, 0x01, /* u16 bcdDevice */
-
- 0x03, /* u8 iManufacturer; */
- 0x02, /* u8 iProduct; */
- 0x01, /* u8 iSerialNumber; */
- 0x01 /* u8 bNumConfigurations; */
-};
-
-/* XXX: patch interrupt size */
-static const uint8_t qemu_hub_config_descriptor[] = {
-
- /* one configuration */
- 0x09, /* u8 bLength; */
- 0x02, /* u8 bDescriptorType; Configuration */
- 0x19, 0x00, /* u16 wTotalLength; */
- 0x01, /* u8 bNumInterfaces; (1) */
- 0x01, /* u8 bConfigurationValue; */
- 0x00, /* u8 iConfiguration; */
- 0xe0, /* u8 bmAttributes;
- Bit 7: must be set,
- 6: Self-powered,
- 5: Remote wakeup,
- 4..0: resvd */
- 0x00, /* u8 MaxPower; */
-
- /* USB 1.1:
- * USB 2.0, single TT organization (mandatory):
- * one interface, protocol 0
- *
- * USB 2.0, multiple TT organization (optional):
- * two interfaces, protocols 1 (like single TT)
- * and 2 (multiple TT mode) ... config is
- * sometimes settable
- * NOT IMPLEMENTED
- */
-
- /* one interface */
- 0x09, /* u8 if_bLength; */
- 0x04, /* u8 if_bDescriptorType; Interface */
- 0x00, /* u8 if_bInterfaceNumber; */
- 0x00, /* u8 if_bAlternateSetting; */
- 0x01, /* u8 if_bNumEndpoints; */
- 0x09, /* u8 if_bInterfaceClass; HUB_CLASSCODE */
- 0x00, /* u8 if_bInterfaceSubClass; */
- 0x00, /* u8 if_bInterfaceProtocol; [usb1.1 or single tt] */
- 0x00, /* u8 if_iInterface; */
-
- /* one endpoint (status change endpoint) */
- 0x07, /* u8 ep_bLength; */
- 0x05, /* u8 ep_bDescriptorType; Endpoint */
- 0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */
- 0x03, /* u8 ep_bmAttributes; Interrupt */
- 0x02, 0x00, /* u16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8) */
- 0xff /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */
-};
-
static const uint8_t qemu_hub_hub_descriptor[] =
{
0x00, /* u8 bLength; patched in later */
@@ -238,6 +170,9 @@ static void usb_hub_detach(USBPort *port1)
USBHubState *s = port1->opaque;
USBHubPort *port = &s->ports[port1->index];
+ /* Let upstream know the device on this port is gone */
+ s->dev.port->ops->child_detach(s->dev.port, port1->dev);
+
port->wPortStatus &= ~PORT_STAT_CONNECTION;
port->wPortChange |= PORT_STAT_C_CONNECTION;
if (port->wPortStatus & PORT_STAT_ENABLE) {
@@ -246,10 +181,18 @@ static void usb_hub_detach(USBPort *port1)
}
}
-static void usb_hub_wakeup(USBDevice *dev)
+static void usb_hub_child_detach(USBPort *port1, USBDevice *child)
+{
+ USBHubState *s = port1->opaque;
+
+ /* Pass along upstream */
+ s->dev.port->ops->child_detach(s->dev.port, child);
+}
+
+static void usb_hub_wakeup(USBPort *port1)
{
- USBHubState *s = dev->port->opaque;
- USBHubPort *port = &s->ports[dev->port->index];
+ USBHubState *s = port1->opaque;
+ USBHubPort *port = &s->ports[port1->index];
if (port->wPortStatus & PORT_STAT_SUSPEND) {
port->wPortChange |= PORT_STAT_C_SUSPEND;
@@ -257,9 +200,9 @@ static void usb_hub_wakeup(USBDevice *dev)
}
}
-static void usb_hub_complete(USBDevice *dev, USBPacket *packet)
+static void usb_hub_complete(USBPort *port, USBPacket *packet)
{
- USBHubState *s = dev->port->opaque;
+ USBHubState *s = port->opaque;
/*
* Just pass it along upstream for now.
@@ -451,11 +394,12 @@ static int usb_hub_handle_data(USBDevice *dev, USBPacket *p)
if (p->devep == 1) {
USBHubPort *port;
unsigned int status;
+ uint8_t buf[4];
int i, n;
n = (NUM_PORTS + 1 + 7) / 8;
- if (p->len == 1) { /* FreeBSD workaround */
+ if (p->iov.size == 1) { /* FreeBSD workaround */
n = 1;
- } else if (n > p->len) {
+ } else if (n > p->iov.size) {
return USB_RET_BABBLE;
}
status = 0;
@@ -466,8 +410,9 @@ static int usb_hub_handle_data(USBDevice *dev, USBPacket *p)
}
if (status != 0) {
for(i = 0; i < n; i++) {
- p->data[i] = status >> (8 * i);
+ buf[i] = status >> (8 * i);
}
+ usb_packet_copy(p, buf, n);
ret = n;
} else {
ret = USB_RET_NAK; /* usb11 11.13.1 */
@@ -537,6 +482,7 @@ static void usb_hub_handle_destroy(USBDevice *dev)
static USBPortOps usb_hub_port_ops = {
.attach = usb_hub_attach,
.detach = usb_hub_detach,
+ .child_detach = usb_hub_child_detach,
.wakeup = usb_hub_wakeup,
.complete = usb_hub_complete,
};
diff --git a/hw/usb-libhw.c b/hw/usb-libhw.c
new file mode 100644
index 0000000..162b42b
--- /dev/null
+++ b/hw/usb-libhw.c
@@ -0,0 +1,63 @@
+/*
+ * QEMU USB emulation, libhw bits.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu-common.h"
+#include "cpu-common.h"
+#include "usb.h"
+#include "dma.h"
+
+int usb_packet_map(USBPacket *p, QEMUSGList *sgl)
+{
+ int is_write = (p->pid == USB_TOKEN_IN);
+ target_phys_addr_t len;
+ void *mem;
+ int i;
+
+ for (i = 0; i < sgl->nsg; i++) {
+ len = sgl->sg[i].len;
+ mem = cpu_physical_memory_map(sgl->sg[i].base, &len,
+ is_write);
+ if (!mem) {
+ goto err;
+ }
+ qemu_iovec_add(&p->iov, mem, len);
+ if (len != sgl->sg[i].len) {
+ goto err;
+ }
+ }
+ return 0;
+
+err:
+ usb_packet_unmap(p);
+ return -1;
+}
+
+void usb_packet_unmap(USBPacket *p)
+{
+ int is_write = (p->pid == USB_TOKEN_IN);
+ int i;
+
+ for (i = 0; i < p->iov.niov; i++) {
+ cpu_physical_memory_unmap(p->iov.iov[i].iov_base,
+ p->iov.iov[i].iov_len, is_write,
+ p->iov.iov[i].iov_len);
+ }
+}
diff --git a/hw/usb-msd.c b/hw/usb-msd.c
index 86582cc..90e57fb 100644
--- a/hw/usb-msd.c
+++ b/hw/usb-msd.c
@@ -4,7 +4,7 @@
* Copyright (c) 2006 CodeSourcery.
* Written by Paul Brook
*
- * This code is licenced under the LGPL.
+ * This code is licensed under the LGPL.
*/
#include "qemu-common.h"
@@ -43,8 +43,6 @@ typedef struct {
enum USBMSDMode mode;
uint32_t scsi_len;
uint8_t *scsi_buf;
- uint32_t usb_len;
- uint8_t *usb_buf;
uint32_t data_len;
uint32_t residue;
uint32_t tag;
@@ -176,20 +174,14 @@ static const USBDesc desc = {
.str = desc_strings,
};
-static void usb_msd_copy_data(MSDState *s)
+static void usb_msd_copy_data(MSDState *s, USBPacket *p)
{
uint32_t len;
- len = s->usb_len;
+ len = p->iov.size - p->result;
if (len > s->scsi_len)
len = s->scsi_len;
- if (s->mode == USB_MSDM_DATAIN) {
- memcpy(s->usb_buf, s->scsi_buf, len);
- } else {
- memcpy(s->scsi_buf, s->usb_buf, len);
- }
- s->usb_len -= len;
+ usb_packet_copy(p, s->scsi_buf, len);
s->scsi_len -= len;
- s->usb_buf += len;
s->scsi_buf += len;
s->data_len -= len;
if (s->scsi_len == 0 || s->data_len == 0) {
@@ -207,8 +199,9 @@ static void usb_msd_send_status(MSDState *s, USBPacket *p)
csw.residue = s->residue;
csw.status = s->result;
- len = MIN(sizeof(csw), p->len);
- memcpy(p->data, &csw, len);
+ len = MIN(sizeof(csw), p->iov.size);
+ usb_packet_copy(p, &csw, len);
+ p->result = len;
}
static void usb_msd_transfer_data(SCSIRequest *req, uint32_t len)
@@ -216,16 +209,13 @@ static void usb_msd_transfer_data(SCSIRequest *req, uint32_t len)
MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent);
USBPacket *p = s->packet;
- if (req->tag != s->tag) {
- fprintf(stderr, "usb-msd: Unexpected SCSI Tag 0x%x\n", req->tag);
- }
-
assert((s->mode == USB_MSDM_DATAOUT) == (req->cmd.mode == SCSI_XFER_TO_DEV));
s->scsi_len = len;
s->scsi_buf = scsi_req_get_buf(req);
if (p) {
- usb_msd_copy_data(s);
- if (s->packet && s->usb_len == 0) {
+ usb_msd_copy_data(s, p);
+ p = s->packet;
+ if (p && p->result == p->iov.size) {
/* Set s->packet to NULL before calling usb_packet_complete
because another request may be issued before
usb_packet_complete returns. */
@@ -241,9 +231,6 @@ static void usb_msd_command_complete(SCSIRequest *req, uint32_t status)
MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent);
USBPacket *p = s->packet;
- if (req->tag != s->tag) {
- fprintf(stderr, "usb-msd: Unexpected SCSI Tag 0x%x\n", req->tag);
- }
DPRINTF("Command complete %d\n", status);
s->residue = s->data_len;
s->result = status != 0;
@@ -255,11 +242,9 @@ static void usb_msd_command_complete(SCSIRequest *req, uint32_t status)
s->mode = USB_MSDM_CBW;
} else {
if (s->data_len) {
- s->data_len -= s->usb_len;
- if (s->mode == USB_MSDM_DATAIN) {
- memset(s->usb_buf, 0, s->usb_len);
- }
- s->usb_len = 0;
+ int len = (p->iov.size - p->result);
+ usb_packet_skip(p, len);
+ s->data_len -= len;
}
if (s->data_len == 0) {
s->mode = USB_MSDM_CSW;
@@ -349,8 +334,6 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
int ret = 0;
struct usb_msd_cbw cbw;
uint8_t devep = p->devep;
- uint8_t *data = p->data;
- int len = p->len;
switch (p->pid) {
case USB_TOKEN_OUT:
@@ -359,11 +342,11 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
switch (s->mode) {
case USB_MSDM_CBW:
- if (len != 31) {
+ if (p->iov.size != 31) {
fprintf(stderr, "usb-msd: Bad CBW size");
goto fail;
}
- memcpy(&cbw, data, 31);
+ usb_packet_copy(p, &cbw, 31);
if (le32_to_cpu(cbw.sig) != 0x43425355) {
fprintf(stderr, "usb-msd: Bad signature %08x\n",
le32_to_cpu(cbw.sig));
@@ -387,43 +370,46 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
s->tag, cbw.flags, cbw.cmd_len, s->data_len);
s->residue = 0;
s->scsi_len = 0;
- s->req = scsi_req_new(s->scsi_dev, s->tag, 0);
+ s->req = scsi_req_new(s->scsi_dev, s->tag, 0, NULL);
scsi_req_enqueue(s->req, cbw.cmd);
/* ??? Should check that USB and SCSI data transfer
directions match. */
if (s->mode != USB_MSDM_CSW && s->residue == 0) {
scsi_req_continue(s->req);
}
- ret = len;
+ ret = p->result;
break;
case USB_MSDM_DATAOUT:
- DPRINTF("Data out %d/%d\n", len, s->data_len);
- if (len > s->data_len)
+ DPRINTF("Data out %zd/%d\n", p->iov.size, s->data_len);
+ if (p->iov.size > s->data_len) {
goto fail;
+ }
- s->usb_buf = data;
- s->usb_len = len;
if (s->scsi_len) {
- usb_msd_copy_data(s);
+ usb_msd_copy_data(s, p);
}
- if (s->residue && s->usb_len) {
- s->data_len -= s->usb_len;
- if (s->data_len == 0)
- s->mode = USB_MSDM_CSW;
- s->usb_len = 0;
+ if (s->residue) {
+ int len = p->iov.size - p->result;
+ if (len) {
+ usb_packet_skip(p, len);
+ s->data_len -= len;
+ if (s->data_len == 0) {
+ s->mode = USB_MSDM_CSW;
+ }
+ }
}
- if (s->usb_len) {
+ if (p->result < p->iov.size) {
DPRINTF("Deferring packet %p\n", p);
s->packet = p;
ret = USB_RET_ASYNC;
} else {
- ret = len;
+ ret = p->result;
}
break;
default:
- DPRINTF("Unexpected write (len %d)\n", len);
+ DPRINTF("Unexpected write (len %zd)\n", p->iov.size);
goto fail;
}
break;
@@ -434,18 +420,20 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
switch (s->mode) {
case USB_MSDM_DATAOUT:
- if (s->data_len != 0 || len < 13)
+ if (s->data_len != 0 || p->iov.size < 13) {
goto fail;
+ }
/* Waiting for SCSI write to complete. */
s->packet = p;
ret = USB_RET_ASYNC;
break;
case USB_MSDM_CSW:
- DPRINTF("Command status %d tag 0x%x, len %d\n",
- s->result, s->tag, len);
- if (len < 13)
+ DPRINTF("Command status %d tag 0x%x, len %zd\n",
+ s->result, s->tag, p->iov.size);
+ if (p->iov.size < 13) {
goto fail;
+ }
usb_msd_send_status(s, p);
s->mode = USB_MSDM_CBW;
@@ -453,32 +441,32 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
break;
case USB_MSDM_DATAIN:
- DPRINTF("Data in %d/%d, scsi_len %d\n", len, s->data_len, s->scsi_len);
- if (len > s->data_len)
- len = s->data_len;
- s->usb_buf = data;
- s->usb_len = len;
+ DPRINTF("Data in %zd/%d, scsi_len %d\n",
+ p->iov.size, s->data_len, s->scsi_len);
if (s->scsi_len) {
- usb_msd_copy_data(s);
+ usb_msd_copy_data(s, p);
}
- if (s->residue && s->usb_len) {
- s->data_len -= s->usb_len;
- memset(s->usb_buf, 0, s->usb_len);
- if (s->data_len == 0)
- s->mode = USB_MSDM_CSW;
- s->usb_len = 0;
+ if (s->residue) {
+ int len = p->iov.size - p->result;
+ if (len) {
+ usb_packet_skip(p, len);
+ s->data_len -= len;
+ if (s->data_len == 0) {
+ s->mode = USB_MSDM_CSW;
+ }
+ }
}
- if (s->usb_len) {
+ if (p->result < p->iov.size) {
DPRINTF("Deferring packet %p\n", p);
s->packet = p;
ret = USB_RET_ASYNC;
} else {
- ret = len;
+ ret = p->result;
}
break;
default:
- DPRINTF("Unexpected read (len %d)\n", len);
+ DPRINTF("Unexpected read (len %zd)\n", p->iov.size);
goto fail;
}
break;
@@ -623,11 +611,23 @@ static USBDevice *usb_msd_init(const char *filename)
return dev;
}
+static const VMStateDescription vmstate_usb_msd = {
+ .name = "usb-storage",
+ .unmigratable = 1, /* FIXME: handle transactions which are in flight */
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField []) {
+ VMSTATE_USB_DEVICE(dev, MSDState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
static struct USBDeviceInfo msd_info = {
.product_desc = "QEMU USB MSD",
.qdev.name = "usb-storage",
.qdev.fw_name = "storage",
.qdev.size = sizeof(MSDState),
+ .qdev.vmsd = &vmstate_usb_msd,
.usb_desc = &desc,
.init = usb_msd_initfn,
.handle_packet = usb_generic_handle_packet,
diff --git a/hw/usb-musb.c b/hw/usb-musb.c
index 21f35afa..d3ccde9 100644
--- a/hw/usb-musb.c
+++ b/hw/usb-musb.c
@@ -261,17 +261,18 @@
static void musb_attach(USBPort *port);
static void musb_detach(USBPort *port);
-static void musb_schedule_cb(USBDevice *dev, USBPacket *p);
-static void musb_device_destroy(USBBus *bus, USBDevice *dev);
+static void musb_child_detach(USBPort *port, USBDevice *child);
+static void musb_schedule_cb(USBPort *port, USBPacket *p);
+static void musb_async_cancel_device(MUSBState *s, USBDevice *dev);
static USBPortOps musb_port_ops = {
.attach = musb_attach,
.detach = musb_detach,
+ .child_detach = musb_child_detach,
.complete = musb_schedule_cb,
};
static USBBusOps musb_bus_ops = {
- .device_destroy = musb_device_destroy,
};
typedef struct MUSBPacket MUSBPacket;
@@ -364,12 +365,13 @@ struct MUSBState *musb_init(qemu_irq *irqs)
s->ep[i].maxp[1] = 0x40;
s->ep[i].musb = s;
s->ep[i].epnum = i;
+ usb_packet_init(&s->ep[i].packey[0].p);
+ usb_packet_init(&s->ep[i].packey[1].p);
}
usb_bus_new(&s->bus, &musb_bus_ops, NULL /* FIXME */);
usb_register_port(&s->bus, &s->port, s, 0, &musb_port_ops,
USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL);
- usb_port_location(&s->port, NULL, 1);
return s;
}
@@ -498,10 +500,19 @@ static void musb_detach(USBPort *port)
{
MUSBState *s = (MUSBState *) port->opaque;
+ musb_async_cancel_device(s, port->dev);
+
musb_intr_set(s, musb_irq_disconnect, 1);
musb_session_update(s, 1, s->session);
}
+static void musb_child_detach(USBPort *port, USBDevice *child)
+{
+ MUSBState *s = (MUSBState *) port->opaque;
+
+ musb_async_cancel_device(s, child);
+}
+
static void musb_cb_tick0(void *opaque)
{
MUSBEndPoint *ep = (MUSBEndPoint *) opaque;
@@ -518,7 +529,7 @@ static void musb_cb_tick1(void *opaque)
#define musb_cb_tick (dir ? musb_cb_tick1 : musb_cb_tick0)
-static void musb_schedule_cb(USBDevice *dev, USBPacket *packey)
+static void musb_schedule_cb(USBPort *port, USBPacket *packey)
{
MUSBPacket *p = container_of(packey, MUSBPacket, p);
MUSBEndPoint *ep = p->ep;
@@ -596,12 +607,10 @@ static void musb_packet(MUSBState *s, MUSBEndPoint *ep,
ep->interrupt[dir] = ttype == USB_ENDPOINT_XFER_INT;
ep->delayed_cb[dir] = cb;
- ep->packey[dir].p.pid = pid;
/* A wild guess on the FADDR semantics... */
- ep->packey[dir].p.devaddr = ep->faddr[idx];
- ep->packey[dir].p.devep = ep->type[idx] & 0xf;
- ep->packey[dir].p.data = (void *) ep->buf[idx];
- ep->packey[dir].p.len = len;
+ usb_packet_setup(&ep->packey[dir].p, pid, ep->faddr[idx],
+ ep->type[idx] & 0xf);
+ usb_packet_addbuf(&ep->packey[dir].p, ep->buf[idx], len);
ep->packey[dir].ep = ep;
ep->packey[dir].dir = dir;
@@ -616,7 +625,7 @@ static void musb_packet(MUSBState *s, MUSBEndPoint *ep,
}
ep->status[dir] = ret;
- usb_packet_complete(s->port.dev, &ep->packey[dir].p);
+ musb_schedule_cb(&s->port, &ep->packey[dir].p);
}
static void musb_tx_packet_complete(USBPacket *packey, void *opaque)
@@ -729,7 +738,7 @@ static void musb_rx_packet_complete(USBPacket *packey, void *opaque)
if (ep->status[1] == USB_RET_STALL) {
ep->status[1] = 0;
- packey->len = 0;
+ packey->result = 0;
ep->csr[1] |= MGC_M_RXCSR_H_RXSTALL;
if (!epnum)
@@ -743,7 +752,7 @@ static void musb_rx_packet_complete(USBPacket *packey, void *opaque)
* Data-errors in Isochronous. */
if (ep->interrupt[1])
return musb_packet(s, ep, epnum, USB_TOKEN_IN,
- packey->len, musb_rx_packet_complete, 1);
+ packey->iov.size, musb_rx_packet_complete, 1);
ep->csr[1] |= MGC_M_RXCSR_DATAERROR;
if (!epnum)
@@ -768,14 +777,14 @@ static void musb_rx_packet_complete(USBPacket *packey, void *opaque)
/* TODO: check len for over/underruns of an OUT packet? */
/* TODO: perhaps make use of e->ext_size[1] here. */
- packey->len = ep->status[1];
+ packey->result = ep->status[1];
if (!(ep->csr[1] & (MGC_M_RXCSR_H_RXSTALL | MGC_M_RXCSR_DATAERROR))) {
ep->csr[1] |= MGC_M_RXCSR_FIFOFULL | MGC_M_RXCSR_RXPKTRDY;
if (!epnum)
ep->csr[0] |= MGC_M_CSR0_RXPKTRDY;
- ep->rxcount = packey->len; /* XXX: MIN(packey->len, ep->maxp[1]); */
+ ep->rxcount = packey->result; /* XXX: MIN(packey->len, ep->maxp[1]); */
/* In DMA mode: assert DMA request for this EP */
}
@@ -783,9 +792,8 @@ static void musb_rx_packet_complete(USBPacket *packey, void *opaque)
musb_rx_intr_set(s, epnum, 1);
}
-static void musb_device_destroy(USBBus *bus, USBDevice *dev)
+static void musb_async_cancel_device(MUSBState *s, USBDevice *dev)
{
- MUSBState *s = container_of(bus, MUSBState, bus);
int ep, dir;
for (ep = 0; ep < 16; ep++) {
@@ -848,12 +856,12 @@ static void musb_rx_req(MUSBState *s, int epnum)
* 64 bytes of the FIFO, only move the FIFO start and return. (Obsolete) */
if (ep->packey[1].p.pid == USB_TOKEN_IN && ep->status[1] >= 0 &&
(ep->fifostart[1]) + ep->rxcount <
- ep->packey[1].p.len) {
+ ep->packey[1].p.iov.size) {
TRACE("0x%08x, %d", ep->fifostart[1], ep->rxcount );
ep->fifostart[1] += ep->rxcount;
ep->fifolen[1] = 0;
- ep->rxcount = MIN(ep->packey[0].p.len - (ep->fifostart[1]),
+ ep->rxcount = MIN(ep->packey[0].p.iov.size - (ep->fifostart[1]),
ep->maxp[1]);
ep->csr[1] &= ~MGC_M_RXCSR_H_REQPKT;
diff --git a/hw/usb-net.c b/hw/usb-net.c
index 9be709f..0cb47d6 100644
--- a/hw/usb-net.c
+++ b/hw/usb-net.c
@@ -29,6 +29,7 @@
#include "net.h"
#include "qemu-queue.h"
#include "sysemu.h"
+#include "iov.h"
/*#define TRAFFIC_DEBUG*/
/* Thanks to NetChip Technologies for donating this product ID.
@@ -1121,28 +1122,23 @@ static int usb_net_handle_control(USBDevice *dev, USBPacket *p,
static int usb_net_handle_statusin(USBNetState *s, USBPacket *p)
{
+ le32 buf[2];
int ret = 8;
- if (p->len < 8)
+ if (p->iov.size < 8) {
return USB_RET_STALL;
+ }
- ((le32 *) p->data)[0] = cpu_to_le32(1);
- ((le32 *) p->data)[1] = cpu_to_le32(0);
+ buf[0] = cpu_to_le32(1);
+ buf[1] = cpu_to_le32(0);
+ usb_packet_copy(p, buf, 8);
if (!s->rndis_resp.tqh_first)
ret = USB_RET_NAK;
#ifdef TRAFFIC_DEBUG
- fprintf(stderr, "usbnet: interrupt poll len %u return %d", p->len, ret);
- {
- int i;
- fprintf(stderr, ":");
- for (i = 0; i < ret; i++) {
- if (!(i & 15))
- fprintf(stderr, "\n%04x:", i);
- fprintf(stderr, " %02x", p->data[i]);
- }
- fprintf(stderr, "\n\n");
- }
+ fprintf(stderr, "usbnet: interrupt poll len %zu return %d",
+ p->iov.size, ret);
+ iov_hexdump(p->iov.iov, p->iov.niov, stderr, "usbnet", ret);
#endif
return ret;
@@ -1162,9 +1158,10 @@ static int usb_net_handle_datain(USBNetState *s, USBPacket *p)
return ret;
}
ret = s->in_len - s->in_ptr;
- if (ret > p->len)
- ret = p->len;
- memcpy(p->data, &s->in_buf[s->in_ptr], ret);
+ if (ret > p->iov.size) {
+ ret = p->iov.size;
+ }
+ usb_packet_copy(p, &s->in_buf[s->in_ptr], ret);
s->in_ptr += ret;
if (s->in_ptr >= s->in_len &&
(is_rndis(s) || (s->in_len & (64 - 1)) || !ret)) {
@@ -1173,17 +1170,8 @@ static int usb_net_handle_datain(USBNetState *s, USBPacket *p)
}
#ifdef TRAFFIC_DEBUG
- fprintf(stderr, "usbnet: data in len %u return %d", p->len, ret);
- {
- int i;
- fprintf(stderr, ":");
- for (i = 0; i < ret; i++) {
- if (!(i & 15))
- fprintf(stderr, "\n%04x:", i);
- fprintf(stderr, " %02x", p->data[i]);
- }
- fprintf(stderr, "\n\n");
- }
+ fprintf(stderr, "usbnet: data in len %zu return %d", p->iov.size, ret);
+ iov_hexdump(p->iov.iov, p->iov.niov, stderr, "usbnet", ret);
#endif
return ret;
@@ -1191,29 +1179,20 @@ static int usb_net_handle_datain(USBNetState *s, USBPacket *p)
static int usb_net_handle_dataout(USBNetState *s, USBPacket *p)
{
- int ret = p->len;
+ int ret = p->iov.size;
int sz = sizeof(s->out_buf) - s->out_ptr;
struct rndis_packet_msg_type *msg =
(struct rndis_packet_msg_type *) s->out_buf;
uint32_t len;
#ifdef TRAFFIC_DEBUG
- fprintf(stderr, "usbnet: data out len %u\n", p->len);
- {
- int i;
- fprintf(stderr, ":");
- for (i = 0; i < p->len; i++) {
- if (!(i & 15))
- fprintf(stderr, "\n%04x:", i);
- fprintf(stderr, " %02x", p->data[i]);
- }
- fprintf(stderr, "\n\n");
- }
+ fprintf(stderr, "usbnet: data out len %zu\n", p->iov.size);
+ iov_hexdump(p->iov.iov, p->iov.niov, stderr, "usbnet", p->iov.size);
#endif
if (sz > ret)
sz = ret;
- memcpy(&s->out_buf[s->out_ptr], p->data, sz);
+ usb_packet_copy(p, &s->out_buf[s->out_ptr], sz);
s->out_ptr += sz;
if (!is_rndis(s)) {
@@ -1277,8 +1256,8 @@ static int usb_net_handle_data(USBDevice *dev, USBPacket *p)
}
if (ret == USB_RET_STALL)
fprintf(stderr, "usbnet: failed data transaction: "
- "pid 0x%x ep 0x%x len 0x%x\n",
- p->pid, p->devep, p->len);
+ "pid 0x%x ep 0x%x len 0x%zx\n",
+ p->pid, p->devep, p->iov.size);
return ret;
}
@@ -1414,11 +1393,17 @@ static USBDevice *usb_net_init(const char *cmdline)
return dev;
}
+static const VMStateDescription vmstate_usb_net = {
+ .name = "usb-net",
+ .unmigratable = 1,
+};
+
static struct USBDeviceInfo net_info = {
.product_desc = "QEMU USB Network Interface",
.qdev.name = "usb-net",
.qdev.fw_name = "network",
.qdev.size = sizeof(USBNetState),
+ .qdev.vmsd = &vmstate_usb_net,
.usb_desc = &desc_net,
.init = usb_net_initfn,
.handle_packet = usb_generic_handle_packet,
diff --git a/hw/usb-ohci.c b/hw/usb-ohci.c
index 1c29b9f..d39bcb0 100644
--- a/hw/usb-ohci.c
+++ b/hw/usb-ohci.c
@@ -62,7 +62,7 @@ typedef struct OHCIPort {
typedef struct {
USBBus bus;
qemu_irq irq;
- int mem;
+ MemoryRegion mem;
int num_ports;
const char *name;
@@ -124,6 +124,7 @@ struct ohci_hcca {
};
static void ohci_bus_stop(OHCIState *ohci);
+static void ohci_async_cancel_device(OHCIState *ohci, USBDevice *dev);
/* Bitfields for the first word of an Endpoint Desciptor. */
#define OHCI_ED_FA_SHIFT 0
@@ -326,6 +327,7 @@ static void ohci_attach(USBPort *port1)
{
OHCIState *s = port1->opaque;
OHCIPort *port = &s->rhport[port1->index];
+ uint32_t old_state = port->ctrl;
/* set connect status */
port->ctrl |= OHCI_PORT_CCS | OHCI_PORT_CSC;
@@ -343,6 +345,10 @@ static void ohci_attach(USBPort *port1)
}
DPRINTF("usb-ohci: Attached port %d\n", port1->index);
+
+ if (old_state != port->ctrl) {
+ ohci_set_interrupt(s, OHCI_INTR_RHSC);
+ }
}
static void ohci_detach(USBPort *port1)
@@ -351,6 +357,8 @@ static void ohci_detach(USBPort *port1)
OHCIPort *port = &s->rhport[port1->index];
uint32_t old_state = port->ctrl;
+ ohci_async_cancel_device(s, port1->dev);
+
/* set connect status */
if (port->ctrl & OHCI_PORT_CCS) {
port->ctrl &= ~OHCI_PORT_CCS;
@@ -363,19 +371,18 @@ static void ohci_detach(USBPort *port1)
}
DPRINTF("usb-ohci: Detached port %d\n", port1->index);
- if (old_state != port->ctrl)
+ if (old_state != port->ctrl) {
ohci_set_interrupt(s, OHCI_INTR_RHSC);
+ }
}
-static void ohci_wakeup(USBDevice *dev)
+static void ohci_wakeup(USBPort *port1)
{
- USBBus *bus = usb_bus_from_device(dev);
- OHCIState *s = container_of(bus, OHCIState, bus);
- int portnum = dev->port->index;
- OHCIPort *port = &s->rhport[portnum];
+ OHCIState *s = port1->opaque;
+ OHCIPort *port = &s->rhport[port1->index];
uint32_t intr = 0;
if (port->ctrl & OHCI_PORT_PSS) {
- DPRINTF("usb-ohci: port %d: wakeup\n", portnum);
+ DPRINTF("usb-ohci: port %d: wakeup\n", port1->index);
port->ctrl |= OHCI_PORT_PSSC;
port->ctrl &= ~OHCI_PORT_PSS;
intr = OHCI_INTR_RHSC;
@@ -394,6 +401,13 @@ static void ohci_wakeup(USBDevice *dev)
ohci_set_interrupt(s, intr);
}
+static void ohci_child_detach(USBPort *port1, USBDevice *child)
+{
+ OHCIState *s = port1->opaque;
+
+ ohci_async_cancel_device(s, child);
+}
+
/* Reset the controller */
static void ohci_reset(void *opaque)
{
@@ -602,7 +616,7 @@ static void ohci_copy_iso_td(OHCIState *ohci,
static void ohci_process_lists(OHCIState *ohci, int completion);
-static void ohci_async_complete_packet(USBDevice *dev, USBPacket *packet)
+static void ohci_async_complete_packet(USBPort *port, USBPacket *packet)
{
OHCIState *ohci = container_of(packet, OHCIState, usb_packet);
#ifdef DEBUG_PACKET
@@ -763,18 +777,17 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed,
}
if (completion) {
- ret = ohci->usb_packet.len;
+ ret = ohci->usb_packet.result;
} else {
ret = USB_RET_NODEV;
for (i = 0; i < ohci->num_ports; i++) {
dev = ohci->rhport[i].port.dev;
if ((ohci->rhport[i].ctrl & OHCI_PORT_PES) == 0)
continue;
- ohci->usb_packet.pid = pid;
- ohci->usb_packet.devaddr = OHCI_BM(ed->flags, ED_FA);
- ohci->usb_packet.devep = OHCI_BM(ed->flags, ED_EN);
- ohci->usb_packet.data = ohci->usb_buf;
- ohci->usb_packet.len = len;
+ usb_packet_setup(&ohci->usb_packet, pid,
+ OHCI_BM(ed->flags, ED_FA),
+ OHCI_BM(ed->flags, ED_EN));
+ usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, len);
ret = usb_handle_packet(dev, &ohci->usb_packet);
if (ret != USB_RET_NODEV)
break;
@@ -945,7 +958,7 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
}
#endif
if (completion) {
- ret = ohci->usb_packet.len;
+ ret = ohci->usb_packet.result;
ohci->async_td = 0;
ohci->async_complete = 0;
} else {
@@ -966,11 +979,10 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
#endif
return 1;
}
- ohci->usb_packet.pid = pid;
- ohci->usb_packet.devaddr = OHCI_BM(ed->flags, ED_FA);
- ohci->usb_packet.devep = OHCI_BM(ed->flags, ED_EN);
- ohci->usb_packet.data = ohci->usb_buf;
- ohci->usb_packet.len = len;
+ usb_packet_setup(&ohci->usb_packet, pid,
+ OHCI_BM(ed->flags, ED_FA),
+ OHCI_BM(ed->flags, ED_EN));
+ usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, len);
ret = usb_handle_packet(dev, &ohci->usb_packet);
if (ret != USB_RET_NODEV)
break;
@@ -1426,13 +1438,13 @@ static void ohci_port_set_status(OHCIState *ohci, int portnum, uint32_t val)
return;
}
-static uint32_t ohci_mem_read(void *ptr, target_phys_addr_t addr)
+static uint64_t ohci_mem_read(void *opaque,
+ target_phys_addr_t addr,
+ unsigned size)
{
- OHCIState *ohci = ptr;
+ OHCIState *ohci = opaque;
uint32_t retval;
- addr &= 0xff;
-
/* Only aligned reads are allowed on OHCI */
if (addr & 3) {
fprintf(stderr, "usb-ohci: Mis-aligned read\n");
@@ -1549,11 +1561,12 @@ static uint32_t ohci_mem_read(void *ptr, target_phys_addr_t addr)
return retval;
}
-static void ohci_mem_write(void *ptr, target_phys_addr_t addr, uint32_t val)
+static void ohci_mem_write(void *opaque,
+ target_phys_addr_t addr,
+ uint64_t val,
+ unsigned size)
{
- OHCIState *ohci = ptr;
-
- addr &= 0xff;
+ OHCIState *ohci = opaque;
/* Only aligned reads are allowed on OHCI */
if (addr & 3) {
@@ -1675,43 +1688,34 @@ static void ohci_mem_write(void *ptr, target_phys_addr_t addr, uint32_t val)
}
}
-static void ohci_device_destroy(USBBus *bus, USBDevice *dev)
+static void ohci_async_cancel_device(OHCIState *ohci, USBDevice *dev)
{
- OHCIState *ohci = container_of(bus, OHCIState, bus);
-
if (ohci->async_td && ohci->usb_packet.owner == dev) {
usb_cancel_packet(&ohci->usb_packet);
ohci->async_td = 0;
}
}
-/* Only dword reads are defined on OHCI register space */
-static CPUReadMemoryFunc * const ohci_readfn[3]={
- ohci_mem_read,
- ohci_mem_read,
- ohci_mem_read
-};
-
-/* Only dword writes are defined on OHCI register space */
-static CPUWriteMemoryFunc * const ohci_writefn[3]={
- ohci_mem_write,
- ohci_mem_write,
- ohci_mem_write
+static const MemoryRegionOps ohci_mem_ops = {
+ .read = ohci_mem_read,
+ .write = ohci_mem_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
};
static USBPortOps ohci_port_ops = {
.attach = ohci_attach,
.detach = ohci_detach,
+ .child_detach = ohci_child_detach,
.wakeup = ohci_wakeup,
.complete = ohci_async_complete_packet,
};
static USBBusOps ohci_bus_ops = {
- .device_destroy = ohci_device_destroy,
};
-static void usb_ohci_init(OHCIState *ohci, DeviceState *dev,
- int num_ports, uint32_t localmem_base)
+static int usb_ohci_init(OHCIState *ohci, DeviceState *dev,
+ int num_ports, uint32_t localmem_base,
+ char *masterbus, uint32_t firstport)
{
int i;
@@ -1731,43 +1735,62 @@ static void usb_ohci_init(OHCIState *ohci, DeviceState *dev,
usb_frame_time, usb_bit_time);
}
- ohci->mem = cpu_register_io_memory(ohci_readfn, ohci_writefn, ohci,
- DEVICE_LITTLE_ENDIAN);
+ ohci->num_ports = num_ports;
+ if (masterbus) {
+ USBPort *ports[OHCI_MAX_PORTS];
+ for(i = 0; i < num_ports; i++) {
+ ports[i] = &ohci->rhport[i].port;
+ }
+ if (usb_register_companion(masterbus, ports, num_ports,
+ firstport, ohci, &ohci_port_ops,
+ USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL) != 0) {
+ return -1;
+ }
+ } else {
+ usb_bus_new(&ohci->bus, &ohci_bus_ops, dev);
+ for (i = 0; i < num_ports; i++) {
+ usb_register_port(&ohci->bus, &ohci->rhport[i].port,
+ ohci, i, &ohci_port_ops,
+ USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL);
+ }
+ }
+
+ memory_region_init_io(&ohci->mem, &ohci_mem_ops, ohci, "ohci", 256);
ohci->localmem_base = localmem_base;
ohci->name = dev->info->name;
-
- usb_bus_new(&ohci->bus, &ohci_bus_ops, dev);
- ohci->num_ports = num_ports;
- for (i = 0; i < num_ports; i++) {
- usb_register_port(&ohci->bus, &ohci->rhport[i].port, ohci, i, &ohci_port_ops,
- USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL);
- usb_port_location(&ohci->rhport[i].port, NULL, i+1);
- }
+ usb_packet_init(&ohci->usb_packet);
ohci->async_td = 0;
qemu_register_reset(ohci_reset, ohci);
+
+ return 0;
}
typedef struct {
PCIDevice pci_dev;
OHCIState state;
+ char *masterbus;
+ uint32_t num_ports;
+ uint32_t firstport;
} OHCIPCIState;
static int usb_ohci_initfn_pci(struct PCIDevice *dev)
{
OHCIPCIState *ohci = DO_UPCAST(OHCIPCIState, pci_dev, dev);
- int num_ports = 3;
ohci->pci_dev.config[PCI_CLASS_PROG] = 0x10; /* OHCI */
/* TODO: RST# value should be 0. */
ohci->pci_dev.config[PCI_INTERRUPT_PIN] = 0x01; /* interrupt pin 1 */
- usb_ohci_init(&ohci->state, &dev->qdev, num_ports, 0);
+ if (usb_ohci_init(&ohci->state, &dev->qdev, ohci->num_ports, 0,
+ ohci->masterbus, ohci->firstport) != 0) {
+ return -1;
+ }
ohci->state.irq = ohci->pci_dev.irq[0];
/* TODO: avoid cast below by using dev */
- pci_register_bar_simple(&ohci->pci_dev, 0, 256, 0, ohci->state.mem);
+ pci_register_bar_region(&ohci->pci_dev, 0, 0, &ohci->state.mem);
return 0;
}
@@ -1787,9 +1810,10 @@ static int ohci_init_pxa(SysBusDevice *dev)
{
OHCISysBusState *s = FROM_SYSBUS(OHCISysBusState, dev);
- usb_ohci_init(&s->ohci, &dev->qdev, s->num_ports, s->dma_offset);
+ /* Cannot fail as we pass NULL for masterbus */
+ usb_ohci_init(&s->ohci, &dev->qdev, s->num_ports, s->dma_offset, NULL, 0);
sysbus_init_irq(dev, &s->ohci.irq);
- sysbus_init_mmio(dev, 0x1000, s->ohci.mem);
+ sysbus_init_mmio_region(dev, &s->ohci.mem);
return 0;
}
@@ -1802,6 +1826,12 @@ static PCIDeviceInfo ohci_pci_info = {
.vendor_id = PCI_VENDOR_ID_APPLE,
.device_id = PCI_DEVICE_ID_APPLE_IPID_USB,
.class_id = PCI_CLASS_SERIAL_USB,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_STRING("masterbus", OHCIPCIState, masterbus),
+ DEFINE_PROP_UINT32("num-ports", OHCIPCIState, num_ports, 3),
+ DEFINE_PROP_UINT32("firstport", OHCIPCIState, firstport, 0),
+ DEFINE_PROP_END_OF_LIST(),
+ },
};
static SysBusDeviceInfo ohci_sysbus_info = {
diff --git a/hw/usb-serial.c b/hw/usb-serial.c
index 59cb0fb..bf2b775 100644
--- a/hw/usb-serial.c
+++ b/hw/usb-serial.c
@@ -5,7 +5,7 @@
* Copyright (c) 2008 Samuel Thibault <samuel.thibault@ens-lyon.org>
* Written by Paul Brook, reused for FTDI by Samuel Thibault
*
- * This code is licenced under the LGPL.
+ * This code is licensed under the LGPL.
*/
#include "qemu-common.h"
@@ -359,37 +359,42 @@ static int usb_serial_handle_control(USBDevice *dev, USBPacket *p,
static int usb_serial_handle_data(USBDevice *dev, USBPacket *p)
{
USBSerialState *s = (USBSerialState *)dev;
- int ret = 0;
+ int i, ret = 0;
uint8_t devep = p->devep;
- uint8_t *data = p->data;
- int len = p->len;
- int first_len;
+ struct iovec *iov;
+ uint8_t header[2];
+ int first_len, len;
switch (p->pid) {
case USB_TOKEN_OUT:
if (devep != 2)
goto fail;
- qemu_chr_write(s->cs, data, len);
+ for (i = 0; i < p->iov.niov; i++) {
+ iov = p->iov.iov + i;
+ qemu_chr_write(s->cs, iov->iov_base, iov->iov_len);
+ }
break;
case USB_TOKEN_IN:
if (devep != 1)
goto fail;
first_len = RECV_BUF - s->recv_ptr;
+ len = p->iov.size;
if (len <= 2) {
ret = USB_RET_NAK;
break;
}
- *data++ = usb_get_modem_lines(s) | 1;
+ header[0] = usb_get_modem_lines(s) | 1;
/* We do not have the uart details */
/* handle serial break */
if (s->event_trigger && s->event_trigger & FTDI_BI) {
s->event_trigger &= ~FTDI_BI;
- *data = FTDI_BI;
+ header[1] = FTDI_BI;
+ usb_packet_copy(p, header, 2);
ret = 2;
break;
} else {
- *data++ = 0;
+ header[1] = 0;
}
len -= 2;
if (len > s->recv_used)
@@ -400,9 +405,10 @@ static int usb_serial_handle_data(USBDevice *dev, USBPacket *p)
}
if (first_len > len)
first_len = len;
- memcpy(data, s->recv_buf + s->recv_ptr, first_len);
+ usb_packet_copy(p, header, 2);
+ usb_packet_copy(p, s->recv_buf + s->recv_ptr, first_len);
if (len > first_len)
- memcpy(data + first_len, s->recv_buf, len - first_len);
+ usb_packet_copy(p, s->recv_buf, len - first_len);
s->recv_used -= len;
s->recv_ptr = (s->recv_ptr + len) % RECV_BUF;
ret = len + 2;
@@ -566,10 +572,16 @@ static USBDevice *usb_braille_init(const char *unused)
return dev;
}
+static const VMStateDescription vmstate_usb_serial = {
+ .name = "usb-serial",
+ .unmigratable = 1,
+};
+
static struct USBDeviceInfo serial_info = {
.product_desc = "QEMU USB Serial",
.qdev.name = "usb-serial",
.qdev.size = sizeof(USBSerialState),
+ .qdev.vmsd = &vmstate_usb_serial,
.usb_desc = &desc_serial,
.init = usb_serial_initfn,
.handle_packet = usb_generic_handle_packet,
@@ -589,6 +601,7 @@ static struct USBDeviceInfo braille_info = {
.product_desc = "QEMU USB Braille",
.qdev.name = "usb-braille",
.qdev.size = sizeof(USBSerialState),
+ .qdev.vmsd = &vmstate_usb_serial,
.usb_desc = &desc_braille,
.init = usb_serial_initfn,
.handle_packet = usb_generic_handle_packet,
diff --git a/hw/usb-uhci.c b/hw/usb-uhci.c
index 405fa7b..824e3a5 100644
--- a/hw/usb-uhci.c
+++ b/hw/usb-uhci.c
@@ -30,6 +30,8 @@
#include "pci.h"
#include "qemu-timer.h"
#include "usb-uhci.h"
+#include "iov.h"
+#include "dma.h"
//#define DEBUG
//#define DEBUG_DUMP_DATA
@@ -93,17 +95,12 @@ static const char *pid2str(int pid)
#endif
#ifdef DEBUG_DUMP_DATA
-static void dump_data(const uint8_t *data, int len)
+static void dump_data(USBPacket *p, int ret)
{
- int i;
-
- printf("uhci: data: ");
- for(i = 0; i < len; i++)
- printf(" %02x", data[i]);
- printf("\n");
+ iov_hexdump(p->iov.iov, p->iov.niov, stderr, "uhci", ret);
}
#else
-static void dump_data(const uint8_t *data, int len) {}
+static void dump_data(USBPacket *p, int ret) {}
#endif
typedef struct UHCIState UHCIState;
@@ -115,6 +112,7 @@ typedef struct UHCIState UHCIState;
*/
typedef struct UHCIAsync {
USBPacket packet;
+ QEMUSGList sgl;
UHCIState *uhci;
QTAILQ_ENTRY(UHCIAsync) next;
uint32_t td;
@@ -122,7 +120,6 @@ typedef struct UHCIAsync {
int8_t valid;
uint8_t isoc;
uint8_t done;
- uint8_t buffer[2048];
} UHCIAsync;
typedef struct UHCIPort {
@@ -132,7 +129,7 @@ typedef struct UHCIPort {
struct UHCIState {
PCIDevice dev;
- USBBus bus;
+ USBBus bus; /* Note unused when we're a companion controller */
uint16_t cmd; /* cmd register */
uint16_t status;
uint16_t intr; /* interrupt enable register */
@@ -150,6 +147,10 @@ struct UHCIState {
/* Active packets */
QTAILQ_HEAD(,UHCIAsync) async_pending;
uint8_t num_ports_vmstate;
+
+ /* Properties */
+ char *masterbus;
+ uint32_t firstport;
};
typedef struct UHCI_TD {
@@ -175,12 +176,16 @@ static UHCIAsync *uhci_async_alloc(UHCIState *s)
async->token = 0;
async->done = 0;
async->isoc = 0;
+ usb_packet_init(&async->packet);
+ qemu_sglist_init(&async->sgl, 1);
return async;
}
static void uhci_async_free(UHCIState *s, UHCIAsync *async)
{
+ usb_packet_cleanup(&async->packet);
+ qemu_sglist_destroy(&async->sgl);
qemu_free(async);
}
@@ -606,6 +611,8 @@ static void uhci_detach(USBPort *port1)
UHCIState *s = port1->opaque;
UHCIPort *port = &s->ports[port1->index];
+ uhci_async_cancel_device(s, port1->dev);
+
/* set connect status */
if (port->ctrl & UHCI_PORT_CCS) {
port->ctrl &= ~UHCI_PORT_CCS;
@@ -620,11 +627,17 @@ static void uhci_detach(USBPort *port1)
uhci_resume(s);
}
-static void uhci_wakeup(USBDevice *dev)
+static void uhci_child_detach(USBPort *port1, USBDevice *child)
{
- USBBus *bus = usb_bus_from_device(dev);
- UHCIState *s = container_of(bus, UHCIState, bus);
- UHCIPort *port = s->ports + dev->port->index;
+ UHCIState *s = port1->opaque;
+
+ uhci_async_cancel_device(s, child);
+}
+
+static void uhci_wakeup(USBPort *port1)
+{
+ UHCIState *s = port1->opaque;
+ UHCIPort *port = &s->ports[port1->index];
if (port->ctrl & UHCI_PORT_SUSPEND && !(port->ctrl & UHCI_PORT_RD)) {
port->ctrl |= UHCI_PORT_RD;
@@ -636,10 +649,10 @@ static int uhci_broadcast_packet(UHCIState *s, USBPacket *p)
{
int i, ret;
- DPRINTF("uhci: packet enter. pid %s addr 0x%02x ep %d len %d\n",
- pid2str(p->pid), p->devaddr, p->devep, p->len);
+ DPRINTF("uhci: packet enter. pid %s addr 0x%02x ep %d len %zd\n",
+ pid2str(p->pid), p->devaddr, p->devep, p->iov.size);
if (p->pid == USB_TOKEN_OUT || p->pid == USB_TOKEN_SETUP)
- dump_data(p->data, p->len);
+ dump_data(p, 0);
ret = USB_RET_NODEV;
for (i = 0; i < NB_PORTS && ret == USB_RET_NODEV; i++) {
@@ -650,14 +663,14 @@ static int uhci_broadcast_packet(UHCIState *s, USBPacket *p)
ret = usb_handle_packet(dev, p);
}
- DPRINTF("uhci: packet exit. ret %d len %d\n", ret, p->len);
+ DPRINTF("uhci: packet exit. ret %d len %zd\n", ret, p->iov.size);
if (p->pid == USB_TOKEN_IN && ret > 0)
- dump_data(p->data, ret);
+ dump_data(p, ret);
return ret;
}
-static void uhci_async_complete(USBDevice *dev, USBPacket *packet);
+static void uhci_async_complete(USBPort *port, USBPacket *packet);
static void uhci_process_frame(UHCIState *s);
/* return -1 if fatal error (frame must be stopped)
@@ -672,7 +685,7 @@ static int uhci_complete_td(UHCIState *s, UHCI_TD *td, UHCIAsync *async, uint32_
max_len = ((td->token >> 21) + 1) & 0x7ff;
pid = td->token & 0xff;
- ret = async->packet.len;
+ ret = async->packet.result;
if (td->ctrl & TD_CTRL_IOS)
td->ctrl &= ~TD_CTRL_ACTIVE;
@@ -680,7 +693,7 @@ static int uhci_complete_td(UHCIState *s, UHCI_TD *td, UHCIAsync *async, uint32_
if (ret < 0)
goto out;
- len = async->packet.len;
+ len = async->packet.result;
td->ctrl = (td->ctrl & ~0x7ff) | ((len - 1) & 0x7ff);
/* The NAK bit may have been set by a previous frame, so clear it
@@ -696,11 +709,6 @@ static int uhci_complete_td(UHCIState *s, UHCI_TD *td, UHCIAsync *async, uint32_
goto out;
}
- if (len > 0) {
- /* write the data back */
- cpu_physical_memory_write(td->buffer, async->buffer, len);
- }
-
if ((td->ctrl & TD_CTRL_SPD) && len < max_len) {
*int_mask |= 0x02;
/* short packet: do not update QH */
@@ -718,6 +726,9 @@ out:
td->ctrl |= TD_CTRL_STALL;
td->ctrl &= ~TD_CTRL_ACTIVE;
s->status |= UHCI_STS_USBERR;
+ if (td->ctrl & TD_CTRL_IOC) {
+ *int_mask |= 0x01;
+ }
uhci_update_irq(s);
return 1;
@@ -725,6 +736,9 @@ out:
td->ctrl |= TD_CTRL_BABBLE | TD_CTRL_STALL;
td->ctrl &= ~TD_CTRL_ACTIVE;
s->status |= UHCI_STS_USBERR;
+ if (td->ctrl & TD_CTRL_IOC) {
+ *int_mask |= 0x01;
+ }
uhci_update_irq(s);
/* frame interrupted */
return -1;
@@ -809,16 +823,14 @@ static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td, uint32_t *in
max_len = ((td->token >> 21) + 1) & 0x7ff;
pid = td->token & 0xff;
- async->packet.pid = pid;
- async->packet.devaddr = (td->token >> 8) & 0x7f;
- async->packet.devep = (td->token >> 15) & 0xf;
- async->packet.data = async->buffer;
- async->packet.len = max_len;
+ usb_packet_setup(&async->packet, pid, (td->token >> 8) & 0x7f,
+ (td->token >> 15) & 0xf);
+ qemu_sglist_add(&async->sgl, td->buffer, max_len);
+ usb_packet_map(&async->packet, &async->sgl);
switch(pid) {
case USB_TOKEN_OUT:
case USB_TOKEN_SETUP:
- cpu_physical_memory_read(td->buffer, async->buffer, max_len);
len = uhci_broadcast_packet(s, &async->packet);
if (len >= 0)
len = max_len;
@@ -841,15 +853,16 @@ static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td, uint32_t *in
return 2;
}
- async->packet.len = len;
+ async->packet.result = len;
done:
len = uhci_complete_td(s, td, async, int_mask);
+ usb_packet_unmap(&async->packet);
uhci_async_free(s, async);
return len;
}
-static void uhci_async_complete(USBDevice *dev, USBPacket *packet)
+static void uhci_async_complete(USBPort *port, USBPacket *packet)
{
UHCIAsync *async = container_of(packet, UHCIAsync, packet);
UHCIState *s = async->uhci;
@@ -1096,22 +1109,15 @@ static void uhci_map(PCIDevice *pci_dev, int region_num,
register_ioport_read(addr, 32, 1, uhci_ioport_readb, s);
}
-static void uhci_device_destroy(USBBus *bus, USBDevice *dev)
-{
- UHCIState *s = container_of(bus, UHCIState, bus);
-
- uhci_async_cancel_device(s, dev);
-}
-
static USBPortOps uhci_port_ops = {
.attach = uhci_attach,
.detach = uhci_detach,
+ .child_detach = uhci_child_detach,
.wakeup = uhci_wakeup,
.complete = uhci_async_complete,
};
static USBBusOps uhci_bus_ops = {
- .device_destroy = uhci_device_destroy,
};
static int usb_uhci_common_initfn(PCIDevice *dev)
@@ -1125,11 +1131,22 @@ static int usb_uhci_common_initfn(PCIDevice *dev)
pci_conf[PCI_INTERRUPT_PIN] = 4; // interrupt pin 3
pci_conf[USB_SBRN] = USB_RELEASE_1; // release number
- usb_bus_new(&s->bus, &uhci_bus_ops, &s->dev.qdev);
- for(i = 0; i < NB_PORTS; i++) {
- usb_register_port(&s->bus, &s->ports[i].port, s, i, &uhci_port_ops,
- USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL);
- usb_port_location(&s->ports[i].port, NULL, i+1);
+ if (s->masterbus) {
+ USBPort *ports[NB_PORTS];
+ for(i = 0; i < NB_PORTS; i++) {
+ ports[i] = &s->ports[i].port;
+ }
+ if (usb_register_companion(s->masterbus, ports, NB_PORTS,
+ s->firstport, s, &uhci_port_ops,
+ USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL) != 0) {
+ return -1;
+ }
+ } else {
+ usb_bus_new(&s->bus, &uhci_bus_ops, &s->dev.qdev);
+ for (i = 0; i < NB_PORTS; i++) {
+ usb_register_port(&s->bus, &s->ports[i].port, s, i, &uhci_port_ops,
+ USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL);
+ }
}
s->frame_timer = qemu_new_timer_ns(vm_clock, uhci_frame_timer, s);
s->num_ports_vmstate = NB_PORTS;
@@ -1160,6 +1177,12 @@ static int usb_uhci_vt82c686b_initfn(PCIDevice *dev)
return usb_uhci_common_initfn(dev);
}
+static Property uhci_properties[] = {
+ DEFINE_PROP_STRING("masterbus", UHCIState, masterbus),
+ DEFINE_PROP_UINT32("firstport", UHCIState, firstport, 0),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
static PCIDeviceInfo uhci_info[] = {
{
.qdev.name = "piix3-usb-uhci",
@@ -1170,6 +1193,7 @@ static PCIDeviceInfo uhci_info[] = {
.device_id = PCI_DEVICE_ID_INTEL_82371SB_2,
.revision = 0x01,
.class_id = PCI_CLASS_SERIAL_USB,
+ .qdev.props = uhci_properties,
},{
.qdev.name = "piix4-usb-uhci",
.qdev.size = sizeof(UHCIState),
@@ -1179,6 +1203,7 @@ static PCIDeviceInfo uhci_info[] = {
.device_id = PCI_DEVICE_ID_INTEL_82371AB_2,
.revision = 0x01,
.class_id = PCI_CLASS_SERIAL_USB,
+ .qdev.props = uhci_properties,
},{
.qdev.name = "vt82c686b-usb-uhci",
.qdev.size = sizeof(UHCIState),
@@ -1188,6 +1213,37 @@ static PCIDeviceInfo uhci_info[] = {
.device_id = PCI_DEVICE_ID_VIA_UHCI,
.revision = 0x01,
.class_id = PCI_CLASS_SERIAL_USB,
+ .qdev.props = uhci_properties,
+ },{
+ .qdev.name = "ich9-usb-uhci1",
+ .qdev.size = sizeof(UHCIState),
+ .qdev.vmsd = &vmstate_uhci,
+ .init = usb_uhci_common_initfn,
+ .vendor_id = PCI_VENDOR_ID_INTEL,
+ .device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI1,
+ .revision = 0x03,
+ .class_id = PCI_CLASS_SERIAL_USB,
+ .qdev.props = uhci_properties,
+ },{
+ .qdev.name = "ich9-usb-uhci2",
+ .qdev.size = sizeof(UHCIState),
+ .qdev.vmsd = &vmstate_uhci,
+ .init = usb_uhci_common_initfn,
+ .vendor_id = PCI_VENDOR_ID_INTEL,
+ .device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI2,
+ .revision = 0x03,
+ .class_id = PCI_CLASS_SERIAL_USB,
+ .qdev.props = uhci_properties,
+ },{
+ .qdev.name = "ich9-usb-uhci3",
+ .qdev.size = sizeof(UHCIState),
+ .qdev.vmsd = &vmstate_uhci,
+ .init = usb_uhci_common_initfn,
+ .vendor_id = PCI_VENDOR_ID_INTEL,
+ .device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI3,
+ .revision = 0x03,
+ .class_id = PCI_CLASS_SERIAL_USB,
+ .qdev.props = uhci_properties,
},{
/* end of list */
}
diff --git a/hw/usb-wacom.c b/hw/usb-wacom.c
index 9d348e1..2558006 100644
--- a/hw/usb-wacom.c
+++ b/hw/usb-wacom.c
@@ -308,6 +308,7 @@ static int usb_wacom_handle_control(USBDevice *dev, USBPacket *p,
static int usb_wacom_handle_data(USBDevice *dev, USBPacket *p)
{
USBWacomState *s = (USBWacomState *) dev;
+ uint8_t buf[p->iov.size];
int ret = 0;
switch (p->pid) {
@@ -317,9 +318,10 @@ static int usb_wacom_handle_data(USBDevice *dev, USBPacket *p)
return USB_RET_NAK;
s->changed = 0;
if (s->mode == WACOM_MODE_HID)
- ret = usb_mouse_poll(s, p->data, p->len);
+ ret = usb_mouse_poll(s, buf, p->iov.size);
else if (s->mode == WACOM_MODE_WACOM)
- ret = usb_wacom_poll(s, p->data, p->len);
+ ret = usb_wacom_poll(s, buf, p->iov.size);
+ usb_packet_copy(p, buf, ret);
break;
}
/* Fall through. */
@@ -349,6 +351,11 @@ static int usb_wacom_initfn(USBDevice *dev)
return 0;
}
+static const VMStateDescription vmstate_usb_wacom = {
+ .name = "usb-wacom",
+ .unmigratable = 1,
+};
+
static struct USBDeviceInfo wacom_info = {
.product_desc = "QEMU PenPartner Tablet",
.qdev.name = "usb-wacom-tablet",
@@ -356,6 +363,7 @@ static struct USBDeviceInfo wacom_info = {
.usbdevice_name = "wacom-tablet",
.usb_desc = &desc_wacom,
.qdev.size = sizeof(USBWacomState),
+ .qdev.vmsd = &vmstate_usb_wacom,
.init = usb_wacom_initfn,
.handle_packet = usb_generic_handle_packet,
.handle_reset = usb_wacom_handle_reset,
diff --git a/hw/usb.c b/hw/usb.c
index 4a39cbc..685e775 100644
--- a/hw/usb.c
+++ b/hw/usb.c
@@ -25,6 +25,7 @@
*/
#include "qemu-common.h"
#include "usb.h"
+#include "iov.h"
void usb_attach(USBPort *port, USBDevice *dev)
{
@@ -40,19 +41,18 @@ void usb_attach(USBPort *port, USBDevice *dev)
} else {
/* detach */
dev = port->dev;
+ assert(dev);
port->ops->detach(port);
- if (dev) {
- usb_send_msg(dev, USB_MSG_DETACH);
- dev->port = NULL;
- port->dev = NULL;
- }
+ usb_send_msg(dev, USB_MSG_DETACH);
+ dev->port = NULL;
+ port->dev = NULL;
}
}
void usb_wakeup(USBDevice *dev)
{
if (dev->remote_wakeup && dev->port && dev->port->ops->wakeup) {
- dev->port->ops->wakeup(dev);
+ dev->port->ops->wakeup(dev->port);
}
}
@@ -73,10 +73,11 @@ static int do_token_setup(USBDevice *s, USBPacket *p)
int request, value, index;
int ret = 0;
- if (p->len != 8)
+ if (p->iov.size != 8) {
return USB_RET_STALL;
-
- memcpy(s->setup_buf, p->data, 8);
+ }
+
+ usb_packet_copy(p, s->setup_buf, p->iov.size);
s->setup_len = (s->setup_buf[7] << 8) | s->setup_buf[6];
s->setup_index = 0;
@@ -145,9 +146,10 @@ static int do_token_in(USBDevice *s, USBPacket *p)
case SETUP_STATE_DATA:
if (s->setup_buf[0] & USB_DIR_IN) {
int len = s->setup_len - s->setup_index;
- if (len > p->len)
- len = p->len;
- memcpy(p->data, s->data_buf + s->setup_index, len);
+ if (len > p->iov.size) {
+ len = p->iov.size;
+ }
+ usb_packet_copy(p, s->data_buf + s->setup_index, len);
s->setup_index += len;
if (s->setup_index >= s->setup_len)
s->setup_state = SETUP_STATE_ACK;
@@ -180,9 +182,10 @@ static int do_token_out(USBDevice *s, USBPacket *p)
case SETUP_STATE_DATA:
if (!(s->setup_buf[0] & USB_DIR_IN)) {
int len = s->setup_len - s->setup_index;
- if (len > p->len)
- len = p->len;
- memcpy(s->data_buf + s->setup_index, p->data, len);
+ if (len > p->iov.size) {
+ len = p->iov.size;
+ }
+ usb_packet_copy(p, s->data_buf + s->setup_index, len);
s->setup_index += len;
if (s->setup_index >= s->setup_len)
s->setup_state = SETUP_STATE_ACK;
@@ -252,22 +255,22 @@ int usb_generic_handle_packet(USBDevice *s, USBPacket *p)
usb_packet_complete to complete their async control packets. */
void usb_generic_async_ctrl_complete(USBDevice *s, USBPacket *p)
{
- if (p->len < 0) {
+ if (p->result < 0) {
s->setup_state = SETUP_STATE_IDLE;
}
switch (s->setup_state) {
case SETUP_STATE_SETUP:
- if (p->len < s->setup_len) {
- s->setup_len = p->len;
+ if (p->result < s->setup_len) {
+ s->setup_len = p->result;
}
s->setup_state = SETUP_STATE_DATA;
- p->len = 8;
+ p->result = 8;
break;
case SETUP_STATE_ACK:
s->setup_state = SETUP_STATE_IDLE;
- p->len = 0;
+ p->result = 0;
break;
default:
@@ -335,7 +338,7 @@ void usb_packet_complete(USBDevice *dev, USBPacket *p)
{
/* Note: p->owner != dev is possible in case dev is a hub */
assert(p->owner != NULL);
- dev->port->ops->complete(dev, p);
+ dev->port->ops->complete(dev->port, p);
p->owner = NULL;
}
@@ -348,3 +351,57 @@ void usb_cancel_packet(USBPacket * p)
p->owner->info->cancel_packet(p->owner, p);
p->owner = NULL;
}
+
+
+void usb_packet_init(USBPacket *p)
+{
+ qemu_iovec_init(&p->iov, 1);
+}
+
+void usb_packet_setup(USBPacket *p, int pid, uint8_t addr, uint8_t ep)
+{
+ p->pid = pid;
+ p->devaddr = addr;
+ p->devep = ep;
+ p->result = 0;
+ qemu_iovec_reset(&p->iov);
+}
+
+void usb_packet_addbuf(USBPacket *p, void *ptr, size_t len)
+{
+ qemu_iovec_add(&p->iov, ptr, len);
+}
+
+void usb_packet_copy(USBPacket *p, void *ptr, size_t bytes)
+{
+ assert(p->result >= 0);
+ assert(p->result + bytes <= p->iov.size);
+ switch (p->pid) {
+ case USB_TOKEN_SETUP:
+ case USB_TOKEN_OUT:
+ iov_to_buf(p->iov.iov, p->iov.niov, ptr, p->result, bytes);
+ break;
+ case USB_TOKEN_IN:
+ iov_from_buf(p->iov.iov, p->iov.niov, ptr, p->result, bytes);
+ break;
+ default:
+ fprintf(stderr, "%s: invalid pid: %x\n", __func__, p->pid);
+ abort();
+ }
+ p->result += bytes;
+}
+
+void usb_packet_skip(USBPacket *p, size_t bytes)
+{
+ assert(p->result >= 0);
+ assert(p->result + bytes <= p->iov.size);
+ if (p->pid == USB_TOKEN_IN) {
+ iov_clear(p->iov.iov, p->iov.niov, p->result, bytes);
+ }
+ p->result += bytes;
+}
+
+void usb_packet_cleanup(USBPacket *p)
+{
+ qemu_iovec_destroy(&p->iov);
+}
diff --git a/hw/usb.h b/hw/usb.h
index 076e2ff..84d04df 100644
--- a/hw/usb.h
+++ b/hw/usb.h
@@ -252,8 +252,18 @@ struct USBDeviceInfo {
typedef struct USBPortOps {
void (*attach)(USBPort *port);
void (*detach)(USBPort *port);
- void (*wakeup)(USBDevice *dev);
- void (*complete)(USBDevice *dev, USBPacket *p);
+ /*
+ * This gets called when a device downstream from the device attached to
+ * the port (iow attached through a hub) gets detached.
+ */
+ void (*child_detach)(USBPort *port, USBDevice *child);
+ void (*wakeup)(USBPort *port);
+ /*
+ * Note that port->dev will be different then the device from which
+ * the packet originated when a hub is involved, if you want the orginating
+ * device use p->owner
+ */
+ void (*complete)(USBPort *port, USBPacket *p);
} USBPortOps;
/* USB port on which a device can be connected */
@@ -275,12 +285,21 @@ struct USBPacket {
int pid;
uint8_t devaddr;
uint8_t devep;
- uint8_t *data;
- int len;
+ QEMUIOVector iov;
+ int result; /* transfer length or USB_RET_* status code */
/* Internal use by the USB layer. */
USBDevice *owner;
};
+void usb_packet_init(USBPacket *p);
+void usb_packet_setup(USBPacket *p, int pid, uint8_t addr, uint8_t ep);
+void usb_packet_addbuf(USBPacket *p, void *ptr, size_t len);
+int usb_packet_map(USBPacket *p, QEMUSGList *sgl);
+void usb_packet_unmap(USBPacket *p);
+void usb_packet_copy(USBPacket *p, void *ptr, size_t bytes);
+void usb_packet_skip(USBPacket *p, size_t bytes);
+void usb_packet_cleanup(USBPacket *p);
+
int usb_handle_packet(USBDevice *dev, USBPacket *p);
void usb_packet_complete(USBDevice *dev, USBPacket *p);
void usb_cancel_packet(USBPacket * p);
@@ -344,7 +363,8 @@ struct USBBus {
};
struct USBBusOps {
- void (*device_destroy)(USBBus *bus, USBDevice *dev);
+ int (*register_companion)(USBBus *bus, USBPort *ports[],
+ uint32_t portcount, uint32_t firstport);
};
void usb_bus_new(USBBus *bus, USBBusOps *ops, DeviceState *host);
@@ -356,6 +376,9 @@ USBDevice *usb_create_simple(USBBus *bus, const char *name);
USBDevice *usbdevice_create(const char *cmdline);
void usb_register_port(USBBus *bus, USBPort *port, void *opaque, int index,
USBPortOps *ops, int speedmask);
+int usb_register_companion(const char *masterbus, USBPort *ports[],
+ uint32_t portcount, uint32_t firstport,
+ void *opaque, USBPortOps *ops, int speedmask);
void usb_port_location(USBPort *downstream, USBPort *upstream, int portnr);
void usb_unregister_port(USBBus *bus, USBPort *port);
int usb_device_attach(USBDevice *dev);
diff --git a/hw/versatile_pci.c b/hw/versatile_pci.c
index 8e75ffc..cffe387 100644
--- a/hw/versatile_pci.c
+++ b/hw/versatile_pci.c
@@ -4,12 +4,13 @@
* Copyright (c) 2006-2009 CodeSourcery.
* Written by Paul Brook
*
- * This code is licenced under the LGPL.
+ * This code is licensed under the LGPL.
*/
#include "sysbus.h"
#include "pci.h"
#include "pci_host.h"
+#include "exec-memory.h"
typedef struct {
SysBusDevice busdev;
@@ -111,6 +112,7 @@ static int pci_vpb_init(SysBusDevice *dev)
}
bus = pci_register_bus(&dev->qdev, "pci",
pci_vpb_set_irq, pci_vpb_map_irq, s->irq,
+ get_system_memory(),
PCI_DEVFN(11, 0), 4);
/* ??? Register memory space. */
diff --git a/hw/versatilepb.c b/hw/versatilepb.c
index 46b6a3f..147fe29 100644
--- a/hw/versatilepb.c
+++ b/hw/versatilepb.c
@@ -4,7 +4,7 @@
* Copyright (c) 2005-2007 CodeSourcery.
* Written by Paul Brook
*
- * This code is licenced under the GPL.
+ * This code is licensed under the GPL.
*/
#include "sysbus.h"
diff --git a/hw/vga_int.h b/hw/vga_int.h
index d2811bd..eee91a8 100644
--- a/hw/vga_int.h
+++ b/hw/vga_int.h
@@ -106,13 +106,13 @@ typedef void (* vga_update_retrace_info_fn)(struct VGACommonState *s);
typedef struct VGACommonState {
uint8_t *vram_ptr;
ram_addr_t vram_offset;
+ target_phys_addr_t lfb_addr;
+ target_phys_addr_t lfb_end;
+ target_phys_addr_t map_addr;
+ target_phys_addr_t map_end;
uint32_t vram_size;
- uint32_t lfb_addr;
- uint32_t lfb_end;
- uint32_t map_addr;
- uint32_t map_end;
- uint32_t lfb_vram_mapped; /* whether 0xa0000 is mapped as ram */
uint32_t latch;
+ uint32_t lfb_vram_mapped; /* whether 0xa0000 is mapped as ram */
uint8_t sr_index;
uint8_t sr[256];
uint8_t gr_index;
diff --git a/hw/virtio-balloon.c b/hw/virtio-balloon.c
index 70a8710..072a88a 100644
--- a/hw/virtio-balloon.c
+++ b/hw/virtio-balloon.c
@@ -1,7 +1,9 @@
/*
- * Virtio Block Device
+ * Virtio Balloon Device
*
* Copyright IBM, Corp. 2008
+ * Copyright (C) 2011 Red Hat, Inc.
+ * Copyright (C) 2011 Amit Shah <amit.shah@redhat.com>
*
* Authors:
* Anthony Liguori <aliguori@us.ibm.com>
@@ -43,6 +45,7 @@ typedef struct VirtIOBalloon
size_t stats_vq_offset;
MonitorCompletion *stats_callback;
void *stats_opaque_callback_data;
+ DeviceState *qdev;
} VirtIOBalloon;
static VirtIOBalloon *to_virtio_balloon(VirtIODevice *vdev)
@@ -199,36 +202,44 @@ static uint32_t virtio_balloon_get_features(VirtIODevice *vdev, uint32_t f)
return f;
}
-static void virtio_balloon_to_target(void *opaque, ram_addr_t target,
- MonitorCompletion cb, void *cb_data)
+static void virtio_balloon_stat(void *opaque, MonitorCompletion cb,
+ void *cb_data)
{
VirtIOBalloon *dev = opaque;
- if (target > ram_size)
- target = ram_size;
+ /* For now, only allow one request at a time. This restriction can be
+ * removed later by queueing callback and data pairs.
+ */
+ if (dev->stats_callback != NULL) {
+ return;
+ }
+ dev->stats_callback = cb;
+ dev->stats_opaque_callback_data = cb_data;
+
+ 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);
+ return;
+ }
+
+ /* Stats are not supported. Clear out any stale values that might
+ * have been set by a more featureful guest kernel.
+ */
+ reset_stats(dev);
+ complete_stats_request(dev);
+}
+static void virtio_balloon_to_target(void *opaque, ram_addr_t target)
+{
+ VirtIOBalloon *dev = opaque;
+
+ if (target > ram_size) {
+ target = ram_size;
+ }
if (target) {
dev->num_pages = (ram_size - target) >> VIRTIO_BALLOON_PFN_SHIFT;
virtio_notify_config(&dev->vdev);
- } else {
- /* For now, only allow one request at a time. This restriction can be
- * removed later by queueing callback and data pairs.
- */
- if (dev->stats_callback != NULL) {
- return;
- }
- dev->stats_callback = cb;
- dev->stats_opaque_callback_data = cb_data;
- 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 {
- /* Stats are not supported. Clear out any stale values that might
- * have been set by a more featureful guest kernel.
- */
- reset_stats(dev);
- complete_stats_request(dev);
- }
}
}
@@ -259,6 +270,7 @@ static int virtio_balloon_load(QEMUFile *f, void *opaque, int version_id)
VirtIODevice *virtio_balloon_init(DeviceState *dev)
{
VirtIOBalloon *s;
+ int ret;
s = (VirtIOBalloon *)virtio_common_init("virtio-balloon",
VIRTIO_ID_BALLOON,
@@ -268,15 +280,29 @@ VirtIODevice *virtio_balloon_init(DeviceState *dev)
s->vdev.set_config = virtio_balloon_set_config;
s->vdev.get_features = virtio_balloon_get_features;
+ ret = qemu_add_balloon_handler(virtio_balloon_to_target,
+ virtio_balloon_stat, s);
+ if (ret < 0) {
+ virtio_cleanup(&s->vdev);
+ return NULL;
+ }
+
s->ivq = virtio_add_queue(&s->vdev, 128, virtio_balloon_handle_output);
s->dvq = virtio_add_queue(&s->vdev, 128, virtio_balloon_handle_output);
s->svq = virtio_add_queue(&s->vdev, 128, virtio_balloon_receive_stats);
reset_stats(s);
- qemu_add_balloon_handler(virtio_balloon_to_target, s);
+ s->qdev = dev;
register_savevm(dev, "virtio-balloon", -1, 1,
virtio_balloon_save, virtio_balloon_load, s);
return &s->vdev;
}
+
+void virtio_balloon_exit(VirtIODevice *vdev)
+{
+ VirtIOBalloon *s = DO_UPCAST(VirtIOBalloon, vdev, vdev);
+ unregister_savevm(s->qdev, "virtio-balloon", s);
+ virtio_cleanup(vdev);
+}
diff --git a/hw/virtio-console.c b/hw/virtio-console.c
index b076331..fe5e188 100644
--- a/hw/virtio-console.c
+++ b/hw/virtio-console.c
@@ -12,6 +12,7 @@
#include "qemu-char.h"
#include "qemu-error.h"
+#include "trace.h"
#include "virtio-serial.h"
typedef struct VirtConsole {
@@ -24,8 +25,26 @@ typedef struct VirtConsole {
static ssize_t flush_buf(VirtIOSerialPort *port, const uint8_t *buf, size_t len)
{
VirtConsole *vcon = DO_UPCAST(VirtConsole, port, port);
-
- return qemu_chr_write(vcon->chr, buf, len);
+ ssize_t ret;
+
+ ret = qemu_chr_write(vcon->chr, buf, len);
+ trace_virtio_console_flush_buf(port->id, len, ret);
+
+ if (ret < 0) {
+ /*
+ * Ideally we'd get a better error code than just -1, but
+ * that's what the chardev interface gives us right now. If
+ * we had a finer-grained message, like -EPIPE, we could close
+ * this connection. Absent such error messages, the most we
+ * can do is to return 0 here.
+ *
+ * This will prevent stray -1 values to go to
+ * virtio-serial-bus.c and cause abort()s in
+ * do_flush_queued_data().
+ */
+ ret = 0;
+ }
+ return ret;
}
/* Callback function that's called when the guest opens the port */
@@ -57,6 +76,7 @@ static void chr_read(void *opaque, const uint8_t *buf, int size)
{
VirtConsole *vcon = opaque;
+ trace_virtio_console_chr_read(vcon->port.id, size);
virtio_serial_write(&vcon->port, buf, size);
}
@@ -64,6 +84,7 @@ static void chr_event(void *opaque, int event)
{
VirtConsole *vcon = opaque;
+ trace_virtio_console_chr_event(vcon->port.id, event);
switch (event) {
case CHR_EVENT_OPENED:
virtio_serial_open(&vcon->port);
@@ -118,9 +139,7 @@ static VirtIOSerialPortInfo virtconsole_info = {
.init = virtconsole_initfn,
.exit = virtconsole_exitfn,
.qdev.props = (Property[]) {
- DEFINE_PROP_UINT32("nr", VirtConsole, port.id, VIRTIO_CONSOLE_BAD_ID),
DEFINE_PROP_CHR("chardev", VirtConsole, chr),
- DEFINE_PROP_STRING("name", VirtConsole, port.name),
DEFINE_PROP_END_OF_LIST(),
},
};
@@ -137,9 +156,7 @@ static VirtIOSerialPortInfo virtserialport_info = {
.init = virtconsole_initfn,
.exit = virtconsole_exitfn,
.qdev.props = (Property[]) {
- DEFINE_PROP_UINT32("nr", VirtConsole, port.id, VIRTIO_CONSOLE_BAD_ID),
DEFINE_PROP_CHR("chardev", VirtConsole, chr),
- DEFINE_PROP_STRING("name", VirtConsole, port.name),
DEFINE_PROP_END_OF_LIST(),
},
};
diff --git a/hw/virtio-net.c b/hw/virtio-net.c
index 09c665b..3f10391 100644
--- a/hw/virtio-net.c
+++ b/hw/virtio-net.c
@@ -657,7 +657,7 @@ static ssize_t virtio_net_receive(VLANClientState *nc, const uint8_t *buf, size_
/* copy in packet. ugh */
len = iov_from_buf(sg, elem.in_num,
- buf + offset, size - offset);
+ buf + offset, 0, size - offset);
total += len;
offset += len;
/* If buffers can't be merged, at this point we
diff --git a/hw/virtio-pci.c b/hw/virtio-pci.c
index 4f770fe..f3b3293 100644
--- a/hw/virtio-pci.c
+++ b/hw/virtio-pci.c
@@ -788,10 +788,22 @@ static int virtio_balloon_init_pci(PCIDevice *pci_dev)
VirtIODevice *vdev;
vdev = virtio_balloon_init(&pci_dev->qdev);
+ if (!vdev) {
+ return -1;
+ }
virtio_init_pci(proxy, vdev);
return 0;
}
+static int virtio_balloon_exit_pci(PCIDevice *pci_dev)
+{
+ VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev);
+
+ virtio_pci_stop_ioeventfd(proxy);
+ virtio_balloon_exit(proxy->vdev);
+ return virtio_exit_pci(pci_dev);
+}
+
static PCIDeviceInfo virtio_info[] = {
{
.qdev.name = "virtio-blk-pci",
@@ -866,7 +878,7 @@ static PCIDeviceInfo virtio_info[] = {
.qdev.alias = "virtio-balloon",
.qdev.size = sizeof(VirtIOPCIProxy),
.init = virtio_balloon_init_pci,
- .exit = virtio_exit_pci,
+ .exit = virtio_balloon_exit_pci,
.vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET,
.device_id = PCI_DEVICE_ID_VIRTIO_BALLOON,
.revision = VIRTIO_PCI_ABI_VERSION,
diff --git a/hw/virtio-serial-bus.c b/hw/virtio-serial-bus.c
index 7f6db7b..c5eb931 100644
--- a/hw/virtio-serial-bus.c
+++ b/hw/virtio-serial-bus.c
@@ -19,6 +19,7 @@
#include "monitor.h"
#include "qemu-queue.h"
#include "sysbus.h"
+#include "trace.h"
#include "virtio-serial.h"
/* The virtio-serial bus on top of which the ports will ride as devices */
@@ -103,7 +104,7 @@ static size_t write_to_port(VirtIOSerialPort *port,
}
len = iov_from_buf(elem.in_sg, elem.in_num,
- buf + offset, size - offset);
+ buf + offset, 0, size - offset);
offset += len;
virtqueue_push(vq, &elem, len);
@@ -221,6 +222,7 @@ static size_t send_control_event(VirtIOSerialPort *port, uint16_t event,
stw_p(&cpkt.event, event);
stw_p(&cpkt.value, value);
+ trace_virtio_serial_send_control_event(port->id, event, value);
return send_control_msg(port, &cpkt, sizeof(cpkt));
}
@@ -302,6 +304,7 @@ void virtio_serial_throttle_port(VirtIOSerialPort *port, bool throttle)
return;
}
+ trace_virtio_serial_throttle_port(port->id, throttle);
port->throttled = throttle;
if (throttle) {
return;
@@ -328,6 +331,8 @@ static void handle_control_message(VirtIOSerial *vser, void *buf, size_t len)
cpkt.event = lduw_p(&gcpkt->event);
cpkt.value = lduw_p(&gcpkt->value);
+ trace_virtio_serial_handle_control_message(cpkt.event, cpkt.value);
+
if (cpkt.event == VIRTIO_CONSOLE_DEVICE_READY) {
if (!cpkt.value) {
error_report("virtio-serial-bus: Guest failure in adding device %s",
@@ -346,11 +351,13 @@ static void handle_control_message(VirtIOSerial *vser, void *buf, size_t len)
port = find_port_by_id(vser, ldl_p(&gcpkt->id));
if (!port) {
- error_report("virtio-serial-bus: Unexpected port id %u for device %s\n",
+ error_report("virtio-serial-bus: Unexpected port id %u for device %s",
ldl_p(&gcpkt->id), vser->bus.qbus.name);
return;
}
+ trace_virtio_serial_handle_control_message_port(port->id);
+
info = DO_UPCAST(VirtIOSerialPortInfo, qdev, port->dev.info);
switch(cpkt.event) {
@@ -661,20 +668,22 @@ static struct BusInfo virtser_bus_info = {
.name = "virtio-serial-bus",
.size = sizeof(VirtIOSerialBus),
.print_dev = virtser_bus_dev_print,
+ .props = (Property[]) {
+ DEFINE_PROP_UINT32("nr", VirtIOSerialPort, id, VIRTIO_CONSOLE_BAD_ID),
+ DEFINE_PROP_STRING("name", VirtIOSerialPort, name),
+ DEFINE_PROP_END_OF_LIST()
+ }
};
static void virtser_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent)
{
VirtIOSerialPort *port = DO_UPCAST(VirtIOSerialPort, dev, qdev);
- monitor_printf(mon, "%*s dev-prop-int: id: %u\n",
- indent, "", port->id);
- monitor_printf(mon, "%*s dev-prop-int: guest_connected: %d\n",
- indent, "", port->guest_connected);
- monitor_printf(mon, "%*s dev-prop-int: host_connected: %d\n",
- indent, "", port->host_connected);
- monitor_printf(mon, "%*s dev-prop-int: throttled: %d\n",
- indent, "", port->throttled);
+ monitor_printf(mon, "%*sport %d, guest %s, host %s, throttle %s\n",
+ indent, "", port->id,
+ port->guest_connected ? "on" : "off",
+ port->host_connected ? "on" : "off",
+ port->throttled ? "on" : "off");
}
/* This function is only used if a port id is not provided by the user */
diff --git a/hw/virtio.h b/hw/virtio.h
index 0fd0bb0..c129264 100644
--- a/hw/virtio.h
+++ b/hw/virtio.h
@@ -213,6 +213,7 @@ 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);
+void virtio_balloon_exit(VirtIODevice *vdev);
#define DEFINE_VIRTIO_COMMON_FEATURES(_state, _field) \
DEFINE_PROP_BIT("indirect_desc", _state, _field, \
diff --git a/hw/xen.h b/hw/xen.h
index d435ca0..2162111 100644
--- a/hw/xen.h
+++ b/hw/xen.h
@@ -24,22 +24,13 @@ extern int xen_allowed;
static inline int xen_enabled(void)
{
-#ifdef CONFIG_XEN
+#if defined(CONFIG_XEN_BACKEND) && !defined(CONFIG_NO_XEN)
return xen_allowed;
#else
return 0;
#endif
}
-static inline int xen_mapcache_enabled(void)
-{
-#ifdef CONFIG_XEN_MAPCACHE
- return xen_enabled();
-#else
- return 0;
-#endif
-}
-
int xen_pci_slot_get_pirq(PCIDevice *pci_dev, int irq_num);
void xen_piix3_set_irq(void *opaque, int irq_num, int level);
void xen_piix_pci_write_config_client(uint32_t address, uint32_t val, int len);
@@ -50,6 +41,7 @@ qemu_irq *xen_interrupt_controller_init(void);
int xen_init(void);
int xen_hvm_init(void);
void xen_vcpu_init(void);
+void xenstore_store_pv_console_info(int i, struct CharDriverState *chr);
#if defined(NEED_CPU_H) && !defined(CONFIG_USER_ONLY)
void xen_ram_alloc(ram_addr_t ram_addr, ram_addr_t size);
diff --git a/hw/xen_common.h b/hw/xen_common.h
index 2c79af6..0409ac7 100644
--- a/hw/xen_common.h
+++ b/hw/xen_common.h
@@ -85,6 +85,18 @@ static inline int xc_domain_add_to_physmap(int xc_handle, uint32_t domid,
return xc_memory_op(xc_handle, XENMEM_add_to_physmap, &xatp);
}
+static inline struct xs_handle *xs_open(unsigned long flags)
+{
+ return xs_daemon_open();
+}
+
+static inline void xs_close(struct xs_handle *xsh)
+{
+ if (xsh != NULL) {
+ xs_daemon_close(xsh);
+ }
+}
+
/* Xen 4.1 */
#else
diff --git a/hw/xen_console.c b/hw/xen_console.c
index c6c8163..8ef104c 100644
--- a/hw/xen_console.c
+++ b/hw/xen_console.c
@@ -179,7 +179,9 @@ static void xencons_send(struct XenConsole *con)
static int con_init(struct XenDevice *xendev)
{
struct XenConsole *con = container_of(xendev, struct XenConsole, xendev);
- char *type, *dom;
+ char *type, *dom, label[32];
+ int ret = 0;
+ const char *output;
/* setup */
dom = xs_get_domain_path(xenstore, con->xendev.dom);
@@ -189,16 +191,25 @@ static int con_init(struct XenDevice *xendev)
type = xenstore_read_str(con->console, "type");
if (!type || strcmp(type, "ioemu") != 0) {
xen_be_printf(xendev, 1, "not for me (type=%s)\n", type);
- return -1;
+ ret = -1;
+ goto out;
}
- if (!serial_hds[con->xendev.dev])
- xen_be_printf(xendev, 1, "WARNING: serial line %d not configured\n",
- con->xendev.dev);
- else
+ output = xenstore_read_str(con->console, "output");
+
+ /* no Xen override, use qemu output device */
+ if (output == NULL) {
con->chr = serial_hds[con->xendev.dev];
+ } else {
+ snprintf(label, sizeof(label), "xencons%d", con->xendev.dev);
+ con->chr = qemu_chr_open(label, output, NULL);
+ }
- return 0;
+ xenstore_store_pv_console_info(con->xendev.dev, con->chr);
+
+out:
+ qemu_free(type);
+ return ret;
}
static int con_connect(struct XenDevice *xendev)
diff --git a/hw/xen_devconfig.c b/hw/xen_devconfig.c
index 3a92155..6926c54 100644
--- a/hw/xen_devconfig.c
+++ b/hw/xen_devconfig.c
@@ -126,8 +126,8 @@ int xen_config_dev_nic(NICInfo *nic)
char mac[20];
snprintf(mac, sizeof(mac), "%02x:%02x:%02x:%02x:%02x:%02x",
- nic->macaddr[0], nic->macaddr[1], nic->macaddr[2],
- nic->macaddr[3], nic->macaddr[4], nic->macaddr[5]);
+ nic->macaddr.a[0], nic->macaddr.a[1], nic->macaddr.a[2],
+ nic->macaddr.a[3], nic->macaddr.a[4], nic->macaddr.a[5]);
xen_be_printf(NULL, 1, "config nic %d: mac=\"%s\"\n", nic->vlan->id, mac);
xen_config_dev_dirs("vif", "qnic", nic->vlan->id, fe, be, sizeof(fe));
diff --git a/hw/xen_disk.c b/hw/xen_disk.c
index 0c298af..add815f 100644
--- a/hw/xen_disk.c
+++ b/hw/xen_disk.c
@@ -616,12 +616,14 @@ static int blk_init(struct XenDevice *xendev)
{
struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev);
int index, qflags, have_barriers, info = 0;
- char *h;
/* read xenstore entries */
if (blkdev->params == NULL) {
+ char *h = NULL;
blkdev->params = xenstore_read_be_str(&blkdev->xendev, "params");
- h = strchr(blkdev->params, ':');
+ if (blkdev->params != NULL) {
+ h = strchr(blkdev->params, ':');
+ }
if (h != NULL) {
blkdev->fileproto = blkdev->params;
blkdev->filename = h+1;
@@ -631,6 +633,9 @@ static int blk_init(struct XenDevice *xendev)
blkdev->filename = blkdev->params;
}
}
+ if (!strcmp("aio", blkdev->fileproto)) {
+ blkdev->fileproto = "raw";
+ }
if (blkdev->mode == NULL) {
blkdev->mode = xenstore_read_be_str(&blkdev->xendev, "mode");
}
@@ -649,7 +654,7 @@ static int blk_init(struct XenDevice *xendev)
blkdev->mode == NULL ||
blkdev->type == NULL ||
blkdev->dev == NULL) {
- return -1;
+ goto out_error;
}
/* read-only ? */
@@ -672,10 +677,15 @@ static int blk_init(struct XenDevice *xendev)
/* setup via xenbus -> create new block driver instance */
xen_be_printf(&blkdev->xendev, 2, "create new bdrv (xenbus setup)\n");
blkdev->bs = bdrv_new(blkdev->dev);
- if (bdrv_open(blkdev->bs, blkdev->filename, qflags,
- bdrv_find_whitelisted_format(blkdev->fileproto)) != 0) {
- bdrv_delete(blkdev->bs);
- return -1;
+ if (blkdev->bs) {
+ if (bdrv_open(blkdev->bs, blkdev->filename, qflags,
+ bdrv_find_whitelisted_format(blkdev->fileproto)) != 0) {
+ bdrv_delete(blkdev->bs);
+ blkdev->bs = NULL;
+ }
+ }
+ if (!blkdev->bs) {
+ goto out_error;
}
} else {
/* setup via qemu cmdline -> already setup for us */
@@ -704,6 +714,19 @@ static int blk_init(struct XenDevice *xendev)
xenstore_write_be_int(&blkdev->xendev, "sectors",
blkdev->file_size / blkdev->file_blk);
return 0;
+
+out_error:
+ qemu_free(blkdev->params);
+ blkdev->params = NULL;
+ qemu_free(blkdev->mode);
+ blkdev->mode = NULL;
+ qemu_free(blkdev->type);
+ blkdev->type = NULL;
+ qemu_free(blkdev->dev);
+ blkdev->dev = NULL;
+ qemu_free(blkdev->devtype);
+ blkdev->devtype = NULL;
+ return -1;
}
static int blk_connect(struct XenDevice *xendev)
diff --git a/hw/xen_platform.c b/hw/xen_platform.c
index f43e175..fb6be6a 100644
--- a/hw/xen_platform.c
+++ b/hw/xen_platform.c
@@ -76,6 +76,35 @@ static void log_writeb(PCIXenPlatformState *s, char val)
}
/* Xen Platform, Fixed IOPort */
+#define UNPLUG_ALL_IDE_DISKS 1
+#define UNPLUG_ALL_NICS 2
+#define UNPLUG_AUX_IDE_DISKS 4
+
+static void unplug_nic(PCIBus *b, PCIDevice *d)
+{
+ if (pci_get_word(d->config + PCI_CLASS_DEVICE) ==
+ PCI_CLASS_NETWORK_ETHERNET) {
+ qdev_unplug(&(d->qdev));
+ }
+}
+
+static void pci_unplug_nics(PCIBus *bus)
+{
+ pci_for_each_device(bus, 0, unplug_nic);
+}
+
+static void unplug_disks(PCIBus *b, PCIDevice *d)
+{
+ if (pci_get_word(d->config + PCI_CLASS_DEVICE) ==
+ PCI_CLASS_STORAGE_IDE) {
+ qdev_unplug(&(d->qdev));
+ }
+}
+
+static void pci_unplug_disks(PCIBus *bus)
+{
+ pci_for_each_device(bus, 0, unplug_disks);
+}
static void platform_fixed_ioport_writew(void *opaque, uint32_t addr, uint32_t val)
{
@@ -83,10 +112,22 @@ static void platform_fixed_ioport_writew(void *opaque, uint32_t addr, uint32_t v
switch (addr - XEN_PLATFORM_IOPORT) {
case 0:
- /* TODO: */
/* Unplug devices. Value is a bitmask of which devices to
unplug, with bit 0 the IDE devices, bit 1 the network
devices, and bit 2 the non-primary-master IDE devices. */
+ if (val & UNPLUG_ALL_IDE_DISKS) {
+ DPRINTF("unplug disks\n");
+ qemu_aio_flush();
+ bdrv_flush_all();
+ pci_unplug_disks(s->pci_dev.bus);
+ }
+ if (val & UNPLUG_ALL_NICS) {
+ DPRINTF("unplug nics\n");
+ pci_unplug_nics(s->pci_dev.bus);
+ }
+ if (val & UNPLUG_AUX_IDE_DISKS) {
+ DPRINTF("unplug auxiliary disks not supported\n");
+ }
break;
case 2:
switch (val) {
diff --git a/hw/xenfb.c b/hw/xenfb.c
index 1db75fb..0a01ae3 100644
--- a/hw/xenfb.c
+++ b/hw/xenfb.c
@@ -347,13 +347,6 @@ static void xenfb_mouse_event(void *opaque,
static int input_init(struct XenDevice *xendev)
{
- struct XenInput *in = container_of(xendev, struct XenInput, c.xendev);
-
- if (!in->c.ds) {
- xen_be_printf(xendev, 1, "ds not set (yet)\n");
- return -1;
- }
-
xenstore_write_be_int(xendev, "feature-abs-pointer", 1);
return 0;
}
@@ -367,6 +360,18 @@ static int input_connect(struct XenDevice *xendev)
&in->abs_pointer_wanted) == -1)
in->abs_pointer_wanted = 0;
+ if (!in->c.ds) {
+ char *vfb = xenstore_read_str(NULL, "device/vfb");
+ if (vfb == NULL) {
+ /* there is no vfb, run vkbd on its own */
+ in->c.ds = get_displaystate();
+ } else {
+ qemu_free(vfb);
+ xen_be_printf(xendev, 1, "ds not set (yet)\n");
+ return -1;
+ }
+ }
+
rc = common_bind(&in->c);
if (rc != 0)
return rc;
diff --git a/hw/z2.c b/hw/z2.c
new file mode 100644
index 0000000..f93a1bf
--- /dev/null
+++ b/hw/z2.c
@@ -0,0 +1,358 @@
+/*
+ * PXA270-based Zipit Z2 device
+ *
+ * Copyright (c) 2011 by Vasily Khoruzhick <anarsoul@gmail.com>
+ *
+ * Code is based on mainstone platform.
+ *
+ * This code is licensed under the GNU GPL v2.
+ */
+
+#include "hw.h"
+#include "pxa.h"
+#include "arm-misc.h"
+#include "devices.h"
+#include "i2c.h"
+#include "ssi.h"
+#include "boards.h"
+#include "sysemu.h"
+#include "flash.h"
+#include "blockdev.h"
+#include "console.h"
+#include "audio/audio.h"
+
+#ifdef DEBUG_Z2
+#define DPRINTF(fmt, ...) \
+ printf(fmt, ## __VA_ARGS__)
+#else
+#define DPRINTF(fmt, ...)
+#endif
+
+static struct keymap map[0x100] = {
+ [0 ... 0xff] = { -1, -1 },
+ [0x3b] = {0, 0}, /* Option = F1 */
+ [0xc8] = {0, 1}, /* Up */
+ [0xd0] = {0, 2}, /* Down */
+ [0xcb] = {0, 3}, /* Left */
+ [0xcd] = {0, 4}, /* Right */
+ [0xcf] = {0, 5}, /* End */
+ [0x0d] = {0, 6}, /* KPPLUS */
+ [0xc7] = {1, 0}, /* Home */
+ [0x10] = {1, 1}, /* Q */
+ [0x17] = {1, 2}, /* I */
+ [0x22] = {1, 3}, /* G */
+ [0x2d] = {1, 4}, /* X */
+ [0x1c] = {1, 5}, /* Enter */
+ [0x0c] = {1, 6}, /* KPMINUS */
+ [0xc9] = {2, 0}, /* PageUp */
+ [0x11] = {2, 1}, /* W */
+ [0x18] = {2, 2}, /* O */
+ [0x23] = {2, 3}, /* H */
+ [0x2e] = {2, 4}, /* C */
+ [0x38] = {2, 5}, /* LeftAlt */
+ [0xd1] = {3, 0}, /* PageDown */
+ [0x12] = {3, 1}, /* E */
+ [0x19] = {3, 2}, /* P */
+ [0x24] = {3, 3}, /* J */
+ [0x2f] = {3, 4}, /* V */
+ [0x2a] = {3, 5}, /* LeftShift */
+ [0x01] = {4, 0}, /* Esc */
+ [0x13] = {4, 1}, /* R */
+ [0x1e] = {4, 2}, /* A */
+ [0x25] = {4, 3}, /* K */
+ [0x30] = {4, 4}, /* B */
+ [0x1d] = {4, 5}, /* LeftCtrl */
+ [0x0f] = {5, 0}, /* Tab */
+ [0x14] = {5, 1}, /* T */
+ [0x1f] = {5, 2}, /* S */
+ [0x26] = {5, 3}, /* L */
+ [0x31] = {5, 4}, /* N */
+ [0x39] = {5, 5}, /* Space */
+ [0x3c] = {6, 0}, /* Stop = F2 */
+ [0x15] = {6, 1}, /* Y */
+ [0x20] = {6, 2}, /* D */
+ [0x0e] = {6, 3}, /* Backspace */
+ [0x32] = {6, 4}, /* M */
+ [0x33] = {6, 5}, /* Comma */
+ [0x3d] = {7, 0}, /* Play = F3 */
+ [0x16] = {7, 1}, /* U */
+ [0x21] = {7, 2}, /* F */
+ [0x2c] = {7, 3}, /* Z */
+ [0x27] = {7, 4}, /* Semicolon */
+ [0x34] = {7, 5}, /* Dot */
+};
+
+#define Z2_RAM_SIZE 0x02000000
+#define Z2_FLASH_BASE 0x00000000
+#define Z2_FLASH_SIZE 0x00800000
+
+static struct arm_boot_info z2_binfo = {
+ .loader_start = PXA2XX_SDRAM_BASE,
+ .ram_size = Z2_RAM_SIZE,
+};
+
+#define Z2_GPIO_SD_DETECT 96
+#define Z2_GPIO_AC_IN 0
+#define Z2_GPIO_KEY_ON 1
+#define Z2_GPIO_LCD_CS 88
+
+typedef struct {
+ SSISlave ssidev;
+ int32_t selected;
+ int32_t enabled;
+ uint8_t buf[3];
+ uint32_t cur_reg;
+ int pos;
+} ZipitLCD;
+
+static uint32_t zipit_lcd_transfer(SSISlave *dev, uint32_t value)
+{
+ ZipitLCD *z = FROM_SSI_SLAVE(ZipitLCD, dev);
+ uint16_t val;
+ if (z->selected) {
+ z->buf[z->pos] = value & 0xff;
+ z->pos++;
+ }
+ if (z->pos == 3) {
+ switch (z->buf[0]) {
+ case 0x74:
+ DPRINTF("%s: reg: 0x%.2x\n", __func__, z->buf[2]);
+ z->cur_reg = z->buf[2];
+ break;
+ case 0x76:
+ val = z->buf[1] << 8 | z->buf[2];
+ DPRINTF("%s: value: 0x%.4x\n", __func__, val);
+ if (z->cur_reg == 0x22 && val == 0x0000) {
+ z->enabled = 1;
+ printf("%s: LCD enabled\n", __func__);
+ } else if (z->cur_reg == 0x10 && val == 0x0000) {
+ z->enabled = 0;
+ printf("%s: LCD disabled\n", __func__);
+ }
+ break;
+ default:
+ DPRINTF("%s: unknown command!\n", __func__);
+ break;
+ }
+ z->pos = 0;
+ }
+ return 0;
+}
+
+static void z2_lcd_cs(void *opaque, int line, int level)
+{
+ ZipitLCD *z2_lcd = opaque;
+ z2_lcd->selected = !level;
+}
+
+static int zipit_lcd_init(SSISlave *dev)
+{
+ ZipitLCD *z = FROM_SSI_SLAVE(ZipitLCD, dev);
+ z->selected = 0;
+ z->enabled = 0;
+ z->pos = 0;
+
+ return 0;
+}
+
+static VMStateDescription vmstate_zipit_lcd_state = {
+ .name = "zipit-lcd",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_INT32(selected, ZipitLCD),
+ VMSTATE_INT32(enabled, ZipitLCD),
+ VMSTATE_BUFFER(buf, ZipitLCD),
+ VMSTATE_UINT32(cur_reg, ZipitLCD),
+ VMSTATE_INT32(pos, ZipitLCD),
+ VMSTATE_END_OF_LIST(),
+ }
+};
+
+static SSISlaveInfo zipit_lcd_info = {
+ .qdev.name = "zipit-lcd",
+ .qdev.size = sizeof(ZipitLCD),
+ .qdev.vmsd = &vmstate_zipit_lcd_state,
+ .init = zipit_lcd_init,
+ .transfer = zipit_lcd_transfer
+};
+
+typedef struct {
+ i2c_slave i2c;
+ int len;
+ uint8_t buf[3];
+} AER915State;
+
+static int aer915_send(i2c_slave *i2c, uint8_t data)
+{
+ AER915State *s = FROM_I2C_SLAVE(AER915State, i2c);
+ s->buf[s->len] = data;
+ if (s->len++ > 2) {
+ DPRINTF("%s: message too long (%i bytes)\n",
+ __func__, s->len);
+ return 1;
+ }
+
+ if (s->len == 2) {
+ DPRINTF("%s: reg %d value 0x%02x\n", __func__,
+ s->buf[0], s->buf[1]);
+ }
+
+ return 0;
+}
+
+static void aer915_event(i2c_slave *i2c, enum i2c_event event)
+{
+ AER915State *s = FROM_I2C_SLAVE(AER915State, i2c);
+ switch (event) {
+ case I2C_START_SEND:
+ s->len = 0;
+ break;
+ case I2C_START_RECV:
+ if (s->len != 1) {
+ DPRINTF("%s: short message!?\n", __func__);
+ }
+ break;
+ case I2C_FINISH:
+ break;
+ default:
+ break;
+ }
+}
+
+static int aer915_recv(i2c_slave *slave)
+{
+ int retval = 0x00;
+ AER915State *s = FROM_I2C_SLAVE(AER915State, slave);
+
+ switch (s->buf[0]) {
+ /* Return hardcoded battery voltage,
+ * 0xf0 means ~4.1V
+ */
+ case 0x02:
+ retval = 0xf0;
+ break;
+ /* Return 0x00 for other regs,
+ * we don't know what they are for,
+ * anyway they return 0x00 on real hardware.
+ */
+ default:
+ break;
+ }
+
+ return retval;
+}
+
+static int aer915_init(i2c_slave *i2c)
+{
+ /* Nothing to do. */
+ return 0;
+}
+
+static VMStateDescription vmstate_aer915_state = {
+ .name = "aer915",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_INT32(len, AER915State),
+ VMSTATE_BUFFER(buf, AER915State),
+ VMSTATE_END_OF_LIST(),
+ }
+};
+
+static I2CSlaveInfo aer915_info = {
+ .qdev.name = "aer915",
+ .qdev.size = sizeof(AER915State),
+ .qdev.vmsd = &vmstate_aer915_state,
+ .init = aer915_init,
+ .event = aer915_event,
+ .recv = aer915_recv,
+ .send = aer915_send
+};
+
+static void z2_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)
+{
+ uint32_t sector_len = 0x10000;
+ PXA2xxState *cpu;
+ DriveInfo *dinfo;
+ int be;
+ void *z2_lcd;
+ i2c_bus *bus;
+ DeviceState *wm;
+
+ if (!cpu_model) {
+ cpu_model = "pxa270-c5";
+ }
+
+ /* Setup CPU & memory */
+ cpu = pxa270_init(z2_binfo.ram_size, cpu_model);
+
+#ifdef TARGET_WORDS_BIGENDIAN
+ be = 1;
+#else
+ be = 0;
+#endif
+ dinfo = drive_get(IF_PFLASH, 0, 0);
+ if (!dinfo) {
+ fprintf(stderr, "Flash image must be given with the "
+ "'pflash' parameter\n");
+ exit(1);
+ }
+
+ if (!pflash_cfi01_register(Z2_FLASH_BASE,
+ qemu_ram_alloc(NULL, "z2.flash0", Z2_FLASH_SIZE),
+ dinfo->bdrv, sector_len,
+ Z2_FLASH_SIZE / sector_len, 4, 0, 0, 0, 0,
+ be)) {
+ fprintf(stderr, "qemu: Error registering flash memory.\n");
+ exit(1);
+ }
+
+ /* setup keypad */
+ pxa27x_register_keypad(cpu->kp, map, 0x100);
+
+ /* MMC/SD host */
+ pxa2xx_mmci_handlers(cpu->mmc,
+ NULL,
+ qdev_get_gpio_in(cpu->gpio, Z2_GPIO_SD_DETECT));
+
+ ssi_register_slave(&zipit_lcd_info);
+ i2c_register_slave(&aer915_info);
+ z2_lcd = ssi_create_slave(cpu->ssp[1], "zipit-lcd");
+ bus = pxa2xx_i2c_bus(cpu->i2c[0]);
+ i2c_create_slave(bus, "aer915", 0x55);
+ wm = i2c_create_slave(bus, "wm8750", 0x1b);
+ cpu->i2s->opaque = wm;
+ cpu->i2s->codec_out = wm8750_dac_dat;
+ cpu->i2s->codec_in = wm8750_adc_dat;
+ wm8750_data_req_set(wm, cpu->i2s->data_req, cpu->i2s);
+
+ qdev_connect_gpio_out(cpu->gpio, Z2_GPIO_LCD_CS,
+ qemu_allocate_irqs(z2_lcd_cs, z2_lcd, 1)[0]);
+
+ if (kernel_filename) {
+ z2_binfo.kernel_filename = kernel_filename;
+ z2_binfo.kernel_cmdline = kernel_cmdline;
+ z2_binfo.initrd_filename = initrd_filename;
+ z2_binfo.board_id = 0x6dd;
+ arm_load_kernel(cpu->env, &z2_binfo);
+ }
+}
+
+static QEMUMachine z2_machine = {
+ .name = "z2",
+ .desc = "Zipit Z2 (PXA27x)",
+ .init = z2_init,
+};
+
+static void z2_machine_init(void)
+{
+ qemu_register_machine(&z2_machine);
+}
+
+machine_init(z2_machine_init);
diff --git a/input.c b/input.c
index f0a02e7..310bad5 100644
--- a/input.c
+++ b/input.c
@@ -59,7 +59,7 @@ static void check_mode_change(void)
if (is_absolute != current_is_absolute ||
has_absolute != current_has_absolute) {
- notifier_list_notify(&mouse_mode_notifiers);
+ notifier_list_notify(&mouse_mode_notifiers, NULL);
}
current_is_absolute = is_absolute;
diff --git a/ioport.c b/ioport.c
index 2e971fa..a32483b 100644
--- a/ioport.c
+++ b/ioport.c
@@ -146,7 +146,7 @@ int register_ioport_read(pio_addr_t start, int length, int size,
hw_error("register_ioport_read: invalid size");
return -1;
}
- for(i = start; i < start + length; i += size) {
+ for(i = start; i < start + length; ++i) {
ioport_read_table[bsize][i] = func;
if (ioport_opaque[i] != NULL && ioport_opaque[i] != opaque)
hw_error("register_ioport_read: invalid opaque for address 0x%x",
@@ -166,7 +166,7 @@ int register_ioport_write(pio_addr_t start, int length, int size,
hw_error("register_ioport_write: invalid size");
return -1;
}
- for(i = start; i < start + length; i += size) {
+ for(i = start; i < start + length; ++i) {
ioport_write_table[bsize][i] = func;
if (ioport_opaque[i] != NULL && ioport_opaque[i] != opaque)
hw_error("register_ioport_write: invalid opaque for address 0x%x",
@@ -245,18 +245,25 @@ void isa_unassign_ioport(pio_addr_t start, int length)
int i;
for(i = start; i < start + length; i++) {
- ioport_read_table[0][i] = default_ioport_readb;
- ioport_read_table[1][i] = default_ioport_readw;
- ioport_read_table[2][i] = default_ioport_readl;
+ ioport_read_table[0][i] = NULL;
+ ioport_read_table[1][i] = NULL;
+ ioport_read_table[2][i] = NULL;
- ioport_write_table[0][i] = default_ioport_writeb;
- ioport_write_table[1][i] = default_ioport_writew;
- ioport_write_table[2][i] = default_ioport_writel;
+ ioport_write_table[0][i] = NULL;
+ ioport_write_table[1][i] = NULL;
+ ioport_write_table[2][i] = NULL;
ioport_opaque[i] = NULL;
}
}
+bool isa_is_ioport_assigned(pio_addr_t start)
+{
+ return (ioport_read_table[0][start] || ioport_write_table[0][start] ||
+ ioport_read_table[1][start] || ioport_write_table[1][start] ||
+ ioport_read_table[2][start] || ioport_write_table[2][start]);
+}
+
/***********************************************************/
void cpu_outb(pio_addr_t addr, uint8_t val)
diff --git a/ioport.h b/ioport.h
index 5ae62a3..82ffd9d 100644
--- a/ioport.h
+++ b/ioport.h
@@ -43,7 +43,7 @@ int register_ioport_read(pio_addr_t start, int length, int size,
int register_ioport_write(pio_addr_t start, int length, int size,
IOPortWriteFunc *func, void *opaque);
void isa_unassign_ioport(pio_addr_t start, int length);
-
+bool isa_is_ioport_assigned(pio_addr_t start);
void cpu_outb(pio_addr_t addr, uint8_t val);
void cpu_outw(pio_addr_t addr, uint16_t val);
diff --git a/iov.c b/iov.c
index 588cd04..e7385c4 100644
--- a/iov.c
+++ b/iov.c
@@ -14,57 +14,116 @@
#include "iov.h"
-size_t iov_from_buf(struct iovec *iov, unsigned int iovcnt,
- const void *buf, size_t size)
+size_t iov_from_buf(struct iovec *iov, unsigned int iov_cnt,
+ const void *buf, size_t iov_off, size_t size)
{
- size_t offset;
+ size_t iovec_off, buf_off;
unsigned int i;
- offset = 0;
- for (i = 0; offset < size && i < iovcnt; i++) {
- size_t len;
+ iovec_off = 0;
+ buf_off = 0;
+ for (i = 0; i < iov_cnt && size; i++) {
+ if (iov_off < (iovec_off + iov[i].iov_len)) {
+ size_t len = MIN((iovec_off + iov[i].iov_len) - iov_off, size);
- len = MIN(iov[i].iov_len, size - offset);
+ memcpy(iov[i].iov_base + (iov_off - iovec_off), buf + buf_off, len);
- memcpy(iov[i].iov_base, buf + offset, len);
- offset += len;
+ buf_off += len;
+ iov_off += len;
+ size -= len;
+ }
+ iovec_off += iov[i].iov_len;
}
- return offset;
+ return buf_off;
}
-size_t iov_to_buf(const struct iovec *iov, const unsigned int iovcnt,
- void *buf, size_t offset, size_t size)
+size_t iov_to_buf(const struct iovec *iov, const unsigned int iov_cnt,
+ void *buf, size_t iov_off, size_t size)
{
uint8_t *ptr;
- size_t iov_off, buf_off;
+ size_t iovec_off, buf_off;
unsigned int i;
ptr = buf;
- iov_off = 0;
+ iovec_off = 0;
+ buf_off = 0;
+ for (i = 0; i < iov_cnt && size; i++) {
+ if (iov_off < (iovec_off + iov[i].iov_len)) {
+ size_t len = MIN((iovec_off + iov[i].iov_len) - iov_off , size);
+
+ memcpy(ptr + buf_off, iov[i].iov_base + (iov_off - iovec_off), len);
+
+ buf_off += len;
+ iov_off += len;
+ size -= len;
+ }
+ iovec_off += iov[i].iov_len;
+ }
+ return buf_off;
+}
+
+size_t iov_clear(const struct iovec *iov, const unsigned int iov_cnt,
+ size_t iov_off, size_t size)
+{
+ size_t iovec_off, buf_off;
+ unsigned int i;
+
+ iovec_off = 0;
buf_off = 0;
- for (i = 0; i < iovcnt && size; i++) {
- if (offset < (iov_off + iov[i].iov_len)) {
- size_t len = MIN((iov_off + iov[i].iov_len) - offset , size);
+ for (i = 0; i < iov_cnt && size; i++) {
+ if (iov_off < (iovec_off + iov[i].iov_len)) {
+ size_t len = MIN((iovec_off + iov[i].iov_len) - iov_off , size);
- memcpy(ptr + buf_off, iov[i].iov_base + (offset - iov_off), len);
+ memset(iov[i].iov_base + (iov_off - iovec_off), 0, len);
buf_off += len;
- offset += len;
+ iov_off += len;
size -= len;
}
- iov_off += iov[i].iov_len;
+ iovec_off += iov[i].iov_len;
}
return buf_off;
}
-size_t iov_size(const struct iovec *iov, const unsigned int iovcnt)
+size_t iov_size(const struct iovec *iov, const unsigned int iov_cnt)
{
size_t len;
unsigned int i;
len = 0;
- for (i = 0; i < iovcnt; i++) {
+ for (i = 0; i < iov_cnt; i++) {
len += iov[i].iov_len;
}
return len;
}
+
+void iov_hexdump(const struct iovec *iov, const unsigned int iov_cnt,
+ FILE *fp, const char *prefix, size_t limit)
+{
+ unsigned int i, v, b;
+ uint8_t *c;
+
+ c = iov[0].iov_base;
+ for (i = 0, v = 0, b = 0; b < limit; i++, b++) {
+ if (i == iov[v].iov_len) {
+ i = 0; v++;
+ if (v == iov_cnt) {
+ break;
+ }
+ c = iov[v].iov_base;
+ }
+ if ((b % 16) == 0) {
+ fprintf(fp, "%s: %04x:", prefix, b);
+ }
+ if ((b % 4) == 0) {
+ fprintf(fp, " ");
+ }
+ fprintf(fp, " %02x", c[i]);
+ if ((b % 16) == 15) {
+ fprintf(fp, "\n");
+ }
+ }
+ if ((b % 16) != 0) {
+ fprintf(fp, "\n");
+ }
+}
diff --git a/iov.h b/iov.h
index 60a8547..94d2f78 100644
--- a/iov.h
+++ b/iov.h
@@ -12,8 +12,12 @@
#include "qemu-common.h"
-size_t iov_from_buf(struct iovec *iov, unsigned int iovcnt,
- const void *buf, size_t size);
-size_t iov_to_buf(const struct iovec *iov, const unsigned int iovcnt,
- void *buf, size_t offset, size_t size);
-size_t iov_size(const struct iovec *iov, const unsigned int iovcnt);
+size_t iov_from_buf(struct iovec *iov, unsigned int iov_cnt,
+ const void *buf, size_t iov_off, size_t size);
+size_t iov_to_buf(const struct iovec *iov, const unsigned int iov_cnt,
+ void *buf, size_t iov_off, size_t size);
+size_t iov_size(const struct iovec *iov, const unsigned int iov_cnt);
+size_t iov_clear(const struct iovec *iov, const unsigned int iov_cnt,
+ size_t iov_off, size_t size);
+void iov_hexdump(const struct iovec *iov, const unsigned int iov_cnt,
+ FILE *fp, const char *prefix, size_t limit);
diff --git a/libcacard/Makefile b/libcacard/Makefile
index 9802c37..5cd7594 100644
--- a/libcacard/Makefile
+++ b/libcacard/Makefile
@@ -2,7 +2,10 @@
-include $(SRC_PATH)/Makefile.objs
-include $(SRC_PATH)/rules.mak
-$(call set-vpath, $(SRC_PATH):$(SRC_PATH)/libcacard)
+libcacard_srcpath=$(SRC_PATH)/libcacard
+libcacard_includedir=$(includedir)/cacard
+
+$(call set-vpath, $(SRC_PATH):$(libcacard_srcpath))
# objects linked against normal qemu binaries, not compiled with libtool
QEMU_OBJS=$(addprefix ../,$(oslib-obj-y) qemu-malloc.o qemu-timer-common.o $(trace-obj-y))
@@ -18,7 +21,7 @@ vscclient: $(libcacard-y) $(QEMU_OBJS) vscclient.o
$(call quiet-command,$(CC) $(libcacard_libs) -lrt -o $@ $^," LINK $@")
clean:
- rm -f *.o */*.o *.d */*.d *.a */*.a *~ */*~ vscclient *.lo .libs/* *.la
+ rm -f *.o */*.o *.d */*.d *.a */*.a *~ */*~ vscclient *.lo .libs/* *.la *.pc
rm -Rf .libs
all: vscclient
@@ -36,7 +39,24 @@ else
libcacard.la: $(libcacard.lib-y) $(QEMU_OBJS_LIB)
$(call quiet-command,libtool --mode=link --quiet --tag=CC $(CC) $(libcacard_libs) -lrt -rpath $(libdir) -o $@ $^," lt LINK $@")
-install-libcacard: libcacard.la
+libcacard.pc: $(libcacard_srcpath)/libcacard.pc.in
+ sed -e 's|@LIBDIR@|$(libdir)|' \
+ -e 's|@INCLUDEDIR@|$(libcacard_includedir)|' \
+ -e 's|@VERSION@|$(shell cat $(SRC_PATH)/VERSION)|' \
+ -e 's|@PREFIX@|$(prefix)|' \
+ < $(libcacard_srcpath)/libcacard.pc.in > libcacard.pc
+
+.PHONY: install-libcacard
+
+install-libcacard: libcacard.pc libcacard.la vscclient
$(INSTALL_DIR) "$(DESTDIR)$(libdir)"
+ $(INSTALL_DIR) "$(DESTDIR)$(libdir)/pkgconfig"
+ $(INSTALL_DIR) "$(DESTDIR)$(libcacard_includedir)"
+ $(INSTALL_DIR) "$(DESTDIR)$(bindir)"
+ libtool --mode=install $(INSTALL_PROG) vscclient "$(DESTDIR)$(bindir)"
libtool --mode=install $(INSTALL_PROG) libcacard.la "$(DESTDIR)$(libdir)"
+ libtool --mode=install $(INSTALL_DATA) libcacard.pc "$(DESTDIR)$(libdir)/pkgconfig"
+ for inc in *.h; do \
+ libtool --mode=install $(INSTALL_DATA) $(libcacard_srcpath)/$$inc "$(DESTDIR)$(libcacard_includedir)"; \
+ done
endif
diff --git a/libcacard/libcacard.pc.in b/libcacard/libcacard.pc.in
new file mode 100644
index 0000000..b6859b0
--- /dev/null
+++ b/libcacard/libcacard.pc.in
@@ -0,0 +1,13 @@
+prefix=@PREFIX@
+exec_prefix=${prefix}
+libdir=@LIBDIR@
+includedir=@INCLUDEDIR@
+
+Name: cacard
+Description: CA Card library
+Version: @VERSION@
+
+Requires: nss
+Libs: -L${libdir} -lcacard
+Libs.private:
+Cflags: -I${includedir}
diff --git a/libcacard/vcard_emul_nss.c b/libcacard/vcard_emul_nss.c
index f3db657..84fc490 100644
--- a/libcacard/vcard_emul_nss.c
+++ b/libcacard/vcard_emul_nss.c
@@ -33,10 +33,17 @@
#include "vreader.h"
#include "vevent.h"
+typedef enum {
+ VCardEmulUnknown = -1,
+ VCardEmulFalse = 0,
+ VCardEmulTrue = 1
+} VCardEmulTriState;
+
struct VCardKeyStruct {
CERTCertificate *cert;
PK11SlotInfo *slot;
SECKEYPrivateKey *key;
+ VCardEmulTriState failedX509;
};
@@ -140,6 +147,7 @@ vcard_emul_make_key(PK11SlotInfo *slot, CERTCertificate *cert)
/* NOTE: the cert is a temp cert, not necessarily the cert in the token,
* use the DER version of this function */
key->key = PK11_FindKeyByDERCert(slot, cert, NULL);
+ key->failedX509 = VCardEmulUnknown;
return key;
}
@@ -208,13 +216,23 @@ vcard_emul_rsa_op(VCard *card, VCardKey *key,
{
SECKEYPrivateKey *priv_key;
unsigned signature_len;
+ PK11SlotInfo *slot;
SECStatus rv;
+ unsigned char buf[2048];
+ unsigned char *bp = NULL;
+ int pad_len;
+ vcard_7816_status_t ret = VCARD7816_STATUS_SUCCESS;
if ((!nss_emul_init) || (key == NULL)) {
/* couldn't get the key, indicate that we aren't logged in */
return VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED;
}
priv_key = vcard_emul_get_nss_key(key);
+ if (priv_key == NULL) {
+ /* couldn't get the key, indicate that we aren't logged in */
+ return VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED;
+ }
+ slot = vcard_emul_card_get_slot(card);
/*
* this is only true of the rsa signature
@@ -223,13 +241,116 @@ vcard_emul_rsa_op(VCard *card, VCardKey *key,
if (buffer_size != signature_len) {
return VCARD7816_STATUS_ERROR_DATA_INVALID;
}
- rv = PK11_PrivDecryptRaw(priv_key, buffer, &signature_len, signature_len,
- buffer, buffer_size);
- if (rv != SECSuccess) {
- return vcard_emul_map_error(PORT_GetError());
+ /* be able to handle larger keys if necessariy */
+ bp = &buf[0];
+ if (sizeof(buf) < signature_len) {
+ bp = qemu_malloc(signature_len);
+ }
+
+ /*
+ * do the raw operations. Some tokens claim to do CKM_RSA_X_509, but then
+ * choke when they try to do the actual operations. Try to detect
+ * those cases and treat them as if the token didn't claim support for
+ * X_509.
+ */
+ if (key->failedX509 != VCardEmulTrue
+ && PK11_DoesMechanism(slot, CKM_RSA_X_509)) {
+ rv = PK11_PrivDecryptRaw(priv_key, bp, &signature_len, signature_len,
+ buffer, buffer_size);
+ if (rv == SECSuccess) {
+ assert(buffer_size == signature_len);
+ memcpy(buffer, bp, signature_len);
+ key->failedX509 = VCardEmulFalse;
+ goto cleanup;
+ }
+ /*
+ * we've had a successful X509 operation, this failure must be
+ * somethine else
+ */
+ if (key->failedX509 == VCardEmulFalse) {
+ ret = vcard_emul_map_error(PORT_GetError());
+ goto cleanup;
+ }
+ /*
+ * key->failedX509 must be Unknown at this point, try the
+ * non-x_509 case
+ */
+ }
+ /* token does not support CKM_RSA_X509, emulate that with CKM_RSA_PKCS */
+ /* is this a PKCS #1 formatted signature? */
+ if ((buffer[0] == 0) && (buffer[1] == 1)) {
+ int i;
+
+ for (i = 2; i < buffer_size; i++) {
+ /* rsa signature pad */
+ if (buffer[i] != 0xff) {
+ break;
+ }
+ }
+ if ((i < buffer_size) && (buffer[i] == 0)) {
+ /* yes, we have a properly formated PKCS #1 signature */
+ /*
+ * NOTE: even if we accidentally got an encrypt buffer, which
+ * through shear luck started with 00, 01, ff, 00, it won't matter
+ * because the resulting Sign operation will effectively decrypt
+ * the real buffer.
+ */
+ SECItem signature;
+ SECItem hash;
+
+ i++;
+ hash.data = &buffer[i];
+ hash.len = buffer_size - i;
+ signature.data = bp;
+ signature.len = signature_len;
+ rv = PK11_Sign(priv_key, &signature, &hash);
+ if (rv != SECSuccess) {
+ ret = vcard_emul_map_error(PORT_GetError());
+ goto cleanup;
+ }
+ assert(buffer_size == signature.len);
+ memcpy(buffer, bp, signature.len);
+ /*
+ * we got here because either the X509 attempt failed, or the
+ * token couldn't do the X509 operation, in either case stay
+ * with the PKCS version for future operations on this key
+ */
+ key->failedX509 = VCardEmulTrue;
+ goto cleanup;
+ }
+ }
+ pad_len = buffer_size - signature_len;
+ assert(pad_len < 4);
+ /*
+ * OK now we've decrypted the payload, package it up in PKCS #1 for the
+ * upper layer.
+ */
+ buffer[0] = 0;
+ buffer[1] = 2; /* RSA_encrypt */
+ pad_len -= 3; /* format is 0 || 2 || pad || 0 || data */
+ /*
+ * padding for PKCS #1 encrypted data is a string of random bytes. The
+ * random butes protect against potential decryption attacks against RSA.
+ * Since PrivDecrypt has already stripped those bytes, we can't reconstruct
+ * them. This shouldn't matter to the upper level code which should just
+ * strip this code out anyway, so We'll pad with a constant 3.
+ */
+ memset(&buffer[2], 0x03, pad_len);
+ pad_len += 2; /* index to the end of the pad */
+ buffer[pad_len] = 0;
+ pad_len++; /* index to the start of the data */
+ memcpy(&buffer[pad_len], bp, signature_len);
+ /*
+ * we got here because either the X509 attempt failed, or the
+ * token couldn't do the X509 operation, in either case stay
+ * with the PKCS version for future operations on this key
+ */
+ key->failedX509 = VCardEmulTrue;
+cleanup:
+ if (bp != buf) {
+ qemu_free(bp);
}
- assert(buffer_size == signature_len);
- return VCARD7816_STATUS_SUCCESS;
+ return ret;
}
/*
@@ -476,6 +597,7 @@ vcard_emul_mirror_card(VReader *vreader)
VCardKey **keys;
PK11SlotInfo *slot;
PRBool ret;
+ VCard *card;
slot = vcard_emul_reader_get_slot(vreader);
if (slot == NULL) {
@@ -535,7 +657,12 @@ vcard_emul_mirror_card(VReader *vreader)
}
/* now create the card */
- return vcard_emul_make_card(vreader, certs, cert_len, keys, cert_count);
+ card = vcard_emul_make_card(vreader, certs, cert_len, keys, cert_count);
+ qemu_free(certs);
+ qemu_free(cert_len);
+ qemu_free(keys);
+
+ return card;
}
static VCardEmulType default_card_type = VCARD_EMUL_NONE;
@@ -820,6 +947,9 @@ vcard_emul_init(const VCardEmulOptions *options)
vreader_free(vreader);
has_readers = PR_TRUE;
}
+ qemu_free(certs);
+ qemu_free(cert_len);
+ qemu_free(keys);
}
/* if we aren't suppose to use hw, skip looking up hardware tokens */
@@ -925,17 +1055,6 @@ vcard_emul_replay_insertion_events(void)
/*
* Silly little functions to help parsing our argument string
*/
-static char *
-copy_string(const char *str, int str_len)
-{
- char *new_str;
-
- new_str = qemu_malloc(str_len+1);
- memcpy(new_str, str, str_len);
- new_str[str_len] = 0;
- return new_str;
-}
-
static int
count_tokens(const char *str, char token, char token_end)
{
@@ -975,13 +1094,31 @@ find_blank(const char *str)
static VCardEmulOptions options;
#define READER_STEP 4
+/* Expects "args" to be at the beginning of a token (ie right after the ','
+ * ending the previous token), and puts the next token start in "token",
+ * and its length in "token_length". "token" will not be nul-terminated.
+ * After calling the macro, "args" will be advanced to the beginning of
+ * the next token.
+ * This macro may call continue or break.
+ */
+#define NEXT_TOKEN(token) \
+ (token) = args; \
+ args = strpbrk(args, ",)"); \
+ if (*args == 0) { \
+ break; \
+ } \
+ if (*args == ')') { \
+ args++; \
+ continue; \
+ } \
+ (token##_length) = args - (token); \
+ args = strip(args+1);
+
VCardEmulOptions *
vcard_emul_options(const char *args)
{
int reader_count = 0;
VCardEmulOptions *opts;
- char type_str[100];
- int type_len;
/* Allow the future use of allocating the options structure on the fly */
memcpy(&options, &default_options, sizeof(options));
@@ -996,63 +1133,32 @@ vcard_emul_options(const char *args)
* cert_2,cert_3...) */
if (strncmp(args, "soft=", 5) == 0) {
const char *name;
+ size_t name_length;
const char *vname;
+ size_t vname_length;
const char *type_params;
+ size_t type_params_length;
+ char type_str[100];
VCardEmulType type;
- int name_length, vname_length, type_params_length, count, i;
+ int count, i;
VirtualReaderOptions *vreaderOpt = NULL;
args = strip(args + 5);
if (*args != '(') {
continue;
}
- name = args;
- args = strpbrk(args + 1, ",)");
- if (*args == 0) {
- break;
- }
- if (*args == ')') {
- args++;
- continue;
- }
- args = strip(args+1);
- name_length = args - name - 2;
- vname = args;
- args = strpbrk(args + 1, ",)");
- if (*args == 0) {
- break;
- }
- if (*args == ')') {
- args++;
- continue;
- }
- vname_length = args - name - 2;
args = strip(args+1);
- type_len = strpbrk(args, ",)") - args;
- assert(sizeof(type_str) > type_len);
- strncpy(type_str, args, type_len);
- type_str[type_len] = 0;
+
+ NEXT_TOKEN(name)
+ NEXT_TOKEN(vname)
+ NEXT_TOKEN(type_params)
+ type_params_length = MIN(type_params_length, sizeof(type_str)-1);
+ strncpy(type_str, type_params, type_params_length);
+ type_str[type_params_length] = 0;
type = vcard_emul_type_from_string(type_str);
- args = strpbrk(args, ",)");
- if (*args == 0) {
- break;
- }
- if (*args == ')') {
- args++;
- continue;
- }
- args = strip(args++);
- type_params = args;
- args = strpbrk(args + 1, ",)");
- if (*args == 0) {
- break;
- }
- if (*args == ')') {
- args++;
- continue;
- }
- type_params_length = args - name;
- args = strip(args++);
+
+ NEXT_TOKEN(type_params)
+
if (*args == 0) {
break;
}
@@ -1067,18 +1173,19 @@ vcard_emul_options(const char *args)
}
opts->vreader = vreaderOpt;
vreaderOpt = &vreaderOpt[opts->vreader_count];
- vreaderOpt->name = copy_string(name, name_length);
- vreaderOpt->vname = copy_string(vname, vname_length);
+ vreaderOpt->name = qemu_strndup(name, name_length);
+ vreaderOpt->vname = qemu_strndup(vname, vname_length);
vreaderOpt->card_type = type;
vreaderOpt->type_params =
- copy_string(type_params, type_params_length);
- count = count_tokens(args, ',', ')');
+ qemu_strndup(type_params, type_params_length);
+ count = count_tokens(args, ',', ')') + 1;
vreaderOpt->cert_count = count;
vreaderOpt->cert_name = (char **)qemu_malloc(count*sizeof(char *));
for (i = 0; i < count; i++) {
- const char *cert = args + 1;
- args = strpbrk(args + 1, ",)");
- vreaderOpt->cert_name[i] = copy_string(cert, args - cert);
+ const char *cert = args;
+ args = strpbrk(args, ",)");
+ vreaderOpt->cert_name[i] = qemu_strndup(cert, args - cert);
+ args = strip(args+1);
}
if (*args == ')') {
args++;
@@ -1104,7 +1211,7 @@ vcard_emul_options(const char *args)
args = strip(args+10);
params = args;
args = find_blank(args);
- opts->hw_type_params = copy_string(params, args-params);
+ opts->hw_type_params = qemu_strndup(params, args-params);
/* db="/data/base/path" */
} else if (strncmp(args, "db=", 3) == 0) {
const char *db;
@@ -1115,7 +1222,7 @@ vcard_emul_options(const char *args)
args++;
db = args;
args = strpbrk(args, "\"\n");
- opts->nss_db = copy_string(db, args-db);
+ opts->nss_db = qemu_strndup(db, args-db);
if (*args != 0) {
args++;
}
diff --git a/linux-aio.c b/linux-aio.c
index 68f4b3d..dc3faf2 100644
--- a/linux-aio.c
+++ b/linux-aio.c
@@ -31,7 +31,6 @@ struct qemu_laiocb {
struct iocb iocb;
ssize_t ret;
size_t nbytes;
- int async_context_id;
QLIST_ENTRY(qemu_laiocb) node;
};
@@ -39,7 +38,6 @@ struct qemu_laio_state {
io_context_t ctx;
int efd;
int count;
- QLIST_HEAD(, qemu_laiocb) completed_reqs;
};
static inline ssize_t io_event_ret(struct io_event *ev)
@@ -49,7 +47,6 @@ static inline ssize_t io_event_ret(struct io_event *ev)
/*
* Completes an AIO request (calls the callback and frees the ACB).
- * Be sure to be in the right AsyncContext before calling this function.
*/
static void qemu_laio_process_completion(struct qemu_laio_state *s,
struct qemu_laiocb *laiocb)
@@ -72,42 +69,12 @@ static void qemu_laio_process_completion(struct qemu_laio_state *s,
}
/*
- * Processes all queued AIO requests, i.e. requests that have return from OS
- * but their callback was not called yet. Requests that cannot have their
- * callback called in the current AsyncContext, remain in the queue.
- *
- * Returns 1 if at least one request could be completed, 0 otherwise.
+ * All requests are directly processed when they complete, so there's nothing
+ * left to do during qemu_aio_wait().
*/
static int qemu_laio_process_requests(void *opaque)
{
- struct qemu_laio_state *s = opaque;
- struct qemu_laiocb *laiocb, *next;
- int res = 0;
-
- QLIST_FOREACH_SAFE (laiocb, &s->completed_reqs, node, next) {
- if (laiocb->async_context_id == get_async_context_id()) {
- qemu_laio_process_completion(s, laiocb);
- QLIST_REMOVE(laiocb, node);
- res = 1;
- }
- }
-
- return res;
-}
-
-/*
- * Puts a request in the completion queue so that its callback is called the
- * next time when it's possible. If we already are in the right AsyncContext,
- * the request is completed immediately instead.
- */
-static void qemu_laio_enqueue_completed(struct qemu_laio_state *s,
- struct qemu_laiocb* laiocb)
-{
- if (laiocb->async_context_id == get_async_context_id()) {
- qemu_laio_process_completion(s, laiocb);
- } else {
- QLIST_INSERT_HEAD(&s->completed_reqs, laiocb, node);
- }
+ return 0;
}
static void qemu_laio_completion_cb(void *opaque)
@@ -141,7 +108,7 @@ static void qemu_laio_completion_cb(void *opaque)
container_of(iocb, struct qemu_laiocb, iocb);
laiocb->ret = io_event_ret(&events[i]);
- qemu_laio_enqueue_completed(s, laiocb);
+ qemu_laio_process_completion(s, laiocb);
}
}
}
@@ -204,7 +171,6 @@ BlockDriverAIOCB *laio_submit(BlockDriverState *bs, void *aio_ctx, int fd,
laiocb->nbytes = nb_sectors * 512;
laiocb->ctx = s;
laiocb->ret = -EINPROGRESS;
- laiocb->async_context_id = get_async_context_id();
iocbs = &laiocb->iocb;
@@ -239,7 +205,6 @@ void *laio_init(void)
struct qemu_laio_state *s;
s = qemu_mallocz(sizeof(*s));
- QLIST_INIT(&s->completed_reqs);
s->efd = eventfd(0, 0);
if (s->efd == -1)
goto out_free_state;
diff --git a/linux-user/alpha/syscall_nr.h b/linux-user/alpha/syscall_nr.h
index e3127df..f6284db 100644
--- a/linux-user/alpha/syscall_nr.h
+++ b/linux-user/alpha/syscall_nr.h
@@ -411,4 +411,25 @@
#define TARGET_NR_signalfd 476
#define TARGET_NR_timerfd 477
#define TARGET_NR_eventfd 478
-
+#define TARGET_NR_recvmmsg 479
+#define TARGET_NR_fallocate 480
+#define TARGET_NR_timerfd_create 481
+#define TARGET_NR_timerfd_settime 482
+#define TARGET_NR_timerfd_gettime 483
+#define TARGET_NR_signalfd4 484
+#define TARGET_NR_eventfd2 485
+#define TARGET_NR_epoll_create1 486
+#define TARGET_NR_dup3 487
+#define TARGET_NR_pipe2 488
+#define TARGET_NR_inotify_init1 489
+#define TARGET_NR_preadv 490
+#define TARGET_NR_pwritev 491
+#define TARGET_NR_rt_tgsigqueueinfo 492
+#define TARGET_NR_perf_event_open 493
+#define TARGET_NR_fanotify_init 494
+#define TARGET_NR_fanotify_mark 495
+#define TARGET_NR_prlimit64 496
+#define TARGET_NR_name_to_handle_at 497
+#define TARGET_NR_open_by_handle_at 498
+#define TARGET_NR_clock_adjtime 499
+#define TARGET_NR_syncfs 500
diff --git a/linux-user/arm/syscall_nr.h b/linux-user/arm/syscall_nr.h
index 79a216a..7f05879 100644
--- a/linux-user/arm/syscall_nr.h
+++ b/linux-user/arm/syscall_nr.h
@@ -365,3 +365,16 @@
#define TARGET_NR_dup3 (358)
#define TARGET_NR_pipe2 (359)
#define TARGET_NR_inotify_init1 (360)
+#define TARGET_NR_preadv (361)
+#define TARGET_NR_pwritev (362)
+#define TARGET_NR_rt_tgsigqueueinfo (363)
+#define TARGET_NR_perf_event_open (364)
+#define TARGET_NR_recvmmsg (365)
+#define TARGET_NR_accept4 (366)
+#define TARGET_NR_fanotify_init (367)
+#define TARGET_NR_fanotify_mark (368)
+#define TARGET_NR_prlimit64 (369)
+#define TARGET_NR_name_to_handle_at (370)
+#define TARGET_NR_open_by_handle_at (371)
+#define TARGET_NR_clock_adjtime (372)
+#define TARGET_NR_syncfs (373)
diff --git a/linux-user/cris/syscall_nr.h b/linux-user/cris/syscall_nr.h
index 6132817..98f1a0b 100644
--- a/linux-user/cris/syscall_nr.h
+++ b/linux-user/cris/syscall_nr.h
@@ -333,3 +333,5 @@
#define TARGET_NR_dup3 330
#define TARGET_NR_pipe2 331
#define TARGET_NR_inotify_init1 332
+#define TARGET_NR_preadv 333
+#define TARGET_NR_pwritev 334
diff --git a/linux-user/i386/syscall_nr.h b/linux-user/i386/syscall_nr.h
index 3ef71ce..74abfca 100644
--- a/linux-user/i386/syscall_nr.h
+++ b/linux-user/i386/syscall_nr.h
@@ -335,3 +335,15 @@
#define TARGET_NR_dup3 330
#define TARGET_NR_pipe2 331
#define TARGET_NR_inotify_init1 332
+#define TARGET_NR_preadv 333
+#define TARGET_NR_pwritev 334
+#define TARGET_NR_rt_tgsigqueueinfo 335
+#define TARGET_NR_perf_event_open 336
+#define TARGET_NR_recvmmsg 337
+#define TARGET_NR_fanotify_init 338
+#define TARGET_NR_fanotify_mark 339
+#define TARGET_NR_prlimit64 340
+#define TARGET_NR_name_to_handle_at 341
+#define TARGET_NR_open_by_handle_at 342
+#define TARGET_NR_clock_adjtime 343
+#define TARGET_NR_syncfs 344
diff --git a/linux-user/ioctls.h b/linux-user/ioctls.h
index 42b3ae3..6514502 100644
--- a/linux-user/ioctls.h
+++ b/linux-user/ioctls.h
@@ -59,6 +59,10 @@
IOCTL(KDSKBMODE, 0, TYPE_INT)
IOCTL(KDGKBENT, IOC_RW, MK_PTR(MK_STRUCT(STRUCT_kbentry)))
IOCTL(KDGKBSENT, IOC_RW, MK_PTR(MK_STRUCT(STRUCT_kbsentry)))
+ IOCTL(KDGKBLED, 0, TYPE_INT)
+ IOCTL(KDSKBLED, 0, TYPE_INT)
+ IOCTL(KDGETLED, 0, TYPE_INT)
+ IOCTL(KDSETLED, 0, TYPE_INT)
IOCTL(BLKROSET, IOC_W, MK_PTR(TYPE_INT))
IOCTL(BLKROGET, IOC_R, MK_PTR(TYPE_INT))
@@ -325,6 +329,11 @@
IOCTL(FBIOGET_FSCREENINFO, IOC_R, MK_PTR(MK_STRUCT(STRUCT_fb_fix_screeninfo)))
IOCTL(FBIOGET_VSCREENINFO, IOC_R, MK_PTR(MK_STRUCT(STRUCT_fb_var_screeninfo)))
IOCTL(FBIOPUT_VSCREENINFO, IOC_W, MK_PTR(MK_STRUCT(STRUCT_fb_var_screeninfo)))
+ IOCTL(FBIOGETCMAP, IOC_RW, MK_PTR(MK_STRUCT(STRUCT_fb_cmap)))
+ IOCTL(FBIOPUTCMAP, IOC_RW, MK_PTR(MK_STRUCT(STRUCT_fb_cmap)))
+ IOCTL(FBIOPAN_DISPLAY, IOC_RW, MK_PTR(MK_STRUCT(STRUCT_fb_var_screeninfo)))
+ IOCTL(FBIOGET_CON2FBMAP, IOC_RW, MK_PTR(MK_STRUCT(STRUCT_fb_con2fbmap)))
+ IOCTL(FBIOPUT_CON2FBMAP, IOC_RW, MK_PTR(MK_STRUCT(STRUCT_fb_con2fbmap)))
IOCTL(VT_OPENQRY, IOC_R, MK_PTR(TYPE_INT))
IOCTL(VT_GETSTATE, IOC_R, MK_PTR(MK_STRUCT(STRUCT_vt_stat)))
@@ -332,3 +341,7 @@
IOCTL(VT_WAITACTIVE, 0, TYPE_INT)
IOCTL(VT_LOCKSWITCH, 0, TYPE_INT)
IOCTL(VT_UNLOCKSWITCH, 0, TYPE_INT)
+ IOCTL(VT_GETMODE, IOC_RW, MK_PTR(MK_STRUCT(STRUCT_vt_mode)))
+ IOCTL(VT_SETMODE, IOC_RW, MK_PTR(MK_STRUCT(STRUCT_vt_mode)))
+ IOCTL(VT_RELDISP, 0, TYPE_INT)
+ IOCTL(VT_DISALLOCATE, 0, TYPE_INT)
diff --git a/linux-user/m68k/syscall_nr.h b/linux-user/m68k/syscall_nr.h
index 1c0ba07..4d0937e 100644
--- a/linux-user/m68k/syscall_nr.h
+++ b/linux-user/m68k/syscall_nr.h
@@ -328,3 +328,19 @@
#define TARGET_NR_dup3 326
#define TARGET_NR_pipe2 327
#define TARGET_NR_inotify_init1 328
+#define TARGET_NR_inotify_init1 328
+#define TARGET_NR_preadv 329
+#define TARGET_NR_pwritev 330
+#define TARGET_NR_rt_tgsigqueueinfo 331
+#define TARGET_NR_perf_event_open 332
+#define TARGET_NR_get_thread_area 333
+#define TARGET_NR_set_thread_area 334
+#define TARGET_NR_atomic_cmpxchg_32 335
+#define TARGET_NR_atomic_barrier 336
+#define TARGET_NR_fanotify_init 337
+#define TARGET_NR_fanotify_mark 338
+#define TARGET_NR_prlimit64 339
+#define TARGET_NR_name_to_handle_at 340
+#define TARGET_NR_open_by_handle_at 341
+#define TARGET_NR_clock_adjtime 342
+#define TARGET_NR_syncfs 343
diff --git a/linux-user/main.c b/linux-user/main.c
index 289054b..6a8f4bd 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -1875,7 +1875,7 @@ static const uint8_t mips_syscall_args[] = {
MIPS_SYS(sys_getcwd , 2)
MIPS_SYS(sys_capget , 2)
MIPS_SYS(sys_capset , 2) /* 4205 */
- MIPS_SYS(sys_sigaltstack , 0)
+ MIPS_SYS(sys_sigaltstack , 2)
MIPS_SYS(sys_sendfile , 4)
MIPS_SYS(sys_ni_syscall , 0)
MIPS_SYS(sys_ni_syscall , 0)
@@ -1985,6 +1985,33 @@ static const uint8_t mips_syscall_args[] = {
MIPS_SYS(sys_epoll_pwait, 6)
MIPS_SYS(sys_ioprio_set, 3)
MIPS_SYS(sys_ioprio_get, 2)
+ MIPS_SYS(sys_utimensat, 4)
+ MIPS_SYS(sys_signalfd, 3)
+ MIPS_SYS(sys_ni_syscall, 0) /* was timerfd */
+ MIPS_SYS(sys_eventfd, 1)
+ MIPS_SYS(sys_fallocate, 6) /* 4320 */
+ MIPS_SYS(sys_timerfd_create, 2)
+ MIPS_SYS(sys_timerfd_gettime, 2)
+ MIPS_SYS(sys_timerfd_settime, 4)
+ MIPS_SYS(sys_signalfd4, 4)
+ MIPS_SYS(sys_eventfd2, 2) /* 4325 */
+ MIPS_SYS(sys_epoll_create1, 1)
+ MIPS_SYS(sys_dup3, 3)
+ MIPS_SYS(sys_pipe2, 2)
+ MIPS_SYS(sys_inotify_init1, 1)
+ MIPS_SYS(sys_preadv, 6) /* 4330 */
+ MIPS_SYS(sys_pwritev, 6)
+ MIPS_SYS(sys_rt_tgsigqueueinfo, 4)
+ MIPS_SYS(sys_perf_event_open, 5)
+ MIPS_SYS(sys_accept4, 4)
+ MIPS_SYS(sys_recvmmsg, 5) /* 4335 */
+ MIPS_SYS(sys_fanotify_init, 2)
+ MIPS_SYS(sys_fanotify_mark, 6)
+ MIPS_SYS(sys_prlimit64, 4)
+ MIPS_SYS(sys_name_to_handle_at, 5)
+ MIPS_SYS(sys_open_by_handle_at, 3) /* 4340 */
+ MIPS_SYS(sys_clock_adjtime, 2)
+ MIPS_SYS(sys_syncfs, 1)
};
#undef MIPS_SYS
@@ -2053,7 +2080,7 @@ void cpu_loop(CPUMIPSState *env)
syscall_num = env->active_tc.gpr[2] - 4000;
env->active_tc.PC += 4;
if (syscall_num >= sizeof(mips_syscall_args)) {
- ret = -ENOSYS;
+ ret = -TARGET_ENOSYS;
} else {
int nb_args;
abi_ulong sp_reg;
@@ -2093,6 +2120,8 @@ void cpu_loop(CPUMIPSState *env)
break;
case EXCP_TLBL:
case EXCP_TLBS:
+ case EXCP_AdEL:
+ case EXCP_AdES:
info.si_signo = TARGET_SIGSEGV;
info.si_errno = 0;
/* XXX: check env->error_code */
@@ -3019,11 +3048,6 @@ int main(int argc, char **argv, char **envp)
usage();
}
}
- if (optind >= argc)
- usage();
- filename = argv[optind];
- exec_path = argv[optind];
-
/* init debug */
cpu_set_log_filename(log_file);
if (log_mask) {
@@ -3041,6 +3065,12 @@ int main(int argc, char **argv, char **envp)
cpu_set_log(mask);
}
+ if (optind >= argc) {
+ usage();
+ }
+ filename = argv[optind];
+ exec_path = argv[optind];
+
/* Zero out regs */
memset(regs, 0, sizeof(struct target_pt_regs));
diff --git a/linux-user/microblaze/syscall_nr.h b/linux-user/microblaze/syscall_nr.h
index 3e641cd..f1fe0e7 100644
--- a/linux-user/microblaze/syscall_nr.h
+++ b/linux-user/microblaze/syscall_nr.h
@@ -364,6 +364,16 @@
#define TARGET_NR_sendmsg 360 /* new */
#define TARGET_NR_recvmsg 361 /* new */
#define TARGET_NR_accept04 362 /* new */
-
-#define TARGET_NR_syscalls 363
+#define TARGET_NR_preadv 363 /* new */
+#define TARGET_NR_pwritev 364 /* new */
+#define TARGET_NR_rt_tgsigqueueinfo 365 /* new */
+#define TARGET_NR_perf_event_open 366 /* new */
+#define TARGET_NR_recvmmsg 367 /* new */
+#define TARGET_NR_fanotify_init 368
+#define TARGET_NR_fanotify_mark 369
+#define TARGET_NR_prlimit64 370
+#define TARGET_NR_name_to_handle_at 371
+#define TARGET_NR_open_by_handle_at 372
+#define TARGET_NR_clock_adjtime 373
+#define TARGET_NR_syncfs 374
diff --git a/linux-user/mips/syscall_nr.h b/linux-user/mips/syscall_nr.h
index 0595308..fbdc348 100644
--- a/linux-user/mips/syscall_nr.h
+++ b/linux-user/mips/syscall_nr.h
@@ -332,3 +332,16 @@
#define TARGET_NR_dup3 (TARGET_NR_Linux + 327)
#define TARGET_NR_pipe2 (TARGET_NR_Linux + 328)
#define TARGET_NR_inotify_init1 (TARGET_NR_Linux + 329)
+#define TARGET_NR_preadv (TARGET_NR_Linux + 330)
+#define TARGET_NR_pwritev (TARGET_NR_Linux + 331)
+#define TARGET_NR_rt_tgsigqueueinfo (TARGET_NR_Linux + 332)
+#define TARGET_NR_perf_event_open (TARGET_NR_Linux + 333)
+#define TARGET_NR_accept4 (TARGET_NR_Linux + 334)
+#define TARGET_NR_recvmmsg (TARGET_NR_Linux + 335)
+#define TARGET_NR_fanotify_init (TARGET_NR_Linux + 336)
+#define TARGET_NR_fanotify_mark (TARGET_NR_Linux + 337)
+#define TARGET_NR_prlimit64 (TARGET_NR_Linux + 338)
+#define TARGET_NR_name_to_handle_at (TARGET_NR_Linux + 339)
+#define TARGET_NR_open_by_handle_at (TARGET_NR_Linux + 340)
+#define TARGET_NR_clock_adjtime (TARGET_NR_Linux + 341)
+#define TARGET_NR_syncfs (TARGET_NR_Linux + 342)
diff --git a/linux-user/mips64/syscall_nr.h b/linux-user/mips64/syscall_nr.h
index ee1d134..36d27b5 100644
--- a/linux-user/mips64/syscall_nr.h
+++ b/linux-user/mips64/syscall_nr.h
@@ -291,3 +291,16 @@
#define TARGET_NR_dup3 (TARGET_NR_Linux + 286)
#define TARGET_NR_pipe2 (TARGET_NR_Linux + 287)
#define TARGET_NR_inotify_init1 (TARGET_NR_Linux + 288)
+#define TARGET_NR_preadv (TARGET_NR_Linux + 289)
+#define TARGET_NR_pwritev (TARGET_NR_Linux + 290)
+#define TARGET_NR_rt_tgsigqueueinfo (TARGET_NR_Linux + 291)
+#define TARGET_NR_perf_event_open (TARGET_NR_Linux + 292)
+#define TARGET_NR_accept4 (TARGET_NR_Linux + 293)
+#define TARGET_NR_recvmmsg (TARGET_NR_Linux + 294)
+#define TARGET_NR_fanotify_init (TARGET_NR_Linux + 295)
+#define TARGET_NR_fanotify_mark (TARGET_NR_Linux + 296)
+#define TARGET_NR_prlimit64 (TARGET_NR_Linux + 297)
+#define TARGET_NR_name_to_handle_at (TARGET_NR_Linux + 298)
+#define TARGET_NR_open_by_handle_at (TARGET_NR_Linux + 299)
+#define TARGET_NR_clock_adjtime (TARGET_NR_Linux + 300)
+#define TARGET_NR_syncfs (TARGET_NR_Linux + 301)
diff --git a/linux-user/mipsn32/syscall_nr.h b/linux-user/mipsn32/syscall_nr.h
index 60a99dd..4e1aca3 100644
--- a/linux-user/mipsn32/syscall_nr.h
+++ b/linux-user/mipsn32/syscall_nr.h
@@ -295,3 +295,17 @@
#define TARGET_NR_dup3 (TARGET_NR_Linux + 290)
#define TARGET_NR_pipe2 (TARGET_NR_Linux + 291)
#define TARGET_NR_inotify_init1 (TARGET_NR_Linux + 292)
+#define TARGET_NR_preadv (TARGET_NR_Linux + 293)
+#define TARGET_NR_pwritev (TARGET_NR_Linux + 294)
+#define TARGET_NR_rt_tgsigqueueinfo (TARGET_NR_Linux + 295)
+#define TARGET_NR_perf_event_open (TARGET_NR_Linux + 296)
+#define TARGET_NR_accept4 (TARGET_NR_Linux + 297)
+#define TARGET_NR_recvmmsg (TARGET_NR_Linux + 298)
+#define TARGET_NR_getdents64 (TARGET_NR_Linux + 299)
+#define TARGET_NR_fanotify_init (TARGET_NR_Linux + 300)
+#define TARGET_NR_fanotify_mark (TARGET_NR_Linux + 301)
+#define TARGET_NR_prlimit64 (TARGET_NR_Linux + 302)
+#define TARGET_NR_name_to_handle_at (TARGET_NR_Linux + 303)
+#define TARGET_NR_open_by_handle_at (TARGET_NR_Linux + 304)
+#define TARGET_NR_clock_adjtime (TARGET_NR_Linux + 305)
+#define TARGET_NR_syncfs (TARGET_NR_Linux + 306)
diff --git a/linux-user/ppc/syscall_nr.h b/linux-user/ppc/syscall_nr.h
index cc84a4c..0673b7d 100644
--- a/linux-user/ppc/syscall_nr.h
+++ b/linux-user/ppc/syscall_nr.h
@@ -332,3 +332,33 @@
#define TARGET_NR_dup3 316
#define TARGET_NR_pipe2 317
#define TARGET_NR_inotify_init1 318
+#define TARGET_NR_perf_event_open 319
+#define TARGET_NR_preadv 320
+#define TARGET_NR_pwritev 321
+#define TARGET_NR_rt_tgsigqueueinfo 322
+#define TARGET_NR_fanotify_init 323
+#define TARGET_NR_fanotify_mark 324
+#define TARGET_NR_prlimit64 325
+#define TARGET_NR_socket 326
+#define TARGET_NR_bind 327
+#define TARGET_NR_connect 328
+#define TARGET_NR_listen 329
+#define TARGET_NR_accept 330
+#define TARGET_NR_getsockname 331
+#define TARGET_NR_getpeername 332
+#define TARGET_NR_socketpair 333
+#define TARGET_NR_send 334
+#define TARGET_NR_sendto 335
+#define TARGET_NR_recv 336
+#define TARGET_NR_recvfrom 337
+#define TARGET_NR_shutdown 338
+#define TARGET_NR_setsockopt 339
+#define TARGET_NR_getsockopt 340
+#define TARGET_NR_sendmsg 341
+#define TARGET_NR_recvmsg 342
+#define TARGET_NR_recvmmsg 343
+#define TARGET_NR_accept4 344
+#define TARGET_NR_name_to_handle_at 345
+#define TARGET_NR_open_by_handle_at 346
+#define TARGET_NR_clock_adjtime 347
+#define TARGET_NR_syncfs 348
diff --git a/linux-user/s390x/syscall_nr.h b/linux-user/s390x/syscall_nr.h
index 7cc6db2..d4529ac 100644
--- a/linux-user/s390x/syscall_nr.h
+++ b/linux-user/s390x/syscall_nr.h
@@ -254,8 +254,17 @@
#define TARGET_NR_pipe2 325
#define TARGET_NR_dup3 326
#define TARGET_NR_epoll_create1 327
-#undef NR_syscalls
-#define NR_syscalls 328
+#define TARGET_NR_preadv 328
+#define TARGET_NR_pwritev 329
+#define TARGET_NR_rt_tgsigqueueinfo 330
+#define TARGET_NR_perf_event_open 331
+#define TARGET_NR_fanotify_init 332
+#define TARGET_NR_fanotify_mark 333
+#define TARGET_NR_prlimit64 334
+#define TARGET_NR_name_to_handle_at 335
+#define TARGET_NR_open_by_handle_at 336
+#define TARGET_NR_clock_adjtime 337
+#define TARGET_NR_syncfs 338
/*
* There are some system calls that are not present on 64 bit, some
diff --git a/linux-user/sh4/syscall_nr.h b/linux-user/sh4/syscall_nr.h
index 262b236..365db58 100644
--- a/linux-user/sh4/syscall_nr.h
+++ b/linux-user/sh4/syscall_nr.h
@@ -125,7 +125,7 @@
#define TARGET_NR_clone 120
#define TARGET_NR_setdomainname 121
#define TARGET_NR_uname 122
-#define TARGET_NR_modify_ldt 123
+#define TARGET_NR_cacheflush 123
#define TARGET_NR_adjtimex 124
#define TARGET_NR_mprotect 125
#define TARGET_NR_sigprocmask 126
@@ -334,3 +334,35 @@
#define TARGET_NR_dup3 330
#define TARGET_NR_pipe2 331
#define TARGET_NR_inotify_init1 332
+#define TARGET_NR_preadv 333
+#define TARGET_NR_pwritev 334
+#define TARGET_NR_rt_tgsigqueueinfo 335
+#define TARGET_NR_perf_event_open 336
+#define TARGET_NR_fanotify_init 337
+#define TARGET_NR_fanotify_mark 338
+#define TARGET_NR_prlimit64 339
+
+/* Non-multiplexed socket family */
+#define TARGET_NR_socket 340
+#define TARGET_NR_bind 341
+#define TARGET_NR_connect 342
+#define TARGET_NR_listen 343
+#define TARGET_NR_accept 344
+#define TARGET_NR_getsockname 345
+#define TARGET_NR_getpeername 346
+#define TARGET_NR_socketpair 347
+#define TARGET_NR_send 348
+#define TARGET_NR_sendto 349
+#define TARGET_NR_recv 350
+#define TARGET_NR_recvfrom 351
+#define TARGET_NR_shutdown 352
+#define TARGET_NR_setsockopt 353
+#define TARGET_NR_getsockopt 354
+#define TARGET_NR_sendmsg 355
+#define TARGET_NR_recvmsg 356
+#define TARGET_NR_recvmmsg 357
+#define TARGET_NR_accept4 358
+#define TARGET_NR_name_to_handle_at 359
+#define TARGET_NR_open_by_handle_at 360
+#define TARGET_NR_clock_adjtime 361
+#define TARGET_NR_syncfs 362
diff --git a/linux-user/signal.c b/linux-user/signal.c
index 7d168e1..07ad07a 100644
--- a/linux-user/signal.c
+++ b/linux-user/signal.c
@@ -3662,11 +3662,11 @@ typedef struct {
} sigframe;
struct target_ucontext {
- target_ulong uc_flags;
- struct target_ucontext *uc_link;
- target_stack_t uc_stack;
- target_sigregs uc_mcontext;
- target_sigset_t uc_sigmask; /* mask last for extensibility */
+ target_ulong tuc_flags;
+ struct target_ucontext *tuc_link;
+ target_stack_t tuc_stack;
+ target_sigregs tuc_mcontext;
+ target_sigset_t tuc_sigmask; /* mask last for extensibility */
};
typedef struct {
@@ -3814,16 +3814,16 @@ static void setup_rt_frame(int sig, struct target_sigaction *ka,
}
/* Create the ucontext. */
- __put_user(0, &frame->uc.uc_flags);
- __put_user((abi_ulong)0, (abi_ulong *)&frame->uc.uc_link);
- __put_user(target_sigaltstack_used.ss_sp, &frame->uc.uc_stack.ss_sp);
+ __put_user(0, &frame->uc.tuc_flags);
+ __put_user((abi_ulong)0, (abi_ulong *)&frame->uc.tuc_link);
+ __put_user(target_sigaltstack_used.ss_sp, &frame->uc.tuc_stack.ss_sp);
__put_user(sas_ss_flags(get_sp_from_cpustate(env)),
- &frame->uc.uc_stack.ss_flags);
- __put_user(target_sigaltstack_used.ss_size, &frame->uc.uc_stack.ss_size);
- save_sigregs(env, &frame->uc.uc_mcontext);
+ &frame->uc.tuc_stack.ss_flags);
+ __put_user(target_sigaltstack_used.ss_size, &frame->uc.tuc_stack.ss_size);
+ save_sigregs(env, &frame->uc.tuc_mcontext);
for (i = 0; i < TARGET_NSIG_WORDS; i++) {
__put_user((abi_ulong)set->sig[i],
- (abi_ulong *)&frame->uc.uc_sigmask.sig[i]);
+ (abi_ulong *)&frame->uc.tuc_sigmask.sig[i]);
}
/* Set up to return from userspace. If provided, use a stub
@@ -3928,15 +3928,15 @@ long do_rt_sigreturn(CPUState *env)
if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) {
goto badframe;
}
- target_to_host_sigset(&set, &frame->uc.uc_sigmask);
+ target_to_host_sigset(&set, &frame->uc.tuc_sigmask);
sigprocmask(SIG_SETMASK, &set, NULL); /* ~_BLOCKABLE? */
- if (restore_sigregs(env, &frame->uc.uc_mcontext)) {
+ if (restore_sigregs(env, &frame->uc.tuc_mcontext)) {
goto badframe;
}
- if (do_sigaltstack(frame_addr + offsetof(rt_sigframe, uc.uc_stack), 0,
+ if (do_sigaltstack(frame_addr + offsetof(rt_sigframe, uc.tuc_stack), 0,
get_sp_from_cpustate(env)) == -EFAULT) {
goto badframe;
}
diff --git a/linux-user/sparc/syscall_nr.h b/linux-user/sparc/syscall_nr.h
index 5d1ac21..be503f2 100644
--- a/linux-user/sparc/syscall_nr.h
+++ b/linux-user/sparc/syscall_nr.h
@@ -285,3 +285,15 @@
#define TARGET_NR_pipe2 321
#define TARGET_NR_inotify_init1 322
#define TARGET_NR_accept4 323
+#define TARGET_NR_preadv 324
+#define TARGET_NR_pwritev 325
+#define TARGET_NR_rt_tgsigqueueinfo 326
+#define TARGET_NR_perf_event_open 327
+#define TARGET_NR_recvmmsg 328
+#define TARGET_NR_fanotify_init 329
+#define TARGET_NR_fanotify_mark 330
+#define TARGET_NR_prlimit64 331
+#define TARGET_NR_name_to_handle_at 332
+#define TARGET_NR_open_by_handle_at 333
+#define TARGET_NR_clock_adjtime 334
+#define TARGET_NR_syncfs 335
diff --git a/linux-user/sparc64/syscall_nr.h b/linux-user/sparc64/syscall_nr.h
index bdca2a7..70988b2 100644
--- a/linux-user/sparc64/syscall_nr.h
+++ b/linux-user/sparc64/syscall_nr.h
@@ -322,3 +322,15 @@
#define TARGET_NR_pipe2 321
#define TARGET_NR_inotify_init1 322
#define TARGET_NR_accept4 323
+#define TARGET_NR_preadv 324
+#define TARGET_NR_pwritev 325
+#define TARGET_NR_rt_tgsigqueueinfo 326
+#define TARGET_NR_perf_event_open 327
+#define TARGET_NR_recvmmsg 328
+#define TARGET_NR_fanotify_init 329
+#define TARGET_NR_fanotify_mark 330
+#define TARGET_NR_prlimit64 331
+#define TARGET_NR_name_to_handle_at 332
+#define TARGET_NR_open_by_handle_at 333
+#define TARGET_NR_clock_adjtime 334
+#define TARGET_NR_syncfs 335
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index fed7a8f..73f9baa 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -559,12 +559,38 @@ _syscall6(int, sys_pselect6, int, nfds, fd_set *, readfds, fd_set *, writefds,
fd_set *, exceptfds, struct timespec *, timeout, void *, sig);
#endif
+#if defined(TARGET_NR_prlimit64)
+#ifndef __NR_prlimit64
+# define __NR_prlimit64 -1
+#endif
+#define __NR_sys_prlimit64 __NR_prlimit64
+/* The glibc rlimit structure may not be that used by the underlying syscall */
+struct host_rlimit64 {
+ uint64_t rlim_cur;
+ uint64_t rlim_max;
+};
+_syscall4(int, sys_prlimit64, pid_t, pid, int, resource,
+ const struct host_rlimit64 *, new_limit,
+ struct host_rlimit64 *, old_limit)
+#endif
+
extern int personality(int);
extern int flock(int, int);
extern int setfsuid(int);
extern int setfsgid(int);
extern int setgroups(int, gid_t *);
+/* ARM EABI and MIPS expect 64bit types aligned even on pairs or registers */
+#ifdef TARGET_ARM
+static inline int regpairs_aligned(void *cpu_env) {
+ return ((((CPUARMState *)cpu_env)->eabi) == 1) ;
+}
+#elif defined(TARGET_MIPS)
+static inline int regpairs_aligned(void *cpu_env) { return 1; }
+#else
+static inline int regpairs_aligned(void *cpu_env) { return 0; }
+#endif
+
#define ERRNO_TABLE_SIZE 1200
/* target_to_host_errno_table[] is initialized from
@@ -919,18 +945,68 @@ static inline abi_long host_to_target_rusage(abi_ulong target_addr,
static inline rlim_t target_to_host_rlim(target_ulong target_rlim)
{
- if (target_rlim == TARGET_RLIM_INFINITY)
- return RLIM_INFINITY;
+ target_ulong target_rlim_swap;
+ rlim_t result;
+
+ target_rlim_swap = tswapl(target_rlim);
+ if (target_rlim_swap == TARGET_RLIM_INFINITY || target_rlim_swap != (rlim_t)target_rlim_swap)
+ result = RLIM_INFINITY;
else
- return tswapl(target_rlim);
+ result = target_rlim_swap;
+
+ return result;
}
static inline target_ulong host_to_target_rlim(rlim_t rlim)
{
+ target_ulong target_rlim_swap;
+ target_ulong result;
+
if (rlim == RLIM_INFINITY || rlim != (target_long)rlim)
- return TARGET_RLIM_INFINITY;
+ target_rlim_swap = TARGET_RLIM_INFINITY;
else
- return tswapl(rlim);
+ target_rlim_swap = rlim;
+ result = tswapl(target_rlim_swap);
+
+ return result;
+}
+
+static inline int target_to_host_resource(int code)
+{
+ switch (code) {
+ case TARGET_RLIMIT_AS:
+ return RLIMIT_AS;
+ case TARGET_RLIMIT_CORE:
+ return RLIMIT_CORE;
+ case TARGET_RLIMIT_CPU:
+ return RLIMIT_CPU;
+ case TARGET_RLIMIT_DATA:
+ return RLIMIT_DATA;
+ case TARGET_RLIMIT_FSIZE:
+ return RLIMIT_FSIZE;
+ case TARGET_RLIMIT_LOCKS:
+ return RLIMIT_LOCKS;
+ case TARGET_RLIMIT_MEMLOCK:
+ return RLIMIT_MEMLOCK;
+ case TARGET_RLIMIT_MSGQUEUE:
+ return RLIMIT_MSGQUEUE;
+ case TARGET_RLIMIT_NICE:
+ return RLIMIT_NICE;
+ case TARGET_RLIMIT_NOFILE:
+ return RLIMIT_NOFILE;
+ case TARGET_RLIMIT_NPROC:
+ return RLIMIT_NPROC;
+ case TARGET_RLIMIT_RSS:
+ return RLIMIT_RSS;
+ case TARGET_RLIMIT_RTPRIO:
+ return RLIMIT_RTPRIO;
+ case TARGET_RLIMIT_SIGPENDING:
+ return RLIMIT_SIGPENDING;
+ case TARGET_RLIMIT_STACK:
+ return RLIMIT_STACK;
+ default:
+ return code;
+ }
}
static inline abi_long copy_from_user_timeval(struct timeval *tv,
@@ -1928,7 +2004,7 @@ static abi_long do_recvfrom(int fd, abi_ulong msg, size_t len, int flags,
ret = get_errno(recvfrom(fd, host_msg, len, flags, addr, &addrlen));
} else {
addr = NULL; /* To keep compiler quiet. */
- ret = get_errno(recv(fd, host_msg, len, flags));
+ ret = get_errno(qemu_recv(fd, host_msg, len, flags));
}
if (!is_error(ret)) {
if (target_addr) {
@@ -4310,13 +4386,10 @@ static inline abi_long target_truncate64(void *cpu_env, const char *arg1,
abi_long arg3,
abi_long arg4)
{
-#ifdef TARGET_ARM
- if (((CPUARMState *)cpu_env)->eabi)
- {
+ if (regpairs_aligned(cpu_env)) {
arg2 = arg3;
arg3 = arg4;
- }
-#endif
+ }
return get_errno(truncate64(arg1, target_offset64(arg2, arg3)));
}
#endif
@@ -4327,13 +4400,10 @@ static inline abi_long target_ftruncate64(void *cpu_env, abi_long arg1,
abi_long arg3,
abi_long arg4)
{
-#ifdef TARGET_ARM
- if (((CPUARMState *)cpu_env)->eabi)
- {
+ if (regpairs_aligned(cpu_env)) {
arg2 = arg3;
arg3 = arg4;
- }
-#endif
+ }
return get_errno(ftruncate64(arg1, target_offset64(arg2, arg3)));
}
#endif
@@ -5543,7 +5613,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
break;
case TARGET_NR_setrlimit:
{
- int resource = arg1;
+ int resource = target_to_host_resource(arg1);
struct target_rlimit *target_rlim;
struct rlimit rlim;
if (!lock_user_struct(VERIFY_READ, target_rlim, arg2, 1))
@@ -5556,7 +5626,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
break;
case TARGET_NR_getrlimit:
{
- int resource = arg1;
+ int resource = target_to_host_resource(arg1);
struct target_rlimit *target_rlim;
struct rlimit rlim;
@@ -5684,6 +5754,11 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
if (arg_sigset) {
sig.set = &set;
+ if (arg_sigsize != sizeof(*target_sigset)) {
+ /* Like the kernel, we enforce correct size sigsets */
+ ret = -TARGET_EINVAL;
+ goto fail;
+ }
target_sigset = lock_user(VERIFY_READ, arg_sigset,
sizeof(*target_sigset), 1);
if (!target_sigset) {
@@ -6787,20 +6862,16 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
#endif
#ifdef TARGET_NR_pread
case TARGET_NR_pread:
-#ifdef TARGET_ARM
- if (((CPUARMState *)cpu_env)->eabi)
+ if (regpairs_aligned(cpu_env))
arg4 = arg5;
-#endif
if (!(p = lock_user(VERIFY_WRITE, arg2, arg3, 0)))
goto efault;
ret = get_errno(pread(arg1, p, arg3, arg4));
unlock_user(p, arg2, ret);
break;
case TARGET_NR_pwrite:
-#ifdef TARGET_ARM
- if (((CPUARMState *)cpu_env)->eabi)
+ if (regpairs_aligned(cpu_env))
arg4 = arg5;
-#endif
if (!(p = lock_user(VERIFY_READ, arg2, arg3, 1)))
goto efault;
ret = get_errno(pwrite(arg1, p, arg3, arg4));
@@ -6860,7 +6931,8 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
case TARGET_NR_ugetrlimit:
{
struct rlimit rlim;
- ret = get_errno(getrlimit(arg1, &rlim));
+ int resource = target_to_host_resource(arg1);
+ ret = get_errno(getrlimit(resource, &rlim));
if (!is_error(ret)) {
struct target_rlimit *target_rlim;
if (!lock_user_struct(VERIFY_WRITE, target_rlim, arg2, 0))
@@ -7550,14 +7622,11 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
#ifdef TARGET_NR_readahead
case TARGET_NR_readahead:
#if TARGET_ABI_BITS == 32
-#ifdef TARGET_ARM
- if (((CPUARMState *)cpu_env)->eabi)
- {
+ if (regpairs_aligned(cpu_env)) {
arg2 = arg3;
arg3 = arg4;
arg4 = arg5;
}
-#endif
ret = get_errno(readahead(arg1, ((off64_t)arg3 << 32) | arg2, arg4));
#else
ret = get_errno(readahead(arg1, arg2, arg3));
@@ -7990,6 +8059,34 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
}
#endif
#endif
+#ifdef TARGET_NR_prlimit64
+ case TARGET_NR_prlimit64:
+ {
+ /* args: pid, resource number, ptr to new rlimit, ptr to old rlimit */
+ struct target_rlimit64 *target_rnew, *target_rold;
+ struct host_rlimit64 rnew, rold, *rnewp = 0;
+ if (arg3) {
+ if (!lock_user_struct(VERIFY_READ, target_rnew, arg3, 1)) {
+ goto efault;
+ }
+ rnew.rlim_cur = tswap64(target_rnew->rlim_cur);
+ rnew.rlim_max = tswap64(target_rnew->rlim_max);
+ unlock_user_struct(target_rnew, arg3, 0);
+ rnewp = &rnew;
+ }
+
+ ret = get_errno(sys_prlimit64(arg1, arg2, rnewp, arg4 ? &rold : 0));
+ if (!is_error(ret) && arg4) {
+ if (!lock_user_struct(VERIFY_WRITE, target_rold, arg4, 1)) {
+ goto efault;
+ }
+ target_rold->rlim_cur = tswap64(rold.rlim_cur);
+ target_rold->rlim_max = tswap64(rold.rlim_max);
+ unlock_user_struct(target_rold, arg4, 1);
+ }
+ break;
+ }
+#endif
default:
unimplemented:
gemu_log("qemu: Unsupported syscall: %d\n", num);
diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h
index 04c268d..a117407 100644
--- a/linux-user/syscall_defs.h
+++ b/linux-user/syscall_defs.h
@@ -693,6 +693,40 @@ struct target_rlimit {
#define TARGET_RLIM_INFINITY ((target_ulong)~0UL)
#endif
+#if defined(TARGET_MIPS)
+#define TARGET_RLIMIT_CPU 0
+#define TARGET_RLIMIT_FSIZE 1
+#define TARGET_RLIMIT_DATA 2
+#define TARGET_RLIMIT_STACK 3
+#define TARGET_RLIMIT_CORE 4
+#define TARGET_RLIMIT_RSS 7
+#define TARGET_RLIMIT_NPROC 8
+#define TARGET_RLIMIT_NOFILE 5
+#define TARGET_RLIMIT_MEMLOCK 9
+#define TARGET_RLIMIT_AS 6
+#define TARGET_RLIMIT_LOCKS 10
+#define TARGET_RLIMIT_SIGPENDING 11
+#define TARGET_RLIMIT_MSGQUEUE 12
+#define TARGET_RLIMIT_NICE 13
+#define TARGET_RLIMIT_RTPRIO 14
+#else
+#define TARGET_RLIMIT_CPU 0
+#define TARGET_RLIMIT_FSIZE 1
+#define TARGET_RLIMIT_DATA 2
+#define TARGET_RLIMIT_STACK 3
+#define TARGET_RLIMIT_CORE 4
+#define TARGET_RLIMIT_RSS 5
+#define TARGET_RLIMIT_NPROC 6
+#define TARGET_RLIMIT_NOFILE 7
+#define TARGET_RLIMIT_MEMLOCK 8
+#define TARGET_RLIMIT_AS 9
+#define TARGET_RLIMIT_LOCKS 10
+#define TARGET_RLIMIT_SIGPENDING 11
+#define TARGET_RLIMIT_MSGQUEUE 12
+#define TARGET_RLIMIT_NICE 13
+#define TARGET_RLIMIT_RTPRIO 14
+#endif
+
struct target_pollfd {
int fd; /* file descriptor */
short events; /* requested events */
@@ -708,6 +742,10 @@ struct target_pollfd {
#define TARGET_KDSKBMODE 0x4b45
#define TARGET_KDGKBENT 0x4B46 /* gets one entry in translation table */
#define TARGET_KDGKBSENT 0x4B48 /* gets one function key string entry */
+#define TARGET_KDGKBLED 0x4B64 /* get led flags (not lights) */
+#define TARGET_KDSKBLED 0x4B65 /* set led flags (not lights) */
+#define TARGET_KDGETLED 0x4B31 /* return current led state */
+#define TARGET_KDSETLED 0x4B32 /* set led state [lights, not flags] */
#define TARGET_SIOCATMARK 0x8905
@@ -928,6 +966,11 @@ struct target_pollfd {
#define TARGET_FBIOGET_VSCREENINFO 0x4600
#define TARGET_FBIOPUT_VSCREENINFO 0x4601
#define TARGET_FBIOGET_FSCREENINFO 0x4602
+#define TARGET_FBIOGETCMAP 0x4604
+#define TARGET_FBIOPUTCMAP 0x4605
+#define TARGET_FBIOPAN_DISPLAY 0x4606
+#define TARGET_FBIOGET_CON2FBMAP 0x460F
+#define TARGET_FBIOPUT_CON2FBMAP 0x4610
/* vt ioctls */
#define TARGET_VT_OPENQRY 0x5600
@@ -936,6 +979,10 @@ struct target_pollfd {
#define TARGET_VT_WAITACTIVE 0x5607
#define TARGET_VT_LOCKSWITCH 0x560b
#define TARGET_VT_UNLOCKSWITCH 0x560c
+#define TARGET_VT_GETMODE 0x5601
+#define TARGET_VT_SETMODE 0x5602
+#define TARGET_VT_RELDISP 0x5605
+#define TARGET_VT_DISALLOCATE 0x5608
/* from asm/termbits.h */
@@ -2280,3 +2327,7 @@ struct target_epoll_event {
target_epoll_data_t data;
};
#endif
+struct target_rlimit64 {
+ uint64_t rlim_cur;
+ uint64_t rlim_max;
+};
diff --git a/linux-user/syscall_types.h b/linux-user/syscall_types.h
index 0e67cd8..c370125 100644
--- a/linux-user/syscall_types.h
+++ b/linux-user/syscall_types.h
@@ -161,11 +161,31 @@ STRUCT(fb_var_screeninfo,
TYPE_INT, /* rotate */
MK_ARRAY(TYPE_INT, 5)) /* reserved */
+STRUCT(fb_cmap,
+ TYPE_INT, /* start */
+ TYPE_INT, /* len */
+ TYPE_PTRVOID, /* red */
+ TYPE_PTRVOID, /* green */
+ TYPE_PTRVOID, /* blue */
+ TYPE_PTRVOID) /* transp */
+
+STRUCT(fb_con2fbmap,
+ TYPE_INT, /* console */
+ TYPE_INT) /* framebuffer */
+
+
STRUCT(vt_stat,
TYPE_SHORT, /* v_active */
TYPE_SHORT, /* v_signal */
TYPE_SHORT) /* v_state */
+STRUCT(vt_mode,
+ TYPE_CHAR, /* mode */
+ TYPE_CHAR, /* waitv */
+ TYPE_SHORT, /* relsig */
+ TYPE_SHORT, /* acqsig */
+ TYPE_SHORT) /* frsig */
+
STRUCT(fiemap_extent,
TYPE_ULONGLONG, /* fe_logical */
TYPE_ULONGLONG, /* fe_physical */
diff --git a/linux-user/x86_64/syscall_nr.h b/linux-user/x86_64/syscall_nr.h
index 568a901..947e961 100644
--- a/linux-user/x86_64/syscall_nr.h
+++ b/linux-user/x86_64/syscall_nr.h
@@ -293,3 +293,15 @@
#define TARGET_NR_dup3 292
#define TARGET_NR_pipe2 293
#define TARGET_NR_inotify_init1 294
+#define TARGET_NR_preadv 295
+#define TARGET_NR_pwritev 296
+#define TARGET_NR_rt_tgsigqueueinfo 297
+#define TARGET_NR_perf_event_open 298
+#define TARGET_NR_recvmmsg 299
+#define TARGET_NR_fanotify_init 300
+#define TARGET_NR_fanotify_mark 301
+#define TARGET_NR_prlimit64 302
+#define TARGET_NR_name_to_handle_at 303
+#define TARGET_NR_open_by_handle_at 304
+#define TARGET_NR_clock_adjtime 305
+#define TARGET_NR_syncfs 306
diff --git a/memory.c b/memory.c
new file mode 100644
index 0000000..5c6e63d
--- /dev/null
+++ b/memory.c
@@ -0,0 +1,1141 @@
+/*
+ * Physical memory management
+ *
+ * Copyright 2011 Red Hat, Inc. and/or its affiliates
+ *
+ * Authors:
+ * Avi Kivity <avi@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 "memory.h"
+#include "exec-memory.h"
+#include "ioport.h"
+#include "bitops.h"
+#include "kvm.h"
+#include <assert.h>
+
+unsigned memory_region_transaction_depth = 0;
+
+typedef struct AddrRange AddrRange;
+
+struct AddrRange {
+ uint64_t start;
+ uint64_t size;
+};
+
+static AddrRange addrrange_make(uint64_t start, uint64_t size)
+{
+ return (AddrRange) { start, size };
+}
+
+static bool addrrange_equal(AddrRange r1, AddrRange r2)
+{
+ return r1.start == r2.start && r1.size == r2.size;
+}
+
+static uint64_t addrrange_end(AddrRange r)
+{
+ return r.start + r.size;
+}
+
+static AddrRange addrrange_shift(AddrRange range, int64_t delta)
+{
+ range.start += delta;
+ return range;
+}
+
+static bool addrrange_intersects(AddrRange r1, AddrRange r2)
+{
+ return (r1.start >= r2.start && r1.start < r2.start + r2.size)
+ || (r2.start >= r1.start && r2.start < r1.start + r1.size);
+}
+
+static AddrRange addrrange_intersection(AddrRange r1, AddrRange r2)
+{
+ uint64_t start = MAX(r1.start, r2.start);
+ /* off-by-one arithmetic to prevent overflow */
+ uint64_t end = MIN(addrrange_end(r1) - 1, addrrange_end(r2) - 1);
+ return addrrange_make(start, end - start + 1);
+}
+
+struct CoalescedMemoryRange {
+ AddrRange addr;
+ QTAILQ_ENTRY(CoalescedMemoryRange) link;
+};
+
+struct MemoryRegionIoeventfd {
+ AddrRange addr;
+ bool match_data;
+ uint64_t data;
+ int fd;
+};
+
+static bool memory_region_ioeventfd_before(MemoryRegionIoeventfd a,
+ MemoryRegionIoeventfd b)
+{
+ if (a.addr.start < b.addr.start) {
+ return true;
+ } else if (a.addr.start > b.addr.start) {
+ return false;
+ } else if (a.addr.size < b.addr.size) {
+ return true;
+ } else if (a.addr.size > b.addr.size) {
+ return false;
+ } else if (a.match_data < b.match_data) {
+ return true;
+ } else if (a.match_data > b.match_data) {
+ return false;
+ } else if (a.match_data) {
+ if (a.data < b.data) {
+ return true;
+ } else if (a.data > b.data) {
+ return false;
+ }
+ }
+ if (a.fd < b.fd) {
+ return true;
+ } else if (a.fd > b.fd) {
+ return false;
+ }
+ return false;
+}
+
+static bool memory_region_ioeventfd_equal(MemoryRegionIoeventfd a,
+ MemoryRegionIoeventfd b)
+{
+ return !memory_region_ioeventfd_before(a, b)
+ && !memory_region_ioeventfd_before(b, a);
+}
+
+typedef struct FlatRange FlatRange;
+typedef struct FlatView FlatView;
+
+/* Range of memory in the global map. Addresses are absolute. */
+struct FlatRange {
+ MemoryRegion *mr;
+ target_phys_addr_t offset_in_region;
+ AddrRange addr;
+ uint8_t dirty_log_mask;
+};
+
+/* Flattened global view of current active memory hierarchy. Kept in sorted
+ * order.
+ */
+struct FlatView {
+ FlatRange *ranges;
+ unsigned nr;
+ unsigned nr_allocated;
+};
+
+typedef struct AddressSpace AddressSpace;
+typedef struct AddressSpaceOps AddressSpaceOps;
+
+/* A system address space - I/O, memory, etc. */
+struct AddressSpace {
+ const AddressSpaceOps *ops;
+ MemoryRegion *root;
+ FlatView current_map;
+ int ioeventfd_nb;
+ MemoryRegionIoeventfd *ioeventfds;
+};
+
+struct AddressSpaceOps {
+ void (*range_add)(AddressSpace *as, FlatRange *fr);
+ void (*range_del)(AddressSpace *as, FlatRange *fr);
+ void (*log_start)(AddressSpace *as, FlatRange *fr);
+ void (*log_stop)(AddressSpace *as, FlatRange *fr);
+ void (*ioeventfd_add)(AddressSpace *as, MemoryRegionIoeventfd *fd);
+ void (*ioeventfd_del)(AddressSpace *as, MemoryRegionIoeventfd *fd);
+};
+
+#define FOR_EACH_FLAT_RANGE(var, view) \
+ for (var = (view)->ranges; var < (view)->ranges + (view)->nr; ++var)
+
+static bool flatrange_equal(FlatRange *a, FlatRange *b)
+{
+ return a->mr == b->mr
+ && addrrange_equal(a->addr, b->addr)
+ && a->offset_in_region == b->offset_in_region;
+}
+
+static void flatview_init(FlatView *view)
+{
+ view->ranges = NULL;
+ view->nr = 0;
+ view->nr_allocated = 0;
+}
+
+/* Insert a range into a given position. Caller is responsible for maintaining
+ * sorting order.
+ */
+static void flatview_insert(FlatView *view, unsigned pos, FlatRange *range)
+{
+ if (view->nr == view->nr_allocated) {
+ view->nr_allocated = MAX(2 * view->nr, 10);
+ view->ranges = qemu_realloc(view->ranges,
+ view->nr_allocated * sizeof(*view->ranges));
+ }
+ memmove(view->ranges + pos + 1, view->ranges + pos,
+ (view->nr - pos) * sizeof(FlatRange));
+ view->ranges[pos] = *range;
+ ++view->nr;
+}
+
+static void flatview_destroy(FlatView *view)
+{
+ qemu_free(view->ranges);
+}
+
+static bool can_merge(FlatRange *r1, FlatRange *r2)
+{
+ return addrrange_end(r1->addr) == r2->addr.start
+ && r1->mr == r2->mr
+ && r1->offset_in_region + r1->addr.size == r2->offset_in_region
+ && r1->dirty_log_mask == r2->dirty_log_mask;
+}
+
+/* Attempt to simplify a view by merging ajacent ranges */
+static void flatview_simplify(FlatView *view)
+{
+ unsigned i, j;
+
+ i = 0;
+ while (i < view->nr) {
+ j = i + 1;
+ while (j < view->nr
+ && can_merge(&view->ranges[j-1], &view->ranges[j])) {
+ view->ranges[i].addr.size += view->ranges[j].addr.size;
+ ++j;
+ }
+ ++i;
+ memmove(&view->ranges[i], &view->ranges[j],
+ (view->nr - j) * sizeof(view->ranges[j]));
+ view->nr -= j - i;
+ }
+}
+
+static void memory_region_prepare_ram_addr(MemoryRegion *mr);
+
+static void as_memory_range_add(AddressSpace *as, FlatRange *fr)
+{
+ ram_addr_t phys_offset, region_offset;
+
+ memory_region_prepare_ram_addr(fr->mr);
+
+ phys_offset = fr->mr->ram_addr;
+ region_offset = fr->offset_in_region;
+ /* cpu_register_physical_memory_log() wants region_offset for
+ * mmio, but prefers offseting phys_offset for RAM. Humour it.
+ */
+ if ((phys_offset & ~TARGET_PAGE_MASK) <= IO_MEM_ROM) {
+ phys_offset += region_offset;
+ region_offset = 0;
+ }
+
+ cpu_register_physical_memory_log(fr->addr.start,
+ fr->addr.size,
+ phys_offset,
+ region_offset,
+ fr->dirty_log_mask);
+}
+
+static void as_memory_range_del(AddressSpace *as, FlatRange *fr)
+{
+ cpu_register_physical_memory(fr->addr.start, fr->addr.size,
+ IO_MEM_UNASSIGNED);
+}
+
+static void as_memory_log_start(AddressSpace *as, FlatRange *fr)
+{
+ cpu_physical_log_start(fr->addr.start, fr->addr.size);
+}
+
+static void as_memory_log_stop(AddressSpace *as, FlatRange *fr)
+{
+ cpu_physical_log_stop(fr->addr.start, fr->addr.size);
+}
+
+static void as_memory_ioeventfd_add(AddressSpace *as, MemoryRegionIoeventfd *fd)
+{
+ int r;
+
+ assert(fd->match_data && fd->addr.size == 4);
+
+ r = kvm_set_ioeventfd_mmio_long(fd->fd, fd->addr.start, fd->data, true);
+ if (r < 0) {
+ abort();
+ }
+}
+
+static void as_memory_ioeventfd_del(AddressSpace *as, MemoryRegionIoeventfd *fd)
+{
+ int r;
+
+ r = kvm_set_ioeventfd_mmio_long(fd->fd, fd->addr.start, fd->data, false);
+ if (r < 0) {
+ abort();
+ }
+}
+
+static const AddressSpaceOps address_space_ops_memory = {
+ .range_add = as_memory_range_add,
+ .range_del = as_memory_range_del,
+ .log_start = as_memory_log_start,
+ .log_stop = as_memory_log_stop,
+ .ioeventfd_add = as_memory_ioeventfd_add,
+ .ioeventfd_del = as_memory_ioeventfd_del,
+};
+
+static AddressSpace address_space_memory = {
+ .ops = &address_space_ops_memory,
+};
+
+static const MemoryRegionPortio *find_portio(MemoryRegion *mr, uint64_t offset,
+ unsigned width, bool write)
+{
+ const MemoryRegionPortio *mrp;
+
+ for (mrp = mr->ops->old_portio; mrp->size; ++mrp) {
+ if (offset >= mrp->offset && offset < mrp->offset + mrp->len
+ && width == mrp->size
+ && (write ? (bool)mrp->write : (bool)mrp->read)) {
+ return mrp;
+ }
+ }
+ return NULL;
+}
+
+static void memory_region_iorange_read(IORange *iorange,
+ uint64_t offset,
+ unsigned width,
+ uint64_t *data)
+{
+ MemoryRegion *mr = container_of(iorange, MemoryRegion, iorange);
+
+ if (mr->ops->old_portio) {
+ const MemoryRegionPortio *mrp = find_portio(mr, offset, width, false);
+
+ *data = ((uint64_t)1 << (width * 8)) - 1;
+ if (mrp) {
+ *data = mrp->read(mr->opaque, offset - mrp->offset);
+ }
+ return;
+ }
+ *data = mr->ops->read(mr->opaque, offset, width);
+}
+
+static void memory_region_iorange_write(IORange *iorange,
+ uint64_t offset,
+ unsigned width,
+ uint64_t data)
+{
+ MemoryRegion *mr = container_of(iorange, MemoryRegion, iorange);
+
+ if (mr->ops->old_portio) {
+ const MemoryRegionPortio *mrp = find_portio(mr, offset, width, true);
+
+ if (mrp) {
+ mrp->write(mr->opaque, offset - mrp->offset, data);
+ }
+ return;
+ }
+ mr->ops->write(mr->opaque, offset, data, width);
+}
+
+static const IORangeOps memory_region_iorange_ops = {
+ .read = memory_region_iorange_read,
+ .write = memory_region_iorange_write,
+};
+
+static void as_io_range_add(AddressSpace *as, FlatRange *fr)
+{
+ iorange_init(&fr->mr->iorange, &memory_region_iorange_ops,
+ fr->addr.start,fr->addr.size);
+ ioport_register(&fr->mr->iorange);
+}
+
+static void as_io_range_del(AddressSpace *as, FlatRange *fr)
+{
+ isa_unassign_ioport(fr->addr.start, fr->addr.size);
+}
+
+static void as_io_ioeventfd_add(AddressSpace *as, MemoryRegionIoeventfd *fd)
+{
+ int r;
+
+ assert(fd->match_data && fd->addr.size == 2);
+
+ r = kvm_set_ioeventfd_pio_word(fd->fd, fd->addr.start, fd->data, true);
+ if (r < 0) {
+ abort();
+ }
+}
+
+static void as_io_ioeventfd_del(AddressSpace *as, MemoryRegionIoeventfd *fd)
+{
+ int r;
+
+ r = kvm_set_ioeventfd_pio_word(fd->fd, fd->addr.start, fd->data, false);
+ if (r < 0) {
+ abort();
+ }
+}
+
+static const AddressSpaceOps address_space_ops_io = {
+ .range_add = as_io_range_add,
+ .range_del = as_io_range_del,
+ .ioeventfd_add = as_io_ioeventfd_add,
+ .ioeventfd_del = as_io_ioeventfd_del,
+};
+
+static AddressSpace address_space_io = {
+ .ops = &address_space_ops_io,
+};
+
+/* Render a memory region into the global view. Ranges in @view obscure
+ * ranges in @mr.
+ */
+static void render_memory_region(FlatView *view,
+ MemoryRegion *mr,
+ target_phys_addr_t base,
+ AddrRange clip)
+{
+ MemoryRegion *subregion;
+ unsigned i;
+ target_phys_addr_t offset_in_region;
+ uint64_t remain;
+ uint64_t now;
+ FlatRange fr;
+ AddrRange tmp;
+
+ base += mr->addr;
+
+ tmp = addrrange_make(base, mr->size);
+
+ if (!addrrange_intersects(tmp, clip)) {
+ return;
+ }
+
+ clip = addrrange_intersection(tmp, clip);
+
+ if (mr->alias) {
+ base -= mr->alias->addr;
+ base -= mr->alias_offset;
+ render_memory_region(view, mr->alias, base, clip);
+ return;
+ }
+
+ /* Render subregions in priority order. */
+ QTAILQ_FOREACH(subregion, &mr->subregions, subregions_link) {
+ render_memory_region(view, subregion, base, clip);
+ }
+
+ if (!mr->terminates) {
+ return;
+ }
+
+ offset_in_region = clip.start - base;
+ base = clip.start;
+ remain = clip.size;
+
+ /* Render the region itself into any gaps left by the current view. */
+ for (i = 0; i < view->nr && remain; ++i) {
+ if (base >= addrrange_end(view->ranges[i].addr)) {
+ continue;
+ }
+ if (base < view->ranges[i].addr.start) {
+ now = MIN(remain, view->ranges[i].addr.start - base);
+ fr.mr = mr;
+ fr.offset_in_region = offset_in_region;
+ fr.addr = addrrange_make(base, now);
+ fr.dirty_log_mask = mr->dirty_log_mask;
+ flatview_insert(view, i, &fr);
+ ++i;
+ base += now;
+ offset_in_region += now;
+ remain -= now;
+ }
+ if (base == view->ranges[i].addr.start) {
+ now = MIN(remain, view->ranges[i].addr.size);
+ base += now;
+ offset_in_region += now;
+ remain -= now;
+ }
+ }
+ if (remain) {
+ fr.mr = mr;
+ fr.offset_in_region = offset_in_region;
+ fr.addr = addrrange_make(base, remain);
+ fr.dirty_log_mask = mr->dirty_log_mask;
+ flatview_insert(view, i, &fr);
+ }
+}
+
+/* Render a memory topology into a list of disjoint absolute ranges. */
+static FlatView generate_memory_topology(MemoryRegion *mr)
+{
+ FlatView view;
+
+ flatview_init(&view);
+
+ render_memory_region(&view, mr, 0, addrrange_make(0, UINT64_MAX));
+ flatview_simplify(&view);
+
+ return view;
+}
+
+static void address_space_add_del_ioeventfds(AddressSpace *as,
+ MemoryRegionIoeventfd *fds_new,
+ unsigned fds_new_nb,
+ MemoryRegionIoeventfd *fds_old,
+ unsigned fds_old_nb)
+{
+ unsigned iold, inew;
+
+ /* Generate a symmetric difference of the old and new fd sets, adding
+ * and deleting as necessary.
+ */
+
+ iold = inew = 0;
+ while (iold < fds_old_nb || inew < fds_new_nb) {
+ if (iold < fds_old_nb
+ && (inew == fds_new_nb
+ || memory_region_ioeventfd_before(fds_old[iold],
+ fds_new[inew]))) {
+ as->ops->ioeventfd_del(as, &fds_old[iold]);
+ ++iold;
+ } else if (inew < fds_new_nb
+ && (iold == fds_old_nb
+ || memory_region_ioeventfd_before(fds_new[inew],
+ fds_old[iold]))) {
+ as->ops->ioeventfd_add(as, &fds_new[inew]);
+ ++inew;
+ } else {
+ ++iold;
+ ++inew;
+ }
+ }
+}
+
+static void address_space_update_ioeventfds(AddressSpace *as)
+{
+ FlatRange *fr;
+ unsigned ioeventfd_nb = 0;
+ MemoryRegionIoeventfd *ioeventfds = NULL;
+ AddrRange tmp;
+ unsigned i;
+
+ FOR_EACH_FLAT_RANGE(fr, &as->current_map) {
+ for (i = 0; i < fr->mr->ioeventfd_nb; ++i) {
+ tmp = addrrange_shift(fr->mr->ioeventfds[i].addr,
+ fr->addr.start - fr->offset_in_region);
+ if (addrrange_intersects(fr->addr, tmp)) {
+ ++ioeventfd_nb;
+ ioeventfds = qemu_realloc(ioeventfds,
+ ioeventfd_nb * sizeof(*ioeventfds));
+ ioeventfds[ioeventfd_nb-1] = fr->mr->ioeventfds[i];
+ ioeventfds[ioeventfd_nb-1].addr = tmp;
+ }
+ }
+ }
+
+ address_space_add_del_ioeventfds(as, ioeventfds, ioeventfd_nb,
+ as->ioeventfds, as->ioeventfd_nb);
+
+ qemu_free(as->ioeventfds);
+ as->ioeventfds = ioeventfds;
+ as->ioeventfd_nb = ioeventfd_nb;
+}
+
+static void address_space_update_topology_pass(AddressSpace *as,
+ FlatView old_view,
+ FlatView new_view,
+ bool adding)
+{
+ unsigned iold, inew;
+ FlatRange *frold, *frnew;
+
+ /* Generate a symmetric difference of the old and new memory maps.
+ * Kill ranges in the old map, and instantiate ranges in the new map.
+ */
+ iold = inew = 0;
+ while (iold < old_view.nr || inew < new_view.nr) {
+ if (iold < old_view.nr) {
+ frold = &old_view.ranges[iold];
+ } else {
+ frold = NULL;
+ }
+ if (inew < new_view.nr) {
+ frnew = &new_view.ranges[inew];
+ } else {
+ frnew = NULL;
+ }
+
+ if (frold
+ && (!frnew
+ || frold->addr.start < frnew->addr.start
+ || (frold->addr.start == frnew->addr.start
+ && !flatrange_equal(frold, frnew)))) {
+ /* In old, but (not in new, or in new but attributes changed). */
+
+ if (!adding) {
+ as->ops->range_del(as, frold);
+ }
+
+ ++iold;
+ } else if (frold && frnew && flatrange_equal(frold, frnew)) {
+ /* In both (logging may have changed) */
+
+ if (adding) {
+ if (frold->dirty_log_mask && !frnew->dirty_log_mask) {
+ as->ops->log_stop(as, frnew);
+ } else if (frnew->dirty_log_mask && !frold->dirty_log_mask) {
+ as->ops->log_start(as, frnew);
+ }
+ }
+
+ ++iold;
+ ++inew;
+ } else {
+ /* In new */
+
+ if (adding) {
+ as->ops->range_add(as, frnew);
+ }
+
+ ++inew;
+ }
+ }
+}
+
+
+static void address_space_update_topology(AddressSpace *as)
+{
+ FlatView old_view = as->current_map;
+ FlatView new_view = generate_memory_topology(as->root);
+
+ address_space_update_topology_pass(as, old_view, new_view, false);
+ address_space_update_topology_pass(as, old_view, new_view, true);
+
+ as->current_map = new_view;
+ flatview_destroy(&old_view);
+ address_space_update_ioeventfds(as);
+}
+
+static void memory_region_update_topology(void)
+{
+ if (memory_region_transaction_depth) {
+ return;
+ }
+
+ if (address_space_memory.root) {
+ address_space_update_topology(&address_space_memory);
+ }
+ if (address_space_io.root) {
+ address_space_update_topology(&address_space_io);
+ }
+}
+
+void memory_region_transaction_begin(void)
+{
+ ++memory_region_transaction_depth;
+}
+
+void memory_region_transaction_commit(void)
+{
+ assert(memory_region_transaction_depth);
+ --memory_region_transaction_depth;
+ memory_region_update_topology();
+}
+
+void memory_region_init(MemoryRegion *mr,
+ const char *name,
+ uint64_t size)
+{
+ mr->ops = NULL;
+ mr->parent = NULL;
+ mr->size = size;
+ mr->addr = 0;
+ mr->offset = 0;
+ mr->terminates = false;
+ mr->priority = 0;
+ mr->may_overlap = false;
+ mr->alias = NULL;
+ QTAILQ_INIT(&mr->subregions);
+ memset(&mr->subregions_link, 0, sizeof mr->subregions_link);
+ QTAILQ_INIT(&mr->coalesced);
+ mr->name = qemu_strdup(name);
+ mr->dirty_log_mask = 0;
+ mr->ioeventfd_nb = 0;
+ mr->ioeventfds = NULL;
+}
+
+static bool memory_region_access_valid(MemoryRegion *mr,
+ target_phys_addr_t addr,
+ unsigned size)
+{
+ if (!mr->ops->valid.unaligned && (addr & (size - 1))) {
+ return false;
+ }
+
+ /* Treat zero as compatibility all valid */
+ if (!mr->ops->valid.max_access_size) {
+ return true;
+ }
+
+ if (size > mr->ops->valid.max_access_size
+ || size < mr->ops->valid.min_access_size) {
+ return false;
+ }
+ return true;
+}
+
+static uint32_t memory_region_read_thunk_n(void *_mr,
+ target_phys_addr_t addr,
+ unsigned size)
+{
+ MemoryRegion *mr = _mr;
+ unsigned access_size, access_size_min, access_size_max;
+ uint64_t access_mask;
+ uint32_t data = 0, tmp;
+ unsigned i;
+
+ if (!memory_region_access_valid(mr, addr, size)) {
+ return -1U; /* FIXME: better signalling */
+ }
+
+ if (!mr->ops->read) {
+ return mr->ops->old_mmio.read[bitops_ffsl(size)](mr->opaque, addr);
+ }
+
+ /* FIXME: support unaligned access */
+
+ access_size_min = mr->ops->impl.min_access_size;
+ if (!access_size_min) {
+ access_size_min = 1;
+ }
+ access_size_max = mr->ops->impl.max_access_size;
+ if (!access_size_max) {
+ access_size_max = 4;
+ }
+ access_size = MAX(MIN(size, access_size_max), access_size_min);
+ access_mask = -1ULL >> (64 - access_size * 8);
+ addr += mr->offset;
+ for (i = 0; i < size; i += access_size) {
+ /* FIXME: big-endian support */
+ tmp = mr->ops->read(mr->opaque, addr + i, access_size);
+ data |= (tmp & access_mask) << (i * 8);
+ }
+
+ return data;
+}
+
+static void memory_region_write_thunk_n(void *_mr,
+ target_phys_addr_t addr,
+ unsigned size,
+ uint64_t data)
+{
+ MemoryRegion *mr = _mr;
+ unsigned access_size, access_size_min, access_size_max;
+ uint64_t access_mask;
+ unsigned i;
+
+ if (!memory_region_access_valid(mr, addr, size)) {
+ return; /* FIXME: better signalling */
+ }
+
+ if (!mr->ops->write) {
+ mr->ops->old_mmio.write[bitops_ffsl(size)](mr->opaque, addr, data);
+ return;
+ }
+
+ /* FIXME: support unaligned access */
+
+ access_size_min = mr->ops->impl.min_access_size;
+ if (!access_size_min) {
+ access_size_min = 1;
+ }
+ access_size_max = mr->ops->impl.max_access_size;
+ if (!access_size_max) {
+ access_size_max = 4;
+ }
+ access_size = MAX(MIN(size, access_size_max), access_size_min);
+ access_mask = -1ULL >> (64 - access_size * 8);
+ addr += mr->offset;
+ for (i = 0; i < size; i += access_size) {
+ /* FIXME: big-endian support */
+ mr->ops->write(mr->opaque, addr + i, (data >> (i * 8)) & access_mask,
+ access_size);
+ }
+}
+
+static uint32_t memory_region_read_thunk_b(void *mr, target_phys_addr_t addr)
+{
+ return memory_region_read_thunk_n(mr, addr, 1);
+}
+
+static uint32_t memory_region_read_thunk_w(void *mr, target_phys_addr_t addr)
+{
+ return memory_region_read_thunk_n(mr, addr, 2);
+}
+
+static uint32_t memory_region_read_thunk_l(void *mr, target_phys_addr_t addr)
+{
+ return memory_region_read_thunk_n(mr, addr, 4);
+}
+
+static void memory_region_write_thunk_b(void *mr, target_phys_addr_t addr,
+ uint32_t data)
+{
+ memory_region_write_thunk_n(mr, addr, 1, data);
+}
+
+static void memory_region_write_thunk_w(void *mr, target_phys_addr_t addr,
+ uint32_t data)
+{
+ memory_region_write_thunk_n(mr, addr, 2, data);
+}
+
+static void memory_region_write_thunk_l(void *mr, target_phys_addr_t addr,
+ uint32_t data)
+{
+ memory_region_write_thunk_n(mr, addr, 4, data);
+}
+
+static CPUReadMemoryFunc * const memory_region_read_thunk[] = {
+ memory_region_read_thunk_b,
+ memory_region_read_thunk_w,
+ memory_region_read_thunk_l,
+};
+
+static CPUWriteMemoryFunc * const memory_region_write_thunk[] = {
+ memory_region_write_thunk_b,
+ memory_region_write_thunk_w,
+ memory_region_write_thunk_l,
+};
+
+static void memory_region_prepare_ram_addr(MemoryRegion *mr)
+{
+ if (mr->backend_registered) {
+ return;
+ }
+
+ mr->ram_addr = cpu_register_io_memory(memory_region_read_thunk,
+ memory_region_write_thunk,
+ mr,
+ mr->ops->endianness);
+ mr->backend_registered = true;
+}
+
+void memory_region_init_io(MemoryRegion *mr,
+ const MemoryRegionOps *ops,
+ void *opaque,
+ const char *name,
+ uint64_t size)
+{
+ memory_region_init(mr, name, size);
+ mr->ops = ops;
+ mr->opaque = opaque;
+ mr->terminates = true;
+ mr->backend_registered = false;
+}
+
+void memory_region_init_ram(MemoryRegion *mr,
+ DeviceState *dev,
+ const char *name,
+ uint64_t size)
+{
+ memory_region_init(mr, name, size);
+ mr->terminates = true;
+ mr->ram_addr = qemu_ram_alloc(dev, name, size);
+ mr->backend_registered = true;
+}
+
+void memory_region_init_ram_ptr(MemoryRegion *mr,
+ DeviceState *dev,
+ const char *name,
+ uint64_t size,
+ void *ptr)
+{
+ memory_region_init(mr, name, size);
+ mr->terminates = true;
+ mr->ram_addr = qemu_ram_alloc_from_ptr(dev, name, size, ptr);
+ mr->backend_registered = true;
+}
+
+void memory_region_init_alias(MemoryRegion *mr,
+ const char *name,
+ MemoryRegion *orig,
+ target_phys_addr_t offset,
+ uint64_t size)
+{
+ memory_region_init(mr, name, size);
+ mr->alias = orig;
+ mr->alias_offset = offset;
+}
+
+void memory_region_destroy(MemoryRegion *mr)
+{
+ assert(QTAILQ_EMPTY(&mr->subregions));
+ memory_region_clear_coalescing(mr);
+ qemu_free((char *)mr->name);
+ qemu_free(mr->ioeventfds);
+}
+
+uint64_t memory_region_size(MemoryRegion *mr)
+{
+ return mr->size;
+}
+
+void memory_region_set_offset(MemoryRegion *mr, target_phys_addr_t offset)
+{
+ mr->offset = offset;
+}
+
+void memory_region_set_log(MemoryRegion *mr, bool log, unsigned client)
+{
+ uint8_t mask = 1 << client;
+
+ mr->dirty_log_mask = (mr->dirty_log_mask & ~mask) | (log * mask);
+ memory_region_update_topology();
+}
+
+bool memory_region_get_dirty(MemoryRegion *mr, target_phys_addr_t addr,
+ unsigned client)
+{
+ assert(mr->terminates);
+ return cpu_physical_memory_get_dirty(mr->ram_addr + addr, 1 << client);
+}
+
+void memory_region_set_dirty(MemoryRegion *mr, target_phys_addr_t addr)
+{
+ assert(mr->terminates);
+ return cpu_physical_memory_set_dirty(mr->ram_addr + addr);
+}
+
+void memory_region_sync_dirty_bitmap(MemoryRegion *mr)
+{
+ FlatRange *fr;
+
+ FOR_EACH_FLAT_RANGE(fr, &address_space_memory.current_map) {
+ if (fr->mr == mr) {
+ cpu_physical_sync_dirty_bitmap(fr->addr.start,
+ fr->addr.start + fr->addr.size);
+ }
+ }
+}
+
+void memory_region_set_readonly(MemoryRegion *mr, bool readonly)
+{
+ /* FIXME */
+}
+
+void memory_region_reset_dirty(MemoryRegion *mr, target_phys_addr_t addr,
+ target_phys_addr_t size, unsigned client)
+{
+ assert(mr->terminates);
+ cpu_physical_memory_reset_dirty(mr->ram_addr + addr,
+ mr->ram_addr + addr + size,
+ 1 << client);
+}
+
+void *memory_region_get_ram_ptr(MemoryRegion *mr)
+{
+ if (mr->alias) {
+ return memory_region_get_ram_ptr(mr->alias) + mr->alias_offset;
+ }
+
+ assert(mr->terminates);
+
+ return qemu_get_ram_ptr(mr->ram_addr);
+}
+
+static void memory_region_update_coalesced_range(MemoryRegion *mr)
+{
+ FlatRange *fr;
+ CoalescedMemoryRange *cmr;
+ AddrRange tmp;
+
+ FOR_EACH_FLAT_RANGE(fr, &address_space_memory.current_map) {
+ if (fr->mr == mr) {
+ qemu_unregister_coalesced_mmio(fr->addr.start, fr->addr.size);
+ QTAILQ_FOREACH(cmr, &mr->coalesced, link) {
+ tmp = addrrange_shift(cmr->addr,
+ fr->addr.start - fr->offset_in_region);
+ if (!addrrange_intersects(tmp, fr->addr)) {
+ continue;
+ }
+ tmp = addrrange_intersection(tmp, fr->addr);
+ qemu_register_coalesced_mmio(tmp.start, tmp.size);
+ }
+ }
+ }
+}
+
+void memory_region_set_coalescing(MemoryRegion *mr)
+{
+ memory_region_clear_coalescing(mr);
+ memory_region_add_coalescing(mr, 0, mr->size);
+}
+
+void memory_region_add_coalescing(MemoryRegion *mr,
+ target_phys_addr_t offset,
+ uint64_t size)
+{
+ CoalescedMemoryRange *cmr = qemu_malloc(sizeof(*cmr));
+
+ cmr->addr = addrrange_make(offset, size);
+ QTAILQ_INSERT_TAIL(&mr->coalesced, cmr, link);
+ memory_region_update_coalesced_range(mr);
+}
+
+void memory_region_clear_coalescing(MemoryRegion *mr)
+{
+ CoalescedMemoryRange *cmr;
+
+ while (!QTAILQ_EMPTY(&mr->coalesced)) {
+ cmr = QTAILQ_FIRST(&mr->coalesced);
+ QTAILQ_REMOVE(&mr->coalesced, cmr, link);
+ qemu_free(cmr);
+ }
+ memory_region_update_coalesced_range(mr);
+}
+
+void memory_region_add_eventfd(MemoryRegion *mr,
+ target_phys_addr_t addr,
+ unsigned size,
+ bool match_data,
+ uint64_t data,
+ int fd)
+{
+ MemoryRegionIoeventfd mrfd = {
+ .addr.start = addr,
+ .addr.size = size,
+ .match_data = match_data,
+ .data = data,
+ .fd = fd,
+ };
+ unsigned i;
+
+ for (i = 0; i < mr->ioeventfd_nb; ++i) {
+ if (memory_region_ioeventfd_before(mrfd, mr->ioeventfds[i])) {
+ break;
+ }
+ }
+ ++mr->ioeventfd_nb;
+ mr->ioeventfds = qemu_realloc(mr->ioeventfds,
+ sizeof(*mr->ioeventfds) * mr->ioeventfd_nb);
+ memmove(&mr->ioeventfds[i+1], &mr->ioeventfds[i],
+ sizeof(*mr->ioeventfds) * (mr->ioeventfd_nb-1 - i));
+ mr->ioeventfds[i] = mrfd;
+ memory_region_update_topology();
+}
+
+void memory_region_del_eventfd(MemoryRegion *mr,
+ target_phys_addr_t addr,
+ unsigned size,
+ bool match_data,
+ uint64_t data,
+ int fd)
+{
+ MemoryRegionIoeventfd mrfd = {
+ .addr.start = addr,
+ .addr.size = size,
+ .match_data = match_data,
+ .data = data,
+ .fd = fd,
+ };
+ unsigned i;
+
+ for (i = 0; i < mr->ioeventfd_nb; ++i) {
+ if (memory_region_ioeventfd_equal(mrfd, mr->ioeventfds[i])) {
+ break;
+ }
+ }
+ assert(i != mr->ioeventfd_nb);
+ memmove(&mr->ioeventfds[i], &mr->ioeventfds[i+1],
+ sizeof(*mr->ioeventfds) * (mr->ioeventfd_nb - (i+1)));
+ --mr->ioeventfd_nb;
+ mr->ioeventfds = qemu_realloc(mr->ioeventfds,
+ sizeof(*mr->ioeventfds)*mr->ioeventfd_nb + 1);
+ memory_region_update_topology();
+}
+
+static void memory_region_add_subregion_common(MemoryRegion *mr,
+ target_phys_addr_t offset,
+ MemoryRegion *subregion)
+{
+ MemoryRegion *other;
+
+ assert(!subregion->parent);
+ subregion->parent = mr;
+ subregion->addr = offset;
+ QTAILQ_FOREACH(other, &mr->subregions, subregions_link) {
+ if (subregion->may_overlap || other->may_overlap) {
+ continue;
+ }
+ if (offset >= other->offset + other->size
+ || offset + subregion->size <= other->offset) {
+ continue;
+ }
+ printf("warning: subregion collision %llx/%llx vs %llx/%llx\n",
+ (unsigned long long)offset,
+ (unsigned long long)subregion->size,
+ (unsigned long long)other->offset,
+ (unsigned long long)other->size);
+ }
+ QTAILQ_FOREACH(other, &mr->subregions, subregions_link) {
+ if (subregion->priority >= other->priority) {
+ QTAILQ_INSERT_BEFORE(other, subregion, subregions_link);
+ goto done;
+ }
+ }
+ QTAILQ_INSERT_TAIL(&mr->subregions, subregion, subregions_link);
+done:
+ memory_region_update_topology();
+}
+
+
+void memory_region_add_subregion(MemoryRegion *mr,
+ target_phys_addr_t offset,
+ MemoryRegion *subregion)
+{
+ subregion->may_overlap = false;
+ subregion->priority = 0;
+ memory_region_add_subregion_common(mr, offset, subregion);
+}
+
+void memory_region_add_subregion_overlap(MemoryRegion *mr,
+ target_phys_addr_t offset,
+ MemoryRegion *subregion,
+ unsigned priority)
+{
+ subregion->may_overlap = true;
+ subregion->priority = priority;
+ memory_region_add_subregion_common(mr, offset, subregion);
+}
+
+void memory_region_del_subregion(MemoryRegion *mr,
+ MemoryRegion *subregion)
+{
+ assert(subregion->parent == mr);
+ subregion->parent = NULL;
+ QTAILQ_REMOVE(&mr->subregions, subregion, subregions_link);
+ memory_region_update_topology();
+}
+
+void set_system_memory_map(MemoryRegion *mr)
+{
+ address_space_memory.root = mr;
+ memory_region_update_topology();
+}
+
+void set_system_io_map(MemoryRegion *mr)
+{
+ address_space_io.root = mr;
+ memory_region_update_topology();
+}
diff --git a/memory.h b/memory.h
new file mode 100644
index 0000000..4e518b2
--- /dev/null
+++ b/memory.h
@@ -0,0 +1,469 @@
+/*
+ * Physical memory management API
+ *
+ * Copyright 2011 Red Hat, Inc. and/or its affiliates
+ *
+ * Authors:
+ * Avi Kivity <avi@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#ifndef MEMORY_H
+#define MEMORY_H
+
+#ifndef CONFIG_USER_ONLY
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "qemu-common.h"
+#include "cpu-common.h"
+#include "targphys.h"
+#include "qemu-queue.h"
+#include "iorange.h"
+#include "ioport.h"
+
+typedef struct MemoryRegionOps MemoryRegionOps;
+typedef struct MemoryRegion MemoryRegion;
+typedef struct MemoryRegionPortio MemoryRegionPortio;
+typedef struct MemoryRegionMmio MemoryRegionMmio;
+
+/* Must match *_DIRTY_FLAGS in cpu-all.h. To be replaced with dynamic
+ * registration.
+ */
+#define DIRTY_MEMORY_VGA 0
+#define DIRTY_MEMORY_CODE 1
+#define DIRTY_MEMORY_MIGRATION 3
+
+struct MemoryRegionMmio {
+ CPUReadMemoryFunc *read[3];
+ CPUWriteMemoryFunc *write[3];
+};
+
+/*
+ * Memory region callbacks
+ */
+struct MemoryRegionOps {
+ /* Read from the memory region. @addr is relative to @mr; @size is
+ * in bytes. */
+ uint64_t (*read)(void *opaque,
+ target_phys_addr_t addr,
+ unsigned size);
+ /* Write to the memory region. @addr is relative to @mr; @size is
+ * in bytes. */
+ void (*write)(void *opaque,
+ target_phys_addr_t addr,
+ uint64_t data,
+ unsigned size);
+
+ enum device_endian endianness;
+ /* Guest-visible constraints: */
+ struct {
+ /* If nonzero, specify bounds on access sizes beyond which a machine
+ * check is thrown.
+ */
+ unsigned min_access_size;
+ unsigned max_access_size;
+ /* If true, unaligned accesses are supported. Otherwise unaligned
+ * accesses throw machine checks.
+ */
+ bool unaligned;
+ } valid;
+ /* Internal implementation constraints: */
+ struct {
+ /* If nonzero, specifies the minimum size implemented. Smaller sizes
+ * will be rounded upwards and a partial result will be returned.
+ */
+ unsigned min_access_size;
+ /* If nonzero, specifies the maximum size implemented. Larger sizes
+ * will be done as a series of accesses with smaller sizes.
+ */
+ unsigned max_access_size;
+ /* If true, unaligned accesses are supported. Otherwise all accesses
+ * are converted to (possibly multiple) naturally aligned accesses.
+ */
+ bool unaligned;
+ } impl;
+
+ /* If .read and .write are not present, old_portio may be used for
+ * backwards compatibility with old portio registration
+ */
+ const MemoryRegionPortio *old_portio;
+ /* If .read and .write are not present, old_mmio may be used for
+ * backwards compatibility with old mmio registration
+ */
+ const MemoryRegionMmio old_mmio;
+};
+
+typedef struct CoalescedMemoryRange CoalescedMemoryRange;
+typedef struct MemoryRegionIoeventfd MemoryRegionIoeventfd;
+
+struct MemoryRegion {
+ /* All fields are private - violators will be prosecuted */
+ const MemoryRegionOps *ops;
+ void *opaque;
+ MemoryRegion *parent;
+ uint64_t size;
+ target_phys_addr_t addr;
+ target_phys_addr_t offset;
+ bool backend_registered;
+ ram_addr_t ram_addr;
+ IORange iorange;
+ bool terminates;
+ MemoryRegion *alias;
+ target_phys_addr_t alias_offset;
+ unsigned priority;
+ bool may_overlap;
+ QTAILQ_HEAD(subregions, MemoryRegion) subregions;
+ QTAILQ_ENTRY(MemoryRegion) subregions_link;
+ QTAILQ_HEAD(coalesced_ranges, CoalescedMemoryRange) coalesced;
+ const char *name;
+ uint8_t dirty_log_mask;
+ unsigned ioeventfd_nb;
+ MemoryRegionIoeventfd *ioeventfds;
+};
+
+struct MemoryRegionPortio {
+ uint32_t offset;
+ uint32_t len;
+ unsigned size;
+ IOPortReadFunc *read;
+ IOPortWriteFunc *write;
+};
+
+#define PORTIO_END { }
+
+/**
+ * memory_region_init: Initialize a memory region
+ *
+ * The region typically acts as a container for other memory regions. Us
+ * memory_region_add_subregion() to add subregions.
+ *
+ * @mr: the #MemoryRegion to be initialized
+ * @name: used for debugging; not visible to the user or ABI
+ * @size: size of the region; any subregions beyond this size will be clipped
+ */
+void memory_region_init(MemoryRegion *mr,
+ const char *name,
+ uint64_t size);
+/**
+ * memory_region_init_io: Initialize an I/O memory region.
+ *
+ * Accesses into the region will be cause the callbacks in @ops to be called.
+ * if @size is nonzero, subregions will be clipped to @size.
+ *
+ * @mr: the #MemoryRegion to be initialized.
+ * @ops: a structure containing read and write callbacks to be used when
+ * I/O is performed on the region.
+ * @opaque: passed to to the read and write callbacks of the @ops structure.
+ * @name: used for debugging; not visible to the user or ABI
+ * @size: size of the region.
+ */
+void memory_region_init_io(MemoryRegion *mr,
+ const MemoryRegionOps *ops,
+ void *opaque,
+ const char *name,
+ uint64_t size);
+
+/**
+ * memory_region_init_ram: Initialize RAM memory region. Accesses into the
+ * region will be modify memory directly.
+ *
+ * @mr: the #MemoryRegion to be initialized.
+ * @dev: a device associated with the region; may be %NULL.
+ * @name: the name of the region; the pair (@dev, @name) must be globally
+ * unique. The name is part of the save/restore ABI and so cannot be
+ * changed.
+ * @size: size of the region.
+ */
+void memory_region_init_ram(MemoryRegion *mr,
+ DeviceState *dev, /* FIXME: layering violation */
+ const char *name,
+ uint64_t size);
+
+/**
+ * memory_region_init_ram: Initialize RAM memory region from a user-provided.
+ * pointer. Accesses into the region will be modify
+ * memory directly.
+ *
+ * @mr: the #MemoryRegion to be initialized.
+ * @dev: a device associated with the region; may be %NULL.
+ * @name: the name of the region; the pair (@dev, @name) must be globally
+ * unique. The name is part of the save/restore ABI and so cannot be
+ * changed.
+ * @size: size of the region.
+ * @ptr: memory to be mapped; must contain at least @size bytes.
+ */
+void memory_region_init_ram_ptr(MemoryRegion *mr,
+ DeviceState *dev, /* FIXME: layering violation */
+ const char *name,
+ uint64_t size,
+ void *ptr);
+
+/**
+ * memory_region_init_alias: Initialize a memory region that aliases all or a
+ * part of another memory region.
+ *
+ * @mr: the #MemoryRegion to be initialized.
+ * @name: used for debugging; not visible to the user or ABI
+ * @orig: the region to be referenced; @mr will be equivalent to
+ * @orig between @offset and @offset + @size - 1.
+ * @offset: start of the section in @orig to be referenced.
+ * @size: size of the region.
+ */
+void memory_region_init_alias(MemoryRegion *mr,
+ const char *name,
+ MemoryRegion *orig,
+ target_phys_addr_t offset,
+ uint64_t size);
+/**
+ * memory_region_destroy: Destroy a memory region and relaim all resources.
+ *
+ * @mr: the region to be destroyed. May not currently be a subregion
+ * (see memory_region_add_subregion()) or referenced in an alias
+ * (see memory_region_init_alias()).
+ */
+void memory_region_destroy(MemoryRegion *mr);
+
+/**
+ * memory_region_size: get a memory region's size.
+ *
+ * @mr: the memory region being queried.
+ */
+uint64_t memory_region_size(MemoryRegion *mr);
+
+/**
+ * memory_region_get_ram_ptr: Get a pointer into a RAM memory region.
+ *
+ * Returns a host pointer to a RAM memory region (created with
+ * memory_region_init_ram() or memory_region_init_ram_ptr()). Use with
+ * care.
+ *
+ * @mr: the memory region being queried.
+ */
+void *memory_region_get_ram_ptr(MemoryRegion *mr);
+
+/**
+ * memory_region_set_offset: Sets an offset to be added to MemoryRegionOps
+ * callbacks.
+ *
+ * This function is deprecated and should not be used in new code.
+ */
+void memory_region_set_offset(MemoryRegion *mr, target_phys_addr_t offset);
+
+/**
+ * memory_region_set_log: Turn dirty logging on or off for a region.
+ *
+ * Turns dirty logging on or off for a specified client (display, migration).
+ * Only meaningful for RAM regions.
+ *
+ * @mr: the memory region being updated.
+ * @log: whether dirty logging is to be enabled or disabled.
+ * @client: the user of the logging information; %DIRTY_MEMORY_MIGRATION or
+ * %DIRTY_MEMORY_VGA.
+ */
+void memory_region_set_log(MemoryRegion *mr, bool log, unsigned client);
+
+/**
+ * memory_region_get_dirty: Check whether a page is dirty for a specified
+ * client.
+ *
+ * Checks whether a page has been written to since the last
+ * call to memory_region_reset_dirty() with the same @client. Dirty logging
+ * must be enabled.
+ *
+ * @mr: the memory region being queried.
+ * @addr: the address (relative to the start of the region) being queried.
+ * @client: the user of the logging information; %DIRTY_MEMORY_MIGRATION or
+ * %DIRTY_MEMORY_VGA.
+ */
+bool memory_region_get_dirty(MemoryRegion *mr, target_phys_addr_t addr,
+ unsigned client);
+
+/**
+ * memory_region_set_dirty: Mark a page as dirty in a memory region.
+ *
+ * Marks a page as dirty, after it has been dirtied outside guest code.
+ *
+ * @mr: the memory region being queried.
+ * @addr: the address (relative to the start of the region) being dirtied.
+ */
+void memory_region_set_dirty(MemoryRegion *mr, target_phys_addr_t addr);
+
+/**
+ * memory_region_sync_dirty_bitmap: Synchronize a region's dirty bitmap with
+ * any external TLBs (e.g. kvm)
+ *
+ * Flushes dirty information from accelerators such as kvm and vhost-net
+ * and makes it available to users of the memory API.
+ *
+ * @mr: the region being flushed.
+ */
+void memory_region_sync_dirty_bitmap(MemoryRegion *mr);
+
+/**
+ * memory_region_reset_dirty: Mark a range of pages as clean, for a specified
+ * client.
+ *
+ * Marks a range of pages as no longer dirty.
+ *
+ * @mr: the region being updated.
+ * @addr: the start of the subrange being cleaned.
+ * @size: the size of the subrange being cleaned.
+ * @client: the user of the logging information; %DIRTY_MEMORY_MIGRATION or
+ * %DIRTY_MEMORY_VGA.
+ */
+void memory_region_reset_dirty(MemoryRegion *mr, target_phys_addr_t addr,
+ target_phys_addr_t size, unsigned client);
+
+/**
+ * memory_region_set_readonly: Turn a memory region read-only (or read-write)
+ *
+ * Allows a memory region to be marked as read-only (turning it into a ROM).
+ * only useful on RAM regions.
+ *
+ * @mr: the region being updated.
+ * @readonly: whether rhe region is to be ROM or RAM.
+ */
+void memory_region_set_readonly(MemoryRegion *mr, bool readonly);
+
+/**
+ * memory_region_set_coalescing: Enable memory coalescing for the region.
+ *
+ * Enabled writes to a region to be queued for later processing. MMIO ->write
+ * callbacks may be delayed until a non-coalesced MMIO is issued.
+ * Only useful for IO regions. Roughly similar to write-combining hardware.
+ *
+ * @mr: the memory region to be write coalesced
+ */
+void memory_region_set_coalescing(MemoryRegion *mr);
+
+/**
+ * memory_region_add_coalescing: Enable memory coalescing for a sub-range of
+ * a region.
+ *
+ * Like memory_region_set_coalescing(), but works on a sub-range of a region.
+ * Multiple calls can be issued coalesced disjoint ranges.
+ *
+ * @mr: the memory region to be updated.
+ * @offset: the start of the range within the region to be coalesced.
+ * @size: the size of the subrange to be coalesced.
+ */
+void memory_region_add_coalescing(MemoryRegion *mr,
+ target_phys_addr_t offset,
+ uint64_t size);
+
+/**
+ * memory_region_clear_coalescing: Disable MMIO coalescing for the region.
+ *
+ * Disables any coalescing caused by memory_region_set_coalescing() or
+ * memory_region_add_coalescing(). Roughly equivalent to uncacheble memory
+ * hardware.
+ *
+ * @mr: the memory region to be updated.
+ */
+void memory_region_clear_coalescing(MemoryRegion *mr);
+
+/**
+ * memory_region_add_eventfd: Request an eventfd to be triggered when a word
+ * is written to a location.
+ *
+ * Marks a word in an IO region (initialized with memory_region_init_io())
+ * as a trigger for an eventfd event. The I/O callback will not be called.
+ * The caller must be prepared to handle failure (hat is, take the required
+ * action if the callback _is_ called).
+ *
+ * @mr: the memory region being updated.
+ * @addr: the address within @mr that is to be monitored
+ * @size: the size of the access to trigger the eventfd
+ * @match_data: whether to match against @data, instead of just @addr
+ * @data: the data to match against the guest write
+ * @fd: the eventfd to be triggered when @addr, @size, and @data all match.
+ **/
+void memory_region_add_eventfd(MemoryRegion *mr,
+ target_phys_addr_t addr,
+ unsigned size,
+ bool match_data,
+ uint64_t data,
+ int fd);
+
+/**
+ * memory_region_del_eventfd: Cancel and eventfd.
+ *
+ * Cancels an eventfd trigger request by a previous memory_region_add_eventfd()
+ * call.
+ *
+ * @mr: the memory region being updated.
+ * @addr: the address within @mr that is to be monitored
+ * @size: the size of the access to trigger the eventfd
+ * @match_data: whether to match against @data, instead of just @addr
+ * @data: the data to match against the guest write
+ * @fd: the eventfd to be triggered when @addr, @size, and @data all match.
+ */
+void memory_region_del_eventfd(MemoryRegion *mr,
+ target_phys_addr_t addr,
+ unsigned size,
+ bool match_data,
+ uint64_t data,
+ int fd);
+/**
+ * memory_region_add_subregion: Add a sub-region to a container.
+ *
+ * Adds a sub-region at @offset. The sub-region may not overlap with other
+ * subregions (except for those explicitly marked as overlapping). A region
+ * may only be added once as a subregion (unless removed with
+ * memory_region_del_subregion()); use memory_region_init_alias() if you
+ * want a region to be a subregion in multiple locations.
+ *
+ * @mr: the region to contain the new subregion; must be a container
+ * initialized with memory_region_init().
+ * @offset: the offset relative to @mr where @subregion is added.
+ * @subregion: the subregion to be added.
+ */
+void memory_region_add_subregion(MemoryRegion *mr,
+ target_phys_addr_t offset,
+ MemoryRegion *subregion);
+/**
+ * memory_region_add_subregion: Add a sub-region to a container, with overlap.
+ *
+ * Adds a sub-region at @offset. The sub-region may overlap with other
+ * subregions. Conflicts are resolved by having a higher @priority hide a
+ * lower @priority. Subregions without priority are taken as @priority 0.
+ * A region may only be added once as a subregion (unless removed with
+ * memory_region_del_subregion()); use memory_region_init_alias() if you
+ * want a region to be a subregion in multiple locations.
+ *
+ * @mr: the region to contain the new subregion; must be a container
+ * initialized with memory_region_init().
+ * @offset: the offset relative to @mr where @subregion is added.
+ * @subregion: the subregion to be added.
+ * @priority: used for resolving overlaps; highest priority wins.
+ */
+void memory_region_add_subregion_overlap(MemoryRegion *mr,
+ target_phys_addr_t offset,
+ MemoryRegion *subregion,
+ unsigned priority);
+/**
+ * memory_region_del_subregion: Remove a subregion.
+ *
+ * Removes a subregion from its container.
+ *
+ * @mr: the container to be updated.
+ * @subregion: the region being removed; must be a current subregion of @mr.
+ */
+void memory_region_del_subregion(MemoryRegion *mr,
+ MemoryRegion *subregion);
+
+/* Start a transaction; changes will be accumulated and made visible only
+ * when the transaction ends.
+ */
+void memory_region_transaction_begin(void);
+/* Commit a transaction and make changes visible to the guest.
+ */
+void memory_region_transaction_commit(void);
+
+#endif
+
+#endif
diff --git a/migration.c b/migration.c
index af3a1f2..2a15b98 100644
--- a/migration.c
+++ b/migration.c
@@ -124,7 +124,7 @@ int do_migrate(Monitor *mon, const QDict *qdict, QObject **ret_data)
}
current_migration = s;
- notifier_list_notify(&migration_state_notifiers);
+ notifier_list_notify(&migration_state_notifiers, NULL);
return 0;
}
@@ -276,7 +276,7 @@ void migrate_fd_error(FdMigrationState *s)
{
DPRINTF("setting error state\n");
s->state = MIG_STATE_ERROR;
- notifier_list_notify(&migration_state_notifiers);
+ notifier_list_notify(&migration_state_notifiers, NULL);
migrate_fd_cleanup(s);
}
@@ -334,7 +334,7 @@ ssize_t migrate_fd_put_buffer(void *opaque, const void *data, size_t size)
monitor_resume(s->mon);
}
s->state = MIG_STATE_ERROR;
- notifier_list_notify(&migration_state_notifiers);
+ notifier_list_notify(&migration_state_notifiers, NULL);
}
return ret;
@@ -395,7 +395,7 @@ void migrate_fd_put_ready(void *opaque)
state = MIG_STATE_ERROR;
}
s->state = state;
- notifier_list_notify(&migration_state_notifiers);
+ notifier_list_notify(&migration_state_notifiers, NULL);
}
}
@@ -415,7 +415,7 @@ void migrate_fd_cancel(MigrationState *mig_state)
DPRINTF("cancelling migration\n");
s->state = MIG_STATE_CANCELLED;
- notifier_list_notify(&migration_state_notifiers);
+ notifier_list_notify(&migration_state_notifiers, NULL);
qemu_savevm_state_cancel(s->mon, s->file);
migrate_fd_cleanup(s);
@@ -429,7 +429,7 @@ void migrate_fd_release(MigrationState *mig_state)
if (s->state == MIG_STATE_ACTIVE) {
s->state = MIG_STATE_CANCELLED;
- notifier_list_notify(&migration_state_notifiers);
+ notifier_list_notify(&migration_state_notifiers, NULL);
migrate_fd_cleanup(s);
}
qemu_free(s);
diff --git a/module.h b/module.h
index 9263f1c..ef66730 100644
--- a/module.h
+++ b/module.h
@@ -24,12 +24,14 @@ typedef enum {
MODULE_INIT_BLOCK,
MODULE_INIT_DEVICE,
MODULE_INIT_MACHINE,
+ MODULE_INIT_QAPI,
MODULE_INIT_MAX
} module_init_type;
#define block_init(function) module_init(function, MODULE_INIT_BLOCK)
#define device_init(function) module_init(function, MODULE_INIT_DEVICE)
#define machine_init(function) module_init(function, MODULE_INIT_MACHINE)
+#define qapi_init(function) module_init(function, MODULE_INIT_QAPI)
void register_module_init(void (*fn)(void), module_init_type type);
diff --git a/monitor.c b/monitor.c
index 67ceb46..1b8ba2c 100644
--- a/monitor.c
+++ b/monitor.c
@@ -1185,6 +1185,40 @@ static int expire_password(Monitor *mon, const QDict *qdict, QObject **ret_data)
return -1;
}
+static int add_graphics_client(Monitor *mon, const QDict *qdict, QObject **ret_data)
+{
+ const char *protocol = qdict_get_str(qdict, "protocol");
+ const char *fdname = qdict_get_str(qdict, "fdname");
+ int skipauth = qdict_get_try_bool(qdict, "skipauth", 0);
+ CharDriverState *s;
+
+ if (strcmp(protocol, "spice") == 0) {
+ if (!using_spice) {
+ /* correct one? spice isn't a device ,,, */
+ qerror_report(QERR_DEVICE_NOT_ACTIVE, "spice");
+ return -1;
+ }
+ qerror_report(QERR_ADD_CLIENT_FAILED);
+ return -1;
+#ifdef CONFIG_VNC
+ } else if (strcmp(protocol, "vnc") == 0) {
+ int fd = monitor_get_fd(mon, fdname);
+ vnc_display_add_client(NULL, fd, skipauth);
+ return 0;
+#endif
+ } else if ((s = qemu_chr_find(protocol)) != NULL) {
+ int fd = monitor_get_fd(mon, fdname);
+ if (qemu_chr_add_client(s, fd) < 0) {
+ qerror_report(QERR_ADD_CLIENT_FAILED);
+ return -1;
+ }
+ return 0;
+ }
+
+ qerror_report(QERR_INVALID_PARAMETER, "protocol");
+ return -1;
+}
+
static int client_migrate_info(Monitor *mon, const QDict *qdict, QObject **ret_data)
{
const char *protocol = qdict_get_str(qdict, "protocol");
diff --git a/nbd.c b/nbd.c
index 0dcd86b..e7a585d 100644
--- a/nbd.c
+++ b/nbd.c
@@ -78,7 +78,7 @@ size_t nbd_wr_sync(int fd, void *buffer, size_t size, bool do_read)
ssize_t len;
if (do_read) {
- len = recv(fd, buffer + offset, size - offset, 0);
+ len = qemu_recv(fd, buffer + offset, size - offset, 0);
} else {
len = send(fd, buffer + offset, size - offset, 0);
}
diff --git a/net.c b/net.c
index e329c69..cb6a2b0 100644
--- a/net.c
+++ b/net.c
@@ -783,18 +783,12 @@ static int net_init_nic(QemuOpts *opts,
nd->devaddr = qemu_strdup(qemu_opt_get(opts, "addr"));
}
- nd->macaddr[0] = 0x52;
- nd->macaddr[1] = 0x54;
- nd->macaddr[2] = 0x00;
- nd->macaddr[3] = 0x12;
- nd->macaddr[4] = 0x34;
- nd->macaddr[5] = 0x56 + idx;
-
if (qemu_opt_get(opts, "macaddr") &&
- net_parse_macaddr(nd->macaddr, qemu_opt_get(opts, "macaddr")) < 0) {
+ net_parse_macaddr(nd->macaddr.a, qemu_opt_get(opts, "macaddr")) < 0) {
error_report("invalid syntax for ethernet address");
return -1;
}
+ qemu_macaddr_default_if_unset(&nd->macaddr);
nd->nvectors = qemu_opt_get_number(opts, "vectors",
DEV_NVECTORS_UNSPECIFIED);
@@ -837,14 +831,15 @@ static const struct {
const char *type;
net_client_init_func init;
QemuOptDesc desc[NET_MAX_DESC];
-} net_client_types[] = {
- {
+} net_client_types[NET_CLIENT_TYPE_MAX] = {
+ [NET_CLIENT_TYPE_NONE] = {
.type = "none",
.desc = {
NET_COMMON_PARAMS_DESC,
{ /* end of list */ }
},
- }, {
+ },
+ [NET_CLIENT_TYPE_NIC] = {
.type = "nic",
.init = net_init_nic,
.desc = {
@@ -873,8 +868,9 @@ static const struct {
},
{ /* end of list */ }
},
+ },
#ifdef CONFIG_SLIRP
- }, {
+ [NET_CLIENT_TYPE_USER] = {
.type = "user",
.init = net_init_slirp,
.desc = {
@@ -934,8 +930,9 @@ static const struct {
},
{ /* end of list */ }
},
+ },
#endif
- }, {
+ [NET_CLIENT_TYPE_TAP] = {
.type = "tap",
.init = net_init_tap,
.desc = {
@@ -982,7 +979,8 @@ static const struct {
#endif /* _WIN32 */
{ /* end of list */ }
},
- }, {
+ },
+ [NET_CLIENT_TYPE_SOCKET] = {
.type = "socket",
.init = net_init_socket,
.desc = {
@@ -1010,8 +1008,9 @@ static const struct {
},
{ /* end of list */ }
},
+ },
#ifdef CONFIG_VDE
- }, {
+ [NET_CLIENT_TYPE_VDE] = {
.type = "vde",
.init = net_init_vde,
.desc = {
@@ -1035,8 +1034,9 @@ static const struct {
},
{ /* end of list */ }
},
+ },
#endif
- }, {
+ [NET_CLIENT_TYPE_DUMP] = {
.type = "dump",
.init = net_init_dump,
.desc = {
@@ -1053,7 +1053,6 @@ static const struct {
{ /* end of list */ }
},
},
- { /* end of list */ }
};
int net_client_init(Monitor *mon, QemuOpts *opts, int is_netdev)
@@ -1101,8 +1100,9 @@ int net_client_init(Monitor *mon, QemuOpts *opts, int is_netdev)
name = qemu_opt_get(opts, "name");
}
- for (i = 0; net_client_types[i].type != NULL; i++) {
- if (!strcmp(net_client_types[i].type, type)) {
+ for (i = 0; i < NET_CLIENT_TYPE_MAX; i++) {
+ if (net_client_types[i].type != NULL &&
+ !strcmp(net_client_types[i].type, type)) {
VLANState *vlan = NULL;
int ret;
@@ -1228,25 +1228,38 @@ int do_netdev_del(Monitor *mon, const QDict *qdict, QObject **ret_data)
return 0;
}
+static void print_net_client(Monitor *mon, VLANClientState *vc)
+{
+ monitor_printf(mon, "%s: type=%s,%s\n", vc->name,
+ net_client_types[vc->info->type].type, vc->info_str);
+}
+
void do_info_network(Monitor *mon)
{
VLANState *vlan;
- VLANClientState *vc;
+ VLANClientState *vc, *peer;
+ net_client_type type;
QTAILQ_FOREACH(vlan, &vlans, next) {
monitor_printf(mon, "VLAN %d devices:\n", vlan->id);
QTAILQ_FOREACH(vc, &vlan->clients, next) {
- monitor_printf(mon, " %s: %s\n", vc->name, vc->info_str);
+ monitor_printf(mon, " ");
+ print_net_client(mon, vc);
}
}
monitor_printf(mon, "Devices not on any VLAN:\n");
QTAILQ_FOREACH(vc, &non_vlan_clients, next) {
- monitor_printf(mon, " %s: %s", vc->name, vc->info_str);
- if (vc->peer) {
- monitor_printf(mon, " peer=%s", vc->peer->name);
+ peer = vc->peer;
+ type = vc->info->type;
+ if (!peer || type == NET_CLIENT_TYPE_NIC) {
+ monitor_printf(mon, " ");
+ print_net_client(mon, vc);
+ } /* else it's a netdev connected to a NIC, printed with the NIC */
+ if (peer && type == NET_CLIENT_TYPE_NIC) {
+ monitor_printf(mon, " \\ ");
+ print_net_client(mon, peer);
}
- monitor_printf(mon, "\n");
}
}
@@ -1337,7 +1350,7 @@ void net_check_clients(void)
case NET_CLIENT_TYPE_NIC:
has_nic = 1;
break;
- case NET_CLIENT_TYPE_SLIRP:
+ case NET_CLIENT_TYPE_USER:
case NET_CLIENT_TYPE_TAP:
case NET_CLIENT_TYPE_SOCKET:
case NET_CLIENT_TYPE_VDE:
diff --git a/net.h b/net.h
index 5b883a9..5a7881c 100644
--- a/net.h
+++ b/net.h
@@ -31,11 +31,13 @@ typedef struct NICConf {
typedef enum {
NET_CLIENT_TYPE_NONE,
NET_CLIENT_TYPE_NIC,
- NET_CLIENT_TYPE_SLIRP,
+ NET_CLIENT_TYPE_USER,
NET_CLIENT_TYPE_TAP,
NET_CLIENT_TYPE_SOCKET,
NET_CLIENT_TYPE_VDE,
- NET_CLIENT_TYPE_DUMP
+ NET_CLIENT_TYPE_DUMP,
+
+ NET_CLIENT_TYPE_MAX
} net_client_type;
typedef void (NetPoll)(VLANClientState *, bool enable);
@@ -127,7 +129,7 @@ int do_set_link(Monitor *mon, const QDict *qdict, QObject **ret_data);
#define MAX_NICS 8
struct NICInfo {
- uint8_t macaddr[6];
+ MACAddr macaddr;
char *model;
char *name;
char *devaddr;
diff --git a/net/slirp.c b/net/slirp.c
index e057a14..157b80a 100644
--- a/net/slirp.c
+++ b/net/slirp.c
@@ -128,7 +128,7 @@ static void net_slirp_cleanup(VLANClientState *nc)
}
static NetClientInfo net_slirp_info = {
- .type = NET_CLIENT_TYPE_SLIRP,
+ .type = NET_CLIENT_TYPE_USER,
.size = sizeof(SlirpState),
.receive = net_slirp_receive,
.cleanup = net_slirp_cleanup,
@@ -240,7 +240,8 @@ static int net_slirp_init(VLANState *vlan, const char *model,
nc = qemu_new_net_client(&net_slirp_info, vlan, NULL, model, name);
snprintf(nc->info_str, sizeof(nc->info_str),
- "net=%s, restricted=%c", inet_ntoa(net), restricted ? 'y' : 'n');
+ "net=%s,restrict=%s", inet_ntoa(net),
+ restricted ? "on" : "off");
s = DO_UPCAST(SlirpState, nc, nc);
@@ -689,6 +690,7 @@ int net_init_slirp(QemuOpts *opts,
const char *bootfile;
const char *smb_export;
const char *vsmbsrv;
+ const char *restrict_opt;
char *vnet = NULL;
int restricted = 0;
int ret;
@@ -702,6 +704,18 @@ int net_init_slirp(QemuOpts *opts,
smb_export = qemu_opt_get(opts, "smb");
vsmbsrv = qemu_opt_get(opts, "smbserver");
+ restrict_opt = qemu_opt_get(opts, "restrict");
+ if (restrict_opt) {
+ if (!strcmp(restrict_opt, "on") ||
+ !strcmp(restrict_opt, "yes") || !strcmp(restrict_opt, "y")) {
+ restricted = 1;
+ } else if (strcmp(restrict_opt, "off") &&
+ strcmp(restrict_opt, "no") && strcmp(restrict_opt, "n")) {
+ error_report("invalid option: 'restrict=%s'", restrict_opt);
+ return -1;
+ }
+ }
+
if (qemu_opt_get(opts, "ip")) {
const char *ip = qemu_opt_get(opts, "ip");
int l = strlen(ip) + strlen("/24") + 1;
@@ -720,11 +734,6 @@ int net_init_slirp(QemuOpts *opts,
vnet = qemu_strdup(qemu_opt_get(opts, "net"));
}
- if (qemu_opt_get(opts, "restrict") &&
- qemu_opt_get(opts, "restrict")[0] == 'y') {
- restricted = 1;
- }
-
qemu_opt_foreach(opts, net_init_slirp_configs, NULL, 0);
ret = net_slirp_init(vlan, "user", name, restricted, vnet, vhost,
diff --git a/net/socket.c b/net/socket.c
index bc1bf58..11fe5f3 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -76,7 +76,7 @@ static void net_socket_send(void *opaque)
uint8_t buf1[4096];
const uint8_t *buf;
- size = recv(s->fd, (void *)buf1, sizeof(buf1), 0);
+ size = qemu_recv(s->fd, buf1, sizeof(buf1), 0);
if (size < 0) {
err = socket_error();
if (err != EWOULDBLOCK)
@@ -138,7 +138,7 @@ static void net_socket_send_dgram(void *opaque)
NetSocketState *s = opaque;
int size;
- size = recv(s->fd, (void *)s->buf, sizeof(s->buf), 0);
+ size = qemu_recv(s->fd, s->buf, sizeof(s->buf), 0);
if (size < 0)
return;
if (size == 0) {
diff --git a/notify.c b/notify.c
index bcd3fc5..a6bac1f 100644
--- a/notify.c
+++ b/notify.c
@@ -29,11 +29,11 @@ void notifier_list_remove(NotifierList *list, Notifier *notifier)
QTAILQ_REMOVE(&list->notifiers, notifier, node);
}
-void notifier_list_notify(NotifierList *list)
+void notifier_list_notify(NotifierList *list, void *data)
{
Notifier *notifier, *next;
QTAILQ_FOREACH_SAFE(notifier, &list->notifiers, node, next) {
- notifier->notify(notifier);
+ notifier->notify(notifier, data);
}
}
diff --git a/notify.h b/notify.h
index b40522f..54fc57c 100644
--- a/notify.h
+++ b/notify.h
@@ -20,7 +20,7 @@ typedef struct Notifier Notifier;
struct Notifier
{
- void (*notify)(Notifier *notifier);
+ void (*notify)(Notifier *notifier, void *data);
QTAILQ_ENTRY(Notifier) node;
};
@@ -38,6 +38,6 @@ void notifier_list_add(NotifierList *list, Notifier *notifier);
void notifier_list_remove(NotifierList *list, Notifier *notifier);
-void notifier_list_notify(NotifierList *list);
+void notifier_list_notify(NotifierList *list, void *data);
#endif
diff --git a/oslib-posix.c b/oslib-posix.c
index 3a18e86..196099c 100644
--- a/oslib-posix.c
+++ b/oslib-posix.c
@@ -79,7 +79,10 @@ void *qemu_memalign(size_t alignment, size_t size)
/* alloc shared memory pages */
void *qemu_vmalloc(size_t size)
{
- return qemu_memalign(getpagesize(), size);
+ void *ptr;
+ ptr = qemu_memalign(getpagesize(), size);
+ trace_qemu_vmalloc(size, ptr);
+ return ptr;
}
void qemu_vfree(void *ptr)
diff --git a/pc-bios/README b/pc-bios/README
index 40568f8..f74b246 100644
--- a/pc-bios/README
+++ b/pc-bios/README
@@ -10,10 +10,9 @@
- 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),
-
- PowerPC is built from OpenBIOS SVN revision 1044
- Sparc32 and Sparc64 are built from OpenBIOS SVN revision 1018.
+ The included image for PowerPC (for 32 and 64 bit PPC CPUs)
+ is built from OpenBIOS SVN revision 1044 and Sparc32 and Sparc64
+ images are built from OpenBIOS SVN revision 1045.
- SLOF (Slimline Open Firmware) is a free IEEE 1275 Open Firmware
implementation for certain IBM POWER hardware. The sources are at
diff --git a/pc-bios/openbios-sparc32 b/pc-bios/openbios-sparc32
index b2dc5c5..ea9cc32 100644
--- a/pc-bios/openbios-sparc32
+++ b/pc-bios/openbios-sparc32
Binary files differ
diff --git a/pc-bios/openbios-sparc64 b/pc-bios/openbios-sparc64
index 70a223d..7e746b4 100644
--- a/pc-bios/openbios-sparc64
+++ b/pc-bios/openbios-sparc64
Binary files differ
diff --git a/poison.h b/poison.h
index 2b18232..5354e77 100644
--- a/poison.h
+++ b/poison.h
@@ -37,6 +37,15 @@
#pragma GCC poison CPUState
#pragma GCC poison env
+#pragma GCC poison lduw_phys
+#pragma GCC poison ldl_phys
+#pragma GCC poison ldq_phys
+#pragma GCC poison stl_phys_notdirty
+#pragma GCC poison stq_phys_notdirty
+#pragma GCC poison stw_phys
+#pragma GCC poison stl_phys
+#pragma GCC poison stq_phys
+
#pragma GCC poison CPU_INTERRUPT_HARD
#pragma GCC poison CPU_INTERRUPT_EXITTB
#pragma GCC poison CPU_INTERRUPT_HALT
diff --git a/posix-aio-compat.c b/posix-aio-compat.c
index c4116e3..8dc00cb 100644
--- a/posix-aio-compat.c
+++ b/posix-aio-compat.c
@@ -49,8 +49,6 @@ struct qemu_paiocb {
ssize_t ret;
int active;
struct qemu_paiocb *next;
-
- int async_context_id;
};
typedef struct PosixAioState {
@@ -200,6 +198,12 @@ static ssize_t handle_aiocb_rw_vector(struct qemu_paiocb *aiocb)
return len;
}
+/*
+ * Read/writes the data to/from a given linear buffer.
+ *
+ * Returns the number of bytes handles or -errno in case of an error. Short
+ * reads are only returned if the end of the file is reached.
+ */
static ssize_t handle_aiocb_rw_linear(struct qemu_paiocb *aiocb, char *buf)
{
ssize_t offset = 0;
@@ -336,6 +340,19 @@ static void *aio_thread(void *unused)
switch (aiocb->aio_type & QEMU_AIO_TYPE_MASK) {
case QEMU_AIO_READ:
+ ret = handle_aiocb_rw(aiocb);
+ if (ret >= 0 && ret < aiocb->aio_nbytes && aiocb->common.bs->growable) {
+ /* A short read means that we have reached EOF. Pad the buffer
+ * with zeros for bytes after EOF. */
+ QEMUIOVector qiov;
+
+ qemu_iovec_init_external(&qiov, aiocb->aio_iov,
+ aiocb->aio_niov);
+ qemu_iovec_memset_skip(&qiov, 0, aiocb->aio_nbytes - ret, ret);
+
+ ret = aiocb->aio_nbytes;
+ }
+ break;
case QEMU_AIO_WRITE:
ret = handle_aiocb_rw(aiocb);
break;
@@ -420,7 +437,6 @@ static int posix_aio_process_queue(void *opaque)
struct qemu_paiocb *acb, **pacb;
int ret;
int result = 0;
- int async_context_id = get_async_context_id();
for(;;) {
pacb = &s->first_aio;
@@ -429,12 +445,6 @@ static int posix_aio_process_queue(void *opaque)
if (!acb)
return result;
- /* we're only interested in requests in the right context */
- if (acb->async_context_id != async_context_id) {
- pacb = &acb->next;
- continue;
- }
-
ret = qemu_paio_error(acb);
if (ret == ECANCELED) {
/* remove the request */
@@ -575,7 +585,6 @@ BlockDriverAIOCB *paio_submit(BlockDriverState *bs, int fd,
acb->aio_type = type;
acb->aio_fildes = fd;
acb->ev_signo = SIGUSR2;
- acb->async_context_id = get_async_context_id();
if (qiov) {
acb->aio_iov = qiov->iov;
@@ -604,7 +613,6 @@ 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/qapi-schema-guest.json b/qapi-schema-guest.json
new file mode 100644
index 0000000..fde5971
--- /dev/null
+++ b/qapi-schema-guest.json
@@ -0,0 +1,217 @@
+# *-*- Mode: Python -*-*
+
+##
+# @guest-sync:
+#
+# Echo back a unique integer value
+#
+# This is used by clients talking to the guest agent over the
+# wire to ensure the stream is in sync and doesn't contain stale
+# data from previous client. All guest agent responses should be
+# ignored until the provided unique integer value is returned,
+# and it is up to the client to handle stale whole or
+# partially-delivered JSON text in such a way that this response
+# can be obtained.
+#
+# Such clients should also preceed this command
+# with a 0xFF byte to make such the guest agent flushes any
+# partially read JSON data from a previous session.
+#
+# @id: randomly generated 64-bit integer
+#
+# Returns: The unique integer id passed in by the client
+#
+# Since: 0.15.0
+##
+{ 'command': 'guest-sync'
+ 'data': { 'id': 'int' },
+ 'returns': 'int' }
+
+##
+# @guest-ping:
+#
+# Ping the guest agent, a non-error return implies success
+#
+# Since: 0.15.0
+##
+{ 'command': 'guest-ping' }
+
+##
+# @guest-info:
+#
+# Get some information about the guest agent.
+#
+# Since: 0.15.0
+##
+{ 'type': 'GuestAgentInfo', 'data': {'version': 'str'} }
+{ 'command': 'guest-info',
+ 'returns': 'GuestAgentInfo' }
+
+##
+# @guest-shutdown:
+#
+# Initiate guest-activated shutdown. Note: this is an asynchronous
+# shutdown request, with no guaruntee of successful shutdown. Errors
+# will be logged to guest's syslog.
+#
+# @mode: #optional "halt", "powerdown" (default), or "reboot"
+#
+# Returns: Nothing on success
+#
+# Since: 0.15.0
+##
+{ 'command': 'guest-shutdown', 'data': { '*mode': 'str' } }
+
+##
+# @guest-file-open:
+#
+# Open a file in the guest and retrieve a file handle for it
+#
+# @filepath: Full path to the file in the guest to open.
+#
+# @mode: #optional open mode, as per fopen(), "r" is the default.
+#
+# Returns: Guest file handle on success.
+#
+# Since: 0.15.0
+##
+{ 'command': 'guest-file-open',
+ 'data': { 'path': 'str', '*mode': 'str' },
+ 'returns': 'int' }
+
+##
+# @guest-file-close:
+#
+# Close an open file in the guest
+#
+# @handle: filehandle returned by guest-file-open
+#
+# Returns: Nothing on success.
+#
+# Since: 0.15.0
+##
+{ 'command': 'guest-file-close',
+ 'data': { 'handle': 'int' } }
+
+##
+# @guest-file-read:
+#
+# Read from an open file in the guest. Data will be base64-encoded
+#
+# @handle: filehandle returned by guest-file-open
+#
+# @count: #optional maximum number of bytes to read (default is 4KB)
+#
+# Returns: GuestFileRead on success. Note: count is number of bytes read
+# *before* base64 encoding bytes read.
+#
+# Since: 0.15.0
+##
+{ 'type': 'GuestFileRead',
+ 'data': { 'count': 'int', 'buf-b64': 'str', 'eof': 'bool' } }
+
+{ 'command': 'guest-file-read',
+ 'data': { 'handle': 'int', '*count': 'int' },
+ 'returns': 'GuestFileRead' }
+
+##
+# @guest-file-write:
+#
+# Write to an open file in the guest.
+#
+# @handle: filehandle returned by guest-file-open
+#
+# @buf-b64: base64-encoded string representing data to be written
+#
+# @count: #optional bytes to write (actual bytes, after base64-decode),
+# default is all content in buf-b64 buffer after base64 decoding
+#
+# Returns: GuestFileWrite on success. Note: count is the number of bytes
+# base64-decoded bytes written
+#
+# Since: 0.15.0
+##
+{ 'type': 'GuestFileWrite',
+ 'data': { 'count': 'int', 'eof': 'bool' } }
+{ 'command': 'guest-file-write',
+ 'data': { 'handle': 'int', 'buf-b64': 'str', '*count': 'int' },
+ 'returns': 'GuestFileWrite' }
+
+##
+# @guest-file-seek:
+#
+# Seek to a position in the file, as with fseek(), and return the
+# current file position afterward. Also encapsulates ftell()'s
+# functionality, just Set offset=0, whence=SEEK_CUR.
+#
+# @handle: filehandle returned by guest-file-open
+#
+# @offset: bytes to skip over in the file stream
+#
+# @whence: SEEK_SET, SEEK_CUR, or SEEK_END, as with fseek()
+#
+# Returns: GuestFileSeek on success.
+#
+# Since: 0.15.0
+##
+{ 'type': 'GuestFileSeek',
+ 'data': { 'position': 'int', 'eof': 'bool' } }
+
+{ 'command': 'guest-file-seek',
+ 'data': { 'handle': 'int', 'offset': 'int', 'whence': 'int' },
+ 'returns': 'GuestFileSeek' }
+
+##
+# @guest-file-flush:
+#
+# Write file changes bufferred in userspace to disk/kernel buffers
+#
+# @handle: filehandle returned by guest-file-open
+#
+# Returns: Nothing on success.
+#
+# Since: 0.15.0
+##
+{ 'command': 'guest-file-flush',
+ 'data': { 'handle': 'int' } }
+
+##
+# @guest-fsfreeze-status:
+#
+# Get guest fsfreeze state. error state indicates failure to thaw 1 or more
+# previously frozen filesystems, or failure to open a previously cached
+# filesytem (filesystem unmounted/directory changes, etc).
+#
+# Returns: GuestFsfreezeStatus ("thawed", "frozen", etc., as defined below)
+#
+# Since: 0.15.0
+##
+{ 'enum': 'GuestFsfreezeStatus',
+ 'data': [ 'thawed', 'frozen', 'error' ] }
+{ 'command': 'guest-fsfreeze-status',
+ 'returns': 'GuestFsfreezeStatus' }
+
+##
+# @guest-fsfreeze-freeze:
+#
+# Sync and freeze all non-network guest filesystems
+#
+# Returns: Number of file systems frozen on success
+#
+# Since: 0.15.0
+##
+{ 'command': 'guest-fsfreeze-freeze',
+ 'returns': 'int' }
+
+##
+# @guest-fsfreeze-thaw:
+#
+# Unfreeze frozen guest fileystems
+#
+# Returns: Number of file systems thawed
+# If error, -1 (unknown error) or -errno
+#
+# Since: 0.15.0
+##
+{ 'command': 'guest-fsfreeze-thaw',
+ 'returns': 'int' }
diff --git a/qapi-schema-test.json b/qapi-schema-test.json
new file mode 100644
index 0000000..3acedad
--- /dev/null
+++ b/qapi-schema-test.json
@@ -0,0 +1,22 @@
+# *-*- Mode: Python -*-*
+
+# for testing enums
+{ 'enum': 'EnumOne',
+ 'data': [ 'value1', 'value2', 'value3' ] }
+{ 'type': 'NestedEnumsOne',
+ 'data': { 'enum1': 'EnumOne', '*enum2': 'EnumOne', 'enum3': 'EnumOne', '*enum4': 'EnumOne' } }
+
+# for testing nested structs
+{ 'type': 'UserDefOne',
+ 'data': { 'integer': 'int', 'string': 'str' } }
+
+{ 'type': 'UserDefTwo',
+ 'data': { 'string': 'str',
+ 'dict': { 'string': 'str',
+ 'dict': { 'userdef': 'UserDefOne', 'string': 'str' },
+ '*dict2': { 'userdef': 'UserDefOne', 'string': 'str' } } } }
+
+# testing commands
+{ 'command': 'user_def_cmd', 'data': {} }
+{ 'command': 'user_def_cmd1', 'data': {'ud1a': 'UserDefOne'} }
+{ 'command': 'user_def_cmd2', 'data': {'ud1a': 'UserDefOne', 'ud1b': 'UserDefOne'}, 'returns': 'UserDefTwo' }
diff --git a/qapi/qapi-dealloc-visitor.c b/qapi/qapi-dealloc-visitor.c
new file mode 100644
index 0000000..8cde4dd
--- /dev/null
+++ b/qapi/qapi-dealloc-visitor.c
@@ -0,0 +1,147 @@
+/*
+ * Dealloc Visitor
+ *
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ * Michael Roth <mdroth@linux.vnet.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#include "qapi-dealloc-visitor.h"
+#include "qemu-queue.h"
+#include "qemu-common.h"
+#include "qemu-objects.h"
+
+typedef struct StackEntry
+{
+ void *value;
+ QTAILQ_ENTRY(StackEntry) node;
+} StackEntry;
+
+struct QapiDeallocVisitor
+{
+ Visitor visitor;
+ QTAILQ_HEAD(, StackEntry) stack;
+};
+
+static QapiDeallocVisitor *to_qov(Visitor *v)
+{
+ return container_of(v, QapiDeallocVisitor, visitor);
+}
+
+static void qapi_dealloc_push(QapiDeallocVisitor *qov, void *value)
+{
+ StackEntry *e = qemu_mallocz(sizeof(*e));
+
+ e->value = value;
+ QTAILQ_INSERT_HEAD(&qov->stack, e, node);
+}
+
+static void *qapi_dealloc_pop(QapiDeallocVisitor *qov)
+{
+ StackEntry *e = QTAILQ_FIRST(&qov->stack);
+ QObject *value;
+ QTAILQ_REMOVE(&qov->stack, e, node);
+ value = e->value;
+ qemu_free(e);
+ return value;
+}
+
+static void qapi_dealloc_start_struct(Visitor *v, void **obj, const char *kind,
+ const char *name, size_t unused,
+ Error **errp)
+{
+ QapiDeallocVisitor *qov = to_qov(v);
+ qapi_dealloc_push(qov, obj);
+}
+
+static void qapi_dealloc_end_struct(Visitor *v, Error **errp)
+{
+ QapiDeallocVisitor *qov = to_qov(v);
+ void **obj = qapi_dealloc_pop(qov);
+ if (obj) {
+ qemu_free(*obj);
+ }
+}
+
+static void qapi_dealloc_start_list(Visitor *v, const char *name, Error **errp)
+{
+}
+
+static GenericList *qapi_dealloc_next_list(Visitor *v, GenericList **list,
+ Error **errp)
+{
+ GenericList *retval = *list;
+ qemu_free(retval->value);
+ *list = retval->next;
+ return retval;
+}
+
+static void qapi_dealloc_end_list(Visitor *v, Error **errp)
+{
+}
+
+static void qapi_dealloc_type_str(Visitor *v, char **obj, const char *name,
+ Error **errp)
+{
+ if (obj) {
+ qemu_free(*obj);
+ }
+}
+
+static void qapi_dealloc_type_int(Visitor *v, int64_t *obj, const char *name,
+ Error **errp)
+{
+}
+
+static void qapi_dealloc_type_bool(Visitor *v, bool *obj, const char *name,
+ Error **errp)
+{
+}
+
+static void qapi_dealloc_type_number(Visitor *v, double *obj, const char *name,
+ Error **errp)
+{
+}
+
+static void qapi_dealloc_type_enum(Visitor *v, int *obj, const char *strings[],
+ const char *kind, const char *name,
+ Error **errp)
+{
+}
+
+Visitor *qapi_dealloc_get_visitor(QapiDeallocVisitor *v)
+{
+ return &v->visitor;
+}
+
+void qapi_dealloc_visitor_cleanup(QapiDeallocVisitor *v)
+{
+ qemu_free(v);
+}
+
+QapiDeallocVisitor *qapi_dealloc_visitor_new(void)
+{
+ QapiDeallocVisitor *v;
+
+ v = qemu_mallocz(sizeof(*v));
+
+ v->visitor.start_struct = qapi_dealloc_start_struct;
+ v->visitor.end_struct = qapi_dealloc_end_struct;
+ v->visitor.start_list = qapi_dealloc_start_list;
+ v->visitor.next_list = qapi_dealloc_next_list;
+ v->visitor.end_list = qapi_dealloc_end_list;
+ v->visitor.type_enum = qapi_dealloc_type_enum;
+ v->visitor.type_int = qapi_dealloc_type_int;
+ v->visitor.type_bool = qapi_dealloc_type_bool;
+ v->visitor.type_str = qapi_dealloc_type_str;
+ v->visitor.type_number = qapi_dealloc_type_number;
+
+ QTAILQ_INIT(&v->stack);
+
+ return v;
+}
diff --git a/qapi/qapi-dealloc-visitor.h b/qapi/qapi-dealloc-visitor.h
new file mode 100644
index 0000000..5842bc7
--- /dev/null
+++ b/qapi/qapi-dealloc-visitor.h
@@ -0,0 +1,26 @@
+/*
+ * Dealloc Visitor
+ *
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ * Michael Roth <mdroth@linux.vnet.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#ifndef QAPI_DEALLOC_VISITOR_H
+#define QAPI_DEALLOC_VISITOR_H
+
+#include "qapi-visit-core.h"
+
+typedef struct QapiDeallocVisitor QapiDeallocVisitor;
+
+QapiDeallocVisitor *qapi_dealloc_visitor_new(void);
+void qapi_dealloc_visitor_cleanup(QapiDeallocVisitor *d);
+
+Visitor *qapi_dealloc_get_visitor(QapiDeallocVisitor *v);
+
+#endif
diff --git a/qapi/qapi-types-core.h b/qapi/qapi-types-core.h
new file mode 100644
index 0000000..a79bc2b
--- /dev/null
+++ b/qapi/qapi-types-core.h
@@ -0,0 +1,20 @@
+/*
+ * Core Definitions for QAPI-generated Types
+ *
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#ifndef QAPI_TYPES_CORE_H
+#define QAPI_TYPES_CORE_H
+
+#include "qemu-common.h"
+#include "error.h"
+
+#endif
diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
new file mode 100644
index 0000000..ddef3ed
--- /dev/null
+++ b/qapi/qapi-visit-core.c
@@ -0,0 +1,118 @@
+/*
+ * Core Definitions for QAPI Visitor Classes
+ *
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#include "qapi/qapi-visit-core.h"
+
+void visit_start_handle(Visitor *v, void **obj, const char *kind,
+ const char *name, Error **errp)
+{
+ if (!error_is_set(errp) && v->start_handle) {
+ v->start_handle(v, obj, kind, name, errp);
+ }
+}
+
+void visit_end_handle(Visitor *v, Error **errp)
+{
+ if (!error_is_set(errp) && v->end_handle) {
+ v->end_handle(v, errp);
+ }
+}
+
+void visit_start_struct(Visitor *v, void **obj, const char *kind,
+ const char *name, size_t size, Error **errp)
+{
+ if (!error_is_set(errp)) {
+ v->start_struct(v, obj, kind, name, size, errp);
+ }
+}
+
+void visit_end_struct(Visitor *v, Error **errp)
+{
+ if (!error_is_set(errp)) {
+ v->end_struct(v, errp);
+ }
+}
+
+void visit_start_list(Visitor *v, const char *name, Error **errp)
+{
+ if (!error_is_set(errp)) {
+ v->start_list(v, name, errp);
+ }
+}
+
+GenericList *visit_next_list(Visitor *v, GenericList **list, Error **errp)
+{
+ if (!error_is_set(errp)) {
+ return v->next_list(v, list, errp);
+ }
+
+ return 0;
+}
+
+void visit_end_list(Visitor *v, Error **errp)
+{
+ if (!error_is_set(errp)) {
+ v->end_list(v, errp);
+ }
+}
+
+void visit_start_optional(Visitor *v, bool *present, const char *name,
+ Error **errp)
+{
+ if (!error_is_set(errp) && v->start_optional) {
+ v->start_optional(v, present, name, errp);
+ }
+}
+
+void visit_end_optional(Visitor *v, Error **errp)
+{
+ if (!error_is_set(errp) && v->end_optional) {
+ v->end_optional(v, errp);
+ }
+}
+
+void visit_type_enum(Visitor *v, int *obj, const char *strings[],
+ const char *kind, const char *name, Error **errp)
+{
+ if (!error_is_set(errp)) {
+ v->type_enum(v, obj, strings, kind, name, errp);
+ }
+}
+
+void visit_type_int(Visitor *v, int64_t *obj, const char *name, Error **errp)
+{
+ if (!error_is_set(errp)) {
+ v->type_int(v, obj, name, errp);
+ }
+}
+
+void visit_type_bool(Visitor *v, bool *obj, const char *name, Error **errp)
+{
+ if (!error_is_set(errp)) {
+ v->type_bool(v, obj, name, errp);
+ }
+}
+
+void visit_type_str(Visitor *v, char **obj, const char *name, Error **errp)
+{
+ if (!error_is_set(errp)) {
+ v->type_str(v, obj, name, errp);
+ }
+}
+
+void visit_type_number(Visitor *v, double *obj, const char *name, Error **errp)
+{
+ if (!error_is_set(errp)) {
+ v->type_number(v, obj, name, errp);
+ }
+}
diff --git a/qapi/qapi-visit-core.h b/qapi/qapi-visit-core.h
new file mode 100644
index 0000000..e850746
--- /dev/null
+++ b/qapi/qapi-visit-core.h
@@ -0,0 +1,76 @@
+/*
+ * Core Definitions for QAPI Visitor Classes
+ *
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+#ifndef QAPI_VISITOR_CORE_H
+#define QAPI_VISITOR_CORE_H
+
+#include "qapi/qapi-types-core.h"
+#include <stdlib.h>
+
+typedef struct GenericList
+{
+ void *value;
+ struct GenericList *next;
+} GenericList;
+
+typedef struct Visitor Visitor;
+
+struct Visitor
+{
+ /* Must be set */
+ void (*start_struct)(Visitor *v, void **obj, const char *kind,
+ const char *name, size_t size, Error **errp);
+ void (*end_struct)(Visitor *v, Error **errp);
+
+ void (*start_list)(Visitor *v, const char *name, Error **errp);
+ GenericList *(*next_list)(Visitor *v, GenericList **list, Error **errp);
+ void (*end_list)(Visitor *v, Error **errp);
+
+ void (*type_enum)(Visitor *v, int *obj, const char *strings[],
+ const char *kind, const char *name, Error **errp);
+
+ void (*type_int)(Visitor *v, int64_t *obj, const char *name, Error **errp);
+ void (*type_bool)(Visitor *v, bool *obj, const char *name, Error **errp);
+ void (*type_str)(Visitor *v, char **obj, const char *name, Error **errp);
+ void (*type_number)(Visitor *v, double *obj, const char *name,
+ Error **errp);
+
+ /* May be NULL */
+ void (*start_optional)(Visitor *v, bool *present, const char *name,
+ Error **errp);
+ void (*end_optional)(Visitor *v, Error **errp);
+
+ void (*start_handle)(Visitor *v, void **obj, const char *kind,
+ const char *name, Error **errp);
+ void (*end_handle)(Visitor *v, Error **errp);
+};
+
+void visit_start_handle(Visitor *v, void **obj, const char *kind,
+ const char *name, Error **errp);
+void visit_end_handle(Visitor *v, Error **errp);
+void visit_start_struct(Visitor *v, void **obj, const char *kind,
+ const char *name, size_t size, Error **errp);
+void visit_end_struct(Visitor *v, Error **errp);
+void visit_start_list(Visitor *v, const char *name, Error **errp);
+GenericList *visit_next_list(Visitor *v, GenericList **list, Error **errp);
+void visit_end_list(Visitor *v, Error **errp);
+void visit_start_optional(Visitor *v, bool *present, const char *name,
+ Error **errp);
+void visit_end_optional(Visitor *v, Error **errp);
+void visit_type_enum(Visitor *v, int *obj, const char *strings[],
+ const char *kind, const char *name, Error **errp);
+void visit_type_int(Visitor *v, int64_t *obj, const char *name, Error **errp);
+void visit_type_bool(Visitor *v, bool *obj, const char *name, Error **errp);
+void visit_type_str(Visitor *v, char **obj, const char *name, Error **errp);
+void visit_type_number(Visitor *v, double *obj, const char *name, Error **errp);
+
+#endif
diff --git a/qapi/qmp-core.h b/qapi/qmp-core.h
new file mode 100644
index 0000000..f1c26e4
--- /dev/null
+++ b/qapi/qmp-core.h
@@ -0,0 +1,41 @@
+/*
+ * Core Definitions for QAPI/QMP Dispatch
+ *
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#ifndef QMP_CORE_H
+#define QMP_CORE_H
+
+#include "qobject.h"
+#include "qdict.h"
+#include "error.h"
+
+typedef void (QmpCommandFunc)(QDict *, QObject **, Error **);
+
+typedef enum QmpCommandType
+{
+ QCT_NORMAL,
+} QmpCommandType;
+
+typedef struct QmpCommand
+{
+ const char *name;
+ QmpCommandType type;
+ QmpCommandFunc *fn;
+ QTAILQ_ENTRY(QmpCommand) node;
+} QmpCommand;
+
+void qmp_register_command(const char *name, QmpCommandFunc *fn);
+QmpCommand *qmp_find_command(const char *name);
+QObject *qmp_dispatch(QObject *request);
+
+#endif
+
diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c
new file mode 100644
index 0000000..5584693
--- /dev/null
+++ b/qapi/qmp-dispatch.c
@@ -0,0 +1,124 @@
+/*
+ * Core Definitions for QAPI/QMP Dispatch
+ *
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#include "qemu-objects.h"
+#include "qapi/qmp-core.h"
+#include "json-parser.h"
+#include "error.h"
+#include "error_int.h"
+#include "qerror.h"
+
+static QDict *qmp_dispatch_check_obj(const QObject *request, Error **errp)
+{
+ const QDictEntry *ent;
+ const char *arg_name;
+ const QObject *arg_obj;
+ bool has_exec_key = false;
+ QDict *dict = NULL;
+
+ if (qobject_type(request) != QTYPE_QDICT) {
+ error_set(errp, QERR_QMP_BAD_INPUT_OBJECT,
+ "request is not a dictionary");
+ return NULL;
+ }
+
+ dict = qobject_to_qdict(request);
+
+ for (ent = qdict_first(dict); ent;
+ ent = qdict_next(dict, ent)) {
+ arg_name = qdict_entry_key(ent);
+ arg_obj = qdict_entry_value(ent);
+
+ if (!strcmp(arg_name, "execute")) {
+ if (qobject_type(arg_obj) != QTYPE_QSTRING) {
+ error_set(errp, QERR_QMP_BAD_INPUT_OBJECT_MEMBER, "execute",
+ "string");
+ return NULL;
+ }
+ has_exec_key = true;
+ } else if (strcmp(arg_name, "arguments")) {
+ error_set(errp, QERR_QMP_EXTRA_MEMBER, arg_name);
+ return NULL;
+ }
+ }
+
+ if (!has_exec_key) {
+ error_set(errp, QERR_QMP_BAD_INPUT_OBJECT, "execute");
+ return NULL;
+ }
+
+ return dict;
+}
+
+static QObject *do_qmp_dispatch(QObject *request, Error **errp)
+{
+ const char *command;
+ QDict *args, *dict;
+ QmpCommand *cmd;
+ QObject *ret = NULL;
+
+
+ dict = qmp_dispatch_check_obj(request, errp);
+ if (!dict || error_is_set(errp)) {
+ return NULL;
+ }
+
+ command = qdict_get_str(dict, "execute");
+ cmd = qmp_find_command(command);
+ if (cmd == NULL) {
+ error_set(errp, QERR_COMMAND_NOT_FOUND, command);
+ return NULL;
+ }
+
+ if (!qdict_haskey(dict, "arguments")) {
+ args = qdict_new();
+ } else {
+ args = qdict_get_qdict(dict, "arguments");
+ QINCREF(args);
+ }
+
+ switch (cmd->type) {
+ case QCT_NORMAL:
+ cmd->fn(args, &ret, errp);
+ if (!error_is_set(errp) && ret == NULL) {
+ ret = QOBJECT(qdict_new());
+ }
+ break;
+ }
+
+ QDECREF(args);
+
+ return ret;
+}
+
+QObject *qmp_dispatch(QObject *request)
+{
+ Error *err = NULL;
+ QObject *ret;
+ QDict *rsp;
+
+ ret = do_qmp_dispatch(request, &err);
+
+ rsp = qdict_new();
+ if (err) {
+ qdict_put_obj(rsp, "error", error_get_qobject(err));
+ error_free(err);
+ } else if (ret) {
+ qdict_put_obj(rsp, "return", ret);
+ } else {
+ QDECREF(rsp);
+ return NULL;
+ }
+
+ return QOBJECT(rsp);
+}
diff --git a/qapi/qmp-input-visitor.c b/qapi/qmp-input-visitor.c
new file mode 100644
index 0000000..6a1adc9
--- /dev/null
+++ b/qapi/qmp-input-visitor.c
@@ -0,0 +1,301 @@
+/*
+ * Input Visitor
+ *
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#include "qmp-input-visitor.h"
+#include "qemu-queue.h"
+#include "qemu-common.h"
+#include "qemu-objects.h"
+#include "qerror.h"
+
+#define QIV_STACK_SIZE 1024
+
+typedef struct StackObject
+{
+ const QObject *obj;
+ const QListEntry *entry;
+} StackObject;
+
+struct QmpInputVisitor
+{
+ Visitor visitor;
+ QObject *obj;
+ StackObject stack[QIV_STACK_SIZE];
+ int nb_stack;
+};
+
+static QmpInputVisitor *to_qiv(Visitor *v)
+{
+ return container_of(v, QmpInputVisitor, visitor);
+}
+
+static const QObject *qmp_input_get_object(QmpInputVisitor *qiv,
+ const char *name)
+{
+ const QObject *qobj;
+
+ if (qiv->nb_stack == 0) {
+ qobj = qiv->obj;
+ } else {
+ qobj = qiv->stack[qiv->nb_stack - 1].obj;
+ }
+
+ if (name && qobject_type(qobj) == QTYPE_QDICT) {
+ return qdict_get(qobject_to_qdict(qobj), name);
+ } else if (qiv->nb_stack > 0 && qobject_type(qobj) == QTYPE_QLIST) {
+ return qlist_entry_obj(qiv->stack[qiv->nb_stack - 1].entry);
+ }
+
+ return qobj;
+}
+
+static void qmp_input_push(QmpInputVisitor *qiv, const QObject *obj, Error **errp)
+{
+ qiv->stack[qiv->nb_stack].obj = obj;
+ if (qobject_type(obj) == QTYPE_QLIST) {
+ qiv->stack[qiv->nb_stack].entry = qlist_first(qobject_to_qlist(obj));
+ }
+ qiv->nb_stack++;
+
+ if (qiv->nb_stack >= QIV_STACK_SIZE) {
+ error_set(errp, QERR_BUFFER_OVERRUN);
+ return;
+ }
+}
+
+static void qmp_input_pop(QmpInputVisitor *qiv, Error **errp)
+{
+ qiv->nb_stack--;
+ if (qiv->nb_stack < 0) {
+ error_set(errp, QERR_BUFFER_OVERRUN);
+ return;
+ }
+}
+
+static void qmp_input_start_struct(Visitor *v, void **obj, const char *kind,
+ const char *name, size_t size, Error **errp)
+{
+ QmpInputVisitor *qiv = to_qiv(v);
+ const QObject *qobj = qmp_input_get_object(qiv, name);
+
+ if (!qobj || qobject_type(qobj) != QTYPE_QDICT) {
+ error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
+ "QDict");
+ return;
+ }
+
+ qmp_input_push(qiv, qobj, errp);
+ if (error_is_set(errp)) {
+ return;
+ }
+
+ if (obj) {
+ *obj = qemu_mallocz(size);
+ }
+}
+
+static void qmp_input_end_struct(Visitor *v, Error **errp)
+{
+ QmpInputVisitor *qiv = to_qiv(v);
+
+ qmp_input_pop(qiv, errp);
+}
+
+static void qmp_input_start_list(Visitor *v, const char *name, Error **errp)
+{
+ QmpInputVisitor *qiv = to_qiv(v);
+ const QObject *qobj = qmp_input_get_object(qiv, name);
+
+ if (!qobj || qobject_type(qobj) != QTYPE_QLIST) {
+ error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
+ "list");
+ return;
+ }
+
+ qmp_input_push(qiv, qobj, errp);
+}
+
+static GenericList *qmp_input_next_list(Visitor *v, GenericList **list,
+ Error **errp)
+{
+ QmpInputVisitor *qiv = to_qiv(v);
+ GenericList *entry;
+ StackObject *so = &qiv->stack[qiv->nb_stack - 1];
+
+ if (so->entry == NULL) {
+ return NULL;
+ }
+
+ entry = qemu_mallocz(sizeof(*entry));
+ if (*list) {
+ so->entry = qlist_next(so->entry);
+ if (so->entry == NULL) {
+ qemu_free(entry);
+ return NULL;
+ }
+ (*list)->next = entry;
+ }
+ *list = entry;
+
+
+ return entry;
+}
+
+static void qmp_input_end_list(Visitor *v, Error **errp)
+{
+ QmpInputVisitor *qiv = to_qiv(v);
+
+ qmp_input_pop(qiv, errp);
+}
+
+static void qmp_input_type_int(Visitor *v, int64_t *obj, const char *name,
+ Error **errp)
+{
+ QmpInputVisitor *qiv = to_qiv(v);
+ const QObject *qobj = qmp_input_get_object(qiv, name);
+
+ if (!qobj || qobject_type(qobj) != QTYPE_QINT) {
+ error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
+ "integer");
+ return;
+ }
+
+ *obj = qint_get_int(qobject_to_qint(qobj));
+}
+
+static void qmp_input_type_bool(Visitor *v, bool *obj, const char *name,
+ Error **errp)
+{
+ QmpInputVisitor *qiv = to_qiv(v);
+ const QObject *qobj = qmp_input_get_object(qiv, name);
+
+ if (!qobj || qobject_type(qobj) != QTYPE_QBOOL) {
+ error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
+ "boolean");
+ return;
+ }
+
+ *obj = qbool_get_int(qobject_to_qbool(qobj));
+}
+
+static void qmp_input_type_str(Visitor *v, char **obj, const char *name,
+ Error **errp)
+{
+ QmpInputVisitor *qiv = to_qiv(v);
+ const QObject *qobj = qmp_input_get_object(qiv, name);
+
+ if (!qobj || qobject_type(qobj) != QTYPE_QSTRING) {
+ error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
+ "string");
+ return;
+ }
+
+ *obj = qemu_strdup(qstring_get_str(qobject_to_qstring(qobj)));
+}
+
+static void qmp_input_type_number(Visitor *v, double *obj, const char *name,
+ Error **errp)
+{
+ QmpInputVisitor *qiv = to_qiv(v);
+ const QObject *qobj = qmp_input_get_object(qiv, name);
+
+ if (!qobj || qobject_type(qobj) != QTYPE_QFLOAT) {
+ error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
+ "double");
+ return;
+ }
+
+ *obj = qfloat_get_double(qobject_to_qfloat(qobj));
+}
+
+static void qmp_input_type_enum(Visitor *v, int *obj, const char *strings[],
+ const char *kind, const char *name,
+ Error **errp)
+{
+ int64_t value = 0;
+ char *enum_str;
+
+ assert(strings);
+
+ qmp_input_type_str(v, &enum_str, name, errp);
+ if (error_is_set(errp)) {
+ return;
+ }
+
+ while (strings[value] != NULL) {
+ if (strcmp(strings[value], enum_str) == 0) {
+ break;
+ }
+ value++;
+ }
+
+ if (strings[value] == NULL) {
+ error_set(errp, QERR_INVALID_PARAMETER, name ? name : "null");
+ return;
+ }
+
+ *obj = value;
+}
+
+static void qmp_input_start_optional(Visitor *v, bool *present,
+ const char *name, Error **errp)
+{
+ QmpInputVisitor *qiv = to_qiv(v);
+ const QObject *qobj = qmp_input_get_object(qiv, name);
+
+ if (!qobj) {
+ *present = false;
+ return;
+ }
+
+ *present = true;
+}
+
+static void qmp_input_end_optional(Visitor *v, Error **errp)
+{
+}
+
+Visitor *qmp_input_get_visitor(QmpInputVisitor *v)
+{
+ return &v->visitor;
+}
+
+void qmp_input_visitor_cleanup(QmpInputVisitor *v)
+{
+ qobject_decref(v->obj);
+ qemu_free(v);
+}
+
+QmpInputVisitor *qmp_input_visitor_new(QObject *obj)
+{
+ QmpInputVisitor *v;
+
+ v = qemu_mallocz(sizeof(*v));
+
+ v->visitor.start_struct = qmp_input_start_struct;
+ v->visitor.end_struct = qmp_input_end_struct;
+ v->visitor.start_list = qmp_input_start_list;
+ v->visitor.next_list = qmp_input_next_list;
+ v->visitor.end_list = qmp_input_end_list;
+ v->visitor.type_enum = qmp_input_type_enum;
+ v->visitor.type_int = qmp_input_type_int;
+ v->visitor.type_bool = qmp_input_type_bool;
+ v->visitor.type_str = qmp_input_type_str;
+ v->visitor.type_number = qmp_input_type_number;
+ v->visitor.start_optional = qmp_input_start_optional;
+ v->visitor.end_optional = qmp_input_end_optional;
+
+ v->obj = obj;
+ qobject_incref(v->obj);
+
+ return v;
+}
diff --git a/qapi/qmp-input-visitor.h b/qapi/qmp-input-visitor.h
new file mode 100644
index 0000000..3f798f0
--- /dev/null
+++ b/qapi/qmp-input-visitor.h
@@ -0,0 +1,27 @@
+/*
+ * Input Visitor
+ *
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#ifndef QMP_INPUT_VISITOR_H
+#define QMP_INPUT_VISITOR_H
+
+#include "qapi-visit-core.h"
+#include "qobject.h"
+
+typedef struct QmpInputVisitor QmpInputVisitor;
+
+QmpInputVisitor *qmp_input_visitor_new(QObject *obj);
+void qmp_input_visitor_cleanup(QmpInputVisitor *v);
+
+Visitor *qmp_input_get_visitor(QmpInputVisitor *v);
+
+#endif
diff --git a/qapi/qmp-output-visitor.c b/qapi/qmp-output-visitor.c
new file mode 100644
index 0000000..c398cac
--- /dev/null
+++ b/qapi/qmp-output-visitor.c
@@ -0,0 +1,239 @@
+/*
+ * Core Definitions for QAPI/QMP Command Registry
+ *
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#include "qmp-output-visitor.h"
+#include "qemu-queue.h"
+#include "qemu-common.h"
+#include "qemu-objects.h"
+#include "qerror.h"
+
+typedef struct QStackEntry
+{
+ QObject *value;
+ QTAILQ_ENTRY(QStackEntry) node;
+} QStackEntry;
+
+typedef QTAILQ_HEAD(QStack, QStackEntry) QStack;
+
+struct QmpOutputVisitor
+{
+ Visitor visitor;
+ QStack stack;
+};
+
+#define qmp_output_add(qov, name, value) \
+ qmp_output_add_obj(qov, name, QOBJECT(value))
+#define qmp_output_push(qov, value) qmp_output_push_obj(qov, QOBJECT(value))
+
+static QmpOutputVisitor *to_qov(Visitor *v)
+{
+ return container_of(v, QmpOutputVisitor, visitor);
+}
+
+static void qmp_output_push_obj(QmpOutputVisitor *qov, QObject *value)
+{
+ QStackEntry *e = qemu_mallocz(sizeof(*e));
+
+ e->value = value;
+ QTAILQ_INSERT_HEAD(&qov->stack, e, node);
+}
+
+static QObject *qmp_output_pop(QmpOutputVisitor *qov)
+{
+ QStackEntry *e = QTAILQ_FIRST(&qov->stack);
+ QObject *value;
+ QTAILQ_REMOVE(&qov->stack, e, node);
+ value = e->value;
+ qemu_free(e);
+ return value;
+}
+
+static QObject *qmp_output_first(QmpOutputVisitor *qov)
+{
+ QStackEntry *e = QTAILQ_LAST(&qov->stack, QStack);
+ return e->value;
+}
+
+static QObject *qmp_output_last(QmpOutputVisitor *qov)
+{
+ QStackEntry *e = QTAILQ_FIRST(&qov->stack);
+ return e->value;
+}
+
+static void qmp_output_add_obj(QmpOutputVisitor *qov, const char *name,
+ QObject *value)
+{
+ QObject *cur;
+
+ if (QTAILQ_EMPTY(&qov->stack)) {
+ qmp_output_push_obj(qov, value);
+ return;
+ }
+
+ cur = qmp_output_last(qov);
+
+ switch (qobject_type(cur)) {
+ case QTYPE_QDICT:
+ qdict_put_obj(qobject_to_qdict(cur), name, value);
+ break;
+ case QTYPE_QLIST:
+ qlist_append_obj(qobject_to_qlist(cur), value);
+ break;
+ default:
+ qobject_decref(qmp_output_pop(qov));
+ qmp_output_push_obj(qov, value);
+ break;
+ }
+}
+
+static void qmp_output_start_struct(Visitor *v, void **obj, const char *kind,
+ const char *name, size_t unused,
+ Error **errp)
+{
+ QmpOutputVisitor *qov = to_qov(v);
+ QDict *dict = qdict_new();
+
+ qmp_output_add(qov, name, dict);
+ qmp_output_push(qov, dict);
+}
+
+static void qmp_output_end_struct(Visitor *v, Error **errp)
+{
+ QmpOutputVisitor *qov = to_qov(v);
+ qmp_output_pop(qov);
+}
+
+static void qmp_output_start_list(Visitor *v, const char *name, Error **errp)
+{
+ QmpOutputVisitor *qov = to_qov(v);
+ QList *list = qlist_new();
+
+ qmp_output_add(qov, name, list);
+ qmp_output_push(qov, list);
+}
+
+static GenericList *qmp_output_next_list(Visitor *v, GenericList **list,
+ Error **errp)
+{
+ GenericList *retval = *list;
+ *list = retval->next;
+ return retval;
+}
+
+static void qmp_output_end_list(Visitor *v, Error **errp)
+{
+ QmpOutputVisitor *qov = to_qov(v);
+ qmp_output_pop(qov);
+}
+
+static void qmp_output_type_int(Visitor *v, int64_t *obj, const char *name,
+ Error **errp)
+{
+ QmpOutputVisitor *qov = to_qov(v);
+ qmp_output_add(qov, name, qint_from_int(*obj));
+}
+
+static void qmp_output_type_bool(Visitor *v, bool *obj, const char *name,
+ Error **errp)
+{
+ QmpOutputVisitor *qov = to_qov(v);
+ qmp_output_add(qov, name, qbool_from_int(*obj));
+}
+
+static void qmp_output_type_str(Visitor *v, char **obj, const char *name,
+ Error **errp)
+{
+ QmpOutputVisitor *qov = to_qov(v);
+ if (*obj) {
+ qmp_output_add(qov, name, qstring_from_str(*obj));
+ } else {
+ qmp_output_add(qov, name, qstring_from_str(""));
+ }
+}
+
+static void qmp_output_type_number(Visitor *v, double *obj, const char *name,
+ Error **errp)
+{
+ QmpOutputVisitor *qov = to_qov(v);
+ qmp_output_add(qov, name, qfloat_from_double(*obj));
+}
+
+static void qmp_output_type_enum(Visitor *v, int *obj, const char *strings[],
+ const char *kind, const char *name,
+ Error **errp)
+{
+ int i = 0;
+ int value = *obj;
+ char *enum_str;
+
+ assert(strings);
+ while (strings[i++] != NULL);
+ if (value >= i - 1) {
+ error_set(errp, QERR_INVALID_PARAMETER, name ? name : "null");
+ return;
+ }
+
+ enum_str = (char *)strings[value];
+ qmp_output_type_str(v, &enum_str, name, errp);
+}
+
+QObject *qmp_output_get_qobject(QmpOutputVisitor *qov)
+{
+ QObject *obj = qmp_output_first(qov);
+ if (obj) {
+ qobject_incref(obj);
+ }
+ return obj;
+}
+
+Visitor *qmp_output_get_visitor(QmpOutputVisitor *v)
+{
+ return &v->visitor;
+}
+
+void qmp_output_visitor_cleanup(QmpOutputVisitor *v)
+{
+ QStackEntry *e, *tmp;
+
+ QTAILQ_FOREACH_SAFE(e, &v->stack, node, tmp) {
+ QTAILQ_REMOVE(&v->stack, e, node);
+ if (e->value) {
+ qobject_decref(e->value);
+ }
+ qemu_free(e);
+ }
+
+ qemu_free(v);
+}
+
+QmpOutputVisitor *qmp_output_visitor_new(void)
+{
+ QmpOutputVisitor *v;
+
+ v = qemu_mallocz(sizeof(*v));
+
+ v->visitor.start_struct = qmp_output_start_struct;
+ v->visitor.end_struct = qmp_output_end_struct;
+ v->visitor.start_list = qmp_output_start_list;
+ v->visitor.next_list = qmp_output_next_list;
+ v->visitor.end_list = qmp_output_end_list;
+ v->visitor.type_enum = qmp_output_type_enum;
+ v->visitor.type_int = qmp_output_type_int;
+ v->visitor.type_bool = qmp_output_type_bool;
+ v->visitor.type_str = qmp_output_type_str;
+ v->visitor.type_number = qmp_output_type_number;
+
+ QTAILQ_INIT(&v->stack);
+
+ return v;
+}
diff --git a/qapi/qmp-output-visitor.h b/qapi/qmp-output-visitor.h
new file mode 100644
index 0000000..4a649c2
--- /dev/null
+++ b/qapi/qmp-output-visitor.h
@@ -0,0 +1,28 @@
+/*
+ * Output Visitor
+ *
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#ifndef QMP_OUTPUT_VISITOR_H
+#define QMP_OUTPUT_VISITOR_H
+
+#include "qapi-visit-core.h"
+#include "qobject.h"
+
+typedef struct QmpOutputVisitor QmpOutputVisitor;
+
+QmpOutputVisitor *qmp_output_visitor_new(void);
+void qmp_output_visitor_cleanup(QmpOutputVisitor *v);
+
+QObject *qmp_output_get_qobject(QmpOutputVisitor *v);
+Visitor *qmp_output_get_visitor(QmpOutputVisitor *v);
+
+#endif
diff --git a/qapi/qmp-registry.c b/qapi/qmp-registry.c
new file mode 100644
index 0000000..3fe8866
--- /dev/null
+++ b/qapi/qmp-registry.c
@@ -0,0 +1,40 @@
+/*
+ * Core Definitions for QAPI/QMP Dispatch
+ *
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ * Michael Roth <mdroth@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#include "qapi/qmp-core.h"
+
+static QTAILQ_HEAD(, QmpCommand) qmp_commands =
+ QTAILQ_HEAD_INITIALIZER(qmp_commands);
+
+void qmp_register_command(const char *name, QmpCommandFunc *fn)
+{
+ QmpCommand *cmd = qemu_mallocz(sizeof(*cmd));
+
+ cmd->name = name;
+ cmd->type = QCT_NORMAL;
+ cmd->fn = fn;
+ QTAILQ_INSERT_TAIL(&qmp_commands, cmd, node);
+}
+
+QmpCommand *qmp_find_command(const char *name)
+{
+ QmpCommand *i;
+
+ QTAILQ_FOREACH(i, &qmp_commands, node) {
+ if (strcmp(i->name, name) == 0) {
+ return i;
+ }
+ }
+ return NULL;
+}
diff --git a/qemu-char.c b/qemu-char.c
index fb13b28..8e8cf31 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -168,6 +168,11 @@ int qemu_chr_get_msgfd(CharDriverState *s)
return s->get_msgfd ? s->get_msgfd(s) : -1;
}
+int qemu_chr_add_client(CharDriverState *s, int fd)
+{
+ return s->chr_add_client ? s->chr_add_client(s, fd) : -1;
+}
+
void qemu_chr_accept_input(CharDriverState *s)
{
if (s->chr_accept_input)
@@ -219,13 +224,15 @@ static int null_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
return len;
}
-static CharDriverState *qemu_chr_open_null(QemuOpts *opts)
+static int qemu_chr_open_null(QemuOpts *opts, CharDriverState **_chr)
{
CharDriverState *chr;
chr = qemu_mallocz(sizeof(CharDriverState));
chr->chr_write = null_chr_write;
- return chr;
+
+ *_chr= chr;
+ return 0;
}
/* MUX driver for serial I/O splitting */
@@ -634,18 +641,21 @@ static CharDriverState *qemu_chr_open_fd(int fd_in, int fd_out)
return chr;
}
-static CharDriverState *qemu_chr_open_file_out(QemuOpts *opts)
+static int qemu_chr_open_file_out(QemuOpts *opts, CharDriverState **_chr)
{
int fd_out;
TFR(fd_out = qemu_open(qemu_opt_get(opts, "path"),
O_WRONLY | O_TRUNC | O_CREAT | O_BINARY, 0666));
- if (fd_out < 0)
- return NULL;
- return qemu_chr_open_fd(-1, fd_out);
+ if (fd_out < 0) {
+ return -errno;
+ }
+
+ *_chr = qemu_chr_open_fd(-1, fd_out);
+ return 0;
}
-static CharDriverState *qemu_chr_open_pipe(QemuOpts *opts)
+static int qemu_chr_open_pipe(QemuOpts *opts, CharDriverState **_chr)
{
int fd_in, fd_out;
char filename_in[256], filename_out[256];
@@ -653,7 +663,7 @@ static CharDriverState *qemu_chr_open_pipe(QemuOpts *opts)
if (filename == NULL) {
fprintf(stderr, "chardev: pipe: no filename given\n");
- return NULL;
+ return -EINVAL;
}
snprintf(filename_in, 256, "%s.in", filename);
@@ -665,11 +675,14 @@ static CharDriverState *qemu_chr_open_pipe(QemuOpts *opts)
close(fd_in);
if (fd_out >= 0)
close(fd_out);
- TFR(fd_in = fd_out = open(filename, O_RDWR | O_BINARY));
- if (fd_in < 0)
- return NULL;
+ TFR(fd_in = fd_out = qemu_open(filename, O_RDWR | O_BINARY));
+ if (fd_in < 0) {
+ return -errno;
+ }
}
- return qemu_chr_open_fd(fd_in, fd_out);
+
+ *_chr = qemu_chr_open_fd(fd_in, fd_out);
+ return 0;
}
@@ -760,12 +773,14 @@ static void qemu_chr_close_stdio(struct CharDriverState *chr)
fd_chr_close(chr);
}
-static CharDriverState *qemu_chr_open_stdio(QemuOpts *opts)
+static int qemu_chr_open_stdio(QemuOpts *opts, CharDriverState **_chr)
{
CharDriverState *chr;
- if (stdio_nb_clients >= STDIO_MAX_CLIENTS)
- return NULL;
+ if (stdio_nb_clients >= STDIO_MAX_CLIENTS) {
+ return -EBUSY;
+ }
+
if (stdio_nb_clients == 0) {
old_fd0_flags = fcntl(0, F_GETFL);
tcgetattr (0, &oldtty);
@@ -782,7 +797,8 @@ static CharDriverState *qemu_chr_open_stdio(QemuOpts *opts)
display_type != DT_NOGRAPHIC);
qemu_chr_set_echo(chr, false);
- return chr;
+ *_chr = chr;
+ return 0;
}
#ifdef __sun__
@@ -969,7 +985,7 @@ static void pty_chr_close(struct CharDriverState *chr)
qemu_chr_event(chr, CHR_EVENT_CLOSED);
}
-static CharDriverState *qemu_chr_open_pty(QemuOpts *opts)
+static int qemu_chr_open_pty(QemuOpts *opts, CharDriverState **_chr)
{
CharDriverState *chr;
PtyCharDriver *s;
@@ -987,7 +1003,7 @@ static CharDriverState *qemu_chr_open_pty(QemuOpts *opts)
s = qemu_mallocz(sizeof(PtyCharDriver));
if (openpty(&s->fd, &slave_fd, pty_name, NULL, NULL) < 0) {
- return NULL;
+ return -errno;
}
/* Set raw attributes on the pty. */
@@ -1009,7 +1025,8 @@ static CharDriverState *qemu_chr_open_pty(QemuOpts *opts)
s->timer = qemu_new_timer_ms(rt_clock, pty_chr_timer, chr);
- return chr;
+ *_chr = chr;
+ return 0;
}
static void tty_serial_init(int fd, int speed,
@@ -1210,30 +1227,28 @@ static void qemu_chr_close_tty(CharDriverState *chr)
}
}
-static CharDriverState *qemu_chr_open_tty(QemuOpts *opts)
+static int qemu_chr_open_tty(QemuOpts *opts, CharDriverState **_chr)
{
const char *filename = qemu_opt_get(opts, "path");
CharDriverState *chr;
int fd;
- TFR(fd = open(filename, O_RDWR | O_NONBLOCK));
+ TFR(fd = qemu_open(filename, O_RDWR | O_NONBLOCK));
if (fd < 0) {
- return NULL;
+ return -errno;
}
tty_serial_init(fd, 115200, 'N', 8, 1);
chr = qemu_chr_open_fd(fd, fd);
- if (!chr) {
- close(fd);
- return NULL;
- }
chr->chr_ioctl = tty_serial_ioctl;
chr->chr_close = qemu_chr_close_tty;
- return chr;
+
+ *_chr = chr;
+ return 0;
}
#else /* ! __linux__ && ! __sun__ */
-static CharDriverState *qemu_chr_open_pty(QemuOpts *opts)
+static int qemu_chr_open_pty(QemuOpts *opts, CharDriverState **_chr)
{
- return NULL;
+ return -ENOTSUP;
}
#endif /* __linux__ || __sun__ */
@@ -1347,7 +1362,7 @@ static void pp_close(CharDriverState *chr)
qemu_chr_event(chr, CHR_EVENT_CLOSED);
}
-static CharDriverState *qemu_chr_open_pp(QemuOpts *opts)
+static int qemu_chr_open_pp(QemuOpts *opts, CharDriverState **_chr)
{
const char *filename = qemu_opt_get(opts, "path");
CharDriverState *chr;
@@ -1355,12 +1370,13 @@ static CharDriverState *qemu_chr_open_pp(QemuOpts *opts)
int fd;
TFR(fd = open(filename, O_RDWR));
- if (fd < 0)
- return NULL;
+ if (fd < 0) {
+ return -errno;
+ }
if (ioctl(fd, PPCLAIM) < 0) {
close(fd);
- return NULL;
+ return -errno;
}
drv = qemu_mallocz(sizeof(ParallelCharDriver));
@@ -1375,7 +1391,8 @@ static CharDriverState *qemu_chr_open_pp(QemuOpts *opts)
qemu_chr_generic_open(chr);
- return chr;
+ *_chr = chr;
+ return 0;
}
#endif /* __linux__ */
@@ -1417,21 +1434,24 @@ static int pp_ioctl(CharDriverState *chr, int cmd, void *arg)
return 0;
}
-static CharDriverState *qemu_chr_open_pp(QemuOpts *opts)
+static int qemu_chr_open_pp(QemuOpts *opts, CharDriverState **_chr)
{
const char *filename = qemu_opt_get(opts, "path");
CharDriverState *chr;
int fd;
- fd = open(filename, O_RDWR);
- if (fd < 0)
- return NULL;
+ fd = qemu_open(filename, O_RDWR);
+ if (fd < 0) {
+ return -errno;
+ }
chr = qemu_mallocz(sizeof(CharDriverState));
chr->opaque = (void *)(intptr_t)fd;
chr->chr_write = null_chr_write;
chr->chr_ioctl = pp_ioctl;
- return chr;
+
+ *_chr = chr;
+ return 0;
}
#endif
@@ -1637,7 +1657,7 @@ static int win_chr_poll(void *opaque)
return 0;
}
-static CharDriverState *qemu_chr_open_win(QemuOpts *opts)
+static int qemu_chr_open_win(QemuOpts *opts, CharDriverState **_chr)
{
const char *filename = qemu_opt_get(opts, "path");
CharDriverState *chr;
@@ -1652,10 +1672,12 @@ static CharDriverState *qemu_chr_open_win(QemuOpts *opts)
if (win_chr_init(chr, filename) < 0) {
free(s);
free(chr);
- return NULL;
+ return -EIO;
}
qemu_chr_generic_open(chr);
- return chr;
+
+ *_chr = chr;
+ return 0;
}
static int win_chr_pipe_poll(void *opaque)
@@ -1737,7 +1759,7 @@ static int win_chr_pipe_init(CharDriverState *chr, const char *filename)
}
-static CharDriverState *qemu_chr_open_win_pipe(QemuOpts *opts)
+static int qemu_chr_open_win_pipe(QemuOpts *opts, CharDriverState **_chr)
{
const char *filename = qemu_opt_get(opts, "path");
CharDriverState *chr;
@@ -1752,13 +1774,15 @@ static CharDriverState *qemu_chr_open_win_pipe(QemuOpts *opts)
if (win_chr_pipe_init(chr, filename) < 0) {
free(s);
free(chr);
- return NULL;
+ return -EIO;
}
qemu_chr_generic_open(chr);
- return chr;
+
+ *_chr = chr;
+ return 0;
}
-static CharDriverState *qemu_chr_open_win_file(HANDLE fd_out)
+static int qemu_chr_open_win_file(HANDLE fd_out, CharDriverState **pchr)
{
CharDriverState *chr;
WinCharState *s;
@@ -1769,25 +1793,27 @@ static CharDriverState *qemu_chr_open_win_file(HANDLE fd_out)
chr->opaque = s;
chr->chr_write = win_chr_write;
qemu_chr_generic_open(chr);
- return chr;
+ *pchr = chr;
+ return 0;
}
-static CharDriverState *qemu_chr_open_win_con(QemuOpts *opts)
+static int qemu_chr_open_win_con(QemuOpts *opts, CharDriverState **chr)
{
- return qemu_chr_open_win_file(GetStdHandle(STD_OUTPUT_HANDLE));
+ return qemu_chr_open_win_file(GetStdHandle(STD_OUTPUT_HANDLE), chr);
}
-static CharDriverState *qemu_chr_open_win_file_out(QemuOpts *opts)
+static int qemu_chr_open_win_file_out(QemuOpts *opts, CharDriverState **_chr)
{
const char *file_out = qemu_opt_get(opts, "path");
HANDLE fd_out;
fd_out = CreateFile(file_out, GENERIC_WRITE, FILE_SHARE_READ, NULL,
OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
- if (fd_out == INVALID_HANDLE_VALUE)
- return NULL;
+ if (fd_out == INVALID_HANDLE_VALUE) {
+ return -EIO;
+ }
- return qemu_chr_open_win_file(fd_out);
+ return qemu_chr_open_win_file(fd_out, _chr);
}
#endif /* !_WIN32 */
@@ -1834,7 +1860,7 @@ static void udp_chr_read(void *opaque)
if (s->max_size == 0)
return;
- s->bufcnt = recv(s->fd, (void *)s->buf, sizeof(s->buf), 0);
+ s->bufcnt = qemu_recv(s->fd, s->buf, sizeof(s->buf), 0);
s->bufptr = s->bufcnt;
if (s->bufcnt <= 0)
return;
@@ -1868,11 +1894,12 @@ static void udp_chr_close(CharDriverState *chr)
qemu_chr_event(chr, CHR_EVENT_CLOSED);
}
-static CharDriverState *qemu_chr_open_udp(QemuOpts *opts)
+static int qemu_chr_open_udp(QemuOpts *opts, CharDriverState **_chr)
{
CharDriverState *chr = NULL;
NetCharDriver *s = NULL;
int fd = -1;
+ int ret;
chr = qemu_mallocz(sizeof(CharDriverState));
s = qemu_mallocz(sizeof(NetCharDriver));
@@ -1880,6 +1907,7 @@ static CharDriverState *qemu_chr_open_udp(QemuOpts *opts)
fd = inet_dgram_opts(opts);
if (fd < 0) {
fprintf(stderr, "inet_dgram_opts failed\n");
+ ret = -errno;
goto return_err;
}
@@ -1890,16 +1918,17 @@ static CharDriverState *qemu_chr_open_udp(QemuOpts *opts)
chr->chr_write = udp_chr_write;
chr->chr_update_read_handler = udp_chr_update_read_handler;
chr->chr_close = udp_chr_close;
- return chr;
+
+ *_chr = chr;
+ return 0;
return_err:
- if (chr)
- free(chr);
- if (s)
- free(s);
- if (fd >= 0)
+ qemu_free(chr);
+ qemu_free(s);
+ if (fd >= 0) {
closesocket(fd);
- return NULL;
+ }
+ return ret;
}
/***********************************************************/
@@ -2049,7 +2078,7 @@ static ssize_t tcp_chr_recv(CharDriverState *chr, char *buf, size_t len)
static ssize_t tcp_chr_recv(CharDriverState *chr, char *buf, size_t len)
{
TCPCharDriver *s = chr->opaque;
- return recv(s->fd, buf, len, 0);
+ return qemu_recv(s->fd, buf, len, 0);
}
#endif
@@ -2123,6 +2152,22 @@ static void socket_set_nodelay(int fd)
setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof(val));
}
+static int tcp_chr_add_client(CharDriverState *chr, int fd)
+{
+ TCPCharDriver *s = chr->opaque;
+ if (s->fd != -1)
+ return -1;
+
+ socket_set_nonblock(fd);
+ if (s->do_nodelay)
+ socket_set_nodelay(fd);
+ s->fd = fd;
+ qemu_set_fd_handler(s->listen_fd, NULL, NULL, NULL);
+ tcp_chr_connect(chr);
+
+ return 0;
+}
+
static void tcp_chr_accept(void *opaque)
{
CharDriverState *chr = opaque;
@@ -2155,12 +2200,8 @@ static void tcp_chr_accept(void *opaque)
break;
}
}
- socket_set_nonblock(fd);
- if (s->do_nodelay)
- socket_set_nodelay(fd);
- s->fd = fd;
- qemu_set_fd_handler(s->listen_fd, NULL, NULL, NULL);
- tcp_chr_connect(chr);
+ if (tcp_chr_add_client(chr, fd) < 0)
+ close(fd);
}
static void tcp_chr_close(CharDriverState *chr)
@@ -2178,7 +2219,7 @@ static void tcp_chr_close(CharDriverState *chr)
qemu_chr_event(chr, CHR_EVENT_CLOSED);
}
-static CharDriverState *qemu_chr_open_socket(QemuOpts *opts)
+static int qemu_chr_open_socket(QemuOpts *opts, CharDriverState **_chr)
{
CharDriverState *chr = NULL;
TCPCharDriver *s = NULL;
@@ -2188,6 +2229,7 @@ static CharDriverState *qemu_chr_open_socket(QemuOpts *opts)
int do_nodelay;
int is_unix;
int is_telnet;
+ int ret;
is_listen = qemu_opt_get_bool(opts, "server", 0);
is_waitconnect = qemu_opt_get_bool(opts, "wait", 1);
@@ -2213,8 +2255,10 @@ static CharDriverState *qemu_chr_open_socket(QemuOpts *opts)
fd = inet_connect_opts(opts);
}
}
- if (fd < 0)
+ if (fd < 0) {
+ ret = -errno;
goto fail;
+ }
if (!is_waitconnect)
socket_set_nonblock(fd);
@@ -2230,6 +2274,7 @@ static CharDriverState *qemu_chr_open_socket(QemuOpts *opts)
chr->chr_write = tcp_chr_write;
chr->chr_close = tcp_chr_close;
chr->get_msgfd = tcp_get_msgfd;
+ chr->chr_add_client = tcp_chr_add_client;
if (is_listen) {
s->listen_fd = fd;
@@ -2266,14 +2311,16 @@ static CharDriverState *qemu_chr_open_socket(QemuOpts *opts)
tcp_chr_accept(chr);
socket_set_nonblock(s->listen_fd);
}
- return chr;
+
+ *_chr = chr;
+ return 0;
fail:
if (fd >= 0)
closesocket(fd);
qemu_free(s);
qemu_free(chr);
- return NULL;
+ return ret;
}
/***********************************************************/
@@ -2466,7 +2513,7 @@ fail:
static const struct {
const char *name;
- CharDriverState *(*open)(QemuOpts *opts);
+ int (*open)(QemuOpts *opts, CharDriverState **chr);
} backend_table[] = {
{ .name = "null", .open = qemu_chr_open_null },
{ .name = "socket", .open = qemu_chr_open_socket },
@@ -2506,6 +2553,7 @@ CharDriverState *qemu_chr_open_opts(QemuOpts *opts,
{
CharDriverState *chr;
int i;
+ int ret;
if (qemu_opts_id(opts) == NULL) {
fprintf(stderr, "chardev: no id specified\n");
@@ -2527,10 +2575,10 @@ CharDriverState *qemu_chr_open_opts(QemuOpts *opts,
return NULL;
}
- chr = backend_table[i].open(opts);
- if (!chr) {
- fprintf(stderr, "chardev: opening backend \"%s\" failed\n",
- qemu_opt_get(opts, "backend"));
+ ret = backend_table[i].open(opts, &chr);
+ if (ret < 0) {
+ fprintf(stderr, "chardev: opening backend \"%s\" failed: %s\n",
+ qemu_opt_get(opts, "backend"), strerror(-ret));
return NULL;
}
diff --git a/qemu-char.h b/qemu-char.h
index 892c6da..f361c6d 100644
--- a/qemu-char.h
+++ b/qemu-char.h
@@ -57,6 +57,7 @@ struct CharDriverState {
void (*chr_update_read_handler)(struct CharDriverState *s);
int (*chr_ioctl)(struct CharDriverState *s, int cmd, void *arg);
int (*get_msgfd)(struct CharDriverState *s);
+ int (*chr_add_client)(struct CharDriverState *chr, int fd);
IOEventHandler *chr_event;
IOCanReadHandler *chr_can_read;
IOReadHandler *chr_read;
@@ -99,6 +100,7 @@ int qemu_chr_can_read(CharDriverState *s);
void qemu_chr_read(CharDriverState *s, uint8_t *buf, int len);
int qemu_chr_get_msgfd(CharDriverState *s);
void qemu_chr_accept_input(CharDriverState *s);
+int qemu_chr_add_client(CharDriverState *s, int fd);
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);
diff --git a/qemu-common.h b/qemu-common.h
index c2b79bd..afbd04d 100644
--- a/qemu-common.h
+++ b/qemu-common.h
@@ -2,16 +2,13 @@
#ifndef QEMU_COMMON_H
#define QEMU_COMMON_H
+#include "compiler.h"
#include "config-host.h"
-#define QEMU_NORETURN __attribute__ ((__noreturn__))
-#ifdef CONFIG_GCC_ATTRIBUTE_WARN_UNUSED_RESULT
-#define QEMU_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
-#else
-#define QEMU_WARN_UNUSED_RESULT
+#if defined(__arm__) || defined(__sparc__) || defined(__mips__) || defined(__hppa__) || defined(__ia64__)
+#define WORDS_ALIGNED
#endif
-#define QEMU_BUILD_BUG_ON(x) typedef char __build_bug_on__##__LINE__[(x)?-1:1];
#define TFR(expr) do { if ((expr) != -1) break; } while (errno == EINTR)
typedef struct QEMUTimer QEMUTimer;
@@ -82,22 +79,6 @@ 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
-
typedef int (*fprintf_function)(FILE *f, const char *fmt, ...)
GCC_FMT_ATTR(2, 3);
@@ -134,10 +115,6 @@ int qemu_main(int argc, char **argv, char **envp);
/* bottom halves */
typedef void QEMUBHFunc(void *opaque);
-void async_context_push(void);
-void async_context_pop(void);
-int get_async_context_id(void);
-
QEMUBH *qemu_bh_new(QEMUBHFunc *cb, void *opaque);
void qemu_bh_schedule(QEMUBH *bh);
/* Bottom halfs that are scheduled from a bottom half handler are instantly
@@ -223,6 +200,12 @@ int qemu_eventfd(int pipefd[2]);
int qemu_pipe(int pipefd[2]);
#endif
+#ifdef _WIN32
+#define qemu_recv(sockfd, buf, len, flags) recv(sockfd, (void *)buf, len, flags)
+#else
+#define qemu_recv(sockfd, buf, len, flags) recv(sockfd, buf, len, flags)
+#endif
+
/* Error handling. */
void QEMU_NORETURN hw_error(const char *fmt, ...) GCC_FMT_ATTR(1, 2);
@@ -283,6 +266,7 @@ typedef struct I2SCodec I2SCodec;
typedef struct SSIBus SSIBus;
typedef struct EventNotifier EventNotifier;
typedef struct VirtIODevice VirtIODevice;
+typedef struct QEMUSGList QEMUSGList;
typedef uint64_t pcibus_t;
diff --git a/qemu-config.c b/qemu-config.c
index c63741c..1eb6b9a 100644
--- a/qemu-config.c
+++ b/qemu-config.c
@@ -23,6 +23,7 @@ static QemuOptsList qemu_drive_opts = {
},{
.name = "index",
.type = QEMU_OPT_NUMBER,
+ .help = "index number",
},{
.name = "cyls",
.type = QEMU_OPT_NUMBER,
@@ -46,6 +47,7 @@ static QemuOptsList qemu_drive_opts = {
},{
.name = "snapshot",
.type = QEMU_OPT_BOOL,
+ .help = "enable/disable snapshot mode",
},{
.name = "file",
.type = QEMU_OPT_STRING,
@@ -65,12 +67,15 @@ static QemuOptsList qemu_drive_opts = {
},{
.name = "serial",
.type = QEMU_OPT_STRING,
+ .help = "disk serial number",
},{
.name = "rerror",
.type = QEMU_OPT_STRING,
+ .help = "read error action",
},{
.name = "werror",
.type = QEMU_OPT_STRING,
+ .help = "write error action",
},{
.name = "addr",
.type = QEMU_OPT_STRING,
@@ -78,6 +83,7 @@ static QemuOptsList qemu_drive_opts = {
},{
.name = "readonly",
.type = QEMU_OPT_BOOL,
+ .help = "open drive file as read-only",
},
{ /* end of list */ }
},
@@ -458,9 +464,14 @@ QemuOptsList qemu_option_rom_opts = {
static QemuOptsList qemu_machine_opts = {
.name = "machine",
+ .implied_opt_name = "type",
.head = QTAILQ_HEAD_INITIALIZER(qemu_machine_opts.head),
.desc = {
{
+ .name = "type",
+ .type = QEMU_OPT_STRING,
+ .help = "emulated machine"
+ }, {
.name = "accel",
.type = QEMU_OPT_STRING,
.help = "accelerator list",
@@ -469,6 +480,32 @@ static QemuOptsList qemu_machine_opts = {
},
};
+QemuOptsList qemu_boot_opts = {
+ .name = "boot-opts",
+ .head = QTAILQ_HEAD_INITIALIZER(qemu_boot_opts.head),
+ .desc = {
+ /* the three names below are not used now */
+ {
+ .name = "order",
+ .type = QEMU_OPT_STRING,
+ }, {
+ .name = "once",
+ .type = QEMU_OPT_STRING,
+ }, {
+ .name = "menu",
+ .type = QEMU_OPT_STRING,
+ /* following are really used */
+ }, {
+ .name = "splash",
+ .type = QEMU_OPT_STRING,
+ }, {
+ .name = "splash-time",
+ .type = QEMU_OPT_STRING,
+ },
+ { /*End of list */ }
+ },
+};
+
static QemuOptsList *vm_config_groups[32] = {
&qemu_drive_opts,
&qemu_chardev_opts,
@@ -484,6 +521,7 @@ static QemuOptsList *vm_config_groups[32] = {
#endif
&qemu_option_rom_opts,
&qemu_machine_opts,
+ &qemu_boot_opts,
NULL,
};
diff --git a/qemu-coroutine-int.h b/qemu-coroutine-int.h
new file mode 100644
index 0000000..d495615
--- /dev/null
+++ b/qemu-coroutine-int.h
@@ -0,0 +1,49 @@
+/*
+ * Coroutine internals
+ *
+ * Copyright (c) 2011 Kevin Wolf <kwolf@redhat.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 QEMU_COROUTINE_INT_H
+#define QEMU_COROUTINE_INT_H
+
+#include "qemu-queue.h"
+#include "qemu-coroutine.h"
+
+typedef enum {
+ COROUTINE_YIELD = 1,
+ COROUTINE_TERMINATE = 2,
+} CoroutineAction;
+
+struct Coroutine {
+ CoroutineEntry *entry;
+ void *entry_arg;
+ Coroutine *caller;
+ QLIST_ENTRY(Coroutine) pool_next;
+ QTAILQ_ENTRY(Coroutine) co_queue_next;
+};
+
+Coroutine *qemu_coroutine_new(void);
+void qemu_coroutine_delete(Coroutine *co);
+CoroutineAction qemu_coroutine_switch(Coroutine *from, Coroutine *to,
+ CoroutineAction action);
+
+#endif
diff --git a/qemu-coroutine-lock.c b/qemu-coroutine-lock.c
new file mode 100644
index 0000000..a80f437
--- /dev/null
+++ b/qemu-coroutine-lock.c
@@ -0,0 +1,117 @@
+/*
+ * coroutine queues and locks
+ *
+ * Copyright (c) 2011 Kevin Wolf <kwolf@redhat.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 "qemu-common.h"
+#include "qemu-coroutine.h"
+#include "qemu-coroutine-int.h"
+#include "qemu-queue.h"
+#include "trace.h"
+
+static QTAILQ_HEAD(, Coroutine) unlock_bh_queue =
+ QTAILQ_HEAD_INITIALIZER(unlock_bh_queue);
+static QEMUBH* unlock_bh;
+
+static void qemu_co_queue_next_bh(void *opaque)
+{
+ Coroutine *next;
+
+ trace_qemu_co_queue_next_bh();
+ while ((next = QTAILQ_FIRST(&unlock_bh_queue))) {
+ QTAILQ_REMOVE(&unlock_bh_queue, next, co_queue_next);
+ qemu_coroutine_enter(next, NULL);
+ }
+}
+
+void qemu_co_queue_init(CoQueue *queue)
+{
+ QTAILQ_INIT(&queue->entries);
+
+ if (!unlock_bh) {
+ unlock_bh = qemu_bh_new(qemu_co_queue_next_bh, NULL);
+ }
+}
+
+void coroutine_fn qemu_co_queue_wait(CoQueue *queue)
+{
+ Coroutine *self = qemu_coroutine_self();
+ QTAILQ_INSERT_TAIL(&queue->entries, self, co_queue_next);
+ qemu_coroutine_yield();
+ assert(qemu_in_coroutine());
+}
+
+bool qemu_co_queue_next(CoQueue *queue)
+{
+ Coroutine *next;
+
+ next = QTAILQ_FIRST(&queue->entries);
+ if (next) {
+ QTAILQ_REMOVE(&queue->entries, next, co_queue_next);
+ QTAILQ_INSERT_TAIL(&unlock_bh_queue, next, co_queue_next);
+ trace_qemu_co_queue_next(next);
+ qemu_bh_schedule(unlock_bh);
+ }
+
+ return (next != NULL);
+}
+
+bool qemu_co_queue_empty(CoQueue *queue)
+{
+ return (QTAILQ_FIRST(&queue->entries) == NULL);
+}
+
+void qemu_co_mutex_init(CoMutex *mutex)
+{
+ memset(mutex, 0, sizeof(*mutex));
+ qemu_co_queue_init(&mutex->queue);
+}
+
+void coroutine_fn qemu_co_mutex_lock(CoMutex *mutex)
+{
+ Coroutine *self = qemu_coroutine_self();
+
+ trace_qemu_co_mutex_lock_entry(mutex, self);
+
+ while (mutex->locked) {
+ qemu_co_queue_wait(&mutex->queue);
+ }
+
+ mutex->locked = true;
+
+ trace_qemu_co_mutex_lock_return(mutex, self);
+}
+
+void coroutine_fn qemu_co_mutex_unlock(CoMutex *mutex)
+{
+ Coroutine *self = qemu_coroutine_self();
+
+ trace_qemu_co_mutex_unlock_entry(mutex, self);
+
+ assert(mutex->locked == true);
+ assert(qemu_in_coroutine());
+
+ mutex->locked = false;
+ qemu_co_queue_next(&mutex->queue);
+
+ trace_qemu_co_mutex_unlock_return(mutex, self);
+}
diff --git a/qemu-coroutine.c b/qemu-coroutine.c
new file mode 100644
index 0000000..600be26
--- /dev/null
+++ b/qemu-coroutine.c
@@ -0,0 +1,75 @@
+/*
+ * QEMU coroutines
+ *
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ * Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
+ * Kevin Wolf <kwolf@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#include "trace.h"
+#include "qemu-common.h"
+#include "qemu-coroutine.h"
+#include "qemu-coroutine-int.h"
+
+Coroutine *qemu_coroutine_create(CoroutineEntry *entry)
+{
+ Coroutine *co = qemu_coroutine_new();
+ co->entry = entry;
+ return co;
+}
+
+static void coroutine_swap(Coroutine *from, Coroutine *to)
+{
+ CoroutineAction ret;
+
+ ret = qemu_coroutine_switch(from, to, COROUTINE_YIELD);
+
+ switch (ret) {
+ case COROUTINE_YIELD:
+ return;
+ case COROUTINE_TERMINATE:
+ trace_qemu_coroutine_terminate(to);
+ qemu_coroutine_delete(to);
+ return;
+ default:
+ abort();
+ }
+}
+
+void qemu_coroutine_enter(Coroutine *co, void *opaque)
+{
+ Coroutine *self = qemu_coroutine_self();
+
+ trace_qemu_coroutine_enter(self, co, opaque);
+
+ if (co->caller) {
+ fprintf(stderr, "Co-routine re-entered recursively\n");
+ abort();
+ }
+
+ co->caller = self;
+ co->entry_arg = opaque;
+ coroutine_swap(self, co);
+}
+
+void coroutine_fn qemu_coroutine_yield(void)
+{
+ Coroutine *self = qemu_coroutine_self();
+ Coroutine *to = self->caller;
+
+ trace_qemu_coroutine_yield(self, to);
+
+ if (!to) {
+ fprintf(stderr, "Co-routine is yielding to no one\n");
+ abort();
+ }
+
+ self->caller = NULL;
+ coroutine_swap(self, to);
+}
diff --git a/qemu-coroutine.h b/qemu-coroutine.h
new file mode 100644
index 0000000..2f2fd95
--- /dev/null
+++ b/qemu-coroutine.h
@@ -0,0 +1,159 @@
+/*
+ * QEMU coroutine implementation
+ *
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ * Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
+ * Kevin Wolf <kwolf@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#ifndef QEMU_COROUTINE_H
+#define QEMU_COROUTINE_H
+
+#include <stdbool.h>
+#include "qemu-queue.h"
+
+/**
+ * Coroutines are a mechanism for stack switching and can be used for
+ * cooperative userspace threading. These functions provide a simple but
+ * useful flavor of coroutines that is suitable for writing sequential code,
+ * rather than callbacks, for operations that need to give up control while
+ * waiting for events to complete.
+ *
+ * These functions are re-entrant and may be used outside the global mutex.
+ */
+
+/**
+ * Mark a function that executes in coroutine context
+ *
+ * Functions that execute in coroutine context cannot be called directly from
+ * normal functions. In the future it would be nice to enable compiler or
+ * static checker support for catching such errors. This annotation might make
+ * it possible and in the meantime it serves as documentation.
+ *
+ * For example:
+ *
+ * static void coroutine_fn foo(void) {
+ * ....
+ * }
+ */
+#define coroutine_fn
+
+typedef struct Coroutine Coroutine;
+
+/**
+ * Coroutine entry point
+ *
+ * When the coroutine is entered for the first time, opaque is passed in as an
+ * argument.
+ *
+ * When this function returns, the coroutine is destroyed automatically and
+ * execution continues in the caller who last entered the coroutine.
+ */
+typedef void coroutine_fn CoroutineEntry(void *opaque);
+
+/**
+ * Create a new coroutine
+ *
+ * Use qemu_coroutine_enter() to actually transfer control to the coroutine.
+ */
+Coroutine *qemu_coroutine_create(CoroutineEntry *entry);
+
+/**
+ * Transfer control to a coroutine
+ *
+ * The opaque argument is passed as the argument to the entry point when
+ * entering the coroutine for the first time. It is subsequently ignored.
+ */
+void qemu_coroutine_enter(Coroutine *coroutine, void *opaque);
+
+/**
+ * Transfer control back to a coroutine's caller
+ *
+ * This function does not return until the coroutine is re-entered using
+ * qemu_coroutine_enter().
+ */
+void coroutine_fn qemu_coroutine_yield(void);
+
+/**
+ * Get the currently executing coroutine
+ */
+Coroutine *coroutine_fn qemu_coroutine_self(void);
+
+/**
+ * Return whether or not currently inside a coroutine
+ *
+ * This can be used to write functions that work both when in coroutine context
+ * and when not in coroutine context. Note that such functions cannot use the
+ * coroutine_fn annotation since they work outside coroutine context.
+ */
+bool qemu_in_coroutine(void);
+
+
+
+/**
+ * CoQueues are a mechanism to queue coroutines in order to continue executing
+ * them later. They provide the fundamental primitives on which coroutine locks
+ * are built.
+ */
+typedef struct CoQueue {
+ QTAILQ_HEAD(, Coroutine) entries;
+} CoQueue;
+
+/**
+ * Initialise a CoQueue. This must be called before any other operation is used
+ * on the CoQueue.
+ */
+void qemu_co_queue_init(CoQueue *queue);
+
+/**
+ * Adds the current coroutine to the CoQueue and transfers control to the
+ * caller of the coroutine.
+ */
+void coroutine_fn qemu_co_queue_wait(CoQueue *queue);
+
+/**
+ * Restarts the next coroutine in the CoQueue and removes it from the queue.
+ *
+ * Returns true if a coroutine was restarted, false if the queue is empty.
+ */
+bool qemu_co_queue_next(CoQueue *queue);
+
+/**
+ * Checks if the CoQueue is empty.
+ */
+bool qemu_co_queue_empty(CoQueue *queue);
+
+
+/**
+ * Provides a mutex that can be used to synchronise coroutines
+ */
+typedef struct CoMutex {
+ bool locked;
+ CoQueue queue;
+} CoMutex;
+
+/**
+ * Initialises a CoMutex. This must be called before any other operation is used
+ * on the CoMutex.
+ */
+void qemu_co_mutex_init(CoMutex *mutex);
+
+/**
+ * Locks the mutex. If the lock cannot be taken immediately, control is
+ * transferred to the caller of the current coroutine.
+ */
+void coroutine_fn qemu_co_mutex_lock(CoMutex *mutex);
+
+/**
+ * Unlocks the mutex and schedules the next coroutine that was waiting for this
+ * lock to be run.
+ */
+void coroutine_fn qemu_co_mutex_unlock(CoMutex *mutex);
+
+#endif /* QEMU_COROUTINE_H */
diff --git a/qemu-ga.c b/qemu-ga.c
new file mode 100644
index 0000000..869ee37
--- /dev/null
+++ b/qemu-ga.c
@@ -0,0 +1,637 @@
+/*
+ * QEMU Guest Agent
+ *
+ * Copyright IBM Corp. 2011
+ *
+ * Authors:
+ * Adam Litke <aglitke@linux.vnet.ibm.com>
+ * Michael Roth <mdroth@linux.vnet.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.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <glib.h>
+#include <getopt.h>
+#include <termios.h>
+#include <syslog.h>
+#include "qemu_socket.h"
+#include "json-streamer.h"
+#include "json-parser.h"
+#include "qint.h"
+#include "qjson.h"
+#include "qga/guest-agent-core.h"
+#include "module.h"
+#include "signal.h"
+#include "qerror.h"
+#include "error_int.h"
+
+#define QGA_VIRTIO_PATH_DEFAULT "/dev/virtio-ports/org.qemu.guest_agent.0"
+#define QGA_PIDFILE_DEFAULT "/var/run/qemu-ga.pid"
+#define QGA_BAUDRATE_DEFAULT B38400 /* for isa-serial channels */
+#define QGA_TIMEOUT_DEFAULT 30*1000 /* ms */
+
+struct GAState {
+ JSONMessageParser parser;
+ GMainLoop *main_loop;
+ GIOChannel *conn_channel;
+ GIOChannel *listen_channel;
+ const char *path;
+ const char *method;
+ bool virtio; /* fastpath to check for virtio to deal with poll() quirks */
+ GACommandState *command_state;
+ GLogLevelFlags log_level;
+ FILE *log_file;
+ bool logging_enabled;
+};
+
+static struct GAState *ga_state;
+
+static void quit_handler(int sig)
+{
+ g_debug("recieved signal num %d, quitting", sig);
+
+ if (g_main_loop_is_running(ga_state->main_loop)) {
+ g_main_loop_quit(ga_state->main_loop);
+ }
+}
+
+static void register_signal_handlers(void)
+{
+ struct sigaction sigact;
+ int ret;
+
+ memset(&sigact, 0, sizeof(struct sigaction));
+ sigact.sa_handler = quit_handler;
+
+ ret = sigaction(SIGINT, &sigact, NULL);
+ if (ret == -1) {
+ g_error("error configuring signal handler: %s", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ ret = sigaction(SIGTERM, &sigact, NULL);
+ if (ret == -1) {
+ g_error("error configuring signal handler: %s", strerror(errno));
+ }
+}
+
+static void usage(const char *cmd)
+{
+ printf(
+"Usage: %s -c <channel_opts>\n"
+"QEMU Guest Agent %s\n"
+"\n"
+" -m, --method transport method: one of unix-listen, virtio-serial, or\n"
+" isa-serial (virtio-serial is the default)\n"
+" -p, --path device/socket path (%s is the default for virtio-serial)\n"
+" -l, --logfile set logfile path, logs to stderr by default\n"
+" -f, --pidfile specify pidfile (default is %s)\n"
+" -v, --verbose log extra debugging information\n"
+" -V, --version print version information and exit\n"
+" -d, --daemonize become a daemon\n"
+" -h, --help display this help and exit\n"
+"\n"
+"Report bugs to <mdroth@linux.vnet.ibm.com>\n"
+ , cmd, QGA_VERSION, QGA_VIRTIO_PATH_DEFAULT, QGA_PIDFILE_DEFAULT);
+}
+
+static void conn_channel_close(GAState *s);
+
+static const char *ga_log_level_str(GLogLevelFlags level)
+{
+ switch (level & G_LOG_LEVEL_MASK) {
+ case G_LOG_LEVEL_ERROR:
+ return "error";
+ case G_LOG_LEVEL_CRITICAL:
+ return "critical";
+ case G_LOG_LEVEL_WARNING:
+ return "warning";
+ case G_LOG_LEVEL_MESSAGE:
+ return "message";
+ case G_LOG_LEVEL_INFO:
+ return "info";
+ case G_LOG_LEVEL_DEBUG:
+ return "debug";
+ default:
+ return "user";
+ }
+}
+
+bool ga_logging_enabled(GAState *s)
+{
+ return s->logging_enabled;
+}
+
+void ga_disable_logging(GAState *s)
+{
+ s->logging_enabled = false;
+}
+
+void ga_enable_logging(GAState *s)
+{
+ s->logging_enabled = true;
+}
+
+static void ga_log(const gchar *domain, GLogLevelFlags level,
+ const gchar *msg, gpointer opaque)
+{
+ GAState *s = opaque;
+ GTimeVal time;
+ const char *level_str = ga_log_level_str(level);
+
+ if (!ga_logging_enabled(s)) {
+ return;
+ }
+
+ level &= G_LOG_LEVEL_MASK;
+ if (g_strcmp0(domain, "syslog") == 0) {
+ syslog(LOG_INFO, "%s: %s", level_str, msg);
+ } else if (level & s->log_level) {
+ g_get_current_time(&time);
+ fprintf(s->log_file,
+ "%lu.%lu: %s: %s\n", time.tv_sec, time.tv_usec, level_str, msg);
+ fflush(s->log_file);
+ }
+}
+
+static void become_daemon(const char *pidfile)
+{
+ pid_t pid, sid;
+ int pidfd;
+ char *pidstr = NULL;
+
+ pid = fork();
+ if (pid < 0) {
+ exit(EXIT_FAILURE);
+ }
+ if (pid > 0) {
+ exit(EXIT_SUCCESS);
+ }
+
+ pidfd = open(pidfile, O_CREAT|O_WRONLY|O_EXCL, S_IRUSR|S_IWUSR);
+ if (pidfd == -1) {
+ g_critical("Cannot create pid file, %s", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ if (asprintf(&pidstr, "%d", getpid()) == -1) {
+ g_critical("Cannot allocate memory");
+ goto fail;
+ }
+ if (write(pidfd, pidstr, strlen(pidstr)) != strlen(pidstr)) {
+ free(pidstr);
+ g_critical("Failed to write pid file");
+ goto fail;
+ }
+
+ umask(0);
+ sid = setsid();
+ if (sid < 0) {
+ goto fail;
+ }
+ if ((chdir("/")) < 0) {
+ goto fail;
+ }
+
+ close(STDIN_FILENO);
+ close(STDOUT_FILENO);
+ close(STDERR_FILENO);
+ free(pidstr);
+ return;
+
+fail:
+ unlink(pidfile);
+ g_critical("failed to daemonize");
+ exit(EXIT_FAILURE);
+}
+
+static int conn_channel_send_buf(GIOChannel *channel, const char *buf,
+ gsize count)
+{
+ GError *err = NULL;
+ gsize written = 0;
+ GIOStatus status;
+
+ while (count) {
+ status = g_io_channel_write_chars(channel, buf, count, &written, &err);
+ g_debug("sending data, count: %d", (int)count);
+ if (err != NULL) {
+ g_warning("error sending newline: %s", err->message);
+ return err->code;
+ }
+ if (status == G_IO_STATUS_ERROR || status == G_IO_STATUS_EOF) {
+ return -EPIPE;
+ }
+
+ if (status == G_IO_STATUS_NORMAL) {
+ count -= written;
+ }
+ }
+
+ return 0;
+}
+
+static int conn_channel_send_payload(GIOChannel *channel, QObject *payload)
+{
+ int ret = 0;
+ const char *buf;
+ QString *payload_qstr;
+ GError *err = NULL;
+
+ g_assert(payload && channel);
+
+ payload_qstr = qobject_to_json(payload);
+ if (!payload_qstr) {
+ return -EINVAL;
+ }
+
+ qstring_append_chr(payload_qstr, '\n');
+ buf = qstring_get_str(payload_qstr);
+ ret = conn_channel_send_buf(channel, buf, strlen(buf));
+ if (ret) {
+ goto out_free;
+ }
+
+ g_io_channel_flush(channel, &err);
+ if (err != NULL) {
+ g_warning("error flushing payload: %s", err->message);
+ ret = err->code;
+ goto out_free;
+ }
+
+out_free:
+ QDECREF(payload_qstr);
+ if (err) {
+ g_error_free(err);
+ }
+ return ret;
+}
+
+static void process_command(GAState *s, QDict *req)
+{
+ QObject *rsp = NULL;
+ int ret;
+
+ g_assert(req);
+ g_debug("processing command");
+ rsp = qmp_dispatch(QOBJECT(req));
+ if (rsp) {
+ ret = conn_channel_send_payload(s->conn_channel, rsp);
+ if (ret) {
+ g_warning("error sending payload: %s", strerror(ret));
+ }
+ qobject_decref(rsp);
+ } else {
+ g_warning("error getting response");
+ }
+}
+
+/* handle requests/control events coming in over the channel */
+static void process_event(JSONMessageParser *parser, QList *tokens)
+{
+ GAState *s = container_of(parser, GAState, parser);
+ QObject *obj;
+ QDict *qdict;
+ Error *err = NULL;
+ int ret;
+
+ g_assert(s && parser);
+
+ g_debug("process_event: called");
+ obj = json_parser_parse_err(tokens, NULL, &err);
+ if (err || !obj || qobject_type(obj) != QTYPE_QDICT) {
+ qobject_decref(obj);
+ qdict = qdict_new();
+ if (!err) {
+ g_warning("failed to parse event: unknown error");
+ error_set(&err, QERR_JSON_PARSING);
+ } else {
+ g_warning("failed to parse event: %s", error_get_pretty(err));
+ }
+ qdict_put_obj(qdict, "error", error_get_qobject(err));
+ error_free(err);
+ } else {
+ qdict = qobject_to_qdict(obj);
+ }
+
+ g_assert(qdict);
+
+ /* handle host->guest commands */
+ if (qdict_haskey(qdict, "execute")) {
+ process_command(s, qdict);
+ } else {
+ if (!qdict_haskey(qdict, "error")) {
+ QDECREF(qdict);
+ qdict = qdict_new();
+ g_warning("unrecognized payload format");
+ error_set(&err, QERR_UNSUPPORTED);
+ qdict_put_obj(qdict, "error", error_get_qobject(err));
+ error_free(err);
+ }
+ ret = conn_channel_send_payload(s->conn_channel, QOBJECT(qdict));
+ if (ret) {
+ g_warning("error sending payload: %s", strerror(ret));
+ }
+ }
+
+ QDECREF(qdict);
+}
+
+static gboolean conn_channel_read(GIOChannel *channel, GIOCondition condition,
+ gpointer data)
+{
+ GAState *s = data;
+ gchar buf[1024];
+ gsize count;
+ GError *err = NULL;
+ memset(buf, 0, 1024);
+ GIOStatus status = g_io_channel_read_chars(channel, buf, 1024,
+ &count, &err);
+ if (err != NULL) {
+ g_warning("error reading channel: %s", err->message);
+ conn_channel_close(s);
+ g_error_free(err);
+ return false;
+ }
+ switch (status) {
+ case G_IO_STATUS_ERROR:
+ g_warning("problem");
+ return false;
+ case G_IO_STATUS_NORMAL:
+ g_debug("read data, count: %d, data: %s", (int)count, buf);
+ json_message_parser_feed(&s->parser, (char *)buf, (int)count);
+ case G_IO_STATUS_AGAIN:
+ /* virtio causes us to spin here when no process is attached to
+ * host-side chardev. sleep a bit to mitigate this
+ */
+ if (s->virtio) {
+ usleep(100*1000);
+ }
+ return true;
+ case G_IO_STATUS_EOF:
+ g_debug("received EOF");
+ conn_channel_close(s);
+ if (s->virtio) {
+ return true;
+ }
+ return false;
+ default:
+ g_warning("unknown channel read status, closing");
+ conn_channel_close(s);
+ return false;
+ }
+ return true;
+}
+
+static int conn_channel_add(GAState *s, int fd)
+{
+ GIOChannel *conn_channel;
+ GError *err = NULL;
+
+ g_assert(s && !s->conn_channel);
+ conn_channel = g_io_channel_unix_new(fd);
+ g_assert(conn_channel);
+ g_io_channel_set_encoding(conn_channel, NULL, &err);
+ if (err != NULL) {
+ g_warning("error setting channel encoding to binary");
+ g_error_free(err);
+ return -1;
+ }
+ g_io_add_watch(conn_channel, G_IO_IN | G_IO_HUP,
+ conn_channel_read, s);
+ s->conn_channel = conn_channel;
+ return 0;
+}
+
+static gboolean listen_channel_accept(GIOChannel *channel,
+ GIOCondition condition, gpointer data)
+{
+ GAState *s = data;
+ g_assert(channel != NULL);
+ int ret, conn_fd;
+ bool accepted = false;
+ struct sockaddr_un addr;
+ socklen_t addrlen = sizeof(addr);
+
+ conn_fd = qemu_accept(g_io_channel_unix_get_fd(s->listen_channel),
+ (struct sockaddr *)&addr, &addrlen);
+ if (conn_fd == -1) {
+ g_warning("error converting fd to gsocket: %s", strerror(errno));
+ goto out;
+ }
+ fcntl(conn_fd, F_SETFL, O_NONBLOCK);
+ ret = conn_channel_add(s, conn_fd);
+ if (ret) {
+ g_warning("error setting up connection");
+ goto out;
+ }
+ accepted = true;
+
+out:
+ /* only accept 1 connection at a time */
+ return !accepted;
+}
+
+/* start polling for readable events on listen fd, new==true
+ * indicates we should use the existing s->listen_channel
+ */
+static int listen_channel_add(GAState *s, int listen_fd, bool new)
+{
+ if (new) {
+ s->listen_channel = g_io_channel_unix_new(listen_fd);
+ }
+ g_io_add_watch(s->listen_channel, G_IO_IN,
+ listen_channel_accept, s);
+ return 0;
+}
+
+/* cleanup state for closed connection/session, start accepting new
+ * connections if we're in listening mode
+ */
+static void conn_channel_close(GAState *s)
+{
+ if (strcmp(s->method, "unix-listen") == 0) {
+ g_io_channel_shutdown(s->conn_channel, true, NULL);
+ listen_channel_add(s, 0, false);
+ } else if (strcmp(s->method, "virtio-serial") == 0) {
+ /* we spin on EOF for virtio-serial, so back off a bit. also,
+ * dont close the connection in this case, it'll resume normal
+ * operation when another process connects to host chardev
+ */
+ usleep(100*1000);
+ goto out_noclose;
+ }
+ g_io_channel_unref(s->conn_channel);
+ s->conn_channel = NULL;
+out_noclose:
+ return;
+}
+
+static void init_guest_agent(GAState *s)
+{
+ struct termios tio;
+ int ret, fd;
+
+ if (s->method == NULL) {
+ /* try virtio-serial as our default */
+ s->method = "virtio-serial";
+ }
+
+ if (s->path == NULL) {
+ if (strcmp(s->method, "virtio-serial") != 0) {
+ g_critical("must specify a path for this channel");
+ exit(EXIT_FAILURE);
+ }
+ /* try the default path for the virtio-serial port */
+ s->path = QGA_VIRTIO_PATH_DEFAULT;
+ }
+
+ if (strcmp(s->method, "virtio-serial") == 0) {
+ s->virtio = true;
+ fd = qemu_open(s->path, O_RDWR | O_NONBLOCK | O_ASYNC);
+ if (fd == -1) {
+ g_critical("error opening channel: %s", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ ret = conn_channel_add(s, fd);
+ if (ret) {
+ g_critical("error adding channel to main loop");
+ exit(EXIT_FAILURE);
+ }
+ } else if (strcmp(s->method, "isa-serial") == 0) {
+ fd = qemu_open(s->path, O_RDWR | O_NOCTTY);
+ if (fd == -1) {
+ g_critical("error opening channel: %s", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ tcgetattr(fd, &tio);
+ /* set up serial port for non-canonical, dumb byte streaming */
+ tio.c_iflag &= ~(IGNBRK | BRKINT | IGNPAR | PARMRK | INPCK | ISTRIP |
+ INLCR | IGNCR | ICRNL | IXON | IXOFF | IXANY |
+ IMAXBEL);
+ tio.c_oflag = 0;
+ tio.c_lflag = 0;
+ tio.c_cflag |= QGA_BAUDRATE_DEFAULT;
+ /* 1 available byte min or reads will block (we'll set non-blocking
+ * elsewhere, else we have to deal with read()=0 instead)
+ */
+ tio.c_cc[VMIN] = 1;
+ tio.c_cc[VTIME] = 0;
+ /* flush everything waiting for read/xmit, it's garbage at this point */
+ tcflush(fd, TCIFLUSH);
+ tcsetattr(fd, TCSANOW, &tio);
+ ret = conn_channel_add(s, fd);
+ if (ret) {
+ g_error("error adding channel to main loop");
+ }
+ } else if (strcmp(s->method, "unix-listen") == 0) {
+ fd = unix_listen(s->path, NULL, strlen(s->path));
+ if (fd == -1) {
+ g_critical("error opening path: %s", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ ret = listen_channel_add(s, fd, true);
+ if (ret) {
+ g_critical("error binding/listening to specified socket");
+ exit(EXIT_FAILURE);
+ }
+ } else {
+ g_critical("unsupported channel method/type: %s", s->method);
+ exit(EXIT_FAILURE);
+ }
+
+ json_message_parser_init(&s->parser, process_event);
+ s->main_loop = g_main_loop_new(NULL, false);
+}
+
+int main(int argc, char **argv)
+{
+ const char *sopt = "hVvdm:p:l:f:";
+ const char *method = NULL, *path = NULL, *pidfile = QGA_PIDFILE_DEFAULT;
+ const struct option lopt[] = {
+ { "help", 0, NULL, 'h' },
+ { "version", 0, NULL, 'V' },
+ { "logfile", 0, NULL, 'l' },
+ { "pidfile", 0, NULL, 'f' },
+ { "verbose", 0, NULL, 'v' },
+ { "method", 0, NULL, 'm' },
+ { "path", 0, NULL, 'p' },
+ { "daemonize", 0, NULL, 'd' },
+ { NULL, 0, NULL, 0 }
+ };
+ int opt_ind = 0, ch, daemonize = 0;
+ GLogLevelFlags log_level = G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL;
+ FILE *log_file = stderr;
+ GAState *s;
+
+ while ((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) {
+ switch (ch) {
+ case 'm':
+ method = optarg;
+ break;
+ case 'p':
+ path = optarg;
+ break;
+ case 'l':
+ log_file = fopen(optarg, "a");
+ if (!log_file) {
+ g_critical("unable to open specified log file: %s",
+ strerror(errno));
+ return EXIT_FAILURE;
+ }
+ break;
+ case 'f':
+ pidfile = optarg;
+ break;
+ case 'v':
+ /* enable all log levels */
+ log_level = G_LOG_LEVEL_MASK;
+ break;
+ case 'V':
+ printf("QEMU Guest Agent %s\n", QGA_VERSION);
+ return 0;
+ case 'd':
+ daemonize = 1;
+ break;
+ case 'h':
+ usage(argv[0]);
+ return 0;
+ case '?':
+ g_print("Unknown option, try '%s --help' for more information.\n",
+ argv[0]);
+ return EXIT_FAILURE;
+ }
+ }
+
+ if (daemonize) {
+ g_debug("starting daemon");
+ become_daemon(pidfile);
+ }
+
+ s = qemu_mallocz(sizeof(GAState));
+ s->conn_channel = NULL;
+ s->path = path;
+ s->method = method;
+ s->log_file = log_file;
+ s->log_level = log_level;
+ g_log_set_default_handler(ga_log, s);
+ g_log_set_fatal_mask(NULL, G_LOG_LEVEL_ERROR);
+ s->logging_enabled = true;
+ s->command_state = ga_command_state_new();
+ ga_command_state_init(s, s->command_state);
+ ga_command_state_init_all(s->command_state);
+ ga_state = s;
+
+ module_call_init(MODULE_INIT_QAPI);
+ init_guest_agent(ga_state);
+ register_signal_handlers();
+
+ g_main_loop_run(ga_state->main_loop);
+
+ ga_command_state_cleanup_all(ga_state->command_state);
+ unlink(pidfile);
+
+ return 0;
+}
diff --git a/qemu-img-cmds.hx b/qemu-img-cmds.hx
index 2b70618..1299e83 100644
--- a/qemu-img-cmds.hx
+++ b/qemu-img-cmds.hx
@@ -30,7 +30,7 @@ ETEXI
DEF("convert", img_convert,
"convert [-c] [-p] [-f fmt] [-t cache] [-O output_fmt] [-o options] [-s snapshot_name] filename [filename2 [...]] output_filename")
STEXI
-@item convert [-c] [-f @var{fmt}] [-O @var{output_fmt}] [-o @var{options}] [-s @var{snapshot_name}] @var{filename} [@var{filename2} [...]] @var{output_filename}
+@item convert [-c] [-p] [-f @var{fmt}] [-O @var{output_fmt}] [-o @var{options}] [-s @var{snapshot_name}] @var{filename} [@var{filename2} [...]] @var{output_filename}
ETEXI
DEF("info", img_info,
@@ -48,7 +48,7 @@ ETEXI
DEF("rebase", img_rebase,
"rebase [-f fmt] [-t cache] [-p] [-u] -b backing_file [-F backing_fmt] filename")
STEXI
-@item rebase [-f @var{fmt}] [-u] -b @var{backing_file} [-F @var{backing_fmt}] @var{filename}
+@item rebase [-f @var{fmt}] [-p] [-u] -b @var{backing_file} [-F @var{backing_fmt}] @var{filename}
ETEXI
DEF("resize", img_resize,
diff --git a/qemu-img.c b/qemu-img.c
index 54137a4..b205e98 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -1024,35 +1024,6 @@ out:
return 0;
}
-#ifdef _WIN32
-static int64_t get_allocated_file_size(const char *filename)
-{
- typedef DWORD (WINAPI * get_compressed_t)(const char *filename, DWORD *high);
- get_compressed_t get_compressed;
- struct _stati64 st;
-
- /* WinNT support GetCompressedFileSize to determine allocate size */
- get_compressed = (get_compressed_t) GetProcAddress(GetModuleHandle("kernel32"), "GetCompressedFileSizeA");
- if (get_compressed) {
- DWORD high, low;
- low = get_compressed(filename, &high);
- if (low != 0xFFFFFFFFlu || GetLastError() == NO_ERROR)
- return (((int64_t) high) << 32) + low;
- }
-
- if (_stati64(filename, &st) < 0)
- return -1;
- return st.st_size;
-}
-#else
-static int64_t get_allocated_file_size(const char *filename)
-{
- struct stat st;
- if (stat(filename, &st) < 0)
- return -1;
- return (int64_t)st.st_blocks * 512;
-}
-#endif
static void dump_snapshots(BlockDriverState *bs)
{
@@ -1112,7 +1083,7 @@ static int img_info(int argc, char **argv)
bdrv_get_format(bs, fmt_name, sizeof(fmt_name));
bdrv_get_geometry(bs, &total_sectors);
get_human_readable_size(size_buf, sizeof(size_buf), total_sectors * 512);
- allocated_size = get_allocated_file_size(filename);
+ allocated_size = bdrv_get_allocated_file_size(bs);
if (allocated_size < 0) {
snprintf(dsize_buf, sizeof(dsize_buf), "unavailable");
} else {
diff --git a/qemu-img.texi b/qemu-img.texi
index 526474c..495a1b6 100644
--- a/qemu-img.texi
+++ b/qemu-img.texi
@@ -38,6 +38,8 @@ by the used format or see the format descriptions below for details.
indicates that target image must be compressed (qcow format only)
@item -h
with or without a command shows help and lists the supported formats
+@item -p
+display progress bar (convert and rebase commands only)
@end table
Parameters to snapshot subcommand:
@@ -84,7 +86,7 @@ it doesn't need to be specified separately in this case.
Commit the changes recorded in @var{filename} in its base image.
-@item convert [-c] [-f @var{fmt}] [-O @var{output_fmt}] [-o @var{options}] [-s @var{snapshot_name}] @var{filename} [@var{filename2} [...]] @var{output_filename}
+@item convert [-c] [-p] [-f @var{fmt}] [-O @var{output_fmt}] [-o @var{options}] [-s @var{snapshot_name}] @var{filename} [@var{filename2} [...]] @var{output_filename}
Convert the disk image @var{filename} or a snapshot @var{snapshot_name} to disk image @var{output_filename}
using format @var{output_fmt}. It can be optionally compressed (@code{-c}
@@ -114,7 +116,7 @@ they are displayed too.
List, apply, create or delete snapshots in image @var{filename}.
-@item rebase [-f @var{fmt}] [-u] -b @var{backing_file} [-F @var{backing_fmt}] @var{filename}
+@item rebase [-f @var{fmt}] [-p] [-u] -b @var{backing_file} [-F @var{backing_fmt}] @var{filename}
Changes the backing file of an image. Only the formats @code{qcow2} and
@code{qed} support changing the backing file.
diff --git a/qemu-io.c b/qemu-io.c
index dd4ebf5..a553d0c 100644
--- a/qemu-io.c
+++ b/qemu-io.c
@@ -20,7 +20,7 @@
#define VERSION "0.0.1"
-#define CMD_NOFILE_OK 0x01
+#define CMD_NOFILE_OK 0x01
char *progname;
static BlockDriverState *bs;
@@ -35,16 +35,16 @@ static int misalign;
*/
static int parse_pattern(const char *arg)
{
- char *endptr = NULL;
- long pattern;
+ char *endptr = NULL;
+ long pattern;
- pattern = strtol(arg, &endptr, 0);
- if (pattern < 0 || pattern > UCHAR_MAX || *endptr != '\0') {
- printf("%s is not a valid pattern byte\n", arg);
- return -1;
- }
+ pattern = strtol(arg, &endptr, 0);
+ if (pattern < 0 || pattern > UCHAR_MAX || *endptr != '\0') {
+ printf("%s is not a valid pattern byte\n", arg);
+ return -1;
+ }
- return pattern;
+ return pattern;
}
/*
@@ -54,70 +54,73 @@ static int parse_pattern(const char *arg)
* that is specified on the command line.
*/
-#define MISALIGN_OFFSET 16
+#define MISALIGN_OFFSET 16
static void *qemu_io_alloc(size_t len, int pattern)
{
- void *buf;
-
- if (misalign)
- len += MISALIGN_OFFSET;
- buf = qemu_blockalign(bs, len);
- memset(buf, pattern, len);
- if (misalign)
- buf += MISALIGN_OFFSET;
- return buf;
+ void *buf;
+
+ if (misalign) {
+ len += MISALIGN_OFFSET;
+ }
+ buf = qemu_blockalign(bs, len);
+ memset(buf, pattern, len);
+ if (misalign) {
+ buf += MISALIGN_OFFSET;
+ }
+ return buf;
}
static void qemu_io_free(void *p)
{
- if (misalign)
- p -= MISALIGN_OFFSET;
- qemu_vfree(p);
+ if (misalign) {
+ p -= MISALIGN_OFFSET;
+ }
+ qemu_vfree(p);
}
-static void
-dump_buffer(const void *buffer, int64_t offset, int len)
+static void dump_buffer(const void *buffer, int64_t offset, int len)
{
- int i, j;
- const uint8_t *p;
-
- for (i = 0, p = buffer; i < len; i += 16) {
- const uint8_t *s = p;
-
- printf("%08" PRIx64 ": ", offset + i);
- for (j = 0; j < 16 && i + j < len; j++, p++)
- printf("%02x ", *p);
- printf(" ");
- for (j = 0; j < 16 && i + j < len; j++, s++) {
- if (isalnum(*s))
- printf("%c", *s);
- else
- printf(".");
- }
- printf("\n");
- }
+ int i, j;
+ const uint8_t *p;
+
+ for (i = 0, p = buffer; i < len; i += 16) {
+ const uint8_t *s = p;
+
+ printf("%08" PRIx64 ": ", offset + i);
+ for (j = 0; j < 16 && i + j < len; j++, p++) {
+ printf("%02x ", *p);
+ }
+ printf(" ");
+ for (j = 0; j < 16 && i + j < len; j++, s++) {
+ if (isalnum(*s)) {
+ printf("%c", *s);
+ } else {
+ printf(".");
+ }
+ }
+ printf("\n");
+ }
}
-static void
-print_report(const char *op, struct timeval *t, int64_t offset,
- int count, int total, int cnt, int Cflag)
+static void print_report(const char *op, struct timeval *t, int64_t offset,
+ int count, int total, int cnt, int Cflag)
{
- char s1[64], s2[64], ts[64];
-
- timestr(t, ts, sizeof(ts), Cflag ? VERBOSE_FIXED_TIME : 0);
- if (!Cflag) {
- cvtstr((double)total, s1, sizeof(s1));
- cvtstr(tdiv((double)total, *t), s2, sizeof(s2));
- printf("%s %d/%d bytes at offset %" PRId64 "\n",
- op, total, count, offset);
- printf("%s, %d ops; %s (%s/sec and %.4f ops/sec)\n",
- s1, cnt, ts, s2, tdiv((double)cnt, *t));
- } else {/* bytes,ops,time,bytes/sec,ops/sec */
- printf("%d,%d,%s,%.3f,%.3f\n",
- total, cnt, ts,
- tdiv((double)total, *t),
- tdiv((double)cnt, *t));
- }
+ char s1[64], s2[64], ts[64];
+
+ timestr(t, ts, sizeof(ts), Cflag ? VERBOSE_FIXED_TIME : 0);
+ if (!Cflag) {
+ cvtstr((double)total, s1, sizeof(s1));
+ cvtstr(tdiv((double)total, *t), s2, sizeof(s2));
+ printf("%s %d/%d bytes at offset %" PRId64 "\n",
+ op, total, count, offset);
+ printf("%s, %d ops; %s (%s/sec and %.4f ops/sec)\n",
+ s1, cnt, ts, s2, tdiv((double)cnt, *t));
+ } else {/* bytes,ops,time,bytes/sec,ops/sec */
+ printf("%d,%d,%s,%.3f,%.3f\n",
+ total, cnt, ts,
+ tdiv((double)total, *t),
+ tdiv((double)cnt, *t));
+ }
}
/*
@@ -127,192 +130,200 @@ print_report(const char *op, struct timeval *t, int64_t offset,
static void *
create_iovec(QEMUIOVector *qiov, char **argv, int nr_iov, int pattern)
{
- size_t *sizes = calloc(nr_iov, sizeof(size_t));
- size_t count = 0;
- void *buf = NULL;
- void *p;
- int i;
-
- for (i = 0; i < nr_iov; i++) {
- char *arg = argv[i];
- int64_t len;
-
- len = cvtnum(arg);
- if (len < 0) {
- printf("non-numeric length argument -- %s\n", arg);
- goto fail;
- }
-
- /* should be SIZE_T_MAX, but that doesn't exist */
- if (len > INT_MAX) {
- printf("too large length argument -- %s\n", arg);
- goto fail;
- }
-
- if (len & 0x1ff) {
- printf("length argument %" PRId64
- " is not sector aligned\n", len);
- goto fail;
- }
-
- sizes[i] = len;
- count += len;
- }
-
- qemu_iovec_init(qiov, nr_iov);
-
- buf = p = qemu_io_alloc(count, pattern);
-
- for (i = 0; i < nr_iov; i++) {
- qemu_iovec_add(qiov, p, sizes[i]);
- p += sizes[i];
- }
+ size_t *sizes = calloc(nr_iov, sizeof(size_t));
+ size_t count = 0;
+ void *buf = NULL;
+ void *p;
+ int i;
+
+ for (i = 0; i < nr_iov; i++) {
+ char *arg = argv[i];
+ int64_t len;
+
+ len = cvtnum(arg);
+ if (len < 0) {
+ printf("non-numeric length argument -- %s\n", arg);
+ goto fail;
+ }
+
+ /* should be SIZE_T_MAX, but that doesn't exist */
+ if (len > INT_MAX) {
+ printf("too large length argument -- %s\n", arg);
+ goto fail;
+ }
+
+ if (len & 0x1ff) {
+ printf("length argument %" PRId64
+ " is not sector aligned\n", len);
+ goto fail;
+ }
+
+ sizes[i] = len;
+ count += len;
+ }
+
+ qemu_iovec_init(qiov, nr_iov);
+
+ buf = p = qemu_io_alloc(count, pattern);
+
+ for (i = 0; i < nr_iov; i++) {
+ qemu_iovec_add(qiov, p, sizes[i]);
+ p += sizes[i];
+ }
fail:
- free(sizes);
- return buf;
+ free(sizes);
+ return buf;
}
static int do_read(char *buf, int64_t offset, int count, int *total)
{
- int ret;
+ int ret;
- ret = bdrv_read(bs, offset >> 9, (uint8_t *)buf, count >> 9);
- if (ret < 0)
- return ret;
- *total = count;
- return 1;
+ ret = bdrv_read(bs, offset >> 9, (uint8_t *)buf, count >> 9);
+ if (ret < 0) {
+ return ret;
+ }
+ *total = count;
+ return 1;
}
static int do_write(char *buf, int64_t offset, int count, int *total)
{
- int ret;
+ int ret;
- ret = bdrv_write(bs, offset >> 9, (uint8_t *)buf, count >> 9);
- if (ret < 0)
- return ret;
- *total = count;
- return 1;
+ ret = bdrv_write(bs, offset >> 9, (uint8_t *)buf, count >> 9);
+ if (ret < 0) {
+ return ret;
+ }
+ *total = count;
+ return 1;
}
static int do_pread(char *buf, int64_t offset, int count, int *total)
{
- *total = bdrv_pread(bs, offset, (uint8_t *)buf, count);
- if (*total < 0)
- return *total;
- return 1;
+ *total = bdrv_pread(bs, offset, (uint8_t *)buf, count);
+ if (*total < 0) {
+ return *total;
+ }
+ return 1;
}
static int do_pwrite(char *buf, int64_t offset, int count, int *total)
{
- *total = bdrv_pwrite(bs, offset, (uint8_t *)buf, count);
- if (*total < 0)
- return *total;
- return 1;
+ *total = bdrv_pwrite(bs, offset, (uint8_t *)buf, count);
+ if (*total < 0) {
+ return *total;
+ }
+ return 1;
}
static int do_load_vmstate(char *buf, int64_t offset, int count, int *total)
{
- *total = bdrv_load_vmstate(bs, (uint8_t *)buf, offset, count);
- if (*total < 0)
- return *total;
- return 1;
+ *total = bdrv_load_vmstate(bs, (uint8_t *)buf, offset, count);
+ if (*total < 0) {
+ return *total;
+ }
+ return 1;
}
static int do_save_vmstate(char *buf, int64_t offset, int count, int *total)
{
- *total = bdrv_save_vmstate(bs, (uint8_t *)buf, offset, count);
- if (*total < 0)
- return *total;
- return 1;
+ *total = bdrv_save_vmstate(bs, (uint8_t *)buf, offset, count);
+ if (*total < 0) {
+ return *total;
+ }
+ return 1;
}
#define NOT_DONE 0x7fffffff
static void aio_rw_done(void *opaque, int ret)
{
- *(int *)opaque = ret;
+ *(int *)opaque = ret;
}
static int do_aio_readv(QEMUIOVector *qiov, int64_t offset, int *total)
{
- BlockDriverAIOCB *acb;
- int async_ret = NOT_DONE;
+ BlockDriverAIOCB *acb;
+ int async_ret = NOT_DONE;
- acb = bdrv_aio_readv(bs, offset >> 9, qiov, qiov->size >> 9,
- aio_rw_done, &async_ret);
- if (!acb)
- return -EIO;
-
- while (async_ret == NOT_DONE)
- qemu_aio_wait();
+ acb = bdrv_aio_readv(bs, offset >> 9, qiov, qiov->size >> 9,
+ aio_rw_done, &async_ret);
+ if (!acb) {
+ return -EIO;
+ }
+ while (async_ret == NOT_DONE) {
+ qemu_aio_wait();
+ }
- *total = qiov->size;
- return async_ret < 0 ? async_ret : 1;
+ *total = qiov->size;
+ return async_ret < 0 ? async_ret : 1;
}
static int do_aio_writev(QEMUIOVector *qiov, int64_t offset, int *total)
{
- BlockDriverAIOCB *acb;
- int async_ret = NOT_DONE;
+ BlockDriverAIOCB *acb;
+ int async_ret = NOT_DONE;
- acb = bdrv_aio_writev(bs, offset >> 9, qiov, qiov->size >> 9,
- aio_rw_done, &async_ret);
- if (!acb)
- return -EIO;
+ acb = bdrv_aio_writev(bs, offset >> 9, qiov, qiov->size >> 9,
+ aio_rw_done, &async_ret);
+ if (!acb) {
+ return -EIO;
+ }
- while (async_ret == NOT_DONE)
- qemu_aio_wait();
+ while (async_ret == NOT_DONE) {
+ qemu_aio_wait();
+ }
- *total = qiov->size;
- return async_ret < 0 ? async_ret : 1;
+ *total = qiov->size;
+ return async_ret < 0 ? async_ret : 1;
}
struct multiwrite_async_ret {
- int num_done;
- int error;
+ int num_done;
+ int error;
};
static void multiwrite_cb(void *opaque, int ret)
{
- struct multiwrite_async_ret *async_ret = opaque;
+ struct multiwrite_async_ret *async_ret = opaque;
- async_ret->num_done++;
- if (ret < 0) {
- async_ret->error = ret;
- }
+ async_ret->num_done++;
+ if (ret < 0) {
+ async_ret->error = ret;
+ }
}
static int do_aio_multiwrite(BlockRequest* reqs, int num_reqs, int *total)
{
- int i, ret;
- struct multiwrite_async_ret async_ret = {
- .num_done = 0,
- .error = 0,
- };
-
- *total = 0;
- for (i = 0; i < num_reqs; i++) {
- reqs[i].cb = multiwrite_cb;
- reqs[i].opaque = &async_ret;
- *total += reqs[i].qiov->size;
- }
-
- ret = bdrv_aio_multiwrite(bs, reqs, num_reqs);
- if (ret < 0) {
- return ret;
- }
-
- while (async_ret.num_done < num_reqs) {
- qemu_aio_wait();
- }
-
- return async_ret.error < 0 ? async_ret.error : 1;
+ int i, ret;
+ struct multiwrite_async_ret async_ret = {
+ .num_done = 0,
+ .error = 0,
+ };
+
+ *total = 0;
+ for (i = 0; i < num_reqs; i++) {
+ reqs[i].cb = multiwrite_cb;
+ reqs[i].opaque = &async_ret;
+ *total += reqs[i].qiov->size;
+ }
+
+ ret = bdrv_aio_multiwrite(bs, reqs, num_reqs);
+ if (ret < 0) {
+ return ret;
+ }
+
+ while (async_ret.num_done < num_reqs) {
+ qemu_aio_wait();
+ }
+
+ return async_ret.error < 0 ? async_ret.error : 1;
}
-static void
-read_help(void)
+static void read_help(void)
{
- printf(
+ printf(
"\n"
" reads a range of bytes from the given offset\n"
"\n"
@@ -335,94 +346,95 @@ read_help(void)
static int read_f(int argc, char **argv);
static const cmdinfo_t read_cmd = {
- .name = "read",
- .altname = "r",
- .cfunc = read_f,
- .argmin = 2,
- .argmax = -1,
- .args = "[-abCpqv] [-P pattern [-s off] [-l len]] off len",
- .oneline = "reads a number of bytes at a specified offset",
- .help = read_help,
+ .name = "read",
+ .altname = "r",
+ .cfunc = read_f,
+ .argmin = 2,
+ .argmax = -1,
+ .args = "[-abCpqv] [-P pattern [-s off] [-l len]] off len",
+ .oneline = "reads a number of bytes at a specified offset",
+ .help = read_help,
};
-static int
-read_f(int argc, char **argv)
+static int read_f(int argc, char **argv)
{
- struct timeval t1, t2;
- int Cflag = 0, pflag = 0, qflag = 0, vflag = 0;
- int Pflag = 0, sflag = 0, lflag = 0, bflag = 0;
- int c, cnt;
- char *buf;
- int64_t offset;
- int count;
- /* Some compilers get confused and warn if this is not initialized. */
- int total = 0;
- int pattern = 0, pattern_offset = 0, pattern_count = 0;
-
- while ((c = getopt(argc, argv, "bCl:pP:qs:v")) != EOF) {
- switch (c) {
- case 'b':
- bflag = 1;
- break;
- case 'C':
- Cflag = 1;
- break;
- case 'l':
- lflag = 1;
- pattern_count = cvtnum(optarg);
- if (pattern_count < 0) {
- printf("non-numeric length argument -- %s\n", optarg);
- return 0;
- }
- break;
- case 'p':
- pflag = 1;
- break;
- case 'P':
- Pflag = 1;
- pattern = parse_pattern(optarg);
- if (pattern < 0)
- return 0;
- break;
- case 'q':
- qflag = 1;
- break;
- case 's':
- sflag = 1;
- pattern_offset = cvtnum(optarg);
- if (pattern_offset < 0) {
- printf("non-numeric length argument -- %s\n", optarg);
- return 0;
- }
- break;
- case 'v':
- vflag = 1;
- break;
- default:
- return command_usage(&read_cmd);
- }
- }
-
- if (optind != argc - 2)
- return command_usage(&read_cmd);
-
- if (bflag && pflag) {
- printf("-b and -p cannot be specified at the same time\n");
- return 0;
- }
-
- offset = cvtnum(argv[optind]);
- if (offset < 0) {
- printf("non-numeric length argument -- %s\n", argv[optind]);
- return 0;
- }
-
- optind++;
- count = cvtnum(argv[optind]);
- if (count < 0) {
- printf("non-numeric length argument -- %s\n", argv[optind]);
- return 0;
- }
+ struct timeval t1, t2;
+ int Cflag = 0, pflag = 0, qflag = 0, vflag = 0;
+ int Pflag = 0, sflag = 0, lflag = 0, bflag = 0;
+ int c, cnt;
+ char *buf;
+ int64_t offset;
+ int count;
+ /* Some compilers get confused and warn if this is not initialized. */
+ int total = 0;
+ int pattern = 0, pattern_offset = 0, pattern_count = 0;
+
+ while ((c = getopt(argc, argv, "bCl:pP:qs:v")) != EOF) {
+ switch (c) {
+ case 'b':
+ bflag = 1;
+ break;
+ case 'C':
+ Cflag = 1;
+ break;
+ case 'l':
+ lflag = 1;
+ pattern_count = cvtnum(optarg);
+ if (pattern_count < 0) {
+ printf("non-numeric length argument -- %s\n", optarg);
+ return 0;
+ }
+ break;
+ case 'p':
+ pflag = 1;
+ break;
+ case 'P':
+ Pflag = 1;
+ pattern = parse_pattern(optarg);
+ if (pattern < 0) {
+ return 0;
+ }
+ break;
+ case 'q':
+ qflag = 1;
+ break;
+ case 's':
+ sflag = 1;
+ pattern_offset = cvtnum(optarg);
+ if (pattern_offset < 0) {
+ printf("non-numeric length argument -- %s\n", optarg);
+ return 0;
+ }
+ break;
+ case 'v':
+ vflag = 1;
+ break;
+ default:
+ return command_usage(&read_cmd);
+ }
+ }
+
+ if (optind != argc - 2) {
+ return command_usage(&read_cmd);
+ }
+
+ if (bflag && pflag) {
+ printf("-b and -p cannot be specified at the same time\n");
+ return 0;
+ }
+
+ offset = cvtnum(argv[optind]);
+ if (offset < 0) {
+ printf("non-numeric length argument -- %s\n", argv[optind]);
+ return 0;
+ }
+
+ optind++;
+ count = cvtnum(argv[optind]);
+ if (count < 0) {
+ printf("non-numeric length argument -- %s\n", argv[optind]);
+ return 0;
+ }
if (!Pflag && (lflag || sflag)) {
return command_usage(&read_cmd);
@@ -437,66 +449,68 @@ read_f(int argc, char **argv)
return 0;
}
- if (!pflag)
- if (offset & 0x1ff) {
- printf("offset %" PRId64 " is not sector aligned\n",
- offset);
- return 0;
-
- if (count & 0x1ff) {
- printf("count %d is not sector aligned\n",
- count);
- return 0;
- }
- }
-
- buf = qemu_io_alloc(count, 0xab);
-
- gettimeofday(&t1, NULL);
- if (pflag)
- cnt = do_pread(buf, offset, count, &total);
- else if (bflag)
- cnt = do_load_vmstate(buf, offset, count, &total);
- else
- cnt = do_read(buf, offset, count, &total);
- gettimeofday(&t2, NULL);
-
- if (cnt < 0) {
- printf("read failed: %s\n", strerror(-cnt));
- goto out;
- }
-
- if (Pflag) {
- void* cmp_buf = malloc(pattern_count);
- memset(cmp_buf, pattern, pattern_count);
- if (memcmp(buf + pattern_offset, cmp_buf, pattern_count)) {
- printf("Pattern verification failed at offset %"
- PRId64 ", %d bytes\n",
- offset + pattern_offset, pattern_count);
- }
- free(cmp_buf);
- }
-
- if (qflag)
- goto out;
-
- if (vflag)
- dump_buffer(buf, offset, count);
-
- /* Finally, report back -- -C gives a parsable format */
- t2 = tsub(t2, t1);
- print_report("read", &t2, offset, count, total, cnt, Cflag);
+ if (!pflag) {
+ if (offset & 0x1ff) {
+ printf("offset %" PRId64 " is not sector aligned\n",
+ offset);
+ return 0;
+ }
+ if (count & 0x1ff) {
+ printf("count %d is not sector aligned\n",
+ count);
+ return 0;
+ }
+ }
+
+ buf = qemu_io_alloc(count, 0xab);
+
+ gettimeofday(&t1, NULL);
+ if (pflag) {
+ cnt = do_pread(buf, offset, count, &total);
+ } else if (bflag) {
+ cnt = do_load_vmstate(buf, offset, count, &total);
+ } else {
+ cnt = do_read(buf, offset, count, &total);
+ }
+ gettimeofday(&t2, NULL);
+
+ if (cnt < 0) {
+ printf("read failed: %s\n", strerror(-cnt));
+ goto out;
+ }
+
+ if (Pflag) {
+ void *cmp_buf = malloc(pattern_count);
+ memset(cmp_buf, pattern, pattern_count);
+ if (memcmp(buf + pattern_offset, cmp_buf, pattern_count)) {
+ printf("Pattern verification failed at offset %"
+ PRId64 ", %d bytes\n",
+ offset + pattern_offset, pattern_count);
+ }
+ free(cmp_buf);
+ }
+
+ if (qflag) {
+ goto out;
+ }
+
+ if (vflag) {
+ dump_buffer(buf, offset, count);
+ }
+
+ /* Finally, report back -- -C gives a parsable format */
+ t2 = tsub(t2, t1);
+ print_report("read", &t2, offset, count, total, cnt, Cflag);
out:
- qemu_io_free(buf);
+ qemu_io_free(buf);
- return 0;
+ return 0;
}
-static void
-readv_help(void)
+static void readv_help(void)
{
- printf(
+ printf(
"\n"
" reads a range of bytes from the given offset into multiple buffers\n"
"\n"
@@ -516,111 +530,112 @@ readv_help(void)
static int readv_f(int argc, char **argv);
static const cmdinfo_t readv_cmd = {
- .name = "readv",
- .cfunc = readv_f,
- .argmin = 2,
- .argmax = -1,
- .args = "[-Cqv] [-P pattern ] off len [len..]",
- .oneline = "reads a number of bytes at a specified offset",
- .help = readv_help,
+ .name = "readv",
+ .cfunc = readv_f,
+ .argmin = 2,
+ .argmax = -1,
+ .args = "[-Cqv] [-P pattern ] off len [len..]",
+ .oneline = "reads a number of bytes at a specified offset",
+ .help = readv_help,
};
-static int
-readv_f(int argc, char **argv)
+static int readv_f(int argc, char **argv)
{
- struct timeval t1, t2;
- int Cflag = 0, qflag = 0, vflag = 0;
- int c, cnt;
- char *buf;
- int64_t offset;
- /* Some compilers get confused and warn if this is not initialized. */
- int total = 0;
- int nr_iov;
- QEMUIOVector qiov;
- int pattern = 0;
- int Pflag = 0;
-
- while ((c = getopt(argc, argv, "CP:qv")) != EOF) {
- switch (c) {
- case 'C':
- Cflag = 1;
- break;
- case 'P':
- Pflag = 1;
- pattern = parse_pattern(optarg);
- if (pattern < 0)
- return 0;
- break;
- case 'q':
- qflag = 1;
- break;
- case 'v':
- vflag = 1;
- break;
- default:
- return command_usage(&readv_cmd);
- }
- }
-
- if (optind > argc - 2)
- return command_usage(&readv_cmd);
-
-
- offset = cvtnum(argv[optind]);
- if (offset < 0) {
- printf("non-numeric length argument -- %s\n", argv[optind]);
- return 0;
- }
- optind++;
-
- if (offset & 0x1ff) {
- printf("offset %" PRId64 " is not sector aligned\n",
- offset);
- return 0;
- }
-
- nr_iov = argc - optind;
- buf = create_iovec(&qiov, &argv[optind], nr_iov, 0xab);
-
- gettimeofday(&t1, NULL);
- cnt = do_aio_readv(&qiov, offset, &total);
- gettimeofday(&t2, NULL);
-
- if (cnt < 0) {
- printf("readv failed: %s\n", strerror(-cnt));
- goto out;
- }
-
- if (Pflag) {
- void* cmp_buf = malloc(qiov.size);
- memset(cmp_buf, pattern, qiov.size);
- if (memcmp(buf, cmp_buf, qiov.size)) {
- printf("Pattern verification failed at offset %"
- PRId64 ", %zd bytes\n",
- offset, qiov.size);
- }
- free(cmp_buf);
- }
-
- if (qflag)
- goto out;
-
- if (vflag)
- dump_buffer(buf, offset, qiov.size);
-
- /* Finally, report back -- -C gives a parsable format */
- t2 = tsub(t2, t1);
- print_report("read", &t2, offset, qiov.size, total, cnt, Cflag);
+ struct timeval t1, t2;
+ int Cflag = 0, qflag = 0, vflag = 0;
+ int c, cnt;
+ char *buf;
+ int64_t offset;
+ /* Some compilers get confused and warn if this is not initialized. */
+ int total = 0;
+ int nr_iov;
+ QEMUIOVector qiov;
+ int pattern = 0;
+ int Pflag = 0;
+
+ while ((c = getopt(argc, argv, "CP:qv")) != EOF) {
+ switch (c) {
+ case 'C':
+ Cflag = 1;
+ break;
+ case 'P':
+ Pflag = 1;
+ pattern = parse_pattern(optarg);
+ if (pattern < 0) {
+ return 0;
+ }
+ break;
+ case 'q':
+ qflag = 1;
+ break;
+ case 'v':
+ vflag = 1;
+ break;
+ default:
+ return command_usage(&readv_cmd);
+ }
+ }
+
+ if (optind > argc - 2) {
+ return command_usage(&readv_cmd);
+ }
+
+
+ offset = cvtnum(argv[optind]);
+ if (offset < 0) {
+ printf("non-numeric length argument -- %s\n", argv[optind]);
+ return 0;
+ }
+ optind++;
+
+ if (offset & 0x1ff) {
+ printf("offset %" PRId64 " is not sector aligned\n",
+ offset);
+ return 0;
+ }
+
+ nr_iov = argc - optind;
+ buf = create_iovec(&qiov, &argv[optind], nr_iov, 0xab);
+
+ gettimeofday(&t1, NULL);
+ cnt = do_aio_readv(&qiov, offset, &total);
+ gettimeofday(&t2, NULL);
+
+ if (cnt < 0) {
+ printf("readv failed: %s\n", strerror(-cnt));
+ goto out;
+ }
+
+ if (Pflag) {
+ void *cmp_buf = malloc(qiov.size);
+ memset(cmp_buf, pattern, qiov.size);
+ if (memcmp(buf, cmp_buf, qiov.size)) {
+ printf("Pattern verification failed at offset %"
+ PRId64 ", %zd bytes\n", offset, qiov.size);
+ }
+ free(cmp_buf);
+ }
+
+ if (qflag) {
+ goto out;
+ }
+
+ if (vflag) {
+ dump_buffer(buf, offset, qiov.size);
+ }
+
+ /* Finally, report back -- -C gives a parsable format */
+ t2 = tsub(t2, t1);
+ print_report("read", &t2, offset, qiov.size, total, cnt, Cflag);
out:
- qemu_io_free(buf);
- return 0;
+ qemu_io_free(buf);
+ return 0;
}
-static void
-write_help(void)
+static void write_help(void)
{
- printf(
+ printf(
"\n"
" writes a range of bytes from the given offset\n"
"\n"
@@ -640,121 +655,124 @@ write_help(void)
static int write_f(int argc, char **argv);
static const cmdinfo_t write_cmd = {
- .name = "write",
- .altname = "w",
- .cfunc = write_f,
- .argmin = 2,
- .argmax = -1,
- .args = "[-abCpq] [-P pattern ] off len",
- .oneline = "writes a number of bytes at a specified offset",
- .help = write_help,
+ .name = "write",
+ .altname = "w",
+ .cfunc = write_f,
+ .argmin = 2,
+ .argmax = -1,
+ .args = "[-abCpq] [-P pattern ] off len",
+ .oneline = "writes a number of bytes at a specified offset",
+ .help = write_help,
};
-static int
-write_f(int argc, char **argv)
+static int write_f(int argc, char **argv)
{
- struct timeval t1, t2;
- int Cflag = 0, pflag = 0, qflag = 0, bflag = 0;
- int c, cnt;
- char *buf;
- int64_t offset;
- int count;
- /* Some compilers get confused and warn if this is not initialized. */
- int total = 0;
- int pattern = 0xcd;
-
- while ((c = getopt(argc, argv, "bCpP:q")) != EOF) {
- switch (c) {
- case 'b':
- bflag = 1;
- break;
- case 'C':
- Cflag = 1;
- break;
- case 'p':
- pflag = 1;
- break;
- case 'P':
- pattern = parse_pattern(optarg);
- if (pattern < 0)
- return 0;
- break;
- case 'q':
- qflag = 1;
- break;
- default:
- return command_usage(&write_cmd);
- }
- }
-
- if (optind != argc - 2)
- return command_usage(&write_cmd);
-
- if (bflag && pflag) {
- printf("-b and -p cannot be specified at the same time\n");
- return 0;
- }
-
- offset = cvtnum(argv[optind]);
- if (offset < 0) {
- printf("non-numeric length argument -- %s\n", argv[optind]);
- return 0;
- }
-
- optind++;
- count = cvtnum(argv[optind]);
- if (count < 0) {
- printf("non-numeric length argument -- %s\n", argv[optind]);
- return 0;
- }
-
- if (!pflag) {
- if (offset & 0x1ff) {
- printf("offset %" PRId64 " is not sector aligned\n",
- offset);
- return 0;
- }
-
- if (count & 0x1ff) {
- printf("count %d is not sector aligned\n",
- count);
- return 0;
- }
- }
-
- buf = qemu_io_alloc(count, pattern);
-
- gettimeofday(&t1, NULL);
- if (pflag)
- cnt = do_pwrite(buf, offset, count, &total);
- else if (bflag)
- cnt = do_save_vmstate(buf, offset, count, &total);
- else
- cnt = do_write(buf, offset, count, &total);
- gettimeofday(&t2, NULL);
-
- if (cnt < 0) {
- printf("write failed: %s\n", strerror(-cnt));
- goto out;
- }
-
- if (qflag)
- goto out;
-
- /* Finally, report back -- -C gives a parsable format */
- t2 = tsub(t2, t1);
- print_report("wrote", &t2, offset, count, total, cnt, Cflag);
+ struct timeval t1, t2;
+ int Cflag = 0, pflag = 0, qflag = 0, bflag = 0;
+ int c, cnt;
+ char *buf;
+ int64_t offset;
+ int count;
+ /* Some compilers get confused and warn if this is not initialized. */
+ int total = 0;
+ int pattern = 0xcd;
+
+ while ((c = getopt(argc, argv, "bCpP:q")) != EOF) {
+ switch (c) {
+ case 'b':
+ bflag = 1;
+ break;
+ case 'C':
+ Cflag = 1;
+ break;
+ case 'p':
+ pflag = 1;
+ break;
+ case 'P':
+ pattern = parse_pattern(optarg);
+ if (pattern < 0) {
+ return 0;
+ }
+ break;
+ case 'q':
+ qflag = 1;
+ break;
+ default:
+ return command_usage(&write_cmd);
+ }
+ }
+
+ if (optind != argc - 2) {
+ return command_usage(&write_cmd);
+ }
+
+ if (bflag && pflag) {
+ printf("-b and -p cannot be specified at the same time\n");
+ return 0;
+ }
+
+ offset = cvtnum(argv[optind]);
+ if (offset < 0) {
+ printf("non-numeric length argument -- %s\n", argv[optind]);
+ return 0;
+ }
+
+ optind++;
+ count = cvtnum(argv[optind]);
+ if (count < 0) {
+ printf("non-numeric length argument -- %s\n", argv[optind]);
+ return 0;
+ }
+
+ if (!pflag) {
+ if (offset & 0x1ff) {
+ printf("offset %" PRId64 " is not sector aligned\n",
+ offset);
+ return 0;
+ }
+
+ if (count & 0x1ff) {
+ printf("count %d is not sector aligned\n",
+ count);
+ return 0;
+ }
+ }
+
+ buf = qemu_io_alloc(count, pattern);
+
+ gettimeofday(&t1, NULL);
+ if (pflag) {
+ cnt = do_pwrite(buf, offset, count, &total);
+ } else if (bflag) {
+ cnt = do_save_vmstate(buf, offset, count, &total);
+ } else {
+ cnt = do_write(buf, offset, count, &total);
+ }
+ gettimeofday(&t2, NULL);
+
+ if (cnt < 0) {
+ printf("write failed: %s\n", strerror(-cnt));
+ goto out;
+ }
+
+ if (qflag) {
+ goto out;
+ }
+
+ /* Finally, report back -- -C gives a parsable format */
+ t2 = tsub(t2, t1);
+ print_report("wrote", &t2, offset, count, total, cnt, Cflag);
out:
- qemu_io_free(buf);
+ qemu_io_free(buf);
- return 0;
+ return 0;
}
static void
writev_help(void)
{
- printf(
+ printf(
"\n"
" writes a range of bytes from the given offset source from multiple buffers\n"
"\n"
@@ -772,90 +790,91 @@ writev_help(void)
static int writev_f(int argc, char **argv);
static const cmdinfo_t writev_cmd = {
- .name = "writev",
- .cfunc = writev_f,
- .argmin = 2,
- .argmax = -1,
- .args = "[-Cq] [-P pattern ] off len [len..]",
- .oneline = "writes a number of bytes at a specified offset",
- .help = writev_help,
+ .name = "writev",
+ .cfunc = writev_f,
+ .argmin = 2,
+ .argmax = -1,
+ .args = "[-Cq] [-P pattern ] off len [len..]",
+ .oneline = "writes a number of bytes at a specified offset",
+ .help = writev_help,
};
-static int
-writev_f(int argc, char **argv)
+static int writev_f(int argc, char **argv)
{
- struct timeval t1, t2;
- int Cflag = 0, qflag = 0;
- int c, cnt;
- char *buf;
- int64_t offset;
- /* Some compilers get confused and warn if this is not initialized. */
- int total = 0;
- int nr_iov;
- int pattern = 0xcd;
- QEMUIOVector qiov;
-
- while ((c = getopt(argc, argv, "CqP:")) != EOF) {
- switch (c) {
- case 'C':
- Cflag = 1;
- break;
- case 'q':
- qflag = 1;
- break;
- case 'P':
- pattern = parse_pattern(optarg);
- if (pattern < 0)
- return 0;
- break;
- default:
- return command_usage(&writev_cmd);
- }
- }
-
- if (optind > argc - 2)
- return command_usage(&writev_cmd);
-
- offset = cvtnum(argv[optind]);
- if (offset < 0) {
- printf("non-numeric length argument -- %s\n", argv[optind]);
- return 0;
- }
- optind++;
-
- if (offset & 0x1ff) {
- printf("offset %" PRId64 " is not sector aligned\n",
- offset);
- return 0;
- }
-
- nr_iov = argc - optind;
- buf = create_iovec(&qiov, &argv[optind], nr_iov, pattern);
-
- gettimeofday(&t1, NULL);
- cnt = do_aio_writev(&qiov, offset, &total);
- gettimeofday(&t2, NULL);
-
- if (cnt < 0) {
- printf("writev failed: %s\n", strerror(-cnt));
- goto out;
- }
-
- if (qflag)
- goto out;
-
- /* Finally, report back -- -C gives a parsable format */
- t2 = tsub(t2, t1);
- print_report("wrote", &t2, offset, qiov.size, total, cnt, Cflag);
+ struct timeval t1, t2;
+ int Cflag = 0, qflag = 0;
+ int c, cnt;
+ char *buf;
+ int64_t offset;
+ /* Some compilers get confused and warn if this is not initialized. */
+ int total = 0;
+ int nr_iov;
+ int pattern = 0xcd;
+ QEMUIOVector qiov;
+
+ while ((c = getopt(argc, argv, "CqP:")) != EOF) {
+ switch (c) {
+ case 'C':
+ Cflag = 1;
+ break;
+ case 'q':
+ qflag = 1;
+ break;
+ case 'P':
+ pattern = parse_pattern(optarg);
+ if (pattern < 0) {
+ return 0;
+ }
+ break;
+ default:
+ return command_usage(&writev_cmd);
+ }
+ }
+
+ if (optind > argc - 2) {
+ return command_usage(&writev_cmd);
+ }
+
+ offset = cvtnum(argv[optind]);
+ if (offset < 0) {
+ printf("non-numeric length argument -- %s\n", argv[optind]);
+ return 0;
+ }
+ optind++;
+
+ if (offset & 0x1ff) {
+ printf("offset %" PRId64 " is not sector aligned\n",
+ offset);
+ return 0;
+ }
+
+ nr_iov = argc - optind;
+ buf = create_iovec(&qiov, &argv[optind], nr_iov, pattern);
+
+ gettimeofday(&t1, NULL);
+ cnt = do_aio_writev(&qiov, offset, &total);
+ gettimeofday(&t2, NULL);
+
+ if (cnt < 0) {
+ printf("writev failed: %s\n", strerror(-cnt));
+ goto out;
+ }
+
+ if (qflag) {
+ goto out;
+ }
+
+ /* Finally, report back -- -C gives a parsable format */
+ t2 = tsub(t2, t1);
+ print_report("wrote", &t2, offset, qiov.size, total, cnt, Cflag);
out:
- qemu_io_free(buf);
- return 0;
+ qemu_io_free(buf);
+ return 0;
}
-static void
-multiwrite_help(void)
+static void multiwrite_help(void)
{
- printf(
+ printf(
"\n"
" writes a range of bytes from the given offset source from multiple buffers,\n"
" in a batch of requests that may be merged by qemu\n"
@@ -876,217 +895,215 @@ multiwrite_help(void)
static int multiwrite_f(int argc, char **argv);
static const cmdinfo_t multiwrite_cmd = {
- .name = "multiwrite",
- .cfunc = multiwrite_f,
- .argmin = 2,
- .argmax = -1,
- .args = "[-Cq] [-P pattern ] off len [len..] [; off len [len..]..]",
- .oneline = "issues multiple write requests at once",
- .help = multiwrite_help,
+ .name = "multiwrite",
+ .cfunc = multiwrite_f,
+ .argmin = 2,
+ .argmax = -1,
+ .args = "[-Cq] [-P pattern ] off len [len..] [; off len [len..]..]",
+ .oneline = "issues multiple write requests at once",
+ .help = multiwrite_help,
};
-static int
-multiwrite_f(int argc, char **argv)
+static int multiwrite_f(int argc, char **argv)
{
- struct timeval t1, t2;
- int Cflag = 0, qflag = 0;
- int c, cnt;
- char **buf;
- int64_t offset, first_offset = 0;
- /* Some compilers get confused and warn if this is not initialized. */
- int total = 0;
- int nr_iov;
- int nr_reqs;
- int pattern = 0xcd;
- QEMUIOVector *qiovs;
- int i;
- BlockRequest *reqs;
-
- while ((c = getopt(argc, argv, "CqP:")) != EOF) {
- switch (c) {
- case 'C':
- Cflag = 1;
- break;
- case 'q':
- qflag = 1;
- break;
- case 'P':
- pattern = parse_pattern(optarg);
- if (pattern < 0)
- return 0;
- break;
- default:
- return command_usage(&writev_cmd);
- }
- }
-
- if (optind > argc - 2)
- return command_usage(&writev_cmd);
-
- nr_reqs = 1;
- for (i = optind; i < argc; i++) {
- if (!strcmp(argv[i], ";")) {
- nr_reqs++;
- }
- }
-
- reqs = qemu_malloc(nr_reqs * sizeof(*reqs));
- buf = qemu_malloc(nr_reqs * sizeof(*buf));
- qiovs = qemu_malloc(nr_reqs * sizeof(*qiovs));
-
- for (i = 0; i < nr_reqs; i++) {
- int j;
-
- /* Read the offset of the request */
- offset = cvtnum(argv[optind]);
- if (offset < 0) {
- printf("non-numeric offset argument -- %s\n", argv[optind]);
- return 0;
- }
- optind++;
-
- if (offset & 0x1ff) {
- printf("offset %lld is not sector aligned\n",
- (long long)offset);
- return 0;
- }
+ struct timeval t1, t2;
+ int Cflag = 0, qflag = 0;
+ int c, cnt;
+ char **buf;
+ int64_t offset, first_offset = 0;
+ /* Some compilers get confused and warn if this is not initialized. */
+ int total = 0;
+ int nr_iov;
+ int nr_reqs;
+ int pattern = 0xcd;
+ QEMUIOVector *qiovs;
+ int i;
+ BlockRequest *reqs;
+
+ while ((c = getopt(argc, argv, "CqP:")) != EOF) {
+ switch (c) {
+ case 'C':
+ Cflag = 1;
+ break;
+ case 'q':
+ qflag = 1;
+ break;
+ case 'P':
+ pattern = parse_pattern(optarg);
+ if (pattern < 0) {
+ return 0;
+ }
+ break;
+ default:
+ return command_usage(&writev_cmd);
+ }
+ }
+
+ if (optind > argc - 2) {
+ return command_usage(&writev_cmd);
+ }
+
+ nr_reqs = 1;
+ for (i = optind; i < argc; i++) {
+ if (!strcmp(argv[i], ";")) {
+ nr_reqs++;
+ }
+ }
+
+ reqs = qemu_malloc(nr_reqs * sizeof(*reqs));
+ buf = qemu_malloc(nr_reqs * sizeof(*buf));
+ qiovs = qemu_malloc(nr_reqs * sizeof(*qiovs));
+
+ for (i = 0; i < nr_reqs; i++) {
+ int j;
+
+ /* Read the offset of the request */
+ offset = cvtnum(argv[optind]);
+ if (offset < 0) {
+ printf("non-numeric offset argument -- %s\n", argv[optind]);
+ return 0;
+ }
+ optind++;
+
+ if (offset & 0x1ff) {
+ printf("offset %lld is not sector aligned\n",
+ (long long)offset);
+ return 0;
+ }
if (i == 0) {
first_offset = offset;
}
- /* Read lengths for qiov entries */
- for (j = optind; j < argc; j++) {
- if (!strcmp(argv[j], ";")) {
- break;
- }
- }
+ /* Read lengths for qiov entries */
+ for (j = optind; j < argc; j++) {
+ if (!strcmp(argv[j], ";")) {
+ break;
+ }
+ }
- nr_iov = j - optind;
+ nr_iov = j - optind;
- /* Build request */
- reqs[i].qiov = &qiovs[i];
- buf[i] = create_iovec(reqs[i].qiov, &argv[optind], nr_iov, pattern);
- reqs[i].sector = offset >> 9;
- reqs[i].nb_sectors = reqs[i].qiov->size >> 9;
+ /* Build request */
+ reqs[i].qiov = &qiovs[i];
+ buf[i] = create_iovec(reqs[i].qiov, &argv[optind], nr_iov, pattern);
+ reqs[i].sector = offset >> 9;
+ reqs[i].nb_sectors = reqs[i].qiov->size >> 9;
- optind = j + 1;
+ optind = j + 1;
- offset += reqs[i].qiov->size;
- pattern++;
- }
+ offset += reqs[i].qiov->size;
+ pattern++;
+ }
- gettimeofday(&t1, NULL);
- cnt = do_aio_multiwrite(reqs, nr_reqs, &total);
- gettimeofday(&t2, NULL);
+ gettimeofday(&t1, NULL);
+ cnt = do_aio_multiwrite(reqs, nr_reqs, &total);
+ gettimeofday(&t2, NULL);
- if (cnt < 0) {
- printf("aio_multiwrite failed: %s\n", strerror(-cnt));
- goto out;
- }
+ if (cnt < 0) {
+ printf("aio_multiwrite failed: %s\n", strerror(-cnt));
+ goto out;
+ }
- if (qflag)
- goto out;
+ if (qflag) {
+ goto out;
+ }
- /* Finally, report back -- -C gives a parsable format */
- t2 = tsub(t2, t1);
- print_report("wrote", &t2, first_offset, total, total, cnt, Cflag);
+ /* Finally, report back -- -C gives a parsable format */
+ t2 = tsub(t2, t1);
+ print_report("wrote", &t2, first_offset, total, total, cnt, Cflag);
out:
- for (i = 0; i < nr_reqs; i++) {
- qemu_io_free(buf[i]);
- qemu_iovec_destroy(&qiovs[i]);
- }
- qemu_free(buf);
- qemu_free(reqs);
- qemu_free(qiovs);
- return 0;
+ for (i = 0; i < nr_reqs; i++) {
+ qemu_io_free(buf[i]);
+ qemu_iovec_destroy(&qiovs[i]);
+ }
+ qemu_free(buf);
+ qemu_free(reqs);
+ qemu_free(qiovs);
+ return 0;
}
struct aio_ctx {
- QEMUIOVector qiov;
- int64_t offset;
- char *buf;
- int qflag;
- int vflag;
- int Cflag;
- int Pflag;
- int pattern;
- struct timeval t1;
+ QEMUIOVector qiov;
+ int64_t offset;
+ char *buf;
+ int qflag;
+ int vflag;
+ int Cflag;
+ int Pflag;
+ int pattern;
+ struct timeval t1;
};
-static void
-aio_write_done(void *opaque, int ret)
+static void aio_write_done(void *opaque, int ret)
{
- struct aio_ctx *ctx = opaque;
- struct timeval t2;
+ struct aio_ctx *ctx = opaque;
+ struct timeval t2;
- gettimeofday(&t2, NULL);
+ gettimeofday(&t2, NULL);
- if (ret < 0) {
- printf("aio_write failed: %s\n", strerror(-ret));
- goto out;
- }
+ if (ret < 0) {
+ printf("aio_write failed: %s\n", strerror(-ret));
+ goto out;
+ }
- if (ctx->qflag) {
- goto out;
- }
+ if (ctx->qflag) {
+ goto out;
+ }
- /* Finally, report back -- -C gives a parsable format */
- t2 = tsub(t2, ctx->t1);
- print_report("wrote", &t2, ctx->offset, ctx->qiov.size,
- ctx->qiov.size, 1, ctx->Cflag);
+ /* Finally, report back -- -C gives a parsable format */
+ t2 = tsub(t2, ctx->t1);
+ print_report("wrote", &t2, ctx->offset, ctx->qiov.size,
+ ctx->qiov.size, 1, ctx->Cflag);
out:
- qemu_io_free(ctx->buf);
- free(ctx);
+ qemu_io_free(ctx->buf);
+ free(ctx);
}
-static void
-aio_read_done(void *opaque, int ret)
+static void aio_read_done(void *opaque, int ret)
{
- struct aio_ctx *ctx = opaque;
- struct timeval t2;
-
- gettimeofday(&t2, NULL);
-
- if (ret < 0) {
- printf("readv failed: %s\n", strerror(-ret));
- goto out;
- }
-
- if (ctx->Pflag) {
- void *cmp_buf = malloc(ctx->qiov.size);
-
- memset(cmp_buf, ctx->pattern, ctx->qiov.size);
- if (memcmp(ctx->buf, cmp_buf, ctx->qiov.size)) {
- printf("Pattern verification failed at offset %"
- PRId64 ", %zd bytes\n",
- ctx->offset, ctx->qiov.size);
- }
- free(cmp_buf);
- }
-
- if (ctx->qflag) {
- goto out;
- }
-
- if (ctx->vflag) {
- dump_buffer(ctx->buf, ctx->offset, ctx->qiov.size);
- }
-
- /* Finally, report back -- -C gives a parsable format */
- t2 = tsub(t2, ctx->t1);
- print_report("read", &t2, ctx->offset, ctx->qiov.size,
- ctx->qiov.size, 1, ctx->Cflag);
+ struct aio_ctx *ctx = opaque;
+ struct timeval t2;
+
+ gettimeofday(&t2, NULL);
+
+ if (ret < 0) {
+ printf("readv failed: %s\n", strerror(-ret));
+ goto out;
+ }
+
+ if (ctx->Pflag) {
+ void *cmp_buf = malloc(ctx->qiov.size);
+
+ memset(cmp_buf, ctx->pattern, ctx->qiov.size);
+ if (memcmp(ctx->buf, cmp_buf, ctx->qiov.size)) {
+ printf("Pattern verification failed at offset %"
+ PRId64 ", %zd bytes\n", ctx->offset, ctx->qiov.size);
+ }
+ free(cmp_buf);
+ }
+
+ if (ctx->qflag) {
+ goto out;
+ }
+
+ if (ctx->vflag) {
+ dump_buffer(ctx->buf, ctx->offset, ctx->qiov.size);
+ }
+
+ /* Finally, report back -- -C gives a parsable format */
+ t2 = tsub(t2, ctx->t1);
+ print_report("read", &t2, ctx->offset, ctx->qiov.size,
+ ctx->qiov.size, 1, ctx->Cflag);
out:
- qemu_io_free(ctx->buf);
- free(ctx);
+ qemu_io_free(ctx->buf);
+ free(ctx);
}
-static void
-aio_read_help(void)
+static void aio_read_help(void)
{
- printf(
+ printf(
"\n"
" asynchronously reads a range of bytes from the given offset\n"
"\n"
@@ -1107,88 +1124,86 @@ aio_read_help(void)
static int aio_read_f(int argc, char **argv);
static const cmdinfo_t aio_read_cmd = {
- .name = "aio_read",
- .cfunc = aio_read_f,
- .argmin = 2,
- .argmax = -1,
- .args = "[-Cqv] [-P pattern ] off len [len..]",
- .oneline = "asynchronously reads a number of bytes",
- .help = aio_read_help,
+ .name = "aio_read",
+ .cfunc = aio_read_f,
+ .argmin = 2,
+ .argmax = -1,
+ .args = "[-Cqv] [-P pattern ] off len [len..]",
+ .oneline = "asynchronously reads a number of bytes",
+ .help = aio_read_help,
};
-static int
-aio_read_f(int argc, char **argv)
+static int aio_read_f(int argc, char **argv)
{
- int nr_iov, c;
- struct aio_ctx *ctx = calloc(1, sizeof(struct aio_ctx));
- BlockDriverAIOCB *acb;
-
- while ((c = getopt(argc, argv, "CP:qv")) != EOF) {
- switch (c) {
- case 'C':
- ctx->Cflag = 1;
- break;
- case 'P':
- ctx->Pflag = 1;
- ctx->pattern = parse_pattern(optarg);
- if (ctx->pattern < 0) {
- free(ctx);
- return 0;
- }
- break;
- case 'q':
- ctx->qflag = 1;
- break;
- case 'v':
- ctx->vflag = 1;
- break;
- default:
- free(ctx);
- return command_usage(&aio_read_cmd);
- }
- }
-
- if (optind > argc - 2) {
- free(ctx);
- return command_usage(&aio_read_cmd);
- }
-
- ctx->offset = cvtnum(argv[optind]);
- if (ctx->offset < 0) {
- printf("non-numeric length argument -- %s\n", argv[optind]);
- free(ctx);
- return 0;
- }
- optind++;
-
- if (ctx->offset & 0x1ff) {
- printf("offset %" PRId64 " is not sector aligned\n",
- ctx->offset);
- free(ctx);
- return 0;
- }
-
- nr_iov = argc - optind;
- ctx->buf = create_iovec(&ctx->qiov, &argv[optind], nr_iov, 0xab);
-
- gettimeofday(&ctx->t1, NULL);
- acb = bdrv_aio_readv(bs, ctx->offset >> 9, &ctx->qiov,
- ctx->qiov.size >> 9, aio_read_done, ctx);
- if (!acb) {
- free(ctx->buf);
- free(ctx);
- return -EIO;
- }
-
- return 0;
+ int nr_iov, c;
+ struct aio_ctx *ctx = calloc(1, sizeof(struct aio_ctx));
+ BlockDriverAIOCB *acb;
+
+ while ((c = getopt(argc, argv, "CP:qv")) != EOF) {
+ switch (c) {
+ case 'C':
+ ctx->Cflag = 1;
+ break;
+ case 'P':
+ ctx->Pflag = 1;
+ ctx->pattern = parse_pattern(optarg);
+ if (ctx->pattern < 0) {
+ free(ctx);
+ return 0;
+ }
+ break;
+ case 'q':
+ ctx->qflag = 1;
+ break;
+ case 'v':
+ ctx->vflag = 1;
+ break;
+ default:
+ free(ctx);
+ return command_usage(&aio_read_cmd);
+ }
+ }
+
+ if (optind > argc - 2) {
+ free(ctx);
+ return command_usage(&aio_read_cmd);
+ }
+
+ ctx->offset = cvtnum(argv[optind]);
+ if (ctx->offset < 0) {
+ printf("non-numeric length argument -- %s\n", argv[optind]);
+ free(ctx);
+ return 0;
+ }
+ optind++;
+
+ if (ctx->offset & 0x1ff) {
+ printf("offset %" PRId64 " is not sector aligned\n",
+ ctx->offset);
+ free(ctx);
+ return 0;
+ }
+
+ nr_iov = argc - optind;
+ ctx->buf = create_iovec(&ctx->qiov, &argv[optind], nr_iov, 0xab);
+
+ gettimeofday(&ctx->t1, NULL);
+ acb = bdrv_aio_readv(bs, ctx->offset >> 9, &ctx->qiov,
+ ctx->qiov.size >> 9, aio_read_done, ctx);
+ if (!acb) {
+ free(ctx->buf);
+ free(ctx);
+ return -EIO;
+ }
+
+ return 0;
}
-static void
-aio_write_help(void)
+static void aio_write_help(void)
{
- printf(
+ printf(
"\n"
-" asynchronously writes a range of bytes from the given offset source \n"
+" asynchronously writes a range of bytes from the given offset source\n"
" from multiple buffers\n"
"\n"
" Example:\n"
@@ -1207,199 +1222,196 @@ aio_write_help(void)
static int aio_write_f(int argc, char **argv);
static const cmdinfo_t aio_write_cmd = {
- .name = "aio_write",
- .cfunc = aio_write_f,
- .argmin = 2,
- .argmax = -1,
- .args = "[-Cq] [-P pattern ] off len [len..]",
- .oneline = "asynchronously writes a number of bytes",
- .help = aio_write_help,
+ .name = "aio_write",
+ .cfunc = aio_write_f,
+ .argmin = 2,
+ .argmax = -1,
+ .args = "[-Cq] [-P pattern ] off len [len..]",
+ .oneline = "asynchronously writes a number of bytes",
+ .help = aio_write_help,
};
-static int
-aio_write_f(int argc, char **argv)
+static int aio_write_f(int argc, char **argv)
{
- int nr_iov, c;
- int pattern = 0xcd;
- struct aio_ctx *ctx = calloc(1, sizeof(struct aio_ctx));
- BlockDriverAIOCB *acb;
-
- while ((c = getopt(argc, argv, "CqP:")) != EOF) {
- switch (c) {
- case 'C':
- ctx->Cflag = 1;
- break;
- case 'q':
- ctx->qflag = 1;
- break;
- case 'P':
- pattern = parse_pattern(optarg);
- if (pattern < 0)
- return 0;
- break;
- default:
- free(ctx);
- return command_usage(&aio_write_cmd);
- }
- }
-
- if (optind > argc - 2) {
- free(ctx);
- return command_usage(&aio_write_cmd);
- }
-
- ctx->offset = cvtnum(argv[optind]);
- if (ctx->offset < 0) {
- printf("non-numeric length argument -- %s\n", argv[optind]);
- free(ctx);
- return 0;
- }
- optind++;
-
- if (ctx->offset & 0x1ff) {
- printf("offset %" PRId64 " is not sector aligned\n",
- ctx->offset);
- free(ctx);
- return 0;
- }
-
- nr_iov = argc - optind;
- ctx->buf = create_iovec(&ctx->qiov, &argv[optind], nr_iov, pattern);
-
- gettimeofday(&ctx->t1, NULL);
- acb = bdrv_aio_writev(bs, ctx->offset >> 9, &ctx->qiov,
- ctx->qiov.size >> 9, aio_write_done, ctx);
- if (!acb) {
- free(ctx->buf);
- free(ctx);
- return -EIO;
- }
-
- return 0;
+ int nr_iov, c;
+ int pattern = 0xcd;
+ struct aio_ctx *ctx = calloc(1, sizeof(struct aio_ctx));
+ BlockDriverAIOCB *acb;
+
+ while ((c = getopt(argc, argv, "CqP:")) != EOF) {
+ switch (c) {
+ case 'C':
+ ctx->Cflag = 1;
+ break;
+ case 'q':
+ ctx->qflag = 1;
+ break;
+ case 'P':
+ pattern = parse_pattern(optarg);
+ if (pattern < 0) {
+ return 0;
+ }
+ break;
+ default:
+ free(ctx);
+ return command_usage(&aio_write_cmd);
+ }
+ }
+
+ if (optind > argc - 2) {
+ free(ctx);
+ return command_usage(&aio_write_cmd);
+ }
+
+ ctx->offset = cvtnum(argv[optind]);
+ if (ctx->offset < 0) {
+ printf("non-numeric length argument -- %s\n", argv[optind]);
+ free(ctx);
+ return 0;
+ }
+ optind++;
+
+ if (ctx->offset & 0x1ff) {
+ printf("offset %" PRId64 " is not sector aligned\n",
+ ctx->offset);
+ free(ctx);
+ return 0;
+ }
+
+ nr_iov = argc - optind;
+ ctx->buf = create_iovec(&ctx->qiov, &argv[optind], nr_iov, pattern);
+
+ gettimeofday(&ctx->t1, NULL);
+ acb = bdrv_aio_writev(bs, ctx->offset >> 9, &ctx->qiov,
+ ctx->qiov.size >> 9, aio_write_done, ctx);
+ if (!acb) {
+ free(ctx->buf);
+ free(ctx);
+ return -EIO;
+ }
+
+ return 0;
}
-static int
-aio_flush_f(int argc, char **argv)
+static int aio_flush_f(int argc, char **argv)
{
- qemu_aio_flush();
- return 0;
+ qemu_aio_flush();
+ return 0;
}
static const cmdinfo_t aio_flush_cmd = {
- .name = "aio_flush",
- .cfunc = aio_flush_f,
- .oneline = "completes all outstanding aio requests"
+ .name = "aio_flush",
+ .cfunc = aio_flush_f,
+ .oneline = "completes all outstanding aio requests"
};
-static int
-flush_f(int argc, char **argv)
+static int flush_f(int argc, char **argv)
{
- bdrv_flush(bs);
- return 0;
+ bdrv_flush(bs);
+ return 0;
}
static const cmdinfo_t flush_cmd = {
- .name = "flush",
- .altname = "f",
- .cfunc = flush_f,
- .oneline = "flush all in-core file state to disk",
+ .name = "flush",
+ .altname = "f",
+ .cfunc = flush_f,
+ .oneline = "flush all in-core file state to disk",
};
-static int
-truncate_f(int argc, char **argv)
+static int truncate_f(int argc, char **argv)
{
- int64_t offset;
- int ret;
-
- offset = cvtnum(argv[1]);
- if (offset < 0) {
- printf("non-numeric truncate argument -- %s\n", argv[1]);
- return 0;
- }
-
- ret = bdrv_truncate(bs, offset);
- if (ret < 0) {
- printf("truncate: %s\n", strerror(-ret));
- return 0;
- }
-
- return 0;
+ int64_t offset;
+ int ret;
+
+ offset = cvtnum(argv[1]);
+ if (offset < 0) {
+ printf("non-numeric truncate argument -- %s\n", argv[1]);
+ return 0;
+ }
+
+ ret = bdrv_truncate(bs, offset);
+ if (ret < 0) {
+ printf("truncate: %s\n", strerror(-ret));
+ return 0;
+ }
+
+ return 0;
}
static const cmdinfo_t truncate_cmd = {
- .name = "truncate",
- .altname = "t",
- .cfunc = truncate_f,
- .argmin = 1,
- .argmax = 1,
- .args = "off",
- .oneline = "truncates the current file at the given offset",
+ .name = "truncate",
+ .altname = "t",
+ .cfunc = truncate_f,
+ .argmin = 1,
+ .argmax = 1,
+ .args = "off",
+ .oneline = "truncates the current file at the given offset",
};
-static int
-length_f(int argc, char **argv)
+static int length_f(int argc, char **argv)
{
- int64_t size;
- char s1[64];
-
- size = bdrv_getlength(bs);
- if (size < 0) {
- printf("getlength: %s\n", strerror(-size));
- return 0;
- }
-
- cvtstr(size, s1, sizeof(s1));
- printf("%s\n", s1);
- return 0;
+ int64_t size;
+ char s1[64];
+
+ size = bdrv_getlength(bs);
+ if (size < 0) {
+ printf("getlength: %s\n", strerror(-size));
+ return 0;
+ }
+
+ cvtstr(size, s1, sizeof(s1));
+ printf("%s\n", s1);
+ return 0;
}
static const cmdinfo_t length_cmd = {
- .name = "length",
- .altname = "l",
- .cfunc = length_f,
- .oneline = "gets the length of the current file",
+ .name = "length",
+ .altname = "l",
+ .cfunc = length_f,
+ .oneline = "gets the length of the current file",
};
-static int
-info_f(int argc, char **argv)
+static int info_f(int argc, char **argv)
{
- BlockDriverInfo bdi;
- char s1[64], s2[64];
- int ret;
+ BlockDriverInfo bdi;
+ char s1[64], s2[64];
+ int ret;
- if (bs->drv && bs->drv->format_name)
- printf("format name: %s\n", bs->drv->format_name);
- if (bs->drv && bs->drv->protocol_name)
- printf("format name: %s\n", bs->drv->protocol_name);
+ if (bs->drv && bs->drv->format_name) {
+ printf("format name: %s\n", bs->drv->format_name);
+ }
+ if (bs->drv && bs->drv->protocol_name) {
+ printf("format name: %s\n", bs->drv->protocol_name);
+ }
- ret = bdrv_get_info(bs, &bdi);
- if (ret)
- return 0;
+ ret = bdrv_get_info(bs, &bdi);
+ if (ret) {
+ return 0;
+ }
- cvtstr(bdi.cluster_size, s1, sizeof(s1));
- cvtstr(bdi.vm_state_offset, s2, sizeof(s2));
+ cvtstr(bdi.cluster_size, s1, sizeof(s1));
+ cvtstr(bdi.vm_state_offset, s2, sizeof(s2));
- printf("cluster size: %s\n", s1);
- printf("vm state offset: %s\n", s2);
+ printf("cluster size: %s\n", s1);
+ printf("vm state offset: %s\n", s2);
- return 0;
+ return 0;
}
static const cmdinfo_t info_cmd = {
- .name = "info",
- .altname = "i",
- .cfunc = info_f,
- .oneline = "prints information about the current file",
+ .name = "info",
+ .altname = "i",
+ .cfunc = info_f,
+ .oneline = "prints information about the current file",
};
-static void
-discard_help(void)
+static void discard_help(void)
{
- printf(
+ printf(
"\n"
" discards a range of bytes from the given offset\n"
"\n"
@@ -1415,148 +1427,147 @@ discard_help(void)
static int discard_f(int argc, char **argv);
static const cmdinfo_t discard_cmd = {
- .name = "discard",
- .altname = "d",
- .cfunc = discard_f,
- .argmin = 2,
- .argmax = -1,
- .args = "[-Cq] off len",
- .oneline = "discards a number of bytes at a specified offset",
- .help = discard_help,
+ .name = "discard",
+ .altname = "d",
+ .cfunc = discard_f,
+ .argmin = 2,
+ .argmax = -1,
+ .args = "[-Cq] off len",
+ .oneline = "discards a number of bytes at a specified offset",
+ .help = discard_help,
};
-static int
-discard_f(int argc, char **argv)
+static int discard_f(int argc, char **argv)
{
- struct timeval t1, t2;
- int Cflag = 0, qflag = 0;
- int c, ret;
- int64_t offset;
- int count;
-
- while ((c = getopt(argc, argv, "Cq")) != EOF) {
- switch (c) {
- case 'C':
- Cflag = 1;
- break;
- case 'q':
- qflag = 1;
- break;
- default:
- return command_usage(&discard_cmd);
- }
- }
-
- if (optind != argc - 2) {
- return command_usage(&discard_cmd);
- }
-
- offset = cvtnum(argv[optind]);
- if (offset < 0) {
- printf("non-numeric length argument -- %s\n", argv[optind]);
- return 0;
- }
-
- optind++;
- count = cvtnum(argv[optind]);
- if (count < 0) {
- printf("non-numeric length argument -- %s\n", argv[optind]);
- return 0;
- }
-
- gettimeofday(&t1, NULL);
- ret = bdrv_discard(bs, offset >> BDRV_SECTOR_BITS, count >> BDRV_SECTOR_BITS);
- gettimeofday(&t2, NULL);
-
- if (ret < 0) {
- printf("discard failed: %s\n", strerror(-ret));
- goto out;
- }
-
- /* Finally, report back -- -C gives a parsable format */
- if (!qflag) {
- t2 = tsub(t2, t1);
- print_report("discard", &t2, offset, count, count, 1, Cflag);
- }
+ struct timeval t1, t2;
+ int Cflag = 0, qflag = 0;
+ int c, ret;
+ int64_t offset;
+ int count;
+
+ while ((c = getopt(argc, argv, "Cq")) != EOF) {
+ switch (c) {
+ case 'C':
+ Cflag = 1;
+ break;
+ case 'q':
+ qflag = 1;
+ break;
+ default:
+ return command_usage(&discard_cmd);
+ }
+ }
+
+ if (optind != argc - 2) {
+ return command_usage(&discard_cmd);
+ }
+
+ offset = cvtnum(argv[optind]);
+ if (offset < 0) {
+ printf("non-numeric length argument -- %s\n", argv[optind]);
+ return 0;
+ }
+
+ optind++;
+ count = cvtnum(argv[optind]);
+ if (count < 0) {
+ printf("non-numeric length argument -- %s\n", argv[optind]);
+ return 0;
+ }
+
+ gettimeofday(&t1, NULL);
+ ret = bdrv_discard(bs, offset >> BDRV_SECTOR_BITS,
+ count >> BDRV_SECTOR_BITS);
+ gettimeofday(&t2, NULL);
+
+ if (ret < 0) {
+ printf("discard failed: %s\n", strerror(-ret));
+ goto out;
+ }
+
+ /* Finally, report back -- -C gives a parsable format */
+ if (!qflag) {
+ t2 = tsub(t2, t1);
+ print_report("discard", &t2, offset, count, count, 1, Cflag);
+ }
out:
- return 0;
+ return 0;
}
-static int
-alloc_f(int argc, char **argv)
+static int alloc_f(int argc, char **argv)
{
- int64_t offset;
- int nb_sectors, remaining;
- char s1[64];
- int num, sum_alloc;
- int ret;
-
- offset = cvtnum(argv[1]);
- if (offset & 0x1ff) {
- printf("offset %" PRId64 " is not sector aligned\n",
- offset);
- return 0;
- }
-
- if (argc == 3)
- nb_sectors = cvtnum(argv[2]);
- else
- nb_sectors = 1;
-
- remaining = nb_sectors;
- sum_alloc = 0;
- while (remaining) {
- ret = bdrv_is_allocated(bs, offset >> 9, nb_sectors, &num);
- remaining -= num;
- if (ret) {
- sum_alloc += num;
- }
- }
-
- cvtstr(offset, s1, sizeof(s1));
-
- printf("%d/%d sectors allocated at offset %s\n",
- sum_alloc, nb_sectors, s1);
- return 0;
+ int64_t offset;
+ int nb_sectors, remaining;
+ char s1[64];
+ int num, sum_alloc;
+ int ret;
+
+ offset = cvtnum(argv[1]);
+ if (offset & 0x1ff) {
+ printf("offset %" PRId64 " is not sector aligned\n",
+ offset);
+ return 0;
+ }
+
+ if (argc == 3) {
+ nb_sectors = cvtnum(argv[2]);
+ } else {
+ nb_sectors = 1;
+ }
+
+ remaining = nb_sectors;
+ sum_alloc = 0;
+ while (remaining) {
+ ret = bdrv_is_allocated(bs, offset >> 9, nb_sectors, &num);
+ remaining -= num;
+ if (ret) {
+ sum_alloc += num;
+ }
+ }
+
+ cvtstr(offset, s1, sizeof(s1));
+
+ printf("%d/%d sectors allocated at offset %s\n",
+ sum_alloc, nb_sectors, s1);
+ return 0;
}
static const cmdinfo_t alloc_cmd = {
- .name = "alloc",
- .altname = "a",
- .argmin = 1,
- .argmax = 2,
- .cfunc = alloc_f,
- .args = "off [sectors]",
- .oneline = "checks if a sector is present in the file",
+ .name = "alloc",
+ .altname = "a",
+ .argmin = 1,
+ .argmax = 2,
+ .cfunc = alloc_f,
+ .args = "off [sectors]",
+ .oneline = "checks if a sector is present in the file",
};
-static int
-map_f(int argc, char **argv)
+static int map_f(int argc, char **argv)
{
- int64_t offset;
- int64_t nb_sectors;
- char s1[64];
- int num, num_checked;
- int ret;
- const char *retstr;
-
- offset = 0;
- nb_sectors = bs->total_sectors;
-
- do {
- num_checked = MIN(nb_sectors, INT_MAX);
- ret = bdrv_is_allocated(bs, offset, num_checked, &num);
- retstr = ret ? " allocated" : "not allocated";
- cvtstr(offset << 9ULL, s1, sizeof(s1));
- printf("[% 24" PRId64 "] % 8d/% 8d sectors %s at offset %s (%d)\n",
- offset << 9ULL, num, num_checked, retstr, s1, ret);
-
- offset += num;
- nb_sectors -= num;
- } while(offset < bs->total_sectors);
-
- return 0;
+ int64_t offset;
+ int64_t nb_sectors;
+ char s1[64];
+ int num, num_checked;
+ int ret;
+ const char *retstr;
+
+ offset = 0;
+ nb_sectors = bs->total_sectors;
+
+ do {
+ num_checked = MIN(nb_sectors, INT_MAX);
+ ret = bdrv_is_allocated(bs, offset, num_checked, &num);
+ retstr = ret ? " allocated" : "not allocated";
+ cvtstr(offset << 9ULL, s1, sizeof(s1));
+ printf("[% 24" PRId64 "] % 8d/% 8d sectors %s at offset %s (%d)\n",
+ offset << 9ULL, num, num_checked, retstr, s1, ret);
+
+ offset += num;
+ nb_sectors -= num;
+ } while (offset < bs->total_sectors);
+
+ return 0;
}
static const cmdinfo_t map_cmd = {
@@ -1569,50 +1580,48 @@ static const cmdinfo_t map_cmd = {
};
-static int
-close_f(int argc, char **argv)
+static int close_f(int argc, char **argv)
{
- bdrv_close(bs);
- bs = NULL;
- return 0;
+ bdrv_close(bs);
+ bs = NULL;
+ return 0;
}
static const cmdinfo_t close_cmd = {
- .name = "close",
- .altname = "c",
- .cfunc = close_f,
- .oneline = "close the current open file",
+ .name = "close",
+ .altname = "c",
+ .cfunc = close_f,
+ .oneline = "close the current open file",
};
static int openfile(char *name, int flags, int growable)
{
- if (bs) {
- fprintf(stderr, "file open already, try 'help close'\n");
- return 1;
- }
-
- if (growable) {
- if (bdrv_file_open(&bs, name, flags)) {
- fprintf(stderr, "%s: can't open device %s\n", progname, name);
- return 1;
- }
- } else {
- bs = bdrv_new("hda");
-
- if (bdrv_open(bs, name, flags, NULL) < 0) {
- fprintf(stderr, "%s: can't open device %s\n", progname, name);
- bs = NULL;
- return 1;
- }
- }
-
- return 0;
+ if (bs) {
+ fprintf(stderr, "file open already, try 'help close'\n");
+ return 1;
+ }
+
+ if (growable) {
+ if (bdrv_file_open(&bs, name, flags)) {
+ fprintf(stderr, "%s: can't open device %s\n", progname, name);
+ return 1;
+ }
+ } else {
+ bs = bdrv_new("hda");
+
+ if (bdrv_open(bs, name, flags, NULL) < 0) {
+ fprintf(stderr, "%s: can't open device %s\n", progname, name);
+ bs = NULL;
+ return 1;
+ }
+ }
+
+ return 0;
}
-static void
-open_help(void)
+static void open_help(void)
{
- printf(
+ printf(
"\n"
" opens a new file in the requested mode\n"
"\n"
@@ -1630,80 +1639,78 @@ open_help(void)
static int open_f(int argc, char **argv);
static const cmdinfo_t open_cmd = {
- .name = "open",
- .altname = "o",
- .cfunc = open_f,
- .argmin = 1,
- .argmax = -1,
- .flags = CMD_NOFILE_OK,
- .args = "[-Crsn] [path]",
- .oneline = "open the file specified by path",
- .help = open_help,
+ .name = "open",
+ .altname = "o",
+ .cfunc = open_f,
+ .argmin = 1,
+ .argmax = -1,
+ .flags = CMD_NOFILE_OK,
+ .args = "[-Crsn] [path]",
+ .oneline = "open the file specified by path",
+ .help = open_help,
};
-static int
-open_f(int argc, char **argv)
+static int open_f(int argc, char **argv)
{
- int flags = 0;
- int readonly = 0;
- int growable = 0;
- int c;
-
- while ((c = getopt(argc, argv, "snrg")) != EOF) {
- switch (c) {
- case 's':
- flags |= BDRV_O_SNAPSHOT;
- break;
- case 'n':
- flags |= BDRV_O_NOCACHE | BDRV_O_CACHE_WB;
- break;
- case 'r':
- readonly = 1;
- break;
- case 'g':
- growable = 1;
- break;
- default:
- return command_usage(&open_cmd);
- }
- }
-
- if (!readonly) {
- flags |= BDRV_O_RDWR;
+ int flags = 0;
+ int readonly = 0;
+ int growable = 0;
+ int c;
+
+ while ((c = getopt(argc, argv, "snrg")) != EOF) {
+ switch (c) {
+ case 's':
+ flags |= BDRV_O_SNAPSHOT;
+ break;
+ case 'n':
+ flags |= BDRV_O_NOCACHE | BDRV_O_CACHE_WB;
+ break;
+ case 'r':
+ readonly = 1;
+ break;
+ case 'g':
+ growable = 1;
+ break;
+ default:
+ return command_usage(&open_cmd);
}
+ }
+
+ if (!readonly) {
+ flags |= BDRV_O_RDWR;
+ }
- if (optind != argc - 1)
- return command_usage(&open_cmd);
+ if (optind != argc - 1) {
+ return command_usage(&open_cmd);
+ }
- return openfile(argv[optind], flags, growable);
+ return openfile(argv[optind], flags, growable);
}
-static int
-init_args_command(
- int index)
+static int init_args_command(int index)
{
- /* only one device allowed so far */
- if (index >= 1)
- return 0;
- return ++index;
+ /* only one device allowed so far */
+ if (index >= 1) {
+ return 0;
+ }
+ return ++index;
}
-static int
-init_check_command(
- const cmdinfo_t *ct)
+static int init_check_command(const cmdinfo_t *ct)
{
- if (ct->flags & CMD_FLAG_GLOBAL)
- return 1;
- if (!(ct->flags & CMD_NOFILE_OK) && !bs) {
- fprintf(stderr, "no file open, try 'help open'\n");
- return 0;
- }
- return 1;
+ if (ct->flags & CMD_FLAG_GLOBAL) {
+ return 1;
+ }
+ if (!(ct->flags & CMD_NOFILE_OK) && !bs) {
+ fprintf(stderr, "no file open, try 'help open'\n");
+ return 0;
+ }
+ return 1;
}
static void usage(const char *name)
{
- printf(
+ printf(
"Usage: %s [-h] [-V] [-rsnm] [-c cmd] ... [file]\n"
"QEMU Disk exerciser\n"
"\n"
@@ -1717,115 +1724,117 @@ static void usage(const char *name)
" -h, --help display this help and exit\n"
" -V, --version output version information and exit\n"
"\n",
- name);
+ name);
}
int main(int argc, char **argv)
{
- int readonly = 0;
- int growable = 0;
- const char *sopt = "hVc:rsnmgk";
- const struct option lopt[] = {
- { "help", 0, NULL, 'h' },
- { "version", 0, NULL, 'V' },
- { "offset", 1, NULL, 'o' },
- { "cmd", 1, NULL, 'c' },
- { "read-only", 0, NULL, 'r' },
- { "snapshot", 0, NULL, 's' },
- { "nocache", 0, NULL, 'n' },
- { "misalign", 0, NULL, 'm' },
- { "growable", 0, NULL, 'g' },
- { "native-aio", 0, NULL, 'k' },
- { NULL, 0, NULL, 0 }
- };
- int c;
- int opt_index = 0;
- int flags = 0;
-
- progname = basename(argv[0]);
-
- while ((c = getopt_long(argc, argv, sopt, lopt, &opt_index)) != -1) {
- switch (c) {
- case 's':
- flags |= BDRV_O_SNAPSHOT;
- break;
- case 'n':
- flags |= BDRV_O_NOCACHE | BDRV_O_CACHE_WB;
- break;
- case 'c':
- add_user_command(optarg);
- break;
- case 'r':
- readonly = 1;
- break;
- case 'm':
- misalign = 1;
- break;
- case 'g':
- growable = 1;
- break;
- case 'k':
- flags |= BDRV_O_NATIVE_AIO;
- break;
- case 'V':
- printf("%s version %s\n", progname, VERSION);
- exit(0);
- case 'h':
- usage(progname);
- exit(0);
- default:
- usage(progname);
- exit(1);
- }
- }
-
- if ((argc - optind) > 1) {
- usage(progname);
- exit(1);
- }
-
- bdrv_init();
-
- /* initialize commands */
- quit_init();
- help_init();
- add_command(&open_cmd);
- add_command(&close_cmd);
- add_command(&read_cmd);
- add_command(&readv_cmd);
- add_command(&write_cmd);
- add_command(&writev_cmd);
- add_command(&multiwrite_cmd);
- add_command(&aio_read_cmd);
- add_command(&aio_write_cmd);
- add_command(&aio_flush_cmd);
- add_command(&flush_cmd);
- add_command(&truncate_cmd);
- add_command(&length_cmd);
- add_command(&info_cmd);
- add_command(&discard_cmd);
- add_command(&alloc_cmd);
- add_command(&map_cmd);
-
- add_args_command(init_args_command);
- add_check_command(init_check_command);
-
- /* open the device */
- if (!readonly) {
- flags |= BDRV_O_RDWR;
+ int readonly = 0;
+ int growable = 0;
+ const char *sopt = "hVc:rsnmgk";
+ const struct option lopt[] = {
+ { "help", 0, NULL, 'h' },
+ { "version", 0, NULL, 'V' },
+ { "offset", 1, NULL, 'o' },
+ { "cmd", 1, NULL, 'c' },
+ { "read-only", 0, NULL, 'r' },
+ { "snapshot", 0, NULL, 's' },
+ { "nocache", 0, NULL, 'n' },
+ { "misalign", 0, NULL, 'm' },
+ { "growable", 0, NULL, 'g' },
+ { "native-aio", 0, NULL, 'k' },
+ { NULL, 0, NULL, 0 }
+ };
+ int c;
+ int opt_index = 0;
+ int flags = 0;
+
+ progname = basename(argv[0]);
+
+ while ((c = getopt_long(argc, argv, sopt, lopt, &opt_index)) != -1) {
+ switch (c) {
+ case 's':
+ flags |= BDRV_O_SNAPSHOT;
+ break;
+ case 'n':
+ flags |= BDRV_O_NOCACHE | BDRV_O_CACHE_WB;
+ break;
+ case 'c':
+ add_user_command(optarg);
+ break;
+ case 'r':
+ readonly = 1;
+ break;
+ case 'm':
+ misalign = 1;
+ break;
+ case 'g':
+ growable = 1;
+ break;
+ case 'k':
+ flags |= BDRV_O_NATIVE_AIO;
+ break;
+ case 'V':
+ printf("%s version %s\n", progname, VERSION);
+ exit(0);
+ case 'h':
+ usage(progname);
+ exit(0);
+ default:
+ usage(progname);
+ exit(1);
}
+ }
+
+ if ((argc - optind) > 1) {
+ usage(progname);
+ exit(1);
+ }
- if ((argc - optind) == 1)
- openfile(argv[optind], flags, growable);
- command_loop();
+ bdrv_init();
+
+ /* initialize commands */
+ quit_init();
+ help_init();
+ add_command(&open_cmd);
+ add_command(&close_cmd);
+ add_command(&read_cmd);
+ add_command(&readv_cmd);
+ add_command(&write_cmd);
+ add_command(&writev_cmd);
+ add_command(&multiwrite_cmd);
+ add_command(&aio_read_cmd);
+ add_command(&aio_write_cmd);
+ add_command(&aio_flush_cmd);
+ add_command(&flush_cmd);
+ add_command(&truncate_cmd);
+ add_command(&length_cmd);
+ add_command(&info_cmd);
+ add_command(&discard_cmd);
+ add_command(&alloc_cmd);
+ add_command(&map_cmd);
+
+ add_args_command(init_args_command);
+ add_check_command(init_check_command);
+
+ /* open the device */
+ if (!readonly) {
+ flags |= BDRV_O_RDWR;
+ }
+
+ if ((argc - optind) == 1) {
+ openfile(argv[optind], flags, growable);
+ }
+ command_loop();
- /*
- * Make sure all outstanding requests get flushed the program exits.
- */
- qemu_aio_flush();
+ /*
+ * Make sure all outstanding requests get flushed the program exits.
+ */
+ qemu_aio_flush();
- if (bs)
- bdrv_close(bs);
- return 0;
+ if (bs) {
+ bdrv_close(bs);
+ }
+ return 0;
}
diff --git a/qemu-options.hx b/qemu-options.hx
index e6d7adc..d86815d 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -27,14 +27,29 @@ STEXI
Display version information and exit
ETEXI
-DEF("M", HAS_ARG, QEMU_OPTION_M,
- "-M machine select emulated machine (-M ? for list)\n", QEMU_ARCH_ALL)
+DEF("machine", HAS_ARG, QEMU_OPTION_machine, \
+ "-machine [type=]name[,prop[=value][,...]]\n"
+ " selects emulated machine (-machine ? for list)\n"
+ " property accel=accel1[:accel2[:...]] selects accelerator\n"
+ " supported accelerators are kvm, xen, tcg (default: tcg)\n",
+ QEMU_ARCH_ALL)
STEXI
-@item -M @var{machine}
-@findex -M
-Select the emulated @var{machine} (@code{-M ?} for list)
+@item -machine [type=]@var{name}[,prop=@var{value}[,...]]
+@findex -machine
+Select the emulated machine by @var{name}. Use @code{-machine ?} to list
+available machines. Supported machine properties are:
+@table @option
+@item accel=@var{accels1}[:@var{accels2}[:...]]
+This is used to enable an accelerator. Depending on the target architecture,
+kvm, xen, or tcg can be available. By default, tcg is used. If there is more
+than one accelerator specified, the next one is used if the previous one fails
+to initialize.
+@end table
ETEXI
+HXCOMM Deprecated by -machine
+DEF("M", HAS_ARG, QEMU_OPTION_M, "", QEMU_ARCH_ALL)
+
DEF("cpu", HAS_ARG, QEMU_OPTION_cpu,
"-cpu cpu select CPU (-cpu ? for list)\n", QEMU_ARCH_ALL)
STEXI
@@ -160,6 +175,14 @@ an untrusted format header.
This option specifies the serial number to assign to the device.
@item addr=@var{addr}
Specify the controller's PCI address (if=virtio only).
+@item werror=@var{action},rerror=@var{action}
+Specify which @var{action} to take on write and read errors. Valid actions are:
+"ignore" (ignore the error and try to continue), "stop" (pause QEMU),
+"report" (report the error to the guest), "enospc" (pause QEMU only if the
+host disk is full; report the error to the guest otherwise).
+The default setting is @option{werror=enospc} and @option{rerror=report}.
+@item readonly
+Open drive @option{file} as read-only. Guest write attempts will fail.
@end table
By default, writethrough caching is used for all block device. This means that
@@ -280,10 +303,13 @@ ETEXI
DEF("boot", HAS_ARG, QEMU_OPTION_boot,
"-boot [order=drives][,once=drives][,menu=on|off]\n"
- " 'drives': floppy (a), hard disk (c), CD-ROM (d), network (n)\n",
+ " [,splash=sp_name][,splash-time=sp_time]\n"
+ " 'drives': floppy (a), hard disk (c), CD-ROM (d), network (n)\n"
+ " 'sp_name': the file's name that would be passed to bios as logo picture, if menu=on\n"
+ " 'sp_time': the period that splash picture last if menu=on, unit is ms\n",
QEMU_ARCH_ALL)
STEXI
-@item -boot [order=@var{drives}][,once=@var{drives}][,menu=on|off]
+@item -boot [order=@var{drives}][,once=@var{drives}][,menu=on|off][,splash=@var{sp_name}][,splash-time=@var{sp_time}]
@findex -boot
Specify boot order @var{drives} as a string of drive letters. Valid
drive letters depend on the target achitecture. The x86 PC uses: a, b
@@ -295,11 +321,20 @@ particular boot order only on the first startup, specify it via
Interactive boot menus/prompts can be enabled via @option{menu=on} as far
as firmware/BIOS supports them. The default is non-interactive boot.
+A splash picture could be passed to bios, enabling user to show it as logo,
+when option splash=@var{sp_name} is given and menu=on, If firmware/BIOS
+supports them. Currently Seabios for X86 system support it.
+limitation: The splash file could be a jpeg file or a BMP file in 24 BPP
+format(true color). The resolution should be supported by the SVGA mode, so
+the recommended is 320x240, 640x480, 800x640.
+
@example
# try to boot from network first, then from hard disk
qemu -boot order=nc
# boot from CD-ROM first, switch back to default order after reboot
qemu -boot once=d
+# boot with a splash picture for 5 seconds.
+qemu -boot menu=on,splash=/root/boot.bmp,splash-time=5000
@end example
Note: The legacy format '-boot @var{drives}' is still supported but its
@@ -1039,12 +1074,17 @@ Enable virtio balloon device (default), optionally with PCI address
ETEXI
DEF("acpitable", HAS_ARG, QEMU_OPTION_acpitable,
- "-acpitable [sig=str][,rev=n][,oem_id=str][,oem_table_id=str][,oem_rev=n][,asl_compiler_id=str][,asl_compiler_rev=n][,data=file1[:file2]...]\n"
+ "-acpitable [sig=str][,rev=n][,oem_id=str][,oem_table_id=str][,oem_rev=n][,asl_compiler_id=str][,asl_compiler_rev=n][,{data|file}=file1[:file2]...]\n"
" ACPI table description\n", QEMU_ARCH_I386)
STEXI
@item -acpitable [sig=@var{str}][,rev=@var{n}][,oem_id=@var{str}][,oem_table_id=@var{str}][,oem_rev=@var{n}] [,asl_compiler_id=@var{str}][,asl_compiler_rev=@var{n}][,data=@var{file1}[:@var{file2}]...]
@findex -acpitable
Add ACPI table with specified header fields and context from specified files.
+For file=, take whole ACPI table from the specified files, including all
+ACPI headers (possible overridden by other options).
+For data=, only data
+portion of the table is used, all header information is specified in the
+command line.
ETEXI
DEF("smbios", HAS_ARG, QEMU_OPTION_smbios,
@@ -1092,7 +1132,7 @@ DEF("net", HAS_ARG, QEMU_OPTION_net,
"-net nic[,vlan=n][,macaddr=mac][,model=type][,name=str][,addr=str][,vectors=v]\n"
" create a new Network Interface Card and connect it to VLAN 'n'\n"
#ifdef CONFIG_SLIRP
- "-net user[,vlan=n][,name=str][,net=addr[/mask]][,host=addr][,restrict=y|n]\n"
+ "-net user[,vlan=n][,name=str][,net=addr[/mask]][,host=addr][,restrict=on|off]\n"
" [,hostname=host][,dhcpstart=addr][,dns=addr][,tftp=dir][,bootfile=f]\n"
" [,hostfwd=rule][,guestfwd=rule]"
#ifndef _WIN32
@@ -1185,7 +1225,7 @@ either in the form a.b.c.d or as number of valid top-most bits. Default is
Specify the guest-visible address of the host. Default is the 2nd IP in the
guest network, i.e. x.x.x.2.
-@item restrict=y|yes|n|no
+@item restrict=on|off
If this option is enabled, the guest will be isolated, i.e. it will not be
able to contact the host and no guest IP packets will be routed over the host
to the outside. This option does not affect any explicitly set forwarding rules.
@@ -2066,16 +2106,6 @@ Enable KVM full virtualization support. This option is only available
if KVM support is enabled when compiling.
ETEXI
-DEF("machine", HAS_ARG, QEMU_OPTION_machine, \
- "-machine accel=accel1[:accel2] use an accelerator (kvm,xen,tcg), default is tcg\n", QEMU_ARCH_ALL)
-STEXI
-@item -machine accel=@var{accels}
-@findex -machine
-This is use to enable an accelerator, in kvm,xen,tcg.
-By default, it use only tcg. If there a more than one accelerator
-specified, the next one is used if the first don't work.
-ETEXI
-
DEF("xen-domid", HAS_ARG, QEMU_OPTION_xen_domid,
"-xen-domid id specify xen guest domain id\n", QEMU_ARCH_ALL)
DEF("xen-create", 0, QEMU_OPTION_xen_create,
diff --git a/qemu-timer.c b/qemu-timer.c
index 72066c7..30e8f12 100644
--- a/qemu-timer.c
+++ b/qemu-timer.c
@@ -150,6 +150,9 @@ struct QEMUClock {
int enabled;
QEMUTimer *warp_timer;
+
+ NotifierList reset_notifiers;
+ int64_t last;
};
struct QEMUTimer {
@@ -218,6 +221,7 @@ static void win32_rearm_timer(struct qemu_alarm_timer *t);
static int unix_start_timer(struct qemu_alarm_timer *t);
static void unix_stop_timer(struct qemu_alarm_timer *t);
+static void unix_rearm_timer(struct qemu_alarm_timer *t);
#ifdef __linux__
@@ -290,7 +294,7 @@ static struct qemu_alarm_timer alarm_timers[] = {
{"dynticks", dynticks_start_timer,
dynticks_stop_timer, dynticks_rearm_timer},
#endif
- {"unix", unix_start_timer, unix_stop_timer, NULL},
+ {"unix", unix_start_timer, unix_stop_timer, unix_rearm_timer},
#else
{"mmtimer", mm_start_timer, mm_stop_timer, NULL},
{"mmtimer2", mm_start_timer, mm_stop_timer, mm_rearm_timer},
@@ -375,9 +379,15 @@ static QEMUTimer *active_timers[QEMU_NUM_CLOCKS];
static QEMUClock *qemu_new_clock(int type)
{
QEMUClock *clock;
+
clock = qemu_mallocz(sizeof(QEMUClock));
clock->type = type;
clock->enabled = 1;
+ notifier_list_init(&clock->reset_notifiers);
+ /* required to detect & report backward jumps */
+ if (type == QEMU_CLOCK_HOST) {
+ clock->last = get_clock_realtime();
+ }
return clock;
}
@@ -592,6 +602,8 @@ static void qemu_run_timers(QEMUClock *clock)
int64_t qemu_get_clock_ns(QEMUClock *clock)
{
+ int64_t now, last;
+
switch(clock->type) {
case QEMU_CLOCK_REALTIME:
return get_clock();
@@ -603,10 +615,26 @@ int64_t qemu_get_clock_ns(QEMUClock *clock)
return cpu_get_clock();
}
case QEMU_CLOCK_HOST:
- return get_clock_realtime();
+ now = get_clock_realtime();
+ last = clock->last;
+ clock->last = now;
+ if (now < last) {
+ notifier_list_notify(&clock->reset_notifiers, &now);
+ }
+ return now;
}
}
+void qemu_register_clock_reset_notifier(QEMUClock *clock, Notifier *notifier)
+{
+ notifier_list_add(&clock->reset_notifiers, notifier);
+}
+
+void qemu_unregister_clock_reset_notifier(QEMUClock *clock, Notifier *notifier)
+{
+ notifier_list_remove(&clock->reset_notifiers, notifier);
+}
+
void init_clocks(void)
{
rt_clock = qemu_new_clock(QEMU_CLOCK_REALTIME);
@@ -803,6 +831,8 @@ static int64_t qemu_next_alarm_deadline(void)
#if defined(__linux__)
+#include "compatfd.h"
+
static int dynticks_start_timer(struct qemu_alarm_timer *t)
{
struct sigevent ev;
@@ -822,6 +852,12 @@ static int dynticks_start_timer(struct qemu_alarm_timer *t)
memset(&ev, 0, sizeof(ev));
ev.sigev_value.sival_int = 0;
ev.sigev_notify = SIGEV_SIGNAL;
+#ifdef SIGEV_THREAD_ID
+ if (qemu_signalfd_available()) {
+ ev.sigev_notify = SIGEV_THREAD_ID;
+ ev._sigev_un._tid = qemu_get_thread_id();
+ }
+#endif /* SIGEV_THREAD_ID */
ev.sigev_signo = SIGALRM;
if (timer_create(CLOCK_REALTIME, &ev, &host_timer)) {
@@ -890,8 +926,6 @@ static void dynticks_rearm_timer(struct qemu_alarm_timer *t)
static int unix_start_timer(struct qemu_alarm_timer *t)
{
struct sigaction act;
- struct itimerval itv;
- int err;
/* timer signal */
sigfillset(&act.sa_mask);
@@ -899,18 +933,35 @@ static int unix_start_timer(struct qemu_alarm_timer *t)
act.sa_handler = host_alarm_handler;
sigaction(SIGALRM, &act, NULL);
+ return 0;
+}
- itv.it_interval.tv_sec = 0;
- /* for i386 kernel 2.6 to get 1 ms */
- itv.it_interval.tv_usec = 999;
- itv.it_value.tv_sec = 0;
- itv.it_value.tv_usec = 10 * 1000;
+static void unix_rearm_timer(struct qemu_alarm_timer *t)
+{
+ struct itimerval itv;
+ int64_t nearest_delta_ns = INT64_MAX;
+ int err;
- err = setitimer(ITIMER_REAL, &itv, NULL);
- if (err)
- return -1;
+ assert(alarm_has_dynticks(t));
+ if (!active_timers[QEMU_CLOCK_REALTIME] &&
+ !active_timers[QEMU_CLOCK_VIRTUAL] &&
+ !active_timers[QEMU_CLOCK_HOST])
+ return;
- return 0;
+ nearest_delta_ns = qemu_next_alarm_deadline();
+ if (nearest_delta_ns < MIN_TIMER_REARM_NS)
+ nearest_delta_ns = MIN_TIMER_REARM_NS;
+
+ itv.it_interval.tv_sec = 0;
+ itv.it_interval.tv_usec = 0; /* 0 for one-shot timer */
+ itv.it_value.tv_sec = nearest_delta_ns / 1000000000;
+ itv.it_value.tv_usec = (nearest_delta_ns % 1000000000) / 1000;
+ err = setitimer(ITIMER_REAL, &itv, NULL);
+ if (err) {
+ perror("setitimer");
+ fprintf(stderr, "Internal timer error: aborting\n");
+ exit(1);
+ }
}
static void unix_stop_timer(struct qemu_alarm_timer *t)
diff --git a/qemu-timer.h b/qemu-timer.h
index 06cbe20..0a43469 100644
--- a/qemu-timer.h
+++ b/qemu-timer.h
@@ -2,6 +2,7 @@
#define QEMU_TIMER_H
#include "qemu-common.h"
+#include "notify.h"
#include <time.h>
#include <sys/time.h>
@@ -40,6 +41,10 @@ int64_t qemu_get_clock_ns(QEMUClock *clock);
void qemu_clock_enable(QEMUClock *clock, int enabled);
void qemu_clock_warp(QEMUClock *clock);
+void qemu_register_clock_reset_notifier(QEMUClock *clock, Notifier *notifier);
+void qemu_unregister_clock_reset_notifier(QEMUClock *clock,
+ Notifier *notifier);
+
QEMUTimer *qemu_new_timer(QEMUClock *clock, int scale,
QEMUTimerCB *cb, void *opaque);
void qemu_free_timer(QEMUTimer *ts);
diff --git a/qerror.c b/qerror.c
index d7fcd93..69c1bc9 100644
--- a/qerror.c
+++ b/qerror.c
@@ -141,6 +141,11 @@ static const QErrorStringTable qerror_table[] = {
.desc = "Invalid JSON syntax",
},
{
+ .error_fmt = QERR_JSON_PARSE_ERROR,
+ .desc = "JSON parse error, %(message)",
+
+ },
+ {
.error_fmt = QERR_KVM_MISSING_CAP,
.desc = "Using KVM without %(capability), %(feature) unavailable",
},
@@ -193,6 +198,10 @@ static const QErrorStringTable qerror_table[] = {
.desc = "Could not set password",
},
{
+ .error_fmt = QERR_ADD_CLIENT_FAILED,
+ .desc = "Could not add client",
+ },
+ {
.error_fmt = QERR_TOO_MANY_FILES,
.desc = "Too many open files",
},
@@ -213,6 +222,14 @@ static const QErrorStringTable qerror_table[] = {
.error_fmt = QERR_VNC_SERVER_FAILED,
.desc = "Could not start VNC server on %(target)",
},
+ {
+ .error_fmt = QERR_QGA_LOGGING_FAILED,
+ .desc = "Guest agent failed to log non-optional log statement",
+ },
+ {
+ .error_fmt = QERR_QGA_COMMAND_FAILED,
+ .desc = "Guest agent command failed, error was '%(message)'",
+ },
{}
};
diff --git a/qerror.h b/qerror.h
index 16c830d..8058456 100644
--- a/qerror.h
+++ b/qerror.h
@@ -124,6 +124,9 @@ QError *qobject_to_qerror(const QObject *obj);
#define QERR_JSON_PARSE_ERROR \
"{ 'class': 'JSONParseError', 'data': { 'message': %s } }"
+#define QERR_BUFFER_OVERRUN \
+ "{ 'class': 'BufferOverrun', 'data': {} }"
+
#define QERR_KVM_MISSING_CAP \
"{ 'class': 'KVMMissingCap', 'data': { 'capability': %s, 'feature': %s } }"
@@ -163,6 +166,9 @@ QError *qobject_to_qerror(const QObject *obj);
#define QERR_SET_PASSWD_FAILED \
"{ 'class': 'SetPasswdFailed', 'data': {} }"
+#define QERR_ADD_CLIENT_FAILED \
+ "{ 'class': 'AddClientFailed', 'data': {} }"
+
#define QERR_TOO_MANY_FILES \
"{ 'class': 'TooManyFiles', 'data': {} }"
@@ -181,4 +187,10 @@ QError *qobject_to_qerror(const QObject *obj);
#define QERR_FEATURE_DISABLED \
"{ 'class': 'FeatureDisabled', 'data': { 'name': %s } }"
+#define QERR_QGA_LOGGING_FAILED \
+ "{ 'class': 'QgaLoggingFailed', 'data': {} }"
+
+#define QERR_QGA_COMMAND_FAILED \
+ "{ 'class': 'QgaCommandFailed', 'data': { 'message': %s } }"
+
#endif /* QERROR_H */
diff --git a/qga/guest-agent-command-state.c b/qga/guest-agent-command-state.c
new file mode 100644
index 0000000..bc6e0bd
--- /dev/null
+++ b/qga/guest-agent-command-state.c
@@ -0,0 +1,73 @@
+/*
+ * QEMU Guest Agent command state interfaces
+ *
+ * Copyright IBM Corp. 2011
+ *
+ * Authors:
+ * Michael Roth <mdroth@linux.vnet.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.
+ */
+#include <glib.h>
+#include "qga/guest-agent-core.h"
+
+struct GACommandState {
+ GSList *groups;
+};
+
+typedef struct GACommandGroup {
+ void (*init)(void);
+ void (*cleanup)(void);
+} GACommandGroup;
+
+/* handle init/cleanup for stateful guest commands */
+
+void ga_command_state_add(GACommandState *cs,
+ void (*init)(void),
+ void (*cleanup)(void))
+{
+ GACommandGroup *cg = qemu_mallocz(sizeof(GACommandGroup));
+ cg->init = init;
+ cg->cleanup = cleanup;
+ cs->groups = g_slist_append(cs->groups, cg);
+}
+
+static void ga_command_group_init(gpointer opaque, gpointer unused)
+{
+ GACommandGroup *cg = opaque;
+
+ g_assert(cg);
+ if (cg->init) {
+ cg->init();
+ }
+}
+
+void ga_command_state_init_all(GACommandState *cs)
+{
+ g_assert(cs);
+ g_slist_foreach(cs->groups, ga_command_group_init, NULL);
+}
+
+static void ga_command_group_cleanup(gpointer opaque, gpointer unused)
+{
+ GACommandGroup *cg = opaque;
+
+ g_assert(cg);
+ if (cg->cleanup) {
+ cg->cleanup();
+ }
+}
+
+void ga_command_state_cleanup_all(GACommandState *cs)
+{
+ g_assert(cs);
+ g_slist_foreach(cs->groups, ga_command_group_cleanup, NULL);
+}
+
+GACommandState *ga_command_state_new(void)
+{
+ GACommandState *cs = qemu_mallocz(sizeof(GACommandState));
+ cs->groups = NULL;
+ return cs;
+}
diff --git a/qga/guest-agent-commands.c b/qga/guest-agent-commands.c
new file mode 100644
index 0000000..30c4068
--- /dev/null
+++ b/qga/guest-agent-commands.c
@@ -0,0 +1,561 @@
+/*
+ * QEMU Guest Agent commands
+ *
+ * Copyright IBM Corp. 2011
+ *
+ * Authors:
+ * Michael Roth <mdroth@linux.vnet.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.
+ */
+
+#include <glib.h>
+
+#if defined(__linux__)
+#include <mntent.h>
+#include <linux/fs.h>
+
+#if defined(__linux__) && defined(FIFREEZE)
+#define CONFIG_FSFREEZE
+#endif
+#endif
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include "qga/guest-agent-core.h"
+#include "qga-qmp-commands.h"
+#include "qerror.h"
+#include "qemu-queue.h"
+
+static GAState *ga_state;
+
+/* Note: in some situations, like with the fsfreeze, logging may be
+ * temporarilly disabled. if it is necessary that a command be able
+ * to log for accounting purposes, check ga_logging_enabled() beforehand,
+ * and use the QERR_QGA_LOGGING_DISABLED to generate an error
+ */
+static void slog(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ g_logv("syslog", G_LOG_LEVEL_INFO, fmt, ap);
+ va_end(ap);
+}
+
+int64_t qmp_guest_sync(int64_t id, Error **errp)
+{
+ return id;
+}
+
+void qmp_guest_ping(Error **err)
+{
+ slog("guest-ping called");
+}
+
+struct GuestAgentInfo *qmp_guest_info(Error **err)
+{
+ GuestAgentInfo *info = qemu_mallocz(sizeof(GuestAgentInfo));
+
+ info->version = g_strdup(QGA_VERSION);
+
+ return info;
+}
+
+void qmp_guest_shutdown(bool has_mode, const char *mode, Error **err)
+{
+ int ret;
+ const char *shutdown_flag;
+
+ slog("guest-shutdown called, mode: %s", mode);
+ if (!has_mode || strcmp(mode, "powerdown") == 0) {
+ shutdown_flag = "-P";
+ } else if (strcmp(mode, "halt") == 0) {
+ shutdown_flag = "-H";
+ } else if (strcmp(mode, "reboot") == 0) {
+ shutdown_flag = "-r";
+ } else {
+ error_set(err, QERR_INVALID_PARAMETER_VALUE, "mode",
+ "halt|powerdown|reboot");
+ return;
+ }
+
+ ret = fork();
+ if (ret == 0) {
+ /* child, start the shutdown */
+ setsid();
+ fclose(stdin);
+ fclose(stdout);
+ fclose(stderr);
+
+ ret = execl("/sbin/shutdown", "shutdown", shutdown_flag, "+0",
+ "hypervisor initiated shutdown", (char*)NULL);
+ if (ret) {
+ slog("guest-shutdown failed: %s", strerror(errno));
+ }
+ exit(!!ret);
+ } else if (ret < 0) {
+ error_set(err, QERR_UNDEFINED_ERROR);
+ }
+}
+
+typedef struct GuestFileHandle {
+ uint64_t id;
+ FILE *fh;
+ QTAILQ_ENTRY(GuestFileHandle) next;
+} GuestFileHandle;
+
+static struct {
+ QTAILQ_HEAD(, GuestFileHandle) filehandles;
+} guest_file_state;
+
+static void guest_file_handle_add(FILE *fh)
+{
+ GuestFileHandle *gfh;
+
+ gfh = qemu_mallocz(sizeof(GuestFileHandle));
+ gfh->id = fileno(fh);
+ gfh->fh = fh;
+ QTAILQ_INSERT_TAIL(&guest_file_state.filehandles, gfh, next);
+}
+
+static GuestFileHandle *guest_file_handle_find(int64_t id)
+{
+ GuestFileHandle *gfh;
+
+ QTAILQ_FOREACH(gfh, &guest_file_state.filehandles, next)
+ {
+ if (gfh->id == id) {
+ return gfh;
+ }
+ }
+
+ return NULL;
+}
+
+int64_t qmp_guest_file_open(const char *path, bool has_mode, const char *mode, Error **err)
+{
+ FILE *fh;
+ int fd;
+ int64_t ret = -1;
+
+ if (!has_mode) {
+ mode = "r";
+ }
+ slog("guest-file-open called, filepath: %s, mode: %s", path, mode);
+ fh = fopen(path, mode);
+ if (!fh) {
+ error_set(err, QERR_OPEN_FILE_FAILED, path);
+ return -1;
+ }
+
+ /* set fd non-blocking to avoid common use cases (like reading from a
+ * named pipe) from hanging the agent
+ */
+ fd = fileno(fh);
+ ret = fcntl(fd, F_GETFL);
+ ret = fcntl(fd, F_SETFL, ret | O_NONBLOCK);
+ if (ret == -1) {
+ error_set(err, QERR_QGA_COMMAND_FAILED, "fcntl() failed");
+ fclose(fh);
+ return -1;
+ }
+
+ guest_file_handle_add(fh);
+ slog("guest-file-open, handle: %d", fd);
+ return fd;
+}
+
+void qmp_guest_file_close(int64_t handle, Error **err)
+{
+ GuestFileHandle *gfh = guest_file_handle_find(handle);
+ int ret;
+
+ slog("guest-file-close called, handle: %ld", handle);
+ if (!gfh) {
+ error_set(err, QERR_FD_NOT_FOUND, "handle");
+ return;
+ }
+
+ ret = fclose(gfh->fh);
+ if (ret == -1) {
+ error_set(err, QERR_QGA_COMMAND_FAILED, "fclose() failed");
+ return;
+ }
+
+ QTAILQ_REMOVE(&guest_file_state.filehandles, gfh, next);
+ qemu_free(gfh);
+}
+
+struct GuestFileRead *qmp_guest_file_read(int64_t handle, bool has_count,
+ int64_t count, Error **err)
+{
+ GuestFileHandle *gfh = guest_file_handle_find(handle);
+ GuestFileRead *read_data = NULL;
+ guchar *buf;
+ FILE *fh;
+ size_t read_count;
+
+ if (!gfh) {
+ error_set(err, QERR_FD_NOT_FOUND, "handle");
+ return NULL;
+ }
+
+ if (!has_count) {
+ count = QGA_READ_COUNT_DEFAULT;
+ } else if (count < 0) {
+ error_set(err, QERR_INVALID_PARAMETER, "count");
+ return NULL;
+ }
+
+ fh = gfh->fh;
+ buf = qemu_mallocz(count+1);
+ read_count = fread(buf, 1, count, fh);
+ if (ferror(fh)) {
+ slog("guest-file-read failed, handle: %ld", handle);
+ error_set(err, QERR_QGA_COMMAND_FAILED, "fread() failed");
+ } else {
+ buf[read_count] = 0;
+ read_data = qemu_mallocz(sizeof(GuestFileRead));
+ read_data->count = read_count;
+ read_data->eof = feof(fh);
+ if (read_count) {
+ read_data->buf_b64 = g_base64_encode(buf, read_count);
+ }
+ }
+ qemu_free(buf);
+ clearerr(fh);
+
+ return read_data;
+}
+
+GuestFileWrite *qmp_guest_file_write(int64_t handle, const char *buf_b64,
+ bool has_count, int64_t count, Error **err)
+{
+ GuestFileWrite *write_data = NULL;
+ guchar *buf;
+ gsize buf_len;
+ int write_count;
+ GuestFileHandle *gfh = guest_file_handle_find(handle);
+ FILE *fh;
+
+ if (!gfh) {
+ error_set(err, QERR_FD_NOT_FOUND, "handle");
+ return NULL;
+ }
+
+ fh = gfh->fh;
+ buf = g_base64_decode(buf_b64, &buf_len);
+
+ if (!has_count) {
+ count = buf_len;
+ } else if (count < 0 || count > buf_len) {
+ qemu_free(buf);
+ error_set(err, QERR_INVALID_PARAMETER, "count");
+ return NULL;
+ }
+
+ write_count = fwrite(buf, 1, count, fh);
+ if (ferror(fh)) {
+ slog("guest-file-write failed, handle: %ld", handle);
+ error_set(err, QERR_QGA_COMMAND_FAILED, "fwrite() error");
+ } else {
+ write_data = qemu_mallocz(sizeof(GuestFileWrite));
+ write_data->count = write_count;
+ write_data->eof = feof(fh);
+ }
+ qemu_free(buf);
+ clearerr(fh);
+
+ return write_data;
+}
+
+struct GuestFileSeek *qmp_guest_file_seek(int64_t handle, int64_t offset,
+ int64_t whence, Error **err)
+{
+ GuestFileHandle *gfh = guest_file_handle_find(handle);
+ GuestFileSeek *seek_data = NULL;
+ FILE *fh;
+ int ret;
+
+ if (!gfh) {
+ error_set(err, QERR_FD_NOT_FOUND, "handle");
+ return NULL;
+ }
+
+ fh = gfh->fh;
+ ret = fseek(fh, offset, whence);
+ if (ret == -1) {
+ error_set(err, QERR_QGA_COMMAND_FAILED, strerror(errno));
+ } else {
+ seek_data = qemu_mallocz(sizeof(GuestFileRead));
+ seek_data->position = ftell(fh);
+ seek_data->eof = feof(fh);
+ }
+ clearerr(fh);
+
+ return seek_data;
+}
+
+void qmp_guest_file_flush(int64_t handle, Error **err)
+{
+ GuestFileHandle *gfh = guest_file_handle_find(handle);
+ FILE *fh;
+ int ret;
+
+ if (!gfh) {
+ error_set(err, QERR_FD_NOT_FOUND, "handle");
+ return;
+ }
+
+ fh = gfh->fh;
+ ret = fflush(fh);
+ if (ret == EOF) {
+ error_set(err, QERR_QGA_COMMAND_FAILED, strerror(errno));
+ }
+}
+
+static void guest_file_init(void)
+{
+ QTAILQ_INIT(&guest_file_state.filehandles);
+}
+
+#if defined(CONFIG_FSFREEZE)
+static void disable_logging(void)
+{
+ ga_disable_logging(ga_state);
+}
+
+static void enable_logging(void)
+{
+ ga_enable_logging(ga_state);
+}
+
+typedef struct GuestFsfreezeMount {
+ char *dirname;
+ char *devtype;
+ QTAILQ_ENTRY(GuestFsfreezeMount) next;
+} GuestFsfreezeMount;
+
+struct {
+ GuestFsfreezeStatus status;
+ QTAILQ_HEAD(, GuestFsfreezeMount) mount_list;
+} guest_fsfreeze_state;
+
+/*
+ * Walk the mount table and build a list of local file systems
+ */
+static int guest_fsfreeze_build_mount_list(void)
+{
+ struct mntent *ment;
+ GuestFsfreezeMount *mount, *temp;
+ char const *mtab = MOUNTED;
+ FILE *fp;
+
+ QTAILQ_FOREACH_SAFE(mount, &guest_fsfreeze_state.mount_list, next, temp) {
+ QTAILQ_REMOVE(&guest_fsfreeze_state.mount_list, mount, next);
+ qemu_free(mount->dirname);
+ qemu_free(mount->devtype);
+ qemu_free(mount);
+ }
+
+ fp = setmntent(mtab, "r");
+ if (!fp) {
+ g_warning("fsfreeze: unable to read mtab");
+ return -1;
+ }
+
+ while ((ment = getmntent(fp))) {
+ /*
+ * An entry which device name doesn't start with a '/' is
+ * either a dummy file system or a network file system.
+ * Add special handling for smbfs and cifs as is done by
+ * coreutils as well.
+ */
+ if ((ment->mnt_fsname[0] != '/') ||
+ (strcmp(ment->mnt_type, "smbfs") == 0) ||
+ (strcmp(ment->mnt_type, "cifs") == 0)) {
+ continue;
+ }
+
+ mount = qemu_mallocz(sizeof(GuestFsfreezeMount));
+ mount->dirname = qemu_strdup(ment->mnt_dir);
+ mount->devtype = qemu_strdup(ment->mnt_type);
+
+ QTAILQ_INSERT_TAIL(&guest_fsfreeze_state.mount_list, mount, next);
+ }
+
+ endmntent(fp);
+
+ return 0;
+}
+
+/*
+ * Return status of freeze/thaw
+ */
+GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **err)
+{
+ return guest_fsfreeze_state.status;
+}
+
+/*
+ * Walk list of mounted file systems in the guest, and freeze the ones which
+ * are real local file systems.
+ */
+int64_t qmp_guest_fsfreeze_freeze(Error **err)
+{
+ int ret = 0, i = 0;
+ struct GuestFsfreezeMount *mount, *temp;
+ int fd;
+ char err_msg[512];
+
+ slog("guest-fsfreeze called");
+
+ if (guest_fsfreeze_state.status == GUEST_FSFREEZE_STATUS_FROZEN) {
+ return 0;
+ }
+
+ ret = guest_fsfreeze_build_mount_list();
+ if (ret < 0) {
+ return ret;
+ }
+
+ /* cannot risk guest agent blocking itself on a write in this state */
+ disable_logging();
+
+ QTAILQ_FOREACH_SAFE(mount, &guest_fsfreeze_state.mount_list, next, temp) {
+ fd = qemu_open(mount->dirname, O_RDONLY);
+ if (fd == -1) {
+ sprintf(err_msg, "failed to open %s, %s", mount->dirname, strerror(errno));
+ error_set(err, QERR_QGA_COMMAND_FAILED, err_msg);
+ goto error;
+ }
+
+ /* we try to cull filesytems we know won't work in advance, but other
+ * filesytems may not implement fsfreeze for less obvious reasons.
+ * these will report EOPNOTSUPP, so we simply ignore them. when
+ * thawing, these filesystems will return an EINVAL instead, due to
+ * not being in a frozen state. Other filesystem-specific
+ * errors may result in EINVAL, however, so the user should check the
+ * number * of filesystems returned here against those returned by the
+ * thaw operation to determine whether everything completed
+ * successfully
+ */
+ ret = ioctl(fd, FIFREEZE);
+ if (ret < 0 && errno != EOPNOTSUPP) {
+ sprintf(err_msg, "failed to freeze %s, %s", mount->dirname, strerror(errno));
+ error_set(err, QERR_QGA_COMMAND_FAILED, err_msg);
+ close(fd);
+ goto error;
+ }
+ close(fd);
+
+ i++;
+ }
+
+ guest_fsfreeze_state.status = GUEST_FSFREEZE_STATUS_FROZEN;
+ return i;
+
+error:
+ if (i > 0) {
+ qmp_guest_fsfreeze_thaw(NULL);
+ }
+ return 0;
+}
+
+/*
+ * Walk list of frozen file systems in the guest, and thaw them.
+ */
+int64_t qmp_guest_fsfreeze_thaw(Error **err)
+{
+ int ret;
+ GuestFsfreezeMount *mount, *temp;
+ int fd, i = 0;
+ bool has_error = false;
+
+ QTAILQ_FOREACH_SAFE(mount, &guest_fsfreeze_state.mount_list, next, temp) {
+ fd = qemu_open(mount->dirname, O_RDONLY);
+ if (fd == -1) {
+ has_error = true;
+ continue;
+ }
+ ret = ioctl(fd, FITHAW);
+ if (ret < 0 && errno != EOPNOTSUPP && errno != EINVAL) {
+ has_error = true;
+ close(fd);
+ continue;
+ }
+ close(fd);
+ i++;
+ }
+
+ if (has_error) {
+ guest_fsfreeze_state.status = GUEST_FSFREEZE_STATUS_ERROR;
+ } else {
+ guest_fsfreeze_state.status = GUEST_FSFREEZE_STATUS_THAWED;
+ }
+ enable_logging();
+ return i;
+}
+
+static void guest_fsfreeze_init(void)
+{
+ guest_fsfreeze_state.status = GUEST_FSFREEZE_STATUS_THAWED;
+ QTAILQ_INIT(&guest_fsfreeze_state.mount_list);
+}
+
+static void guest_fsfreeze_cleanup(void)
+{
+ int64_t ret;
+ Error *err = NULL;
+
+ if (guest_fsfreeze_state.status == GUEST_FSFREEZE_STATUS_FROZEN) {
+ ret = qmp_guest_fsfreeze_thaw(&err);
+ if (ret < 0 || err) {
+ slog("failed to clean up frozen filesystems");
+ }
+ }
+}
+#else
+/*
+ * Return status of freeze/thaw
+ */
+GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **err)
+{
+ error_set(err, QERR_UNSUPPORTED);
+
+ return 0;
+}
+
+/*
+ * Walk list of mounted file systems in the guest, and freeze the ones which
+ * are real local file systems.
+ */
+int64_t qmp_guest_fsfreeze_freeze(Error **err)
+{
+ error_set(err, QERR_UNSUPPORTED);
+
+ return 0;
+}
+
+/*
+ * Walk list of frozen file systems in the guest, and thaw them.
+ */
+int64_t qmp_guest_fsfreeze_thaw(Error **err)
+{
+ error_set(err, QERR_UNSUPPORTED);
+
+ return 0;
+}
+#endif
+
+/* register init/cleanup routines for stateful command groups */
+void ga_command_state_init(GAState *s, GACommandState *cs)
+{
+ ga_state = s;
+#if defined(CONFIG_FSFREEZE)
+ ga_command_state_add(cs, guest_fsfreeze_init, guest_fsfreeze_cleanup);
+#endif
+ ga_command_state_add(cs, guest_file_init, NULL);
+}
diff --git a/qga/guest-agent-core.h b/qga/guest-agent-core.h
new file mode 100644
index 0000000..e42b91d
--- /dev/null
+++ b/qga/guest-agent-core.h
@@ -0,0 +1,31 @@
+/*
+ * QEMU Guest Agent core declarations
+ *
+ * Copyright IBM Corp. 2011
+ *
+ * Authors:
+ * Adam Litke <aglitke@linux.vnet.ibm.com>
+ * Michael Roth <mdroth@linux.vnet.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.
+ */
+#include "qapi/qmp-core.h"
+#include "qemu-common.h"
+
+#define QGA_VERSION "1.0"
+#define QGA_READ_COUNT_DEFAULT 4 << 10
+
+typedef struct GAState GAState;
+typedef struct GACommandState GACommandState;
+
+void ga_command_state_init(GAState *s, GACommandState *cs);
+void ga_command_state_add(GACommandState *cs,
+ void (*init)(void),
+ void (*cleanup)(void));
+void ga_command_state_init_all(GACommandState *cs);
+void ga_command_state_cleanup_all(GACommandState *cs);
+GACommandState *ga_command_state_new(void);
+bool ga_logging_enabled(GAState *s);
+void ga_disable_logging(GAState *s);
+void ga_enable_logging(GAState *s);
diff --git a/qlist.h b/qlist.h
index dbe7b92..d426bd4 100644
--- a/qlist.h
+++ b/qlist.h
@@ -16,6 +16,7 @@
#include "qobject.h"
#include "qemu-queue.h"
#include "qemu-common.h"
+#include "qemu-queue.h"
typedef struct QListEntry {
QObject *value;
@@ -50,4 +51,14 @@ QObject *qlist_peek(QList *qlist);
int qlist_empty(const QList *qlist);
QList *qobject_to_qlist(const QObject *obj);
+static inline const QListEntry *qlist_first(const QList *qlist)
+{
+ return QTAILQ_FIRST(&qlist->head);
+}
+
+static inline const QListEntry *qlist_next(const QListEntry *entry)
+{
+ return QTAILQ_NEXT(entry, next);
+}
+
#endif /* QLIST_H */
diff --git a/qmp-commands.hx b/qmp-commands.hx
index 92c5c3a..03f67da 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -42,7 +42,7 @@ and we're going to establish a deprecation policy for badly defined commands.
If you're planning to adopt QMP, please observe the following:
- 1. The deprecation policy will take efect and be documented soon, please
+ 1. The deprecation policy will take effect and be documented soon, please
check the documentation of each used command as soon as a new release of
QEMU is available
@@ -694,6 +694,40 @@ Example:
EQMP
{
+ .name = "blockdev-snapshot-sync",
+ .args_type = "device:B,snapshot-file:s?,format:s?",
+ .params = "device [new-image-file] [format]",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_snapshot_blkdev,
+ },
+
+SQMP
+blockdev-snapshot-sync
+----------------------
+
+Synchronous snapshot of a block device. snapshot-file specifies the
+target of the new image. If the file exists, or if it is a device, the
+snapshot will be created in the existing file/device. If does not
+exist, a new file will be created. format specifies the format of the
+snapshot image, default is qcow2.
+
+Arguments:
+
+- "device": device name to snapshot (json-string)
+- "snapshot-file": name of new image file (json-string)
+- "format": format of new image (json-string, optional)
+
+Example:
+
+-> { "execute": "blockdev-snapshot", "arguments": { "device": "ide-hd0",
+ "snapshot-file":
+ "/some/place/my-image",
+ "format": "qcow2" } }
+<- { "return": {} }
+
+EQMP
+
+ {
.name = "balloon",
.args_type = "value:M",
.params = "target",
@@ -885,6 +919,33 @@ Example:
EQMP
{
+ .name = "add_client",
+ .args_type = "protocol:s,fdname:s,skipauth:b?",
+ .params = "protocol fdname skipauth",
+ .help = "add a graphics client",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = add_graphics_client,
+ },
+
+SQMP
+add_client
+----------
+
+Add a graphics client
+
+Arguments:
+
+- "protocol": protocol name (json-string)
+- "fdname": file descriptor name (json-string)
+
+Example:
+
+-> { "execute": "add_client", "arguments": { "protocol": "vnc",
+ "fdname": "myclient" } }
+<- { "return": {} }
+
+EQMP
+ {
.name = "qmp_capabilities",
.args_type = "",
.params = "",
diff --git a/savevm.c b/savevm.c
index 8139bc7..7801aa7 100644
--- a/savevm.c
+++ b/savevm.c
@@ -194,7 +194,7 @@ static int socket_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size)
ssize_t len;
do {
- len = recv(s->fd, (void *)buf, size, 0);
+ len = qemu_recv(s->fd, buf, size, 0);
} while (len == -1 && socket_error() == EINTR);
if (len == -1)
@@ -1234,6 +1234,7 @@ int vmstate_register_with_alias_id(DeviceState *dev, int instance_id,
se->opaque = opaque;
se->vmsd = vmsd;
se->alias_id = alias_id;
+ se->no_migrate = vmsd->unmigratable;
if (dev && dev->parent_bus && dev->parent_bus->info->get_dev_path) {
char *id = dev->parent_bus->info->get_dev_path(dev);
diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl
index 075b614..3498425 100755
--- a/scripts/checkpatch.pl
+++ b/scripts/checkpatch.pl
@@ -2068,8 +2068,10 @@ sub process {
}
# , must have a space on the right.
+ # not required when having a single },{ on one line
} elsif ($op eq ',') {
- if ($ctx !~ /.x[WEC]/ && $cc !~ /^}/) {
+ if ($ctx !~ /.x[WEC]/ && $cc !~ /^}/ &&
+ ($elements[$n] . $elements[$n + 2]) !~ " *}{") {
ERROR("space required after that '$op' $at\n" . $hereptr);
}
@@ -2537,6 +2539,7 @@ sub process {
}
if (!defined $suppress_ifbraces{$linenr - 1} &&
$line =~ /\b(if|while|for|else)\b/ &&
+ $line !~ /\#\s*if/ &&
$line !~ /\#\s*else/) {
my $allowed = 0;
diff --git a/scripts/ordereddict.py b/scripts/ordereddict.py
new file mode 100644
index 0000000..7242b50
--- /dev/null
+++ b/scripts/ordereddict.py
@@ -0,0 +1,127 @@
+# Copyright (c) 2009 Raymond Hettinger
+#
+# 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.
+
+from UserDict import DictMixin
+
+class OrderedDict(dict, DictMixin):
+
+ def __init__(self, *args, **kwds):
+ if len(args) > 1:
+ raise TypeError('expected at most 1 arguments, got %d' % len(args))
+ try:
+ self.__end
+ except AttributeError:
+ self.clear()
+ self.update(*args, **kwds)
+
+ def clear(self):
+ self.__end = end = []
+ end += [None, end, end] # sentinel node for doubly linked list
+ self.__map = {} # key --> [key, prev, next]
+ dict.clear(self)
+
+ def __setitem__(self, key, value):
+ if key not in self:
+ end = self.__end
+ curr = end[1]
+ curr[2] = end[1] = self.__map[key] = [key, curr, end]
+ dict.__setitem__(self, key, value)
+
+ def __delitem__(self, key):
+ dict.__delitem__(self, key)
+ key, prev, next = self.__map.pop(key)
+ prev[2] = next
+ next[1] = prev
+
+ def __iter__(self):
+ end = self.__end
+ curr = end[2]
+ while curr is not end:
+ yield curr[0]
+ curr = curr[2]
+
+ def __reversed__(self):
+ end = self.__end
+ curr = end[1]
+ while curr is not end:
+ yield curr[0]
+ curr = curr[1]
+
+ def popitem(self, last=True):
+ if not self:
+ raise KeyError('dictionary is empty')
+ if last:
+ key = reversed(self).next()
+ else:
+ key = iter(self).next()
+ value = self.pop(key)
+ return key, value
+
+ def __reduce__(self):
+ items = [[k, self[k]] for k in self]
+ tmp = self.__map, self.__end
+ del self.__map, self.__end
+ inst_dict = vars(self).copy()
+ self.__map, self.__end = tmp
+ if inst_dict:
+ return (self.__class__, (items,), inst_dict)
+ return self.__class__, (items,)
+
+ def keys(self):
+ return list(self)
+
+ setdefault = DictMixin.setdefault
+ update = DictMixin.update
+ pop = DictMixin.pop
+ values = DictMixin.values
+ items = DictMixin.items
+ iterkeys = DictMixin.iterkeys
+ itervalues = DictMixin.itervalues
+ iteritems = DictMixin.iteritems
+
+ def __repr__(self):
+ if not self:
+ return '%s()' % (self.__class__.__name__,)
+ return '%s(%r)' % (self.__class__.__name__, self.items())
+
+ def copy(self):
+ return self.__class__(self)
+
+ @classmethod
+ def fromkeys(cls, iterable, value=None):
+ d = cls()
+ for key in iterable:
+ d[key] = value
+ return d
+
+ def __eq__(self, other):
+ if isinstance(other, OrderedDict):
+ if len(self) != len(other):
+ return False
+ for p, q in zip(self.items(), other.items()):
+ if p != q:
+ return False
+ return True
+ return dict.__eq__(self, other)
+
+ def __ne__(self, other):
+ return not self == other
diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py
new file mode 100644
index 0000000..9ad4c54
--- /dev/null
+++ b/scripts/qapi-commands.py
@@ -0,0 +1,385 @@
+#
+# QAPI command marshaller generator
+#
+# Copyright IBM, Corp. 2011
+#
+# Authors:
+# Anthony Liguori <aliguori@us.ibm.com>
+# Michael Roth <mdroth@linux.vnet.ibm.com>
+#
+# This work is licensed under the terms of the GNU GPLv2.
+# See the COPYING.LIB file in the top-level directory.
+
+from ordereddict import OrderedDict
+from qapi import *
+import sys
+import os
+import getopt
+import errno
+
+def generate_decl_enum(name, members, genlist=True):
+ return mcgen('''
+
+void visit_type_%(name)s(Visitor *m, %(name)s * obj, const char *name, Error **errp);
+''',
+ name=name)
+
+def generate_command_decl(name, args, ret_type):
+ arglist=""
+ for argname, argtype, optional, structured in parse_args(args):
+ argtype = c_type(argtype)
+ if argtype == "char *":
+ argtype = "const char *"
+ if optional:
+ arglist += "bool has_%s, " % c_var(argname)
+ arglist += "%s %s, " % (argtype, c_var(argname))
+ return mcgen('''
+%(ret_type)s qmp_%(name)s(%(args)sError **errp);
+''',
+ ret_type=c_type(ret_type), name=c_var(name), args=arglist).strip()
+
+def gen_sync_call(name, args, ret_type, indent=0):
+ ret = ""
+ arglist=""
+ retval=""
+ if ret_type:
+ retval = "retval = "
+ for argname, argtype, optional, structured in parse_args(args):
+ if optional:
+ arglist += "has_%s, " % c_var(argname)
+ arglist += "%s, " % (c_var(argname))
+ push_indent(indent)
+ ret = mcgen('''
+%(retval)sqmp_%(name)s(%(args)serrp);
+
+''',
+ name=c_var(name), args=arglist, retval=retval).rstrip()
+ if ret_type:
+ ret += "\n" + mcgen(''''
+%(marshal_output_call)s
+''',
+ marshal_output_call=gen_marshal_output_call(name, ret_type)).rstrip()
+ pop_indent(indent)
+ return ret.rstrip()
+
+
+def gen_marshal_output_call(name, ret_type):
+ if not ret_type:
+ return ""
+ return "qmp_marshal_output_%s(retval, ret, errp);" % c_var(name)
+
+def gen_visitor_output_containers_decl(ret_type):
+ ret = ""
+ push_indent()
+ if ret_type:
+ ret += mcgen('''
+QmpOutputVisitor *mo;
+QapiDeallocVisitor *md;
+Visitor *v;
+''')
+ pop_indent()
+
+ return ret
+
+def gen_visitor_input_containers_decl(args):
+ ret = ""
+
+ push_indent()
+ if len(args) > 0:
+ ret += mcgen('''
+QmpInputVisitor *mi;
+QapiDeallocVisitor *md;
+Visitor *v;
+''')
+ pop_indent()
+
+ return ret.rstrip()
+
+def gen_visitor_input_vars_decl(args):
+ ret = ""
+ push_indent()
+ for argname, argtype, optional, structured in parse_args(args):
+ if optional:
+ ret += mcgen('''
+bool has_%(argname)s = false;
+''',
+ argname=c_var(argname))
+ if c_type(argtype).endswith("*"):
+ ret += mcgen('''
+%(argtype)s %(argname)s = NULL;
+''',
+ argname=c_var(argname), argtype=c_type(argtype))
+ else:
+ ret += mcgen('''
+%(argtype)s %(argname)s;
+''',
+ argname=c_var(argname), argtype=c_type(argtype))
+
+ pop_indent()
+ return ret.rstrip()
+
+def gen_visitor_input_block(args, obj, dealloc=False):
+ ret = ""
+ if len(args) == 0:
+ return ret
+
+ push_indent()
+
+ if dealloc:
+ ret += mcgen('''
+md = qapi_dealloc_visitor_new();
+v = qapi_dealloc_get_visitor(md);
+''')
+ else:
+ ret += mcgen('''
+mi = qmp_input_visitor_new(%(obj)s);
+v = qmp_input_get_visitor(mi);
+''',
+ obj=obj)
+
+ for argname, argtype, optional, structured in parse_args(args):
+ if optional:
+ ret += mcgen('''
+visit_start_optional(v, &has_%(c_name)s, "%(name)s", errp);
+if (has_%(c_name)s) {
+''',
+ c_name=c_var(argname), name=argname)
+ push_indent()
+ ret += mcgen('''
+visit_type_%(argtype)s(v, &%(c_name)s, "%(name)s", errp);
+''',
+ c_name=c_var(argname), name=argname, argtype=argtype)
+ if optional:
+ pop_indent()
+ ret += mcgen('''
+}
+visit_end_optional(v, errp);
+''')
+
+ if dealloc:
+ ret += mcgen('''
+qapi_dealloc_visitor_cleanup(md);
+''')
+ else:
+ ret += mcgen('''
+qmp_input_visitor_cleanup(mi);
+''')
+ pop_indent()
+ return ret.rstrip()
+
+def gen_marshal_output(name, args, ret_type):
+ if not ret_type:
+ return ""
+ ret = mcgen('''
+static void qmp_marshal_output_%(c_name)s(%(c_ret_type)s ret_in, QObject **ret_out, Error **errp)
+{
+ QapiDeallocVisitor *md = qapi_dealloc_visitor_new();
+ QmpOutputVisitor *mo = qmp_output_visitor_new();
+ Visitor *v;
+
+ v = qmp_output_get_visitor(mo);
+ visit_type_%(ret_type)s(v, &ret_in, "unused", errp);
+ if (!error_is_set(errp)) {
+ *ret_out = qmp_output_get_qobject(mo);
+ }
+ qmp_output_visitor_cleanup(mo);
+ v = qapi_dealloc_get_visitor(md);
+ visit_type_%(ret_type)s(v, &ret_in, "unused", errp);
+ qapi_dealloc_visitor_cleanup(md);
+}
+''',
+ c_ret_type=c_type(ret_type), c_name=c_var(name), ret_type=ret_type)
+
+ return ret
+
+def gen_marshal_input(name, args, ret_type):
+ ret = mcgen('''
+static void qmp_marshal_input_%(c_name)s(QDict *args, QObject **ret, Error **errp)
+{
+''',
+ c_name=c_var(name))
+
+ if ret_type:
+ if c_type(ret_type).endswith("*"):
+ retval = " %s retval = NULL;" % c_type(ret_type)
+ else:
+ retval = " %s retval;" % c_type(ret_type)
+ ret += mcgen('''
+%(retval)s
+''',
+ retval=retval)
+
+ if len(args) > 0:
+ ret += mcgen('''
+%(visitor_input_containers_decl)s
+%(visitor_input_vars_decl)s
+
+%(visitor_input_block)s
+
+''',
+ visitor_input_containers_decl=gen_visitor_input_containers_decl(args),
+ visitor_input_vars_decl=gen_visitor_input_vars_decl(args),
+ visitor_input_block=gen_visitor_input_block(args, "QOBJECT(args)"))
+
+ ret += mcgen('''
+ if (error_is_set(errp)) {
+ goto out;
+ }
+%(sync_call)s
+''',
+ sync_call=gen_sync_call(name, args, ret_type, indent=4))
+ ret += mcgen('''
+
+out:
+''')
+ ret += mcgen('''
+%(visitor_input_block_cleanup)s
+ return;
+}
+''',
+ visitor_input_block_cleanup=gen_visitor_input_block(args, None, dealloc=True))
+ return ret
+
+def gen_registry(commands):
+ registry=""
+ push_indent()
+ for cmd in commands:
+ registry += mcgen('''
+qmp_register_command("%(name)s", qmp_marshal_input_%(c_name)s);
+''',
+ name=cmd['command'], c_name=c_var(cmd['command']))
+ pop_indent()
+ ret = mcgen('''
+static void qmp_init_marshal(void)
+{
+%(registry)s
+}
+
+qapi_init(qmp_init_marshal);
+''',
+ registry=registry.rstrip())
+ return ret
+
+def gen_command_decl_prologue(header, guard, prefix=""):
+ ret = mcgen('''
+/* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */
+
+/*
+ * schema-defined QAPI function prototypes
+ *
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#ifndef %(guard)s
+#define %(guard)s
+
+#include "%(prefix)sqapi-types.h"
+#include "error.h"
+
+''',
+ header=basename(h_file), guard=guardname(h_file), prefix=prefix)
+ return ret
+
+def gen_command_def_prologue(prefix="", proxy=False):
+ ret = mcgen('''
+/* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */
+
+/*
+ * schema-defined QMP->QAPI command dispatch
+ *
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#include "qemu-objects.h"
+#include "qapi/qmp-core.h"
+#include "qapi/qapi-visit-core.h"
+#include "qapi/qmp-output-visitor.h"
+#include "qapi/qmp-input-visitor.h"
+#include "qapi/qapi-dealloc-visitor.h"
+#include "%(prefix)sqapi-types.h"
+#include "%(prefix)sqapi-visit.h"
+
+''',
+ prefix=prefix)
+ if not proxy:
+ ret += '#include "%sqmp-commands.h"' % prefix
+ return ret + "\n"
+
+
+try:
+ opts, args = getopt.gnu_getopt(sys.argv[1:], "p:o:", ["prefix=", "output-dir=", "type="])
+except getopt.GetoptError, err:
+ print str(err)
+ sys.exit(1)
+
+output_dir = ""
+prefix = ""
+dispatch_type = "sync"
+c_file = 'qmp-marshal.c'
+h_file = 'qmp-commands.h'
+
+for o, a in opts:
+ if o in ("-p", "--prefix"):
+ prefix = a
+ elif o in ("-o", "--output-dir"):
+ output_dir = a + "/"
+ elif o in ("-t", "--type"):
+ dispatch_type = a
+
+c_file = output_dir + prefix + c_file
+h_file = output_dir + prefix + h_file
+
+try:
+ os.makedirs(output_dir)
+except os.error, e:
+ if e.errno != errno.EEXIST:
+ raise
+
+exprs = parse_schema(sys.stdin)
+commands = filter(lambda expr: expr.has_key('command'), exprs)
+
+if dispatch_type == "sync":
+ fdecl = open(h_file, 'w')
+ fdef = open(c_file, 'w')
+ ret = gen_command_decl_prologue(header=basename(h_file), guard=guardname(h_file), prefix=prefix)
+ fdecl.write(ret)
+ ret = gen_command_def_prologue(prefix=prefix)
+ fdef.write(ret)
+
+ for cmd in commands:
+ arglist = []
+ ret_type = None
+ if cmd.has_key('data'):
+ arglist = cmd['data']
+ if cmd.has_key('returns'):
+ ret_type = cmd['returns']
+ ret = generate_command_decl(cmd['command'], arglist, ret_type) + "\n"
+ fdecl.write(ret)
+ if ret_type:
+ ret = gen_marshal_output(cmd['command'], arglist, ret_type) + "\n"
+ fdef.write(ret)
+ ret = gen_marshal_input(cmd['command'], arglist, ret_type) + "\n"
+ fdef.write(ret)
+
+ fdecl.write("\n#endif");
+ ret = gen_registry(commands)
+ fdef.write(ret)
+
+ fdef.flush()
+ fdef.close()
+ fdecl.flush()
+ fdecl.close()
diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py
new file mode 100644
index 0000000..cece325
--- /dev/null
+++ b/scripts/qapi-types.py
@@ -0,0 +1,270 @@
+#
+# QAPI types generator
+#
+# Copyright IBM, Corp. 2011
+#
+# Authors:
+# Anthony Liguori <aliguori@us.ibm.com>
+#
+# This work is licensed under the terms of the GNU GPLv2.
+# See the COPYING.LIB file in the top-level directory.
+
+from ordereddict import OrderedDict
+from qapi import *
+import sys
+import os
+import getopt
+import errno
+
+def generate_fwd_struct(name, members):
+ return mcgen('''
+typedef struct %(name)s %(name)s;
+
+typedef struct %(name)sList
+{
+ %(name)s *value;
+ struct %(name)sList *next;
+} %(name)sList;
+''',
+ name=name)
+
+def generate_struct(structname, fieldname, members):
+ ret = mcgen('''
+struct %(name)s
+{
+''',
+ name=structname)
+
+ for argname, argentry, optional, structured in parse_args(members):
+ if optional:
+ ret += mcgen('''
+ bool has_%(c_name)s;
+''',
+ c_name=c_var(argname))
+ if structured:
+ push_indent()
+ ret += generate_struct("", argname, argentry)
+ pop_indent()
+ else:
+ ret += mcgen('''
+ %(c_type)s %(c_name)s;
+''',
+ c_type=c_type(argentry), c_name=c_var(argname))
+
+ if len(fieldname):
+ fieldname = " " + fieldname
+ ret += mcgen('''
+}%(field)s;
+''',
+ field=fieldname)
+
+ return ret
+
+def generate_enum_lookup(name, values):
+ ret = mcgen('''
+const char *%(name)s_lookup[] = {
+''',
+ name=name)
+ i = 0
+ for value in values:
+ ret += mcgen('''
+ "%(value)s",
+''',
+ value=c_var(value).lower())
+
+ ret += mcgen('''
+ NULL,
+};
+
+''')
+ return ret
+
+def generate_enum(name, values):
+ lookup_decl = mcgen('''
+extern const char *%(name)s_lookup[];
+''',
+ name=name)
+
+ enum_decl = mcgen('''
+typedef enum %(name)s
+{
+''',
+ name=name)
+
+ i = 0
+ for value in values:
+ enum_decl += mcgen('''
+ %(abbrev)s_%(value)s = %(i)d,
+''',
+ abbrev=de_camel_case(name).upper(),
+ value=c_var(value).upper(),
+ i=i)
+ i += 1
+
+ enum_decl += mcgen('''
+} %(name)s;
+''',
+ name=name)
+
+ return lookup_decl + enum_decl
+
+def generate_union(name, typeinfo):
+ ret = mcgen('''
+struct %(name)s
+{
+ %(name)sKind kind;
+ union {
+''',
+ name=name)
+
+ for key in typeinfo:
+ ret += mcgen('''
+ %(c_type)s %(c_name)s;
+''',
+ c_type=c_type(typeinfo[key]),
+ c_name=c_var(key))
+
+ ret += mcgen('''
+ };
+};
+''')
+
+ return ret
+
+def generate_type_cleanup_decl(name):
+ ret = mcgen('''
+void qapi_free_%(type)s(%(c_type)s obj);
+''',
+ c_type=c_type(name),type=name)
+ return ret
+
+def generate_type_cleanup(name):
+ ret = mcgen('''
+void qapi_free_%(type)s(%(c_type)s obj)
+{
+ QapiDeallocVisitor *md;
+ Visitor *v;
+
+ if (!obj) {
+ return;
+ }
+
+ md = qapi_dealloc_visitor_new();
+ v = qapi_dealloc_get_visitor(md);
+ visit_type_%(type)s(v, &obj, NULL, NULL);
+ qapi_dealloc_visitor_cleanup(md);
+}
+''',
+ c_type=c_type(name),type=name)
+ return ret
+
+
+try:
+ opts, args = getopt.gnu_getopt(sys.argv[1:], "p:o:", ["prefix=", "output-dir="])
+except getopt.GetoptError, err:
+ print str(err)
+ sys.exit(1)
+
+output_dir = ""
+prefix = ""
+c_file = 'qapi-types.c'
+h_file = 'qapi-types.h'
+
+for o, a in opts:
+ if o in ("-p", "--prefix"):
+ prefix = a
+ elif o in ("-o", "--output-dir"):
+ output_dir = a + "/"
+
+c_file = output_dir + prefix + c_file
+h_file = output_dir + prefix + h_file
+
+try:
+ os.makedirs(output_dir)
+except os.error, e:
+ if e.errno != errno.EEXIST:
+ raise
+
+fdef = open(c_file, 'w')
+fdecl = open(h_file, 'w')
+
+fdef.write(mcgen('''
+/* AUTOMATICALLY GENERATED, DO NOT MODIFY */
+
+/*
+ * deallocation functions for schema-defined QAPI types
+ *
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ * Michael Roth <mdroth@linux.vnet.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#include "qapi/qapi-dealloc-visitor.h"
+#include "%(prefix)sqapi-types.h"
+#include "%(prefix)sqapi-visit.h"
+
+''', prefix=prefix))
+
+fdecl.write(mcgen('''
+/* AUTOMATICALLY GENERATED, DO NOT MODIFY */
+
+/*
+ * schema-defined QAPI types
+ *
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#ifndef %(guard)s
+#define %(guard)s
+
+#include "qapi/qapi-types-core.h"
+''',
+ guard=guardname(h_file)))
+
+exprs = parse_schema(sys.stdin)
+
+for expr in exprs:
+ ret = "\n"
+ if expr.has_key('type'):
+ ret += generate_fwd_struct(expr['type'], expr['data'])
+ elif expr.has_key('enum'):
+ ret += generate_enum(expr['enum'], expr['data'])
+ fdef.write(generate_enum_lookup(expr['enum'], expr['data']))
+ elif expr.has_key('union'):
+ ret += generate_fwd_struct(expr['union'], expr['data']) + "\n"
+ ret += generate_enum('%sKind' % expr['union'], expr['data'].keys())
+ else:
+ continue
+ fdecl.write(ret)
+
+for expr in exprs:
+ ret = "\n"
+ if expr.has_key('type'):
+ ret += generate_struct(expr['type'], "", expr['data']) + "\n"
+ ret += generate_type_cleanup_decl(expr['type'])
+ fdef.write(generate_type_cleanup(expr['type']) + "\n")
+ elif expr.has_key('union'):
+ ret += generate_union(expr['union'], expr['data'])
+ else:
+ continue
+ fdecl.write(ret)
+
+fdecl.write('''
+#endif
+''')
+
+fdecl.flush()
+fdecl.close()
diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
new file mode 100644
index 0000000..252230e
--- /dev/null
+++ b/scripts/qapi-visit.py
@@ -0,0 +1,246 @@
+#
+# QAPI visitor generator
+#
+# Copyright IBM, Corp. 2011
+#
+# Authors:
+# Anthony Liguori <aliguori@us.ibm.com>
+# Michael Roth <mdroth@linux.vnet.ibm.com>
+#
+# This work is licensed under the terms of the GNU GPLv2.
+# See the COPYING.LIB file in the top-level directory.
+
+from ordereddict import OrderedDict
+from qapi import *
+import sys
+import os
+import getopt
+import errno
+
+def generate_visit_struct_body(field_prefix, members):
+ ret = ""
+ if len(field_prefix):
+ field_prefix = field_prefix + "."
+ for argname, argentry, optional, structured in parse_args(members):
+ if optional:
+ ret += mcgen('''
+visit_start_optional(m, (obj && *obj) ? &(*obj)->%(c_prefix)shas_%(c_name)s : NULL, "%(name)s", errp);
+if ((*obj)->%(prefix)shas_%(c_name)s) {
+''',
+ c_prefix=c_var(field_prefix), prefix=field_prefix,
+ c_name=c_var(argname), name=argname)
+ push_indent()
+
+ if structured:
+ ret += mcgen('''
+visit_start_struct(m, NULL, "", "%(name)s", 0, errp);
+''',
+ name=argname)
+ ret += generate_visit_struct_body(field_prefix + argname, argentry)
+ ret += mcgen('''
+visit_end_struct(m, errp);
+''')
+ else:
+ ret += mcgen('''
+visit_type_%(type)s(m, (obj && *obj) ? &(*obj)->%(c_prefix)s%(c_name)s : NULL, "%(name)s", errp);
+''',
+ c_prefix=c_var(field_prefix), prefix=field_prefix,
+ type=type_name(argentry), c_name=c_var(argname),
+ name=argname)
+
+ if optional:
+ pop_indent()
+ ret += mcgen('''
+}
+visit_end_optional(m, errp);
+''')
+ return ret
+
+def generate_visit_struct(name, members):
+ ret = mcgen('''
+
+void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error **errp)
+{
+ visit_start_struct(m, (void **)obj, "%(name)s", name, sizeof(%(name)s), errp);
+''',
+ name=name)
+ push_indent()
+ ret += generate_visit_struct_body("", members)
+ pop_indent()
+
+ ret += mcgen('''
+ visit_end_struct(m, errp);
+}
+''')
+ return ret
+
+def generate_visit_list(name, members):
+ return mcgen('''
+
+void visit_type_%(name)sList(Visitor *m, %(name)sList ** obj, const char *name, Error **errp)
+{
+ GenericList *i;
+
+ visit_start_list(m, name, errp);
+
+ for (i = visit_next_list(m, (GenericList **)obj, errp); i; i = visit_next_list(m, &i, errp)) {
+ %(name)sList *native_i = (%(name)sList *)i;
+ visit_type_%(name)s(m, &native_i->value, NULL, errp);
+ }
+
+ visit_end_list(m, errp);
+}
+''',
+ name=name)
+
+def generate_visit_enum(name, members):
+ return mcgen('''
+
+void visit_type_%(name)s(Visitor *m, %(name)s * obj, const char *name, Error **errp)
+{
+ visit_type_enum(m, (int *)obj, %(name)s_lookup, "%(name)s", name, errp);
+}
+''',
+ name=name)
+
+def generate_visit_union(name, members):
+ ret = generate_visit_enum('%sKind' % name, members.keys())
+
+ ret += mcgen('''
+
+void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error **errp)
+{
+}
+''',
+ name=name)
+
+ return ret
+
+def generate_declaration(name, members, genlist=True):
+ ret = mcgen('''
+
+void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error **errp);
+''',
+ name=name)
+
+ if genlist:
+ ret += mcgen('''
+void visit_type_%(name)sList(Visitor *m, %(name)sList ** obj, const char *name, Error **errp);
+''',
+ name=name)
+
+ return ret
+
+def generate_decl_enum(name, members, genlist=True):
+ return mcgen('''
+
+void visit_type_%(name)s(Visitor *m, %(name)s * obj, const char *name, Error **errp);
+''',
+ name=name)
+
+try:
+ opts, args = getopt.gnu_getopt(sys.argv[1:], "p:o:", ["prefix=", "output-dir="])
+except getopt.GetoptError, err:
+ print str(err)
+ sys.exit(1)
+
+output_dir = ""
+prefix = ""
+c_file = 'qapi-visit.c'
+h_file = 'qapi-visit.h'
+
+for o, a in opts:
+ if o in ("-p", "--prefix"):
+ prefix = a
+ elif o in ("-o", "--output-dir"):
+ output_dir = a + "/"
+
+c_file = output_dir + prefix + c_file
+h_file = output_dir + prefix + h_file
+
+try:
+ os.makedirs(output_dir)
+except os.error, e:
+ if e.errno != errno.EEXIST:
+ raise
+
+fdef = open(c_file, 'w')
+fdecl = open(h_file, 'w')
+
+fdef.write(mcgen('''
+/* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */
+
+/*
+ * schema-defined QAPI visitor functions
+ *
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#include "%(header)s"
+''',
+ header=basename(h_file)))
+
+fdecl.write(mcgen('''
+/* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */
+
+/*
+ * schema-defined QAPI visitor function
+ *
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#ifndef %(guard)s
+#define %(guard)s
+
+#include "qapi/qapi-visit-core.h"
+#include "%(prefix)sqapi-types.h"
+''',
+ prefix=prefix, guard=guardname(h_file)))
+
+exprs = parse_schema(sys.stdin)
+
+for expr in exprs:
+ if expr.has_key('type'):
+ ret = generate_visit_struct(expr['type'], expr['data'])
+ ret += generate_visit_list(expr['type'], expr['data'])
+ fdef.write(ret)
+
+ ret = generate_declaration(expr['type'], expr['data'])
+ fdecl.write(ret)
+ elif expr.has_key('union'):
+ ret = generate_visit_union(expr['union'], expr['data'])
+ fdef.write(ret)
+
+ ret = generate_decl_enum('%sKind' % expr['union'], expr['data'].keys())
+ ret += generate_declaration(expr['union'], expr['data'])
+ fdecl.write(ret)
+ elif expr.has_key('enum'):
+ ret = generate_visit_enum(expr['enum'], expr['data'])
+ fdef.write(ret)
+
+ ret = generate_decl_enum(expr['enum'], expr['data'])
+ fdecl.write(ret)
+
+fdecl.write('''
+#endif
+''')
+
+fdecl.flush()
+fdecl.close()
+
+fdef.flush()
+fdef.close()
diff --git a/scripts/qapi.py b/scripts/qapi.py
new file mode 100644
index 0000000..56af232
--- /dev/null
+++ b/scripts/qapi.py
@@ -0,0 +1,203 @@
+#
+# QAPI helper library
+#
+# Copyright IBM, Corp. 2011
+#
+# Authors:
+# Anthony Liguori <aliguori@us.ibm.com>
+#
+# This work is licensed under the terms of the GNU GPLv2.
+# See the COPYING.LIB file in the top-level directory.
+
+from ordereddict import OrderedDict
+
+def tokenize(data):
+ while len(data):
+ if data[0] in ['{', '}', ':', ',', '[', ']']:
+ yield data[0]
+ data = data[1:]
+ elif data[0] in ' \n':
+ data = data[1:]
+ elif data[0] == "'":
+ data = data[1:]
+ string = ''
+ while data[0] != "'":
+ string += data[0]
+ data = data[1:]
+ data = data[1:]
+ yield string
+
+def parse(tokens):
+ if tokens[0] == '{':
+ ret = OrderedDict()
+ tokens = tokens[1:]
+ while tokens[0] != '}':
+ key = tokens[0]
+ tokens = tokens[1:]
+
+ tokens = tokens[1:] # :
+
+ value, tokens = parse(tokens)
+
+ if tokens[0] == ',':
+ tokens = tokens[1:]
+
+ ret[key] = value
+ tokens = tokens[1:]
+ return ret, tokens
+ elif tokens[0] == '[':
+ ret = []
+ tokens = tokens[1:]
+ while tokens[0] != ']':
+ value, tokens = parse(tokens)
+ if tokens[0] == ',':
+ tokens = tokens[1:]
+ ret.append(value)
+ tokens = tokens[1:]
+ return ret, tokens
+ else:
+ return tokens[0], tokens[1:]
+
+def evaluate(string):
+ return parse(map(lambda x: x, tokenize(string)))[0]
+
+def parse_schema(fp):
+ exprs = []
+ expr = ''
+ expr_eval = None
+
+ for line in fp:
+ if line.startswith('#') or line == '\n':
+ continue
+
+ if line.startswith(' '):
+ expr += line
+ elif expr:
+ expr_eval = evaluate(expr)
+ if expr_eval.has_key('enum'):
+ add_enum(expr_eval['enum'])
+ elif expr_eval.has_key('union'):
+ add_enum('%sKind' % expr_eval['union'])
+ exprs.append(expr_eval)
+ expr = line
+ else:
+ expr += line
+
+ if expr:
+ expr_eval = evaluate(expr)
+ if expr_eval.has_key('enum'):
+ add_enum(expr_eval['enum'])
+ elif expr_eval.has_key('union'):
+ add_enum('%sKind' % expr_eval['union'])
+ exprs.append(expr_eval)
+
+ return exprs
+
+def parse_args(typeinfo):
+ for member in typeinfo:
+ argname = member
+ argentry = typeinfo[member]
+ optional = False
+ structured = False
+ if member.startswith('*'):
+ argname = member[1:]
+ optional = True
+ if isinstance(argentry, OrderedDict):
+ structured = True
+ yield (argname, argentry, optional, structured)
+
+def de_camel_case(name):
+ new_name = ''
+ for ch in name:
+ if ch.isupper() and new_name:
+ new_name += '_'
+ if ch == '-':
+ new_name += '_'
+ else:
+ new_name += ch.lower()
+ return new_name
+
+def camel_case(name):
+ new_name = ''
+ first = True
+ for ch in name:
+ if ch in ['_', '-']:
+ first = True
+ elif first:
+ new_name += ch.upper()
+ first = False
+ else:
+ new_name += ch.lower()
+ return new_name
+
+def c_var(name):
+ return '_'.join(name.split('-')).lstrip("*")
+
+def c_list_type(name):
+ return '%sList' % name
+
+def type_name(name):
+ if type(name) == list:
+ return c_list_type(name[0])
+ return name
+
+enum_types = []
+
+def add_enum(name):
+ global enum_types
+ enum_types.append(name)
+
+def is_enum(name):
+ global enum_types
+ return (name in enum_types)
+
+def c_type(name):
+ if name == 'str':
+ return 'char *'
+ elif name == 'int':
+ return 'int64_t'
+ elif name == 'bool':
+ return 'bool'
+ elif name == 'number':
+ return 'double'
+ elif type(name) == list:
+ return '%s *' % c_list_type(name[0])
+ elif is_enum(name):
+ return name
+ elif name == None or len(name) == 0:
+ return 'void'
+ elif name == name.upper():
+ return '%sEvent *' % camel_case(name)
+ else:
+ return '%s *' % name
+
+def genindent(count):
+ ret = ""
+ for i in range(count):
+ ret += " "
+ return ret
+
+indent_level = 0
+
+def push_indent(indent_amount=4):
+ global indent_level
+ indent_level += indent_amount
+
+def pop_indent(indent_amount=4):
+ global indent_level
+ indent_level -= indent_amount
+
+def cgen(code, **kwds):
+ indent = genindent(indent_level)
+ lines = code.split('\n')
+ lines = map(lambda x: indent + x, lines)
+ return '\n'.join(lines) % kwds + '\n'
+
+def mcgen(code, **kwds):
+ return cgen('\n'.join(code.split('\n')[1:-1]), **kwds)
+
+def basename(filename):
+ return filename.split("/")[-1]
+
+def guardname(filename):
+ return filename.replace("/", "_").replace("-", "_").split(".")[0].upper()
diff --git a/simpletrace.c b/simpletrace.c
index f1dbb5e..de355e9 100644
--- a/simpletrace.c
+++ b/simpletrace.c
@@ -119,7 +119,7 @@ static void *writeout_thread(void *opaque)
TraceRecord record;
unsigned int writeout_idx = 0;
unsigned int num_available, idx;
- size_t unused;
+ size_t unused __attribute__ ((unused));
for (;;) {
wait_for_trace_records_available();
diff --git a/slirp/arp_table.c b/slirp/arp_table.c
new file mode 100644
index 0000000..820dee2
--- /dev/null
+++ b/slirp/arp_table.c
@@ -0,0 +1,95 @@
+/*
+ * ARP table
+ *
+ * Copyright (c) 2011 AdaCore
+ *
+ * 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 "slirp.h"
+
+void arp_table_add(Slirp *slirp, int ip_addr, uint8_t ethaddr[ETH_ALEN])
+{
+ const in_addr_t broadcast_addr =
+ ~slirp->vnetwork_mask.s_addr | slirp->vnetwork_addr.s_addr;
+ ArpTable *arptbl = &slirp->arp_table;
+ int i;
+
+ DEBUG_CALL("arp_table_add");
+ DEBUG_ARG("ip = 0x%x", ip_addr);
+ DEBUG_ARGS((dfd, " hw addr = %02x:%02x:%02x:%02x:%02x:%02x\n",
+ ethaddr[0], ethaddr[1], ethaddr[2],
+ ethaddr[3], ethaddr[4], ethaddr[5]));
+
+ /* Check 0.0.0.0/8 invalid source-only addresses */
+ assert((ip_addr & htonl(~(0xf << 28))) != 0);
+
+ if (ip_addr == 0xffffffff || ip_addr == broadcast_addr) {
+ /* Do not register broadcast addresses */
+ return;
+ }
+
+ /* Search for an entry */
+ for (i = 0; i < ARP_TABLE_SIZE; i++) {
+ if (arptbl->table[i].ar_sip == ip_addr) {
+ /* Update the entry */
+ memcpy(arptbl->table[i].ar_sha, ethaddr, ETH_ALEN);
+ return;
+ }
+ }
+
+ /* No entry found, create a new one */
+ arptbl->table[arptbl->next_victim].ar_sip = ip_addr;
+ memcpy(arptbl->table[arptbl->next_victim].ar_sha, ethaddr, ETH_ALEN);
+ arptbl->next_victim = (arptbl->next_victim + 1) % ARP_TABLE_SIZE;
+}
+
+bool arp_table_search(Slirp *slirp, int in_ip_addr,
+ uint8_t out_ethaddr[ETH_ALEN])
+{
+ const in_addr_t broadcast_addr =
+ ~slirp->vnetwork_mask.s_addr | slirp->vnetwork_addr.s_addr;
+ ArpTable *arptbl = &slirp->arp_table;
+ int i;
+
+ DEBUG_CALL("arp_table_search");
+ DEBUG_ARG("ip = 0x%x", in_ip_addr);
+
+ /* Check 0.0.0.0/8 invalid source-only addresses */
+ assert((in_ip_addr & htonl(~(0xf << 28))) != 0);
+
+ /* If broadcast address */
+ if (in_ip_addr == 0xffffffff || in_ip_addr == broadcast_addr) {
+ /* return Ethernet broadcast address */
+ memset(out_ethaddr, 0xff, ETH_ALEN);
+ return 1;
+ }
+
+ for (i = 0; i < ARP_TABLE_SIZE; i++) {
+ if (arptbl->table[i].ar_sip == in_ip_addr) {
+ memcpy(out_ethaddr, arptbl->table[i].ar_sha, ETH_ALEN);
+ DEBUG_ARGS((dfd, " found hw addr = %02x:%02x:%02x:%02x:%02x:%02x\n",
+ out_ethaddr[0], out_ethaddr[1], out_ethaddr[2],
+ out_ethaddr[3], out_ethaddr[4], out_ethaddr[5]));
+ return 1;
+ }
+ }
+
+ return 0;
+}
diff --git a/slirp/bootp.c b/slirp/bootp.c
index 1eb2ed1..efd1fe7 100644
--- a/slirp/bootp.c
+++ b/slirp/bootp.c
@@ -149,6 +149,7 @@ static void bootp_reply(Slirp *slirp, const struct bootp_t *bp)
struct in_addr preq_addr;
int dhcp_msg_type, val;
uint8_t *q;
+ uint8_t client_ethaddr[ETH_ALEN];
/* extract exact DHCP msg type */
dhcp_decode(bp, &dhcp_msg_type, &preq_addr);
@@ -164,8 +165,9 @@ static void bootp_reply(Slirp *slirp, const struct bootp_t *bp)
if (dhcp_msg_type != DHCPDISCOVER &&
dhcp_msg_type != DHCPREQUEST)
return;
- /* XXX: this is a hack to get the client mac address */
- memcpy(slirp->client_ethaddr, bp->bp_hwaddr, 6);
+
+ /* Get client's hardware address from bootp request */
+ memcpy(client_ethaddr, bp->bp_hwaddr, ETH_ALEN);
m = m_get(slirp);
if (!m) {
@@ -178,25 +180,25 @@ static void bootp_reply(Slirp *slirp, const struct bootp_t *bp)
if (dhcp_msg_type == DHCPDISCOVER) {
if (preq_addr.s_addr != htonl(0L)) {
- bc = request_addr(slirp, &preq_addr, slirp->client_ethaddr);
+ bc = request_addr(slirp, &preq_addr, client_ethaddr);
if (bc) {
daddr.sin_addr = preq_addr;
}
}
if (!bc) {
new_addr:
- bc = get_new_addr(slirp, &daddr.sin_addr, slirp->client_ethaddr);
+ bc = get_new_addr(slirp, &daddr.sin_addr, client_ethaddr);
if (!bc) {
DPRINTF("no address left\n");
return;
}
}
- memcpy(bc->macaddr, slirp->client_ethaddr, 6);
+ memcpy(bc->macaddr, client_ethaddr, ETH_ALEN);
} else if (preq_addr.s_addr != htonl(0L)) {
- bc = request_addr(slirp, &preq_addr, slirp->client_ethaddr);
+ bc = request_addr(slirp, &preq_addr, client_ethaddr);
if (bc) {
daddr.sin_addr = preq_addr;
- memcpy(bc->macaddr, slirp->client_ethaddr, 6);
+ memcpy(bc->macaddr, client_ethaddr, ETH_ALEN);
} else {
daddr.sin_addr.s_addr = 0;
}
@@ -209,6 +211,9 @@ static void bootp_reply(Slirp *slirp, const struct bootp_t *bp)
}
}
+ /* Update ARP table for this IP address */
+ arp_table_add(slirp, daddr.sin_addr.s_addr, client_ethaddr);
+
saddr.sin_addr = slirp->vhost_addr;
saddr.sin_port = htons(BOOTP_SERVER);
@@ -218,7 +223,7 @@ static void bootp_reply(Slirp *slirp, const struct bootp_t *bp)
rbp->bp_xid = bp->bp_xid;
rbp->bp_htype = 1;
rbp->bp_hlen = 6;
- memcpy(rbp->bp_hwaddr, bp->bp_hwaddr, 6);
+ memcpy(rbp->bp_hwaddr, bp->bp_hwaddr, ETH_ALEN);
rbp->bp_yiaddr = daddr.sin_addr; /* Client IP address */
rbp->bp_siaddr = saddr.sin_addr; /* Server IP address */
diff --git a/slirp/if.c b/slirp/if.c
index 0f04e13..2d79e45 100644
--- a/slirp/if.c
+++ b/slirp/if.c
@@ -6,6 +6,7 @@
*/
#include <slirp.h>
+#include "qemu-timer.h"
#define ifs_init(ifm) ((ifm)->ifs_next = (ifm)->ifs_prev = (ifm))
@@ -105,6 +106,9 @@ if_output(struct socket *so, struct mbuf *ifm)
ifs_init(ifm);
insque(ifm, ifq);
+ /* Expiration date = Now + 1 second */
+ ifm->expiration_date = qemu_get_clock_ns(rt_clock) + 1000000000ULL;
+
diddit:
slirp->if_queued++;
@@ -153,6 +157,9 @@ diddit:
void
if_start(Slirp *slirp)
{
+ int requeued = 0;
+ uint64_t now;
+
struct mbuf *ifm, *ifqt;
DEBUG_CALL("if_start");
@@ -165,6 +172,8 @@ if_start(Slirp *slirp)
if (!slirp_can_output(slirp->opaque))
return;
+ now = qemu_get_clock_ns(rt_clock);
+
/*
* See which queue to get next packet from
* If there's something in the fastq, select it immediately
@@ -199,11 +208,22 @@ if_start(Slirp *slirp)
ifm->ifq_so->so_nqueued = 0;
}
- /* Encapsulate the packet for sending */
- if_encap(slirp, (uint8_t *)ifm->m_data, ifm->m_len);
-
- m_free(ifm);
+ if (ifm->expiration_date < now) {
+ /* Expired */
+ m_free(ifm);
+ } else {
+ /* Encapsulate the packet for sending */
+ if (if_encap(slirp, ifm)) {
+ m_free(ifm);
+ } else {
+ /* re-queue */
+ insque(ifm, ifqt);
+ requeued++;
+ }
+ }
if (slirp->if_queued)
goto again;
+
+ slirp->if_queued = requeued;
}
diff --git a/slirp/ip_icmp.c b/slirp/ip_icmp.c
index 751a8e2..4b43994 100644
--- a/slirp/ip_icmp.c
+++ b/slirp/ip_icmp.c
@@ -60,6 +60,52 @@ static const int icmp_flush[19] = {
/* ADDR MASK REPLY (18) */ 0
};
+void icmp_init(Slirp *slirp)
+{
+ slirp->icmp.so_next = slirp->icmp.so_prev = &slirp->icmp;
+ slirp->icmp_last_so = &slirp->icmp;
+}
+
+static int icmp_send(struct socket *so, struct mbuf *m, int hlen)
+{
+ struct ip *ip = mtod(m, struct ip *);
+ struct sockaddr_in addr;
+
+ so->s = qemu_socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP);
+ if (so->s == -1) {
+ return -1;
+ }
+
+ so->so_m = m;
+ so->so_faddr = ip->ip_dst;
+ so->so_laddr = ip->ip_src;
+ so->so_iptos = ip->ip_tos;
+ so->so_type = IPPROTO_ICMP;
+ so->so_state = SS_ISFCONNECTED;
+ so->so_expire = curtime + SO_EXPIRE;
+
+ addr.sin_family = AF_INET;
+ addr.sin_addr = so->so_faddr;
+
+ insque(so, &so->slirp->icmp);
+
+ if (sendto(so->s, m->m_data + hlen, m->m_len - hlen, 0,
+ (struct sockaddr *)&addr, sizeof(addr)) == -1) {
+ DEBUG_MISC((dfd, "icmp_input icmp sendto tx errno = %d-%s\n",
+ errno, strerror(errno)));
+ icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_NET, 0, strerror(errno));
+ icmp_detach(so);
+ }
+
+ return 0;
+}
+
+void icmp_detach(struct socket *so)
+{
+ closesocket(so->s);
+ sofree(so);
+}
+
/*
* Process a received ICMP message.
*/
@@ -81,7 +127,7 @@ icmp_input(struct mbuf *m, int hlen)
*/
if (icmplen < ICMP_MINLEN) { /* min 8 bytes payload */
freeit:
- m_freem(m);
+ m_free(m);
goto end_error;
}
@@ -97,14 +143,18 @@ icmp_input(struct mbuf *m, int hlen)
DEBUG_ARG("icmp_type = %d", icp->icmp_type);
switch (icp->icmp_type) {
case ICMP_ECHO:
- icp->icmp_type = ICMP_ECHOREPLY;
ip->ip_len += hlen; /* since ip_input subtracts this */
if (ip->ip_dst.s_addr == slirp->vhost_addr.s_addr) {
icmp_reflect(m);
+ } else if (slirp->restricted) {
+ goto freeit;
} else {
struct socket *so;
struct sockaddr_in addr;
if ((so = socreate(slirp)) == NULL) goto freeit;
+ if (icmp_send(so, m, hlen) == 0) {
+ return;
+ }
if(udp_attach(so) == -1) {
DEBUG_MISC((dfd,"icmp_input udp_attach errno = %d-%s\n",
errno,strerror(errno)));
@@ -153,11 +203,11 @@ icmp_input(struct mbuf *m, int hlen)
case ICMP_TSTAMP:
case ICMP_MASKREQ:
case ICMP_REDIRECT:
- m_freem(m);
+ m_free(m);
break;
default:
- m_freem(m);
+ m_free(m);
} /* swith */
end_error:
@@ -319,6 +369,7 @@ icmp_reflect(struct mbuf *m)
m->m_len -= hlen;
icp = mtod(m, struct icmp *);
+ icp->icmp_type = ICMP_ECHOREPLY;
icp->icmp_cksum = 0;
icp->icmp_cksum = cksum(m, ip->ip_len - hlen);
@@ -349,3 +400,39 @@ icmp_reflect(struct mbuf *m)
(void ) ip_output((struct socket *)NULL, m);
}
+
+void icmp_receive(struct socket *so)
+{
+ struct mbuf *m = so->so_m;
+ struct ip *ip = mtod(m, struct ip *);
+ int hlen = ip->ip_hl << 2;
+ u_char error_code;
+ struct icmp *icp;
+ int id, len;
+
+ m->m_data += hlen;
+ m->m_len -= hlen;
+ icp = mtod(m, struct icmp *);
+
+ id = icp->icmp_id;
+ len = qemu_recv(so->s, icp, m->m_len, 0);
+ icp->icmp_id = id;
+
+ m->m_data -= hlen;
+ m->m_len += hlen;
+
+ if (len == -1 || len == 0) {
+ if (errno == ENETUNREACH) {
+ error_code = ICMP_UNREACH_NET;
+ } else {
+ error_code = ICMP_UNREACH_HOST;
+ }
+ DEBUG_MISC((dfd, " udp icmp rx errno = %d-%s\n", errno,
+ strerror(errno)));
+ icmp_error(so->so_m, ICMP_UNREACH, error_code, 0, strerror(errno));
+ } else {
+ icmp_reflect(so->so_m);
+ so->so_m = NULL; /* Don't m_free() it again! */
+ }
+ icmp_detach(so);
+}
diff --git a/slirp/ip_icmp.h b/slirp/ip_icmp.h
index 2692822..b3da1f2 100644
--- a/slirp/ip_icmp.h
+++ b/slirp/ip_icmp.h
@@ -153,9 +153,12 @@ struct icmp {
(type) == ICMP_IREQ || (type) == ICMP_IREQREPLY || \
(type) == ICMP_MASKREQ || (type) == ICMP_MASKREPLY)
+void icmp_init(Slirp *slirp);
void icmp_input(struct mbuf *, int);
void icmp_error(struct mbuf *msrc, u_char type, u_char code, int minsize,
const char *message);
void icmp_reflect(struct mbuf *);
+void icmp_receive(struct socket *so);
+void icmp_detach(struct socket *so);
#endif
diff --git a/slirp/ip_input.c b/slirp/ip_input.c
index 768ab0c..c7b3eb4 100644
--- a/slirp/ip_input.c
+++ b/slirp/ip_input.c
@@ -58,6 +58,7 @@ ip_init(Slirp *slirp)
slirp->ipq.ip_link.next = slirp->ipq.ip_link.prev = &slirp->ipq.ip_link;
udp_init(slirp);
tcp_init(slirp);
+ icmp_init(slirp);
}
/*
@@ -118,27 +119,6 @@ ip_input(struct mbuf *m)
goto bad;
}
- if (slirp->restricted) {
- if ((ip->ip_dst.s_addr & slirp->vnetwork_mask.s_addr) ==
- slirp->vnetwork_addr.s_addr) {
- if (ip->ip_dst.s_addr == 0xffffffff && ip->ip_p != IPPROTO_UDP)
- goto bad;
- } else {
- uint32_t inv_mask = ~slirp->vnetwork_mask.s_addr;
- struct ex_list *ex_ptr;
-
- if ((ip->ip_dst.s_addr & inv_mask) == inv_mask) {
- goto bad;
- }
- for (ex_ptr = slirp->exec_list; ex_ptr; ex_ptr = ex_ptr->ex_next)
- if (ex_ptr->ex_addr.s_addr == ip->ip_dst.s_addr)
- break;
-
- if (!ex_ptr)
- goto bad;
- }
- }
-
/* Should drop packet if mbuf too long? hmmm... */
if (m->m_len > ip->ip_len)
m_adj(m, ip->ip_len - m->m_len);
@@ -225,7 +205,7 @@ ip_input(struct mbuf *m)
}
return;
bad:
- m_freem(m);
+ m_free(m);
return;
}
@@ -318,7 +298,7 @@ ip_reass(Slirp *slirp, struct ip *ip, struct ipq *fp)
break;
}
q = q->ipf_next;
- m_freem(dtom(slirp, q->ipf_prev));
+ m_free(dtom(slirp, q->ipf_prev));
ip_deq(q->ipf_prev);
}
@@ -384,7 +364,7 @@ insert:
return ip;
dropfrag:
- m_freem(m);
+ m_free(m);
return NULL;
}
@@ -400,7 +380,7 @@ ip_freef(Slirp *slirp, struct ipq *fp)
for (q = fp->frag_link.next; q != (struct ipasfrag*)&fp->frag_link; q = p) {
p = q->ipf_next;
ip_deq(q);
- m_freem(dtom(slirp, q));
+ m_free(dtom(slirp, q));
}
remque(&fp->ip_link);
(void) m_free(dtom(slirp, fp));
@@ -531,7 +511,7 @@ typedef uint32_t n_time;
*/
break;
}
- off--; / * 0 origin * /
+ off--; /* 0 origin */
if (off > optlen - sizeof(struct in_addr)) {
/*
* End of source route. Should be for us.
@@ -574,7 +554,7 @@ typedef uint32_t n_time;
/*
* If no space remains, ignore.
*/
- off--; * 0 origin *
+ off--; /* 0 origin */
if (off > optlen - sizeof(struct in_addr))
break;
bcopy((caddr_t)(&ip->ip_dst), (caddr_t)&ipaddr.sin_addr,
diff --git a/slirp/ip_output.c b/slirp/ip_output.c
index 542f318..c82830f 100644
--- a/slirp/ip_output.c
+++ b/slirp/ip_output.c
@@ -159,7 +159,7 @@ sendorfree:
if (error == 0)
if_output(so, m);
else
- m_freem(m);
+ m_free(m);
}
}
@@ -167,6 +167,6 @@ done:
return (error);
bad:
- m_freem(m0);
+ m_free(m0);
goto done;
}
diff --git a/slirp/main.h b/slirp/main.h
index 0dd8d81..028df4b 100644
--- a/slirp/main.h
+++ b/slirp/main.h
@@ -42,5 +42,5 @@ extern int tcp_keepintvl;
#define PROTO_PPP 0x2
#endif
-void if_encap(Slirp *slirp, const uint8_t *ip_data, int ip_data_len);
+int if_encap(Slirp *slirp, struct mbuf *ifm);
ssize_t slirp_send(struct socket *so, const void *buf, size_t len, int flags);
diff --git a/slirp/mbuf.c b/slirp/mbuf.c
index ce2eb84..c699c75 100644
--- a/slirp/mbuf.c
+++ b/slirp/mbuf.c
@@ -70,6 +70,8 @@ m_get(Slirp *slirp)
m->m_len = 0;
m->m_nextpkt = NULL;
m->m_prevpkt = NULL;
+ m->arp_requested = false;
+ m->expiration_date = (uint64_t)-1;
end_error:
DEBUG_ARG("m = %lx", (long )m);
return m;
diff --git a/slirp/mbuf.h b/slirp/mbuf.h
index 97729e2..55170e5 100644
--- a/slirp/mbuf.h
+++ b/slirp/mbuf.h
@@ -33,9 +33,6 @@
#ifndef _MBUF_H_
#define _MBUF_H_
-#define m_freem m_free
-
-
#define MINCSIZE 4096 /* Amount to increase mbuf if too small */
/*
@@ -89,6 +86,8 @@ struct mbuf {
char m_dat_[1]; /* ANSI don't like 0 sized arrays */
char *m_ext_;
} M_dat;
+ bool arp_requested;
+ uint64_t expiration_date;
};
#define m_next m_hdr.mh_next
diff --git a/slirp/misc.c b/slirp/misc.c
index 08eba6a..6002550 100644
--- a/slirp/misc.c
+++ b/slirp/misc.c
@@ -153,11 +153,12 @@ fork_exec(struct socket *so, const char *ex, int do_pty)
return 0;
case 0:
+ setsid();
+
/* Set the DISPLAY */
if (do_pty == 2) {
(void) close(master);
#ifdef TIOCSCTTY /* XXXXX */
- (void) setsid();
ioctl(s, TIOCSCTTY, (char *)NULL);
#endif
} else {
@@ -406,4 +407,17 @@ void slirp_connection_info(Slirp *slirp, Monitor *mon)
inet_ntoa(dst_addr), ntohs(dst_port),
so->so_rcv.sb_cc, so->so_snd.sb_cc);
}
+
+ for (so = slirp->icmp.so_next; so != &slirp->icmp; so = so->so_next) {
+ n = snprintf(buf, sizeof(buf), " ICMP[%d sec]",
+ (so->so_expire - curtime) / 1000);
+ src.sin_addr = so->so_laddr;
+ dst_addr = so->so_faddr;
+ memset(&buf[n], ' ', 19 - n);
+ buf[19] = 0;
+ monitor_printf(mon, "%s %3d %15s - ", buf, so->s,
+ src.sin_addr.s_addr ? inet_ntoa(src.sin_addr) : "*");
+ monitor_printf(mon, "%15s - %5d %5d\n", inet_ntoa(dst_addr),
+ so->so_rcv.sb_cc, so->so_snd.sb_cc);
+ }
}
diff --git a/slirp/slirp.c b/slirp/slirp.c
index 1593be1..a86cc6e 100644
--- a/slirp/slirp.c
+++ b/slirp/slirp.c
@@ -31,11 +31,11 @@
struct in_addr loopback_addr;
/* emulated hosts use the MAC addr 52:55:IP:IP:IP:IP */
-static const uint8_t special_ethaddr[6] = {
+static const uint8_t special_ethaddr[ETH_ALEN] = {
0x52, 0x55, 0x00, 0x00, 0x00, 0x00
};
-static const uint8_t zero_ethaddr[6] = { 0, 0, 0, 0, 0, 0 };
+static const uint8_t zero_ethaddr[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
/* XXX: suppress those select globals */
fd_set *global_readfds, *global_writefds, *global_xfds;
@@ -373,6 +373,31 @@ void slirp_select_fill(int *pnfds,
UPD_NFDS(so->s);
}
}
+
+ /*
+ * ICMP sockets
+ */
+ for (so = slirp->icmp.so_next; so != &slirp->icmp;
+ so = so_next) {
+ so_next = so->so_next;
+
+ /*
+ * See if it's timed out
+ */
+ if (so->so_expire) {
+ if (so->so_expire <= curtime) {
+ icmp_detach(so);
+ continue;
+ } else {
+ do_slowtimo = 1; /* Let socket expire */
+ }
+ }
+
+ if (so->so_state & SS_ISFCONNECTED) {
+ FD_SET(so->s, readfds);
+ UPD_NFDS(so->s);
+ }
+ }
}
*pnfds = nfds;
@@ -497,7 +522,7 @@ void slirp_select_poll(fd_set *readfds, fd_set *writefds, fd_set *xfds,
*/
#ifdef PROBE_CONN
if (so->so_state & SS_ISFCONNECTING) {
- ret = recv(so->s, (char *)&ret, 0,0);
+ ret = qemu_recv(so->s, &ret, 0,0);
if (ret < 0) {
/* XXX */
@@ -542,6 +567,18 @@ void slirp_select_poll(fd_set *readfds, fd_set *writefds, fd_set *xfds,
sorecvfrom(so);
}
}
+
+ /*
+ * Check incoming ICMP relies.
+ */
+ for (so = slirp->icmp.so_next; so != &slirp->icmp;
+ so = so_next) {
+ so_next = so->so_next;
+
+ if (so->s != -1 && FD_ISSET(so->s, readfds)) {
+ icmp_receive(so);
+ }
+ }
}
/*
@@ -562,42 +599,8 @@ void slirp_select_poll(fd_set *readfds, fd_set *writefds, fd_set *xfds,
global_xfds = NULL;
}
-#define ETH_ALEN 6
-#define ETH_HLEN 14
-
-#define ETH_P_IP 0x0800 /* Internet Protocol packet */
-#define ETH_P_ARP 0x0806 /* Address Resolution packet */
-
-#define ARPOP_REQUEST 1 /* ARP request */
-#define ARPOP_REPLY 2 /* ARP reply */
-
-struct ethhdr
-{
- unsigned char h_dest[ETH_ALEN]; /* destination eth addr */
- unsigned char h_source[ETH_ALEN]; /* source ether addr */
- unsigned short h_proto; /* packet type ID field */
-};
-
-struct arphdr
-{
- unsigned short ar_hrd; /* format of hardware address */
- unsigned short ar_pro; /* format of protocol address */
- unsigned char ar_hln; /* length of hardware address */
- unsigned char ar_pln; /* length of protocol address */
- unsigned short ar_op; /* ARP opcode (command) */
-
- /*
- * Ethernet looks like this : This bit is variable sized however...
- */
- unsigned char ar_sha[ETH_ALEN]; /* sender hardware address */
- uint32_t ar_sip; /* sender IP address */
- unsigned char ar_tha[ETH_ALEN]; /* target hardware address */
- uint32_t ar_tip ; /* target IP address */
-} __attribute__((packed));
-
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[max(ETH_HLEN + sizeof(struct arphdr), 64)];
struct ethhdr *reh = (struct ethhdr *)arp_reply;
@@ -608,6 +611,12 @@ static void arp_input(Slirp *slirp, const uint8_t *pkt, int pkt_len)
ar_op = ntohs(ah->ar_op);
switch(ar_op) {
case ARPOP_REQUEST:
+ if (ah->ar_tip == ah->ar_sip) {
+ /* Gratuitous ARP */
+ arp_table_add(slirp, ah->ar_sip, ah->ar_sha);
+ return;
+ }
+
if ((ah->ar_tip & slirp->vnetwork_mask.s_addr) ==
slirp->vnetwork_addr.s_addr) {
if (ah->ar_tip == slirp->vnameserver_addr.s_addr ||
@@ -620,8 +629,8 @@ 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);
+
+ arp_table_add(slirp, ah->ar_sip, ah->ar_sha);
/* ARP request for alias/dns mac address */
memcpy(reh->h_dest, pkt + ETH_ALEN, ETH_ALEN);
@@ -642,11 +651,7 @@ static void arp_input(Slirp *slirp, const uint8_t *pkt, int pkt_len)
}
break;
case ARPOP_REPLY:
- /* reply to request of client mac address ? */
- if (!memcmp(slirp->client_ethaddr, zero_ethaddr, ETH_ALEN) &&
- ah->ar_sip == slirp->client_ipaddr.s_addr) {
- memcpy(slirp->client_ethaddr, ah->ar_sha, ETH_ALEN);
- }
+ arp_table_add(slirp, ah->ar_sip, ah->ar_sha);
break;
default:
break;
@@ -687,54 +692,63 @@ void slirp_input(Slirp *slirp, const uint8_t *pkt, int pkt_len)
}
}
-/* output the IP packet to the ethernet device */
-void if_encap(Slirp *slirp, const uint8_t *ip_data, int ip_data_len)
+/* Output the IP packet to the ethernet device. Returns 0 if the packet must be
+ * re-queued.
+ */
+int if_encap(Slirp *slirp, struct mbuf *ifm)
{
uint8_t buf[1600];
struct ethhdr *eh = (struct ethhdr *)buf;
+ uint8_t ethaddr[ETH_ALEN];
+ const struct ip *iph = (const struct ip *)ifm->m_data;
- if (ip_data_len + ETH_HLEN > sizeof(buf))
- return;
-
- if (!memcmp(slirp->client_ethaddr, zero_ethaddr, ETH_ALEN)) {
+ if (ifm->m_len + ETH_HLEN > sizeof(buf)) {
+ return 1;
+ }
+
+ if (!arp_table_search(slirp, iph->ip_dst.s_addr, ethaddr)) {
uint8_t arp_req[ETH_HLEN + sizeof(struct arphdr)];
struct ethhdr *reh = (struct ethhdr *)arp_req;
struct arphdr *rah = (struct arphdr *)(arp_req + ETH_HLEN);
- const struct ip *iph = (const struct ip *)ip_data;
-
- /* If the client addr is not known, there is no point in
- sending the packet to it. Normally the sender should have
- done an ARP request to get its MAC address. Here we do it
- in place of sending the packet and we hope that the sender
- will retry sending its packet. */
- memset(reh->h_dest, 0xff, ETH_ALEN);
- memcpy(reh->h_source, special_ethaddr, ETH_ALEN - 4);
- memcpy(&reh->h_source[2], &slirp->vhost_addr, 4);
- reh->h_proto = htons(ETH_P_ARP);
- rah->ar_hrd = htons(1);
- rah->ar_pro = htons(ETH_P_IP);
- rah->ar_hln = ETH_ALEN;
- rah->ar_pln = 4;
- rah->ar_op = htons(ARPOP_REQUEST);
- /* source hw addr */
- memcpy(rah->ar_sha, special_ethaddr, ETH_ALEN - 4);
- memcpy(&rah->ar_sha[2], &slirp->vhost_addr, 4);
- /* source IP */
- rah->ar_sip = slirp->vhost_addr.s_addr;
- /* target hw addr (none) */
- memset(rah->ar_tha, 0, ETH_ALEN);
- /* target IP */
- rah->ar_tip = iph->ip_dst.s_addr;
- slirp->client_ipaddr = iph->ip_dst;
- slirp_output(slirp->opaque, arp_req, sizeof(arp_req));
+
+ if (!ifm->arp_requested) {
+ /* If the client addr is not known, send an ARP request */
+ memset(reh->h_dest, 0xff, ETH_ALEN);
+ memcpy(reh->h_source, special_ethaddr, ETH_ALEN - 4);
+ memcpy(&reh->h_source[2], &slirp->vhost_addr, 4);
+ reh->h_proto = htons(ETH_P_ARP);
+ rah->ar_hrd = htons(1);
+ rah->ar_pro = htons(ETH_P_IP);
+ rah->ar_hln = ETH_ALEN;
+ rah->ar_pln = 4;
+ rah->ar_op = htons(ARPOP_REQUEST);
+
+ /* source hw addr */
+ memcpy(rah->ar_sha, special_ethaddr, ETH_ALEN - 4);
+ memcpy(&rah->ar_sha[2], &slirp->vhost_addr, 4);
+
+ /* source IP */
+ rah->ar_sip = slirp->vhost_addr.s_addr;
+
+ /* target hw addr (none) */
+ memset(rah->ar_tha, 0, ETH_ALEN);
+
+ /* target IP */
+ rah->ar_tip = iph->ip_dst.s_addr;
+ slirp->client_ipaddr = iph->ip_dst;
+ slirp_output(slirp->opaque, arp_req, sizeof(arp_req));
+ ifm->arp_requested = true;
+ }
+ return 0;
} else {
- memcpy(eh->h_dest, slirp->client_ethaddr, ETH_ALEN);
+ memcpy(eh->h_dest, ethaddr, ETH_ALEN);
memcpy(eh->h_source, special_ethaddr, ETH_ALEN - 4);
/* XXX: not correct */
memcpy(&eh->h_source[2], &slirp->vhost_addr, 4);
eh->h_proto = htons(ETH_P_IP);
- memcpy(buf + sizeof(struct ethhdr), ip_data, ip_data_len);
- slirp_output(slirp->opaque, buf, ip_data_len + ETH_HLEN);
+ memcpy(buf + sizeof(struct ethhdr), ifm->m_data, ifm->m_len);
+ slirp_output(slirp->opaque, buf, ifm->m_len + ETH_HLEN);
+ return 1;
}
}
diff --git a/slirp/slirp.h b/slirp/slirp.h
index 954289a..2a070e6 100644
--- a/slirp/slirp.h
+++ b/slirp/slirp.h
@@ -152,6 +152,7 @@ int inet_aton(const char *cp, struct in_addr *ia);
#include "tcp_var.h"
#include "tcpip.h"
#include "udp.h"
+#include "ip_icmp.h"
#include "mbuf.h"
#include "sbuf.h"
#include "socket.h"
@@ -169,6 +170,48 @@ int inet_aton(const char *cp, struct in_addr *ia);
/* osdep.c */
int qemu_socket(int domain, int type, int protocol);
+#define ETH_ALEN 6
+#define ETH_HLEN 14
+
+#define ETH_P_IP 0x0800 /* Internet Protocol packet */
+#define ETH_P_ARP 0x0806 /* Address Resolution packet */
+
+#define ARPOP_REQUEST 1 /* ARP request */
+#define ARPOP_REPLY 2 /* ARP reply */
+
+struct ethhdr {
+ unsigned char h_dest[ETH_ALEN]; /* destination eth addr */
+ unsigned char h_source[ETH_ALEN]; /* source ether addr */
+ unsigned short h_proto; /* packet type ID field */
+};
+
+struct arphdr {
+ unsigned short ar_hrd; /* format of hardware address */
+ unsigned short ar_pro; /* format of protocol address */
+ unsigned char ar_hln; /* length of hardware address */
+ unsigned char ar_pln; /* length of protocol address */
+ unsigned short ar_op; /* ARP opcode (command) */
+
+ /*
+ * Ethernet looks like this : This bit is variable sized however...
+ */
+ unsigned char ar_sha[ETH_ALEN]; /* sender hardware address */
+ uint32_t ar_sip; /* sender IP address */
+ unsigned char ar_tha[ETH_ALEN]; /* target hardware address */
+ uint32_t ar_tip; /* target IP address */
+} __attribute__((packed));
+
+#define ARP_TABLE_SIZE 16
+
+typedef struct ArpTable {
+ struct arphdr table[ARP_TABLE_SIZE];
+ int next_victim;
+} ArpTable;
+
+void arp_table_add(Slirp *slirp, int ip_addr, uint8_t ethaddr[ETH_ALEN]);
+
+bool arp_table_search(Slirp *slirp, int in_ip_addr,
+ uint8_t out_ethaddr[ETH_ALEN]);
struct Slirp {
QTAILQ_ENTRY(Slirp) entry;
@@ -180,9 +223,6 @@ struct Slirp {
struct in_addr vdhcp_startaddr;
struct in_addr vnameserver_addr;
- /* ARP cache for the guest IP addresses (XXX: allow many entries) */
- uint8_t client_ethaddr[6];
-
struct in_addr client_ipaddr;
char client_hostname[33];
@@ -218,10 +258,16 @@ struct Slirp {
struct socket udb;
struct socket *udp_last_so;
+ /* icmp states */
+ struct socket icmp;
+ struct socket *icmp_last_so;
+
/* tftp states */
char *tftp_prefix;
struct tftp_session tftp_sessions[TFTP_SESSIONS_MAX];
+ ArpTable arp_table;
+
void *opaque;
};
diff --git a/slirp/socket.c b/slirp/socket.c
index 6119234..77b0c98 100644
--- a/slirp/socket.c
+++ b/slirp/socket.c
@@ -71,6 +71,8 @@ sofree(struct socket *so)
slirp->tcp_last_so = &slirp->tcb;
} else if (so == slirp->udp_last_so) {
slirp->udp_last_so = &slirp->udb;
+ } else if (so == slirp->icmp_last_so) {
+ slirp->icmp_last_so = &slirp->icmp;
}
m_free(so->so_m);
@@ -164,7 +166,7 @@ soread(struct socket *so)
nn = readv(so->s, (struct iovec *)iov, n);
DEBUG_MISC((dfd, " ... read nn = %d bytes\n", nn));
#else
- nn = recv(so->s, iov[0].iov_base, iov[0].iov_len,0);
+ nn = qemu_recv(so->s, iov[0].iov_base, iov[0].iov_len,0);
#endif
if (nn <= 0) {
if (nn < 0 && (errno == EINTR || errno == EAGAIN))
@@ -189,7 +191,7 @@ soread(struct socket *so)
*/
if (n == 2 && nn == iov[0].iov_len) {
int ret;
- ret = recv(so->s, iov[1].iov_base, iov[1].iov_len,0);
+ ret = qemu_recv(so->s, iov[1].iov_base, iov[1].iov_len,0);
if (ret > 0)
nn += ret;
}
diff --git a/slirp/tcp_input.c b/slirp/tcp_input.c
index e4a7731..c1214c0 100644
--- a/slirp/tcp_input.c
+++ b/slirp/tcp_input.c
@@ -136,7 +136,7 @@ tcp_reass(register struct tcpcb *tp, register struct tcpiphdr *ti,
i = q->ti_seq + q->ti_len - ti->ti_seq;
if (i > 0) {
if (i >= ti->ti_len) {
- m_freem(m);
+ m_free(m);
/*
* Try to present any queued data
* at the left window edge to the user.
@@ -170,7 +170,7 @@ tcp_reass(register struct tcpcb *tp, register struct tcpiphdr *ti,
q = tcpiphdr_next(q);
m = tcpiphdr_prev(q)->ti_mbuf;
remque(tcpiphdr2qlink(tcpiphdr_prev(q)));
- m_freem(m);
+ m_free(m);
}
/*
@@ -197,7 +197,7 @@ present:
m = ti->ti_mbuf;
ti = tcpiphdr_next(ti);
if (so->so_state & SS_FCANTSENDMORE)
- m_freem(m);
+ m_free(m);
else {
if (so->so_emu) {
if (tcp_emu(so,m)) sbappend(so, m);
@@ -451,7 +451,7 @@ findso:
acked = ti->ti_ack - tp->snd_una;
sbdrop(&so->so_snd, acked);
tp->snd_una = ti->ti_ack;
- m_freem(m);
+ m_free(m);
/*
* If all outstanding data are acked, stop
@@ -1260,7 +1260,7 @@ dropafterack:
*/
if (tiflags & TH_RST)
goto drop;
- m_freem(m);
+ m_free(m);
tp->t_flags |= TF_ACKNOW;
(void) tcp_output(tp);
return;
diff --git a/slirp/tcp_subr.c b/slirp/tcp_subr.c
index b661d26..61079b1 100644
--- a/slirp/tcp_subr.c
+++ b/slirp/tcp_subr.c
@@ -250,7 +250,7 @@ tcp_close(struct tcpcb *tp)
t = tcpiphdr_next(t);
m = tcpiphdr_prev(t)->ti_mbuf;
remque(tcpiphdr2qlink(tcpiphdr_prev(t)));
- m_freem(m);
+ m_free(m);
}
free(tp);
so->so_tcpcb = NULL;
diff --git a/slirp/udp.c b/slirp/udp.c
index 02b3793..5b060f3 100644
--- a/slirp/udp.c
+++ b/slirp/udp.c
@@ -120,23 +120,26 @@ udp_input(register struct mbuf *m, int iphlen)
/*
* handle DHCP/BOOTP
*/
- if (ntohs(uh->uh_dport) == BOOTP_SERVER) {
- bootp_input(m);
- goto bad;
- }
-
- if (slirp->restricted) {
- goto bad;
- }
+ if (ntohs(uh->uh_dport) == BOOTP_SERVER &&
+ (ip->ip_dst.s_addr == slirp->vhost_addr.s_addr ||
+ ip->ip_dst.s_addr == 0xffffffff)) {
+ bootp_input(m);
+ goto bad;
+ }
/*
* handle TFTP
*/
- if (ntohs(uh->uh_dport) == TFTP_SERVER) {
+ if (ntohs(uh->uh_dport) == TFTP_SERVER &&
+ ip->ip_dst.s_addr == slirp->vhost_addr.s_addr) {
tftp_input(m);
goto bad;
}
+ if (slirp->restricted) {
+ goto bad;
+ }
+
/*
* Locate pcb for datagram.
*/
@@ -219,7 +222,7 @@ udp_input(register struct mbuf *m, int iphlen)
return;
bad:
- m_freem(m);
+ m_free(m);
return;
}
diff --git a/softmmu-semi.h b/softmmu-semi.h
index 79278cc..86a9f8a 100644
--- a/softmmu-semi.h
+++ b/softmmu-semi.h
@@ -4,7 +4,7 @@
*
* Copyright (c) 2007 CodeSourcery.
*
- * This code is licenced under the GPL
+ * This code is licensed under the GPL
*/
static inline uint32_t softmmu_tget32(CPUState *env, uint32_t addr)
diff --git a/spice-qemu-char.c b/spice-qemu-char.c
index 605c241..95bf6b6 100644
--- a/spice-qemu-char.c
+++ b/spice-qemu-char.c
@@ -159,7 +159,7 @@ static void print_allowed_subtypes(void)
fprintf(stderr, "\n");
}
-CharDriverState *qemu_chr_open_spice(QemuOpts *opts)
+int qemu_chr_open_spice(QemuOpts *opts, CharDriverState **_chr)
{
CharDriverState *chr;
SpiceCharDriver *s;
@@ -171,7 +171,7 @@ CharDriverState *qemu_chr_open_spice(QemuOpts *opts)
if (name == NULL) {
fprintf(stderr, "spice-qemu-char: missing name parameter\n");
print_allowed_subtypes();
- return NULL;
+ return -EINVAL;
}
for(;*psubtype != NULL; ++psubtype) {
if (strcmp(name, *psubtype) == 0) {
@@ -182,7 +182,7 @@ CharDriverState *qemu_chr_open_spice(QemuOpts *opts)
if (subtype == NULL) {
fprintf(stderr, "spice-qemu-char: unsupported name\n");
print_allowed_subtypes();
- return NULL;
+ return -EINVAL;
}
chr = qemu_mallocz(sizeof(CharDriverState));
@@ -199,5 +199,6 @@ CharDriverState *qemu_chr_open_spice(QemuOpts *opts)
qemu_chr_generic_open(chr);
- return chr;
+ *_chr = chr;
+ return 0;
}
diff --git a/sysemu.h b/sysemu.h
index d3013f5..bd830e5 100644
--- a/sysemu.h
+++ b/sysemu.h
@@ -123,6 +123,9 @@ extern int no_shutdown;
extern int semihosting_enabled;
extern int old_param;
extern int boot_menu;
+extern uint8_t *boot_splash_filedata;
+extern int boot_splash_filedata_size;
+extern uint8_t qemu_extra_params_fw[2];
extern QEMUClock *rtc_clock;
#define MAX_NODES 64
diff --git a/target-alpha/cpu.h b/target-alpha/cpu.h
index 78caa79..919be12 100644
--- a/target-alpha/cpu.h
+++ b/target-alpha/cpu.h
@@ -434,8 +434,9 @@ uint64_t cpu_alpha_load_fpcr (CPUState *env);
void cpu_alpha_store_fpcr (CPUState *env, uint64_t val);
#ifndef CONFIG_USER_ONLY
void swap_shadow_regs(CPUState *env);
-extern QEMU_NORETURN void do_unassigned_access(target_phys_addr_t addr,
- int, int, int, int);
+QEMU_NORETURN void cpu_unassigned_access(CPUState *env1,
+ target_phys_addr_t addr, int is_write,
+ int is_exec, int unused, int size);
#endif
/* Bits in TB->FLAGS that control how translation is processed. */
diff --git a/target-alpha/exec.h b/target-alpha/exec.h
deleted file mode 100644
index afb01d3..0000000
--- a/target-alpha/exec.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Alpha emulation cpu run-time definitions for qemu.
- *
- * Copyright (c) 2007 Jocelyn Mayer
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#if !defined (__ALPHA_EXEC_H__)
-#define __ALPHA_EXEC_H__
-
-#include "config.h"
-
-#include "dyngen-exec.h"
-
-#define TARGET_LONG_BITS 64
-
-register struct CPUAlphaState *env asm(AREG0);
-
-#define FP_STATUS (env->fp_status)
-
-#include "cpu.h"
-
-#if !defined(CONFIG_USER_ONLY)
-#include "softmmu_exec.h"
-#endif /* !defined(CONFIG_USER_ONLY) */
-
-#endif /* !defined (__ALPHA_EXEC_H__) */
diff --git a/target-alpha/op_helper.c b/target-alpha/op_helper.c
index 51d1bd7..c2bb679 100644
--- a/target-alpha/op_helper.c
+++ b/target-alpha/op_helper.c
@@ -17,12 +17,15 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
-#include "exec.h"
+#include "cpu.h"
+#include "dyngen-exec.h"
#include "host-utils.h"
#include "softfloat.h"
#include "helper.h"
#include "qemu-timer.h"
+#define FP_STATUS (env->fp_status)
+
/*****************************************************************************/
/* Exceptions processing helpers */
@@ -1301,14 +1304,18 @@ static void QEMU_NORETURN do_unaligned_access(target_ulong addr, int is_write,
helper_excp(EXCP_UNALIGN, 0);
}
-void QEMU_NORETURN do_unassigned_access(target_phys_addr_t addr, int is_write,
- int is_exec, int unused, int size)
+void QEMU_NORETURN cpu_unassigned_access(CPUState *env1,
+ target_phys_addr_t addr, int is_write,
+ int is_exec, int unused, int size)
{
+ env = env1;
env->trap_arg0 = addr;
env->trap_arg1 = is_write;
dynamic_excp(EXCP_MCHK, 0);
}
+#include "softmmu_exec.h"
+
#define MMUSUFFIX _mmu
#define ALIGNED_ONLY
diff --git a/target-arm/cpu.h b/target-arm/cpu.h
index 1022a03..adef427 100644
--- a/target-arm/cpu.h
+++ b/target-arm/cpu.h
@@ -133,6 +133,12 @@ typedef struct CPUARMState {
uint32_t c7_par; /* Translation result. */
uint32_t c9_insn; /* Cache lockdown registers. */
uint32_t c9_data;
+ uint32_t c9_pmcr; /* performance monitor control register */
+ uint32_t c9_pmcnten; /* perf monitor counter enables */
+ uint32_t c9_pmovsr; /* perf monitor overflow status */
+ uint32_t c9_pmxevtyper; /* perf monitor event type */
+ uint32_t c9_pmuserenr; /* perf monitor user enable */
+ uint32_t c9_pminten; /* perf monitor interrupt enables */
uint32_t c13_fcse; /* FCSE PID. */
uint32_t c13_context; /* Context ID. */
uint32_t c13_tls1; /* User RW Thread register. */
@@ -438,7 +444,7 @@ void cpu_arm_set_cp_io(CPUARMState *env, int cpnum,
#define cpu_signal_handler cpu_arm_signal_handler
#define cpu_list arm_cpu_list
-#define CPU_SAVE_VERSION 3
+#define CPU_SAVE_VERSION 4
/* MMU modes definitions */
#define MMU_MODE0_SUFFIX _kernel
diff --git a/target-arm/exec.h b/target-arm/exec.h
deleted file mode 100644
index 6793288..0000000
--- a/target-arm/exec.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * ARM execution defines
- *
- * Copyright (c) 2003 Fabrice Bellard
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
- */
-#include "config.h"
-#include "dyngen-exec.h"
-
-register struct CPUARMState *env asm(AREG0);
-
-#include "cpu.h"
-
-#if !defined(CONFIG_USER_ONLY)
-#include "softmmu_exec.h"
-#endif
-
-void raise_exception(int);
diff --git a/target-arm/helper.c b/target-arm/helper.c
index f4d12aa..ae4f334 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -269,6 +269,10 @@ void cpu_reset(CPUARMState *env)
}
env->vfp.xregs[ARM_VFP_FPEXC] = 0;
env->cp15.c2_base_mask = 0xffffc000u;
+ /* v7 performance monitor control register: same implementor
+ * field as main ID register, and we implement no event counters.
+ */
+ env->cp15.c9_pmcr = (id & 0xff000000);
#endif
set_flush_to_zero(1, &env->vfp.standard_fp_status);
set_flush_inputs_to_zero(1, &env->vfp.standard_fp_status);
@@ -1587,6 +1591,81 @@ void HELPER(set_cp15)(CPUState *env, uint32_t insn, uint32_t val)
case 1: /* TCM memory region registers. */
/* Not implemented. */
goto bad_reg;
+ case 12: /* Performance monitor control */
+ /* Performance monitors are implementation defined in v7,
+ * but with an ARM recommended set of registers, which we
+ * follow (although we don't actually implement any counters)
+ */
+ if (!arm_feature(env, ARM_FEATURE_V7)) {
+ goto bad_reg;
+ }
+ switch (op2) {
+ case 0: /* performance monitor control register */
+ /* only the DP, X, D and E bits are writable */
+ env->cp15.c9_pmcr &= ~0x39;
+ env->cp15.c9_pmcr |= (val & 0x39);
+ break;
+ case 1: /* Count enable set register */
+ val &= (1 << 31);
+ env->cp15.c9_pmcnten |= val;
+ break;
+ case 2: /* Count enable clear */
+ val &= (1 << 31);
+ env->cp15.c9_pmcnten &= ~val;
+ break;
+ case 3: /* Overflow flag status */
+ env->cp15.c9_pmovsr &= ~val;
+ break;
+ case 4: /* Software increment */
+ /* RAZ/WI since we don't implement the software-count event */
+ break;
+ case 5: /* Event counter selection register */
+ /* Since we don't implement any events, writing to this register
+ * is actually UNPREDICTABLE. So we choose to RAZ/WI.
+ */
+ break;
+ default:
+ goto bad_reg;
+ }
+ break;
+ case 13: /* Performance counters */
+ if (!arm_feature(env, ARM_FEATURE_V7)) {
+ goto bad_reg;
+ }
+ switch (op2) {
+ case 0: /* Cycle count register: not implemented, so RAZ/WI */
+ break;
+ case 1: /* Event type select */
+ env->cp15.c9_pmxevtyper = val & 0xff;
+ break;
+ case 2: /* Event count register */
+ /* Unimplemented (we have no events), RAZ/WI */
+ break;
+ default:
+ goto bad_reg;
+ }
+ break;
+ case 14: /* Performance monitor control */
+ if (!arm_feature(env, ARM_FEATURE_V7)) {
+ goto bad_reg;
+ }
+ switch (op2) {
+ case 0: /* user enable */
+ env->cp15.c9_pmuserenr = val & 1;
+ /* changes access rights for cp registers, so flush tbs */
+ tb_flush(env);
+ break;
+ case 1: /* interrupt enable set */
+ /* We have no event counters so only the C bit can be changed */
+ val &= (1 << 31);
+ env->cp15.c9_pminten |= val;
+ break;
+ case 2: /* interrupt enable clear */
+ val &= (1 << 31);
+ env->cp15.c9_pminten &= ~val;
+ break;
+ }
+ break;
default:
goto bad_reg;
}
@@ -1878,27 +1957,81 @@ uint32_t HELPER(get_cp15)(CPUState *env, uint32_t insn)
return 0;
case 8: /* MMU TLB control. */
goto bad_reg;
- case 9: /* Cache lockdown. */
- switch (op1) {
- case 0: /* L1 cache. */
- if (arm_feature(env, ARM_FEATURE_OMAPCP))
- return 0;
+ case 9:
+ switch (crm) {
+ case 0: /* Cache lockdown */
+ switch (op1) {
+ case 0: /* L1 cache. */
+ if (arm_feature(env, ARM_FEATURE_OMAPCP)) {
+ return 0;
+ }
+ switch (op2) {
+ case 0:
+ return env->cp15.c9_data;
+ case 1:
+ return env->cp15.c9_insn;
+ default:
+ goto bad_reg;
+ }
+ case 1: /* L2 cache */
+ if (crm != 0) {
+ goto bad_reg;
+ }
+ /* L2 Lockdown and Auxiliary control. */
+ return 0;
+ default:
+ goto bad_reg;
+ }
+ break;
+ case 12: /* Performance monitor control */
+ if (!arm_feature(env, ARM_FEATURE_V7)) {
+ goto bad_reg;
+ }
switch (op2) {
- case 0:
- return env->cp15.c9_data;
- case 1:
- return env->cp15.c9_insn;
+ case 0: /* performance monitor control register */
+ return env->cp15.c9_pmcr;
+ case 1: /* count enable set */
+ case 2: /* count enable clear */
+ return env->cp15.c9_pmcnten;
+ case 3: /* overflow flag status */
+ return env->cp15.c9_pmovsr;
+ case 4: /* software increment */
+ case 5: /* event counter selection register */
+ return 0; /* Unimplemented, RAZ/WI */
default:
goto bad_reg;
}
- case 1: /* L2 cache */
- if (crm != 0)
+ case 13: /* Performance counters */
+ if (!arm_feature(env, ARM_FEATURE_V7)) {
+ goto bad_reg;
+ }
+ switch (op2) {
+ case 1: /* Event type select */
+ return env->cp15.c9_pmxevtyper;
+ case 0: /* Cycle count register */
+ case 2: /* Event count register */
+ /* Unimplemented, so RAZ/WI */
+ return 0;
+ default:
+ goto bad_reg;
+ }
+ case 14: /* Performance monitor control */
+ if (!arm_feature(env, ARM_FEATURE_V7)) {
goto bad_reg;
- /* L2 Lockdown and Auxiliary control. */
- return 0;
+ }
+ switch (op2) {
+ case 0: /* user enable */
+ return env->cp15.c9_pmuserenr;
+ case 1: /* interrupt enable set */
+ case 2: /* interrupt enable clear */
+ return env->cp15.c9_pminten;
+ default:
+ goto bad_reg;
+ }
default:
goto bad_reg;
}
+ break;
case 10: /* MMU TLB lockdown. */
/* ??? TLB lockdown not implemented. */
return 0;
@@ -1994,11 +2127,11 @@ uint32_t HELPER(v7m_mrs)(CPUState *env, uint32_t reg)
return env->v7m.current_sp ? env->regs[13] : env->v7m.other_sp;
case 16: /* PRIMASK */
return (env->uncached_cpsr & CPSR_I) != 0;
- case 17: /* FAULTMASK */
- return (env->uncached_cpsr & CPSR_F) != 0;
- case 18: /* BASEPRI */
- case 19: /* BASEPRI_MAX */
+ case 17: /* BASEPRI */
+ case 18: /* BASEPRI_MAX */
return env->v7m.basepri;
+ case 19: /* FAULTMASK */
+ return (env->uncached_cpsr & CPSR_F) != 0;
case 20: /* CONTROL */
return env->v7m.control;
default:
@@ -2050,20 +2183,20 @@ void HELPER(v7m_msr)(CPUState *env, uint32_t reg, uint32_t val)
else
env->uncached_cpsr &= ~CPSR_I;
break;
- case 17: /* FAULTMASK */
- if (val & 1)
- env->uncached_cpsr |= CPSR_F;
- else
- env->uncached_cpsr &= ~CPSR_F;
- break;
- case 18: /* BASEPRI */
+ case 17: /* BASEPRI */
env->v7m.basepri = val & 0xff;
break;
- case 19: /* BASEPRI_MAX */
+ case 18: /* BASEPRI_MAX */
val &= 0xff;
if (val != 0 && (val < env->v7m.basepri || env->v7m.basepri == 0))
env->v7m.basepri = val;
break;
+ case 19: /* FAULTMASK */
+ if (val & 1)
+ env->uncached_cpsr |= CPSR_F;
+ else
+ env->uncached_cpsr &= ~CPSR_F;
+ break;
case 20: /* CONTROL */
env->v7m.control = val & 3;
switch_v7m_sp(env, (val & 2) != 0);
@@ -2452,13 +2585,15 @@ void vfp_set_fpscr(CPUState *env, uint32_t val)
#define VFP_HELPER(name, p) HELPER(glue(glue(vfp_,name),p))
#define VFP_BINOP(name) \
-float32 VFP_HELPER(name, s)(float32 a, float32 b, CPUState *env) \
+float32 VFP_HELPER(name, s)(float32 a, float32 b, void *fpstp) \
{ \
- return float32_ ## name (a, b, &env->vfp.fp_status); \
+ float_status *fpst = fpstp; \
+ return float32_ ## name(a, b, fpst); \
} \
-float64 VFP_HELPER(name, d)(float64 a, float64 b, CPUState *env) \
+float64 VFP_HELPER(name, d)(float64 a, float64 b, void *fpstp) \
{ \
- return float64_ ## name (a, b, &env->vfp.fp_status); \
+ float_status *fpst = fpstp; \
+ return float64_ ## name(a, b, fpst); \
}
VFP_BINOP(add)
VFP_BINOP(sub)
diff --git a/target-arm/helper.h b/target-arm/helper.h
index 7d5533f..3ad1cb0 100644
--- a/target-arm/helper.h
+++ b/target-arm/helper.h
@@ -74,14 +74,14 @@ DEF_HELPER_2(set_user_reg, void, i32, i32)
DEF_HELPER_1(vfp_get_fpscr, i32, env)
DEF_HELPER_2(vfp_set_fpscr, void, env, i32)
-DEF_HELPER_3(vfp_adds, f32, f32, f32, env)
-DEF_HELPER_3(vfp_addd, f64, f64, f64, env)
-DEF_HELPER_3(vfp_subs, f32, f32, f32, env)
-DEF_HELPER_3(vfp_subd, f64, f64, f64, env)
-DEF_HELPER_3(vfp_muls, f32, f32, f32, env)
-DEF_HELPER_3(vfp_muld, f64, f64, f64, env)
-DEF_HELPER_3(vfp_divs, f32, f32, f32, env)
-DEF_HELPER_3(vfp_divd, f64, f64, f64, env)
+DEF_HELPER_3(vfp_adds, f32, f32, f32, ptr)
+DEF_HELPER_3(vfp_addd, f64, f64, f64, ptr)
+DEF_HELPER_3(vfp_subs, f32, f32, f32, ptr)
+DEF_HELPER_3(vfp_subd, f64, f64, f64, ptr)
+DEF_HELPER_3(vfp_muls, f32, f32, f32, ptr)
+DEF_HELPER_3(vfp_muld, f64, f64, f64, ptr)
+DEF_HELPER_3(vfp_divs, f32, f32, f32, ptr)
+DEF_HELPER_3(vfp_divd, f64, f64, f64, ptr)
DEF_HELPER_1(vfp_negs, f32, f32)
DEF_HELPER_1(vfp_negd, f64, f64)
DEF_HELPER_1(vfp_abss, f32, f32)
@@ -154,22 +154,22 @@ DEF_HELPER_2(sar_cc, i32, i32, i32)
DEF_HELPER_2(ror_cc, i32, i32, i32)
/* neon_helper.c */
-DEF_HELPER_2(neon_qadd_u8, i32, i32, i32)
-DEF_HELPER_2(neon_qadd_s8, i32, i32, i32)
-DEF_HELPER_2(neon_qadd_u16, i32, i32, i32)
-DEF_HELPER_2(neon_qadd_s16, i32, i32, i32)
-DEF_HELPER_2(neon_qadd_u32, i32, i32, i32)
-DEF_HELPER_2(neon_qadd_s32, i32, i32, i32)
-DEF_HELPER_2(neon_qsub_u8, i32, i32, i32)
-DEF_HELPER_2(neon_qsub_s8, i32, i32, i32)
-DEF_HELPER_2(neon_qsub_u16, i32, i32, i32)
-DEF_HELPER_2(neon_qsub_s16, i32, i32, i32)
-DEF_HELPER_2(neon_qsub_u32, i32, i32, i32)
-DEF_HELPER_2(neon_qsub_s32, i32, i32, i32)
-DEF_HELPER_2(neon_qadd_u64, i64, i64, i64)
-DEF_HELPER_2(neon_qadd_s64, i64, i64, i64)
-DEF_HELPER_2(neon_qsub_u64, i64, i64, i64)
-DEF_HELPER_2(neon_qsub_s64, i64, i64, i64)
+DEF_HELPER_3(neon_qadd_u8, i32, env, i32, i32)
+DEF_HELPER_3(neon_qadd_s8, i32, env, i32, i32)
+DEF_HELPER_3(neon_qadd_u16, i32, env, i32, i32)
+DEF_HELPER_3(neon_qadd_s16, i32, env, i32, i32)
+DEF_HELPER_3(neon_qadd_u32, i32, env, i32, i32)
+DEF_HELPER_3(neon_qadd_s32, i32, env, i32, i32)
+DEF_HELPER_3(neon_qsub_u8, i32, env, i32, i32)
+DEF_HELPER_3(neon_qsub_s8, i32, env, i32, i32)
+DEF_HELPER_3(neon_qsub_u16, i32, env, i32, i32)
+DEF_HELPER_3(neon_qsub_s16, i32, env, i32, i32)
+DEF_HELPER_3(neon_qsub_u32, i32, env, i32, i32)
+DEF_HELPER_3(neon_qsub_s32, i32, env, i32, i32)
+DEF_HELPER_3(neon_qadd_u64, i64, env, i64, i64)
+DEF_HELPER_3(neon_qadd_s64, i64, env, i64, i64)
+DEF_HELPER_3(neon_qsub_u64, i64, env, i64, i64)
+DEF_HELPER_3(neon_qsub_s64, i64, env, i64, i64)
DEF_HELPER_2(neon_hadd_s8, i32, i32, i32)
DEF_HELPER_2(neon_hadd_u8, i32, i32, i32)
@@ -247,26 +247,26 @@ DEF_HELPER_2(neon_rshl_u32, i32, i32, i32)
DEF_HELPER_2(neon_rshl_s32, i32, i32, i32)
DEF_HELPER_2(neon_rshl_u64, i64, i64, i64)
DEF_HELPER_2(neon_rshl_s64, i64, i64, i64)
-DEF_HELPER_2(neon_qshl_u8, i32, i32, i32)
-DEF_HELPER_2(neon_qshl_s8, i32, i32, i32)
-DEF_HELPER_2(neon_qshl_u16, i32, i32, i32)
-DEF_HELPER_2(neon_qshl_s16, i32, i32, i32)
-DEF_HELPER_2(neon_qshl_u32, i32, i32, i32)
-DEF_HELPER_2(neon_qshl_s32, i32, i32, i32)
-DEF_HELPER_2(neon_qshl_u64, i64, i64, i64)
-DEF_HELPER_2(neon_qshl_s64, i64, i64, i64)
-DEF_HELPER_2(neon_qshlu_s8, i32, i32, i32);
-DEF_HELPER_2(neon_qshlu_s16, i32, i32, i32);
-DEF_HELPER_2(neon_qshlu_s32, i32, i32, i32);
-DEF_HELPER_2(neon_qshlu_s64, i64, i64, i64);
-DEF_HELPER_2(neon_qrshl_u8, i32, i32, i32)
-DEF_HELPER_2(neon_qrshl_s8, i32, i32, i32)
-DEF_HELPER_2(neon_qrshl_u16, i32, i32, i32)
-DEF_HELPER_2(neon_qrshl_s16, i32, i32, i32)
-DEF_HELPER_2(neon_qrshl_u32, i32, i32, i32)
-DEF_HELPER_2(neon_qrshl_s32, i32, i32, i32)
-DEF_HELPER_2(neon_qrshl_u64, i64, i64, i64)
-DEF_HELPER_2(neon_qrshl_s64, i64, i64, i64)
+DEF_HELPER_3(neon_qshl_u8, i32, env, i32, i32)
+DEF_HELPER_3(neon_qshl_s8, i32, env, i32, i32)
+DEF_HELPER_3(neon_qshl_u16, i32, env, i32, i32)
+DEF_HELPER_3(neon_qshl_s16, i32, env, i32, i32)
+DEF_HELPER_3(neon_qshl_u32, i32, env, i32, i32)
+DEF_HELPER_3(neon_qshl_s32, i32, env, i32, i32)
+DEF_HELPER_3(neon_qshl_u64, i64, env, i64, i64)
+DEF_HELPER_3(neon_qshl_s64, i64, env, i64, i64)
+DEF_HELPER_3(neon_qshlu_s8, i32, env, i32, i32);
+DEF_HELPER_3(neon_qshlu_s16, i32, env, i32, i32);
+DEF_HELPER_3(neon_qshlu_s32, i32, env, i32, i32);
+DEF_HELPER_3(neon_qshlu_s64, i64, env, i64, i64);
+DEF_HELPER_3(neon_qrshl_u8, i32, env, i32, i32)
+DEF_HELPER_3(neon_qrshl_s8, i32, env, i32, i32)
+DEF_HELPER_3(neon_qrshl_u16, i32, env, i32, i32)
+DEF_HELPER_3(neon_qrshl_s16, i32, env, i32, i32)
+DEF_HELPER_3(neon_qrshl_u32, i32, env, i32, i32)
+DEF_HELPER_3(neon_qrshl_s32, i32, env, i32, i32)
+DEF_HELPER_3(neon_qrshl_u64, i64, env, i64, i64)
+DEF_HELPER_3(neon_qrshl_s64, i64, env, i64, i64)
DEF_HELPER_2(neon_add_u8, i32, i32, i32)
DEF_HELPER_2(neon_add_u16, i32, i32, i32)
@@ -295,22 +295,22 @@ DEF_HELPER_1(neon_cls_s16, i32, i32)
DEF_HELPER_1(neon_cls_s32, i32, i32)
DEF_HELPER_1(neon_cnt_u8, i32, i32)
-DEF_HELPER_2(neon_qdmulh_s16, i32, i32, i32)
-DEF_HELPER_2(neon_qrdmulh_s16, i32, i32, i32)
-DEF_HELPER_2(neon_qdmulh_s32, i32, i32, i32)
-DEF_HELPER_2(neon_qrdmulh_s32, i32, i32, i32)
+DEF_HELPER_3(neon_qdmulh_s16, i32, env, i32, i32)
+DEF_HELPER_3(neon_qrdmulh_s16, i32, env, i32, i32)
+DEF_HELPER_3(neon_qdmulh_s32, i32, env, i32, i32)
+DEF_HELPER_3(neon_qrdmulh_s32, i32, env, i32, i32)
DEF_HELPER_1(neon_narrow_u8, i32, i64)
DEF_HELPER_1(neon_narrow_u16, i32, i64)
-DEF_HELPER_1(neon_unarrow_sat8, i32, i64)
-DEF_HELPER_1(neon_narrow_sat_u8, i32, i64)
-DEF_HELPER_1(neon_narrow_sat_s8, i32, i64)
-DEF_HELPER_1(neon_unarrow_sat16, i32, i64)
-DEF_HELPER_1(neon_narrow_sat_u16, i32, i64)
-DEF_HELPER_1(neon_narrow_sat_s16, i32, i64)
-DEF_HELPER_1(neon_unarrow_sat32, i32, i64)
-DEF_HELPER_1(neon_narrow_sat_u32, i32, i64)
-DEF_HELPER_1(neon_narrow_sat_s32, i32, i64)
+DEF_HELPER_2(neon_unarrow_sat8, i32, env, i64)
+DEF_HELPER_2(neon_narrow_sat_u8, i32, env, i64)
+DEF_HELPER_2(neon_narrow_sat_s8, i32, env, i64)
+DEF_HELPER_2(neon_unarrow_sat16, i32, env, i64)
+DEF_HELPER_2(neon_narrow_sat_u16, i32, env, i64)
+DEF_HELPER_2(neon_narrow_sat_s16, i32, env, i64)
+DEF_HELPER_2(neon_unarrow_sat32, i32, env, i64)
+DEF_HELPER_2(neon_narrow_sat_u32, i32, env, i64)
+DEF_HELPER_2(neon_narrow_sat_s32, i32, env, i64)
DEF_HELPER_1(neon_narrow_high_u8, i32, i64)
DEF_HELPER_1(neon_narrow_high_u16, i32, i64)
DEF_HELPER_1(neon_narrow_round_high_u8, i32, i64)
@@ -326,8 +326,8 @@ DEF_HELPER_2(neon_paddl_u16, i64, i64, i64)
DEF_HELPER_2(neon_paddl_u32, i64, i64, i64)
DEF_HELPER_2(neon_subl_u16, i64, i64, i64)
DEF_HELPER_2(neon_subl_u32, i64, i64, i64)
-DEF_HELPER_2(neon_addl_saturate_s32, i64, i64, i64)
-DEF_HELPER_2(neon_addl_saturate_s64, i64, i64, i64)
+DEF_HELPER_3(neon_addl_saturate_s32, i64, env, i64, i64)
+DEF_HELPER_3(neon_addl_saturate_s64, i64, env, i64, i64)
DEF_HELPER_2(neon_abdl_u16, i64, i32, i32)
DEF_HELPER_2(neon_abdl_s16, i64, i32, i32)
DEF_HELPER_2(neon_abdl_u32, i64, i32, i32)
@@ -343,24 +343,21 @@ DEF_HELPER_1(neon_negl_u16, i64, i64)
DEF_HELPER_1(neon_negl_u32, i64, i64)
DEF_HELPER_1(neon_negl_u64, i64, i64)
-DEF_HELPER_1(neon_qabs_s8, i32, i32)
-DEF_HELPER_1(neon_qabs_s16, i32, i32)
-DEF_HELPER_1(neon_qabs_s32, i32, i32)
-DEF_HELPER_1(neon_qneg_s8, i32, i32)
-DEF_HELPER_1(neon_qneg_s16, i32, i32)
-DEF_HELPER_1(neon_qneg_s32, i32, i32)
-
-DEF_HELPER_2(neon_min_f32, i32, i32, i32)
-DEF_HELPER_2(neon_max_f32, i32, i32, i32)
-DEF_HELPER_2(neon_abd_f32, i32, i32, i32)
-DEF_HELPER_2(neon_add_f32, i32, i32, i32)
-DEF_HELPER_2(neon_sub_f32, i32, i32, i32)
-DEF_HELPER_2(neon_mul_f32, i32, i32, i32)
-DEF_HELPER_2(neon_ceq_f32, i32, i32, i32)
-DEF_HELPER_2(neon_cge_f32, i32, i32, i32)
-DEF_HELPER_2(neon_cgt_f32, i32, i32, i32)
-DEF_HELPER_2(neon_acge_f32, i32, i32, i32)
-DEF_HELPER_2(neon_acgt_f32, i32, i32, i32)
+DEF_HELPER_2(neon_qabs_s8, i32, env, i32)
+DEF_HELPER_2(neon_qabs_s16, i32, env, i32)
+DEF_HELPER_2(neon_qabs_s32, i32, env, i32)
+DEF_HELPER_2(neon_qneg_s8, i32, env, i32)
+DEF_HELPER_2(neon_qneg_s16, i32, env, i32)
+DEF_HELPER_2(neon_qneg_s32, i32, env, i32)
+
+DEF_HELPER_3(neon_min_f32, i32, i32, i32, ptr)
+DEF_HELPER_3(neon_max_f32, i32, i32, i32, ptr)
+DEF_HELPER_3(neon_abd_f32, i32, i32, i32, ptr)
+DEF_HELPER_3(neon_ceq_f32, i32, i32, i32, ptr)
+DEF_HELPER_3(neon_cge_f32, i32, i32, i32, ptr)
+DEF_HELPER_3(neon_cgt_f32, i32, i32, i32, ptr)
+DEF_HELPER_3(neon_acge_f32, i32, i32, i32, ptr)
+DEF_HELPER_3(neon_acgt_f32, i32, i32, i32, ptr)
/* iwmmxt_helper.c */
DEF_HELPER_2(iwmmxt_maddsq, i64, i64, i64)
@@ -375,47 +372,47 @@ DEF_HELPER_2(iwmmxt_macsw, i64, i64, i64)
DEF_HELPER_2(iwmmxt_macuw, i64, i64, i64)
DEF_HELPER_1(iwmmxt_setpsr_nz, i32, i64)
-#define DEF_IWMMXT_HELPER_SIZE(name) \
-DEF_HELPER_2(iwmmxt_##name##b, i64, i64, i64) \
-DEF_HELPER_2(iwmmxt_##name##w, i64, i64, i64) \
-DEF_HELPER_2(iwmmxt_##name##l, i64, i64, i64) \
-
-DEF_IWMMXT_HELPER_SIZE(unpackl)
-DEF_IWMMXT_HELPER_SIZE(unpackh)
-
-DEF_HELPER_1(iwmmxt_unpacklub, i64, i64)
-DEF_HELPER_1(iwmmxt_unpackluw, i64, i64)
-DEF_HELPER_1(iwmmxt_unpacklul, i64, i64)
-DEF_HELPER_1(iwmmxt_unpackhub, i64, i64)
-DEF_HELPER_1(iwmmxt_unpackhuw, i64, i64)
-DEF_HELPER_1(iwmmxt_unpackhul, i64, i64)
-DEF_HELPER_1(iwmmxt_unpacklsb, i64, i64)
-DEF_HELPER_1(iwmmxt_unpacklsw, i64, i64)
-DEF_HELPER_1(iwmmxt_unpacklsl, i64, i64)
-DEF_HELPER_1(iwmmxt_unpackhsb, i64, i64)
-DEF_HELPER_1(iwmmxt_unpackhsw, i64, i64)
-DEF_HELPER_1(iwmmxt_unpackhsl, i64, i64)
-
-DEF_IWMMXT_HELPER_SIZE(cmpeq)
-DEF_IWMMXT_HELPER_SIZE(cmpgtu)
-DEF_IWMMXT_HELPER_SIZE(cmpgts)
-
-DEF_IWMMXT_HELPER_SIZE(mins)
-DEF_IWMMXT_HELPER_SIZE(minu)
-DEF_IWMMXT_HELPER_SIZE(maxs)
-DEF_IWMMXT_HELPER_SIZE(maxu)
-
-DEF_IWMMXT_HELPER_SIZE(subn)
-DEF_IWMMXT_HELPER_SIZE(addn)
-DEF_IWMMXT_HELPER_SIZE(subu)
-DEF_IWMMXT_HELPER_SIZE(addu)
-DEF_IWMMXT_HELPER_SIZE(subs)
-DEF_IWMMXT_HELPER_SIZE(adds)
-
-DEF_HELPER_2(iwmmxt_avgb0, i64, i64, i64)
-DEF_HELPER_2(iwmmxt_avgb1, i64, i64, i64)
-DEF_HELPER_2(iwmmxt_avgw0, i64, i64, i64)
-DEF_HELPER_2(iwmmxt_avgw1, i64, i64, i64)
+#define DEF_IWMMXT_HELPER_SIZE_ENV(name) \
+DEF_HELPER_3(iwmmxt_##name##b, i64, env, i64, i64) \
+DEF_HELPER_3(iwmmxt_##name##w, i64, env, i64, i64) \
+DEF_HELPER_3(iwmmxt_##name##l, i64, env, i64, i64) \
+
+DEF_IWMMXT_HELPER_SIZE_ENV(unpackl)
+DEF_IWMMXT_HELPER_SIZE_ENV(unpackh)
+
+DEF_HELPER_2(iwmmxt_unpacklub, i64, env, i64)
+DEF_HELPER_2(iwmmxt_unpackluw, i64, env, i64)
+DEF_HELPER_2(iwmmxt_unpacklul, i64, env, i64)
+DEF_HELPER_2(iwmmxt_unpackhub, i64, env, i64)
+DEF_HELPER_2(iwmmxt_unpackhuw, i64, env, i64)
+DEF_HELPER_2(iwmmxt_unpackhul, i64, env, i64)
+DEF_HELPER_2(iwmmxt_unpacklsb, i64, env, i64)
+DEF_HELPER_2(iwmmxt_unpacklsw, i64, env, i64)
+DEF_HELPER_2(iwmmxt_unpacklsl, i64, env, i64)
+DEF_HELPER_2(iwmmxt_unpackhsb, i64, env, i64)
+DEF_HELPER_2(iwmmxt_unpackhsw, i64, env, i64)
+DEF_HELPER_2(iwmmxt_unpackhsl, i64, env, i64)
+
+DEF_IWMMXT_HELPER_SIZE_ENV(cmpeq)
+DEF_IWMMXT_HELPER_SIZE_ENV(cmpgtu)
+DEF_IWMMXT_HELPER_SIZE_ENV(cmpgts)
+
+DEF_IWMMXT_HELPER_SIZE_ENV(mins)
+DEF_IWMMXT_HELPER_SIZE_ENV(minu)
+DEF_IWMMXT_HELPER_SIZE_ENV(maxs)
+DEF_IWMMXT_HELPER_SIZE_ENV(maxu)
+
+DEF_IWMMXT_HELPER_SIZE_ENV(subn)
+DEF_IWMMXT_HELPER_SIZE_ENV(addn)
+DEF_IWMMXT_HELPER_SIZE_ENV(subu)
+DEF_IWMMXT_HELPER_SIZE_ENV(addu)
+DEF_IWMMXT_HELPER_SIZE_ENV(subs)
+DEF_IWMMXT_HELPER_SIZE_ENV(adds)
+
+DEF_HELPER_3(iwmmxt_avgb0, i64, env, i64, i64)
+DEF_HELPER_3(iwmmxt_avgb1, i64, env, i64, i64)
+DEF_HELPER_3(iwmmxt_avgw0, i64, env, i64, i64)
+DEF_HELPER_3(iwmmxt_avgw1, i64, env, i64, i64)
DEF_HELPER_2(iwmmxt_msadb, i64, i64, i64)
@@ -434,26 +431,26 @@ DEF_HELPER_1(iwmmxt_msbb, i32, i64)
DEF_HELPER_1(iwmmxt_msbw, i32, i64)
DEF_HELPER_1(iwmmxt_msbl, i32, i64)
-DEF_HELPER_2(iwmmxt_srlw, i64, i64, i32)
-DEF_HELPER_2(iwmmxt_srll, i64, i64, i32)
-DEF_HELPER_2(iwmmxt_srlq, i64, i64, i32)
-DEF_HELPER_2(iwmmxt_sllw, i64, i64, i32)
-DEF_HELPER_2(iwmmxt_slll, i64, i64, i32)
-DEF_HELPER_2(iwmmxt_sllq, i64, i64, i32)
-DEF_HELPER_2(iwmmxt_sraw, i64, i64, i32)
-DEF_HELPER_2(iwmmxt_sral, i64, i64, i32)
-DEF_HELPER_2(iwmmxt_sraq, i64, i64, i32)
-DEF_HELPER_2(iwmmxt_rorw, i64, i64, i32)
-DEF_HELPER_2(iwmmxt_rorl, i64, i64, i32)
-DEF_HELPER_2(iwmmxt_rorq, i64, i64, i32)
-DEF_HELPER_2(iwmmxt_shufh, i64, i64, i32)
-
-DEF_HELPER_2(iwmmxt_packuw, i64, i64, i64)
-DEF_HELPER_2(iwmmxt_packul, i64, i64, i64)
-DEF_HELPER_2(iwmmxt_packuq, i64, i64, i64)
-DEF_HELPER_2(iwmmxt_packsw, i64, i64, i64)
-DEF_HELPER_2(iwmmxt_packsl, i64, i64, i64)
-DEF_HELPER_2(iwmmxt_packsq, i64, i64, i64)
+DEF_HELPER_3(iwmmxt_srlw, i64, env, i64, i32)
+DEF_HELPER_3(iwmmxt_srll, i64, env, i64, i32)
+DEF_HELPER_3(iwmmxt_srlq, i64, env, i64, i32)
+DEF_HELPER_3(iwmmxt_sllw, i64, env, i64, i32)
+DEF_HELPER_3(iwmmxt_slll, i64, env, i64, i32)
+DEF_HELPER_3(iwmmxt_sllq, i64, env, i64, i32)
+DEF_HELPER_3(iwmmxt_sraw, i64, env, i64, i32)
+DEF_HELPER_3(iwmmxt_sral, i64, env, i64, i32)
+DEF_HELPER_3(iwmmxt_sraq, i64, env, i64, i32)
+DEF_HELPER_3(iwmmxt_rorw, i64, env, i64, i32)
+DEF_HELPER_3(iwmmxt_rorl, i64, env, i64, i32)
+DEF_HELPER_3(iwmmxt_rorq, i64, env, i64, i32)
+DEF_HELPER_3(iwmmxt_shufh, i64, env, i64, i32)
+
+DEF_HELPER_3(iwmmxt_packuw, i64, env, i64, i64)
+DEF_HELPER_3(iwmmxt_packul, i64, env, i64, i64)
+DEF_HELPER_3(iwmmxt_packuq, i64, env, i64, i64)
+DEF_HELPER_3(iwmmxt_packsw, i64, env, i64, i64)
+DEF_HELPER_3(iwmmxt_packsl, i64, env, i64, i64)
+DEF_HELPER_3(iwmmxt_packsq, i64, env, i64, i64)
DEF_HELPER_3(iwmmxt_muladdsl, i64, i64, i32, i32)
DEF_HELPER_3(iwmmxt_muladdsw, i64, i64, i32, i32)
@@ -461,15 +458,15 @@ DEF_HELPER_3(iwmmxt_muladdswl, i64, i64, i32, i32)
DEF_HELPER_2(set_teecr, void, env, i32)
-DEF_HELPER_2(neon_unzip8, void, i32, i32)
-DEF_HELPER_2(neon_unzip16, void, i32, i32)
-DEF_HELPER_2(neon_qunzip8, void, i32, i32)
-DEF_HELPER_2(neon_qunzip16, void, i32, i32)
-DEF_HELPER_2(neon_qunzip32, void, i32, i32)
-DEF_HELPER_2(neon_zip8, void, i32, i32)
-DEF_HELPER_2(neon_zip16, void, i32, i32)
-DEF_HELPER_2(neon_qzip8, void, i32, i32)
-DEF_HELPER_2(neon_qzip16, void, i32, i32)
-DEF_HELPER_2(neon_qzip32, void, i32, i32)
+DEF_HELPER_3(neon_unzip8, void, env, i32, i32)
+DEF_HELPER_3(neon_unzip16, void, env, i32, i32)
+DEF_HELPER_3(neon_qunzip8, void, env, i32, i32)
+DEF_HELPER_3(neon_qunzip16, void, env, i32, i32)
+DEF_HELPER_3(neon_qunzip32, void, env, i32, i32)
+DEF_HELPER_3(neon_zip8, void, env, i32, i32)
+DEF_HELPER_3(neon_zip16, void, env, i32, i32)
+DEF_HELPER_3(neon_qzip8, void, env, i32, i32)
+DEF_HELPER_3(neon_qzip16, void, env, i32, i32)
+DEF_HELPER_3(neon_qzip32, void, env, i32, i32)
#include "def-helper.h"
diff --git a/target-arm/iwmmxt_helper.c b/target-arm/iwmmxt_helper.c
index ebe6eb9..843994d 100644
--- a/target-arm/iwmmxt_helper.c
+++ b/target-arm/iwmmxt_helper.c
@@ -23,7 +23,7 @@
#include <stdio.h>
#include "cpu.h"
-#include "exec.h"
+#include "exec-all.h"
#include "helper.h"
/* iwMMXt macros extracted from GNU gdb. */
@@ -162,7 +162,8 @@ uint64_t HELPER(iwmmxt_macuw)(uint64_t a, uint64_t b)
SIMD64_SET(NBIT64(x), SIMD_NBIT) | \
SIMD64_SET(ZBIT64(x), SIMD_ZBIT)
#define IWMMXT_OP_UNPACK(S, SH0, SH1, SH2, SH3) \
-uint64_t HELPER(glue(iwmmxt_unpack, glue(S, b)))(uint64_t a, uint64_t b) \
+uint64_t HELPER(glue(iwmmxt_unpack, glue(S, b)))(CPUState *env, \
+ uint64_t a, uint64_t b) \
{ \
a = \
(((a >> SH0) & 0xff) << 0) | (((b >> SH0) & 0xff) << 8) | \
@@ -176,7 +177,8 @@ uint64_t HELPER(glue(iwmmxt_unpack, glue(S, b)))(uint64_t a, uint64_t b) \
NZBIT8(a >> 48, 6) | NZBIT8(a >> 56, 7); \
return a; \
} \
-uint64_t HELPER(glue(iwmmxt_unpack, glue(S, w)))(uint64_t a, uint64_t b) \
+uint64_t HELPER(glue(iwmmxt_unpack, glue(S, w)))(CPUState *env, \
+ uint64_t a, uint64_t b) \
{ \
a = \
(((a >> SH0) & 0xffff) << 0) | \
@@ -188,7 +190,8 @@ uint64_t HELPER(glue(iwmmxt_unpack, glue(S, w)))(uint64_t a, uint64_t b) \
NZBIT8(a >> 32, 2) | NZBIT8(a >> 48, 3); \
return a; \
} \
-uint64_t HELPER(glue(iwmmxt_unpack, glue(S, l)))(uint64_t a, uint64_t b) \
+uint64_t HELPER(glue(iwmmxt_unpack, glue(S, l)))(CPUState *env, \
+ uint64_t a, uint64_t b) \
{ \
a = \
(((a >> SH0) & 0xffffffff) << 0) | \
@@ -197,7 +200,8 @@ uint64_t HELPER(glue(iwmmxt_unpack, glue(S, l)))(uint64_t a, uint64_t b) \
NZBIT32(a >> 0, 0) | NZBIT32(a >> 32, 1); \
return a; \
} \
-uint64_t HELPER(glue(iwmmxt_unpack, glue(S, ub)))(uint64_t x) \
+uint64_t HELPER(glue(iwmmxt_unpack, glue(S, ub)))(CPUState *env, \
+ uint64_t x) \
{ \
x = \
(((x >> SH0) & 0xff) << 0) | \
@@ -209,7 +213,8 @@ uint64_t HELPER(glue(iwmmxt_unpack, glue(S, ub)))(uint64_t x) \
NZBIT16(x >> 32, 2) | NZBIT16(x >> 48, 3); \
return x; \
} \
-uint64_t HELPER(glue(iwmmxt_unpack, glue(S, uw)))(uint64_t x) \
+uint64_t HELPER(glue(iwmmxt_unpack, glue(S, uw)))(CPUState *env, \
+ uint64_t x) \
{ \
x = \
(((x >> SH0) & 0xffff) << 0) | \
@@ -218,13 +223,15 @@ uint64_t HELPER(glue(iwmmxt_unpack, glue(S, uw)))(uint64_t x) \
NZBIT32(x >> 0, 0) | NZBIT32(x >> 32, 1); \
return x; \
} \
-uint64_t HELPER(glue(iwmmxt_unpack, glue(S, ul)))(uint64_t x) \
+uint64_t HELPER(glue(iwmmxt_unpack, glue(S, ul)))(CPUState *env, \
+ uint64_t x) \
{ \
x = (((x >> SH0) & 0xffffffff) << 0); \
env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = NZBIT64(x >> 0); \
return x; \
} \
-uint64_t HELPER(glue(iwmmxt_unpack, glue(S, sb)))(uint64_t x) \
+uint64_t HELPER(glue(iwmmxt_unpack, glue(S, sb)))(CPUState *env, \
+ uint64_t x) \
{ \
x = \
((uint64_t) EXTEND8H((x >> SH0) & 0xff) << 0) | \
@@ -236,7 +243,8 @@ uint64_t HELPER(glue(iwmmxt_unpack, glue(S, sb)))(uint64_t x) \
NZBIT16(x >> 32, 2) | NZBIT16(x >> 48, 3); \
return x; \
} \
-uint64_t HELPER(glue(iwmmxt_unpack, glue(S, sw)))(uint64_t x) \
+uint64_t HELPER(glue(iwmmxt_unpack, glue(S, sw)))(CPUState *env, \
+ uint64_t x) \
{ \
x = \
((uint64_t) EXTEND16((x >> SH0) & 0xffff) << 0) | \
@@ -245,7 +253,8 @@ uint64_t HELPER(glue(iwmmxt_unpack, glue(S, sw)))(uint64_t x) \
NZBIT32(x >> 0, 0) | NZBIT32(x >> 32, 1); \
return x; \
} \
-uint64_t HELPER(glue(iwmmxt_unpack, glue(S, sl)))(uint64_t x) \
+uint64_t HELPER(glue(iwmmxt_unpack, glue(S, sl)))(CPUState *env, \
+ uint64_t x) \
{ \
x = EXTEND32((x >> SH0) & 0xffffffff); \
env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = NZBIT64(x >> 0); \
@@ -255,7 +264,8 @@ IWMMXT_OP_UNPACK(l, 0, 8, 16, 24)
IWMMXT_OP_UNPACK(h, 32, 40, 48, 56)
#define IWMMXT_OP_CMP(SUFF, Tb, Tw, Tl, O) \
-uint64_t HELPER(glue(iwmmxt_, glue(SUFF, b)))(uint64_t a, uint64_t b) \
+uint64_t HELPER(glue(iwmmxt_, glue(SUFF, b)))(CPUState *env, \
+ uint64_t a, uint64_t b) \
{ \
a = \
CMP(0, Tb, O, 0xff) | CMP(8, Tb, O, 0xff) | \
@@ -269,7 +279,8 @@ uint64_t HELPER(glue(iwmmxt_, glue(SUFF, b)))(uint64_t a, uint64_t b) \
NZBIT8(a >> 48, 6) | NZBIT8(a >> 56, 7); \
return a; \
} \
-uint64_t HELPER(glue(iwmmxt_, glue(SUFF, w)))(uint64_t a, uint64_t b) \
+uint64_t HELPER(glue(iwmmxt_, glue(SUFF, w)))(CPUState *env, \
+ uint64_t a, uint64_t b) \
{ \
a = CMP(0, Tw, O, 0xffff) | CMP(16, Tw, O, 0xffff) | \
CMP(32, Tw, O, 0xffff) | CMP(48, Tw, O, 0xffff); \
@@ -278,7 +289,8 @@ uint64_t HELPER(glue(iwmmxt_, glue(SUFF, w)))(uint64_t a, uint64_t b) \
NZBIT16(a >> 32, 2) | NZBIT16(a >> 48, 3); \
return a; \
} \
-uint64_t HELPER(glue(iwmmxt_, glue(SUFF, l)))(uint64_t a, uint64_t b) \
+uint64_t HELPER(glue(iwmmxt_, glue(SUFF, l)))(CPUState *env, \
+ uint64_t a, uint64_t b) \
{ \
a = CMP(0, Tl, O, 0xffffffff) | \
CMP(32, Tl, O, 0xffffffff); \
@@ -317,7 +329,7 @@ IWMMXT_OP_CMP(adds, int8_t, int16_t, int32_t, +)
#define AVGB(SHR) ((( \
((a >> SHR) & 0xff) + ((b >> SHR) & 0xff) + round) >> 1) << SHR)
#define IWMMXT_OP_AVGB(r) \
-uint64_t HELPER(iwmmxt_avgb##r)(uint64_t a, uint64_t b) \
+uint64_t HELPER(iwmmxt_avgb##r)(CPUState *env, uint64_t a, uint64_t b) \
{ \
const int round = r; \
a = AVGB(0) | AVGB(8) | AVGB(16) | AVGB(24) | \
@@ -341,7 +353,7 @@ IWMMXT_OP_AVGB(1)
#define AVGW(SHR) ((( \
((a >> SHR) & 0xffff) + ((b >> SHR) & 0xffff) + round) >> 1) << SHR)
#define IWMMXT_OP_AVGW(r) \
-uint64_t HELPER(iwmmxt_avgw##r)(uint64_t a, uint64_t b) \
+uint64_t HELPER(iwmmxt_avgw##r)(CPUState *env, uint64_t a, uint64_t b) \
{ \
const int round = r; \
a = AVGW(0) | AVGW(16) | AVGW(32) | AVGW(48); \
@@ -452,7 +464,7 @@ uint32_t HELPER(iwmmxt_msbl)(uint64_t x)
}
/* FIXME: Split wCASF setting into a separate op to avoid env use. */
-uint64_t HELPER(iwmmxt_srlw)(uint64_t x, uint32_t n)
+uint64_t HELPER(iwmmxt_srlw)(CPUState *env, uint64_t x, uint32_t n)
{
x = (((x & (0xffffll << 0)) >> n) & (0xffffll << 0)) |
(((x & (0xffffll << 16)) >> n) & (0xffffll << 16)) |
@@ -464,7 +476,7 @@ uint64_t HELPER(iwmmxt_srlw)(uint64_t x, uint32_t n)
return x;
}
-uint64_t HELPER(iwmmxt_srll)(uint64_t x, uint32_t n)
+uint64_t HELPER(iwmmxt_srll)(CPUState *env, uint64_t x, uint32_t n)
{
x = ((x & (0xffffffffll << 0)) >> n) |
((x >> n) & (0xffffffffll << 32));
@@ -473,14 +485,14 @@ uint64_t HELPER(iwmmxt_srll)(uint64_t x, uint32_t n)
return x;
}
-uint64_t HELPER(iwmmxt_srlq)(uint64_t x, uint32_t n)
+uint64_t HELPER(iwmmxt_srlq)(CPUState *env, uint64_t x, uint32_t n)
{
x >>= n;
env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = NZBIT64(x);
return x;
}
-uint64_t HELPER(iwmmxt_sllw)(uint64_t x, uint32_t n)
+uint64_t HELPER(iwmmxt_sllw)(CPUState *env, uint64_t x, uint32_t n)
{
x = (((x & (0xffffll << 0)) << n) & (0xffffll << 0)) |
(((x & (0xffffll << 16)) << n) & (0xffffll << 16)) |
@@ -492,7 +504,7 @@ uint64_t HELPER(iwmmxt_sllw)(uint64_t x, uint32_t n)
return x;
}
-uint64_t HELPER(iwmmxt_slll)(uint64_t x, uint32_t n)
+uint64_t HELPER(iwmmxt_slll)(CPUState *env, uint64_t x, uint32_t n)
{
x = ((x << n) & (0xffffffffll << 0)) |
((x & (0xffffffffll << 32)) << n);
@@ -501,14 +513,14 @@ uint64_t HELPER(iwmmxt_slll)(uint64_t x, uint32_t n)
return x;
}
-uint64_t HELPER(iwmmxt_sllq)(uint64_t x, uint32_t n)
+uint64_t HELPER(iwmmxt_sllq)(CPUState *env, uint64_t x, uint32_t n)
{
x <<= n;
env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = NZBIT64(x);
return x;
}
-uint64_t HELPER(iwmmxt_sraw)(uint64_t x, uint32_t n)
+uint64_t HELPER(iwmmxt_sraw)(CPUState *env, uint64_t x, uint32_t n)
{
x = ((uint64_t) ((EXTEND16(x >> 0) >> n) & 0xffff) << 0) |
((uint64_t) ((EXTEND16(x >> 16) >> n) & 0xffff) << 16) |
@@ -520,7 +532,7 @@ uint64_t HELPER(iwmmxt_sraw)(uint64_t x, uint32_t n)
return x;
}
-uint64_t HELPER(iwmmxt_sral)(uint64_t x, uint32_t n)
+uint64_t HELPER(iwmmxt_sral)(CPUState *env, uint64_t x, uint32_t n)
{
x = (((EXTEND32(x >> 0) >> n) & 0xffffffff) << 0) |
(((EXTEND32(x >> 32) >> n) & 0xffffffff) << 32);
@@ -529,14 +541,14 @@ uint64_t HELPER(iwmmxt_sral)(uint64_t x, uint32_t n)
return x;
}
-uint64_t HELPER(iwmmxt_sraq)(uint64_t x, uint32_t n)
+uint64_t HELPER(iwmmxt_sraq)(CPUState *env, uint64_t x, uint32_t n)
{
x = (int64_t) x >> n;
env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = NZBIT64(x);
return x;
}
-uint64_t HELPER(iwmmxt_rorw)(uint64_t x, uint32_t n)
+uint64_t HELPER(iwmmxt_rorw)(CPUState *env, uint64_t x, uint32_t n)
{
x = ((((x & (0xffffll << 0)) >> n) |
((x & (0xffffll << 0)) << (16 - n))) & (0xffffll << 0)) |
@@ -552,7 +564,7 @@ uint64_t HELPER(iwmmxt_rorw)(uint64_t x, uint32_t n)
return x;
}
-uint64_t HELPER(iwmmxt_rorl)(uint64_t x, uint32_t n)
+uint64_t HELPER(iwmmxt_rorl)(CPUState *env, uint64_t x, uint32_t n)
{
x = ((x & (0xffffffffll << 0)) >> n) |
((x >> n) & (0xffffffffll << 32)) |
@@ -563,14 +575,14 @@ uint64_t HELPER(iwmmxt_rorl)(uint64_t x, uint32_t n)
return x;
}
-uint64_t HELPER(iwmmxt_rorq)(uint64_t x, uint32_t n)
+uint64_t HELPER(iwmmxt_rorq)(CPUState *env, uint64_t x, uint32_t n)
{
x = (x >> n) | (x << (64 - n));
env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = NZBIT64(x);
return x;
}
-uint64_t HELPER(iwmmxt_shufh)(uint64_t x, uint32_t n)
+uint64_t HELPER(iwmmxt_shufh)(CPUState *env, uint64_t x, uint32_t n)
{
x = (((x >> ((n << 4) & 0x30)) & 0xffff) << 0) |
(((x >> ((n << 2) & 0x30)) & 0xffff) << 16) |
@@ -583,7 +595,7 @@ uint64_t HELPER(iwmmxt_shufh)(uint64_t x, uint32_t n)
}
/* TODO: Unsigned-Saturation */
-uint64_t HELPER(iwmmxt_packuw)(uint64_t a, uint64_t b)
+uint64_t HELPER(iwmmxt_packuw)(CPUState *env, uint64_t a, uint64_t b)
{
a = (((a >> 0) & 0xff) << 0) | (((a >> 16) & 0xff) << 8) |
(((a >> 32) & 0xff) << 16) | (((a >> 48) & 0xff) << 24) |
@@ -597,7 +609,7 @@ uint64_t HELPER(iwmmxt_packuw)(uint64_t a, uint64_t b)
return a;
}
-uint64_t HELPER(iwmmxt_packul)(uint64_t a, uint64_t b)
+uint64_t HELPER(iwmmxt_packul)(CPUState *env, uint64_t a, uint64_t b)
{
a = (((a >> 0) & 0xffff) << 0) | (((a >> 32) & 0xffff) << 16) |
(((b >> 0) & 0xffff) << 32) | (((b >> 32) & 0xffff) << 48);
@@ -607,7 +619,7 @@ uint64_t HELPER(iwmmxt_packul)(uint64_t a, uint64_t b)
return a;
}
-uint64_t HELPER(iwmmxt_packuq)(uint64_t a, uint64_t b)
+uint64_t HELPER(iwmmxt_packuq)(CPUState *env, uint64_t a, uint64_t b)
{
a = (a & 0xffffffff) | ((b & 0xffffffff) << 32);
env->iwmmxt.cregs[ARM_IWMMXT_wCASF] =
@@ -616,7 +628,7 @@ uint64_t HELPER(iwmmxt_packuq)(uint64_t a, uint64_t b)
}
/* TODO: Signed-Saturation */
-uint64_t HELPER(iwmmxt_packsw)(uint64_t a, uint64_t b)
+uint64_t HELPER(iwmmxt_packsw)(CPUState *env, uint64_t a, uint64_t b)
{
a = (((a >> 0) & 0xff) << 0) | (((a >> 16) & 0xff) << 8) |
(((a >> 32) & 0xff) << 16) | (((a >> 48) & 0xff) << 24) |
@@ -630,7 +642,7 @@ uint64_t HELPER(iwmmxt_packsw)(uint64_t a, uint64_t b)
return a;
}
-uint64_t HELPER(iwmmxt_packsl)(uint64_t a, uint64_t b)
+uint64_t HELPER(iwmmxt_packsl)(CPUState *env, uint64_t a, uint64_t b)
{
a = (((a >> 0) & 0xffff) << 0) | (((a >> 32) & 0xffff) << 16) |
(((b >> 0) & 0xffff) << 32) | (((b >> 32) & 0xffff) << 48);
@@ -640,7 +652,7 @@ uint64_t HELPER(iwmmxt_packsl)(uint64_t a, uint64_t b)
return a;
}
-uint64_t HELPER(iwmmxt_packsq)(uint64_t a, uint64_t b)
+uint64_t HELPER(iwmmxt_packsq)(CPUState *env, uint64_t a, uint64_t b)
{
a = (a & 0xffffffff) | ((b & 0xffffffff) << 32);
env->iwmmxt.cregs[ARM_IWMMXT_wCASF] =
diff --git a/target-arm/machine.c b/target-arm/machine.c
index a18b7dc..7d4fc54 100644
--- a/target-arm/machine.c
+++ b/target-arm/machine.c
@@ -44,6 +44,12 @@ void cpu_save(QEMUFile *f, void *opaque)
qemu_put_be32(f, env->cp15.c7_par);
qemu_put_be32(f, env->cp15.c9_insn);
qemu_put_be32(f, env->cp15.c9_data);
+ qemu_put_be32(f, env->cp15.c9_pmcr);
+ qemu_put_be32(f, env->cp15.c9_pmcnten);
+ qemu_put_be32(f, env->cp15.c9_pmovsr);
+ qemu_put_be32(f, env->cp15.c9_pmxevtyper);
+ qemu_put_be32(f, env->cp15.c9_pmuserenr);
+ qemu_put_be32(f, env->cp15.c9_pminten);
qemu_put_be32(f, env->cp15.c13_fcse);
qemu_put_be32(f, env->cp15.c13_context);
qemu_put_be32(f, env->cp15.c13_tls1);
@@ -152,6 +158,12 @@ int cpu_load(QEMUFile *f, void *opaque, int version_id)
env->cp15.c7_par = qemu_get_be32(f);
env->cp15.c9_insn = qemu_get_be32(f);
env->cp15.c9_data = qemu_get_be32(f);
+ env->cp15.c9_pmcr = qemu_get_be32(f);
+ env->cp15.c9_pmcnten = qemu_get_be32(f);
+ env->cp15.c9_pmovsr = qemu_get_be32(f);
+ env->cp15.c9_pmxevtyper = qemu_get_be32(f);
+ env->cp15.c9_pmuserenr = qemu_get_be32(f);
+ env->cp15.c9_pminten = qemu_get_be32(f);
env->cp15.c13_fcse = qemu_get_be32(f);
env->cp15.c13_context = qemu_get_be32(f);
env->cp15.c13_tls1 = qemu_get_be32(f);
diff --git a/target-arm/neon_helper.c b/target-arm/neon_helper.c
index 9165519..b51e35a 100644
--- a/target-arm/neon_helper.c
+++ b/target-arm/neon_helper.c
@@ -4,13 +4,13 @@
* Copyright (c) 2007, 2008 CodeSourcery.
* Written by Paul Brook
*
- * This code is licenced under the GNU GPL v2.
+ * This code is licensed under the GNU GPL v2.
*/
#include <stdlib.h>
#include <stdio.h>
#include "cpu.h"
-#include "exec.h"
+#include "exec-all.h"
#include "helper.h"
#define SIGNBIT (uint32_t)0x80000000
@@ -18,8 +18,6 @@
#define SET_QC() env->vfp.xregs[ARM_VFP_FPSCR] = CPSR_Q
-#define NFS (&env->vfp.standard_fp_status)
-
#define NEON_TYPE1(name, type) \
typedef struct \
{ \
@@ -115,6 +113,10 @@ NEON_TYPE1(u32, uint32_t)
uint32_t HELPER(glue(neon_,name))(uint32_t arg1, uint32_t arg2) \
NEON_VOP_BODY(vtype, n)
+#define NEON_VOP_ENV(name, vtype, n) \
+uint32_t HELPER(glue(neon_,name))(CPUState *env, uint32_t arg1, uint32_t arg2) \
+NEON_VOP_BODY(vtype, n)
+
/* Pairwise operations. */
/* For 32-bit elements each segment only contains a single element, so
the elementwise and pairwise operations are the same. */
@@ -163,14 +165,14 @@ uint32_t HELPER(glue(neon_,name))(uint32_t arg) \
dest = tmp; \
}} while(0)
#define NEON_FN(dest, src1, src2) NEON_USAT(dest, src1, src2, uint8_t)
-NEON_VOP(qadd_u8, neon_u8, 4)
+NEON_VOP_ENV(qadd_u8, neon_u8, 4)
#undef NEON_FN
#define NEON_FN(dest, src1, src2) NEON_USAT(dest, src1, src2, uint16_t)
-NEON_VOP(qadd_u16, neon_u16, 2)
+NEON_VOP_ENV(qadd_u16, neon_u16, 2)
#undef NEON_FN
#undef NEON_USAT
-uint32_t HELPER(neon_qadd_u32)(uint32_t a, uint32_t b)
+uint32_t HELPER(neon_qadd_u32)(CPUState *env, uint32_t a, uint32_t b)
{
uint32_t res = a + b;
if (res < a) {
@@ -180,7 +182,7 @@ uint32_t HELPER(neon_qadd_u32)(uint32_t a, uint32_t b)
return res;
}
-uint64_t HELPER(neon_qadd_u64)(uint64_t src1, uint64_t src2)
+uint64_t HELPER(neon_qadd_u64)(CPUState *env, uint64_t src1, uint64_t src2)
{
uint64_t res;
@@ -205,14 +207,14 @@ uint64_t HELPER(neon_qadd_u64)(uint64_t src1, uint64_t src2)
dest = tmp; \
} while(0)
#define NEON_FN(dest, src1, src2) NEON_SSAT(dest, src1, src2, int8_t)
-NEON_VOP(qadd_s8, neon_s8, 4)
+NEON_VOP_ENV(qadd_s8, neon_s8, 4)
#undef NEON_FN
#define NEON_FN(dest, src1, src2) NEON_SSAT(dest, src1, src2, int16_t)
-NEON_VOP(qadd_s16, neon_s16, 2)
+NEON_VOP_ENV(qadd_s16, neon_s16, 2)
#undef NEON_FN
#undef NEON_SSAT
-uint32_t HELPER(neon_qadd_s32)(uint32_t a, uint32_t b)
+uint32_t HELPER(neon_qadd_s32)(CPUState *env, uint32_t a, uint32_t b)
{
uint32_t res = a + b;
if (((res ^ a) & SIGNBIT) && !((a ^ b) & SIGNBIT)) {
@@ -222,7 +224,7 @@ uint32_t HELPER(neon_qadd_s32)(uint32_t a, uint32_t b)
return res;
}
-uint64_t HELPER(neon_qadd_s64)(uint64_t src1, uint64_t src2)
+uint64_t HELPER(neon_qadd_s64)(CPUState *env, uint64_t src1, uint64_t src2)
{
uint64_t res;
@@ -243,14 +245,14 @@ uint64_t HELPER(neon_qadd_s64)(uint64_t src1, uint64_t src2)
dest = tmp; \
}} while(0)
#define NEON_FN(dest, src1, src2) NEON_USAT(dest, src1, src2, uint8_t)
-NEON_VOP(qsub_u8, neon_u8, 4)
+NEON_VOP_ENV(qsub_u8, neon_u8, 4)
#undef NEON_FN
#define NEON_FN(dest, src1, src2) NEON_USAT(dest, src1, src2, uint16_t)
-NEON_VOP(qsub_u16, neon_u16, 2)
+NEON_VOP_ENV(qsub_u16, neon_u16, 2)
#undef NEON_FN
#undef NEON_USAT
-uint32_t HELPER(neon_qsub_u32)(uint32_t a, uint32_t b)
+uint32_t HELPER(neon_qsub_u32)(CPUState *env, uint32_t a, uint32_t b)
{
uint32_t res = a - b;
if (res > a) {
@@ -260,7 +262,7 @@ uint32_t HELPER(neon_qsub_u32)(uint32_t a, uint32_t b)
return res;
}
-uint64_t HELPER(neon_qsub_u64)(uint64_t src1, uint64_t src2)
+uint64_t HELPER(neon_qsub_u64)(CPUState *env, uint64_t src1, uint64_t src2)
{
uint64_t res;
@@ -286,14 +288,14 @@ uint64_t HELPER(neon_qsub_u64)(uint64_t src1, uint64_t src2)
dest = tmp; \
} while(0)
#define NEON_FN(dest, src1, src2) NEON_SSAT(dest, src1, src2, int8_t)
-NEON_VOP(qsub_s8, neon_s8, 4)
+NEON_VOP_ENV(qsub_s8, neon_s8, 4)
#undef NEON_FN
#define NEON_FN(dest, src1, src2) NEON_SSAT(dest, src1, src2, int16_t)
-NEON_VOP(qsub_s16, neon_s16, 2)
+NEON_VOP_ENV(qsub_s16, neon_s16, 2)
#undef NEON_FN
#undef NEON_SSAT
-uint32_t HELPER(neon_qsub_s32)(uint32_t a, uint32_t b)
+uint32_t HELPER(neon_qsub_s32)(CPUState *env, uint32_t a, uint32_t b)
{
uint32_t res = a - b;
if (((res ^ a) & SIGNBIT) && ((a ^ b) & SIGNBIT)) {
@@ -303,7 +305,7 @@ uint32_t HELPER(neon_qsub_s32)(uint32_t a, uint32_t b)
return res;
}
-uint64_t HELPER(neon_qsub_s64)(uint64_t src1, uint64_t src2)
+uint64_t HELPER(neon_qsub_s64)(CPUState *env, uint64_t src1, uint64_t src2)
{
uint64_t res;
@@ -654,12 +656,12 @@ uint64_t HELPER(neon_rshl_u64)(uint64_t val, uint64_t shiftop)
dest = ~0; \
} \
}} while (0)
-NEON_VOP(qshl_u8, neon_u8, 4)
-NEON_VOP(qshl_u16, neon_u16, 2)
-NEON_VOP(qshl_u32, neon_u32, 1)
+NEON_VOP_ENV(qshl_u8, neon_u8, 4)
+NEON_VOP_ENV(qshl_u16, neon_u16, 2)
+NEON_VOP_ENV(qshl_u32, neon_u32, 1)
#undef NEON_FN
-uint64_t HELPER(neon_qshl_u64)(uint64_t val, uint64_t shiftop)
+uint64_t HELPER(neon_qshl_u64)(CPUState *env, uint64_t val, uint64_t shiftop)
{
int8_t shift = (int8_t)shiftop;
if (shift >= 64) {
@@ -709,12 +711,12 @@ uint64_t HELPER(neon_qshl_u64)(uint64_t val, uint64_t shiftop)
} \
} \
}} while (0)
-NEON_VOP(qshl_s8, neon_s8, 4)
-NEON_VOP(qshl_s16, neon_s16, 2)
-NEON_VOP(qshl_s32, neon_s32, 1)
+NEON_VOP_ENV(qshl_s8, neon_s8, 4)
+NEON_VOP_ENV(qshl_s16, neon_s16, 2)
+NEON_VOP_ENV(qshl_s32, neon_s32, 1)
#undef NEON_FN
-uint64_t HELPER(neon_qshl_s64)(uint64_t valop, uint64_t shiftop)
+uint64_t HELPER(neon_qshl_s64)(CPUState *env, uint64_t valop, uint64_t shiftop)
{
int8_t shift = (uint8_t)shiftop;
int64_t val = valop;
@@ -764,26 +766,26 @@ uint64_t HELPER(neon_qshl_s64)(uint64_t valop, uint64_t shiftop)
} \
} \
}} while (0)
-NEON_VOP(qshlu_s8, neon_u8, 4)
-NEON_VOP(qshlu_s16, neon_u16, 2)
+NEON_VOP_ENV(qshlu_s8, neon_u8, 4)
+NEON_VOP_ENV(qshlu_s16, neon_u16, 2)
#undef NEON_FN
-uint32_t HELPER(neon_qshlu_s32)(uint32_t valop, uint32_t shiftop)
+uint32_t HELPER(neon_qshlu_s32)(CPUState *env, uint32_t valop, uint32_t shiftop)
{
if ((int32_t)valop < 0) {
SET_QC();
return 0;
}
- return helper_neon_qshl_u32(valop, shiftop);
+ return helper_neon_qshl_u32(env, valop, shiftop);
}
-uint64_t HELPER(neon_qshlu_s64)(uint64_t valop, uint64_t shiftop)
+uint64_t HELPER(neon_qshlu_s64)(CPUState *env, uint64_t valop, uint64_t shiftop)
{
if ((int64_t)valop < 0) {
SET_QC();
return 0;
}
- return helper_neon_qshl_u64(valop, shiftop);
+ return helper_neon_qshl_u64(env, valop, shiftop);
}
/* FIXME: This is wrong. */
@@ -810,13 +812,13 @@ uint64_t HELPER(neon_qshlu_s64)(uint64_t valop, uint64_t shiftop)
dest = ~0; \
} \
}} while (0)
-NEON_VOP(qrshl_u8, neon_u8, 4)
-NEON_VOP(qrshl_u16, neon_u16, 2)
+NEON_VOP_ENV(qrshl_u8, neon_u8, 4)
+NEON_VOP_ENV(qrshl_u16, neon_u16, 2)
#undef NEON_FN
/* The addition of the rounding constant may overflow, so we use an
* intermediate 64 bits accumulator. */
-uint32_t HELPER(neon_qrshl_u32)(uint32_t val, uint32_t shiftop)
+uint32_t HELPER(neon_qrshl_u32)(CPUState *env, uint32_t val, uint32_t shiftop)
{
uint32_t dest;
int8_t shift = (int8_t)shiftop;
@@ -846,7 +848,7 @@ uint32_t HELPER(neon_qrshl_u32)(uint32_t val, uint32_t shiftop)
/* Handling addition overflow with 64 bits inputs values is more
* tricky than with 32 bits values. */
-uint64_t HELPER(neon_qrshl_u64)(uint64_t val, uint64_t shiftop)
+uint64_t HELPER(neon_qrshl_u64)(CPUState *env, uint64_t val, uint64_t shiftop)
{
int8_t shift = (int8_t)shiftop;
if (shift >= 64) {
@@ -907,13 +909,13 @@ uint64_t HELPER(neon_qrshl_u64)(uint64_t val, uint64_t shiftop)
} \
} \
}} while (0)
-NEON_VOP(qrshl_s8, neon_s8, 4)
-NEON_VOP(qrshl_s16, neon_s16, 2)
+NEON_VOP_ENV(qrshl_s8, neon_s8, 4)
+NEON_VOP_ENV(qrshl_s16, neon_s16, 2)
#undef NEON_FN
/* The addition of the rounding constant may overflow, so we use an
* intermediate 64 bits accumulator. */
-uint32_t HELPER(neon_qrshl_s32)(uint32_t valop, uint32_t shiftop)
+uint32_t HELPER(neon_qrshl_s32)(CPUState *env, uint32_t valop, uint32_t shiftop)
{
int32_t dest;
int32_t val = (int32_t)valop;
@@ -942,7 +944,7 @@ uint32_t HELPER(neon_qrshl_s32)(uint32_t valop, uint32_t shiftop)
/* Handling addition overflow with 64 bits inputs values is more
* tricky than with 32 bits values. */
-uint64_t HELPER(neon_qrshl_s64)(uint64_t valop, uint64_t shiftop)
+uint64_t HELPER(neon_qrshl_s64)(CPUState *env, uint64_t valop, uint64_t shiftop)
{
int8_t shift = (uint8_t)shiftop;
int64_t val = valop;
@@ -1151,10 +1153,10 @@ uint32_t HELPER(neon_cnt_u8)(uint32_t x)
dest = tmp >> 16; \
} while(0)
#define NEON_FN(dest, src1, src2) NEON_QDMULH16(dest, src1, src2, 0)
-NEON_VOP(qdmulh_s16, neon_s16, 2)
+NEON_VOP_ENV(qdmulh_s16, neon_s16, 2)
#undef NEON_FN
#define NEON_FN(dest, src1, src2) NEON_QDMULH16(dest, src1, src2, 1)
-NEON_VOP(qrdmulh_s16, neon_s16, 2)
+NEON_VOP_ENV(qrdmulh_s16, neon_s16, 2)
#undef NEON_FN
#undef NEON_QDMULH16
@@ -1177,10 +1179,10 @@ NEON_VOP(qrdmulh_s16, neon_s16, 2)
dest = tmp >> 32; \
} while(0)
#define NEON_FN(dest, src1, src2) NEON_QDMULH32(dest, src1, src2, 0)
-NEON_VOP(qdmulh_s32, neon_s32, 1)
+NEON_VOP_ENV(qdmulh_s32, neon_s32, 1)
#undef NEON_FN
#define NEON_FN(dest, src1, src2) NEON_QDMULH32(dest, src1, src2, 1)
-NEON_VOP(qrdmulh_s32, neon_s32, 1)
+NEON_VOP_ENV(qrdmulh_s32, neon_s32, 1)
#undef NEON_FN
#undef NEON_QDMULH32
@@ -1221,7 +1223,7 @@ uint32_t HELPER(neon_narrow_round_high_u16)(uint64_t x)
return ((x >> 16) & 0xffff) | ((x >> 32) & 0xffff0000);
}
-uint32_t HELPER(neon_unarrow_sat8)(uint64_t x)
+uint32_t HELPER(neon_unarrow_sat8)(CPUState *env, uint64_t x)
{
uint16_t s;
uint8_t d;
@@ -1248,7 +1250,7 @@ uint32_t HELPER(neon_unarrow_sat8)(uint64_t x)
return res;
}
-uint32_t HELPER(neon_narrow_sat_u8)(uint64_t x)
+uint32_t HELPER(neon_narrow_sat_u8)(CPUState *env, uint64_t x)
{
uint16_t s;
uint8_t d;
@@ -1271,7 +1273,7 @@ uint32_t HELPER(neon_narrow_sat_u8)(uint64_t x)
return res;
}
-uint32_t HELPER(neon_narrow_sat_s8)(uint64_t x)
+uint32_t HELPER(neon_narrow_sat_s8)(CPUState *env, uint64_t x)
{
int16_t s;
uint8_t d;
@@ -1294,7 +1296,7 @@ uint32_t HELPER(neon_narrow_sat_s8)(uint64_t x)
return res;
}
-uint32_t HELPER(neon_unarrow_sat16)(uint64_t x)
+uint32_t HELPER(neon_unarrow_sat16)(CPUState *env, uint64_t x)
{
uint32_t high;
uint32_t low;
@@ -1317,7 +1319,7 @@ uint32_t HELPER(neon_unarrow_sat16)(uint64_t x)
return low | (high << 16);
}
-uint32_t HELPER(neon_narrow_sat_u16)(uint64_t x)
+uint32_t HELPER(neon_narrow_sat_u16)(CPUState *env, uint64_t x)
{
uint32_t high;
uint32_t low;
@@ -1334,7 +1336,7 @@ uint32_t HELPER(neon_narrow_sat_u16)(uint64_t x)
return low | (high << 16);
}
-uint32_t HELPER(neon_narrow_sat_s16)(uint64_t x)
+uint32_t HELPER(neon_narrow_sat_s16)(CPUState *env, uint64_t x)
{
int32_t low;
int32_t high;
@@ -1351,7 +1353,7 @@ uint32_t HELPER(neon_narrow_sat_s16)(uint64_t x)
return (uint16_t)low | (high << 16);
}
-uint32_t HELPER(neon_unarrow_sat32)(uint64_t x)
+uint32_t HELPER(neon_unarrow_sat32)(CPUState *env, uint64_t x)
{
if (x & 0x8000000000000000ull) {
SET_QC();
@@ -1364,7 +1366,7 @@ uint32_t HELPER(neon_unarrow_sat32)(uint64_t x)
return x;
}
-uint32_t HELPER(neon_narrow_sat_u32)(uint64_t x)
+uint32_t HELPER(neon_narrow_sat_u32)(CPUState *env, uint64_t x)
{
if (x > 0xffffffffu) {
SET_QC();
@@ -1373,7 +1375,7 @@ uint32_t HELPER(neon_narrow_sat_u32)(uint64_t x)
return x;
}
-uint32_t HELPER(neon_narrow_sat_s32)(uint64_t x)
+uint32_t HELPER(neon_narrow_sat_s32)(CPUState *env, uint64_t x)
{
if ((int64_t)x != (int32_t)x) {
SET_QC();
@@ -1480,7 +1482,7 @@ uint64_t HELPER(neon_subl_u32)(uint64_t a, uint64_t b)
return (a - b) ^ mask;
}
-uint64_t HELPER(neon_addl_saturate_s32)(uint64_t a, uint64_t b)
+uint64_t HELPER(neon_addl_saturate_s32)(CPUState *env, uint64_t a, uint64_t b)
{
uint32_t x, y;
uint32_t low, high;
@@ -1502,7 +1504,7 @@ uint64_t HELPER(neon_addl_saturate_s32)(uint64_t a, uint64_t b)
return low | ((uint64_t)high << 32);
}
-uint64_t HELPER(neon_addl_saturate_s64)(uint64_t a, uint64_t b)
+uint64_t HELPER(neon_addl_saturate_s64)(CPUState *env, uint64_t a, uint64_t b)
{
uint64_t result;
@@ -1678,7 +1680,7 @@ uint64_t HELPER(neon_negl_u64)(uint64_t x)
} else if (x < 0) { \
x = -x; \
}} while (0)
-uint32_t HELPER(neon_qabs_s8)(uint32_t x)
+uint32_t HELPER(neon_qabs_s8)(CPUState *env, uint32_t x)
{
neon_s8 vec;
NEON_UNPACK(neon_s8, vec, x);
@@ -1698,7 +1700,7 @@ uint32_t HELPER(neon_qabs_s8)(uint32_t x)
} else { \
x = -x; \
}} while (0)
-uint32_t HELPER(neon_qneg_s8)(uint32_t x)
+uint32_t HELPER(neon_qneg_s8)(CPUState *env, uint32_t x)
{
neon_s8 vec;
NEON_UNPACK(neon_s8, vec, x);
@@ -1718,7 +1720,7 @@ uint32_t HELPER(neon_qneg_s8)(uint32_t x)
} else if (x < 0) { \
x = -x; \
}} while (0)
-uint32_t HELPER(neon_qabs_s16)(uint32_t x)
+uint32_t HELPER(neon_qabs_s16)(CPUState *env, uint32_t x)
{
neon_s16 vec;
NEON_UNPACK(neon_s16, vec, x);
@@ -1736,7 +1738,7 @@ uint32_t HELPER(neon_qabs_s16)(uint32_t x)
} else { \
x = -x; \
}} while (0)
-uint32_t HELPER(neon_qneg_s16)(uint32_t x)
+uint32_t HELPER(neon_qneg_s16)(CPUState *env, uint32_t x)
{
neon_s16 vec;
NEON_UNPACK(neon_s16, vec, x);
@@ -1747,7 +1749,7 @@ uint32_t HELPER(neon_qneg_s16)(uint32_t x)
}
#undef DO_QNEG16
-uint32_t HELPER(neon_qabs_s32)(uint32_t x)
+uint32_t HELPER(neon_qabs_s32)(CPUState *env, uint32_t x)
{
if (x == SIGNBIT) {
SET_QC();
@@ -1758,7 +1760,7 @@ uint32_t HELPER(neon_qabs_s32)(uint32_t x)
return x;
}
-uint32_t HELPER(neon_qneg_s32)(uint32_t x)
+uint32_t HELPER(neon_qneg_s32)(CPUState *env, uint32_t x)
{
if (x == SIGNBIT) {
SET_QC();
@@ -1770,74 +1772,67 @@ uint32_t HELPER(neon_qneg_s32)(uint32_t x)
}
/* NEON Float helpers. */
-uint32_t HELPER(neon_min_f32)(uint32_t a, uint32_t b)
+uint32_t HELPER(neon_min_f32)(uint32_t a, uint32_t b, void *fpstp)
{
- return float32_val(float32_min(make_float32(a), make_float32(b), NFS));
+ float_status *fpst = fpstp;
+ return float32_val(float32_min(make_float32(a), make_float32(b), fpst));
}
-uint32_t HELPER(neon_max_f32)(uint32_t a, uint32_t b)
+uint32_t HELPER(neon_max_f32)(uint32_t a, uint32_t b, void *fpstp)
{
- return float32_val(float32_max(make_float32(a), make_float32(b), NFS));
+ float_status *fpst = fpstp;
+ return float32_val(float32_max(make_float32(a), make_float32(b), fpst));
}
-uint32_t HELPER(neon_abd_f32)(uint32_t a, uint32_t b)
+uint32_t HELPER(neon_abd_f32)(uint32_t a, uint32_t b, void *fpstp)
{
+ float_status *fpst = fpstp;
float32 f0 = make_float32(a);
float32 f1 = make_float32(b);
- return float32_val(float32_abs(float32_sub(f0, f1, NFS)));
-}
-
-uint32_t HELPER(neon_add_f32)(uint32_t a, uint32_t b)
-{
- return float32_val(float32_add(make_float32(a), make_float32(b), NFS));
-}
-
-uint32_t HELPER(neon_sub_f32)(uint32_t a, uint32_t b)
-{
- return float32_val(float32_sub(make_float32(a), make_float32(b), NFS));
-}
-
-uint32_t HELPER(neon_mul_f32)(uint32_t a, uint32_t b)
-{
- return float32_val(float32_mul(make_float32(a), make_float32(b), NFS));
+ return float32_val(float32_abs(float32_sub(f0, f1, fpst)));
}
/* Floating point comparisons produce an integer result.
* Note that EQ doesn't signal InvalidOp for QNaNs but GE and GT do.
* Softfloat routines return 0/1, which we convert to the 0/-1 Neon requires.
*/
-uint32_t HELPER(neon_ceq_f32)(uint32_t a, uint32_t b)
+uint32_t HELPER(neon_ceq_f32)(uint32_t a, uint32_t b, void *fpstp)
{
- return -float32_eq_quiet(make_float32(a), make_float32(b), NFS);
+ float_status *fpst = fpstp;
+ return -float32_eq_quiet(make_float32(a), make_float32(b), fpst);
}
-uint32_t HELPER(neon_cge_f32)(uint32_t a, uint32_t b)
+uint32_t HELPER(neon_cge_f32)(uint32_t a, uint32_t b, void *fpstp)
{
- return -float32_le(make_float32(b), make_float32(a), NFS);
+ float_status *fpst = fpstp;
+ return -float32_le(make_float32(b), make_float32(a), fpst);
}
-uint32_t HELPER(neon_cgt_f32)(uint32_t a, uint32_t b)
+uint32_t HELPER(neon_cgt_f32)(uint32_t a, uint32_t b, void *fpstp)
{
- return -float32_lt(make_float32(b), make_float32(a), NFS);
+ float_status *fpst = fpstp;
+ return -float32_lt(make_float32(b), make_float32(a), fpst);
}
-uint32_t HELPER(neon_acge_f32)(uint32_t a, uint32_t b)
+uint32_t HELPER(neon_acge_f32)(uint32_t a, uint32_t b, void *fpstp)
{
+ float_status *fpst = fpstp;
float32 f0 = float32_abs(make_float32(a));
float32 f1 = float32_abs(make_float32(b));
- return -float32_le(f1, f0, NFS);
+ return -float32_le(f1, f0, fpst);
}
-uint32_t HELPER(neon_acgt_f32)(uint32_t a, uint32_t b)
+uint32_t HELPER(neon_acgt_f32)(uint32_t a, uint32_t b, void *fpstp)
{
+ float_status *fpst = fpstp;
float32 f0 = float32_abs(make_float32(a));
float32 f1 = float32_abs(make_float32(b));
- return -float32_lt(f1, f0, NFS);
+ return -float32_lt(f1, f0, fpst);
}
#define ELEM(V, N, SIZE) (((V) >> ((N) * (SIZE))) & ((1ull << (SIZE)) - 1))
-void HELPER(neon_qunzip8)(uint32_t rd, uint32_t rm)
+void HELPER(neon_qunzip8)(CPUState *env, uint32_t rd, uint32_t rm)
{
uint64_t zm0 = float64_val(env->vfp.regs[rm]);
uint64_t zm1 = float64_val(env->vfp.regs[rm + 1]);
@@ -1865,7 +1860,7 @@ void HELPER(neon_qunzip8)(uint32_t rd, uint32_t rm)
env->vfp.regs[rd + 1] = make_float64(d1);
}
-void HELPER(neon_qunzip16)(uint32_t rd, uint32_t rm)
+void HELPER(neon_qunzip16)(CPUState *env, uint32_t rd, uint32_t rm)
{
uint64_t zm0 = float64_val(env->vfp.regs[rm]);
uint64_t zm1 = float64_val(env->vfp.regs[rm + 1]);
@@ -1885,7 +1880,7 @@ void HELPER(neon_qunzip16)(uint32_t rd, uint32_t rm)
env->vfp.regs[rd + 1] = make_float64(d1);
}
-void HELPER(neon_qunzip32)(uint32_t rd, uint32_t rm)
+void HELPER(neon_qunzip32)(CPUState *env, uint32_t rd, uint32_t rm)
{
uint64_t zm0 = float64_val(env->vfp.regs[rm]);
uint64_t zm1 = float64_val(env->vfp.regs[rm + 1]);
@@ -1901,7 +1896,7 @@ void HELPER(neon_qunzip32)(uint32_t rd, uint32_t rm)
env->vfp.regs[rd + 1] = make_float64(d1);
}
-void HELPER(neon_unzip8)(uint32_t rd, uint32_t rm)
+void HELPER(neon_unzip8)(CPUState *env, uint32_t rd, uint32_t rm)
{
uint64_t zm = float64_val(env->vfp.regs[rm]);
uint64_t zd = float64_val(env->vfp.regs[rd]);
@@ -1917,7 +1912,7 @@ void HELPER(neon_unzip8)(uint32_t rd, uint32_t rm)
env->vfp.regs[rd] = make_float64(d0);
}
-void HELPER(neon_unzip16)(uint32_t rd, uint32_t rm)
+void HELPER(neon_unzip16)(CPUState *env, uint32_t rd, uint32_t rm)
{
uint64_t zm = float64_val(env->vfp.regs[rm]);
uint64_t zd = float64_val(env->vfp.regs[rd]);
@@ -1929,7 +1924,7 @@ void HELPER(neon_unzip16)(uint32_t rd, uint32_t rm)
env->vfp.regs[rd] = make_float64(d0);
}
-void HELPER(neon_qzip8)(uint32_t rd, uint32_t rm)
+void HELPER(neon_qzip8)(CPUState *env, uint32_t rd, uint32_t rm)
{
uint64_t zm0 = float64_val(env->vfp.regs[rm]);
uint64_t zm1 = float64_val(env->vfp.regs[rm + 1]);
@@ -1957,7 +1952,7 @@ void HELPER(neon_qzip8)(uint32_t rd, uint32_t rm)
env->vfp.regs[rd + 1] = make_float64(d1);
}
-void HELPER(neon_qzip16)(uint32_t rd, uint32_t rm)
+void HELPER(neon_qzip16)(CPUState *env, uint32_t rd, uint32_t rm)
{
uint64_t zm0 = float64_val(env->vfp.regs[rm]);
uint64_t zm1 = float64_val(env->vfp.regs[rm + 1]);
@@ -1977,7 +1972,7 @@ void HELPER(neon_qzip16)(uint32_t rd, uint32_t rm)
env->vfp.regs[rd + 1] = make_float64(d1);
}
-void HELPER(neon_qzip32)(uint32_t rd, uint32_t rm)
+void HELPER(neon_qzip32)(CPUState *env, uint32_t rd, uint32_t rm)
{
uint64_t zm0 = float64_val(env->vfp.regs[rm]);
uint64_t zm1 = float64_val(env->vfp.regs[rm + 1]);
@@ -1993,7 +1988,7 @@ void HELPER(neon_qzip32)(uint32_t rd, uint32_t rm)
env->vfp.regs[rd + 1] = make_float64(d1);
}
-void HELPER(neon_zip8)(uint32_t rd, uint32_t rm)
+void HELPER(neon_zip8)(CPUState *env, uint32_t rd, uint32_t rm)
{
uint64_t zm = float64_val(env->vfp.regs[rm]);
uint64_t zd = float64_val(env->vfp.regs[rd]);
@@ -2009,7 +2004,7 @@ void HELPER(neon_zip8)(uint32_t rd, uint32_t rm)
env->vfp.regs[rd] = make_float64(d0);
}
-void HELPER(neon_zip16)(uint32_t rd, uint32_t rm)
+void HELPER(neon_zip16)(CPUState *env, uint32_t rd, uint32_t rm)
{
uint64_t zm = float64_val(env->vfp.regs[rm]);
uint64_t zd = float64_val(env->vfp.regs[rd]);
diff --git a/target-arm/op_addsub.h b/target-arm/op_addsub.h
index c02c92a..ca4a189 100644
--- a/target-arm/op_addsub.h
+++ b/target-arm/op_addsub.h
@@ -4,7 +4,7 @@
* Copyright (c) 2007 CodeSourcery.
* Written by Paul Brook
*
- * This code is licenced under the GPL.
+ * This code is licensed under the GPL.
*/
#ifdef ARITH_GE
diff --git a/target-arm/op_helper.c b/target-arm/op_helper.c
index 4635884..57e4977 100644
--- a/target-arm/op_helper.c
+++ b/target-arm/op_helper.c
@@ -16,17 +16,20 @@
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
-#include "exec.h"
+#include "cpu.h"
+#include "dyngen-exec.h"
#include "helper.h"
#define SIGNBIT (uint32_t)0x80000000
#define SIGNBIT64 ((uint64_t)1 << 63)
-void raise_exception(int tt)
+#if !defined(CONFIG_USER_ONLY)
+static void raise_exception(int tt)
{
env->exception_index = tt;
cpu_loop_exit(env);
}
+#endif
uint32_t HELPER(neon_tbl)(uint32_t ireg, uint32_t def,
uint32_t rn, uint32_t maxindex)
@@ -52,6 +55,8 @@ uint32_t HELPER(neon_tbl)(uint32_t ireg, uint32_t def,
#if !defined(CONFIG_USER_ONLY)
+#include "softmmu_exec.h"
+
#define MMUSUFFIX _mmu
#define SHIFT 0
diff --git a/target-arm/translate.c b/target-arm/translate.c
index badbc5f..34d5e6e 100644
--- a/target-arm/translate.c
+++ b/target-arm/translate.c
@@ -892,13 +892,29 @@ static inline void gen_add_datah_offset(DisasContext *s, unsigned int insn,
}
}
+static TCGv_ptr get_fpstatus_ptr(int neon)
+{
+ TCGv_ptr statusptr = tcg_temp_new_ptr();
+ int offset;
+ if (neon) {
+ offset = offsetof(CPUState, vfp.standard_fp_status);
+ } else {
+ offset = offsetof(CPUState, vfp.fp_status);
+ }
+ tcg_gen_addi_ptr(statusptr, cpu_env, offset);
+ return statusptr;
+}
+
#define VFP_OP2(name) \
static inline void gen_vfp_##name(int dp) \
{ \
- if (dp) \
- gen_helper_vfp_##name##d(cpu_F0d, cpu_F0d, cpu_F1d, cpu_env); \
- else \
- gen_helper_vfp_##name##s(cpu_F0s, cpu_F0s, cpu_F1s, cpu_env); \
+ TCGv_ptr fpst = get_fpstatus_ptr(0); \
+ if (dp) { \
+ gen_helper_vfp_##name##d(cpu_F0d, cpu_F0d, cpu_F1d, fpst); \
+ } else { \
+ gen_helper_vfp_##name##s(cpu_F0s, cpu_F0s, cpu_F1s, fpst); \
+ } \
+ tcg_temp_free_ptr(fpst); \
}
VFP_OP2(add)
@@ -911,11 +927,13 @@ VFP_OP2(div)
static inline void gen_vfp_F1_mul(int dp)
{
/* Like gen_vfp_mul() but put result in F1 */
+ TCGv_ptr fpst = get_fpstatus_ptr(0);
if (dp) {
- gen_helper_vfp_muld(cpu_F1d, cpu_F0d, cpu_F1d, cpu_env);
+ gen_helper_vfp_muld(cpu_F1d, cpu_F0d, cpu_F1d, fpst);
} else {
- gen_helper_vfp_muls(cpu_F1s, cpu_F0s, cpu_F1s, cpu_env);
+ gen_helper_vfp_muls(cpu_F1s, cpu_F0s, cpu_F1s, fpst);
}
+ tcg_temp_free_ptr(fpst);
}
static inline void gen_vfp_F1_neg(int dp)
@@ -979,14 +997,7 @@ static inline void gen_vfp_F1_ld0(int dp)
#define VFP_GEN_ITOF(name) \
static inline void gen_vfp_##name(int dp, int neon) \
{ \
- TCGv_ptr statusptr = tcg_temp_new_ptr(); \
- int offset; \
- if (neon) { \
- offset = offsetof(CPUState, vfp.standard_fp_status); \
- } else { \
- offset = offsetof(CPUState, vfp.fp_status); \
- } \
- tcg_gen_addi_ptr(statusptr, cpu_env, offset); \
+ TCGv_ptr statusptr = get_fpstatus_ptr(neon); \
if (dp) { \
gen_helper_vfp_##name##d(cpu_F0d, cpu_F0s, statusptr); \
} else { \
@@ -1002,14 +1013,7 @@ VFP_GEN_ITOF(sito)
#define VFP_GEN_FTOI(name) \
static inline void gen_vfp_##name(int dp, int neon) \
{ \
- TCGv_ptr statusptr = tcg_temp_new_ptr(); \
- int offset; \
- if (neon) { \
- offset = offsetof(CPUState, vfp.standard_fp_status); \
- } else { \
- offset = offsetof(CPUState, vfp.fp_status); \
- } \
- tcg_gen_addi_ptr(statusptr, cpu_env, offset); \
+ TCGv_ptr statusptr = get_fpstatus_ptr(neon); \
if (dp) { \
gen_helper_vfp_##name##d(cpu_F0s, cpu_F0d, statusptr); \
} else { \
@@ -1028,14 +1032,7 @@ VFP_GEN_FTOI(tosiz)
static inline void gen_vfp_##name(int dp, int shift, int neon) \
{ \
TCGv tmp_shift = tcg_const_i32(shift); \
- TCGv_ptr statusptr = tcg_temp_new_ptr(); \
- int offset; \
- if (neon) { \
- offset = offsetof(CPUState, vfp.standard_fp_status); \
- } else { \
- offset = offsetof(CPUState, vfp.fp_status); \
- } \
- tcg_gen_addi_ptr(statusptr, cpu_env, offset); \
+ TCGv_ptr statusptr = get_fpstatus_ptr(neon); \
if (dp) { \
gen_helper_vfp_##name##d(cpu_F0d, cpu_F0d, tmp_shift, statusptr); \
} else { \
@@ -1206,15 +1203,22 @@ static inline void gen_op_iwmmxt_##name##_M0_wRn(int rn) \
gen_helper_iwmmxt_##name(cpu_M0, cpu_M0, cpu_V1); \
}
-#define IWMMXT_OP_SIZE(name) \
-IWMMXT_OP(name##b) \
-IWMMXT_OP(name##w) \
-IWMMXT_OP(name##l)
+#define IWMMXT_OP_ENV(name) \
+static inline void gen_op_iwmmxt_##name##_M0_wRn(int rn) \
+{ \
+ iwmmxt_load_reg(cpu_V1, rn); \
+ gen_helper_iwmmxt_##name(cpu_M0, cpu_env, cpu_M0, cpu_V1); \
+}
-#define IWMMXT_OP_1(name) \
+#define IWMMXT_OP_ENV_SIZE(name) \
+IWMMXT_OP_ENV(name##b) \
+IWMMXT_OP_ENV(name##w) \
+IWMMXT_OP_ENV(name##l)
+
+#define IWMMXT_OP_ENV1(name) \
static inline void gen_op_iwmmxt_##name##_M0(void) \
{ \
- gen_helper_iwmmxt_##name(cpu_M0, cpu_M0); \
+ gen_helper_iwmmxt_##name(cpu_M0, cpu_env, cpu_M0); \
}
IWMMXT_OP(maddsq)
@@ -1228,51 +1232,51 @@ IWMMXT_OP(muluhw)
IWMMXT_OP(macsw)
IWMMXT_OP(macuw)
-IWMMXT_OP_SIZE(unpackl)
-IWMMXT_OP_SIZE(unpackh)
-
-IWMMXT_OP_1(unpacklub)
-IWMMXT_OP_1(unpackluw)
-IWMMXT_OP_1(unpacklul)
-IWMMXT_OP_1(unpackhub)
-IWMMXT_OP_1(unpackhuw)
-IWMMXT_OP_1(unpackhul)
-IWMMXT_OP_1(unpacklsb)
-IWMMXT_OP_1(unpacklsw)
-IWMMXT_OP_1(unpacklsl)
-IWMMXT_OP_1(unpackhsb)
-IWMMXT_OP_1(unpackhsw)
-IWMMXT_OP_1(unpackhsl)
-
-IWMMXT_OP_SIZE(cmpeq)
-IWMMXT_OP_SIZE(cmpgtu)
-IWMMXT_OP_SIZE(cmpgts)
-
-IWMMXT_OP_SIZE(mins)
-IWMMXT_OP_SIZE(minu)
-IWMMXT_OP_SIZE(maxs)
-IWMMXT_OP_SIZE(maxu)
-
-IWMMXT_OP_SIZE(subn)
-IWMMXT_OP_SIZE(addn)
-IWMMXT_OP_SIZE(subu)
-IWMMXT_OP_SIZE(addu)
-IWMMXT_OP_SIZE(subs)
-IWMMXT_OP_SIZE(adds)
-
-IWMMXT_OP(avgb0)
-IWMMXT_OP(avgb1)
-IWMMXT_OP(avgw0)
-IWMMXT_OP(avgw1)
+IWMMXT_OP_ENV_SIZE(unpackl)
+IWMMXT_OP_ENV_SIZE(unpackh)
+
+IWMMXT_OP_ENV1(unpacklub)
+IWMMXT_OP_ENV1(unpackluw)
+IWMMXT_OP_ENV1(unpacklul)
+IWMMXT_OP_ENV1(unpackhub)
+IWMMXT_OP_ENV1(unpackhuw)
+IWMMXT_OP_ENV1(unpackhul)
+IWMMXT_OP_ENV1(unpacklsb)
+IWMMXT_OP_ENV1(unpacklsw)
+IWMMXT_OP_ENV1(unpacklsl)
+IWMMXT_OP_ENV1(unpackhsb)
+IWMMXT_OP_ENV1(unpackhsw)
+IWMMXT_OP_ENV1(unpackhsl)
+
+IWMMXT_OP_ENV_SIZE(cmpeq)
+IWMMXT_OP_ENV_SIZE(cmpgtu)
+IWMMXT_OP_ENV_SIZE(cmpgts)
+
+IWMMXT_OP_ENV_SIZE(mins)
+IWMMXT_OP_ENV_SIZE(minu)
+IWMMXT_OP_ENV_SIZE(maxs)
+IWMMXT_OP_ENV_SIZE(maxu)
+
+IWMMXT_OP_ENV_SIZE(subn)
+IWMMXT_OP_ENV_SIZE(addn)
+IWMMXT_OP_ENV_SIZE(subu)
+IWMMXT_OP_ENV_SIZE(addu)
+IWMMXT_OP_ENV_SIZE(subs)
+IWMMXT_OP_ENV_SIZE(adds)
+
+IWMMXT_OP_ENV(avgb0)
+IWMMXT_OP_ENV(avgb1)
+IWMMXT_OP_ENV(avgw0)
+IWMMXT_OP_ENV(avgw1)
IWMMXT_OP(msadb)
-IWMMXT_OP(packuw)
-IWMMXT_OP(packul)
-IWMMXT_OP(packuq)
-IWMMXT_OP(packsw)
-IWMMXT_OP(packsl)
-IWMMXT_OP(packsq)
+IWMMXT_OP_ENV(packuw)
+IWMMXT_OP_ENV(packul)
+IWMMXT_OP_ENV(packuq)
+IWMMXT_OP_ENV(packsw)
+IWMMXT_OP_ENV(packsl)
+IWMMXT_OP_ENV(packsq)
static void gen_op_iwmmxt_set_mup(void)
{
@@ -2006,13 +2010,13 @@ static int disas_iwmmxt_insn(CPUState *env, DisasContext *s, uint32_t insn)
}
switch ((insn >> 22) & 3) {
case 1:
- gen_helper_iwmmxt_srlw(cpu_M0, cpu_M0, tmp);
+ gen_helper_iwmmxt_srlw(cpu_M0, cpu_env, cpu_M0, tmp);
break;
case 2:
- gen_helper_iwmmxt_srll(cpu_M0, cpu_M0, tmp);
+ gen_helper_iwmmxt_srll(cpu_M0, cpu_env, cpu_M0, tmp);
break;
case 3:
- gen_helper_iwmmxt_srlq(cpu_M0, cpu_M0, tmp);
+ gen_helper_iwmmxt_srlq(cpu_M0, cpu_env, cpu_M0, tmp);
break;
}
tcg_temp_free_i32(tmp);
@@ -2034,13 +2038,13 @@ static int disas_iwmmxt_insn(CPUState *env, DisasContext *s, uint32_t insn)
}
switch ((insn >> 22) & 3) {
case 1:
- gen_helper_iwmmxt_sraw(cpu_M0, cpu_M0, tmp);
+ gen_helper_iwmmxt_sraw(cpu_M0, cpu_env, cpu_M0, tmp);
break;
case 2:
- gen_helper_iwmmxt_sral(cpu_M0, cpu_M0, tmp);
+ gen_helper_iwmmxt_sral(cpu_M0, cpu_env, cpu_M0, tmp);
break;
case 3:
- gen_helper_iwmmxt_sraq(cpu_M0, cpu_M0, tmp);
+ gen_helper_iwmmxt_sraq(cpu_M0, cpu_env, cpu_M0, tmp);
break;
}
tcg_temp_free_i32(tmp);
@@ -2062,13 +2066,13 @@ static int disas_iwmmxt_insn(CPUState *env, DisasContext *s, uint32_t insn)
}
switch ((insn >> 22) & 3) {
case 1:
- gen_helper_iwmmxt_sllw(cpu_M0, cpu_M0, tmp);
+ gen_helper_iwmmxt_sllw(cpu_M0, cpu_env, cpu_M0, tmp);
break;
case 2:
- gen_helper_iwmmxt_slll(cpu_M0, cpu_M0, tmp);
+ gen_helper_iwmmxt_slll(cpu_M0, cpu_env, cpu_M0, tmp);
break;
case 3:
- gen_helper_iwmmxt_sllq(cpu_M0, cpu_M0, tmp);
+ gen_helper_iwmmxt_sllq(cpu_M0, cpu_env, cpu_M0, tmp);
break;
}
tcg_temp_free_i32(tmp);
@@ -2090,21 +2094,21 @@ static int disas_iwmmxt_insn(CPUState *env, DisasContext *s, uint32_t insn)
tcg_temp_free_i32(tmp);
return 1;
}
- gen_helper_iwmmxt_rorw(cpu_M0, cpu_M0, tmp);
+ gen_helper_iwmmxt_rorw(cpu_M0, cpu_env, cpu_M0, tmp);
break;
case 2:
if (gen_iwmmxt_shift(insn, 0x1f, tmp)) {
tcg_temp_free_i32(tmp);
return 1;
}
- gen_helper_iwmmxt_rorl(cpu_M0, cpu_M0, tmp);
+ gen_helper_iwmmxt_rorl(cpu_M0, cpu_env, cpu_M0, tmp);
break;
case 3:
if (gen_iwmmxt_shift(insn, 0x3f, tmp)) {
tcg_temp_free_i32(tmp);
return 1;
}
- gen_helper_iwmmxt_rorq(cpu_M0, cpu_M0, tmp);
+ gen_helper_iwmmxt_rorq(cpu_M0, cpu_env, cpu_M0, tmp);
break;
}
tcg_temp_free_i32(tmp);
@@ -2238,7 +2242,7 @@ static int disas_iwmmxt_insn(CPUState *env, DisasContext *s, uint32_t insn)
rd0 = (insn >> 16) & 0xf;
gen_op_iwmmxt_movq_M0_wRn(rd0);
tmp = tcg_const_i32(((insn >> 16) & 0xf0) | (insn & 0x0f));
- gen_helper_iwmmxt_shufh(cpu_M0, cpu_M0, tmp);
+ gen_helper_iwmmxt_shufh(cpu_M0, cpu_env, cpu_M0, tmp);
tcg_temp_free(tmp);
gen_op_iwmmxt_movq_wRn_M0(wrd);
gen_op_iwmmxt_set_mup();
@@ -2467,12 +2471,28 @@ static int disas_cp_insn(CPUState *env, DisasContext *s, uint32_t insn)
return 0;
}
-static int cp15_user_ok(uint32_t insn)
+static int cp15_user_ok(CPUState *env, uint32_t insn)
{
int cpn = (insn >> 16) & 0xf;
int cpm = insn & 0xf;
int op = ((insn >> 5) & 7) | ((insn >> 18) & 0x38);
+ if (arm_feature(env, ARM_FEATURE_V7) && cpn == 9) {
+ /* Performance monitor registers fall into three categories:
+ * (a) always UNDEF in usermode
+ * (b) UNDEF only if PMUSERENR.EN is 0
+ * (c) always read OK and UNDEF on write (PMUSERENR only)
+ */
+ if ((cpm == 12 && (op < 6)) ||
+ (cpm == 13 && (op < 3))) {
+ return env->cp15.c9_pmuserenr;
+ } else if (cpm == 14 && op == 0 && (insn & ARM_CP_RW_BIT)) {
+ /* PMUSERENR, read only */
+ return 1;
+ }
+ return 0;
+ }
+
if (cpn == 13 && cpm == 0) {
/* TLS register. */
if (op == 2 || (op == 3 && (insn & ARM_CP_RW_BIT)))
@@ -2559,7 +2579,7 @@ static int disas_cp15_insn(CPUState *env, DisasContext *s, uint32_t insn)
/* cdp */
return 1;
}
- if (IS_USER(s) && !cp15_user_ok(insn)) {
+ if (IS_USER(s) && !cp15_user_ok(env, insn)) {
return 1;
}
@@ -3706,13 +3726,13 @@ static int gen_neon_unzip(int rd, int rm, int size, int q)
if (q) {
switch (size) {
case 0:
- gen_helper_neon_qunzip8(tmp, tmp2);
+ gen_helper_neon_qunzip8(cpu_env, tmp, tmp2);
break;
case 1:
- gen_helper_neon_qunzip16(tmp, tmp2);
+ gen_helper_neon_qunzip16(cpu_env, tmp, tmp2);
break;
case 2:
- gen_helper_neon_qunzip32(tmp, tmp2);
+ gen_helper_neon_qunzip32(cpu_env, tmp, tmp2);
break;
default:
abort();
@@ -3720,10 +3740,10 @@ static int gen_neon_unzip(int rd, int rm, int size, int q)
} else {
switch (size) {
case 0:
- gen_helper_neon_unzip8(tmp, tmp2);
+ gen_helper_neon_unzip8(cpu_env, tmp, tmp2);
break;
case 1:
- gen_helper_neon_unzip16(tmp, tmp2);
+ gen_helper_neon_unzip16(cpu_env, tmp, tmp2);
break;
default:
abort();
@@ -3745,13 +3765,13 @@ static int gen_neon_zip(int rd, int rm, int size, int q)
if (q) {
switch (size) {
case 0:
- gen_helper_neon_qzip8(tmp, tmp2);
+ gen_helper_neon_qzip8(cpu_env, tmp, tmp2);
break;
case 1:
- gen_helper_neon_qzip16(tmp, tmp2);
+ gen_helper_neon_qzip16(cpu_env, tmp, tmp2);
break;
case 2:
- gen_helper_neon_qzip32(tmp, tmp2);
+ gen_helper_neon_qzip32(cpu_env, tmp, tmp2);
break;
default:
abort();
@@ -3759,10 +3779,10 @@ static int gen_neon_zip(int rd, int rm, int size, int q)
} else {
switch (size) {
case 0:
- gen_helper_neon_zip8(tmp, tmp2);
+ gen_helper_neon_zip8(cpu_env, tmp, tmp2);
break;
case 1:
- gen_helper_neon_zip16(tmp, tmp2);
+ gen_helper_neon_zip16(cpu_env, tmp, tmp2);
break;
default:
abort();
@@ -4162,9 +4182,9 @@ static inline void gen_neon_narrow(int size, TCGv dest, TCGv_i64 src)
static inline void gen_neon_narrow_sats(int size, TCGv dest, TCGv_i64 src)
{
switch (size) {
- case 0: gen_helper_neon_narrow_sat_s8(dest, src); break;
- case 1: gen_helper_neon_narrow_sat_s16(dest, src); break;
- case 2: gen_helper_neon_narrow_sat_s32(dest, src); break;
+ case 0: gen_helper_neon_narrow_sat_s8(dest, cpu_env, src); break;
+ case 1: gen_helper_neon_narrow_sat_s16(dest, cpu_env, src); break;
+ case 2: gen_helper_neon_narrow_sat_s32(dest, cpu_env, src); break;
default: abort();
}
}
@@ -4172,9 +4192,9 @@ static inline void gen_neon_narrow_sats(int size, TCGv dest, TCGv_i64 src)
static inline void gen_neon_narrow_satu(int size, TCGv dest, TCGv_i64 src)
{
switch (size) {
- case 0: gen_helper_neon_narrow_sat_u8(dest, src); break;
- case 1: gen_helper_neon_narrow_sat_u16(dest, src); break;
- case 2: gen_helper_neon_narrow_sat_u32(dest, src); break;
+ case 0: gen_helper_neon_narrow_sat_u8(dest, cpu_env, src); break;
+ case 1: gen_helper_neon_narrow_sat_u16(dest, cpu_env, src); break;
+ case 2: gen_helper_neon_narrow_sat_u32(dest, cpu_env, src); break;
default: abort();
}
}
@@ -4182,9 +4202,9 @@ static inline void gen_neon_narrow_satu(int size, TCGv dest, TCGv_i64 src)
static inline void gen_neon_unarrow_sats(int size, TCGv dest, TCGv_i64 src)
{
switch (size) {
- case 0: gen_helper_neon_unarrow_sat8(dest, src); break;
- case 1: gen_helper_neon_unarrow_sat16(dest, src); break;
- case 2: gen_helper_neon_unarrow_sat32(dest, src); break;
+ case 0: gen_helper_neon_unarrow_sat8(dest, cpu_env, src); break;
+ case 1: gen_helper_neon_unarrow_sat16(dest, cpu_env, src); break;
+ case 2: gen_helper_neon_unarrow_sat32(dest, cpu_env, src); break;
default: abort();
}
}
@@ -4276,8 +4296,8 @@ static inline void gen_neon_negl(TCGv_i64 var, int size)
static inline void gen_neon_addl_saturate(TCGv_i64 op0, TCGv_i64 op1, int size)
{
switch (size) {
- case 1: gen_helper_neon_addl_saturate_s32(op0, op0, op1); break;
- case 2: gen_helper_neon_addl_saturate_s64(op0, op0, op1); break;
+ case 1: gen_helper_neon_addl_saturate_s32(op0, cpu_env, op0, op1); break;
+ case 2: gen_helper_neon_addl_saturate_s64(op0, cpu_env, op0, op1); break;
default: abort();
}
}
@@ -4553,16 +4573,20 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
switch (op) {
case NEON_3R_VQADD:
if (u) {
- gen_helper_neon_qadd_u64(cpu_V0, cpu_V0, cpu_V1);
+ gen_helper_neon_qadd_u64(cpu_V0, cpu_env,
+ cpu_V0, cpu_V1);
} else {
- gen_helper_neon_qadd_s64(cpu_V0, cpu_V0, cpu_V1);
+ gen_helper_neon_qadd_s64(cpu_V0, cpu_env,
+ cpu_V0, cpu_V1);
}
break;
case NEON_3R_VQSUB:
if (u) {
- gen_helper_neon_qsub_u64(cpu_V0, cpu_V0, cpu_V1);
+ gen_helper_neon_qsub_u64(cpu_V0, cpu_env,
+ cpu_V0, cpu_V1);
} else {
- gen_helper_neon_qsub_s64(cpu_V0, cpu_V0, cpu_V1);
+ gen_helper_neon_qsub_s64(cpu_V0, cpu_env,
+ cpu_V0, cpu_V1);
}
break;
case NEON_3R_VSHL:
@@ -4574,9 +4598,11 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
break;
case NEON_3R_VQSHL:
if (u) {
- gen_helper_neon_qshl_u64(cpu_V0, cpu_V1, cpu_V0);
+ gen_helper_neon_qshl_u64(cpu_V0, cpu_env,
+ cpu_V1, cpu_V0);
} else {
- gen_helper_neon_qshl_s64(cpu_V0, cpu_V1, cpu_V0);
+ gen_helper_neon_qshl_s64(cpu_V0, cpu_env,
+ cpu_V1, cpu_V0);
}
break;
case NEON_3R_VRSHL:
@@ -4588,9 +4614,11 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
break;
case NEON_3R_VQRSHL:
if (u) {
- gen_helper_neon_qrshl_u64(cpu_V0, cpu_V1, cpu_V0);
+ gen_helper_neon_qrshl_u64(cpu_V0, cpu_env,
+ cpu_V1, cpu_V0);
} else {
- gen_helper_neon_qrshl_s64(cpu_V0, cpu_V1, cpu_V0);
+ gen_helper_neon_qrshl_s64(cpu_V0, cpu_env,
+ cpu_V1, cpu_V0);
}
break;
case NEON_3R_VADD_VSUB:
@@ -4688,7 +4716,7 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
GEN_NEON_INTEGER_OP(hadd);
break;
case NEON_3R_VQADD:
- GEN_NEON_INTEGER_OP(qadd);
+ GEN_NEON_INTEGER_OP_ENV(qadd);
break;
case NEON_3R_VRHADD:
GEN_NEON_INTEGER_OP(rhadd);
@@ -4731,7 +4759,7 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
GEN_NEON_INTEGER_OP(hsub);
break;
case NEON_3R_VQSUB:
- GEN_NEON_INTEGER_OP(qsub);
+ GEN_NEON_INTEGER_OP_ENV(qsub);
break;
case NEON_3R_VCGT:
GEN_NEON_INTEGER_OP(cgt);
@@ -4743,13 +4771,13 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
GEN_NEON_INTEGER_OP(shl);
break;
case NEON_3R_VQSHL:
- GEN_NEON_INTEGER_OP(qshl);
+ GEN_NEON_INTEGER_OP_ENV(qshl);
break;
case NEON_3R_VRSHL:
GEN_NEON_INTEGER_OP(rshl);
break;
case NEON_3R_VQRSHL:
- GEN_NEON_INTEGER_OP(qrshl);
+ GEN_NEON_INTEGER_OP_ENV(qrshl);
break;
case NEON_3R_VMAX:
GEN_NEON_INTEGER_OP(max);
@@ -4831,14 +4859,22 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
case NEON_3R_VQDMULH_VQRDMULH: /* Multiply high. */
if (!u) { /* VQDMULH */
switch (size) {
- case 1: gen_helper_neon_qdmulh_s16(tmp, tmp, tmp2); break;
- case 2: gen_helper_neon_qdmulh_s32(tmp, tmp, tmp2); break;
+ case 1:
+ gen_helper_neon_qdmulh_s16(tmp, cpu_env, tmp, tmp2);
+ break;
+ case 2:
+ gen_helper_neon_qdmulh_s32(tmp, cpu_env, tmp, tmp2);
+ break;
default: abort();
}
} else { /* VQRDMULH */
switch (size) {
- case 1: gen_helper_neon_qrdmulh_s16(tmp, tmp, tmp2); break;
- case 2: gen_helper_neon_qrdmulh_s32(tmp, tmp, tmp2); break;
+ case 1:
+ gen_helper_neon_qrdmulh_s16(tmp, cpu_env, tmp, tmp2);
+ break;
+ case 2:
+ gen_helper_neon_qrdmulh_s32(tmp, cpu_env, tmp, tmp2);
+ break;
default: abort();
}
}
@@ -4852,57 +4888,78 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
}
break;
case NEON_3R_FLOAT_ARITH: /* Floating point arithmetic. */
+ {
+ TCGv_ptr fpstatus = get_fpstatus_ptr(1);
switch ((u << 2) | size) {
case 0: /* VADD */
- gen_helper_neon_add_f32(tmp, tmp, tmp2);
+ case 4: /* VPADD */
+ gen_helper_vfp_adds(tmp, tmp, tmp2, fpstatus);
break;
case 2: /* VSUB */
- gen_helper_neon_sub_f32(tmp, tmp, tmp2);
- break;
- case 4: /* VPADD */
- gen_helper_neon_add_f32(tmp, tmp, tmp2);
+ gen_helper_vfp_subs(tmp, tmp, tmp2, fpstatus);
break;
case 6: /* VABD */
- gen_helper_neon_abd_f32(tmp, tmp, tmp2);
+ gen_helper_neon_abd_f32(tmp, tmp, tmp2, fpstatus);
break;
default:
abort();
}
+ tcg_temp_free_ptr(fpstatus);
break;
+ }
case NEON_3R_FLOAT_MULTIPLY:
- gen_helper_neon_mul_f32(tmp, tmp, tmp2);
+ {
+ TCGv_ptr fpstatus = get_fpstatus_ptr(1);
+ gen_helper_vfp_muls(tmp, tmp, tmp2, fpstatus);
if (!u) {
tcg_temp_free_i32(tmp2);
tmp2 = neon_load_reg(rd, pass);
if (size == 0) {
- gen_helper_neon_add_f32(tmp, tmp, tmp2);
+ gen_helper_vfp_adds(tmp, tmp, tmp2, fpstatus);
} else {
- gen_helper_neon_sub_f32(tmp, tmp2, tmp);
+ gen_helper_vfp_subs(tmp, tmp2, tmp, fpstatus);
}
}
+ tcg_temp_free_ptr(fpstatus);
break;
+ }
case NEON_3R_FLOAT_CMP:
+ {
+ TCGv_ptr fpstatus = get_fpstatus_ptr(1);
if (!u) {
- gen_helper_neon_ceq_f32(tmp, tmp, tmp2);
+ gen_helper_neon_ceq_f32(tmp, tmp, tmp2, fpstatus);
} else {
- if (size == 0)
- gen_helper_neon_cge_f32(tmp, tmp, tmp2);
- else
- gen_helper_neon_cgt_f32(tmp, tmp, tmp2);
+ if (size == 0) {
+ gen_helper_neon_cge_f32(tmp, tmp, tmp2, fpstatus);
+ } else {
+ gen_helper_neon_cgt_f32(tmp, tmp, tmp2, fpstatus);
+ }
}
+ tcg_temp_free_ptr(fpstatus);
break;
+ }
case NEON_3R_FLOAT_ACMP:
- if (size == 0)
- gen_helper_neon_acge_f32(tmp, tmp, tmp2);
- else
- gen_helper_neon_acgt_f32(tmp, tmp, tmp2);
+ {
+ TCGv_ptr fpstatus = get_fpstatus_ptr(1);
+ if (size == 0) {
+ gen_helper_neon_acge_f32(tmp, tmp, tmp2, fpstatus);
+ } else {
+ gen_helper_neon_acgt_f32(tmp, tmp, tmp2, fpstatus);
+ }
+ tcg_temp_free_ptr(fpstatus);
break;
+ }
case NEON_3R_FLOAT_MINMAX:
- if (size == 0)
- gen_helper_neon_max_f32(tmp, tmp, tmp2);
- else
- gen_helper_neon_min_f32(tmp, tmp, tmp2);
+ {
+ TCGv_ptr fpstatus = get_fpstatus_ptr(1);
+ if (size == 0) {
+ gen_helper_neon_max_f32(tmp, tmp, tmp2, fpstatus);
+ } else {
+ gen_helper_neon_min_f32(tmp, tmp, tmp2, fpstatus);
+ }
+ tcg_temp_free_ptr(fpstatus);
break;
+ }
case NEON_3R_VRECPS_VRSQRTS:
if (size == 0)
gen_helper_recps_f32(tmp, tmp, tmp2, cpu_env);
@@ -5009,14 +5066,15 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
gen_helper_neon_shl_u64(cpu_V0, cpu_V0, cpu_V1);
break;
case 6: /* VQSHLU */
- gen_helper_neon_qshlu_s64(cpu_V0, cpu_V0, cpu_V1);
+ gen_helper_neon_qshlu_s64(cpu_V0, cpu_env,
+ cpu_V0, cpu_V1);
break;
case 7: /* VQSHL */
if (u) {
- gen_helper_neon_qshl_u64(cpu_V0,
+ gen_helper_neon_qshl_u64(cpu_V0, cpu_env,
cpu_V0, cpu_V1);
} else {
- gen_helper_neon_qshl_s64(cpu_V0,
+ gen_helper_neon_qshl_s64(cpu_V0, cpu_env,
cpu_V0, cpu_V1);
}
break;
@@ -5068,20 +5126,23 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
case 6: /* VQSHLU */
switch (size) {
case 0:
- gen_helper_neon_qshlu_s8(tmp, tmp, tmp2);
+ gen_helper_neon_qshlu_s8(tmp, cpu_env,
+ tmp, tmp2);
break;
case 1:
- gen_helper_neon_qshlu_s16(tmp, tmp, tmp2);
+ gen_helper_neon_qshlu_s16(tmp, cpu_env,
+ tmp, tmp2);
break;
case 2:
- gen_helper_neon_qshlu_s32(tmp, tmp, tmp2);
+ gen_helper_neon_qshlu_s32(tmp, cpu_env,
+ tmp, tmp2);
break;
default:
abort();
}
break;
case 7: /* VQSHL */
- GEN_NEON_INTEGER_OP(qshl);
+ GEN_NEON_INTEGER_OP_ENV(qshl);
break;
}
tcg_temp_free_i32(tmp2);
@@ -5590,18 +5651,20 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
tmp2 = neon_load_reg(rn, pass);
if (op == 12) {
if (size == 1) {
- gen_helper_neon_qdmulh_s16(tmp, tmp, tmp2);
+ gen_helper_neon_qdmulh_s16(tmp, cpu_env, tmp, tmp2);
} else {
- gen_helper_neon_qdmulh_s32(tmp, tmp, tmp2);
+ gen_helper_neon_qdmulh_s32(tmp, cpu_env, tmp, tmp2);
}
} else if (op == 13) {
if (size == 1) {
- gen_helper_neon_qrdmulh_s16(tmp, tmp, tmp2);
+ gen_helper_neon_qrdmulh_s16(tmp, cpu_env, tmp, tmp2);
} else {
- gen_helper_neon_qrdmulh_s32(tmp, tmp, tmp2);
+ gen_helper_neon_qrdmulh_s32(tmp, cpu_env, tmp, tmp2);
}
} else if (op & 1) {
- gen_helper_neon_mul_f32(tmp, tmp, tmp2);
+ TCGv_ptr fpstatus = get_fpstatus_ptr(1);
+ gen_helper_vfp_muls(tmp, tmp, tmp2, fpstatus);
+ tcg_temp_free_ptr(fpstatus);
} else {
switch (size) {
case 0: gen_helper_neon_mul_u8(tmp, tmp, tmp2); break;
@@ -5619,14 +5682,22 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
gen_neon_add(size, tmp, tmp2);
break;
case 1:
- gen_helper_neon_add_f32(tmp, tmp, tmp2);
+ {
+ TCGv_ptr fpstatus = get_fpstatus_ptr(1);
+ gen_helper_vfp_adds(tmp, tmp, tmp2, fpstatus);
+ tcg_temp_free_ptr(fpstatus);
break;
+ }
case 4:
gen_neon_rsb(size, tmp, tmp2);
break;
case 5:
- gen_helper_neon_sub_f32(tmp, tmp2, tmp);
+ {
+ TCGv_ptr fpstatus = get_fpstatus_ptr(1);
+ gen_helper_vfp_subs(tmp, tmp2, tmp, fpstatus);
+ tcg_temp_free_ptr(fpstatus);
break;
+ }
default:
abort();
}
@@ -5960,17 +6031,29 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
break;
case NEON_2RM_VQABS:
switch (size) {
- case 0: gen_helper_neon_qabs_s8(tmp, tmp); break;
- case 1: gen_helper_neon_qabs_s16(tmp, tmp); break;
- case 2: gen_helper_neon_qabs_s32(tmp, tmp); break;
+ case 0:
+ gen_helper_neon_qabs_s8(tmp, cpu_env, tmp);
+ break;
+ case 1:
+ gen_helper_neon_qabs_s16(tmp, cpu_env, tmp);
+ break;
+ case 2:
+ gen_helper_neon_qabs_s32(tmp, cpu_env, tmp);
+ break;
default: abort();
}
break;
case NEON_2RM_VQNEG:
switch (size) {
- case 0: gen_helper_neon_qneg_s8(tmp, tmp); break;
- case 1: gen_helper_neon_qneg_s16(tmp, tmp); break;
- case 2: gen_helper_neon_qneg_s32(tmp, tmp); break;
+ case 0:
+ gen_helper_neon_qneg_s8(tmp, cpu_env, tmp);
+ break;
+ case 1:
+ gen_helper_neon_qneg_s16(tmp, cpu_env, tmp);
+ break;
+ case 2:
+ gen_helper_neon_qneg_s32(tmp, cpu_env, tmp);
+ break;
default: abort();
}
break;
@@ -6024,30 +6107,50 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
tcg_temp_free(tmp2);
break;
case NEON_2RM_VCGT0_F:
+ {
+ TCGv_ptr fpstatus = get_fpstatus_ptr(1);
tmp2 = tcg_const_i32(0);
- gen_helper_neon_cgt_f32(tmp, tmp, tmp2);
+ gen_helper_neon_cgt_f32(tmp, tmp, tmp2, fpstatus);
tcg_temp_free(tmp2);
+ tcg_temp_free_ptr(fpstatus);
break;
+ }
case NEON_2RM_VCGE0_F:
+ {
+ TCGv_ptr fpstatus = get_fpstatus_ptr(1);
tmp2 = tcg_const_i32(0);
- gen_helper_neon_cge_f32(tmp, tmp, tmp2);
+ gen_helper_neon_cge_f32(tmp, tmp, tmp2, fpstatus);
tcg_temp_free(tmp2);
+ tcg_temp_free_ptr(fpstatus);
break;
+ }
case NEON_2RM_VCEQ0_F:
+ {
+ TCGv_ptr fpstatus = get_fpstatus_ptr(1);
tmp2 = tcg_const_i32(0);
- gen_helper_neon_ceq_f32(tmp, tmp, tmp2);
+ gen_helper_neon_ceq_f32(tmp, tmp, tmp2, fpstatus);
tcg_temp_free(tmp2);
+ tcg_temp_free_ptr(fpstatus);
break;
+ }
case NEON_2RM_VCLE0_F:
+ {
+ TCGv_ptr fpstatus = get_fpstatus_ptr(1);
tmp2 = tcg_const_i32(0);
- gen_helper_neon_cge_f32(tmp, tmp2, tmp);
+ gen_helper_neon_cge_f32(tmp, tmp2, tmp, fpstatus);
tcg_temp_free(tmp2);
+ tcg_temp_free_ptr(fpstatus);
break;
+ }
case NEON_2RM_VCLT0_F:
+ {
+ TCGv_ptr fpstatus = get_fpstatus_ptr(1);
tmp2 = tcg_const_i32(0);
- gen_helper_neon_cgt_f32(tmp, tmp2, tmp);
+ gen_helper_neon_cgt_f32(tmp, tmp2, tmp, fpstatus);
tcg_temp_free(tmp2);
+ tcg_temp_free_ptr(fpstatus);
break;
+ }
case NEON_2RM_VABS_F:
gen_vfp_abs(0);
break;
diff --git a/target-cris/exec.h b/target-cris/exec.h
deleted file mode 100644
index 3294abe..0000000
--- a/target-cris/exec.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * CRIS execution defines
- *
- * Copyright (c) 2007 AXIS Communications AB
- * Written by Edgar E. Iglesias
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library 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 Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
- */
-#include "dyngen-exec.h"
-
-register struct CPUCRISState *env asm(AREG0);
-
-#include "cpu.h"
-
-#if !defined(CONFIG_USER_ONLY)
-#include "softmmu_exec.h"
-#endif
diff --git a/target-cris/op_helper.c b/target-cris/op_helper.c
index b3ddd33..246f08f 100644
--- a/target-cris/op_helper.c
+++ b/target-cris/op_helper.c
@@ -18,7 +18,8 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
-#include "exec.h"
+#include "cpu.h"
+#include "dyngen-exec.h"
#include "mmu.h"
#include "helper.h"
#include "host-utils.h"
@@ -35,6 +36,7 @@
#endif
#if !defined(CONFIG_USER_ONLY)
+#include "softmmu_exec.h"
#define MMUSUFFIX _mmu
diff --git a/target-i386/cpu.h b/target-i386/cpu.h
index 9819b5f..dd6c5fa 100644
--- a/target-i386/cpu.h
+++ b/target-i386/cpu.h
@@ -1050,6 +1050,9 @@ void cpu_x86_inject_mce(Monitor *mon, CPUState *cenv, int bank,
/* op_helper.c */
void do_interrupt(CPUState *env);
void do_interrupt_x86_hardirq(CPUState *env, int intno, int is_hw);
+void QEMU_NORETURN raise_exception_env(int exception_index, CPUState *nenv);
+void QEMU_NORETURN raise_exception_err_env(CPUState *nenv, int exception_index,
+ int error_code);
void do_smm_enter(CPUState *env1);
diff --git a/target-i386/exec.h b/target-i386/exec.h
deleted file mode 100644
index dd9bce4..0000000
--- a/target-i386/exec.h
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * i386 execution defines
- *
- * Copyright (c) 2003 Fabrice Bellard
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
- */
-#include "config.h"
-#include "dyngen-exec.h"
-
-/* XXX: factorize this mess */
-#ifdef TARGET_X86_64
-#define TARGET_LONG_BITS 64
-#else
-#define TARGET_LONG_BITS 32
-#endif
-
-#include "cpu-defs.h"
-
-register struct CPUX86State *env asm(AREG0);
-
-#include "qemu-common.h"
-#include "qemu-log.h"
-
-#include "cpu.h"
-
-/* op_helper.c */
-void QEMU_NORETURN raise_exception_err(int exception_index, int error_code);
-void QEMU_NORETURN raise_exception(int exception_index);
-void QEMU_NORETURN raise_exception_env(int exception_index, CPUState *nenv);
-
-/* n must be a constant to be efficient */
-static inline target_long lshift(target_long x, int n)
-{
- if (n >= 0)
- return x << n;
- else
- return x >> (-n);
-}
-
-#include "helper.h"
-
-#if !defined(CONFIG_USER_ONLY)
-
-#include "softmmu_exec.h"
-
-#endif /* !defined(CONFIG_USER_ONLY) */
-
-#define RC_MASK 0xc00
-#define RC_NEAR 0x000
-#define RC_DOWN 0x400
-#define RC_UP 0x800
-#define RC_CHOP 0xc00
-
-#define MAXTAN 9223372036854775808.0
-
-/* the following deal with x86 long double-precision numbers */
-#define MAXEXPD 0x7fff
-#define EXPBIAS 16383
-#define EXPD(fp) (fp.l.upper & 0x7fff)
-#define SIGND(fp) ((fp.l.upper) & 0x8000)
-#define MANTD(fp) (fp.l.lower)
-#define BIASEXPONENT(fp) fp.l.upper = (fp.l.upper & ~(0x7fff)) | EXPBIAS
-
-static inline void fpush(void)
-{
- env->fpstt = (env->fpstt - 1) & 7;
- env->fptags[env->fpstt] = 0; /* validate stack entry */
-}
-
-static inline void fpop(void)
-{
- env->fptags[env->fpstt] = 1; /* invvalidate stack entry */
- env->fpstt = (env->fpstt + 1) & 7;
-}
-
-static inline floatx80 helper_fldt(target_ulong ptr)
-{
- CPU_LDoubleU temp;
-
- temp.l.lower = ldq(ptr);
- temp.l.upper = lduw(ptr + 8);
- return temp.d;
-}
-
-static inline void helper_fstt(floatx80 f, target_ulong ptr)
-{
- CPU_LDoubleU temp;
-
- temp.d = f;
- stq(ptr, temp.l.lower);
- stw(ptr + 8, temp.l.upper);
-}
-
-#define FPUS_IE (1 << 0)
-#define FPUS_DE (1 << 1)
-#define FPUS_ZE (1 << 2)
-#define FPUS_OE (1 << 3)
-#define FPUS_UE (1 << 4)
-#define FPUS_PE (1 << 5)
-#define FPUS_SF (1 << 6)
-#define FPUS_SE (1 << 7)
-#define FPUS_B (1 << 15)
-
-#define FPUC_EM 0x3f
-
-static inline uint32_t compute_eflags(void)
-{
- return env->eflags | helper_cc_compute_all(CC_OP) | (DF & DF_MASK);
-}
-
-/* NOTE: CC_OP must be modified manually to CC_OP_EFLAGS */
-static inline void load_eflags(int eflags, int update_mask)
-{
- CC_SRC = eflags & (CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C);
- DF = 1 - (2 * ((eflags >> 10) & 1));
- env->eflags = (env->eflags & ~update_mask) |
- (eflags & update_mask) | 0x2;
-}
-
-/* load efer and update the corresponding hflags. XXX: do consistency
- checks with cpuid bits ? */
-static inline void cpu_load_efer(CPUState *env, uint64_t val)
-{
- env->efer = val;
- env->hflags &= ~(HF_LMA_MASK | HF_SVME_MASK);
- if (env->efer & MSR_EFER_LMA)
- env->hflags |= HF_LMA_MASK;
- if (env->efer & MSR_EFER_SVME)
- env->hflags |= HF_SVME_MASK;
-}
diff --git a/target-i386/helper.c b/target-i386/helper.c
index e9be104..182009a 100644
--- a/target-i386/helper.c
+++ b/target-i386/helper.c
@@ -1027,8 +1027,6 @@ int check_hw_breakpoints(CPUState *env, int force_dr6_update)
static CPUDebugExcpHandler *prev_debug_excp_handler;
-void raise_exception_env(int exception_index, CPUState *env);
-
static void breakpoint_handler(CPUState *env)
{
CPUBreakpoint *bp;
diff --git a/target-i386/op_helper.c b/target-i386/op_helper.c
index 315e18b..1380934 100644
--- a/target-i386/op_helper.c
+++ b/target-i386/op_helper.c
@@ -18,12 +18,20 @@
*/
#include <math.h>
-#include "exec.h"
+#include "cpu.h"
+#include "dyngen-exec.h"
#include "host-utils.h"
#include "ioport.h"
+#include "qemu-common.h"
+#include "qemu-log.h"
+#include "cpu-defs.h"
+#include "helper.h"
-//#define DEBUG_PCALL
+#if !defined(CONFIG_USER_ONLY)
+#include "softmmu_exec.h"
+#endif /* !defined(CONFIG_USER_ONLY) */
+//#define DEBUG_PCALL
#ifdef DEBUG_PCALL
# define LOG_PCALL(...) qemu_log_mask(CPU_LOG_PCALL, ## __VA_ARGS__)
@@ -34,6 +42,101 @@
# define LOG_PCALL_STATE(env) do { } while (0)
#endif
+/* n must be a constant to be efficient */
+static inline target_long lshift(target_long x, int n)
+{
+ if (n >= 0) {
+ return x << n;
+ } else {
+ return x >> (-n);
+ }
+}
+
+#define RC_MASK 0xc00
+#define RC_NEAR 0x000
+#define RC_DOWN 0x400
+#define RC_UP 0x800
+#define RC_CHOP 0xc00
+
+#define MAXTAN 9223372036854775808.0
+
+/* the following deal with x86 long double-precision numbers */
+#define MAXEXPD 0x7fff
+#define EXPBIAS 16383
+#define EXPD(fp) (fp.l.upper & 0x7fff)
+#define SIGND(fp) ((fp.l.upper) & 0x8000)
+#define MANTD(fp) (fp.l.lower)
+#define BIASEXPONENT(fp) fp.l.upper = (fp.l.upper & ~(0x7fff)) | EXPBIAS
+
+static inline void fpush(void)
+{
+ env->fpstt = (env->fpstt - 1) & 7;
+ env->fptags[env->fpstt] = 0; /* validate stack entry */
+}
+
+static inline void fpop(void)
+{
+ env->fptags[env->fpstt] = 1; /* invvalidate stack entry */
+ env->fpstt = (env->fpstt + 1) & 7;
+}
+
+static inline floatx80 helper_fldt(target_ulong ptr)
+{
+ CPU_LDoubleU temp;
+
+ temp.l.lower = ldq(ptr);
+ temp.l.upper = lduw(ptr + 8);
+ return temp.d;
+}
+
+static inline void helper_fstt(floatx80 f, target_ulong ptr)
+{
+ CPU_LDoubleU temp;
+
+ temp.d = f;
+ stq(ptr, temp.l.lower);
+ stw(ptr + 8, temp.l.upper);
+}
+
+#define FPUS_IE (1 << 0)
+#define FPUS_DE (1 << 1)
+#define FPUS_ZE (1 << 2)
+#define FPUS_OE (1 << 3)
+#define FPUS_UE (1 << 4)
+#define FPUS_PE (1 << 5)
+#define FPUS_SF (1 << 6)
+#define FPUS_SE (1 << 7)
+#define FPUS_B (1 << 15)
+
+#define FPUC_EM 0x3f
+
+static inline uint32_t compute_eflags(void)
+{
+ return env->eflags | helper_cc_compute_all(CC_OP) | (DF & DF_MASK);
+}
+
+/* NOTE: CC_OP must be modified manually to CC_OP_EFLAGS */
+static inline void load_eflags(int eflags, int update_mask)
+{
+ CC_SRC = eflags & (CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C);
+ DF = 1 - (2 * ((eflags >> 10) & 1));
+ env->eflags = (env->eflags & ~update_mask) |
+ (eflags & update_mask) | 0x2;
+}
+
+/* load efer and update the corresponding hflags. XXX: do consistency
+ checks with cpuid bits ? */
+static inline void cpu_load_efer(CPUState *env, uint64_t val)
+{
+ env->efer = val;
+ env->hflags &= ~(HF_LMA_MASK | HF_SVME_MASK);
+ if (env->efer & MSR_EFER_LMA) {
+ env->hflags |= HF_LMA_MASK;
+ }
+ if (env->efer & MSR_EFER_SVME) {
+ env->hflags |= HF_SVME_MASK;
+ }
+}
#if 0
#define raise_exception_err(a, b)\
@@ -43,6 +146,9 @@ do {\
} while (0)
#endif
+static void QEMU_NORETURN raise_exception_err(int exception_index,
+ int error_code);
+
static const uint8_t parity_table[256] = {
CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
@@ -1381,12 +1487,20 @@ static void QEMU_NORETURN raise_interrupt(int intno, int is_int, int error_code,
/* shortcuts to generate exceptions */
-void raise_exception_err(int exception_index, int error_code)
+static void QEMU_NORETURN raise_exception_err(int exception_index,
+ int error_code)
+{
+ raise_interrupt(exception_index, 0, error_code, 0);
+}
+
+void raise_exception_err_env(CPUState *nenv, int exception_index,
+ int error_code)
{
+ env = nenv;
raise_interrupt(exception_index, 0, error_code, 0);
}
-void raise_exception(int exception_index)
+static void QEMU_NORETURN raise_exception(int exception_index)
{
raise_interrupt(exception_index, 0, 0, 0);
}
@@ -4426,6 +4540,49 @@ void helper_frstor(target_ulong ptr, int data32)
}
}
+
+#if defined(CONFIG_USER_ONLY)
+void cpu_x86_load_seg(CPUX86State *s, int seg_reg, int selector)
+{
+ CPUX86State *saved_env;
+
+ saved_env = env;
+ env = s;
+ if (!(env->cr[0] & CR0_PE_MASK) || (env->eflags & VM_MASK)) {
+ selector &= 0xffff;
+ cpu_x86_load_seg_cache(env, seg_reg, selector,
+ (selector << 4), 0xffff, 0);
+ } else {
+ helper_load_seg(seg_reg, selector);
+ }
+ env = saved_env;
+}
+
+void cpu_x86_fsave(CPUX86State *s, target_ulong ptr, int data32)
+{
+ CPUX86State *saved_env;
+
+ saved_env = env;
+ env = s;
+
+ helper_fsave(ptr, data32);
+
+ env = saved_env;
+}
+
+void cpu_x86_frstor(CPUX86State *s, target_ulong ptr, int data32)
+{
+ CPUX86State *saved_env;
+
+ saved_env = env;
+ env = s;
+
+ helper_frstor(ptr, data32);
+
+ env = saved_env;
+}
+#endif
+
void helper_fxsave(target_ulong ptr, int data64)
{
int fpus, fptag, i, nb_xmm_regs;
diff --git a/target-lm32/exec.h b/target-lm32/exec.h
deleted file mode 100644
index 2a227b2..0000000
--- a/target-lm32/exec.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * LatticeMico32 execution defines.
- *
- * Copyright (c) 2010 Michael Walle <michael@walle.cc>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "dyngen-exec.h"
-
-register struct CPULM32State *env asm(AREG0);
-
-#include "cpu.h"
-
-static inline int cpu_halted(CPUState *env)
-{
- if (!env->halted) {
- return 0;
- }
-
- /* IRQ execeptions wakes us up. */
- if (cpu_has_work(env)) {
- env->halted = 0;
- return 0;
- }
- return EXCP_HALTED;
-}
diff --git a/target-lm32/op_helper.c b/target-lm32/op_helper.c
index a34cecd..32b9a03 100644
--- a/target-lm32/op_helper.c
+++ b/target-lm32/op_helper.c
@@ -1,5 +1,6 @@
#include <assert.h>
-#include "exec.h"
+#include "cpu.h"
+#include "dyngen-exec.h"
#include "helper.h"
#include "host-utils.h"
diff --git a/target-m68k/exec.h b/target-m68k/exec.h
deleted file mode 100644
index 93e7912..0000000
--- a/target-m68k/exec.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * m68k execution defines
- *
- * Copyright (c) 2005-2006 CodeSourcery
- * Written by Paul Brook
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library 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 Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
- */
-#include "dyngen-exec.h"
-
-register struct CPUM68KState *env asm(AREG0);
-
-#include "cpu.h"
-
-#if !defined(CONFIG_USER_ONLY)
-#include "softmmu_exec.h"
-#endif
diff --git a/target-m68k/op_helper.c b/target-m68k/op_helper.c
index 237fc4c..764b6a0 100644
--- a/target-m68k/op_helper.c
+++ b/target-m68k/op_helper.c
@@ -16,7 +16,8 @@
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
-#include "exec.h"
+#include "cpu.h"
+#include "dyngen-exec.h"
#include "helpers.h"
#if defined(CONFIG_USER_ONLY)
@@ -34,6 +35,8 @@ void do_interrupt_m68k_hardirq(CPUState *env1)
extern int semihosting_enabled;
+#include "softmmu_exec.h"
+
#define MMUSUFFIX _mmu
#define SHIFT 0
diff --git a/target-microblaze/cpu.h b/target-microblaze/cpu.h
index 51a13e3..76f4fc4 100644
--- a/target-microblaze/cpu.h
+++ b/target-microblaze/cpu.h
@@ -347,8 +347,8 @@ static inline void cpu_get_tb_cpu_state(CPUState *env, target_ulong *pc,
}
#if !defined(CONFIG_USER_ONLY)
-void do_unassigned_access(target_phys_addr_t addr, int is_write, int is_exec,
- int is_asi, int size);
+void cpu_unassigned_access(CPUState *env1, target_phys_addr_t addr,
+ int is_write, int is_exec, int is_asi, int size);
#endif
static inline bool cpu_has_work(CPUState *env)
diff --git a/target-microblaze/exec.h b/target-microblaze/exec.h
deleted file mode 100644
index 71b4d39..0000000
--- a/target-microblaze/exec.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Microblaze execution defines
- *
- * Copyright (c) 2009 Edgar E. Iglesias
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library 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 Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
- */
-#include "dyngen-exec.h"
-
-register struct CPUMBState *env asm(AREG0);
-
-#include "cpu.h"
-
-#if !defined(CONFIG_USER_ONLY)
-#include "softmmu_exec.h"
-#endif
diff --git a/target-microblaze/op_helper.c b/target-microblaze/op_helper.c
index 1a0a476..189c59c 100644
--- a/target-microblaze/op_helper.c
+++ b/target-microblaze/op_helper.c
@@ -18,13 +18,16 @@
*/
#include <assert.h>
-#include "exec.h"
+#include "cpu.h"
+#include "dyngen-exec.h"
#include "helper.h"
#include "host-utils.h"
#define D(x)
#if !defined(CONFIG_USER_ONLY)
+#include "softmmu_exec.h"
+
#define MMUSUFFIX _mmu
#define SHIFT 0
#include "softmmu_template.h"
@@ -488,20 +491,14 @@ void helper_mmu_write(uint32_t rn, uint32_t v)
mmu_write(env, rn, v);
}
-void do_unassigned_access(target_phys_addr_t addr, int is_write, int is_exec,
- int is_asi, int size)
+void cpu_unassigned_access(CPUState *env1, target_phys_addr_t addr,
+ int is_write, int is_exec, int is_asi, int size)
{
CPUState *saved_env;
- if (!cpu_single_env) {
- /* XXX: ??? */
- return;
- }
-
- /* XXX: hack to restore env in all cases, even if not called from
- generated code */
saved_env = env;
- env = cpu_single_env;
+ env = env1;
+
qemu_log_mask(CPU_LOG_INT, "Unassigned " TARGET_FMT_plx " wr=%d exe=%d\n",
addr, is_write, is_exec);
if (!(env->sregs[SR_MSR] & MSR_EE)) {
diff --git a/target-microblaze/translate.c b/target-microblaze/translate.c
index 31e8306..41beb0a 100644
--- a/target-microblaze/translate.c
+++ b/target-microblaze/translate.c
@@ -1850,6 +1850,7 @@ CPUState *cpu_mb_init (const char *cpu_model)
cpu_exec_init(env);
cpu_reset(env);
+ qemu_init_vcpu(env);
set_float_rounding_mode(float_round_nearest_even, &env->fp_status);
if (tcg_initialized)
diff --git a/target-mips/cpu.h b/target-mips/cpu.h
index b0ac4da..030f499 100644
--- a/target-mips/cpu.h
+++ b/target-mips/cpu.h
@@ -1,6 +1,8 @@
#if !defined (__MIPS_CPU_H__)
#define __MIPS_CPU_H__
+//#define DEBUG_OP
+
#define TARGET_HAS_ICE 1
#define ELF_MACHINE EM_MIPS
@@ -493,8 +495,8 @@ void r4k_helper_tlbwr (void);
void r4k_helper_tlbp (void);
void r4k_helper_tlbr (void);
-void do_unassigned_access(target_phys_addr_t addr, int is_write, int is_exec,
- int unused, int size);
+void cpu_unassigned_access(CPUState *env, target_phys_addr_t addr,
+ int is_write, int is_exec, int unused, int size);
#endif
void mips_cpu_list (FILE *f, fprintf_function cpu_fprintf);
diff --git a/target-mips/exec.h b/target-mips/exec.h
deleted file mode 100644
index e787e9a..0000000
--- a/target-mips/exec.h
+++ /dev/null
@@ -1,60 +0,0 @@
-#if !defined(__QEMU_MIPS_EXEC_H__)
-#define __QEMU_MIPS_EXEC_H__
-
-//#define DEBUG_OP
-
-#include "config.h"
-#include "mips-defs.h"
-#include "dyngen-exec.h"
-#include "cpu-defs.h"
-
-register struct CPUMIPSState *env asm(AREG0);
-
-#include "cpu.h"
-
-#if !defined(CONFIG_USER_ONLY)
-#include "softmmu_exec.h"
-#endif /* !defined(CONFIG_USER_ONLY) */
-
-static inline void compute_hflags(CPUState *env)
-{
- env->hflags &= ~(MIPS_HFLAG_COP1X | MIPS_HFLAG_64 | MIPS_HFLAG_CP0 |
- MIPS_HFLAG_F64 | MIPS_HFLAG_FPU | MIPS_HFLAG_KSU |
- MIPS_HFLAG_UX);
- if (!(env->CP0_Status & (1 << CP0St_EXL)) &&
- !(env->CP0_Status & (1 << CP0St_ERL)) &&
- !(env->hflags & MIPS_HFLAG_DM)) {
- env->hflags |= (env->CP0_Status >> CP0St_KSU) & MIPS_HFLAG_KSU;
- }
-#if defined(TARGET_MIPS64)
- if (((env->hflags & MIPS_HFLAG_KSU) != MIPS_HFLAG_UM) ||
- (env->CP0_Status & (1 << CP0St_PX)) ||
- (env->CP0_Status & (1 << CP0St_UX)))
- env->hflags |= MIPS_HFLAG_64;
- if (env->CP0_Status & (1 << CP0St_UX))
- env->hflags |= MIPS_HFLAG_UX;
-#endif
- if ((env->CP0_Status & (1 << CP0St_CU0)) ||
- !(env->hflags & MIPS_HFLAG_KSU))
- env->hflags |= MIPS_HFLAG_CP0;
- if (env->CP0_Status & (1 << CP0St_CU1))
- env->hflags |= MIPS_HFLAG_FPU;
- if (env->CP0_Status & (1 << CP0St_FR))
- env->hflags |= MIPS_HFLAG_F64;
- if (env->insn_flags & ISA_MIPS32R2) {
- if (env->active_fpu.fcr0 & (1 << FCR0_F64))
- env->hflags |= MIPS_HFLAG_COP1X;
- } else if (env->insn_flags & ISA_MIPS32) {
- if (env->hflags & MIPS_HFLAG_64)
- env->hflags |= MIPS_HFLAG_COP1X;
- } else if (env->insn_flags & ISA_MIPS4) {
- /* All supported MIPS IV CPUs use the XX (CU3) to enable
- and disable the MIPS IV extensions to the MIPS III ISA.
- Some other MIPS IV CPUs ignore the bit, so the check here
- would be too restrictive for them. */
- if (env->CP0_Status & (1 << CP0St_CU3))
- env->hflags |= MIPS_HFLAG_COP1X;
- }
-}
-
-#endif /* !defined(__QEMU_MIPS_EXEC_H__) */
diff --git a/target-mips/op_helper.c b/target-mips/op_helper.c
index 6b966b1..185ae40 100644
--- a/target-mips/op_helper.c
+++ b/target-mips/op_helper.c
@@ -17,16 +17,70 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
-#include "exec.h"
+#include "cpu.h"
+#include "dyngen-exec.h"
#include "host-utils.h"
#include "helper.h"
+#if !defined(CONFIG_USER_ONLY)
+#include "softmmu_exec.h"
+#endif /* !defined(CONFIG_USER_ONLY) */
+
#ifndef CONFIG_USER_ONLY
static inline void cpu_mips_tlb_flush (CPUState *env, int flush_global);
#endif
+static inline void compute_hflags(CPUState *env)
+{
+ env->hflags &= ~(MIPS_HFLAG_COP1X | MIPS_HFLAG_64 | MIPS_HFLAG_CP0 |
+ MIPS_HFLAG_F64 | MIPS_HFLAG_FPU | MIPS_HFLAG_KSU |
+ MIPS_HFLAG_UX);
+ if (!(env->CP0_Status & (1 << CP0St_EXL)) &&
+ !(env->CP0_Status & (1 << CP0St_ERL)) &&
+ !(env->hflags & MIPS_HFLAG_DM)) {
+ env->hflags |= (env->CP0_Status >> CP0St_KSU) & MIPS_HFLAG_KSU;
+ }
+#if defined(TARGET_MIPS64)
+ if (((env->hflags & MIPS_HFLAG_KSU) != MIPS_HFLAG_UM) ||
+ (env->CP0_Status & (1 << CP0St_PX)) ||
+ (env->CP0_Status & (1 << CP0St_UX))) {
+ env->hflags |= MIPS_HFLAG_64;
+ }
+ if (env->CP0_Status & (1 << CP0St_UX)) {
+ env->hflags |= MIPS_HFLAG_UX;
+ }
+#endif
+ if ((env->CP0_Status & (1 << CP0St_CU0)) ||
+ !(env->hflags & MIPS_HFLAG_KSU)) {
+ env->hflags |= MIPS_HFLAG_CP0;
+ }
+ if (env->CP0_Status & (1 << CP0St_CU1)) {
+ env->hflags |= MIPS_HFLAG_FPU;
+ }
+ if (env->CP0_Status & (1 << CP0St_FR)) {
+ env->hflags |= MIPS_HFLAG_F64;
+ }
+ if (env->insn_flags & ISA_MIPS32R2) {
+ if (env->active_fpu.fcr0 & (1 << FCR0_F64)) {
+ env->hflags |= MIPS_HFLAG_COP1X;
+ }
+ } else if (env->insn_flags & ISA_MIPS32) {
+ if (env->hflags & MIPS_HFLAG_64) {
+ env->hflags |= MIPS_HFLAG_COP1X;
+ }
+ } else if (env->insn_flags & ISA_MIPS4) {
+ /* All supported MIPS IV CPUs use the XX (CU3) to enable
+ and disable the MIPS IV extensions to the MIPS III ISA.
+ Some other MIPS IV CPUs ignore the bit, so the check here
+ would be too restrictive for them. */
+ if (env->CP0_Status & (1 << CP0St_CU3)) {
+ env->hflags |= MIPS_HFLAG_COP1X;
+ }
+ }
+}
+
/*****************************************************************************/
/* Exceptions processing helpers */
@@ -1980,9 +2034,11 @@ void tlb_fill (target_ulong addr, int is_write, int mmu_idx, void *retaddr)
env = saved_env;
}
-void do_unassigned_access(target_phys_addr_t addr, int is_write, int is_exec,
- int unused, int size)
+void cpu_unassigned_access(CPUState *env1, target_phys_addr_t addr,
+ int is_write, int is_exec, int unused, int size)
{
+ env = env1;
+
if (is_exec)
helper_raise_exception(EXCP_IBE);
else
diff --git a/target-ppc/exec.h b/target-ppc/exec.h
deleted file mode 100644
index f4453e4..0000000
--- a/target-ppc/exec.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * PowerPC emulation definitions for qemu.
- *
- * Copyright (c) 2003-2007 Jocelyn Mayer
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
- */
-#if !defined (__PPC_H__)
-#define __PPC_H__
-
-#include "config.h"
-
-#include "dyngen-exec.h"
-
-#include "cpu.h"
-
-register struct CPUPPCState *env asm(AREG0);
-
-#if !defined(CONFIG_USER_ONLY)
-#include "softmmu_exec.h"
-#endif /* !defined(CONFIG_USER_ONLY) */
-
-#endif /* !defined (__PPC_H__) */
diff --git a/target-ppc/op_helper.c b/target-ppc/op_helper.c
index dde7595..6e100d9 100644
--- a/target-ppc/op_helper.c
+++ b/target-ppc/op_helper.c
@@ -17,12 +17,17 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
-#include "exec.h"
+#include "cpu.h"
+#include "dyngen-exec.h"
#include "host-utils.h"
#include "helper.h"
#include "helper_regs.h"
+#if !defined(CONFIG_USER_ONLY)
+#include "softmmu_exec.h"
+#endif /* !defined(CONFIG_USER_ONLY) */
+
//#define DEBUG_OP
//#define DEBUG_EXCEPTIONS
//#define DEBUG_SOFTWARE_TLB
diff --git a/target-s390x/exec.h b/target-s390x/exec.h
deleted file mode 100644
index fb73f31..0000000
--- a/target-s390x/exec.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * S/390 execution defines
- *
- * Copyright (c) 2009 Ulrich Hecht
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "dyngen-exec.h"
-
-register struct CPUS390XState *env asm(AREG0);
-
-#include "config.h"
-#include "cpu.h"
-
-#if !defined(CONFIG_USER_ONLY)
-#include "softmmu_exec.h"
-#endif /* !defined(CONFIG_USER_ONLY) */
-
-static inline void regs_to_env(void)
-{
-}
-
-static inline void env_to_regs(void)
-{
-}
diff --git a/target-s390x/op_helper.c b/target-s390x/op_helper.c
index cd33f99..25a1e81 100644
--- a/target-s390x/op_helper.c
+++ b/target-s390x/op_helper.c
@@ -18,7 +18,8 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
-#include "exec.h"
+#include "cpu.h"
+#include "dyngen-exec.h"
#include "host-utils.h"
#include "helpers.h"
#include <string.h>
@@ -31,6 +32,7 @@
/*****************************************************************************/
/* Softmmu support */
#if !defined (CONFIG_USER_ONLY)
+#include "softmmu_exec.h"
#define MMUSUFFIX _mmu
diff --git a/target-sh4/exec.h b/target-sh4/exec.h
deleted file mode 100644
index 4a6ae58..0000000
--- a/target-sh4/exec.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * SH4 emulation
- *
- * Copyright (c) 2005 Samuel Tardieu
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
- */
-#ifndef _EXEC_SH4_H
-#define _EXEC_SH4_H
-
-#include "config.h"
-#include "dyngen-exec.h"
-
-register struct CPUSH4State *env asm(AREG0);
-
-#include "cpu.h"
-
-#ifndef CONFIG_USER_ONLY
-#include "softmmu_exec.h"
-#endif
-
-#endif /* _EXEC_SH4_H */
diff --git a/target-sh4/op_helper.c b/target-sh4/op_helper.c
index a932225..568bf0d 100644
--- a/target-sh4/op_helper.c
+++ b/target-sh4/op_helper.c
@@ -18,7 +18,8 @@
*/
#include <assert.h>
#include <stdlib.h>
-#include "exec.h"
+#include "cpu.h"
+#include "dyngen-exec.h"
#include "helper.h"
static void cpu_restore_state_from_retaddr(void *retaddr)
@@ -38,6 +39,7 @@ static void cpu_restore_state_from_retaddr(void *retaddr)
}
#ifndef CONFIG_USER_ONLY
+#include "softmmu_exec.h"
#define MMUSUFFIX _mmu
diff --git a/target-sparc/cpu.h b/target-sparc/cpu.h
index 22ee274..a51863c 100644
--- a/target-sparc/cpu.h
+++ b/target-sparc/cpu.h
@@ -290,18 +290,51 @@ enum {
#endif
#define TTE_VALID_BIT (1ULL << 63)
+#define TTE_NFO_BIT (1ULL << 60)
#define TTE_USED_BIT (1ULL << 41)
#define TTE_LOCKED_BIT (1ULL << 6)
+#define TTE_SIDEEFFECT_BIT (1ULL << 3)
+#define TTE_PRIV_BIT (1ULL << 2)
+#define TTE_W_OK_BIT (1ULL << 1)
#define TTE_GLOBAL_BIT (1ULL << 0)
#define TTE_IS_VALID(tte) ((tte) & TTE_VALID_BIT)
+#define TTE_IS_NFO(tte) ((tte) & TTE_NFO_BIT)
#define TTE_IS_USED(tte) ((tte) & TTE_USED_BIT)
#define TTE_IS_LOCKED(tte) ((tte) & TTE_LOCKED_BIT)
+#define TTE_IS_SIDEEFFECT(tte) ((tte) & TTE_SIDEEFFECT_BIT)
+#define TTE_IS_PRIV(tte) ((tte) & TTE_PRIV_BIT)
+#define TTE_IS_W_OK(tte) ((tte) & TTE_W_OK_BIT)
#define TTE_IS_GLOBAL(tte) ((tte) & TTE_GLOBAL_BIT)
#define TTE_SET_USED(tte) ((tte) |= TTE_USED_BIT)
#define TTE_SET_UNUSED(tte) ((tte) &= ~TTE_USED_BIT)
+#define TTE_PGSIZE(tte) (((tte) >> 61) & 3ULL)
+#define TTE_PA(tte) ((tte) & 0x1ffffffe000ULL)
+
+#define SFSR_NF_BIT (1ULL << 24) /* JPS1 NoFault */
+#define SFSR_TM_BIT (1ULL << 15) /* JPS1 TLB Miss */
+#define SFSR_FT_VA_IMMU_BIT (1ULL << 13) /* USIIi VA out of range (IMMU) */
+#define SFSR_FT_VA_DMMU_BIT (1ULL << 12) /* USIIi VA out of range (DMMU) */
+#define SFSR_FT_NFO_BIT (1ULL << 11) /* NFO page access */
+#define SFSR_FT_ILL_BIT (1ULL << 10) /* illegal LDA/STA ASI */
+#define SFSR_FT_ATOMIC_BIT (1ULL << 9) /* atomic op on noncacheable area */
+#define SFSR_FT_NF_E_BIT (1ULL << 8) /* NF access on side effect area */
+#define SFSR_FT_PRIV_BIT (1ULL << 7) /* privilege violation */
+#define SFSR_PR_BIT (1ULL << 3) /* privilege mode */
+#define SFSR_WRITE_BIT (1ULL << 2) /* write access mode */
+#define SFSR_OW_BIT (1ULL << 1) /* status overwritten */
+#define SFSR_VALID_BIT (1ULL << 0) /* status valid */
+
+#define SFSR_ASI_SHIFT 16 /* 23:16 ASI value */
+#define SFSR_ASI_MASK (0xffULL << SFSR_ASI_SHIFT)
+#define SFSR_CT_PRIMARY (0ULL << 4) /* 5:4 context type */
+#define SFSR_CT_SECONDARY (1ULL << 4)
+#define SFSR_CT_NUCLEUS (2ULL << 4)
+#define SFSR_CT_NOTRANS (3ULL << 4)
+#define SFSR_CT_MASK (3ULL << 4)
+
typedef struct SparcTLBEntry {
uint64_t tag;
uint64_t tte;
@@ -510,12 +543,14 @@ static inline int tlb_compare_context(const SparcTLBEntry *tlb,
/* cpu-exec.c */
#if !defined(CONFIG_USER_ONLY)
-void do_unassigned_access(target_phys_addr_t addr, int is_write, int is_exec,
- int is_asi, int size);
+void cpu_unassigned_access(CPUState *env1, target_phys_addr_t addr,
+ int is_write, int is_exec, int is_asi, int size);
+#if defined(TARGET_SPARC64)
target_phys_addr_t cpu_get_phys_page_nofault(CPUState *env, target_ulong addr,
int mmu_idx);
#endif
+#endif
int cpu_sparc_signal_handler(int host_signum, void *pinfo, void *puc);
#define cpu_init cpu_sparc_init
diff --git a/target-sparc/exec.h b/target-sparc/exec.h
deleted file mode 100644
index 2395b00..0000000
--- a/target-sparc/exec.h
+++ /dev/null
@@ -1,15 +0,0 @@
-#ifndef EXEC_SPARC_H
-#define EXEC_SPARC_H 1
-#include "config.h"
-#include "dyngen-exec.h"
-
-register struct CPUSPARCState *env asm(AREG0);
-
-#include "cpu.h"
-#include "exec-all.h"
-
-#if !defined(CONFIG_USER_ONLY)
-#include "softmmu_exec.h"
-#endif /* !defined(CONFIG_USER_ONLY) */
-
-#endif
diff --git a/target-sparc/helper.c b/target-sparc/helper.c
index 7eea1ac..efab885 100644
--- a/target-sparc/helper.c
+++ b/target-sparc/helper.c
@@ -378,7 +378,7 @@ static inline int ultrasparc_tag_match(SparcTLBEntry *tlb,
{
uint64_t mask;
- switch ((tlb->tte >> 61) & 3) {
+ switch (TTE_PGSIZE(tlb->tte)) {
default:
case 0x0: // 8k
mask = 0xffffffffffffe000ULL;
@@ -413,6 +413,7 @@ static int get_physical_address_data(CPUState *env,
{
unsigned int i;
uint64_t context;
+ uint64_t sfsr = 0;
int is_user = (mmu_idx == MMU_USER_IDX ||
mmu_idx == MMU_USER_SECONDARY_IDX);
@@ -427,54 +428,88 @@ static int get_physical_address_data(CPUState *env,
case MMU_USER_IDX:
case MMU_KERNEL_IDX:
context = env->dmmu.mmu_primary_context & 0x1fff;
+ sfsr |= SFSR_CT_PRIMARY;
break;
case MMU_USER_SECONDARY_IDX:
case MMU_KERNEL_SECONDARY_IDX:
context = env->dmmu.mmu_secondary_context & 0x1fff;
+ sfsr |= SFSR_CT_SECONDARY;
break;
case MMU_NUCLEUS_IDX:
+ sfsr |= SFSR_CT_NUCLEUS;
+ /* FALLTHRU */
default:
context = 0;
break;
}
+ if (rw == 1) {
+ sfsr |= SFSR_WRITE_BIT;
+ } else if (rw == 4) {
+ sfsr |= SFSR_NF_BIT;
+ }
+
for (i = 0; i < 64; i++) {
// ctx match, vaddr match, valid?
if (ultrasparc_tag_match(&env->dtlb[i], address, context, physical)) {
-
- uint8_t fault_type = 0;
+ int do_fault = 0;
// access ok?
- if ((env->dtlb[i].tte & 0x4) && is_user) {
- fault_type |= 1; /* privilege violation */
- env->exception_index = TT_DFAULT;
+ /* multiple bits in SFSR.FT may be set on TT_DFAULT */
+ if (TTE_IS_PRIV(env->dtlb[i].tte) && is_user) {
+ do_fault = 1;
+ sfsr |= SFSR_FT_PRIV_BIT; /* privilege violation */
DPRINTF_MMU("DFAULT at %" PRIx64 " context %" PRIx64
" mmu_idx=%d tl=%d\n",
address, context, mmu_idx, env->tl);
- } else if (!(env->dtlb[i].tte & 0x2) && (rw == 1)) {
+ }
+ if (rw == 4) {
+ if (TTE_IS_SIDEEFFECT(env->dtlb[i].tte)) {
+ do_fault = 1;
+ sfsr |= SFSR_FT_NF_E_BIT;
+ }
+ } else {
+ if (TTE_IS_NFO(env->dtlb[i].tte)) {
+ do_fault = 1;
+ sfsr |= SFSR_FT_NFO_BIT;
+ }
+ }
+
+ if (do_fault) {
+ /* faults above are reported with TT_DFAULT. */
+ env->exception_index = TT_DFAULT;
+ } else if (!TTE_IS_W_OK(env->dtlb[i].tte) && (rw == 1)) {
+ do_fault = 1;
env->exception_index = TT_DPROT;
DPRINTF_MMU("DPROT at %" PRIx64 " context %" PRIx64
" mmu_idx=%d tl=%d\n",
address, context, mmu_idx, env->tl);
- } else {
+ }
+
+ if (!do_fault) {
*prot = PAGE_READ;
- if (env->dtlb[i].tte & 0x2)
+ if (TTE_IS_W_OK(env->dtlb[i].tte)) {
*prot |= PAGE_WRITE;
+ }
TTE_SET_USED(env->dtlb[i].tte);
return 0;
}
- if (env->dmmu.sfsr & 1) /* Fault status register */
- env->dmmu.sfsr = 2; /* overflow (not read before
- another fault) */
+ if (env->dmmu.sfsr & SFSR_VALID_BIT) { /* Fault status register */
+ sfsr |= SFSR_OW_BIT; /* overflow (not read before
+ another fault) */
+ }
- env->dmmu.sfsr |= (is_user << 3) | ((rw == 1) << 2) | 1;
+ if (env->pstate & PS_PRIV) {
+ sfsr |= SFSR_PR_BIT;
+ }
- env->dmmu.sfsr |= (fault_type << 7);
+ /* FIXME: ASI field in SFSR must be set */
+ env->dmmu.sfsr = sfsr | SFSR_VALID_BIT;
env->dmmu.sfar = address; /* Fault address register */
@@ -487,6 +522,11 @@ static int get_physical_address_data(CPUState *env,
DPRINTF_MMU("DMISS at %" PRIx64 " context %" PRIx64 "\n",
address, context);
+ /*
+ * On MMU misses:
+ * - UltraSPARC IIi: SFSR and SFAR unmodified
+ * - JPS1: SFAR updated and some fields of SFSR updated
+ */
env->dmmu.tag_access = (address & ~0x1fffULL) | context;
env->exception_index = TT_DMISS;
return 1;
@@ -522,11 +562,23 @@ static int get_physical_address_code(CPUState *env,
if (ultrasparc_tag_match(&env->itlb[i],
address, context, physical)) {
// access ok?
- if ((env->itlb[i].tte & 0x4) && is_user) {
- if (env->immu.sfsr) /* Fault status register */
- env->immu.sfsr = 2; /* overflow (not read before
- another fault) */
- env->immu.sfsr |= (is_user << 3) | 1;
+ if (TTE_IS_PRIV(env->itlb[i].tte) && is_user) {
+ /* Fault status register */
+ if (env->immu.sfsr & SFSR_VALID_BIT) {
+ env->immu.sfsr = SFSR_OW_BIT; /* overflow (not read before
+ another fault) */
+ } else {
+ env->immu.sfsr = 0;
+ }
+ if (env->pstate & PS_PRIV) {
+ env->immu.sfsr |= SFSR_PR_BIT;
+ }
+ if (env->tl > 0) {
+ env->immu.sfsr |= SFSR_CT_NUCLEUS;
+ }
+
+ /* FIXME: ASI field in SFSR must be set */
+ env->immu.sfsr |= SFSR_FT_PRIV_BIT | SFSR_VALID_BIT;
env->exception_index = TT_TFAULT;
env->immu.tag_access = (address & ~0x1fffULL) | context;
@@ -632,7 +684,7 @@ void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUState *env)
} else {
(*cpu_fprintf)(f, "DMMU dump\n");
for (i = 0; i < 64; i++) {
- switch ((env->dtlb[i].tte >> 61) & 3) {
+ switch (TTE_PGSIZE(env->dtlb[i].tte)) {
default:
case 0x0:
mask = " 8k";
@@ -647,16 +699,17 @@ void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUState *env)
mask = " 4M";
break;
}
- if ((env->dtlb[i].tte & 0x8000000000000000ULL) != 0) {
- (*cpu_fprintf)(f, "[%02u] VA: %" PRIx64 ", PA: %" PRIx64
+ if (TTE_IS_VALID(env->dtlb[i].tte)) {
+ (*cpu_fprintf)(f, "[%02u] VA: %" PRIx64 ", PA: %llx"
", %s, %s, %s, %s, ctx %" PRId64 " %s\n",
i,
env->dtlb[i].tag & (uint64_t)~0x1fffULL,
- env->dtlb[i].tte & (uint64_t)0x1ffffffe000ULL,
+ TTE_PA(env->dtlb[i].tte),
mask,
- env->dtlb[i].tte & 0x4? "priv": "user",
- env->dtlb[i].tte & 0x2? "RW": "RO",
- env->dtlb[i].tte & 0x40? "locked": "unlocked",
+ TTE_IS_PRIV(env->dtlb[i].tte) ? "priv" : "user",
+ TTE_IS_W_OK(env->dtlb[i].tte) ? "RW" : "RO",
+ TTE_IS_LOCKED(env->dtlb[i].tte) ?
+ "locked" : "unlocked",
env->dtlb[i].tag & (uint64_t)0x1fffULL,
TTE_IS_GLOBAL(env->dtlb[i].tte)?
"global" : "local");
@@ -668,7 +721,7 @@ void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUState *env)
} else {
(*cpu_fprintf)(f, "IMMU dump\n");
for (i = 0; i < 64; i++) {
- switch ((env->itlb[i].tte >> 61) & 3) {
+ switch (TTE_PGSIZE(env->itlb[i].tte)) {
default:
case 0x0:
mask = " 8k";
@@ -683,15 +736,16 @@ void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUState *env)
mask = " 4M";
break;
}
- if ((env->itlb[i].tte & 0x8000000000000000ULL) != 0) {
- (*cpu_fprintf)(f, "[%02u] VA: %" PRIx64 ", PA: %" PRIx64
+ if (TTE_IS_VALID(env->itlb[i].tte)) {
+ (*cpu_fprintf)(f, "[%02u] VA: %" PRIx64 ", PA: %llx"
", %s, %s, %s, ctx %" PRId64 " %s\n",
i,
env->itlb[i].tag & (uint64_t)~0x1fffULL,
- env->itlb[i].tte & (uint64_t)0x1ffffffe000ULL,
+ TTE_PA(env->itlb[i].tte),
mask,
- env->itlb[i].tte & 0x4? "priv": "user",
- env->itlb[i].tte & 0x40? "locked": "unlocked",
+ TTE_IS_PRIV(env->itlb[i].tte) ? "priv" : "user",
+ TTE_IS_LOCKED(env->itlb[i].tte) ?
+ "locked" : "unlocked",
env->itlb[i].tag & (uint64_t)0x1fffULL,
TTE_IS_GLOBAL(env->itlb[i].tte)?
"global" : "local");
@@ -705,26 +759,43 @@ void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUState *env)
#if !defined(CONFIG_USER_ONLY)
+static int cpu_sparc_get_phys_page(CPUState *env, target_phys_addr_t *phys,
+ target_ulong addr, int rw, int mmu_idx)
+{
+ target_ulong page_size;
+ int prot, access_index;
+
+ return get_physical_address(env, phys, &prot, &access_index, addr, rw,
+ mmu_idx, &page_size);
+}
+
+#if defined(TARGET_SPARC64)
target_phys_addr_t cpu_get_phys_page_nofault(CPUState *env, target_ulong addr,
int mmu_idx)
{
target_phys_addr_t phys_addr;
- target_ulong page_size;
- int prot, access_index;
- if (get_physical_address(env, &phys_addr, &prot, &access_index, addr, 2,
- mmu_idx, &page_size) != 0)
- if (get_physical_address(env, &phys_addr, &prot, &access_index, addr,
- 0, mmu_idx, &page_size) != 0)
- return -1;
- if (cpu_get_physical_page_desc(phys_addr) == IO_MEM_UNASSIGNED)
+ if (cpu_sparc_get_phys_page(env, &phys_addr, addr, 4, mmu_idx) != 0) {
return -1;
+ }
return phys_addr;
}
+#endif
target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr)
{
- return cpu_get_phys_page_nofault(env, addr, cpu_mmu_index(env));
+ target_phys_addr_t phys_addr;
+ int mmu_idx = cpu_mmu_index(env);
+
+ if (cpu_sparc_get_phys_page(env, &phys_addr, addr, 2, mmu_idx) != 0) {
+ if (cpu_sparc_get_phys_page(env, &phys_addr, addr, 0, mmu_idx) != 0) {
+ return -1;
+ }
+ }
+ if (cpu_get_physical_page_desc(phys_addr) == IO_MEM_UNASSIGNED) {
+ return -1;
+ }
+ return phys_addr;
}
#endif
diff --git a/target-sparc/helper.h b/target-sparc/helper.h
index 023f4d6..2d36af3 100644
--- a/target-sparc/helper.h
+++ b/target-sparc/helper.h
@@ -148,8 +148,8 @@ F_HELPER_0_0(expand);
VIS_HELPER(padd);
VIS_HELPER(psub);
#define VIS_CMPHELPER(name) \
- F_HELPER_0_0(name##16); \
- F_HELPER_0_0(name##32)
+ DEF_HELPER_0(f##name##16, i64); \
+ DEF_HELPER_0(f##name##32, i64)
VIS_CMPHELPER(cmpgt);
VIS_CMPHELPER(cmpeq);
VIS_CMPHELPER(cmple);
diff --git a/target-sparc/op_helper.c b/target-sparc/op_helper.c
index 15af27b..5aeca2b 100644
--- a/target-sparc/op_helper.c
+++ b/target-sparc/op_helper.c
@@ -1,8 +1,13 @@
-#include "exec.h"
+#include "cpu.h"
+#include "dyngen-exec.h"
#include "host-utils.h"
#include "helper.h"
#include "sysemu.h"
+#if !defined(CONFIG_USER_ONLY)
+#include "softmmu_exec.h"
+#endif
+
//#define DEBUG_MMU
//#define DEBUG_MXCC
//#define DEBUG_UNALIGNED
@@ -79,9 +84,14 @@
#define CACHE_CTRL_FD (1 << 22) /* Flush Data cache (Write only) */
#define CACHE_CTRL_DS (1 << 23) /* Data cache snoop enable */
-#if defined(CONFIG_USER_ONLY) && defined(TARGET_SPARC64)
+#if !defined(CONFIG_USER_ONLY)
+static void do_unassigned_access(target_phys_addr_t addr, int is_write,
+ int is_exec, int is_asi, int size);
+#else
+#ifdef TARGET_SPARC64
static void do_unassigned_access(target_ulong addr, int is_write, int is_exec,
- int is_asi, int size);
+ int is_asi, int size);
+#endif
#endif
#if defined(TARGET_SPARC64) && !defined(CONFIG_USER_ONLY)
@@ -287,7 +297,8 @@ static inline int is_translating_asi(int asi)
*/
switch (asi) {
case 0x04 ... 0x11:
- case 0x18 ... 0x19:
+ case 0x16 ... 0x19:
+ case 0x1E ... 0x1F:
case 0x24 ... 0x2C:
case 0x70 ... 0x73:
case 0x78 ... 0x79:
@@ -525,6 +536,7 @@ typedef union {
uint16_t w[4];
int16_t sw[4];
uint32_t l[2];
+ uint64_t ll;
float64 d;
} vis64;
@@ -789,32 +801,34 @@ VIS_HELPER(helper_fpadd, FADD)
VIS_HELPER(helper_fpsub, FSUB)
#define VIS_CMPHELPER(name, F) \
- void name##16(void) \
+ uint64_t name##16(void) \
{ \
vis64 s, d; \
\
s.d = DT0; \
d.d = DT1; \
\
- d.VIS_W64(0) = F(d.VIS_W64(0), s.VIS_W64(0))? 1: 0; \
- d.VIS_W64(0) |= F(d.VIS_W64(1), s.VIS_W64(1))? 2: 0; \
- d.VIS_W64(0) |= F(d.VIS_W64(2), s.VIS_W64(2))? 4: 0; \
- d.VIS_W64(0) |= F(d.VIS_W64(3), s.VIS_W64(3))? 8: 0; \
+ d.VIS_W64(0) = F(s.VIS_W64(0), d.VIS_W64(0)) ? 1 : 0; \
+ d.VIS_W64(0) |= F(s.VIS_W64(1), d.VIS_W64(1)) ? 2 : 0; \
+ d.VIS_W64(0) |= F(s.VIS_W64(2), d.VIS_W64(2)) ? 4 : 0; \
+ d.VIS_W64(0) |= F(s.VIS_W64(3), d.VIS_W64(3)) ? 8 : 0; \
+ d.VIS_W64(1) = d.VIS_W64(2) = d.VIS_W64(3) = 0; \
\
- DT0 = d.d; \
+ return d.ll; \
} \
\
- void name##32(void) \
+ uint64_t name##32(void) \
{ \
vis64 s, d; \
\
s.d = DT0; \
d.d = DT1; \
\
- d.VIS_L64(0) = F(d.VIS_L64(0), s.VIS_L64(0))? 1: 0; \
- d.VIS_L64(0) |= F(d.VIS_L64(1), s.VIS_L64(1))? 2: 0; \
+ d.VIS_L64(0) = F(s.VIS_L64(0), d.VIS_L64(0)) ? 1 : 0; \
+ d.VIS_L64(0) |= F(s.VIS_L64(1), d.VIS_L64(1)) ? 2 : 0; \
+ d.VIS_L64(1) = 0; \
\
- DT0 = d.d; \
+ return d.ll; \
}
#define FCMPGT(a, b) ((a) > (b))
@@ -2558,24 +2572,30 @@ uint64_t helper_ld_asi(target_ulong addr, int asi, int size, int sign)
helper_check_align(addr, size - 1);
addr = asi_address_mask(env, asi, addr);
- switch (asi) {
- case 0x82: // Primary no-fault
- case 0x8a: // Primary no-fault LE
- case 0x83: // Secondary no-fault
- case 0x8b: // Secondary no-fault LE
- {
- /* secondary space access has lowest asi bit equal to 1 */
- int access_mmu_idx = ( asi & 1 ) ? MMU_KERNEL_IDX
- : MMU_KERNEL_SECONDARY_IDX;
+ /* process nonfaulting loads first */
+ if ((asi & 0xf6) == 0x82) {
+ int mmu_idx;
- if (cpu_get_phys_page_nofault(env, addr, access_mmu_idx) == -1ULL) {
+ /* secondary space access has lowest asi bit equal to 1 */
+ if (env->pstate & PS_PRIV) {
+ mmu_idx = (asi & 1) ? MMU_KERNEL_SECONDARY_IDX : MMU_KERNEL_IDX;
+ } else {
+ mmu_idx = (asi & 1) ? MMU_USER_SECONDARY_IDX : MMU_USER_IDX;
+ }
+
+ if (cpu_get_phys_page_nofault(env, addr, mmu_idx) == -1ULL) {
#ifdef DEBUG_ASI
- dump_asi("read ", last_addr, asi, size, ret);
+ dump_asi("read ", last_addr, asi, size, ret);
#endif
- return 0;
- }
+ /* env->exception_index is set in get_physical_address_data(). */
+ raise_exception(env->exception_index);
}
- // Fall through
+
+ /* convert nonfaulting load ASIs to normal load ASIs */
+ asi &= ~0x02;
+ }
+
+ switch (asi) {
case 0x10: // As if user primary
case 0x11: // As if user secondary
case 0x18: // As if user primary LE
@@ -2853,8 +2873,6 @@ uint64_t helper_ld_asi(target_ulong addr, int asi, int size, int sign)
case 0x1d: // Bypass, non-cacheable LE
case 0x88: // Primary LE
case 0x89: // Secondary LE
- case 0x8a: // Primary no-fault LE
- case 0x8b: // Secondary no-fault LE
switch(size) {
case 2:
ret = bswap16(ret);
@@ -4231,16 +4249,11 @@ void tlb_fill(target_ulong addr, int is_write, int mmu_idx, void *retaddr)
#ifndef TARGET_SPARC64
#if !defined(CONFIG_USER_ONLY)
-void do_unassigned_access(target_phys_addr_t addr, int is_write, int is_exec,
- int is_asi, int size)
+static void do_unassigned_access(target_phys_addr_t addr, int is_write,
+ int is_exec, int is_asi, int size)
{
- CPUState *saved_env;
int fault_type;
- /* XXX: hack to restore env in all cases, even if not called from
- generated code */
- saved_env = env;
- env = cpu_single_env;
#ifdef DEBUG_UNASSIGNED
if (is_asi)
printf("Unassigned mem %s access of %d byte%s to " TARGET_FMT_plx
@@ -4288,8 +4301,6 @@ void do_unassigned_access(target_phys_addr_t addr, int is_write, int is_exec,
if (env->mmuregs[0] & MMU_NF) {
tlb_flush(env, 1);
}
-
- env = saved_env;
}
#endif
#else
@@ -4297,17 +4308,10 @@ void do_unassigned_access(target_phys_addr_t addr, int is_write, int is_exec,
static void do_unassigned_access(target_ulong addr, int is_write, int is_exec,
int is_asi, int size)
#else
-void do_unassigned_access(target_phys_addr_t addr, int is_write, int is_exec,
- int is_asi, int size)
+static void do_unassigned_access(target_phys_addr_t addr, int is_write,
+ int is_exec, int is_asi, int size)
#endif
{
- CPUState *saved_env;
-
- /* XXX: hack to restore env in all cases, even if not called from
- generated code */
- saved_env = env;
- env = cpu_single_env;
-
#ifdef DEBUG_UNASSIGNED
printf("Unassigned mem access to " TARGET_FMT_plx " from " TARGET_FMT_lx
"\n", addr, env->pc);
@@ -4317,8 +4321,6 @@ void do_unassigned_access(target_phys_addr_t addr, int is_write, int is_exec,
raise_exception(TT_CODE_ACCESS);
else
raise_exception(TT_DATA_ACCESS);
-
- env = saved_env;
}
#endif
@@ -4347,3 +4349,19 @@ void helper_tick_set_limit(void *opaque, uint64_t limit)
#endif
}
#endif
+
+#if !defined(CONFIG_USER_ONLY)
+void cpu_unassigned_access(CPUState *env1, target_phys_addr_t addr,
+ int is_write, int is_exec, int is_asi, int size)
+{
+ CPUState *saved_env;
+
+ saved_env = env;
+ env = env1;
+ /* Ignore unassigned accesses outside of CPU context */
+ if (env1) {
+ do_unassigned_access(addr, is_write, is_exec, is_asi, size);
+ }
+ env = saved_env;
+}
+#endif
diff --git a/target-sparc/translate.c b/target-sparc/translate.c
index 27c2cf9..958fbc5 100644
--- a/target-sparc/translate.c
+++ b/target-sparc/translate.c
@@ -1558,6 +1558,13 @@ static int gen_trap_ifnofpu(DisasContext *dc, TCGv r_cond)
return 0;
}
+static inline void gen_update_fprs_dirty(int rd)
+{
+#if defined(TARGET_SPARC64)
+ tcg_gen_ori_i32(cpu_fprs, cpu_fprs, (rd < 32) ? 1 : 2);
+#endif
+}
+
static inline void gen_op_clear_ieee_excp_and_FTT(void)
{
tcg_gen_andi_tl(cpu_fsr, cpu_fsr, FSR_FTT_CEXC_NMASK);
@@ -2351,12 +2358,15 @@ static void disas_sparc_insn(DisasContext * dc)
switch (xop) {
case 0x1: /* fmovs */
tcg_gen_mov_i32(cpu_fpr[rd], cpu_fpr[rs2]);
+ gen_update_fprs_dirty(rd);
break;
case 0x5: /* fnegs */
gen_helper_fnegs(cpu_fpr[rd], cpu_fpr[rs2]);
+ gen_update_fprs_dirty(rd);
break;
case 0x9: /* fabss */
gen_helper_fabss(cpu_fpr[rd], cpu_fpr[rs2]);
+ gen_update_fprs_dirty(rd);
break;
case 0x29: /* fsqrts */
CHECK_FPU_FEATURE(dc, FSQRT);
@@ -2364,6 +2374,7 @@ static void disas_sparc_insn(DisasContext * dc)
gen_helper_fsqrts(cpu_tmp32, cpu_fpr[rs2]);
gen_helper_check_ieee_exceptions();
tcg_gen_mov_i32(cpu_fpr[rd], cpu_tmp32);
+ gen_update_fprs_dirty(rd);
break;
case 0x2a: /* fsqrtd */
CHECK_FPU_FEATURE(dc, FSQRT);
@@ -2372,6 +2383,7 @@ static void disas_sparc_insn(DisasContext * dc)
gen_helper_fsqrtd();
gen_helper_check_ieee_exceptions();
gen_op_store_DT0_fpr(DFPREG(rd));
+ gen_update_fprs_dirty(DFPREG(rd));
break;
case 0x2b: /* fsqrtq */
CHECK_FPU_FEATURE(dc, FLOAT128);
@@ -2380,12 +2392,14 @@ static void disas_sparc_insn(DisasContext * dc)
gen_helper_fsqrtq();
gen_helper_check_ieee_exceptions();
gen_op_store_QT0_fpr(QFPREG(rd));
+ gen_update_fprs_dirty(QFPREG(rd));
break;
case 0x41: /* fadds */
gen_clear_float_exceptions();
gen_helper_fadds(cpu_tmp32, cpu_fpr[rs1], cpu_fpr[rs2]);
gen_helper_check_ieee_exceptions();
tcg_gen_mov_i32(cpu_fpr[rd], cpu_tmp32);
+ gen_update_fprs_dirty(rd);
break;
case 0x42: /* faddd */
gen_op_load_fpr_DT0(DFPREG(rs1));
@@ -2394,6 +2408,7 @@ static void disas_sparc_insn(DisasContext * dc)
gen_helper_faddd();
gen_helper_check_ieee_exceptions();
gen_op_store_DT0_fpr(DFPREG(rd));
+ gen_update_fprs_dirty(DFPREG(rd));
break;
case 0x43: /* faddq */
CHECK_FPU_FEATURE(dc, FLOAT128);
@@ -2403,12 +2418,14 @@ static void disas_sparc_insn(DisasContext * dc)
gen_helper_faddq();
gen_helper_check_ieee_exceptions();
gen_op_store_QT0_fpr(QFPREG(rd));
+ gen_update_fprs_dirty(QFPREG(rd));
break;
case 0x45: /* fsubs */
gen_clear_float_exceptions();
gen_helper_fsubs(cpu_tmp32, cpu_fpr[rs1], cpu_fpr[rs2]);
gen_helper_check_ieee_exceptions();
tcg_gen_mov_i32(cpu_fpr[rd], cpu_tmp32);
+ gen_update_fprs_dirty(rd);
break;
case 0x46: /* fsubd */
gen_op_load_fpr_DT0(DFPREG(rs1));
@@ -2417,6 +2434,7 @@ static void disas_sparc_insn(DisasContext * dc)
gen_helper_fsubd();
gen_helper_check_ieee_exceptions();
gen_op_store_DT0_fpr(DFPREG(rd));
+ gen_update_fprs_dirty(DFPREG(rd));
break;
case 0x47: /* fsubq */
CHECK_FPU_FEATURE(dc, FLOAT128);
@@ -2426,6 +2444,7 @@ static void disas_sparc_insn(DisasContext * dc)
gen_helper_fsubq();
gen_helper_check_ieee_exceptions();
gen_op_store_QT0_fpr(QFPREG(rd));
+ gen_update_fprs_dirty(QFPREG(rd));
break;
case 0x49: /* fmuls */
CHECK_FPU_FEATURE(dc, FMUL);
@@ -2433,6 +2452,7 @@ static void disas_sparc_insn(DisasContext * dc)
gen_helper_fmuls(cpu_tmp32, cpu_fpr[rs1], cpu_fpr[rs2]);
gen_helper_check_ieee_exceptions();
tcg_gen_mov_i32(cpu_fpr[rd], cpu_tmp32);
+ gen_update_fprs_dirty(rd);
break;
case 0x4a: /* fmuld */
CHECK_FPU_FEATURE(dc, FMUL);
@@ -2442,6 +2462,7 @@ static void disas_sparc_insn(DisasContext * dc)
gen_helper_fmuld();
gen_helper_check_ieee_exceptions();
gen_op_store_DT0_fpr(DFPREG(rd));
+ gen_update_fprs_dirty(DFPREG(rd));
break;
case 0x4b: /* fmulq */
CHECK_FPU_FEATURE(dc, FLOAT128);
@@ -2452,12 +2473,14 @@ static void disas_sparc_insn(DisasContext * dc)
gen_helper_fmulq();
gen_helper_check_ieee_exceptions();
gen_op_store_QT0_fpr(QFPREG(rd));
+ gen_update_fprs_dirty(QFPREG(rd));
break;
case 0x4d: /* fdivs */
gen_clear_float_exceptions();
gen_helper_fdivs(cpu_tmp32, cpu_fpr[rs1], cpu_fpr[rs2]);
gen_helper_check_ieee_exceptions();
tcg_gen_mov_i32(cpu_fpr[rd], cpu_tmp32);
+ gen_update_fprs_dirty(rd);
break;
case 0x4e: /* fdivd */
gen_op_load_fpr_DT0(DFPREG(rs1));
@@ -2466,6 +2489,7 @@ static void disas_sparc_insn(DisasContext * dc)
gen_helper_fdivd();
gen_helper_check_ieee_exceptions();
gen_op_store_DT0_fpr(DFPREG(rd));
+ gen_update_fprs_dirty(DFPREG(rd));
break;
case 0x4f: /* fdivq */
CHECK_FPU_FEATURE(dc, FLOAT128);
@@ -2475,6 +2499,7 @@ static void disas_sparc_insn(DisasContext * dc)
gen_helper_fdivq();
gen_helper_check_ieee_exceptions();
gen_op_store_QT0_fpr(QFPREG(rd));
+ gen_update_fprs_dirty(QFPREG(rd));
break;
case 0x69: /* fsmuld */
CHECK_FPU_FEATURE(dc, FSMULD);
@@ -2482,6 +2507,7 @@ static void disas_sparc_insn(DisasContext * dc)
gen_helper_fsmuld(cpu_fpr[rs1], cpu_fpr[rs2]);
gen_helper_check_ieee_exceptions();
gen_op_store_DT0_fpr(DFPREG(rd));
+ gen_update_fprs_dirty(DFPREG(rd));
break;
case 0x6e: /* fdmulq */
CHECK_FPU_FEATURE(dc, FLOAT128);
@@ -2491,12 +2517,14 @@ static void disas_sparc_insn(DisasContext * dc)
gen_helper_fdmulq();
gen_helper_check_ieee_exceptions();
gen_op_store_QT0_fpr(QFPREG(rd));
+ gen_update_fprs_dirty(QFPREG(rd));
break;
case 0xc4: /* fitos */
gen_clear_float_exceptions();
gen_helper_fitos(cpu_tmp32, cpu_fpr[rs2]);
gen_helper_check_ieee_exceptions();
tcg_gen_mov_i32(cpu_fpr[rd], cpu_tmp32);
+ gen_update_fprs_dirty(rd);
break;
case 0xc6: /* fdtos */
gen_op_load_fpr_DT1(DFPREG(rs2));
@@ -2504,6 +2532,7 @@ static void disas_sparc_insn(DisasContext * dc)
gen_helper_fdtos(cpu_tmp32);
gen_helper_check_ieee_exceptions();
tcg_gen_mov_i32(cpu_fpr[rd], cpu_tmp32);
+ gen_update_fprs_dirty(rd);
break;
case 0xc7: /* fqtos */
CHECK_FPU_FEATURE(dc, FLOAT128);
@@ -2512,14 +2541,17 @@ static void disas_sparc_insn(DisasContext * dc)
gen_helper_fqtos(cpu_tmp32);
gen_helper_check_ieee_exceptions();
tcg_gen_mov_i32(cpu_fpr[rd], cpu_tmp32);
+ gen_update_fprs_dirty(rd);
break;
case 0xc8: /* fitod */
gen_helper_fitod(cpu_fpr[rs2]);
gen_op_store_DT0_fpr(DFPREG(rd));
+ gen_update_fprs_dirty(DFPREG(rd));
break;
case 0xc9: /* fstod */
gen_helper_fstod(cpu_fpr[rs2]);
gen_op_store_DT0_fpr(DFPREG(rd));
+ gen_update_fprs_dirty(DFPREG(rd));
break;
case 0xcb: /* fqtod */
CHECK_FPU_FEATURE(dc, FLOAT128);
@@ -2528,28 +2560,33 @@ static void disas_sparc_insn(DisasContext * dc)
gen_helper_fqtod();
gen_helper_check_ieee_exceptions();
gen_op_store_DT0_fpr(DFPREG(rd));
+ gen_update_fprs_dirty(DFPREG(rd));
break;
case 0xcc: /* fitoq */
CHECK_FPU_FEATURE(dc, FLOAT128);
gen_helper_fitoq(cpu_fpr[rs2]);
gen_op_store_QT0_fpr(QFPREG(rd));
+ gen_update_fprs_dirty(QFPREG(rd));
break;
case 0xcd: /* fstoq */
CHECK_FPU_FEATURE(dc, FLOAT128);
gen_helper_fstoq(cpu_fpr[rs2]);
gen_op_store_QT0_fpr(QFPREG(rd));
+ gen_update_fprs_dirty(QFPREG(rd));
break;
case 0xce: /* fdtoq */
CHECK_FPU_FEATURE(dc, FLOAT128);
gen_op_load_fpr_DT1(DFPREG(rs2));
gen_helper_fdtoq();
gen_op_store_QT0_fpr(QFPREG(rd));
+ gen_update_fprs_dirty(QFPREG(rd));
break;
case 0xd1: /* fstoi */
gen_clear_float_exceptions();
gen_helper_fstoi(cpu_tmp32, cpu_fpr[rs2]);
gen_helper_check_ieee_exceptions();
tcg_gen_mov_i32(cpu_fpr[rd], cpu_tmp32);
+ gen_update_fprs_dirty(rd);
break;
case 0xd2: /* fdtoi */
gen_op_load_fpr_DT1(DFPREG(rs2));
@@ -2557,6 +2594,7 @@ static void disas_sparc_insn(DisasContext * dc)
gen_helper_fdtoi(cpu_tmp32);
gen_helper_check_ieee_exceptions();
tcg_gen_mov_i32(cpu_fpr[rd], cpu_tmp32);
+ gen_update_fprs_dirty(rd);
break;
case 0xd3: /* fqtoi */
CHECK_FPU_FEATURE(dc, FLOAT128);
@@ -2565,12 +2603,14 @@ static void disas_sparc_insn(DisasContext * dc)
gen_helper_fqtoi(cpu_tmp32);
gen_helper_check_ieee_exceptions();
tcg_gen_mov_i32(cpu_fpr[rd], cpu_tmp32);
+ gen_update_fprs_dirty(rd);
break;
#ifdef TARGET_SPARC64
case 0x2: /* V9 fmovd */
tcg_gen_mov_i32(cpu_fpr[DFPREG(rd)], cpu_fpr[DFPREG(rs2)]);
tcg_gen_mov_i32(cpu_fpr[DFPREG(rd) + 1],
cpu_fpr[DFPREG(rs2) + 1]);
+ gen_update_fprs_dirty(DFPREG(rd));
break;
case 0x3: /* V9 fmovq */
CHECK_FPU_FEATURE(dc, FLOAT128);
@@ -2581,34 +2621,40 @@ static void disas_sparc_insn(DisasContext * dc)
cpu_fpr[QFPREG(rs2) + 2]);
tcg_gen_mov_i32(cpu_fpr[QFPREG(rd) + 3],
cpu_fpr[QFPREG(rs2) + 3]);
+ gen_update_fprs_dirty(QFPREG(rd));
break;
case 0x6: /* V9 fnegd */
gen_op_load_fpr_DT1(DFPREG(rs2));
gen_helper_fnegd();
gen_op_store_DT0_fpr(DFPREG(rd));
+ gen_update_fprs_dirty(DFPREG(rd));
break;
case 0x7: /* V9 fnegq */
CHECK_FPU_FEATURE(dc, FLOAT128);
gen_op_load_fpr_QT1(QFPREG(rs2));
gen_helper_fnegq();
gen_op_store_QT0_fpr(QFPREG(rd));
+ gen_update_fprs_dirty(QFPREG(rd));
break;
case 0xa: /* V9 fabsd */
gen_op_load_fpr_DT1(DFPREG(rs2));
gen_helper_fabsd();
gen_op_store_DT0_fpr(DFPREG(rd));
+ gen_update_fprs_dirty(DFPREG(rd));
break;
case 0xb: /* V9 fabsq */
CHECK_FPU_FEATURE(dc, FLOAT128);
gen_op_load_fpr_QT1(QFPREG(rs2));
gen_helper_fabsq();
gen_op_store_QT0_fpr(QFPREG(rd));
+ gen_update_fprs_dirty(QFPREG(rd));
break;
case 0x81: /* V9 fstox */
gen_clear_float_exceptions();
gen_helper_fstox(cpu_fpr[rs2]);
gen_helper_check_ieee_exceptions();
gen_op_store_DT0_fpr(DFPREG(rd));
+ gen_update_fprs_dirty(DFPREG(rd));
break;
case 0x82: /* V9 fdtox */
gen_op_load_fpr_DT1(DFPREG(rs2));
@@ -2616,6 +2662,7 @@ static void disas_sparc_insn(DisasContext * dc)
gen_helper_fdtox();
gen_helper_check_ieee_exceptions();
gen_op_store_DT0_fpr(DFPREG(rd));
+ gen_update_fprs_dirty(DFPREG(rd));
break;
case 0x83: /* V9 fqtox */
CHECK_FPU_FEATURE(dc, FLOAT128);
@@ -2624,6 +2671,7 @@ static void disas_sparc_insn(DisasContext * dc)
gen_helper_fqtox();
gen_helper_check_ieee_exceptions();
gen_op_store_DT0_fpr(DFPREG(rd));
+ gen_update_fprs_dirty(DFPREG(rd));
break;
case 0x84: /* V9 fxtos */
gen_op_load_fpr_DT1(DFPREG(rs2));
@@ -2631,6 +2679,7 @@ static void disas_sparc_insn(DisasContext * dc)
gen_helper_fxtos(cpu_tmp32);
gen_helper_check_ieee_exceptions();
tcg_gen_mov_i32(cpu_fpr[rd], cpu_tmp32);
+ gen_update_fprs_dirty(rd);
break;
case 0x88: /* V9 fxtod */
gen_op_load_fpr_DT1(DFPREG(rs2));
@@ -2638,6 +2687,7 @@ static void disas_sparc_insn(DisasContext * dc)
gen_helper_fxtod();
gen_helper_check_ieee_exceptions();
gen_op_store_DT0_fpr(DFPREG(rd));
+ gen_update_fprs_dirty(DFPREG(rd));
break;
case 0x8c: /* V9 fxtoq */
CHECK_FPU_FEATURE(dc, FLOAT128);
@@ -2646,6 +2696,7 @@ static void disas_sparc_insn(DisasContext * dc)
gen_helper_fxtoq();
gen_helper_check_ieee_exceptions();
gen_op_store_QT0_fpr(QFPREG(rd));
+ gen_update_fprs_dirty(QFPREG(rd));
break;
#endif
default:
@@ -2672,6 +2723,7 @@ static void disas_sparc_insn(DisasContext * dc)
tcg_gen_brcondi_tl(gen_tcg_cond_reg[cond], cpu_src1,
0, l1);
tcg_gen_mov_i32(cpu_fpr[rd], cpu_fpr[rs2]);
+ gen_update_fprs_dirty(rd);
gen_set_label(l1);
break;
} else if ((xop & 0x11f) == 0x006) { // V9 fmovdr
@@ -2684,6 +2736,7 @@ static void disas_sparc_insn(DisasContext * dc)
0, l1);
tcg_gen_mov_i32(cpu_fpr[DFPREG(rd)], cpu_fpr[DFPREG(rs2)]);
tcg_gen_mov_i32(cpu_fpr[DFPREG(rd) + 1], cpu_fpr[DFPREG(rs2) + 1]);
+ gen_update_fprs_dirty(DFPREG(rd));
gen_set_label(l1);
break;
} else if ((xop & 0x11f) == 0x007) { // V9 fmovqr
@@ -2699,6 +2752,7 @@ static void disas_sparc_insn(DisasContext * dc)
tcg_gen_mov_i32(cpu_fpr[QFPREG(rd) + 1], cpu_fpr[QFPREG(rs2) + 1]);
tcg_gen_mov_i32(cpu_fpr[QFPREG(rd) + 2], cpu_fpr[QFPREG(rs2) + 2]);
tcg_gen_mov_i32(cpu_fpr[QFPREG(rd) + 3], cpu_fpr[QFPREG(rs2) + 3]);
+ gen_update_fprs_dirty(QFPREG(rd));
gen_set_label(l1);
break;
}
@@ -2717,6 +2771,7 @@ static void disas_sparc_insn(DisasContext * dc)
tcg_gen_brcondi_tl(TCG_COND_EQ, r_cond, \
0, l1); \
tcg_gen_mov_i32(cpu_fpr[rd], cpu_fpr[rs2]); \
+ gen_update_fprs_dirty(rd); \
gen_set_label(l1); \
tcg_temp_free(r_cond); \
}
@@ -2735,6 +2790,7 @@ static void disas_sparc_insn(DisasContext * dc)
cpu_fpr[DFPREG(rs2)]); \
tcg_gen_mov_i32(cpu_fpr[DFPREG(rd) + 1], \
cpu_fpr[DFPREG(rs2) + 1]); \
+ gen_update_fprs_dirty(DFPREG(rd)); \
gen_set_label(l1); \
tcg_temp_free(r_cond); \
}
@@ -2757,6 +2813,7 @@ static void disas_sparc_insn(DisasContext * dc)
cpu_fpr[QFPREG(rs2) + 2]); \
tcg_gen_mov_i32(cpu_fpr[QFPREG(rd) + 3], \
cpu_fpr[QFPREG(rs2) + 3]); \
+ gen_update_fprs_dirty(QFPREG(rd)); \
gen_set_label(l1); \
tcg_temp_free(r_cond); \
}
@@ -2815,6 +2872,7 @@ static void disas_sparc_insn(DisasContext * dc)
tcg_gen_brcondi_tl(TCG_COND_EQ, r_cond, \
0, l1); \
tcg_gen_mov_i32(cpu_fpr[rd], cpu_fpr[rs2]); \
+ gen_update_fprs_dirty(rd); \
gen_set_label(l1); \
tcg_temp_free(r_cond); \
}
@@ -2833,6 +2891,7 @@ static void disas_sparc_insn(DisasContext * dc)
cpu_fpr[DFPREG(rs2)]); \
tcg_gen_mov_i32(cpu_fpr[DFPREG(rd) + 1], \
cpu_fpr[DFPREG(rs2) + 1]); \
+ gen_update_fprs_dirty(DFPREG(rd)); \
gen_set_label(l1); \
tcg_temp_free(r_cond); \
}
@@ -2855,6 +2914,7 @@ static void disas_sparc_insn(DisasContext * dc)
cpu_fpr[QFPREG(rs2) + 2]); \
tcg_gen_mov_i32(cpu_fpr[QFPREG(rd) + 3], \
cpu_fpr[QFPREG(rs2) + 3]); \
+ gen_update_fprs_dirty(QFPREG(rd)); \
gen_set_label(l1); \
tcg_temp_free(r_cond); \
}
@@ -2864,6 +2924,7 @@ static void disas_sparc_insn(DisasContext * dc)
break;
case 0x102: /* V9 fmovdcc %icc */
FMOVDCC(0);
+ break;
case 0x103: /* V9 fmovqcc %icc */
CHECK_FPU_FEATURE(dc, FLOAT128);
FMOVQCC(0);
@@ -3789,57 +3850,57 @@ static void disas_sparc_insn(DisasContext * dc)
CHECK_FPU_FEATURE(dc, VIS1);
gen_op_load_fpr_DT0(DFPREG(rs1));
gen_op_load_fpr_DT1(DFPREG(rs2));
- gen_helper_fcmple16();
- gen_op_store_DT0_fpr(DFPREG(rd));
+ gen_helper_fcmple16(cpu_dst);
+ gen_movl_TN_reg(rd, cpu_dst);
break;
case 0x022: /* VIS I fcmpne16 */
CHECK_FPU_FEATURE(dc, VIS1);
gen_op_load_fpr_DT0(DFPREG(rs1));
gen_op_load_fpr_DT1(DFPREG(rs2));
- gen_helper_fcmpne16();
- gen_op_store_DT0_fpr(DFPREG(rd));
+ gen_helper_fcmpne16(cpu_dst);
+ gen_movl_TN_reg(rd, cpu_dst);
break;
case 0x024: /* VIS I fcmple32 */
CHECK_FPU_FEATURE(dc, VIS1);
gen_op_load_fpr_DT0(DFPREG(rs1));
gen_op_load_fpr_DT1(DFPREG(rs2));
- gen_helper_fcmple32();
- gen_op_store_DT0_fpr(DFPREG(rd));
+ gen_helper_fcmple32(cpu_dst);
+ gen_movl_TN_reg(rd, cpu_dst);
break;
case 0x026: /* VIS I fcmpne32 */
CHECK_FPU_FEATURE(dc, VIS1);
gen_op_load_fpr_DT0(DFPREG(rs1));
gen_op_load_fpr_DT1(DFPREG(rs2));
- gen_helper_fcmpne32();
- gen_op_store_DT0_fpr(DFPREG(rd));
+ gen_helper_fcmpne32(cpu_dst);
+ gen_movl_TN_reg(rd, cpu_dst);
break;
case 0x028: /* VIS I fcmpgt16 */
CHECK_FPU_FEATURE(dc, VIS1);
gen_op_load_fpr_DT0(DFPREG(rs1));
gen_op_load_fpr_DT1(DFPREG(rs2));
- gen_helper_fcmpgt16();
- gen_op_store_DT0_fpr(DFPREG(rd));
+ gen_helper_fcmpgt16(cpu_dst);
+ gen_movl_TN_reg(rd, cpu_dst);
break;
case 0x02a: /* VIS I fcmpeq16 */
CHECK_FPU_FEATURE(dc, VIS1);
gen_op_load_fpr_DT0(DFPREG(rs1));
gen_op_load_fpr_DT1(DFPREG(rs2));
- gen_helper_fcmpeq16();
- gen_op_store_DT0_fpr(DFPREG(rd));
+ gen_helper_fcmpeq16(cpu_dst);
+ gen_movl_TN_reg(rd, cpu_dst);
break;
case 0x02c: /* VIS I fcmpgt32 */
CHECK_FPU_FEATURE(dc, VIS1);
gen_op_load_fpr_DT0(DFPREG(rs1));
gen_op_load_fpr_DT1(DFPREG(rs2));
- gen_helper_fcmpgt32();
- gen_op_store_DT0_fpr(DFPREG(rd));
+ gen_helper_fcmpgt32(cpu_dst);
+ gen_movl_TN_reg(rd, cpu_dst);
break;
case 0x02e: /* VIS I fcmpeq32 */
CHECK_FPU_FEATURE(dc, VIS1);
gen_op_load_fpr_DT0(DFPREG(rs1));
gen_op_load_fpr_DT1(DFPREG(rs2));
- gen_helper_fcmpeq32();
- gen_op_store_DT0_fpr(DFPREG(rd));
+ gen_helper_fcmpeq32(cpu_dst);
+ gen_movl_TN_reg(rd, cpu_dst);
break;
case 0x031: /* VIS I fmul8x16 */
CHECK_FPU_FEATURE(dc, VIS1);
@@ -3847,6 +3908,7 @@ static void disas_sparc_insn(DisasContext * dc)
gen_op_load_fpr_DT1(DFPREG(rs2));
gen_helper_fmul8x16();
gen_op_store_DT0_fpr(DFPREG(rd));
+ gen_update_fprs_dirty(DFPREG(rd));
break;
case 0x033: /* VIS I fmul8x16au */
CHECK_FPU_FEATURE(dc, VIS1);
@@ -3854,6 +3916,7 @@ static void disas_sparc_insn(DisasContext * dc)
gen_op_load_fpr_DT1(DFPREG(rs2));
gen_helper_fmul8x16au();
gen_op_store_DT0_fpr(DFPREG(rd));
+ gen_update_fprs_dirty(DFPREG(rd));
break;
case 0x035: /* VIS I fmul8x16al */
CHECK_FPU_FEATURE(dc, VIS1);
@@ -3861,6 +3924,7 @@ static void disas_sparc_insn(DisasContext * dc)
gen_op_load_fpr_DT1(DFPREG(rs2));
gen_helper_fmul8x16al();
gen_op_store_DT0_fpr(DFPREG(rd));
+ gen_update_fprs_dirty(DFPREG(rd));
break;
case 0x036: /* VIS I fmul8sux16 */
CHECK_FPU_FEATURE(dc, VIS1);
@@ -3868,6 +3932,7 @@ static void disas_sparc_insn(DisasContext * dc)
gen_op_load_fpr_DT1(DFPREG(rs2));
gen_helper_fmul8sux16();
gen_op_store_DT0_fpr(DFPREG(rd));
+ gen_update_fprs_dirty(DFPREG(rd));
break;
case 0x037: /* VIS I fmul8ulx16 */
CHECK_FPU_FEATURE(dc, VIS1);
@@ -3875,6 +3940,7 @@ static void disas_sparc_insn(DisasContext * dc)
gen_op_load_fpr_DT1(DFPREG(rs2));
gen_helper_fmul8ulx16();
gen_op_store_DT0_fpr(DFPREG(rd));
+ gen_update_fprs_dirty(DFPREG(rd));
break;
case 0x038: /* VIS I fmuld8sux16 */
CHECK_FPU_FEATURE(dc, VIS1);
@@ -3882,6 +3948,7 @@ static void disas_sparc_insn(DisasContext * dc)
gen_op_load_fpr_DT1(DFPREG(rs2));
gen_helper_fmuld8sux16();
gen_op_store_DT0_fpr(DFPREG(rd));
+ gen_update_fprs_dirty(DFPREG(rd));
break;
case 0x039: /* VIS I fmuld8ulx16 */
CHECK_FPU_FEATURE(dc, VIS1);
@@ -3889,6 +3956,7 @@ static void disas_sparc_insn(DisasContext * dc)
gen_op_load_fpr_DT1(DFPREG(rs2));
gen_helper_fmuld8ulx16();
gen_op_store_DT0_fpr(DFPREG(rd));
+ gen_update_fprs_dirty(DFPREG(rd));
break;
case 0x03a: /* VIS I fpack32 */
case 0x03b: /* VIS I fpack16 */
@@ -3902,6 +3970,7 @@ static void disas_sparc_insn(DisasContext * dc)
gen_op_load_fpr_DT1(DFPREG(rs2));
gen_helper_faligndata();
gen_op_store_DT0_fpr(DFPREG(rd));
+ gen_update_fprs_dirty(DFPREG(rd));
break;
case 0x04b: /* VIS I fpmerge */
CHECK_FPU_FEATURE(dc, VIS1);
@@ -3909,6 +3978,7 @@ static void disas_sparc_insn(DisasContext * dc)
gen_op_load_fpr_DT1(DFPREG(rs2));
gen_helper_fpmerge();
gen_op_store_DT0_fpr(DFPREG(rd));
+ gen_update_fprs_dirty(DFPREG(rd));
break;
case 0x04c: /* VIS II bshuffle */
// XXX
@@ -3919,6 +3989,7 @@ static void disas_sparc_insn(DisasContext * dc)
gen_op_load_fpr_DT1(DFPREG(rs2));
gen_helper_fexpand();
gen_op_store_DT0_fpr(DFPREG(rd));
+ gen_update_fprs_dirty(DFPREG(rd));
break;
case 0x050: /* VIS I fpadd16 */
CHECK_FPU_FEATURE(dc, VIS1);
@@ -3926,11 +3997,13 @@ static void disas_sparc_insn(DisasContext * dc)
gen_op_load_fpr_DT1(DFPREG(rs2));
gen_helper_fpadd16();
gen_op_store_DT0_fpr(DFPREG(rd));
+ gen_update_fprs_dirty(DFPREG(rd));
break;
case 0x051: /* VIS I fpadd16s */
CHECK_FPU_FEATURE(dc, VIS1);
gen_helper_fpadd16s(cpu_fpr[rd],
cpu_fpr[rs1], cpu_fpr[rs2]);
+ gen_update_fprs_dirty(rd);
break;
case 0x052: /* VIS I fpadd32 */
CHECK_FPU_FEATURE(dc, VIS1);
@@ -3938,11 +4011,13 @@ static void disas_sparc_insn(DisasContext * dc)
gen_op_load_fpr_DT1(DFPREG(rs2));
gen_helper_fpadd32();
gen_op_store_DT0_fpr(DFPREG(rd));
+ gen_update_fprs_dirty(DFPREG(rd));
break;
case 0x053: /* VIS I fpadd32s */
CHECK_FPU_FEATURE(dc, VIS1);
gen_helper_fpadd32s(cpu_fpr[rd],
cpu_fpr[rs1], cpu_fpr[rs2]);
+ gen_update_fprs_dirty(rd);
break;
case 0x054: /* VIS I fpsub16 */
CHECK_FPU_FEATURE(dc, VIS1);
@@ -3950,11 +4025,13 @@ static void disas_sparc_insn(DisasContext * dc)
gen_op_load_fpr_DT1(DFPREG(rs2));
gen_helper_fpsub16();
gen_op_store_DT0_fpr(DFPREG(rd));
+ gen_update_fprs_dirty(DFPREG(rd));
break;
case 0x055: /* VIS I fpsub16s */
CHECK_FPU_FEATURE(dc, VIS1);
gen_helper_fpsub16s(cpu_fpr[rd],
cpu_fpr[rs1], cpu_fpr[rs2]);
+ gen_update_fprs_dirty(rd);
break;
case 0x056: /* VIS I fpsub32 */
CHECK_FPU_FEATURE(dc, VIS1);
@@ -3962,31 +4039,38 @@ static void disas_sparc_insn(DisasContext * dc)
gen_op_load_fpr_DT1(DFPREG(rs2));
gen_helper_fpsub32();
gen_op_store_DT0_fpr(DFPREG(rd));
+ gen_update_fprs_dirty(DFPREG(rd));
break;
case 0x057: /* VIS I fpsub32s */
CHECK_FPU_FEATURE(dc, VIS1);
gen_helper_fpsub32s(cpu_fpr[rd],
cpu_fpr[rs1], cpu_fpr[rs2]);
+ gen_update_fprs_dirty(rd);
break;
case 0x060: /* VIS I fzero */
CHECK_FPU_FEATURE(dc, VIS1);
tcg_gen_movi_i32(cpu_fpr[DFPREG(rd)], 0);
tcg_gen_movi_i32(cpu_fpr[DFPREG(rd) + 1], 0);
+ gen_update_fprs_dirty(DFPREG(rd));
break;
case 0x061: /* VIS I fzeros */
CHECK_FPU_FEATURE(dc, VIS1);
tcg_gen_movi_i32(cpu_fpr[rd], 0);
+ gen_update_fprs_dirty(rd);
break;
case 0x062: /* VIS I fnor */
CHECK_FPU_FEATURE(dc, VIS1);
- tcg_gen_nor_i32(cpu_tmp32, cpu_fpr[DFPREG(rs1)],
+ tcg_gen_nor_i32(cpu_fpr[DFPREG(rd)], cpu_fpr[DFPREG(rs1)],
cpu_fpr[DFPREG(rs2)]);
- tcg_gen_nor_i32(cpu_tmp32, cpu_fpr[DFPREG(rs1) + 1],
+ tcg_gen_nor_i32(cpu_fpr[DFPREG(rd) + 1],
+ cpu_fpr[DFPREG(rs1) + 1],
cpu_fpr[DFPREG(rs2) + 1]);
+ gen_update_fprs_dirty(DFPREG(rd));
break;
case 0x063: /* VIS I fnors */
CHECK_FPU_FEATURE(dc, VIS1);
- tcg_gen_nor_i32(cpu_tmp32, cpu_fpr[rs1], cpu_fpr[rs2]);
+ tcg_gen_nor_i32(cpu_fpr[rd], cpu_fpr[rs1], cpu_fpr[rs2]);
+ gen_update_fprs_dirty(rd);
break;
case 0x064: /* VIS I fandnot2 */
CHECK_FPU_FEATURE(dc, VIS1);
@@ -3995,20 +4079,24 @@ static void disas_sparc_insn(DisasContext * dc)
tcg_gen_andc_i32(cpu_fpr[DFPREG(rd) + 1],
cpu_fpr[DFPREG(rs1) + 1],
cpu_fpr[DFPREG(rs2) + 1]);
+ gen_update_fprs_dirty(DFPREG(rd));
break;
case 0x065: /* VIS I fandnot2s */
CHECK_FPU_FEATURE(dc, VIS1);
tcg_gen_andc_i32(cpu_fpr[rd], cpu_fpr[rs1], cpu_fpr[rs2]);
+ gen_update_fprs_dirty(rd);
break;
case 0x066: /* VIS I fnot2 */
CHECK_FPU_FEATURE(dc, VIS1);
tcg_gen_not_i32(cpu_fpr[DFPREG(rd)], cpu_fpr[DFPREG(rs2)]);
tcg_gen_not_i32(cpu_fpr[DFPREG(rd) + 1],
cpu_fpr[DFPREG(rs2) + 1]);
+ gen_update_fprs_dirty(DFPREG(rd));
break;
case 0x067: /* VIS I fnot2s */
CHECK_FPU_FEATURE(dc, VIS1);
tcg_gen_not_i32(cpu_fpr[rd], cpu_fpr[rs2]);
+ gen_update_fprs_dirty(rd);
break;
case 0x068: /* VIS I fandnot1 */
CHECK_FPU_FEATURE(dc, VIS1);
@@ -4017,20 +4105,24 @@ static void disas_sparc_insn(DisasContext * dc)
tcg_gen_andc_i32(cpu_fpr[DFPREG(rd) + 1],
cpu_fpr[DFPREG(rs2) + 1],
cpu_fpr[DFPREG(rs1) + 1]);
+ gen_update_fprs_dirty(DFPREG(rd));
break;
case 0x069: /* VIS I fandnot1s */
CHECK_FPU_FEATURE(dc, VIS1);
tcg_gen_andc_i32(cpu_fpr[rd], cpu_fpr[rs2], cpu_fpr[rs1]);
+ gen_update_fprs_dirty(rd);
break;
case 0x06a: /* VIS I fnot1 */
CHECK_FPU_FEATURE(dc, VIS1);
tcg_gen_not_i32(cpu_fpr[DFPREG(rd)], cpu_fpr[DFPREG(rs1)]);
tcg_gen_not_i32(cpu_fpr[DFPREG(rd) + 1],
cpu_fpr[DFPREG(rs1) + 1]);
+ gen_update_fprs_dirty(DFPREG(rd));
break;
case 0x06b: /* VIS I fnot1s */
CHECK_FPU_FEATURE(dc, VIS1);
tcg_gen_not_i32(cpu_fpr[rd], cpu_fpr[rs1]);
+ gen_update_fprs_dirty(rd);
break;
case 0x06c: /* VIS I fxor */
CHECK_FPU_FEATURE(dc, VIS1);
@@ -4039,21 +4131,26 @@ static void disas_sparc_insn(DisasContext * dc)
tcg_gen_xor_i32(cpu_fpr[DFPREG(rd) + 1],
cpu_fpr[DFPREG(rs1) + 1],
cpu_fpr[DFPREG(rs2) + 1]);
+ gen_update_fprs_dirty(DFPREG(rd));
break;
case 0x06d: /* VIS I fxors */
CHECK_FPU_FEATURE(dc, VIS1);
tcg_gen_xor_i32(cpu_fpr[rd], cpu_fpr[rs1], cpu_fpr[rs2]);
+ gen_update_fprs_dirty(rd);
break;
case 0x06e: /* VIS I fnand */
CHECK_FPU_FEATURE(dc, VIS1);
- tcg_gen_nand_i32(cpu_tmp32, cpu_fpr[DFPREG(rs1)],
+ tcg_gen_nand_i32(cpu_fpr[DFPREG(rd)], cpu_fpr[DFPREG(rs1)],
cpu_fpr[DFPREG(rs2)]);
- tcg_gen_nand_i32(cpu_tmp32, cpu_fpr[DFPREG(rs1) + 1],
+ tcg_gen_nand_i32(cpu_fpr[DFPREG(rd) + 1],
+ cpu_fpr[DFPREG(rs1) + 1],
cpu_fpr[DFPREG(rs2) + 1]);
+ gen_update_fprs_dirty(DFPREG(rd));
break;
case 0x06f: /* VIS I fnands */
CHECK_FPU_FEATURE(dc, VIS1);
- tcg_gen_nand_i32(cpu_tmp32, cpu_fpr[rs1], cpu_fpr[rs2]);
+ tcg_gen_nand_i32(cpu_fpr[rd], cpu_fpr[rs1], cpu_fpr[rs2]);
+ gen_update_fprs_dirty(rd);
break;
case 0x070: /* VIS I fand */
CHECK_FPU_FEATURE(dc, VIS1);
@@ -4062,10 +4159,12 @@ static void disas_sparc_insn(DisasContext * dc)
tcg_gen_and_i32(cpu_fpr[DFPREG(rd) + 1],
cpu_fpr[DFPREG(rs1) + 1],
cpu_fpr[DFPREG(rs2) + 1]);
+ gen_update_fprs_dirty(DFPREG(rd));
break;
case 0x071: /* VIS I fands */
CHECK_FPU_FEATURE(dc, VIS1);
tcg_gen_and_i32(cpu_fpr[rd], cpu_fpr[rs1], cpu_fpr[rs2]);
+ gen_update_fprs_dirty(rd);
break;
case 0x072: /* VIS I fxnor */
CHECK_FPU_FEATURE(dc, VIS1);
@@ -4075,21 +4174,25 @@ static void disas_sparc_insn(DisasContext * dc)
tcg_gen_xori_i32(cpu_tmp32, cpu_fpr[DFPREG(rs2) + 1], -1);
tcg_gen_xor_i32(cpu_fpr[DFPREG(rd) + 1], cpu_tmp32,
cpu_fpr[DFPREG(rs1) + 1]);
+ gen_update_fprs_dirty(DFPREG(rd));
break;
case 0x073: /* VIS I fxnors */
CHECK_FPU_FEATURE(dc, VIS1);
tcg_gen_xori_i32(cpu_tmp32, cpu_fpr[rs2], -1);
tcg_gen_xor_i32(cpu_fpr[rd], cpu_tmp32, cpu_fpr[rs1]);
+ gen_update_fprs_dirty(rd);
break;
case 0x074: /* VIS I fsrc1 */
CHECK_FPU_FEATURE(dc, VIS1);
tcg_gen_mov_i32(cpu_fpr[DFPREG(rd)], cpu_fpr[DFPREG(rs1)]);
tcg_gen_mov_i32(cpu_fpr[DFPREG(rd) + 1],
cpu_fpr[DFPREG(rs1) + 1]);
+ gen_update_fprs_dirty(DFPREG(rd));
break;
case 0x075: /* VIS I fsrc1s */
CHECK_FPU_FEATURE(dc, VIS1);
tcg_gen_mov_i32(cpu_fpr[rd], cpu_fpr[rs1]);
+ gen_update_fprs_dirty(rd);
break;
case 0x076: /* VIS I fornot2 */
CHECK_FPU_FEATURE(dc, VIS1);
@@ -4098,19 +4201,23 @@ static void disas_sparc_insn(DisasContext * dc)
tcg_gen_orc_i32(cpu_fpr[DFPREG(rd) + 1],
cpu_fpr[DFPREG(rs1) + 1],
cpu_fpr[DFPREG(rs2) + 1]);
+ gen_update_fprs_dirty(DFPREG(rd));
break;
case 0x077: /* VIS I fornot2s */
CHECK_FPU_FEATURE(dc, VIS1);
tcg_gen_orc_i32(cpu_fpr[rd], cpu_fpr[rs1], cpu_fpr[rs2]);
+ gen_update_fprs_dirty(rd);
break;
case 0x078: /* VIS I fsrc2 */
CHECK_FPU_FEATURE(dc, VIS1);
gen_op_load_fpr_DT0(DFPREG(rs2));
gen_op_store_DT0_fpr(DFPREG(rd));
+ gen_update_fprs_dirty(DFPREG(rd));
break;
case 0x079: /* VIS I fsrc2s */
CHECK_FPU_FEATURE(dc, VIS1);
tcg_gen_mov_i32(cpu_fpr[rd], cpu_fpr[rs2]);
+ gen_update_fprs_dirty(rd);
break;
case 0x07a: /* VIS I fornot1 */
CHECK_FPU_FEATURE(dc, VIS1);
@@ -4119,10 +4226,12 @@ static void disas_sparc_insn(DisasContext * dc)
tcg_gen_orc_i32(cpu_fpr[DFPREG(rd) + 1],
cpu_fpr[DFPREG(rs2) + 1],
cpu_fpr[DFPREG(rs1) + 1]);
+ gen_update_fprs_dirty(DFPREG(rd));
break;
case 0x07b: /* VIS I fornot1s */
CHECK_FPU_FEATURE(dc, VIS1);
tcg_gen_orc_i32(cpu_fpr[rd], cpu_fpr[rs2], cpu_fpr[rs1]);
+ gen_update_fprs_dirty(rd);
break;
case 0x07c: /* VIS I for */
CHECK_FPU_FEATURE(dc, VIS1);
@@ -4131,19 +4240,23 @@ static void disas_sparc_insn(DisasContext * dc)
tcg_gen_or_i32(cpu_fpr[DFPREG(rd) + 1],
cpu_fpr[DFPREG(rs1) + 1],
cpu_fpr[DFPREG(rs2) + 1]);
+ gen_update_fprs_dirty(DFPREG(rd));
break;
case 0x07d: /* VIS I fors */
CHECK_FPU_FEATURE(dc, VIS1);
tcg_gen_or_i32(cpu_fpr[rd], cpu_fpr[rs1], cpu_fpr[rs2]);
+ gen_update_fprs_dirty(rd);
break;
case 0x07e: /* VIS I fone */
CHECK_FPU_FEATURE(dc, VIS1);
tcg_gen_movi_i32(cpu_fpr[DFPREG(rd)], -1);
tcg_gen_movi_i32(cpu_fpr[DFPREG(rd) + 1], -1);
+ gen_update_fprs_dirty(DFPREG(rd));
break;
case 0x07f: /* VIS I fones */
CHECK_FPU_FEATURE(dc, VIS1);
tcg_gen_movi_i32(cpu_fpr[rd], -1);
+ gen_update_fprs_dirty(rd);
break;
case 0x080: /* VIS I shutdown */
case 0x081: /* VIS II siam */
@@ -4489,6 +4602,7 @@ static void disas_sparc_insn(DisasContext * dc)
}
save_state(dc, cpu_cond);
gen_ldf_asi(cpu_addr, insn, 4, rd);
+ gen_update_fprs_dirty(rd);
goto skip_move;
case 0x33: /* V9 lddfa */
if (gen_trap_ifnofpu(dc, cpu_cond)) {
@@ -4496,6 +4610,7 @@ static void disas_sparc_insn(DisasContext * dc)
}
save_state(dc, cpu_cond);
gen_ldf_asi(cpu_addr, insn, 8, DFPREG(rd));
+ gen_update_fprs_dirty(DFPREG(rd));
goto skip_move;
case 0x3d: /* V9 prefetcha, no effect */
goto skip_move;
@@ -4506,6 +4621,7 @@ static void disas_sparc_insn(DisasContext * dc)
}
save_state(dc, cpu_cond);
gen_ldf_asi(cpu_addr, insn, 16, QFPREG(rd));
+ gen_update_fprs_dirty(QFPREG(rd));
goto skip_move;
#endif
default:
@@ -4524,6 +4640,7 @@ static void disas_sparc_insn(DisasContext * dc)
gen_address_mask(dc, cpu_addr);
tcg_gen_qemu_ld32u(cpu_tmp0, cpu_addr, dc->mem_idx);
tcg_gen_trunc_tl_i32(cpu_fpr[rd], cpu_tmp0);
+ gen_update_fprs_dirty(rd);
break;
case 0x21: /* ldfsr, V9 ldxfsr */
#ifdef TARGET_SPARC64
@@ -4553,6 +4670,7 @@ static void disas_sparc_insn(DisasContext * dc)
gen_helper_ldqf(cpu_addr, r_const);
tcg_temp_free_i32(r_const);
gen_op_store_QT0_fpr(QFPREG(rd));
+ gen_update_fprs_dirty(QFPREG(rd));
}
break;
case 0x23: /* lddf, load double fpreg */
@@ -4564,6 +4682,7 @@ static void disas_sparc_insn(DisasContext * dc)
gen_helper_lddf(cpu_addr, r_const);
tcg_temp_free_i32(r_const);
gen_op_store_DT0_fpr(DFPREG(rd));
+ gen_update_fprs_dirty(DFPREG(rd));
}
break;
default:
diff --git a/target-unicore32/exec.h b/target-unicore32/exec.h
deleted file mode 100644
index 7912105..0000000
--- a/target-unicore32/exec.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * UniCore32 execution defines
- *
- * Copyright (C) 2010-2011 GUAN Xue-tao
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-#ifndef __UC32_EXEC_H__
-#define __UC32_EXEC_H__
-
-#include "config.h"
-#include "dyngen-exec.h"
-
-register struct CPUState_UniCore32 *env asm(AREG0);
-
-#include "cpu.h"
-
-static inline void env_to_regs(void)
-{
-}
-
-static inline void regs_to_env(void)
-{
-}
-
-static inline int cpu_halted(CPUState *env)
-{
- if (!env->halted) {
- return 0;
- }
- /* An interrupt wakes the CPU even if the I and R ASR bits are
- set. We use EXITTB to silently wake CPU without causing an
- actual interrupt. */
- if (cpu_has_work(env)) {
- env->halted = 0;
- return 0;
- }
- return EXCP_HALTED;
-}
-
-#endif /* __UC32_EXEC_H__ */
diff --git a/target-unicore32/op_helper.c b/target-unicore32/op_helper.c
index 541e6f0..6cf5255 100644
--- a/target-unicore32/op_helper.c
+++ b/target-unicore32/op_helper.c
@@ -7,7 +7,8 @@
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
-#include "exec.h"
+#include "cpu.h"
+#include "dyngen-exec.h"
#include "helper.h"
#define SIGNBIT (uint32_t)0x80000000
diff --git a/tcg/mips/tcg-target.c b/tcg/mips/tcg-target.c
index 12ff9d5..a33d21f 100644
--- a/tcg/mips/tcg-target.c
+++ b/tcg/mips/tcg-target.c
@@ -1484,7 +1484,7 @@ static void tcg_target_qemu_prologue(TCGContext *s)
}
/* Call generated code */
- tcg_out_opc_reg(s, OPC_JR, 0, tcg_target_call_iarg_regs[1]), 0);
+ tcg_out_opc_reg(s, OPC_JR, 0, tcg_target_call_iarg_regs[1], 0);
tcg_out_mov(s, TCG_TYPE_PTR, TCG_AREG0, tcg_target_call_iarg_regs[0]);
tb_ret_addr = s->code_ptr;
diff --git a/tcg/optimize.c b/tcg/optimize.c
new file mode 100644
index 0000000..a3bfa5e
--- /dev/null
+++ b/tcg/optimize.c
@@ -0,0 +1,675 @@
+/*
+ * Optimizations for Tiny Code Generator for QEMU
+ *
+ * Copyright (c) 2010 Samsung Electronics.
+ * Contributed by Kirill Batuzov <batuzovk@ispras.ru>
+ *
+ * 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.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "qemu-common.h"
+#include "tcg-op.h"
+
+#if TCG_TARGET_REG_BITS == 64
+#define CASE_OP_32_64(x) \
+ glue(glue(case INDEX_op_, x), _i32): \
+ glue(glue(case INDEX_op_, x), _i64)
+#else
+#define CASE_OP_32_64(x) \
+ glue(glue(case INDEX_op_, x), _i32)
+#endif
+
+typedef enum {
+ TCG_TEMP_UNDEF = 0,
+ TCG_TEMP_CONST,
+ TCG_TEMP_COPY,
+ TCG_TEMP_HAS_COPY,
+ TCG_TEMP_ANY
+} tcg_temp_state;
+
+struct tcg_temp_info {
+ tcg_temp_state state;
+ uint16_t prev_copy;
+ uint16_t next_copy;
+ tcg_target_ulong val;
+};
+
+static struct tcg_temp_info temps[TCG_MAX_TEMPS];
+
+/* Reset TEMP's state to TCG_TEMP_ANY. If TEMP was a representative of some
+ class of equivalent temp's, a new representative should be chosen in this
+ class. */
+static void reset_temp(TCGArg temp, int nb_temps, int nb_globals)
+{
+ int i;
+ TCGArg new_base = (TCGArg)-1;
+ if (temps[temp].state == TCG_TEMP_HAS_COPY) {
+ for (i = temps[temp].next_copy; i != temp; i = temps[i].next_copy) {
+ if (i >= nb_globals) {
+ temps[i].state = TCG_TEMP_HAS_COPY;
+ new_base = i;
+ break;
+ }
+ }
+ for (i = temps[temp].next_copy; i != temp; i = temps[i].next_copy) {
+ if (new_base == (TCGArg)-1) {
+ temps[i].state = TCG_TEMP_ANY;
+ } else {
+ temps[i].val = new_base;
+ }
+ }
+ temps[temps[temp].next_copy].prev_copy = temps[temp].prev_copy;
+ temps[temps[temp].prev_copy].next_copy = temps[temp].next_copy;
+ } else if (temps[temp].state == TCG_TEMP_COPY) {
+ temps[temps[temp].next_copy].prev_copy = temps[temp].prev_copy;
+ temps[temps[temp].prev_copy].next_copy = temps[temp].next_copy;
+ new_base = temps[temp].val;
+ }
+ temps[temp].state = TCG_TEMP_ANY;
+ if (new_base != (TCGArg)-1 && temps[new_base].next_copy == new_base) {
+ temps[new_base].state = TCG_TEMP_ANY;
+ }
+}
+
+static int op_bits(int op)
+{
+ switch (op) {
+ case INDEX_op_mov_i32:
+ case INDEX_op_add_i32:
+ case INDEX_op_sub_i32:
+ case INDEX_op_mul_i32:
+ case INDEX_op_and_i32:
+ case INDEX_op_or_i32:
+ case INDEX_op_xor_i32:
+ case INDEX_op_shl_i32:
+ case INDEX_op_shr_i32:
+ case INDEX_op_sar_i32:
+#ifdef TCG_TARGET_HAS_rot_i32
+ case INDEX_op_rotl_i32:
+ case INDEX_op_rotr_i32:
+#endif
+#ifdef TCG_TARGET_HAS_not_i32
+ case INDEX_op_not_i32:
+#endif
+#ifdef TCG_TARGET_HAS_ext8s_i32
+ case INDEX_op_ext8s_i32:
+#endif
+#ifdef TCG_TARGET_HAS_ext16s_i32
+ case INDEX_op_ext16s_i32:
+#endif
+#ifdef TCG_TARGET_HAS_ext8u_i32
+ case INDEX_op_ext8u_i32:
+#endif
+#ifdef TCG_TARGET_HAS_ext16u_i32
+ case INDEX_op_ext16u_i32:
+#endif
+ return 32;
+#if TCG_TARGET_REG_BITS == 64
+ case INDEX_op_mov_i64:
+ case INDEX_op_add_i64:
+ case INDEX_op_sub_i64:
+ case INDEX_op_mul_i64:
+ case INDEX_op_and_i64:
+ case INDEX_op_or_i64:
+ case INDEX_op_xor_i64:
+ case INDEX_op_shl_i64:
+ case INDEX_op_shr_i64:
+ case INDEX_op_sar_i64:
+#ifdef TCG_TARGET_HAS_rot_i64
+ case INDEX_op_rotl_i64:
+ case INDEX_op_rotr_i64:
+#endif
+#ifdef TCG_TARGET_HAS_not_i64
+ case INDEX_op_not_i64:
+#endif
+#ifdef TCG_TARGET_HAS_ext8s_i64
+ case INDEX_op_ext8s_i64:
+#endif
+#ifdef TCG_TARGET_HAS_ext16s_i64
+ case INDEX_op_ext16s_i64:
+#endif
+#ifdef TCG_TARGET_HAS_ext32s_i64
+ case INDEX_op_ext32s_i64:
+#endif
+#ifdef TCG_TARGET_HAS_ext8u_i64
+ case INDEX_op_ext8u_i64:
+#endif
+#ifdef TCG_TARGET_HAS_ext16u_i64
+ case INDEX_op_ext16u_i64:
+#endif
+#ifdef TCG_TARGET_HAS_ext32u_i64
+ case INDEX_op_ext32u_i64:
+#endif
+ return 64;
+#endif
+ default:
+ fprintf(stderr, "Unrecognized operation %d in op_bits.\n", op);
+ tcg_abort();
+ }
+}
+
+static int op_to_movi(int op)
+{
+ switch (op_bits(op)) {
+ case 32:
+ return INDEX_op_movi_i32;
+#if TCG_TARGET_REG_BITS == 64
+ case 64:
+ return INDEX_op_movi_i64;
+#endif
+ default:
+ fprintf(stderr, "op_to_movi: unexpected return value of "
+ "function op_bits.\n");
+ tcg_abort();
+ }
+}
+
+static void tcg_opt_gen_mov(TCGArg *gen_args, TCGArg dst, TCGArg src,
+ int nb_temps, int nb_globals)
+{
+ reset_temp(dst, nb_temps, nb_globals);
+ assert(temps[src].state != TCG_TEMP_COPY);
+ if (src >= nb_globals) {
+ assert(temps[src].state != TCG_TEMP_CONST);
+ if (temps[src].state != TCG_TEMP_HAS_COPY) {
+ temps[src].state = TCG_TEMP_HAS_COPY;
+ temps[src].next_copy = src;
+ temps[src].prev_copy = src;
+ }
+ temps[dst].state = TCG_TEMP_COPY;
+ temps[dst].val = src;
+ temps[dst].next_copy = temps[src].next_copy;
+ temps[dst].prev_copy = src;
+ temps[temps[dst].next_copy].prev_copy = dst;
+ temps[src].next_copy = dst;
+ }
+ gen_args[0] = dst;
+ gen_args[1] = src;
+}
+
+static void tcg_opt_gen_movi(TCGArg *gen_args, TCGArg dst, TCGArg val,
+ int nb_temps, int nb_globals)
+{
+ reset_temp(dst, nb_temps, nb_globals);
+ temps[dst].state = TCG_TEMP_CONST;
+ temps[dst].val = val;
+ gen_args[0] = dst;
+ gen_args[1] = val;
+}
+
+static int op_to_mov(int op)
+{
+ switch (op_bits(op)) {
+ case 32:
+ return INDEX_op_mov_i32;
+#if TCG_TARGET_REG_BITS == 64
+ case 64:
+ return INDEX_op_mov_i64;
+#endif
+ default:
+ fprintf(stderr, "op_to_mov: unexpected return value of "
+ "function op_bits.\n");
+ tcg_abort();
+ }
+}
+
+static TCGArg do_constant_folding_2(int op, TCGArg x, TCGArg y)
+{
+ switch (op) {
+ CASE_OP_32_64(add):
+ return x + y;
+
+ CASE_OP_32_64(sub):
+ return x - y;
+
+ CASE_OP_32_64(mul):
+ return x * y;
+
+ CASE_OP_32_64(and):
+ return x & y;
+
+ CASE_OP_32_64(or):
+ return x | y;
+
+ CASE_OP_32_64(xor):
+ return x ^ y;
+
+ case INDEX_op_shl_i32:
+ return (uint32_t)x << (uint32_t)y;
+
+#if TCG_TARGET_REG_BITS == 64
+ case INDEX_op_shl_i64:
+ return (uint64_t)x << (uint64_t)y;
+#endif
+
+ case INDEX_op_shr_i32:
+ return (uint32_t)x >> (uint32_t)y;
+
+#if TCG_TARGET_REG_BITS == 64
+ case INDEX_op_shr_i64:
+ return (uint64_t)x >> (uint64_t)y;
+#endif
+
+ case INDEX_op_sar_i32:
+ return (int32_t)x >> (int32_t)y;
+
+#if TCG_TARGET_REG_BITS == 64
+ case INDEX_op_sar_i64:
+ return (int64_t)x >> (int64_t)y;
+#endif
+
+#ifdef TCG_TARGET_HAS_rot_i32
+ case INDEX_op_rotr_i32:
+#if TCG_TARGET_REG_BITS == 64
+ x &= 0xffffffff;
+ y &= 0xffffffff;
+#endif
+ x = (x << (32 - y)) | (x >> y);
+ return x;
+#endif
+
+#ifdef TCG_TARGET_HAS_rot_i64
+#if TCG_TARGET_REG_BITS == 64
+ case INDEX_op_rotr_i64:
+ x = (x << (64 - y)) | (x >> y);
+ return x;
+#endif
+#endif
+
+#ifdef TCG_TARGET_HAS_rot_i32
+ case INDEX_op_rotl_i32:
+#if TCG_TARGET_REG_BITS == 64
+ x &= 0xffffffff;
+ y &= 0xffffffff;
+#endif
+ x = (x << y) | (x >> (32 - y));
+ return x;
+#endif
+
+#ifdef TCG_TARGET_HAS_rot_i64
+#if TCG_TARGET_REG_BITS == 64
+ case INDEX_op_rotl_i64:
+ x = (x << y) | (x >> (64 - y));
+ return x;
+#endif
+#endif
+
+#if defined(TCG_TARGET_HAS_not_i32) || defined(TCG_TARGET_HAS_not_i64)
+#ifdef TCG_TARGET_HAS_not_i32
+ case INDEX_op_not_i32:
+#endif
+#ifdef TCG_TARGET_HAS_not_i64
+ case INDEX_op_not_i64:
+#endif
+ return ~x;
+#endif
+
+#if defined(TCG_TARGET_HAS_ext8s_i32) || defined(TCG_TARGET_HAS_ext8s_i64)
+#ifdef TCG_TARGET_HAS_ext8s_i32
+ case INDEX_op_ext8s_i32:
+#endif
+#ifdef TCG_TARGET_HAS_ext8s_i64
+ case INDEX_op_ext8s_i64:
+#endif
+ return (int8_t)x;
+#endif
+
+#if defined(TCG_TARGET_HAS_ext16s_i32) || defined(TCG_TARGET_HAS_ext16s_i64)
+#ifdef TCG_TARGET_HAS_ext16s_i32
+ case INDEX_op_ext16s_i32:
+#endif
+#ifdef TCG_TARGET_HAS_ext16s_i64
+ case INDEX_op_ext16s_i64:
+#endif
+ return (int16_t)x;
+#endif
+
+#if defined(TCG_TARGET_HAS_ext8u_i32) || defined(TCG_TARGET_HAS_ext8u_i64)
+#ifdef TCG_TARGET_HAS_ext8u_i32
+ case INDEX_op_ext8u_i32:
+#endif
+#ifdef TCG_TARGET_HAS_ext8u_i64
+ case INDEX_op_ext8u_i64:
+#endif
+ return (uint8_t)x;
+#endif
+
+#if defined(TCG_TARGET_HAS_ext16u_i32) || defined(TCG_TARGET_HAS_ext16u_i64)
+#ifdef TCG_TARGET_HAS_ext16u_i32
+ case INDEX_op_ext16u_i32:
+#endif
+#ifdef TCG_TARGET_HAS_ext16u_i64
+ case INDEX_op_ext16u_i64:
+#endif
+ return (uint16_t)x;
+#endif
+
+#if TCG_TARGET_REG_BITS == 64
+#ifdef TCG_TARGET_HAS_ext32s_i64
+ case INDEX_op_ext32s_i64:
+ return (int32_t)x;
+#endif
+
+#ifdef TCG_TARGET_HAS_ext32u_i64
+ case INDEX_op_ext32u_i64:
+ return (uint32_t)x;
+#endif
+#endif
+
+ default:
+ fprintf(stderr,
+ "Unrecognized operation %d in do_constant_folding.\n", op);
+ tcg_abort();
+ }
+}
+
+static TCGArg do_constant_folding(int op, TCGArg x, TCGArg y)
+{
+ TCGArg res = do_constant_folding_2(op, x, y);
+#if TCG_TARGET_REG_BITS == 64
+ if (op_bits(op) == 32) {
+ res &= 0xffffffff;
+ }
+#endif
+ return res;
+}
+
+/* Propagate constants and copies, fold constant expressions. */
+static TCGArg *tcg_constant_folding(TCGContext *s, uint16_t *tcg_opc_ptr,
+ TCGArg *args, TCGOpDef *tcg_op_defs)
+{
+ int i, nb_ops, op_index, op, nb_temps, nb_globals, nb_call_args;
+ const TCGOpDef *def;
+ TCGArg *gen_args;
+ TCGArg tmp;
+ /* Array VALS has an element for each temp.
+ If this temp holds a constant then its value is kept in VALS' element.
+ If this temp is a copy of other ones then this equivalence class'
+ representative is kept in VALS' element.
+ If this temp is neither copy nor constant then corresponding VALS'
+ element is unused. */
+
+ nb_temps = s->nb_temps;
+ nb_globals = s->nb_globals;
+ memset(temps, 0, nb_temps * sizeof(struct tcg_temp_info));
+
+ nb_ops = tcg_opc_ptr - gen_opc_buf;
+ gen_args = args;
+ for (op_index = 0; op_index < nb_ops; op_index++) {
+ op = gen_opc_buf[op_index];
+ def = &tcg_op_defs[op];
+ /* Do copy propagation */
+ if (!(def->flags & (TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS))) {
+ assert(op != INDEX_op_call);
+ for (i = def->nb_oargs; i < def->nb_oargs + def->nb_iargs; i++) {
+ if (temps[args[i]].state == TCG_TEMP_COPY) {
+ args[i] = temps[args[i]].val;
+ }
+ }
+ }
+
+ /* For commutative operations make constant second argument */
+ switch (op) {
+ CASE_OP_32_64(add):
+ CASE_OP_32_64(mul):
+ CASE_OP_32_64(and):
+ CASE_OP_32_64(or):
+ CASE_OP_32_64(xor):
+ if (temps[args[1]].state == TCG_TEMP_CONST) {
+ tmp = args[1];
+ args[1] = args[2];
+ args[2] = tmp;
+ }
+ break;
+ default:
+ break;
+ }
+
+ /* Simplify expression if possible. */
+ switch (op) {
+ CASE_OP_32_64(add):
+ CASE_OP_32_64(sub):
+ CASE_OP_32_64(shl):
+ CASE_OP_32_64(shr):
+ CASE_OP_32_64(sar):
+#ifdef TCG_TARGET_HAS_rot_i32
+ case INDEX_op_rotl_i32:
+ case INDEX_op_rotr_i32:
+#endif
+#ifdef TCG_TARGET_HAS_rot_i64
+ case INDEX_op_rotl_i64:
+ case INDEX_op_rotr_i64:
+#endif
+ if (temps[args[1]].state == TCG_TEMP_CONST) {
+ /* Proceed with possible constant folding. */
+ break;
+ }
+ if (temps[args[2]].state == TCG_TEMP_CONST
+ && temps[args[2]].val == 0) {
+ if ((temps[args[0]].state == TCG_TEMP_COPY
+ && temps[args[0]].val == args[1])
+ || args[0] == args[1]) {
+ args += 3;
+ gen_opc_buf[op_index] = INDEX_op_nop;
+ } else {
+ gen_opc_buf[op_index] = op_to_mov(op);
+ tcg_opt_gen_mov(gen_args, args[0], args[1],
+ nb_temps, nb_globals);
+ gen_args += 2;
+ args += 3;
+ }
+ continue;
+ }
+ break;
+ CASE_OP_32_64(mul):
+ if ((temps[args[2]].state == TCG_TEMP_CONST
+ && temps[args[2]].val == 0)) {
+ gen_opc_buf[op_index] = op_to_movi(op);
+ tcg_opt_gen_movi(gen_args, args[0], 0, nb_temps, nb_globals);
+ args += 3;
+ gen_args += 2;
+ continue;
+ }
+ break;
+ CASE_OP_32_64(or):
+ CASE_OP_32_64(and):
+ if (args[1] == args[2]) {
+ if (args[1] == args[0]) {
+ args += 3;
+ gen_opc_buf[op_index] = INDEX_op_nop;
+ } else {
+ gen_opc_buf[op_index] = op_to_mov(op);
+ tcg_opt_gen_mov(gen_args, args[0], args[1], nb_temps,
+ nb_globals);
+ gen_args += 2;
+ args += 3;
+ }
+ continue;
+ }
+ break;
+ }
+
+ /* Propagate constants through copy operations and do constant
+ folding. Constants will be substituted to arguments by register
+ allocator where needed and possible. Also detect copies. */
+ switch (op) {
+ CASE_OP_32_64(mov):
+ if ((temps[args[1]].state == TCG_TEMP_COPY
+ && temps[args[1]].val == args[0])
+ || args[0] == args[1]) {
+ args += 2;
+ gen_opc_buf[op_index] = INDEX_op_nop;
+ break;
+ }
+ if (temps[args[1]].state != TCG_TEMP_CONST) {
+ tcg_opt_gen_mov(gen_args, args[0], args[1],
+ nb_temps, nb_globals);
+ gen_args += 2;
+ args += 2;
+ break;
+ }
+ /* Source argument is constant. Rewrite the operation and
+ let movi case handle it. */
+ op = op_to_movi(op);
+ gen_opc_buf[op_index] = op;
+ args[1] = temps[args[1]].val;
+ /* fallthrough */
+ CASE_OP_32_64(movi):
+ tcg_opt_gen_movi(gen_args, args[0], args[1], nb_temps, nb_globals);
+ gen_args += 2;
+ args += 2;
+ break;
+ CASE_OP_32_64(not):
+#ifdef TCG_TARGET_HAS_ext8s_i32
+ case INDEX_op_ext8s_i32:
+#endif
+#ifdef TCG_TARGET_HAS_ext8s_i64
+ case INDEX_op_ext8s_i64:
+#endif
+#ifdef TCG_TARGET_HAS_ext16s_i32
+ case INDEX_op_ext16s_i32:
+#endif
+#ifdef TCG_TARGET_HAS_ext16s_i64
+ case INDEX_op_ext16s_i64:
+#endif
+#ifdef TCG_TARGET_HAS_ext8u_i32
+ case INDEX_op_ext8u_i32:
+#endif
+#ifdef TCG_TARGET_HAS_ext8u_i64
+ case INDEX_op_ext8u_i64:
+#endif
+#ifdef TCG_TARGET_HAS_ext16u_i32
+ case INDEX_op_ext16u_i32:
+#endif
+#ifdef TCG_TARGET_HAS_ext16u_i64
+ case INDEX_op_ext16u_i64:
+#endif
+#if TCG_TARGET_REG_BITS == 64
+ case INDEX_op_ext32s_i64:
+ case INDEX_op_ext32u_i64:
+#endif
+ if (temps[args[1]].state == TCG_TEMP_CONST) {
+ gen_opc_buf[op_index] = op_to_movi(op);
+ tmp = do_constant_folding(op, temps[args[1]].val, 0);
+ tcg_opt_gen_movi(gen_args, args[0], tmp, nb_temps, nb_globals);
+ gen_args += 2;
+ args += 2;
+ break;
+ } else {
+ reset_temp(args[0], nb_temps, nb_globals);
+ gen_args[0] = args[0];
+ gen_args[1] = args[1];
+ gen_args += 2;
+ args += 2;
+ break;
+ }
+ CASE_OP_32_64(add):
+ CASE_OP_32_64(sub):
+ CASE_OP_32_64(mul):
+ CASE_OP_32_64(or):
+ CASE_OP_32_64(and):
+ CASE_OP_32_64(xor):
+ CASE_OP_32_64(shl):
+ CASE_OP_32_64(shr):
+ CASE_OP_32_64(sar):
+#ifdef TCG_TARGET_HAS_rot_i32
+ case INDEX_op_rotl_i32:
+ case INDEX_op_rotr_i32:
+#endif
+#ifdef TCG_TARGET_HAS_rot_i64
+ case INDEX_op_rotl_i64:
+ case INDEX_op_rotr_i64:
+#endif
+ if (temps[args[1]].state == TCG_TEMP_CONST
+ && temps[args[2]].state == TCG_TEMP_CONST) {
+ gen_opc_buf[op_index] = op_to_movi(op);
+ tmp = do_constant_folding(op, temps[args[1]].val,
+ temps[args[2]].val);
+ tcg_opt_gen_movi(gen_args, args[0], tmp, nb_temps, nb_globals);
+ gen_args += 2;
+ args += 3;
+ break;
+ } else {
+ reset_temp(args[0], nb_temps, nb_globals);
+ gen_args[0] = args[0];
+ gen_args[1] = args[1];
+ gen_args[2] = args[2];
+ gen_args += 3;
+ args += 3;
+ break;
+ }
+ case INDEX_op_call:
+ nb_call_args = (args[0] >> 16) + (args[0] & 0xffff);
+ if (!(args[nb_call_args + 1] & (TCG_CALL_CONST | TCG_CALL_PURE))) {
+ for (i = 0; i < nb_globals; i++) {
+ reset_temp(i, nb_temps, nb_globals);
+ }
+ }
+ for (i = 0; i < (args[0] >> 16); i++) {
+ reset_temp(args[i + 1], nb_temps, nb_globals);
+ }
+ i = nb_call_args + 3;
+ while (i) {
+ *gen_args = *args;
+ args++;
+ gen_args++;
+ i--;
+ }
+ break;
+ case INDEX_op_set_label:
+ case INDEX_op_jmp:
+ case INDEX_op_br:
+ CASE_OP_32_64(brcond):
+ memset(temps, 0, nb_temps * sizeof(struct tcg_temp_info));
+ for (i = 0; i < def->nb_args; i++) {
+ *gen_args = *args;
+ args++;
+ gen_args++;
+ }
+ break;
+ default:
+ /* Default case: we do know nothing about operation so no
+ propagation is done. We only trash output args. */
+ for (i = 0; i < def->nb_oargs; i++) {
+ reset_temp(args[i], nb_temps, nb_globals);
+ }
+ for (i = 0; i < def->nb_args; i++) {
+ gen_args[i] = args[i];
+ }
+ args += def->nb_args;
+ gen_args += def->nb_args;
+ break;
+ }
+ }
+
+ return gen_args;
+}
+
+TCGArg *tcg_optimize(TCGContext *s, uint16_t *tcg_opc_ptr,
+ TCGArg *args, TCGOpDef *tcg_op_defs)
+{
+ TCGArg *res;
+ res = tcg_constant_folding(s, tcg_opc_ptr, args, tcg_op_defs);
+ return res;
+}
diff --git a/tcg/tcg.c b/tcg/tcg.c
index c05413b..92f1989 100644
--- a/tcg/tcg.c
+++ b/tcg/tcg.c
@@ -24,6 +24,7 @@
/* define it to use liveness analysis (better code) */
#define USE_LIVENESS_ANALYSIS
+#define USE_TCG_OPTIMIZATIONS
#include "config.h"
@@ -2035,6 +2036,11 @@ static inline int tcg_gen_code_common(TCGContext *s, uint8_t *gen_code_buf,
}
#endif
+#ifdef USE_TCG_OPTIMIZATIONS
+ gen_opparam_ptr =
+ tcg_optimize(s, gen_opc_ptr, gen_opparam_buf, tcg_op_defs);
+#endif
+
#ifdef CONFIG_PROFILER
s->la_time -= profile_getclock();
#endif
diff --git a/tcg/tcg.h b/tcg/tcg.h
index a2dd8b8..e76f9af 100644
--- a/tcg/tcg.h
+++ b/tcg/tcg.h
@@ -502,6 +502,9 @@ void tcg_gen_callN(TCGContext *s, TCGv_ptr func, unsigned int flags,
void tcg_gen_shifti_i64(TCGv_i64 ret, TCGv_i64 arg1,
int c, int right, int arith);
+TCGArg *tcg_optimize(TCGContext *s, uint16_t *tcg_opc_ptr, TCGArg *args,
+ TCGOpDef *tcg_op_def);
+
/* only used for debugging purposes */
void tcg_register_helper(void *func, const char *name);
const char *tcg_helper_get_name(TCGContext *s, void *func);
diff --git a/test-coroutine.c b/test-coroutine.c
new file mode 100644
index 0000000..bf9f3e9
--- /dev/null
+++ b/test-coroutine.c
@@ -0,0 +1,192 @@
+/*
+ * Coroutine tests
+ *
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ * Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#include <glib.h>
+#include "qemu-coroutine.h"
+
+/*
+ * Check that qemu_in_coroutine() works
+ */
+
+static void coroutine_fn verify_in_coroutine(void *opaque)
+{
+ g_assert(qemu_in_coroutine());
+}
+
+static void test_in_coroutine(void)
+{
+ Coroutine *coroutine;
+
+ g_assert(!qemu_in_coroutine());
+
+ coroutine = qemu_coroutine_create(verify_in_coroutine);
+ qemu_coroutine_enter(coroutine, NULL);
+}
+
+/*
+ * Check that qemu_coroutine_self() works
+ */
+
+static void coroutine_fn verify_self(void *opaque)
+{
+ g_assert(qemu_coroutine_self() == opaque);
+}
+
+static void test_self(void)
+{
+ Coroutine *coroutine;
+
+ coroutine = qemu_coroutine_create(verify_self);
+ qemu_coroutine_enter(coroutine, coroutine);
+}
+
+/*
+ * Check that coroutines may nest multiple levels
+ */
+
+typedef struct {
+ unsigned int n_enter; /* num coroutines entered */
+ unsigned int n_return; /* num coroutines returned */
+ unsigned int max; /* maximum level of nesting */
+} NestData;
+
+static void coroutine_fn nest(void *opaque)
+{
+ NestData *nd = opaque;
+
+ nd->n_enter++;
+
+ if (nd->n_enter < nd->max) {
+ Coroutine *child;
+
+ child = qemu_coroutine_create(nest);
+ qemu_coroutine_enter(child, nd);
+ }
+
+ nd->n_return++;
+}
+
+static void test_nesting(void)
+{
+ Coroutine *root;
+ NestData nd = {
+ .n_enter = 0,
+ .n_return = 0,
+ .max = 128,
+ };
+
+ root = qemu_coroutine_create(nest);
+ qemu_coroutine_enter(root, &nd);
+
+ /* Must enter and return from max nesting level */
+ g_assert_cmpint(nd.n_enter, ==, nd.max);
+ g_assert_cmpint(nd.n_return, ==, nd.max);
+}
+
+/*
+ * Check that yield/enter transfer control correctly
+ */
+
+static void coroutine_fn yield_5_times(void *opaque)
+{
+ bool *done = opaque;
+ int i;
+
+ for (i = 0; i < 5; i++) {
+ qemu_coroutine_yield();
+ }
+ *done = true;
+}
+
+static void test_yield(void)
+{
+ Coroutine *coroutine;
+ bool done = false;
+ int i = -1; /* one extra time to return from coroutine */
+
+ coroutine = qemu_coroutine_create(yield_5_times);
+ while (!done) {
+ qemu_coroutine_enter(coroutine, &done);
+ i++;
+ }
+ g_assert_cmpint(i, ==, 5); /* coroutine must yield 5 times */
+}
+
+/*
+ * Check that creation, enter, and return work
+ */
+
+static void coroutine_fn set_and_exit(void *opaque)
+{
+ bool *done = opaque;
+
+ *done = true;
+}
+
+static void test_lifecycle(void)
+{
+ Coroutine *coroutine;
+ bool done = false;
+
+ /* Create, enter, and return from coroutine */
+ coroutine = qemu_coroutine_create(set_and_exit);
+ qemu_coroutine_enter(coroutine, &done);
+ g_assert(done); /* expect done to be true (first time) */
+
+ /* Repeat to check that no state affects this test */
+ done = false;
+ coroutine = qemu_coroutine_create(set_and_exit);
+ qemu_coroutine_enter(coroutine, &done);
+ g_assert(done); /* expect done to be true (second time) */
+}
+
+/*
+ * Lifecycle benchmark
+ */
+
+static void coroutine_fn empty_coroutine(void *opaque)
+{
+ /* Do nothing */
+}
+
+static void perf_lifecycle(void)
+{
+ Coroutine *coroutine;
+ unsigned int i, max;
+ double duration;
+
+ max = 1000000;
+
+ g_test_timer_start();
+ for (i = 0; i < max; i++) {
+ coroutine = qemu_coroutine_create(empty_coroutine);
+ qemu_coroutine_enter(coroutine, NULL);
+ }
+ duration = g_test_timer_elapsed();
+
+ g_test_message("Lifecycle %u iterations: %f s\n", max, duration);
+}
+
+int main(int argc, char **argv)
+{
+ g_test_init(&argc, &argv, NULL);
+ g_test_add_func("/basic/lifecycle", test_lifecycle);
+ g_test_add_func("/basic/yield", test_yield);
+ g_test_add_func("/basic/nesting", test_nesting);
+ g_test_add_func("/basic/self", test_self);
+ g_test_add_func("/basic/in_coroutine", test_in_coroutine);
+ if (g_test_perf()) {
+ g_test_add_func("/perf/lifecycle", perf_lifecycle);
+ }
+ return g_test_run();
+}
diff --git a/test-qmp-commands.c b/test-qmp-commands.c
new file mode 100644
index 0000000..7752904
--- /dev/null
+++ b/test-qmp-commands.c
@@ -0,0 +1,113 @@
+#include <glib.h>
+#include "qemu-objects.h"
+#include "test-qmp-commands.h"
+#include "qapi/qmp-core.h"
+#include "module.h"
+
+void qmp_user_def_cmd(Error **errp)
+{
+}
+
+void qmp_user_def_cmd1(UserDefOne * ud1, Error **errp)
+{
+}
+
+UserDefTwo * qmp_user_def_cmd2(UserDefOne * ud1a, UserDefOne * ud1b, Error **errp)
+{
+ UserDefTwo *ret;
+ UserDefOne *ud1c = qemu_mallocz(sizeof(UserDefOne));
+ UserDefOne *ud1d = qemu_mallocz(sizeof(UserDefOne));
+
+ ud1c->string = strdup(ud1a->string);
+ ud1c->integer = ud1a->integer;
+ ud1d->string = strdup(ud1b->string);
+ ud1d->integer = ud1b->integer;
+
+ ret = qemu_mallocz(sizeof(UserDefTwo));
+ ret->string = strdup("blah1");
+ ret->dict.string = strdup("blah2");
+ ret->dict.dict.userdef = ud1c;
+ ret->dict.dict.string = strdup("blah3");
+ ret->dict.has_dict2 = true;
+ ret->dict.dict2.userdef = ud1d;
+ ret->dict.dict2.string = strdup("blah4");
+
+ return ret;
+}
+
+/* test commands with no input and no return value */
+static void test_dispatch_cmd(void)
+{
+ QDict *req = qdict_new();
+ QObject *resp;
+
+ qdict_put_obj(req, "execute", QOBJECT(qstring_from_str("user_def_cmd")));
+
+ resp = qmp_dispatch(QOBJECT(req));
+ assert(resp != NULL);
+ assert(!qdict_haskey(qobject_to_qdict(resp), "error"));
+ g_print("\nresp: %s\n", qstring_get_str(qobject_to_json(resp)));
+
+ qobject_decref(resp);
+ QDECREF(req);
+}
+
+/* test commands that return an error due to invalid parameters */
+static void test_dispatch_cmd_error(void)
+{
+ QDict *req = qdict_new();
+ QObject *resp;
+
+ qdict_put_obj(req, "execute", QOBJECT(qstring_from_str("user_def_cmd2")));
+
+ resp = qmp_dispatch(QOBJECT(req));
+ assert(resp != NULL);
+ assert(qdict_haskey(qobject_to_qdict(resp), "error"));
+ g_print("\nresp: %s\n", qstring_get_str(qobject_to_json_pretty(resp)));
+
+ qobject_decref(resp);
+ QDECREF(req);
+}
+
+/* test commands that involve both input parameters and return values */
+static void test_dispatch_cmd_io(void)
+{
+ QDict *req = qdict_new();
+ QDict *args = qdict_new();
+ QDict *ud1a = qdict_new();
+ QDict *ud1b = qdict_new();
+ QObject *resp;
+
+ qdict_put_obj(ud1a, "integer", QOBJECT(qint_from_int(42)));
+ qdict_put_obj(ud1a, "string", QOBJECT(qstring_from_str("hello")));
+ qdict_put_obj(ud1b, "integer", QOBJECT(qint_from_int(422)));
+ qdict_put_obj(ud1b, "string", QOBJECT(qstring_from_str("hello2")));
+ qdict_put_obj(args, "ud1a", QOBJECT(ud1a));
+ qdict_put_obj(args, "ud1b", QOBJECT(ud1b));
+ qdict_put_obj(req, "arguments", QOBJECT(args));
+
+ qdict_put_obj(req, "execute", QOBJECT(qstring_from_str("user_def_cmd2")));
+
+ /* TODO: put in full payload and check for errors */
+ resp = qmp_dispatch(QOBJECT(req));
+ assert(resp != NULL);
+ assert(!qdict_haskey(qobject_to_qdict(resp), "error"));
+ g_print("\nresp: %s\n", qstring_get_str(qobject_to_json_pretty(resp)));
+
+ qobject_decref(resp);
+ QDECREF(req);
+}
+
+int main(int argc, char **argv)
+{
+ g_test_init(&argc, &argv, NULL);
+
+ g_test_add_func("/0.15/dispatch_cmd", test_dispatch_cmd);
+ g_test_add_func("/0.15/dispatch_cmd_error", test_dispatch_cmd_error);
+ g_test_add_func("/0.15/dispatch_cmd_io", test_dispatch_cmd_io);
+
+ module_call_init(MODULE_INIT_QAPI);
+ g_test_run();
+
+ return 0;
+}
diff --git a/test-visitor.c b/test-visitor.c
new file mode 100644
index 0000000..5133ad6
--- /dev/null
+++ b/test-visitor.c
@@ -0,0 +1,306 @@
+#include <glib.h>
+#include "qapi/qmp-output-visitor.h"
+#include "qapi/qmp-input-visitor.h"
+#include "test-qapi-types.h"
+#include "test-qapi-visit.h"
+#include "qemu-objects.h"
+
+typedef struct TestStruct
+{
+ int64_t x;
+ int64_t y;
+} TestStruct;
+
+typedef struct TestStructList
+{
+ TestStruct *value;
+ struct TestStructList *next;
+} TestStructList;
+
+static void visit_type_TestStruct(Visitor *v, TestStruct **obj, const char *name, Error **errp)
+{
+ visit_start_struct(v, (void **)obj, "TestStruct", name, sizeof(TestStruct), errp);
+ visit_type_int(v, &(*obj)->x, "x", errp);
+ visit_type_int(v, &(*obj)->y, "y", errp);
+ visit_end_struct(v, errp);
+}
+
+static void visit_type_TestStructList(Visitor *m, TestStructList ** obj, const char *name, Error **errp)
+{
+ GenericList *i;
+
+ visit_start_list(m, name, errp);
+
+ for (i = visit_next_list(m, (GenericList **)obj, errp); i; i = visit_next_list(m, &i, errp)) {
+ TestStructList *native_i = (TestStructList *)i;
+ visit_type_TestStruct(m, &native_i->value, NULL, errp);
+ }
+
+ visit_end_list(m, errp);
+}
+
+/* test core visitor methods */
+static void test_visitor_core(void)
+{
+ QmpOutputVisitor *mo;
+ QmpInputVisitor *mi;
+ Visitor *v;
+ TestStruct ts = { 42, 82 };
+ TestStruct *pts = &ts;
+ TestStructList *lts = NULL;
+ Error *err = NULL;
+ QObject *obj;
+ QString *str;
+ int64_t value = 0;
+
+ mo = qmp_output_visitor_new();
+ v = qmp_output_get_visitor(mo);
+
+ visit_type_TestStruct(v, &pts, NULL, &err);
+
+ obj = qmp_output_get_qobject(mo);
+
+ str = qobject_to_json(obj);
+
+ printf("%s\n", qstring_get_str(str));
+
+ QDECREF(str);
+
+ obj = QOBJECT(qint_from_int(0x42));
+
+ mi = qmp_input_visitor_new(obj);
+ v = qmp_input_get_visitor(mi);
+
+ visit_type_int(v, &value, NULL, &err);
+ if (err) {
+ g_error("%s", error_get_pretty(err));
+ }
+
+ g_assert(value == 0x42);
+
+ qobject_decref(obj);
+
+ obj = qobject_from_json("{'x': 42, 'y': 84}");
+ mi = qmp_input_visitor_new(obj);
+ v = qmp_input_get_visitor(mi);
+
+ pts = NULL;
+
+ visit_type_TestStruct(v, &pts, NULL, &err);
+ if (err) {
+ g_error("%s", error_get_pretty(err));
+ }
+
+ g_assert(pts != NULL);
+ g_assert(pts->x == 42);
+ g_assert(pts->y == 84);
+
+ qobject_decref(obj);
+
+ obj = qobject_from_json("[{'x': 42, 'y': 84}, {'x': 12, 'y': 24}]");
+ mi = qmp_input_visitor_new(obj);
+ v = qmp_input_get_visitor(mi);
+
+ visit_type_TestStructList(v, &lts, NULL, &err);
+ if (err) {
+ g_error("%s", error_get_pretty(err));
+ }
+
+ g_assert(lts != NULL);
+ g_assert(lts->value->x == 42);
+ g_assert(lts->value->y == 84);
+
+ lts = lts->next;
+ g_assert(lts != NULL);
+ g_assert(lts->value->x == 12);
+ g_assert(lts->value->y == 24);
+
+ g_assert(lts->next == NULL);
+
+ qobject_decref(obj);
+}
+
+/* test deep nesting with refs to other user-defined types */
+static void test_nested_structs(void)
+{
+ QmpOutputVisitor *mo;
+ QmpInputVisitor *mi;
+ Visitor *v;
+ UserDefOne ud1;
+ UserDefOne *ud1_p = &ud1, *ud1c_p = NULL;
+ UserDefTwo ud2;
+ UserDefTwo *ud2_p = &ud2, *ud2c_p = NULL;
+ Error *err = NULL;
+ QObject *obj;
+ QString *str;
+
+ ud1.integer = 42;
+ ud1.string = strdup("fourty two");
+
+ /* sanity check */
+ mo = qmp_output_visitor_new();
+ v = qmp_output_get_visitor(mo);
+ visit_type_UserDefOne(v, &ud1_p, "o_O", &err);
+ if (err) {
+ g_error("%s", error_get_pretty(err));
+ }
+ obj = qmp_output_get_qobject(mo);
+ g_assert(obj);
+ qobject_decref(obj);
+
+ ud2.string = strdup("fourty three");
+ ud2.dict.string = strdup("fourty four");
+ ud2.dict.dict.userdef = ud1_p;
+ ud2.dict.dict.string = strdup("fourty five");
+ ud2.dict.has_dict2 = true;
+ ud2.dict.dict2.userdef = ud1_p;
+ ud2.dict.dict2.string = strdup("fourty six");
+
+ /* c type -> qobject */
+ mo = qmp_output_visitor_new();
+ v = qmp_output_get_visitor(mo);
+ visit_type_UserDefTwo(v, &ud2_p, "unused", &err);
+ if (err) {
+ g_error("%s", error_get_pretty(err));
+ }
+ obj = qmp_output_get_qobject(mo);
+ g_assert(obj);
+ str = qobject_to_json_pretty(obj);
+ g_print("%s\n", qstring_get_str(str));
+ QDECREF(str);
+
+ /* qobject -> c type, should match original struct */
+ mi = qmp_input_visitor_new(obj);
+ v = qmp_input_get_visitor(mi);
+ visit_type_UserDefTwo(v, &ud2c_p, NULL, &err);
+ if (err) {
+ g_error("%s", error_get_pretty(err));
+ }
+
+ g_assert(!g_strcmp0(ud2c_p->string, ud2.string));
+ g_assert(!g_strcmp0(ud2c_p->dict.string, ud2.dict.string));
+
+ ud1c_p = ud2c_p->dict.dict.userdef;
+ g_assert(ud1c_p->integer == ud1_p->integer);
+ g_assert(!g_strcmp0(ud1c_p->string, ud1_p->string));
+
+ g_assert(!g_strcmp0(ud2c_p->dict.dict.string, ud2.dict.dict.string));
+
+ ud1c_p = ud2c_p->dict.dict2.userdef;
+ g_assert(ud1c_p->integer == ud1_p->integer);
+ g_assert(!g_strcmp0(ud1c_p->string, ud1_p->string));
+
+ g_assert(!g_strcmp0(ud2c_p->dict.dict2.string, ud2.dict.dict2.string));
+ qemu_free(ud1.string);
+ qemu_free(ud2.string);
+ qemu_free(ud2.dict.string);
+ qemu_free(ud2.dict.dict.string);
+ qemu_free(ud2.dict.dict2.string);
+
+ qapi_free_UserDefTwo(ud2c_p);
+
+ qobject_decref(obj);
+}
+
+/* test enum values */
+static void test_enums(void)
+{
+ QmpOutputVisitor *mo;
+ QmpInputVisitor *mi;
+ Visitor *v;
+ EnumOne enum1 = ENUM_ONE_VALUE2, enum1_cpy = ENUM_ONE_VALUE1;
+ Error *err = NULL;
+ QObject *obj;
+ QString *str;
+
+ /* C type -> QObject */
+ mo = qmp_output_visitor_new();
+ v = qmp_output_get_visitor(mo);
+ visit_type_EnumOne(v, &enum1, "unused", &err);
+ if (err) {
+ g_error("%s", error_get_pretty(err));
+ }
+ obj = qmp_output_get_qobject(mo);
+ g_assert(obj);
+ str = qobject_to_json_pretty(obj);
+ g_print("%s\n", qstring_get_str(str));
+ QDECREF(str);
+ g_assert(g_strcmp0(qstring_get_str(qobject_to_qstring(obj)), "value2") == 0);
+
+ /* QObject -> C type */
+ mi = qmp_input_visitor_new(obj);
+ v = qmp_input_get_visitor(mi);
+ visit_type_EnumOne(v, &enum1_cpy, "unused", &err);
+ if (err) {
+ g_error("%s", error_get_pretty(err));
+ }
+ g_debug("enum1_cpy, enum1: %d, %d", enum1_cpy, enum1);
+ g_assert(enum1_cpy == enum1);
+
+ qobject_decref(obj);
+}
+
+/* test enum values nested in schema-defined structs */
+static void test_nested_enums(void)
+{
+ QmpOutputVisitor *mo;
+ QmpInputVisitor *mi;
+ Visitor *v;
+ NestedEnumsOne *nested_enums, *nested_enums_cpy = NULL;
+ Error *err = NULL;
+ QObject *obj;
+ QString *str;
+
+ nested_enums = qemu_mallocz(sizeof(NestedEnumsOne));
+ nested_enums->enum1 = ENUM_ONE_VALUE1;
+ nested_enums->enum2 = ENUM_ONE_VALUE2;
+ nested_enums->enum3 = ENUM_ONE_VALUE3;
+ nested_enums->enum4 = ENUM_ONE_VALUE3;
+ nested_enums->has_enum2 = false;
+ nested_enums->has_enum4 = true;
+
+ /* C type -> QObject */
+ mo = qmp_output_visitor_new();
+ v = qmp_output_get_visitor(mo);
+ visit_type_NestedEnumsOne(v, &nested_enums, NULL, &err);
+ if (err) {
+ g_error("%s", error_get_pretty(err));
+ }
+ obj = qmp_output_get_qobject(mo);
+ g_assert(obj);
+ str = qobject_to_json_pretty(obj);
+ g_print("%s\n", qstring_get_str(str));
+ QDECREF(str);
+
+ /* QObject -> C type */
+ mi = qmp_input_visitor_new(obj);
+ v = qmp_input_get_visitor(mi);
+ visit_type_NestedEnumsOne(v, &nested_enums_cpy, NULL, &err);
+ if (err) {
+ g_error("%s", error_get_pretty(err));
+ }
+ g_assert(nested_enums_cpy);
+ g_assert(nested_enums_cpy->enum1 == nested_enums->enum1);
+ g_assert(nested_enums_cpy->enum3 == nested_enums->enum3);
+ g_assert(nested_enums_cpy->enum4 == nested_enums->enum4);
+ g_assert(nested_enums_cpy->has_enum2 == false);
+ g_assert(nested_enums_cpy->has_enum4 == true);
+
+ qobject_decref(obj);
+ qapi_free_NestedEnumsOne(nested_enums);
+ qapi_free_NestedEnumsOne(nested_enums_cpy);
+}
+
+int main(int argc, char **argv)
+{
+ g_test_init(&argc, &argv, NULL);
+
+ g_test_add_func("/0.15/visitor_core", test_visitor_core);
+ g_test_add_func("/0.15/nested_structs", test_nested_structs);
+ g_test_add_func("/0.15/enums", test_enums);
+ g_test_add_func("/0.15/nested_enums", test_nested_enums);
+
+ g_test_run();
+
+ return 0;
+}
diff --git a/trace-events b/trace-events
index bebf612f..19d31e3 100644
--- a/trace-events
+++ b/trace-events
@@ -46,6 +46,17 @@ 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"
+# hw/virtio-serial-bus.c
+disable virtio_serial_send_control_event(unsigned int port, uint16_t event, uint16_t value) "port %u, event %u, value %u"
+disable virtio_serial_throttle_port(unsigned int port, bool throttle) "port %u, throttle %d"
+disable virtio_serial_handle_control_message(uint16_t event, uint16_t value) "event %u, value %u"
+disable virtio_serial_handle_control_message_port(unsigned int port) "port %u"
+
+# hw/virtio-console.c
+disable virtio_console_flush_buf(unsigned int port, size_t len, ssize_t ret) "port %u, in_len %zu, out_len %zd"
+disable virtio_console_chr_read(unsigned int port, int size) "port %u, size %d"
+disable virtio_console_chr_event(unsigned int port, int event) "port %u, event %d"
+
# 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"
@@ -55,6 +66,9 @@ disable bdrv_aio_flush(void *bs, void *opaque) "bs %p opaque %p"
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"
disable bdrv_set_locked(void *bs, int locked) "bs %p locked %d"
+disable bdrv_co_readv(void *bs, int64_t sector_num, int nb_sector) "bs %p sector_num %"PRId64" nb_sectors %d"
+disable bdrv_co_writev(void *bs, int64_t sector_num, int nb_sector) "bs %p sector_num %"PRId64" nb_sectors %d"
+disable bdrv_co_io(int is_write, void *acb) "is_write %d acb %p"
# hw/virtio-blk.c
disable virtio_blk_req_complete(void *req, int status) "req %p status %d"
@@ -92,6 +106,10 @@ disable cs4231_mem_readl_reg(uint32_t reg, uint32_t ret) "read reg %d: 0x%08x"
disable cs4231_mem_writel_reg(uint32_t reg, uint32_t old, uint32_t val) "write reg %d: 0x%08x -> 0x%08x"
disable cs4231_mem_writel_dreg(uint32_t reg, uint32_t old, uint32_t val) "write dreg %d: 0x%02x -> 0x%02x"
+# hw/ds1225y.c
+disable nvram_read(uint32_t addr, uint32_t ret) "read addr %d: 0x%02x"
+disable nvram_write(uint32_t addr, uint32_t old, uint32_t val) "write addr %d: 0x%02x -> 0x%02x"
+
# hw/eccmemctl.c
disable ecc_mem_writel_mer(uint32_t val) "Write memory enable %08x"
disable ecc_mem_writel_mdr(uint32_t val) "Write memory delay %08x"
@@ -201,13 +219,13 @@ disable usb_ehci_mmio_writel(uint32_t addr, const char *str, uint32_t val) "wr m
disable usb_ehci_mmio_change(uint32_t addr, const char *str, uint32_t new, uint32_t old) "ch mmio %04x [%s] = %x (old: %x)"
disable usb_ehci_usbsts(const char *sts, int state) "usbsts %s %d"
disable usb_ehci_state(const char *schedule, const char *state) "%s schedule %s"
-disable usb_ehci_qh_ptrs(void *q, uint32_t addr, uint32_t next, uint32_t c_qtd, uint32_t n_qtd, uint32_t a_qtd) "q %p - QH @ %08x: next %08x qtds %08x,%08x,%08x"
+disable usb_ehci_qh_ptrs(void *q, uint32_t addr, uint32_t nxt, uint32_t c_qtd, uint32_t n_qtd, uint32_t a_qtd) "q %p - QH @ %08x: next %08x qtds %08x,%08x,%08x"
disable usb_ehci_qh_fields(uint32_t addr, int rl, int mplen, int eps, int ep, int devaddr) "QH @ %08x - rl %d, mplen %d, eps %d, ep %d, dev %d"
disable usb_ehci_qh_bits(uint32_t addr, int c, int h, int dtc, int i) "QH @ %08x - c %d, h %d, dtc %d, i %d"
-disable usb_ehci_qtd_ptrs(void *q, uint32_t addr, uint32_t next, uint32_t altnext) "q %p - QTD @ %08x: next %08x altnext %08x"
+disable usb_ehci_qtd_ptrs(void *q, uint32_t addr, uint32_t nxt, uint32_t altnext) "q %p - QTD @ %08x: next %08x altnext %08x"
disable usb_ehci_qtd_fields(uint32_t addr, int tbytes, int cpage, int cerr, int pid) "QTD @ %08x - tbytes %d, cpage %d, cerr %d, pid %d"
disable usb_ehci_qtd_bits(uint32_t addr, int ioc, int active, int halt, int babble, int xacterr) "QTD @ %08x - ioc %d, active %d, halt %d, babble %d, xacterr %d"
-disable usb_ehci_itd(uint32_t addr, uint32_t next, uint32_t mplen, uint32_t mult, uint32_t ep, uint32_t devaddr) "ITD @ %08x: next %08x - mplen %d, mult %d, ep %d, dev %d"
+disable usb_ehci_itd(uint32_t addr, uint32_t nxt, uint32_t mplen, uint32_t mult, uint32_t ep, uint32_t devaddr) "ITD @ %08x: next %08x - mplen %d, mult %d, ep %d, dev %d"
disable usb_ehci_port_attach(uint32_t port, const char *device) "attach port #%d - %s"
disable usb_ehci_port_detach(uint32_t port) "detach port #%d"
disable usb_ehci_port_reset(uint32_t port, int enable) "reset port #%d - %d"
@@ -399,9 +417,9 @@ disable xen_ram_alloc(unsigned long ram_addr, unsigned long size) "requested: %#
disable xen_client_set_memory(uint64_t start_addr, unsigned long size, unsigned long phys_offset, bool log_dirty) "%#"PRIx64" size %#lx, offset %#lx, log_dirty %i"
# xen-mapcache.c
-disable qemu_map_cache(uint64_t phys_addr) "want %#"PRIx64""
-disable qemu_remap_bucket(uint64_t index) "index %#"PRIx64""
-disable qemu_map_cache_return(void* ptr) "%p"
+disable xen_map_cache(uint64_t phys_addr) "want %#"PRIx64""
+disable xen_remap_bucket(uint64_t index) "index %#"PRIx64""
+disable xen_map_cache_return(void* ptr) "%p"
disable xen_map_block(uint64_t phys_addr, uint64_t size) "%#"PRIx64", size %#"PRIx64""
disable xen_unmap_block(void* addr, unsigned long size) "%p, size %#lx"
@@ -410,3 +428,16 @@ disable qemu_put_ram_ptr(void* addr) "%p"
# hw/xen_platform.c
disable xen_platform_log(char *s) "xen platform: %s"
+
+# qemu-coroutine.c
+disable qemu_coroutine_enter(void *from, void *to, void *opaque) "from %p to %p opaque %p"
+disable qemu_coroutine_yield(void *from, void *to) "from %p to %p"
+disable qemu_coroutine_terminate(void *co) "self %p"
+
+# qemu-coroutine-lock.c
+disable qemu_co_queue_next_bh(void) ""
+disable qemu_co_queue_next(void *next) "next %p"
+disable qemu_co_mutex_lock_entry(void *mutex, void *self) "mutex %p self %p"
+disable qemu_co_mutex_lock_return(void *mutex, void *self) "mutex %p self %p"
+disable qemu_co_mutex_unlock_entry(void *mutex, void *self) "mutex %p self %p"
+disable qemu_co_mutex_unlock_return(void *mutex, void *self) "mutex %p self %p"
diff --git a/ui/qemu-spice.h b/ui/qemu-spice.h
index 3c6f1fe..f34be69 100644
--- a/ui/qemu-spice.h
+++ b/ui/qemu-spice.h
@@ -42,7 +42,7 @@ int qemu_spice_migrate_info(const char *hostname, int port, int tls_port,
void do_info_spice_print(Monitor *mon, const QObject *data);
void do_info_spice(Monitor *mon, QObject **ret_data);
-CharDriverState *qemu_chr_open_spice(QemuOpts *opts);
+int qemu_chr_open_spice(QemuOpts *opts, CharDriverState **_chr);
#else /* CONFIG_SPICE */
diff --git a/ui/sdl.c b/ui/sdl.c
index f2bd4a0..6dbc5cb 100644
--- a/ui/sdl.c
+++ b/ui/sdl.c
@@ -481,7 +481,7 @@ static void sdl_grab_end(void)
sdl_update_caption();
}
-static void sdl_mouse_mode_change(Notifier *notify)
+static void sdl_mouse_mode_change(Notifier *notify, void *data)
{
if (kbd_mouse_is_absolute()) {
if (!absolute_enabled) {
diff --git a/ui/spice-core.c b/ui/spice-core.c
index dd9905b..3d77c01 100644
--- a/ui/spice-core.c
+++ b/ui/spice-core.c
@@ -416,7 +416,7 @@ void do_info_spice(Monitor *mon, QObject **ret_data)
*ret_data = QOBJECT(server);
}
-static void migration_state_notifier(Notifier *notifier)
+static void migration_state_notifier(Notifier *notifier, void *data)
{
int state = get_migration_state();
@@ -480,7 +480,16 @@ void qemu_spice_init(void)
port = qemu_opt_get_number(opts, "port", 0);
tls_port = qemu_opt_get_number(opts, "tls-port", 0);
if (!port && !tls_port) {
- return;
+ fprintf(stderr, "neither port nor tls-port specified for spice.");
+ exit(1);
+ }
+ if (port < 0 || port > 65535) {
+ fprintf(stderr, "spice port is out of range");
+ exit(1);
+ }
+ if (tls_port < 0 || tls_port > 65535) {
+ fprintf(stderr, "spice tls-port is out of range");
+ exit(1);
}
password = qemu_opt_get(opts, "password");
@@ -602,7 +611,10 @@ void qemu_spice_init(void)
qemu_opt_foreach(opts, add_channel, NULL, 0);
- spice_server_init(spice_server, &core_interface);
+ if (0 != spice_server_init(spice_server, &core_interface)) {
+ fprintf(stderr, "failed to initialize spice server");
+ exit(1);
+ };
using_spice = 1;
migration_state.notify = migration_state_notifier;
diff --git a/ui/spice-display.c b/ui/spice-display.c
index 15f0704..683d454 100644
--- a/ui/spice-display.c
+++ b/ui/spice-display.c
@@ -62,6 +62,70 @@ void qemu_spice_rect_union(QXLRect *dest, const QXLRect *r)
dest->right = MAX(dest->right, r->right);
}
+void qemu_spice_add_memslot(SimpleSpiceDisplay *ssd, QXLDevMemSlot *memslot,
+ qxl_async_io async)
+{
+ if (async != QXL_SYNC) {
+#if SPICE_INTERFACE_QXL_MINOR >= 1
+ spice_qxl_add_memslot_async(&ssd->qxl, memslot, 0);
+#else
+ abort();
+#endif
+ } else {
+ ssd->worker->add_memslot(ssd->worker, memslot);
+ }
+}
+
+void qemu_spice_del_memslot(SimpleSpiceDisplay *ssd, uint32_t gid, uint32_t sid)
+{
+ ssd->worker->del_memslot(ssd->worker, gid, sid);
+}
+
+void qemu_spice_create_primary_surface(SimpleSpiceDisplay *ssd, uint32_t id,
+ QXLDevSurfaceCreate *surface,
+ qxl_async_io async)
+{
+ if (async != QXL_SYNC) {
+#if SPICE_INTERFACE_QXL_MINOR >= 1
+ spice_qxl_create_primary_surface_async(&ssd->qxl, id, surface, 0);
+#else
+ abort();
+#endif
+ } else {
+ ssd->worker->create_primary_surface(ssd->worker, id, surface);
+ }
+}
+
+
+void qemu_spice_destroy_primary_surface(SimpleSpiceDisplay *ssd,
+ uint32_t id, qxl_async_io async)
+{
+ if (async != QXL_SYNC) {
+#if SPICE_INTERFACE_QXL_MINOR >= 1
+ spice_qxl_destroy_primary_surface_async(&ssd->qxl, id, 0);
+#else
+ abort();
+#endif
+ } else {
+ ssd->worker->destroy_primary_surface(ssd->worker, id);
+ }
+}
+
+void qemu_spice_wakeup(SimpleSpiceDisplay *ssd)
+{
+ ssd->worker->wakeup(ssd->worker);
+}
+
+void qemu_spice_start(SimpleSpiceDisplay *ssd)
+{
+ ssd->worker->start(ssd->worker);
+}
+
+void qemu_spice_stop(SimpleSpiceDisplay *ssd)
+{
+ ssd->worker->stop(ssd->worker);
+}
+
static SimpleSpiceUpdate *qemu_spice_create_update(SimpleSpiceDisplay *ssd)
{
SimpleSpiceUpdate *update;
@@ -70,6 +134,7 @@ static SimpleSpiceUpdate *qemu_spice_create_update(SimpleSpiceDisplay *ssd)
QXLCommand *cmd;
uint8_t *src, *dst;
int by, bw, bh;
+ struct timespec time_space;
if (qemu_spice_rect_is_empty(&ssd->dirty)) {
return NULL;
@@ -96,6 +161,10 @@ static SimpleSpiceUpdate *qemu_spice_create_update(SimpleSpiceDisplay *ssd)
drawable->surfaces_dest[0] = -1;
drawable->surfaces_dest[1] = -1;
drawable->surfaces_dest[2] = -1;
+ clock_gettime(CLOCK_MONOTONIC, &time_space);
+ /* time in milliseconds from epoch. */
+ drawable->mm_time = time_space.tv_sec * 1000
+ + time_space.tv_nsec / 1000 / 1000;
drawable->u.copy.rop_descriptor = SPICE_ROPD_OP_PUT;
drawable->u.copy.src_bitmap = (intptr_t)image;
@@ -156,7 +225,7 @@ void qemu_spice_create_host_memslot(SimpleSpiceDisplay *ssd)
memset(&memslot, 0, sizeof(memslot));
memslot.slot_group_id = MEMSLOT_GROUP_HOST;
memslot.virt_end = ~0;
- ssd->worker->add_memslot(ssd->worker, &memslot);
+ qemu_spice_add_memslot(ssd, &memslot, QXL_SYNC);
}
void qemu_spice_create_host_primary(SimpleSpiceDisplay *ssd)
@@ -176,14 +245,14 @@ void qemu_spice_create_host_primary(SimpleSpiceDisplay *ssd)
surface.mem = (intptr_t)ssd->buf;
surface.group_id = MEMSLOT_GROUP_HOST;
- ssd->worker->create_primary_surface(ssd->worker, 0, &surface);
+ qemu_spice_create_primary_surface(ssd, 0, &surface, QXL_SYNC);
}
void qemu_spice_destroy_host_primary(SimpleSpiceDisplay *ssd)
{
dprint(1, "%s:\n", __FUNCTION__);
- ssd->worker->destroy_primary_surface(ssd->worker, 0);
+ qemu_spice_destroy_primary_surface(ssd, 0, QXL_SYNC);
}
void qemu_spice_vm_change_state_handler(void *opaque, int running, int reason)
@@ -191,13 +260,23 @@ void qemu_spice_vm_change_state_handler(void *opaque, int running, int reason)
SimpleSpiceDisplay *ssd = opaque;
if (running) {
- ssd->worker->start(ssd->worker);
+ qemu_spice_start(ssd);
} else {
- ssd->worker->stop(ssd->worker);
+ qemu_spice_stop(ssd);
}
ssd->running = running;
}
+void qemu_spice_display_init_common(SimpleSpiceDisplay *ssd, DisplayState *ds)
+{
+ ssd->ds = ds;
+ qemu_mutex_init(&ssd->lock);
+ ssd->mouse_x = -1;
+ ssd->mouse_y = -1;
+ ssd->bufsize = (16 * 1024 * 1024);
+ ssd->buf = qemu_malloc(ssd->bufsize);
+}
+
/* display listener callbacks */
void qemu_spice_display_update(SimpleSpiceDisplay *ssd,
@@ -262,7 +341,7 @@ void qemu_spice_display_refresh(SimpleSpiceDisplay *ssd)
if (ssd->notify) {
ssd->notify = 0;
- ssd->worker->wakeup(ssd->worker);
+ qemu_spice_wakeup(ssd);
dprint(2, "%s: notify\n", __FUNCTION__);
}
}
@@ -411,12 +490,7 @@ static DisplayChangeListener display_listener = {
void qemu_spice_display_init(DisplayState *ds)
{
assert(sdpy.ds == NULL);
- sdpy.ds = ds;
- qemu_mutex_init(&sdpy.lock);
- sdpy.mouse_x = -1;
- sdpy.mouse_y = -1;
- sdpy.bufsize = (16 * 1024 * 1024);
- sdpy.buf = qemu_malloc(sdpy.bufsize);
+ qemu_spice_display_init_common(&sdpy, ds);
register_displaychangelistener(ds, &display_listener);
sdpy.qxl.base.sif = &dpy_interface.base;
diff --git a/ui/spice-display.h b/ui/spice-display.h
index 2f95f68..1388641 100644
--- a/ui/spice-display.h
+++ b/ui/spice-display.h
@@ -33,6 +33,20 @@
#define NUM_SURFACES 1024
+/*
+ * Internal enum to differenciate between options for
+ * io calls that have a sync (old) version and an _async (new)
+ * version:
+ * QXL_SYNC: use the old version
+ * QXL_ASYNC: use the new version and make sure there are no two
+ * happening at the same time. This is used for guest initiated
+ * calls
+ */
+typedef enum qxl_async_io {
+ QXL_SYNC,
+ QXL_ASYNC,
+} qxl_async_io;
+
typedef struct SimpleSpiceDisplay SimpleSpiceDisplay;
typedef struct SimpleSpiceUpdate SimpleSpiceUpdate;
@@ -75,8 +89,22 @@ 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_init_common(SimpleSpiceDisplay *ssd, DisplayState *ds);
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);
+
+void qemu_spice_add_memslot(SimpleSpiceDisplay *ssd, QXLDevMemSlot *memslot,
+ qxl_async_io async);
+void qemu_spice_del_memslot(SimpleSpiceDisplay *ssd, uint32_t gid,
+ uint32_t sid);
+void qemu_spice_create_primary_surface(SimpleSpiceDisplay *ssd, uint32_t id,
+ QXLDevSurfaceCreate *surface,
+ qxl_async_io async);
+void qemu_spice_destroy_primary_surface(SimpleSpiceDisplay *ssd,
+ uint32_t id, qxl_async_io async);
+void qemu_spice_wakeup(SimpleSpiceDisplay *ssd);
+void qemu_spice_start(SimpleSpiceDisplay *ssd);
+void qemu_spice_stop(SimpleSpiceDisplay *ssd);
diff --git a/ui/spice-input.c b/ui/spice-input.c
index 37c8578..75abf5f 100644
--- a/ui/spice-input.c
+++ b/ui/spice-input.c
@@ -178,7 +178,7 @@ static const SpiceTabletInterface tablet_interface = {
.buttons = tablet_buttons,
};
-static void mouse_mode_notifier(Notifier *notifier)
+static void mouse_mode_notifier(Notifier *notifier, void *data)
{
QemuSpicePointer *pointer = container_of(notifier, QemuSpicePointer, mouse_mode);
bool is_absolute = kbd_mouse_is_absolute();
@@ -213,5 +213,5 @@ void qemu_spice_input_init(void)
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);
+ mouse_mode_notifier(&pointer->mouse_mode, NULL);
}
diff --git a/ui/vnc-auth-sasl.c b/ui/vnc-auth-sasl.c
index 17a621a..15af49b 100644
--- a/ui/vnc-auth-sasl.c
+++ b/ui/vnc-auth-sasl.c
@@ -491,13 +491,6 @@ static int protocol_client_auth_sasl_mechname_len(VncState *vs, uint8_t *data, s
return 0;
}
-#define USES_X509_AUTH(vs) \
- ((vs)->subauth == VNC_AUTH_VENCRYPT_X509NONE || \
- (vs)->subauth == VNC_AUTH_VENCRYPT_X509VNC || \
- (vs)->subauth == VNC_AUTH_VENCRYPT_X509PLAIN || \
- (vs)->subauth == VNC_AUTH_VENCRYPT_X509SASL)
-
-
void start_auth_sasl(VncState *vs)
{
const char *mechlist = NULL;
@@ -538,8 +531,8 @@ void start_auth_sasl(VncState *vs)
#ifdef CONFIG_VNC_TLS
/* Inform SASL that we've got an external SSF layer from TLS/x509 */
- if (vs->vd->auth == VNC_AUTH_VENCRYPT &&
- vs->vd->subauth == VNC_AUTH_VENCRYPT_X509SASL) {
+ if (vs->auth == VNC_AUTH_VENCRYPT &&
+ vs->subauth == VNC_AUTH_VENCRYPT_X509SASL) {
gnutls_cipher_algorithm_t cipher;
sasl_ssf_t ssf;
@@ -570,8 +563,8 @@ void start_auth_sasl(VncState *vs)
#ifdef CONFIG_VNC_TLS
/* Disable SSF, if using TLS+x509+SASL only. TLS without x509
is not sufficiently strong */
- || (vs->vd->auth == VNC_AUTH_VENCRYPT &&
- vs->vd->subauth == VNC_AUTH_VENCRYPT_X509SASL)
+ || (vs->auth == VNC_AUTH_VENCRYPT &&
+ vs->subauth == VNC_AUTH_VENCRYPT_X509SASL)
#endif /* CONFIG_VNC_TLS */
) {
/* If we've got TLS or UNIX domain sock, we don't care about SSF */
diff --git a/ui/vnc-auth-vencrypt.c b/ui/vnc-auth-vencrypt.c
index 07c1691..674ba97 100644
--- a/ui/vnc-auth-vencrypt.c
+++ b/ui/vnc-auth-vencrypt.c
@@ -29,7 +29,7 @@
static void start_auth_vencrypt_subauth(VncState *vs)
{
- switch (vs->vd->subauth) {
+ switch (vs->subauth) {
case VNC_AUTH_VENCRYPT_TLSNONE:
case VNC_AUTH_VENCRYPT_X509NONE:
VNC_DEBUG("Accept TLS auth none\n");
@@ -51,7 +51,7 @@ static void start_auth_vencrypt_subauth(VncState *vs)
#endif /* CONFIG_VNC_SASL */
default: /* Should not be possible, but just in case */
- VNC_DEBUG("Reject subauth %d server bug\n", vs->vd->auth);
+ VNC_DEBUG("Reject subauth %d server bug\n", vs->auth);
vnc_write_u8(vs, 1);
if (vs->minor >= 8) {
static const char err[] = "Unsupported authentication type";
@@ -110,17 +110,17 @@ static void vnc_tls_handshake_io(void *opaque) {
#define NEED_X509_AUTH(vs) \
- ((vs)->vd->subauth == VNC_AUTH_VENCRYPT_X509NONE || \
- (vs)->vd->subauth == VNC_AUTH_VENCRYPT_X509VNC || \
- (vs)->vd->subauth == VNC_AUTH_VENCRYPT_X509PLAIN || \
- (vs)->vd->subauth == VNC_AUTH_VENCRYPT_X509SASL)
+ ((vs)->subauth == VNC_AUTH_VENCRYPT_X509NONE || \
+ (vs)->subauth == VNC_AUTH_VENCRYPT_X509VNC || \
+ (vs)->subauth == VNC_AUTH_VENCRYPT_X509PLAIN || \
+ (vs)->subauth == VNC_AUTH_VENCRYPT_X509SASL)
static int protocol_client_vencrypt_auth(VncState *vs, uint8_t *data, size_t len)
{
int auth = read_u32(data, 0);
- if (auth != vs->vd->subauth) {
+ if (auth != vs->subauth) {
VNC_DEBUG("Rejecting auth %d\n", auth);
vnc_write_u8(vs, 0); /* Reject auth */
vnc_flush(vs);
@@ -153,10 +153,10 @@ static int protocol_client_vencrypt_init(VncState *vs, uint8_t *data, size_t len
vnc_flush(vs);
vnc_client_error(vs);
} else {
- VNC_DEBUG("Sending allowed auth %d\n", vs->vd->subauth);
+ VNC_DEBUG("Sending allowed auth %d\n", vs->subauth);
vnc_write_u8(vs, 0); /* Accept version */
vnc_write_u8(vs, 1); /* Number of sub-auths */
- vnc_write_u32(vs, vs->vd->subauth); /* The supported auth */
+ vnc_write_u32(vs, vs->subauth); /* The supported auth */
vnc_flush(vs);
vnc_read_when(vs, protocol_client_vencrypt_auth, 4);
}
diff --git a/ui/vnc-tls.c b/ui/vnc-tls.c
index dec626c..31f1467 100644
--- a/ui/vnc-tls.c
+++ b/ui/vnc-tls.c
@@ -89,7 +89,7 @@ static ssize_t vnc_tls_pull(gnutls_transport_ptr_t transport,
int ret;
retry:
- ret = recv(vs->csock, data, len, 0);
+ ret = qemu_recv(vs->csock, data, len, 0);
if (ret < 0) {
if (errno == EINTR)
goto retry;
diff --git a/ui/vnc.c b/ui/vnc.c
index 14f2930..f1e27d9 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -1199,7 +1199,7 @@ long vnc_client_read_buf(VncState *vs, uint8_t *data, size_t datalen)
}
} else
#endif /* CONFIG_VNC_TLS */
- ret = recv(vs->csock, (void *)data, datalen, 0);
+ ret = qemu_recv(vs->csock, data, datalen, 0);
VNC_DEBUG("Read wire %p %zd -> %ld\n", data, datalen, ret);
return vnc_client_io_error(vs, ret, socket_error());
}
@@ -1346,7 +1346,7 @@ static void client_cut_text(VncState *vs, size_t len, uint8_t *text)
{
}
-static void check_pointer_type_change(Notifier *notifier)
+static void check_pointer_type_change(Notifier *notifier, void *data)
{
VncState *vs = container_of(notifier, VncState, mouse_mode_notifier);
int absolute = kbd_mouse_is_absolute();
@@ -1769,7 +1769,7 @@ static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings)
}
}
vnc_desktop_resize(vs);
- check_pointer_type_change(&vs->mouse_mode_notifier);
+ check_pointer_type_change(&vs->mouse_mode_notifier, NULL);
}
static void set_pixel_conversion(VncState *vs)
@@ -2124,7 +2124,7 @@ static int protocol_client_auth(VncState *vs, uint8_t *data, size_t len)
{
/* We only advertise 1 auth scheme at a time, so client
* must pick the one we sent. Verify this */
- if (data[0] != vs->vd->auth) { /* Reject auth */
+ if (data[0] != vs->auth) { /* Reject auth */
VNC_DEBUG("Reject auth %d because it didn't match advertized\n", (int)data[0]);
vnc_write_u32(vs, 1);
if (vs->minor >= 8) {
@@ -2135,7 +2135,7 @@ static int protocol_client_auth(VncState *vs, uint8_t *data, size_t len)
vnc_client_error(vs);
} else { /* Accept requested auth */
VNC_DEBUG("Client requested auth %d\n", (int)data[0]);
- switch (vs->vd->auth) {
+ switch (vs->auth) {
case VNC_AUTH_NONE:
VNC_DEBUG("Accept auth none\n");
if (vs->minor >= 8) {
@@ -2165,7 +2165,7 @@ static int protocol_client_auth(VncState *vs, uint8_t *data, size_t len)
#endif /* CONFIG_VNC_SASL */
default: /* Should not be possible, but just in case */
- VNC_DEBUG("Reject auth %d server code bug\n", vs->vd->auth);
+ VNC_DEBUG("Reject auth %d server code bug\n", vs->auth);
vnc_write_u8(vs, 1);
if (vs->minor >= 8) {
static const char err[] = "Authentication failed";
@@ -2210,26 +2210,26 @@ static int protocol_version(VncState *vs, uint8_t *version, size_t len)
vs->minor = 3;
if (vs->minor == 3) {
- if (vs->vd->auth == VNC_AUTH_NONE) {
+ if (vs->auth == VNC_AUTH_NONE) {
VNC_DEBUG("Tell client auth none\n");
- vnc_write_u32(vs, vs->vd->auth);
+ vnc_write_u32(vs, vs->auth);
vnc_flush(vs);
start_client_init(vs);
- } else if (vs->vd->auth == VNC_AUTH_VNC) {
+ } else if (vs->auth == VNC_AUTH_VNC) {
VNC_DEBUG("Tell client VNC auth\n");
- vnc_write_u32(vs, vs->vd->auth);
+ vnc_write_u32(vs, vs->auth);
vnc_flush(vs);
start_auth_vnc(vs);
} else {
- VNC_DEBUG("Unsupported auth %d for protocol 3.3\n", vs->vd->auth);
+ VNC_DEBUG("Unsupported auth %d for protocol 3.3\n", vs->auth);
vnc_write_u32(vs, VNC_AUTH_INVALID);
vnc_flush(vs);
vnc_client_error(vs);
}
} else {
- VNC_DEBUG("Telling client we support auth %d\n", vs->vd->auth);
+ VNC_DEBUG("Telling client we support auth %d\n", vs->auth);
vnc_write_u8(vs, 1); /* num auth */
- vnc_write_u8(vs, vs->vd->auth);
+ vnc_write_u8(vs, vs->auth);
vnc_read_when(vs, protocol_client_auth, 1);
vnc_flush(vs);
}
@@ -2494,12 +2494,25 @@ static void vnc_remove_timer(VncDisplay *vd)
}
}
-static void vnc_connect(VncDisplay *vd, int csock)
+static void vnc_connect(VncDisplay *vd, int csock, int skipauth)
{
VncState *vs = qemu_mallocz(sizeof(VncState));
int i;
vs->csock = csock;
+
+ if (skipauth) {
+ vs->auth = VNC_AUTH_NONE;
+#ifdef CONFIG_VNC_TLS
+ vs->subauth = VNC_AUTH_INVALID;
+#endif
+ } else {
+ vs->auth = vd->auth;
+#ifdef CONFIG_VNC_TLS
+ vs->subauth = vd->subauth;
+#endif
+ }
+
vs->lossy_rect = qemu_mallocz(VNC_STAT_ROWS * sizeof (*vs->lossy_rect));
for (i = 0; i < VNC_STAT_ROWS; ++i) {
vs->lossy_rect[i] = qemu_mallocz(VNC_STAT_COLS * sizeof (uint8_t));
@@ -2557,7 +2570,7 @@ static void vnc_listen_read(void *opaque)
int csock = qemu_accept(vs->lsock, (struct sockaddr *)&addr, &addrlen);
if (csock != -1) {
- vnc_connect(vs, csock);
+ vnc_connect(vs, csock, 0);
}
}
@@ -2887,7 +2900,7 @@ int vnc_display_open(DisplayState *ds, const char *display)
} else {
int csock = vs->lsock;
vs->lsock = -1;
- vnc_connect(vs, csock);
+ vnc_connect(vs, csock, 0);
}
return 0;
@@ -2911,3 +2924,10 @@ int vnc_display_open(DisplayState *ds, const char *display)
}
return qemu_set_fd_handler2(vs->lsock, NULL, vnc_listen_read, NULL, vs);
}
+
+void vnc_display_add_client(DisplayState *ds, int csock, int skipauth)
+{
+ VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display;
+
+ return vnc_connect(vs, csock, skipauth);
+}
diff --git a/ui/vnc.h b/ui/vnc.h
index f10c5dc..66689f1 100644
--- a/ui/vnc.h
+++ b/ui/vnc.h
@@ -256,8 +256,10 @@ struct VncState
int major;
int minor;
+ int auth;
char challenge[VNC_AUTH_CHALLENGE_SIZE];
#ifdef CONFIG_VNC_TLS
+ int subauth; /* Used by VeNCrypt */
VncStateTLS tls;
#endif
#ifdef CONFIG_VNC_SASL
diff --git a/usb-bsd.c b/usb-bsd.c
index 3b97eb4..ab84d93 100644
--- a/usb-bsd.c
+++ b/usb-bsd.c
@@ -62,7 +62,6 @@ typedef struct USBHostDevice {
} USBHostDevice;
-#if 0
static int ensure_ep_open(USBHostDevice *dev, int ep, int mode)
{
char buf[32];
@@ -110,7 +109,6 @@ static void ensure_eps_closed(USBHostDevice *dev)
epnum++;
}
}
-#endif
static void usb_host_handle_reset(USBDevice *dev)
{
@@ -119,7 +117,6 @@ static void usb_host_handle_reset(USBDevice *dev)
#endif
}
-#if 0
/* XXX:
* -check device states against transfer requests
* and return appropriate response
@@ -256,9 +253,9 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
}
if (p->pid == USB_TOKEN_IN)
- ret = read(fd, p->data, p->len);
+ ret = readv(fd, p->iov.iov, p->iov.niov);
else
- ret = write(fd, p->data, p->len);
+ ret = writev(fd, p->iov.iov, p->iov.niov);
sigprocmask(SIG_SETMASK, &old_mask, NULL);
@@ -278,7 +275,6 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
return ret;
}
}
-#endif
static void usb_host_handle_destroy(USBDevice *opaque)
{
@@ -305,8 +301,8 @@ static int usb_host_initfn(USBDevice *dev)
USBDevice *usb_host_device_open(const char *devname)
{
struct usb_device_info bus_info, dev_info;
- USBDevice *d = NULL;
- USBHostDevice *dev, *ret = NULL;
+ USBDevice *d = NULL, *ret = NULL;
+ USBHostDevice *dev;
char ctlpath[PATH_MAX + 1];
char buspath[PATH_MAX + 1];
int bfd, dfd, bus, address, i;
@@ -408,10 +404,8 @@ static struct USBDeviceInfo usb_host_dev_info = {
.init = usb_host_initfn,
.handle_packet = usb_generic_handle_packet,
.handle_reset = usb_host_handle_reset,
-#if 0
.handle_control = usb_host_handle_control,
.handle_data = usb_host_handle_data,
-#endif
.handle_destroy = usb_host_handle_destroy,
};
diff --git a/usb-linux.c b/usb-linux.c
index 1a2deb3..5562187 100644
--- a/usb-linux.c
+++ b/usb-linux.c
@@ -341,16 +341,16 @@ static void async_complete(void *opaque)
if (p) {
switch (aurb->urb.status) {
case 0:
- p->len += aurb->urb.actual_length;
+ p->result += aurb->urb.actual_length;
break;
case -EPIPE:
set_halt(s, p->devep);
- p->len = USB_RET_STALL;
+ p->result = USB_RET_STALL;
break;
default:
- p->len = USB_RET_NAK;
+ p->result = USB_RET_NAK;
break;
}
@@ -604,6 +604,7 @@ static int usb_host_handle_iso_data(USBHostDevice *s, USBPacket *p, int in)
{
AsyncURB *aurb;
int i, j, ret, max_packet_size, offset, len = 0;
+ uint8_t *buf;
max_packet_size = get_max_packet_size(s, p->devep);
if (max_packet_size == 0)
@@ -628,19 +629,19 @@ static int usb_host_handle_iso_data(USBHostDevice *s, USBPacket *p, int in)
len = urb_status_to_usb_ret(
aurb[i].urb.iso_frame_desc[j].status);
/* Check the frame fits */
- } else if (aurb[i].urb.iso_frame_desc[j].actual_length > p->len) {
+ } else if (aurb[i].urb.iso_frame_desc[j].actual_length
+ > p->iov.size) {
printf("husb: received iso data is larger then packet\n");
len = USB_RET_NAK;
/* All good copy data over */
} else {
len = aurb[i].urb.iso_frame_desc[j].actual_length;
- memcpy(p->data,
- aurb[i].urb.buffer +
- j * aurb[i].urb.iso_frame_desc[0].length,
- len);
+ buf = aurb[i].urb.buffer +
+ j * aurb[i].urb.iso_frame_desc[0].length;
+ usb_packet_copy(p, buf, len);
}
} else {
- len = p->len;
+ len = p->iov.size;
offset = (j == 0) ? 0 : get_iso_buffer_used(s, p->devep);
/* Check the frame fits */
@@ -650,7 +651,7 @@ static int usb_host_handle_iso_data(USBHostDevice *s, USBPacket *p, int in)
}
/* All good copy data over */
- memcpy(aurb[i].urb.buffer + offset, p->data, len);
+ usb_packet_copy(p, aurb[i].urb.buffer + offset, len);
aurb[i].urb.iso_frame_desc[j].length = len;
offset += len;
set_iso_buffer_used(s, p->devep, offset);
@@ -706,7 +707,7 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev);
struct usbdevfs_urb *urb;
AsyncURB *aurb;
- int ret, rem;
+ int ret, rem, prem, v;
uint8_t *pbuf;
uint8_t ep;
@@ -734,10 +735,18 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
return usb_host_handle_iso_data(s, p, p->pid == USB_TOKEN_IN);
}
- rem = p->len;
- pbuf = p->data;
- p->len = 0;
+ v = 0;
+ prem = p->iov.iov[v].iov_len;
+ pbuf = p->iov.iov[v].iov_base;
+ rem = p->iov.size;
while (rem) {
+ if (prem == 0) {
+ v++;
+ assert(v < p->iov.niov);
+ prem = p->iov.iov[v].iov_len;
+ pbuf = p->iov.iov[v].iov_base;
+ assert(prem <= rem);
+ }
aurb = async_alloc(s);
aurb->packet = p;
@@ -746,16 +755,17 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
urb->type = USBDEVFS_URB_TYPE_BULK;
urb->usercontext = s;
urb->buffer = pbuf;
+ urb->buffer_length = prem;
- if (rem > MAX_USBFS_BUFFER_SIZE) {
+ if (urb->buffer_length > MAX_USBFS_BUFFER_SIZE) {
urb->buffer_length = MAX_USBFS_BUFFER_SIZE;
- aurb->more = 1;
- } else {
- urb->buffer_length = rem;
- aurb->more = 0;
}
pbuf += urb->buffer_length;
+ prem -= urb->buffer_length;
rem -= urb->buffer_length;
+ if (rem) {
+ aurb->more = 1;
+ }
ret = ioctl(s->fd, USBDEVFS_SUBMITURB, urb);
@@ -1260,7 +1270,7 @@ static int usb_host_close(USBHostDevice *dev)
return 0;
}
-static void usb_host_exit_notifier(struct Notifier* n)
+static void usb_host_exit_notifier(struct Notifier *n, void *data)
{
USBHostDevice *s = container_of(n, USBHostDevice, exit);
diff --git a/usb-redir.c b/usb-redir.c
new file mode 100644
index 0000000..9e5fce2
--- /dev/null
+++ b/usb-redir.c
@@ -0,0 +1,1225 @@
+/*
+ * USB redirector usb-guest
+ *
+ * Copyright (c) 2011 Red Hat, Inc.
+ *
+ * Red Hat Authors:
+ * Hans de Goede <hdegoede@redhat.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 "qemu-common.h"
+#include "qemu-timer.h"
+#include "monitor.h"
+#include "sysemu.h"
+
+#include <dirent.h>
+#include <sys/ioctl.h>
+#include <signal.h>
+#include <usbredirparser.h>
+
+#include "hw/usb.h"
+
+#define MAX_ENDPOINTS 32
+#define EP2I(ep_address) (((ep_address & 0x80) >> 3) | (ep_address & 0x0f))
+#define I2EP(i) (((i & 0x10) << 3) | (i & 0x0f))
+
+typedef struct AsyncURB AsyncURB;
+typedef struct USBRedirDevice USBRedirDevice;
+
+/* Struct to hold buffered packets (iso or int input packets) */
+struct buf_packet {
+ uint8_t *data;
+ int len;
+ int status;
+ QTAILQ_ENTRY(buf_packet)next;
+};
+
+struct endp_data {
+ uint8_t type;
+ uint8_t interval;
+ uint8_t interface; /* bInterfaceNumber this ep belongs to */
+ uint8_t iso_started;
+ uint8_t iso_error; /* For reporting iso errors to the HC */
+ uint8_t interrupt_started;
+ uint8_t interrupt_error;
+ QTAILQ_HEAD(, buf_packet) bufpq;
+};
+
+struct USBRedirDevice {
+ USBDevice dev;
+ /* Properties */
+ CharDriverState *cs;
+ uint8_t debug;
+ /* Data passed from chardev the fd_read cb to the usbredirparser read cb */
+ const uint8_t *read_buf;
+ int read_buf_size;
+ /* For async handling of open/close */
+ QEMUBH *open_close_bh;
+ /* To delay the usb attach in case of quick chardev close + open */
+ QEMUTimer *attach_timer;
+ int64_t next_attach_time;
+ struct usbredirparser *parser;
+ struct endp_data endpoint[MAX_ENDPOINTS];
+ uint32_t packet_id;
+ QTAILQ_HEAD(, AsyncURB) asyncq;
+};
+
+struct AsyncURB {
+ USBRedirDevice *dev;
+ USBPacket *packet;
+ uint32_t packet_id;
+ int get;
+ union {
+ struct usb_redir_control_packet_header control_packet;
+ struct usb_redir_bulk_packet_header bulk_packet;
+ struct usb_redir_interrupt_packet_header interrupt_packet;
+ };
+ QTAILQ_ENTRY(AsyncURB)next;
+};
+
+static void usbredir_device_connect(void *priv,
+ struct usb_redir_device_connect_header *device_connect);
+static void usbredir_device_disconnect(void *priv);
+static void usbredir_interface_info(void *priv,
+ struct usb_redir_interface_info_header *interface_info);
+static void usbredir_ep_info(void *priv,
+ struct usb_redir_ep_info_header *ep_info);
+static void usbredir_configuration_status(void *priv, uint32_t id,
+ struct usb_redir_configuration_status_header *configuration_status);
+static void usbredir_alt_setting_status(void *priv, uint32_t id,
+ struct usb_redir_alt_setting_status_header *alt_setting_status);
+static void usbredir_iso_stream_status(void *priv, uint32_t id,
+ struct usb_redir_iso_stream_status_header *iso_stream_status);
+static void usbredir_interrupt_receiving_status(void *priv, uint32_t id,
+ struct usb_redir_interrupt_receiving_status_header
+ *interrupt_receiving_status);
+static void usbredir_bulk_streams_status(void *priv, uint32_t id,
+ struct usb_redir_bulk_streams_status_header *bulk_streams_status);
+static void usbredir_control_packet(void *priv, uint32_t id,
+ struct usb_redir_control_packet_header *control_packet,
+ uint8_t *data, int data_len);
+static void usbredir_bulk_packet(void *priv, uint32_t id,
+ struct usb_redir_bulk_packet_header *bulk_packet,
+ uint8_t *data, int data_len);
+static void usbredir_iso_packet(void *priv, uint32_t id,
+ struct usb_redir_iso_packet_header *iso_packet,
+ uint8_t *data, int data_len);
+static void usbredir_interrupt_packet(void *priv, uint32_t id,
+ struct usb_redir_interrupt_packet_header *interrupt_header,
+ uint8_t *data, int data_len);
+
+static int usbredir_handle_status(USBRedirDevice *dev,
+ int status, int actual_len);
+
+#define VERSION "qemu usb-redir guest " QEMU_VERSION
+
+/*
+ * Logging stuff
+ */
+
+#define ERROR(...) \
+ do { \
+ if (dev->debug >= usbredirparser_error) { \
+ error_report("usb-redir error: " __VA_ARGS__); \
+ } \
+ } while (0)
+#define WARNING(...) \
+ do { \
+ if (dev->debug >= usbredirparser_warning) { \
+ error_report("usb-redir warning: " __VA_ARGS__); \
+ } \
+ } while (0)
+#define INFO(...) \
+ do { \
+ if (dev->debug >= usbredirparser_info) { \
+ error_report("usb-redir: " __VA_ARGS__); \
+ } \
+ } while (0)
+#define DPRINTF(...) \
+ do { \
+ if (dev->debug >= usbredirparser_debug) { \
+ error_report("usb-redir: " __VA_ARGS__); \
+ } \
+ } while (0)
+#define DPRINTF2(...) \
+ do { \
+ if (dev->debug >= usbredirparser_debug_data) { \
+ error_report("usb-redir: " __VA_ARGS__); \
+ } \
+ } while (0)
+
+static void usbredir_log(void *priv, int level, const char *msg)
+{
+ USBRedirDevice *dev = priv;
+
+ if (dev->debug < level) {
+ return;
+ }
+
+ error_report("%s\n", msg);
+}
+
+static void usbredir_log_data(USBRedirDevice *dev, const char *desc,
+ const uint8_t *data, int len)
+{
+ int i, j, n;
+
+ if (dev->debug < usbredirparser_debug_data) {
+ return;
+ }
+
+ for (i = 0; i < len; i += j) {
+ char buf[128];
+
+ n = sprintf(buf, "%s", desc);
+ for (j = 0; j < 8 && i + j < len; j++) {
+ n += sprintf(buf + n, " %02X", data[i + j]);
+ }
+ error_report("%s\n", buf);
+ }
+}
+
+/*
+ * usbredirparser io functions
+ */
+
+static int usbredir_read(void *priv, uint8_t *data, int count)
+{
+ USBRedirDevice *dev = priv;
+
+ if (dev->read_buf_size < count) {
+ count = dev->read_buf_size;
+ }
+
+ memcpy(data, dev->read_buf, count);
+
+ dev->read_buf_size -= count;
+ if (dev->read_buf_size) {
+ dev->read_buf += count;
+ } else {
+ dev->read_buf = NULL;
+ }
+
+ return count;
+}
+
+static int usbredir_write(void *priv, uint8_t *data, int count)
+{
+ USBRedirDevice *dev = priv;
+
+ return qemu_chr_write(dev->cs, data, count);
+}
+
+/*
+ * Async and buffered packets helpers
+ */
+
+static AsyncURB *async_alloc(USBRedirDevice *dev, USBPacket *p)
+{
+ AsyncURB *aurb = (AsyncURB *) qemu_mallocz(sizeof(AsyncURB));
+ aurb->dev = dev;
+ aurb->packet = p;
+ aurb->packet_id = dev->packet_id;
+ QTAILQ_INSERT_TAIL(&dev->asyncq, aurb, next);
+ dev->packet_id++;
+
+ return aurb;
+}
+
+static void async_free(USBRedirDevice *dev, AsyncURB *aurb)
+{
+ QTAILQ_REMOVE(&dev->asyncq, aurb, next);
+ qemu_free(aurb);
+}
+
+static AsyncURB *async_find(USBRedirDevice *dev, uint32_t packet_id)
+{
+ AsyncURB *aurb;
+
+ QTAILQ_FOREACH(aurb, &dev->asyncq, next) {
+ if (aurb->packet_id == packet_id) {
+ return aurb;
+ }
+ }
+ ERROR("could not find async urb for packet_id %u\n", packet_id);
+ return NULL;
+}
+
+static void usbredir_cancel_packet(USBDevice *udev, USBPacket *p)
+{
+ USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev);
+ AsyncURB *aurb;
+
+ QTAILQ_FOREACH(aurb, &dev->asyncq, next) {
+ if (p != aurb->packet) {
+ continue;
+ }
+
+ DPRINTF("async cancel id %u\n", aurb->packet_id);
+ usbredirparser_send_cancel_data_packet(dev->parser, aurb->packet_id);
+ usbredirparser_do_write(dev->parser);
+
+ /* Mark it as dead */
+ aurb->packet = NULL;
+ break;
+ }
+}
+
+static struct buf_packet *bufp_alloc(USBRedirDevice *dev,
+ uint8_t *data, int len, int status, uint8_t ep)
+{
+ struct buf_packet *bufp = qemu_malloc(sizeof(struct buf_packet));
+ bufp->data = data;
+ bufp->len = len;
+ bufp->status = status;
+ QTAILQ_INSERT_TAIL(&dev->endpoint[EP2I(ep)].bufpq, bufp, next);
+ return bufp;
+}
+
+static void bufp_free(USBRedirDevice *dev, struct buf_packet *bufp,
+ uint8_t ep)
+{
+ QTAILQ_REMOVE(&dev->endpoint[EP2I(ep)].bufpq, bufp, next);
+ free(bufp->data);
+ qemu_free(bufp);
+}
+
+static void usbredir_free_bufpq(USBRedirDevice *dev, uint8_t ep)
+{
+ struct buf_packet *buf, *buf_next;
+
+ QTAILQ_FOREACH_SAFE(buf, &dev->endpoint[EP2I(ep)].bufpq, next, buf_next) {
+ bufp_free(dev, buf, ep);
+ }
+}
+
+/*
+ * USBDevice callbacks
+ */
+
+static void usbredir_handle_reset(USBDevice *udev)
+{
+ USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev);
+
+ DPRINTF("reset device\n");
+ usbredirparser_send_reset(dev->parser);
+ usbredirparser_do_write(dev->parser);
+}
+
+static int usbredir_handle_iso_data(USBRedirDevice *dev, USBPacket *p,
+ uint8_t ep)
+{
+ int status, len;
+
+ if (!dev->endpoint[EP2I(ep)].iso_started &&
+ !dev->endpoint[EP2I(ep)].iso_error) {
+ struct usb_redir_start_iso_stream_header start_iso = {
+ .endpoint = ep,
+ /* TODO maybe do something with these depending on ep interval? */
+ .pkts_per_urb = 32,
+ .no_urbs = 3,
+ };
+ /* No id, we look at the ep when receiving a status back */
+ usbredirparser_send_start_iso_stream(dev->parser, 0, &start_iso);
+ usbredirparser_do_write(dev->parser);
+ DPRINTF("iso stream started ep %02X\n", ep);
+ dev->endpoint[EP2I(ep)].iso_started = 1;
+ }
+
+ if (ep & USB_DIR_IN) {
+ struct buf_packet *isop;
+
+ isop = QTAILQ_FIRST(&dev->endpoint[EP2I(ep)].bufpq);
+ if (isop == NULL) {
+ DPRINTF2("iso-token-in ep %02X, no isop\n", ep);
+ /* Check iso_error for stream errors, otherwise its an underrun */
+ status = dev->endpoint[EP2I(ep)].iso_error;
+ dev->endpoint[EP2I(ep)].iso_error = 0;
+ return usbredir_handle_status(dev, status, 0);
+ }
+ DPRINTF2("iso-token-in ep %02X status %d len %d\n", ep, isop->status,
+ isop->len);
+
+ status = isop->status;
+ if (status != usb_redir_success) {
+ bufp_free(dev, isop, ep);
+ return usbredir_handle_status(dev, status, 0);
+ }
+
+ len = isop->len;
+ if (len > p->iov.size) {
+ ERROR("received iso data is larger then packet ep %02X\n", ep);
+ bufp_free(dev, isop, ep);
+ return USB_RET_NAK;
+ }
+ usb_packet_copy(p, isop->data, len);
+ bufp_free(dev, isop, ep);
+ return len;
+ } else {
+ /* If the stream was not started because of a pending error don't
+ send the packet to the usb-host */
+ if (dev->endpoint[EP2I(ep)].iso_started) {
+ struct usb_redir_iso_packet_header iso_packet = {
+ .endpoint = ep,
+ .length = p->iov.size
+ };
+ uint8_t buf[p->iov.size];
+ /* No id, we look at the ep when receiving a status back */
+ usb_packet_copy(p, buf, p->iov.size);
+ usbredirparser_send_iso_packet(dev->parser, 0, &iso_packet,
+ buf, p->iov.size);
+ usbredirparser_do_write(dev->parser);
+ }
+ status = dev->endpoint[EP2I(ep)].iso_error;
+ dev->endpoint[EP2I(ep)].iso_error = 0;
+ DPRINTF2("iso-token-out ep %02X status %d len %zd\n", ep, status,
+ p->iov.size);
+ return usbredir_handle_status(dev, status, p->iov.size);
+ }
+}
+
+static void usbredir_stop_iso_stream(USBRedirDevice *dev, uint8_t ep)
+{
+ struct usb_redir_stop_iso_stream_header stop_iso_stream = {
+ .endpoint = ep
+ };
+ if (dev->endpoint[EP2I(ep)].iso_started) {
+ usbredirparser_send_stop_iso_stream(dev->parser, 0, &stop_iso_stream);
+ DPRINTF("iso stream stopped ep %02X\n", ep);
+ dev->endpoint[EP2I(ep)].iso_started = 0;
+ }
+ usbredir_free_bufpq(dev, ep);
+}
+
+static int usbredir_handle_bulk_data(USBRedirDevice *dev, USBPacket *p,
+ uint8_t ep)
+{
+ AsyncURB *aurb = async_alloc(dev, p);
+ struct usb_redir_bulk_packet_header bulk_packet;
+
+ DPRINTF("bulk-out ep %02X len %zd id %u\n", ep,
+ p->iov.size, aurb->packet_id);
+
+ bulk_packet.endpoint = ep;
+ bulk_packet.length = p->iov.size;
+ bulk_packet.stream_id = 0;
+ aurb->bulk_packet = bulk_packet;
+
+ if (ep & USB_DIR_IN) {
+ usbredirparser_send_bulk_packet(dev->parser, aurb->packet_id,
+ &bulk_packet, NULL, 0);
+ } else {
+ uint8_t buf[p->iov.size];
+ usb_packet_copy(p, buf, p->iov.size);
+ usbredir_log_data(dev, "bulk data out:", buf, p->iov.size);
+ usbredirparser_send_bulk_packet(dev->parser, aurb->packet_id,
+ &bulk_packet, buf, p->iov.size);
+ }
+ usbredirparser_do_write(dev->parser);
+ return USB_RET_ASYNC;
+}
+
+static int usbredir_handle_interrupt_data(USBRedirDevice *dev,
+ USBPacket *p, uint8_t ep)
+{
+ if (ep & USB_DIR_IN) {
+ /* Input interrupt endpoint, buffered packet input */
+ struct buf_packet *intp;
+ int status, len;
+
+ if (!dev->endpoint[EP2I(ep)].interrupt_started &&
+ !dev->endpoint[EP2I(ep)].interrupt_error) {
+ struct usb_redir_start_interrupt_receiving_header start_int = {
+ .endpoint = ep,
+ };
+ /* No id, we look at the ep when receiving a status back */
+ usbredirparser_send_start_interrupt_receiving(dev->parser, 0,
+ &start_int);
+ usbredirparser_do_write(dev->parser);
+ DPRINTF("interrupt recv started ep %02X\n", ep);
+ dev->endpoint[EP2I(ep)].interrupt_started = 1;
+ }
+
+ intp = QTAILQ_FIRST(&dev->endpoint[EP2I(ep)].bufpq);
+ if (intp == NULL) {
+ DPRINTF2("interrupt-token-in ep %02X, no intp\n", ep);
+ /* Check interrupt_error for stream errors */
+ status = dev->endpoint[EP2I(ep)].interrupt_error;
+ dev->endpoint[EP2I(ep)].interrupt_error = 0;
+ return usbredir_handle_status(dev, status, 0);
+ }
+ DPRINTF("interrupt-token-in ep %02X status %d len %d\n", ep,
+ intp->status, intp->len);
+
+ status = intp->status;
+ if (status != usb_redir_success) {
+ bufp_free(dev, intp, ep);
+ return usbredir_handle_status(dev, status, 0);
+ }
+
+ len = intp->len;
+ if (len > p->iov.size) {
+ ERROR("received int data is larger then packet ep %02X\n", ep);
+ bufp_free(dev, intp, ep);
+ return USB_RET_NAK;
+ }
+ usb_packet_copy(p, intp->data, len);
+ bufp_free(dev, intp, ep);
+ return len;
+ } else {
+ /* Output interrupt endpoint, normal async operation */
+ AsyncURB *aurb = async_alloc(dev, p);
+ struct usb_redir_interrupt_packet_header interrupt_packet;
+ uint8_t buf[p->iov.size];
+
+ DPRINTF("interrupt-out ep %02X len %zd id %u\n", ep, p->iov.size,
+ aurb->packet_id);
+
+ interrupt_packet.endpoint = ep;
+ interrupt_packet.length = p->iov.size;
+ aurb->interrupt_packet = interrupt_packet;
+
+ usb_packet_copy(p, buf, p->iov.size);
+ usbredir_log_data(dev, "interrupt data out:", buf, p->iov.size);
+ usbredirparser_send_interrupt_packet(dev->parser, aurb->packet_id,
+ &interrupt_packet, buf, p->iov.size);
+ usbredirparser_do_write(dev->parser);
+ return USB_RET_ASYNC;
+ }
+}
+
+static void usbredir_stop_interrupt_receiving(USBRedirDevice *dev,
+ uint8_t ep)
+{
+ struct usb_redir_stop_interrupt_receiving_header stop_interrupt_recv = {
+ .endpoint = ep
+ };
+ if (dev->endpoint[EP2I(ep)].interrupt_started) {
+ usbredirparser_send_stop_interrupt_receiving(dev->parser, 0,
+ &stop_interrupt_recv);
+ DPRINTF("interrupt recv stopped ep %02X\n", ep);
+ dev->endpoint[EP2I(ep)].interrupt_started = 0;
+ }
+ usbredir_free_bufpq(dev, ep);
+}
+
+static int usbredir_handle_data(USBDevice *udev, USBPacket *p)
+{
+ USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev);
+ uint8_t ep;
+
+ ep = p->devep;
+ if (p->pid == USB_TOKEN_IN) {
+ ep |= USB_DIR_IN;
+ }
+
+ switch (dev->endpoint[EP2I(ep)].type) {
+ case USB_ENDPOINT_XFER_CONTROL:
+ ERROR("handle_data called for control transfer on ep %02X\n", ep);
+ return USB_RET_NAK;
+ case USB_ENDPOINT_XFER_ISOC:
+ return usbredir_handle_iso_data(dev, p, ep);
+ case USB_ENDPOINT_XFER_BULK:
+ return usbredir_handle_bulk_data(dev, p, ep);;
+ case USB_ENDPOINT_XFER_INT:
+ return usbredir_handle_interrupt_data(dev, p, ep);;
+ default:
+ ERROR("handle_data ep %02X has unknown type %d\n", ep,
+ dev->endpoint[EP2I(ep)].type);
+ return USB_RET_NAK;
+ }
+}
+
+static int usbredir_set_config(USBRedirDevice *dev, USBPacket *p,
+ int config)
+{
+ struct usb_redir_set_configuration_header set_config;
+ AsyncURB *aurb = async_alloc(dev, p);
+ int i;
+
+ DPRINTF("set config %d id %u\n", config, aurb->packet_id);
+
+ for (i = 0; i < MAX_ENDPOINTS; i++) {
+ switch (dev->endpoint[i].type) {
+ case USB_ENDPOINT_XFER_ISOC:
+ usbredir_stop_iso_stream(dev, I2EP(i));
+ break;
+ case USB_ENDPOINT_XFER_INT:
+ if (i & 0x10) {
+ usbredir_stop_interrupt_receiving(dev, I2EP(i));
+ }
+ break;
+ }
+ usbredir_free_bufpq(dev, I2EP(i));
+ }
+
+ set_config.configuration = config;
+ usbredirparser_send_set_configuration(dev->parser, aurb->packet_id,
+ &set_config);
+ usbredirparser_do_write(dev->parser);
+ return USB_RET_ASYNC;
+}
+
+static int usbredir_get_config(USBRedirDevice *dev, USBPacket *p)
+{
+ AsyncURB *aurb = async_alloc(dev, p);
+
+ DPRINTF("get config id %u\n", aurb->packet_id);
+
+ aurb->get = 1;
+ usbredirparser_send_get_configuration(dev->parser, aurb->packet_id);
+ usbredirparser_do_write(dev->parser);
+ return USB_RET_ASYNC;
+}
+
+static int usbredir_set_interface(USBRedirDevice *dev, USBPacket *p,
+ int interface, int alt)
+{
+ struct usb_redir_set_alt_setting_header set_alt;
+ AsyncURB *aurb = async_alloc(dev, p);
+ int i;
+
+ DPRINTF("set interface %d alt %d id %u\n", interface, alt,
+ aurb->packet_id);
+
+ for (i = 0; i < MAX_ENDPOINTS; i++) {
+ if (dev->endpoint[i].interface == interface) {
+ switch (dev->endpoint[i].type) {
+ case USB_ENDPOINT_XFER_ISOC:
+ usbredir_stop_iso_stream(dev, I2EP(i));
+ break;
+ case USB_ENDPOINT_XFER_INT:
+ if (i & 0x10) {
+ usbredir_stop_interrupt_receiving(dev, I2EP(i));
+ }
+ break;
+ }
+ usbredir_free_bufpq(dev, I2EP(i));
+ }
+ }
+
+ set_alt.interface = interface;
+ set_alt.alt = alt;
+ usbredirparser_send_set_alt_setting(dev->parser, aurb->packet_id,
+ &set_alt);
+ usbredirparser_do_write(dev->parser);
+ return USB_RET_ASYNC;
+}
+
+static int usbredir_get_interface(USBRedirDevice *dev, USBPacket *p,
+ int interface)
+{
+ struct usb_redir_get_alt_setting_header get_alt;
+ AsyncURB *aurb = async_alloc(dev, p);
+
+ DPRINTF("get interface %d id %u\n", interface, aurb->packet_id);
+
+ get_alt.interface = interface;
+ aurb->get = 1;
+ usbredirparser_send_get_alt_setting(dev->parser, aurb->packet_id,
+ &get_alt);
+ usbredirparser_do_write(dev->parser);
+ return USB_RET_ASYNC;
+}
+
+static int usbredir_handle_control(USBDevice *udev, USBPacket *p,
+ int request, int value, int index, int length, uint8_t *data)
+{
+ USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev);
+ struct usb_redir_control_packet_header control_packet;
+ AsyncURB *aurb;
+
+ /* Special cases for certain standard device requests */
+ switch (request) {
+ case DeviceOutRequest | USB_REQ_SET_ADDRESS:
+ DPRINTF("set address %d\n", value);
+ dev->dev.addr = value;
+ return 0;
+ case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
+ return usbredir_set_config(dev, p, value & 0xff);
+ case DeviceRequest | USB_REQ_GET_CONFIGURATION:
+ return usbredir_get_config(dev, p);
+ case InterfaceOutRequest | USB_REQ_SET_INTERFACE:
+ return usbredir_set_interface(dev, p, index, value);
+ case InterfaceRequest | USB_REQ_GET_INTERFACE:
+ return usbredir_get_interface(dev, p, index);
+ }
+
+ /* "Normal" ctrl requests */
+ aurb = async_alloc(dev, p);
+
+ /* Note request is (bRequestType << 8) | bRequest */
+ DPRINTF("ctrl-out type 0x%x req 0x%x val 0x%x index %d len %d id %u\n",
+ request >> 8, request & 0xff, value, index, length,
+ aurb->packet_id);
+
+ control_packet.request = request & 0xFF;
+ control_packet.requesttype = request >> 8;
+ control_packet.endpoint = control_packet.requesttype & USB_DIR_IN;
+ control_packet.value = value;
+ control_packet.index = index;
+ control_packet.length = length;
+ aurb->control_packet = control_packet;
+
+ if (control_packet.requesttype & USB_DIR_IN) {
+ usbredirparser_send_control_packet(dev->parser, aurb->packet_id,
+ &control_packet, NULL, 0);
+ } else {
+ usbredir_log_data(dev, "ctrl data out:", data, length);
+ usbredirparser_send_control_packet(dev->parser, aurb->packet_id,
+ &control_packet, data, length);
+ }
+ usbredirparser_do_write(dev->parser);
+ return USB_RET_ASYNC;
+}
+
+/*
+ * Close events can be triggered by usbredirparser_do_write which gets called
+ * from within the USBDevice data / control packet callbacks and doing a
+ * usb_detach from within these callbacks is not a good idea.
+ *
+ * So we use a bh handler to take care of close events. We also handle
+ * open events from this callback to make sure that a close directly followed
+ * by an open gets handled in the right order.
+ */
+static void usbredir_open_close_bh(void *opaque)
+{
+ USBRedirDevice *dev = opaque;
+
+ usbredir_device_disconnect(dev);
+
+ if (dev->parser) {
+ usbredirparser_destroy(dev->parser);
+ dev->parser = NULL;
+ }
+
+ if (dev->cs->opened) {
+ dev->parser = qemu_oom_check(usbredirparser_create());
+ dev->parser->priv = dev;
+ dev->parser->log_func = usbredir_log;
+ dev->parser->read_func = usbredir_read;
+ dev->parser->write_func = usbredir_write;
+ dev->parser->device_connect_func = usbredir_device_connect;
+ dev->parser->device_disconnect_func = usbredir_device_disconnect;
+ dev->parser->interface_info_func = usbredir_interface_info;
+ dev->parser->ep_info_func = usbredir_ep_info;
+ dev->parser->configuration_status_func = usbredir_configuration_status;
+ dev->parser->alt_setting_status_func = usbredir_alt_setting_status;
+ dev->parser->iso_stream_status_func = usbredir_iso_stream_status;
+ dev->parser->interrupt_receiving_status_func =
+ usbredir_interrupt_receiving_status;
+ dev->parser->bulk_streams_status_func = usbredir_bulk_streams_status;
+ dev->parser->control_packet_func = usbredir_control_packet;
+ dev->parser->bulk_packet_func = usbredir_bulk_packet;
+ dev->parser->iso_packet_func = usbredir_iso_packet;
+ dev->parser->interrupt_packet_func = usbredir_interrupt_packet;
+ dev->read_buf = NULL;
+ dev->read_buf_size = 0;
+ usbredirparser_init(dev->parser, VERSION, NULL, 0, 0);
+ usbredirparser_do_write(dev->parser);
+ }
+}
+
+static void usbredir_do_attach(void *opaque)
+{
+ USBRedirDevice *dev = opaque;
+
+ usb_device_attach(&dev->dev);
+}
+
+/*
+ * chardev callbacks
+ */
+
+static int usbredir_chardev_can_read(void *opaque)
+{
+ USBRedirDevice *dev = opaque;
+
+ if (dev->parser) {
+ /* usbredir_parser_do_read will consume *all* data we give it */
+ return 1024 * 1024;
+ } else {
+ /* usbredir_open_close_bh hasn't handled the open event yet */
+ return 0;
+ }
+}
+
+static void usbredir_chardev_read(void *opaque, const uint8_t *buf, int size)
+{
+ USBRedirDevice *dev = opaque;
+
+ /* No recursion allowed! */
+ assert(dev->read_buf == NULL);
+
+ dev->read_buf = buf;
+ dev->read_buf_size = size;
+
+ usbredirparser_do_read(dev->parser);
+ /* Send any acks, etc. which may be queued now */
+ usbredirparser_do_write(dev->parser);
+}
+
+static void usbredir_chardev_event(void *opaque, int event)
+{
+ USBRedirDevice *dev = opaque;
+
+ switch (event) {
+ case CHR_EVENT_OPENED:
+ case CHR_EVENT_CLOSED:
+ qemu_bh_schedule(dev->open_close_bh);
+ break;
+ }
+}
+
+/*
+ * init + destroy
+ */
+
+static int usbredir_initfn(USBDevice *udev)
+{
+ USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev);
+ int i;
+
+ if (dev->cs == NULL) {
+ qerror_report(QERR_MISSING_PARAMETER, "chardev");
+ return -1;
+ }
+
+ dev->open_close_bh = qemu_bh_new(usbredir_open_close_bh, dev);
+ dev->attach_timer = qemu_new_timer_ms(vm_clock, usbredir_do_attach, dev);
+
+ QTAILQ_INIT(&dev->asyncq);
+ for (i = 0; i < MAX_ENDPOINTS; i++) {
+ QTAILQ_INIT(&dev->endpoint[i].bufpq);
+ }
+
+ /* We'll do the attach once we receive the speed from the usb-host */
+ udev->auto_attach = 0;
+
+ qemu_chr_add_handlers(dev->cs, usbredir_chardev_can_read,
+ usbredir_chardev_read, usbredir_chardev_event, dev);
+
+ return 0;
+}
+
+static void usbredir_cleanup_device_queues(USBRedirDevice *dev)
+{
+ AsyncURB *aurb, *next_aurb;
+ int i;
+
+ QTAILQ_FOREACH_SAFE(aurb, &dev->asyncq, next, next_aurb) {
+ async_free(dev, aurb);
+ }
+ for (i = 0; i < MAX_ENDPOINTS; i++) {
+ usbredir_free_bufpq(dev, I2EP(i));
+ }
+}
+
+static void usbredir_handle_destroy(USBDevice *udev)
+{
+ USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev);
+
+ qemu_chr_close(dev->cs);
+ /* Note must be done after qemu_chr_close, as that causes a close event */
+ qemu_bh_delete(dev->open_close_bh);
+
+ qemu_del_timer(dev->attach_timer);
+ qemu_free_timer(dev->attach_timer);
+
+ usbredir_cleanup_device_queues(dev);
+
+ if (dev->parser) {
+ usbredirparser_destroy(dev->parser);
+ }
+}
+
+/*
+ * usbredirparser packet complete callbacks
+ */
+
+static int usbredir_handle_status(USBRedirDevice *dev,
+ int status, int actual_len)
+{
+ switch (status) {
+ case usb_redir_success:
+ return actual_len;
+ case usb_redir_stall:
+ return USB_RET_STALL;
+ case usb_redir_cancelled:
+ WARNING("returning cancelled packet to HC?\n");
+ case usb_redir_inval:
+ case usb_redir_ioerror:
+ case usb_redir_timeout:
+ default:
+ return USB_RET_NAK;
+ }
+}
+
+static void usbredir_device_connect(void *priv,
+ struct usb_redir_device_connect_header *device_connect)
+{
+ USBRedirDevice *dev = priv;
+
+ switch (device_connect->speed) {
+ case usb_redir_speed_low:
+ DPRINTF("attaching low speed device\n");
+ dev->dev.speed = USB_SPEED_LOW;
+ break;
+ case usb_redir_speed_full:
+ DPRINTF("attaching full speed device\n");
+ dev->dev.speed = USB_SPEED_FULL;
+ break;
+ case usb_redir_speed_high:
+ DPRINTF("attaching high speed device\n");
+ dev->dev.speed = USB_SPEED_HIGH;
+ break;
+ case usb_redir_speed_super:
+ DPRINTF("attaching super speed device\n");
+ dev->dev.speed = USB_SPEED_SUPER;
+ break;
+ default:
+ DPRINTF("attaching unknown speed device, assuming full speed\n");
+ dev->dev.speed = USB_SPEED_FULL;
+ }
+ dev->dev.speedmask = (1 << dev->dev.speed);
+ qemu_mod_timer(dev->attach_timer, dev->next_attach_time);
+}
+
+static void usbredir_device_disconnect(void *priv)
+{
+ USBRedirDevice *dev = priv;
+
+ /* Stop any pending attaches */
+ qemu_del_timer(dev->attach_timer);
+
+ if (dev->dev.attached) {
+ usb_device_detach(&dev->dev);
+ usbredir_cleanup_device_queues(dev);
+ /*
+ * Delay next usb device attach to give the guest a chance to see
+ * see the detach / attach in case of quick close / open succession
+ */
+ dev->next_attach_time = qemu_get_clock_ms(vm_clock) + 200;
+ }
+}
+
+static void usbredir_interface_info(void *priv,
+ struct usb_redir_interface_info_header *interface_info)
+{
+ /* The intention is to allow specifying acceptable interface classes
+ for redirection on the cmdline and in the future verify this here,
+ and disconnect (or never connect) the device if a not accepted
+ interface class is detected */
+}
+
+static void usbredir_ep_info(void *priv,
+ struct usb_redir_ep_info_header *ep_info)
+{
+ USBRedirDevice *dev = priv;
+ int i;
+
+ for (i = 0; i < MAX_ENDPOINTS; i++) {
+ dev->endpoint[i].type = ep_info->type[i];
+ dev->endpoint[i].interval = ep_info->interval[i];
+ dev->endpoint[i].interface = ep_info->interface[i];
+ if (dev->endpoint[i].type != usb_redir_type_invalid) {
+ DPRINTF("ep: %02X type: %d interface: %d\n", I2EP(i),
+ dev->endpoint[i].type, dev->endpoint[i].interface);
+ }
+ }
+}
+
+static void usbredir_configuration_status(void *priv, uint32_t id,
+ struct usb_redir_configuration_status_header *config_status)
+{
+ USBRedirDevice *dev = priv;
+ AsyncURB *aurb;
+ int len = 0;
+
+ DPRINTF("set config status %d config %d id %u\n", config_status->status,
+ config_status->configuration, id);
+
+ aurb = async_find(dev, id);
+ if (!aurb) {
+ return;
+ }
+ if (aurb->packet) {
+ if (aurb->get) {
+ dev->dev.data_buf[0] = config_status->configuration;
+ len = 1;
+ }
+ aurb->packet->result =
+ usbredir_handle_status(dev, config_status->status, len);
+ usb_generic_async_ctrl_complete(&dev->dev, aurb->packet);
+ }
+ async_free(dev, aurb);
+}
+
+static void usbredir_alt_setting_status(void *priv, uint32_t id,
+ struct usb_redir_alt_setting_status_header *alt_setting_status)
+{
+ USBRedirDevice *dev = priv;
+ AsyncURB *aurb;
+ int len = 0;
+
+ DPRINTF("alt status %d intf %d alt %d id: %u\n",
+ alt_setting_status->status,
+ alt_setting_status->interface,
+ alt_setting_status->alt, id);
+
+ aurb = async_find(dev, id);
+ if (!aurb) {
+ return;
+ }
+ if (aurb->packet) {
+ if (aurb->get) {
+ dev->dev.data_buf[0] = alt_setting_status->alt;
+ len = 1;
+ }
+ aurb->packet->result =
+ usbredir_handle_status(dev, alt_setting_status->status, len);
+ usb_generic_async_ctrl_complete(&dev->dev, aurb->packet);
+ }
+ async_free(dev, aurb);
+}
+
+static void usbredir_iso_stream_status(void *priv, uint32_t id,
+ struct usb_redir_iso_stream_status_header *iso_stream_status)
+{
+ USBRedirDevice *dev = priv;
+ uint8_t ep = iso_stream_status->endpoint;
+
+ DPRINTF("iso status %d ep %02X id %u\n", iso_stream_status->status,
+ ep, id);
+
+ dev->endpoint[EP2I(ep)].iso_error = iso_stream_status->status;
+ if (iso_stream_status->status == usb_redir_stall) {
+ DPRINTF("iso stream stopped by peer ep %02X\n", ep);
+ dev->endpoint[EP2I(ep)].iso_started = 0;
+ }
+}
+
+static void usbredir_interrupt_receiving_status(void *priv, uint32_t id,
+ struct usb_redir_interrupt_receiving_status_header
+ *interrupt_receiving_status)
+{
+ USBRedirDevice *dev = priv;
+ uint8_t ep = interrupt_receiving_status->endpoint;
+
+ DPRINTF("interrupt recv status %d ep %02X id %u\n",
+ interrupt_receiving_status->status, ep, id);
+
+ dev->endpoint[EP2I(ep)].interrupt_error =
+ interrupt_receiving_status->status;
+ if (interrupt_receiving_status->status == usb_redir_stall) {
+ DPRINTF("interrupt receiving stopped by peer ep %02X\n", ep);
+ dev->endpoint[EP2I(ep)].interrupt_started = 0;
+ }
+}
+
+static void usbredir_bulk_streams_status(void *priv, uint32_t id,
+ struct usb_redir_bulk_streams_status_header *bulk_streams_status)
+{
+}
+
+static void usbredir_control_packet(void *priv, uint32_t id,
+ struct usb_redir_control_packet_header *control_packet,
+ uint8_t *data, int data_len)
+{
+ USBRedirDevice *dev = priv;
+ int len = control_packet->length;
+ AsyncURB *aurb;
+
+ DPRINTF("ctrl-in status %d len %d id %u\n", control_packet->status,
+ len, id);
+
+ aurb = async_find(dev, id);
+ if (!aurb) {
+ free(data);
+ return;
+ }
+
+ aurb->control_packet.status = control_packet->status;
+ aurb->control_packet.length = control_packet->length;
+ if (memcmp(&aurb->control_packet, control_packet,
+ sizeof(*control_packet))) {
+ ERROR("return control packet mismatch, please report this!\n");
+ len = USB_RET_NAK;
+ }
+
+ if (aurb->packet) {
+ len = usbredir_handle_status(dev, control_packet->status, len);
+ if (len > 0) {
+ usbredir_log_data(dev, "ctrl data in:", data, data_len);
+ if (data_len <= sizeof(dev->dev.data_buf)) {
+ memcpy(dev->dev.data_buf, data, data_len);
+ } else {
+ ERROR("ctrl buffer too small (%d > %zu)\n",
+ data_len, sizeof(dev->dev.data_buf));
+ len = USB_RET_STALL;
+ }
+ }
+ aurb->packet->result = len;
+ usb_generic_async_ctrl_complete(&dev->dev, aurb->packet);
+ }
+ async_free(dev, aurb);
+ free(data);
+}
+
+static void usbredir_bulk_packet(void *priv, uint32_t id,
+ struct usb_redir_bulk_packet_header *bulk_packet,
+ uint8_t *data, int data_len)
+{
+ USBRedirDevice *dev = priv;
+ uint8_t ep = bulk_packet->endpoint;
+ int len = bulk_packet->length;
+ AsyncURB *aurb;
+
+ DPRINTF("bulk-in status %d ep %02X len %d id %u\n", bulk_packet->status,
+ ep, len, id);
+
+ aurb = async_find(dev, id);
+ if (!aurb) {
+ free(data);
+ return;
+ }
+
+ if (aurb->bulk_packet.endpoint != bulk_packet->endpoint ||
+ aurb->bulk_packet.stream_id != bulk_packet->stream_id) {
+ ERROR("return bulk packet mismatch, please report this!\n");
+ len = USB_RET_NAK;
+ }
+
+ if (aurb->packet) {
+ len = usbredir_handle_status(dev, bulk_packet->status, len);
+ if (len > 0) {
+ usbredir_log_data(dev, "bulk data in:", data, data_len);
+ if (data_len <= aurb->packet->iov.size) {
+ usb_packet_copy(aurb->packet, data, data_len);
+ } else {
+ ERROR("bulk buffer too small (%d > %zd)\n", data_len,
+ aurb->packet->iov.size);
+ len = USB_RET_STALL;
+ }
+ }
+ aurb->packet->result = len;
+ usb_packet_complete(&dev->dev, aurb->packet);
+ }
+ async_free(dev, aurb);
+ free(data);
+}
+
+static void usbredir_iso_packet(void *priv, uint32_t id,
+ struct usb_redir_iso_packet_header *iso_packet,
+ uint8_t *data, int data_len)
+{
+ USBRedirDevice *dev = priv;
+ uint8_t ep = iso_packet->endpoint;
+
+ DPRINTF2("iso-in status %d ep %02X len %d id %u\n", iso_packet->status, ep,
+ data_len, id);
+
+ if (dev->endpoint[EP2I(ep)].type != USB_ENDPOINT_XFER_ISOC) {
+ ERROR("received iso packet for non iso endpoint %02X\n", ep);
+ free(data);
+ return;
+ }
+
+ if (dev->endpoint[EP2I(ep)].iso_started == 0) {
+ DPRINTF("received iso packet for non started stream ep %02X\n", ep);
+ free(data);
+ return;
+ }
+
+ /* bufp_alloc also adds the packet to the ep queue */
+ bufp_alloc(dev, data, data_len, iso_packet->status, ep);
+}
+
+static void usbredir_interrupt_packet(void *priv, uint32_t id,
+ struct usb_redir_interrupt_packet_header *interrupt_packet,
+ uint8_t *data, int data_len)
+{
+ USBRedirDevice *dev = priv;
+ uint8_t ep = interrupt_packet->endpoint;
+
+ DPRINTF("interrupt-in status %d ep %02X len %d id %u\n",
+ interrupt_packet->status, ep, data_len, id);
+
+ if (dev->endpoint[EP2I(ep)].type != USB_ENDPOINT_XFER_INT) {
+ ERROR("received int packet for non interrupt endpoint %02X\n", ep);
+ free(data);
+ return;
+ }
+
+ if (ep & USB_DIR_IN) {
+ if (dev->endpoint[EP2I(ep)].interrupt_started == 0) {
+ DPRINTF("received int packet while not started ep %02X\n", ep);
+ free(data);
+ return;
+ }
+
+ /* bufp_alloc also adds the packet to the ep queue */
+ bufp_alloc(dev, data, data_len, interrupt_packet->status, ep);
+ } else {
+ int len = interrupt_packet->length;
+
+ AsyncURB *aurb = async_find(dev, id);
+ if (!aurb) {
+ return;
+ }
+
+ if (aurb->interrupt_packet.endpoint != interrupt_packet->endpoint) {
+ ERROR("return int packet mismatch, please report this!\n");
+ len = USB_RET_NAK;
+ }
+
+ if (aurb->packet) {
+ aurb->packet->result = usbredir_handle_status(dev,
+ interrupt_packet->status, len);
+ usb_packet_complete(&dev->dev, aurb->packet);
+ }
+ async_free(dev, aurb);
+ }
+}
+
+static struct USBDeviceInfo usbredir_dev_info = {
+ .product_desc = "USB Redirection Device",
+ .qdev.name = "usb-redir",
+ .qdev.size = sizeof(USBRedirDevice),
+ .init = usbredir_initfn,
+ .handle_destroy = usbredir_handle_destroy,
+ .handle_packet = usb_generic_handle_packet,
+ .cancel_packet = usbredir_cancel_packet,
+ .handle_reset = usbredir_handle_reset,
+ .handle_data = usbredir_handle_data,
+ .handle_control = usbredir_handle_control,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_CHR("chardev", USBRedirDevice, cs),
+ DEFINE_PROP_UINT8("debug", USBRedirDevice, debug, 0),
+ DEFINE_PROP_END_OF_LIST(),
+ },
+};
+
+static void usbredir_register_devices(void)
+{
+ usb_qdev_register(&usbredir_dev_info);
+}
+device_init(usbredir_register_devices);
diff --git a/user-exec.c b/user-exec.c
index 02c2f8b..14c0f25 100644
--- a/user-exec.c
+++ b/user-exec.c
@@ -17,7 +17,8 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
-#include "exec.h"
+#include "cpu.h"
+#include "dyngen-exec.h"
#include "disas.h"
#include "tcg.h"
@@ -40,7 +41,7 @@
static void exception_action(CPUState *env1)
{
#if defined(TARGET_I386)
- raise_exception_err(env1->exception_index, env1->error_code);
+ raise_exception_err_env(env1, env1->exception_index, env1->error_code);
#else
cpu_loop_exit(env1);
#endif
@@ -628,47 +629,3 @@ int cpu_signal_handler(int host_signum, void *pinfo,
#error host CPU specific signal handler needed
#endif
-
-#if defined(TARGET_I386)
-
-void cpu_x86_load_seg(CPUX86State *s, int seg_reg, int selector)
-{
- CPUX86State *saved_env;
-
- saved_env = env;
- env = s;
- if (!(env->cr[0] & CR0_PE_MASK) || (env->eflags & VM_MASK)) {
- selector &= 0xffff;
- cpu_x86_load_seg_cache(env, seg_reg, selector,
- (selector << 4), 0xffff, 0);
- } else {
- helper_load_seg(seg_reg, selector);
- }
- env = saved_env;
-}
-
-void cpu_x86_fsave(CPUX86State *s, target_ulong ptr, int data32)
-{
- CPUX86State *saved_env;
-
- saved_env = env;
- env = s;
-
- helper_fsave(ptr, data32);
-
- env = saved_env;
-}
-
-void cpu_x86_frstor(CPUX86State *s, target_ulong ptr, int data32)
-{
- CPUX86State *saved_env;
-
- saved_env = env;
- env = s;
-
- helper_frstor(ptr, data32);
-
- env = saved_env;
-}
-
-#endif /* TARGET_I386 */
diff --git a/vl.c b/vl.c
index fcd7395..426cea7 100644
--- a/vl.c
+++ b/vl.c
@@ -228,6 +228,9 @@ int ctrl_grab = 0;
unsigned int nb_prom_envs = 0;
const char *prom_envs[MAX_PROM_ENVS];
int boot_menu;
+uint8_t *boot_splash_filedata;
+int boot_splash_filedata_size;
+uint8_t qemu_extra_params_fw[2];
typedef struct FWBootEntry FWBootEntry;
@@ -293,6 +296,14 @@ static struct {
{ .driver = "qxl-vga", .flag = &default_vga },
};
+static void res_free(void)
+{
+ if (boot_splash_filedata != NULL) {
+ qemu_free(boot_splash_filedata);
+ boot_splash_filedata = NULL;
+ }
+}
+
static int default_driver_check(QemuOpts *opts, void *opaque)
{
const char *driver = qemu_opt_get(opts, "driver");
@@ -1398,7 +1409,6 @@ static void main_loop(void)
monitor_protocol_event(QEVENT_SHUTDOWN, NULL);
if (no_shutdown) {
vm_stop(VMSTOP_SHUTDOWN);
- no_shutdown = 0;
} else
break;
}
@@ -1899,6 +1909,27 @@ static int debugcon_parse(const char *devname)
return 0;
}
+static QEMUMachine *machine_parse(const char *name)
+{
+ QEMUMachine *m, *machine = NULL;
+
+ if (name) {
+ machine = find_machine(name);
+ }
+ if (machine) {
+ return machine;
+ }
+ printf("Supported machines are:\n");
+ for (m = first_machine; m != NULL; m = m->next) {
+ if (m->alias) {
+ printf("%-10s %s (alias of %s)\n", m->alias, m->desc, m->name);
+ }
+ printf("%-10s %s%s\n", m->name, m->desc,
+ m->is_default ? " (default)" : "");
+ }
+ exit(!name || *name != '?');
+}
+
static int tcg_init(void)
{
return 0;
@@ -1989,7 +2020,7 @@ void qemu_remove_exit_notifier(Notifier *notify)
static void qemu_run_exit_notifiers(void)
{
- notifier_list_notify(&exit_notifiers);
+ notifier_list_notify(&exit_notifiers, NULL);
}
void qemu_add_machine_init_done_notifier(Notifier *notify)
@@ -1999,7 +2030,7 @@ void qemu_add_machine_init_done_notifier(Notifier *notify)
static void qemu_run_machine_init_done_notifiers(void)
{
- notifier_list_notify(&machine_init_done_notifiers);
+ notifier_list_notify(&machine_init_done_notifiers, NULL);
}
static const QEMUOption *lookup_opt(int argc, char **argv,
@@ -2155,20 +2186,7 @@ int main(int argc, char **argv, char **envp)
}
switch(popt->index) {
case QEMU_OPTION_M:
- machine = find_machine(optarg);
- if (!machine) {
- QEMUMachine *m;
- printf("Supported machines are:\n");
- for(m = first_machine; m != NULL; m = m->next) {
- if (m->alias)
- printf("%-10s %s (alias of %s)\n",
- m->alias, m->desc, m->name);
- printf("%-10s %s%s\n",
- m->name, m->desc,
- m->is_default ? " (default)" : "");
- }
- exit(*optarg != '?');
- }
+ machine = machine_parse(optarg);
break;
case QEMU_OPTION_cpu:
/* hw initialization will check this */
@@ -2323,7 +2341,8 @@ int main(int argc, char **argv, char **envp)
case QEMU_OPTION_boot:
{
static const char * const params[] = {
- "order", "once", "menu", NULL
+ "order", "once", "menu",
+ "splash", "splash-time", NULL
};
char buf[sizeof(boot_devices)];
char *standard_boot_devices;
@@ -2366,6 +2385,8 @@ int main(int argc, char **argv, char **envp)
exit(1);
}
}
+ qemu_opts_parse(qemu_find_opts("boot-opts"),
+ optarg, 0);
}
}
break;
@@ -2433,11 +2454,6 @@ int main(int argc, char **argv, char **envp)
exit(1);
}
- /* On 32-bit hosts, QEMU is limited by virtual address space */
- if (value > (2047 << 20) && HOST_LONG_BITS == 32) {
- fprintf(stderr, "qemu: at most 2047 MB RAM can be simulated\n");
- exit(1);
- }
if (value != (uint64_t)(ram_addr_t)value) {
fprintf(stderr, "qemu: ram size too large\n");
exit(1);
@@ -2698,11 +2714,15 @@ int main(int argc, char **argv, char **envp)
case QEMU_OPTION_machine:
olist = qemu_find_opts("machine");
qemu_opts_reset(olist);
- opts = qemu_opts_parse(olist, optarg, 0);
+ opts = qemu_opts_parse(olist, optarg, 1);
if (!opts) {
fprintf(stderr, "parse error: %s\n", optarg);
exit(1);
}
+ optarg = qemu_opt_get(opts, "type");
+ if (optarg) {
+ machine = machine_parse(optarg);
+ }
break;
case QEMU_OPTION_usb:
usb_enabled = 1;
@@ -2976,8 +2996,8 @@ int main(int argc, char **argv, char **envp)
p = qemu_opt_get(QTAILQ_FIRST(&list->head), "accel");
}
if (p == NULL) {
- opts = qemu_opts_parse(qemu_find_opts("machine"),
- machine->default_machine_opts, 0);
+ qemu_opts_reset(list);
+ opts = qemu_opts_parse(list, machine->default_machine_opts, 0);
if (!opts) {
fprintf(stderr, "parse error for machine %s: %s\n",
machine->name, machine->default_machine_opts);
@@ -3091,8 +3111,17 @@ int main(int argc, char **argv, char **envp)
exit(1);
/* init the memory */
- if (ram_size == 0)
+ if (ram_size == 0) {
ram_size = DEFAULT_RAM_SIZE * 1024 * 1024;
+ }
+
+ if (!xen_enabled()) {
+ /* On 32-bit hosts, QEMU is limited by virtual address space */
+ if (ram_size > (2047 << 20) && HOST_LONG_BITS == 32) {
+ fprintf(stderr, "qemu: at most 2047 MB RAM can be simulated\n");
+ exit(1);
+ }
+ }
/* init the dynamic translator */
cpu_exec_init_all(tb_size * 1024 * 1024);
@@ -3120,8 +3149,8 @@ int main(int argc, char **argv, char **envp)
if (nb_numa_nodes > 0) {
int i;
- if (nb_numa_nodes > smp_cpus) {
- nb_numa_nodes = smp_cpus;
+ if (nb_numa_nodes > MAX_NODES) {
+ nb_numa_nodes = MAX_NODES;
}
/* If no memory size if given for any node, assume the default case
@@ -3327,6 +3356,7 @@ int main(int argc, char **argv, char **envp)
main_loop();
quit_timers();
net_cleanup();
+ res_free();
return 0;
}
diff --git a/xen-all.c b/xen-all.c
index fcb106f..9eaeac1 100644
--- a/xen-all.c
+++ b/xen-all.c
@@ -19,6 +19,7 @@
#include <xen/hvm/ioreq.h>
#include <xen/hvm/params.h>
+#include <xen/hvm/e820.h>
//#define DEBUG_XEN
@@ -144,6 +145,12 @@ static void xen_ram_init(ram_addr_t ram_size)
new_block->host = NULL;
new_block->offset = 0;
new_block->length = ram_size;
+ if (ram_size >= HVM_BELOW_4G_RAM_END) {
+ /* Xen does not allocate the memory continuously, and keep a hole at
+ * HVM_BELOW_4G_MMIO_START of HVM_BELOW_4G_MMIO_LENGTH
+ */
+ new_block->length += HVM_BELOW_4G_MMIO_LENGTH;
+ }
QLIST_INSERT_HEAD(&ram_list.blocks, new_block, next);
@@ -152,20 +159,26 @@ static void xen_ram_init(ram_addr_t ram_size)
memset(ram_list.phys_dirty + (new_block->offset >> TARGET_PAGE_BITS),
0xff, new_block->length >> TARGET_PAGE_BITS);
- if (ram_size >= 0xe0000000 ) {
- above_4g_mem_size = ram_size - 0xe0000000;
- below_4g_mem_size = 0xe0000000;
+ if (ram_size >= HVM_BELOW_4G_RAM_END) {
+ above_4g_mem_size = ram_size - HVM_BELOW_4G_RAM_END;
+ below_4g_mem_size = HVM_BELOW_4G_RAM_END;
} else {
below_4g_mem_size = ram_size;
}
- cpu_register_physical_memory(0, below_4g_mem_size, new_block->offset);
-#if TARGET_PHYS_ADDR_BITS > 32
+ cpu_register_physical_memory(0, 0xa0000, 0);
+ /* Skip of the VGA IO memory space, it will be registered later by the VGA
+ * emulated device.
+ *
+ * The area between 0xc0000 and 0x100000 will be used by SeaBIOS to load
+ * the Options ROM, so it is registered here as RAM.
+ */
+ cpu_register_physical_memory(0xc0000, below_4g_mem_size - 0xc0000,
+ 0xc0000);
if (above_4g_mem_size > 0) {
cpu_register_physical_memory(0x100000000ULL, above_4g_mem_size,
- new_block->offset + below_4g_mem_size);
+ 0x100000000ULL);
}
-#endif
}
void xen_ram_alloc(ram_addr_t ram_addr, ram_addr_t size)
@@ -184,7 +197,7 @@ void xen_ram_alloc(ram_addr_t ram_addr, ram_addr_t size)
}
if (xc_domain_populate_physmap_exact(xen_xc, xen_domid, nr_pfn, 0, 0, pfn_list)) {
- hw_error("xen: failed to populate ram at %lx", ram_addr);
+ hw_error("xen: failed to populate ram at " RAM_ADDR_FMT, ram_addr);
}
qemu_free(pfn_list);
@@ -644,7 +657,7 @@ static void handle_ioreq(ioreq_t *req)
case IOREQ_TYPE_TIMEOFFSET:
break;
case IOREQ_TYPE_INVALIDATE:
- qemu_invalidate_map_cache();
+ xen_invalidate_map_cache();
break;
default:
hw_error("Invalid ioreq type 0x%x\n", req->type);
@@ -737,12 +750,77 @@ static void cpu_handle_ioreq(void *opaque)
}
}
-static void xenstore_record_dm_state(XenIOState *s, const char *state)
+static int store_dev_info(int domid, CharDriverState *cs, const char *string)
+{
+ struct xs_handle *xs = NULL;
+ char *path = NULL;
+ char *newpath = NULL;
+ char *pts = NULL;
+ int ret = -1;
+
+ /* Only continue if we're talking to a pty. */
+ if (strncmp(cs->filename, "pty:", 4)) {
+ return 0;
+ }
+ pts = cs->filename + 4;
+
+ /* We now have everything we need to set the xenstore entry. */
+ xs = xs_open(0);
+ if (xs == NULL) {
+ fprintf(stderr, "Could not contact XenStore\n");
+ goto out;
+ }
+
+ path = xs_get_domain_path(xs, domid);
+ if (path == NULL) {
+ fprintf(stderr, "xs_get_domain_path() error\n");
+ goto out;
+ }
+ newpath = realloc(path, (strlen(path) + strlen(string) +
+ strlen("/tty") + 1));
+ if (newpath == NULL) {
+ fprintf(stderr, "realloc error\n");
+ goto out;
+ }
+ path = newpath;
+
+ strcat(path, string);
+ strcat(path, "/tty");
+ if (!xs_write(xs, XBT_NULL, path, pts, strlen(pts))) {
+ fprintf(stderr, "xs_write for '%s' fail", string);
+ goto out;
+ }
+ ret = 0;
+
+out:
+ free(path);
+ xs_close(xs);
+
+ return ret;
+}
+
+void xenstore_store_pv_console_info(int i, CharDriverState *chr)
+{
+ if (i == 0) {
+ store_dev_info(xen_domid, chr, "/console");
+ } else {
+ char buf[32];
+ snprintf(buf, sizeof(buf), "/device/console/%d", i);
+ store_dev_info(xen_domid, chr, buf);
+ }
+}
+
+static void xenstore_record_dm_state(struct xs_handle *xs, const char *state)
{
char path[50];
+ if (xs == NULL) {
+ fprintf(stderr, "xenstore connection not initialized\n");
+ exit(1);
+ }
+
snprintf(path, sizeof (path), "/local/domain/0/device-model/%u/state", xen_domid);
- if (!xs_write(s->xenstore, XBT_NULL, path, state, strlen(state))) {
+ if (!xs_write(xs, XBT_NULL, path, state, strlen(state))) {
fprintf(stderr, "error recording dm state\n");
exit(1);
}
@@ -763,15 +841,20 @@ static void xen_main_loop_prepare(XenIOState *state)
if (evtchn_fd != -1) {
qemu_set_fd_handler(evtchn_fd, cpu_handle_ioreq, NULL, state);
}
-
- /* record state running */
- xenstore_record_dm_state(state, "running");
}
/* Initialise Xen */
-static void xen_vm_change_state_handler(void *opaque, int running, int reason)
+static void xen_change_state_handler(void *opaque, int running, int reason)
+{
+ if (running) {
+ /* record state running */
+ xenstore_record_dm_state(xenstore, "running");
+ }
+}
+
+static void xen_hvm_change_state_handler(void *opaque, int running, int reason)
{
XenIOState *state = opaque;
if (running) {
@@ -779,7 +862,7 @@ static void xen_vm_change_state_handler(void *opaque, int running, int reason)
}
}
-static void xen_exit_notifier(Notifier *n)
+static void xen_exit_notifier(Notifier *n, void *data)
{
XenIOState *state = container_of(n, XenIOState, exit);
@@ -794,6 +877,7 @@ int xen_init(void)
xen_be_printf(NULL, 0, "can't open xen interface\n");
return -1;
}
+ qemu_add_vm_change_state_handler(xen_change_state_handler, NULL);
return 0;
}
@@ -852,16 +936,25 @@ int xen_hvm_init(void)
}
/* Init RAM management */
- qemu_map_cache_init();
+ xen_map_cache_init();
xen_ram_init(ram_size);
- qemu_add_vm_change_state_handler(xen_vm_change_state_handler, state);
+ qemu_add_vm_change_state_handler(xen_hvm_change_state_handler, state);
state->client = xen_cpu_phys_memory_client;
QLIST_INIT(&state->physmap);
cpu_register_phys_memory_client(&state->client);
state->log_for_dirtybit = NULL;
+ /* Initialize backend core & drivers */
+ if (xen_be_init() != 0) {
+ fprintf(stderr, "%s: xen backend core setup failed\n", __FUNCTION__);
+ exit(1);
+ }
+ xen_be_register("console", &xen_console_ops);
+ xen_be_register("vkbd", &xen_kbdmouse_ops);
+ xen_be_register("qdisk", &xen_blkdev_ops);
+
return 0;
}
diff --git a/xen-mapcache-stub.c b/xen-mapcache-stub.c
deleted file mode 100644
index 90a994d..0000000
--- a/xen-mapcache-stub.c
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2011 Citrix Ltd.
- *
- * This work is licensed under the terms of the GNU GPL, version 2. See
- * the COPYING file in the top-level directory.
- *
- */
-
-#include "config.h"
-
-#include "cpu.h"
-#include "qemu-common.h"
-#include "cpu-common.h"
-#include "xen-mapcache.h"
-
-void qemu_map_cache_init(void)
-{
-}
-
-uint8_t *qemu_map_cache(target_phys_addr_t phys_addr, target_phys_addr_t size, uint8_t lock)
-{
- return qemu_get_ram_ptr(phys_addr);
-}
-
-ram_addr_t qemu_ram_addr_from_mapcache(void *ptr)
-{
- return -1;
-}
-
-void qemu_invalidate_map_cache(void)
-{
-}
-
-void qemu_invalidate_entry(uint8_t *buffer)
-{
-}
diff --git a/xen-mapcache.c b/xen-mapcache.c
index fac47cd..15d1241 100644
--- a/xen-mapcache.c
+++ b/xen-mapcache.c
@@ -40,6 +40,9 @@
#endif
#define MCACHE_BUCKET_SIZE (1UL << MCACHE_BUCKET_SHIFT)
+#define mapcache_lock() ((void)0)
+#define mapcache_unlock() ((void)0)
+
typedef struct MapCacheEntry {
target_phys_addr_t paddr_index;
uint8_t *vaddr_base;
@@ -79,7 +82,7 @@ static inline int test_bits(int nr, int size, const unsigned long *addr)
return 0;
}
-void qemu_map_cache_init(void)
+void xen_map_cache_init(void)
{
unsigned long size;
struct rlimit rlimit_as;
@@ -106,13 +109,14 @@ void qemu_map_cache_init(void)
size = mapcache->nr_buckets * sizeof (MapCacheEntry);
size = (size + XC_PAGE_SIZE - 1) & ~(XC_PAGE_SIZE - 1);
- DPRINTF("qemu_map_cache_init, nr_buckets = %lx size %lu\n", mapcache->nr_buckets, size);
+ DPRINTF("%s, nr_buckets = %lx size %lu\n", __func__,
+ mapcache->nr_buckets, size);
mapcache->entry = qemu_mallocz(size);
}
-static void qemu_remap_bucket(MapCacheEntry *entry,
- target_phys_addr_t size,
- target_phys_addr_t address_index)
+static void xen_remap_bucket(MapCacheEntry *entry,
+ target_phys_addr_t size,
+ target_phys_addr_t address_index)
{
uint8_t *vaddr_base;
xen_pfn_t *pfns;
@@ -120,7 +124,7 @@ static void qemu_remap_bucket(MapCacheEntry *entry,
unsigned int i;
target_phys_addr_t nb_pfn = size >> XC_PAGE_SHIFT;
- trace_qemu_remap_bucket(address_index);
+ trace_xen_remap_bucket(address_index);
pfns = qemu_mallocz(nb_pfn * sizeof (xen_pfn_t));
err = qemu_mallocz(nb_pfn * sizeof (int));
@@ -164,17 +168,18 @@ static void qemu_remap_bucket(MapCacheEntry *entry,
qemu_free(err);
}
-uint8_t *qemu_map_cache(target_phys_addr_t phys_addr, target_phys_addr_t size, uint8_t lock)
+uint8_t *xen_map_cache(target_phys_addr_t phys_addr, target_phys_addr_t size,
+ uint8_t lock)
{
MapCacheEntry *entry, *pentry = NULL;
target_phys_addr_t address_index = phys_addr >> MCACHE_BUCKET_SHIFT;
target_phys_addr_t address_offset = phys_addr & (MCACHE_BUCKET_SIZE - 1);
target_phys_addr_t __size = size;
- trace_qemu_map_cache(phys_addr);
+ trace_xen_map_cache(phys_addr);
if (address_index == mapcache->last_address_index && !lock && !__size) {
- trace_qemu_map_cache_return(mapcache->last_address_vaddr + address_offset);
+ trace_xen_map_cache_return(mapcache->last_address_vaddr + address_offset);
return mapcache->last_address_vaddr + address_offset;
}
@@ -198,20 +203,20 @@ uint8_t *qemu_map_cache(target_phys_addr_t phys_addr, target_phys_addr_t size, u
if (!entry) {
entry = qemu_mallocz(sizeof (MapCacheEntry));
pentry->next = entry;
- qemu_remap_bucket(entry, __size, address_index);
+ xen_remap_bucket(entry, __size, address_index);
} else if (!entry->lock) {
if (!entry->vaddr_base || entry->paddr_index != address_index ||
entry->size != __size ||
!test_bits(address_offset >> XC_PAGE_SHIFT, size >> XC_PAGE_SHIFT,
entry->valid_mapping)) {
- qemu_remap_bucket(entry, __size, address_index);
+ xen_remap_bucket(entry, __size, address_index);
}
}
if(!test_bits(address_offset >> XC_PAGE_SHIFT, size >> XC_PAGE_SHIFT,
entry->valid_mapping)) {
mapcache->last_address_index = -1;
- trace_qemu_map_cache_return(NULL);
+ trace_xen_map_cache_return(NULL);
return NULL;
}
@@ -226,13 +231,13 @@ uint8_t *qemu_map_cache(target_phys_addr_t phys_addr, target_phys_addr_t size, u
QTAILQ_INSERT_HEAD(&mapcache->locked_entries, reventry, next);
}
- trace_qemu_map_cache_return(mapcache->last_address_vaddr + address_offset);
+ trace_xen_map_cache_return(mapcache->last_address_vaddr + address_offset);
return mapcache->last_address_vaddr + address_offset;
}
-ram_addr_t qemu_ram_addr_from_mapcache(void *ptr)
+ram_addr_t xen_ram_addr_from_mapcache(void *ptr)
{
- MapCacheEntry *entry = NULL, *pentry = NULL;
+ MapCacheEntry *entry = NULL;
MapCacheRev *reventry;
target_phys_addr_t paddr_index;
target_phys_addr_t size;
@@ -247,7 +252,7 @@ ram_addr_t qemu_ram_addr_from_mapcache(void *ptr)
}
}
if (!found) {
- fprintf(stderr, "qemu_ram_addr_from_mapcache, could not find %p\n", ptr);
+ fprintf(stderr, "%s, could not find %p\n", __func__, ptr);
QTAILQ_FOREACH(reventry, &mapcache->locked_entries, next) {
DPRINTF(" "TARGET_FMT_plx" -> %p is present\n", reventry->paddr_index,
reventry->vaddr_req);
@@ -258,7 +263,6 @@ ram_addr_t qemu_ram_addr_from_mapcache(void *ptr)
entry = &mapcache->entry[paddr_index % mapcache->nr_buckets];
while (entry && (entry->paddr_index != paddr_index || entry->size != size)) {
- pentry = entry;
entry = entry->next;
}
if (!entry) {
@@ -269,7 +273,7 @@ ram_addr_t qemu_ram_addr_from_mapcache(void *ptr)
((unsigned long) ptr - (unsigned long) entry->vaddr_base);
}
-void qemu_invalidate_entry(uint8_t *buffer)
+void xen_invalidate_map_cache_entry(uint8_t *buffer)
{
MapCacheEntry *entry = NULL, *pentry = NULL;
MapCacheRev *reventry;
@@ -290,7 +294,7 @@ void qemu_invalidate_entry(uint8_t *buffer)
}
}
if (!found) {
- DPRINTF("qemu_invalidate_entry, could not find %p\n", buffer);
+ DPRINTF("%s, could not find %p\n", __func__, buffer);
QTAILQ_FOREACH(reventry, &mapcache->locked_entries, next) {
DPRINTF(" "TARGET_FMT_plx" -> %p is present\n", reventry->paddr_index, reventry->vaddr_req);
}
@@ -322,7 +326,7 @@ void qemu_invalidate_entry(uint8_t *buffer)
qemu_free(entry);
}
-void qemu_invalidate_map_cache(void)
+void xen_invalidate_map_cache(void)
{
unsigned long i;
MapCacheRev *reventry;
diff --git a/xen-mapcache.h b/xen-mapcache.h
index 6216cc3..da874ca 100644
--- a/xen-mapcache.h
+++ b/xen-mapcache.h
@@ -9,13 +9,43 @@
#ifndef XEN_MAPCACHE_H
#define XEN_MAPCACHE_H
-void qemu_map_cache_init(void);
-uint8_t *qemu_map_cache(target_phys_addr_t phys_addr, target_phys_addr_t size, uint8_t lock);
-ram_addr_t qemu_ram_addr_from_mapcache(void *ptr);
-void qemu_invalidate_entry(uint8_t *buffer);
-void qemu_invalidate_map_cache(void);
-
-#define mapcache_lock() ((void)0)
-#define mapcache_unlock() ((void)0)
+#include <stdlib.h>
+
+#ifdef CONFIG_XEN
+
+void xen_map_cache_init(void);
+uint8_t *xen_map_cache(target_phys_addr_t phys_addr, target_phys_addr_t size,
+ uint8_t lock);
+ram_addr_t xen_ram_addr_from_mapcache(void *ptr);
+void xen_invalidate_map_cache_entry(uint8_t *buffer);
+void xen_invalidate_map_cache(void);
+
+#else
+
+static inline void xen_map_cache_init(void)
+{
+}
+
+static inline uint8_t *xen_map_cache(target_phys_addr_t phys_addr,
+ target_phys_addr_t size,
+ uint8_t lock)
+{
+ abort();
+}
+
+static inline ram_addr_t xen_ram_addr_from_mapcache(void *ptr)
+{
+ abort();
+}
+
+static inline void xen_invalidate_map_cache_entry(uint8_t *buffer)
+{
+}
+
+static inline void xen_invalidate_map_cache(void)
+{
+}
+
+#endif
#endif /* !XEN_MAPCACHE_H */
diff --git a/xen-stub.c b/xen-stub.c
index a4f35a1..efe2ab5 100644
--- a/xen-stub.c
+++ b/xen-stub.c
@@ -9,6 +9,10 @@
#include "qemu-common.h"
#include "hw/xen.h"
+void xenstore_store_pv_console_info(int i, CharDriverState *chr)
+{
+}
+
int xen_pci_slot_get_pirq(PCIDevice *pci_dev, int irq_num)
{
return -1;