summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--.gitignore22
-rw-r--r--MAINTAINERS9
-rw-r--r--Makefile158
-rw-r--r--Makefile.objs104
-rw-r--r--Makefile.target14
-rw-r--r--aio-posix.c2
-rw-r--r--backends/baum.c97
-rw-r--r--backends/msmouse.c72
-rw-r--r--backends/rng-egd.c4
-rw-r--r--backends/testdev.c48
-rw-r--r--balloon.c2
-rw-r--r--block.c2
-rw-r--r--block/Makefile.objs1
-rw-r--r--block/file-posix.c19
-rw-r--r--block/iscsi-opts.c69
-rw-r--r--block/iscsi.c8
-rw-r--r--block/sheepdog.c289
-rw-r--r--block/trace-events2
-rw-r--r--blockdev-nbd.c1
-rw-r--r--blockdev.c2
-rw-r--r--blockjob.c1
-rw-r--r--chardev/Makefile.objs17
-rw-r--r--chardev/char-console.c53
-rw-r--r--chardev/char-fd.c170
-rw-r--r--chardev/char-fd.h44
-rw-r--r--chardev/char-file.c139
-rw-r--r--chardev/char-io.c192
-rw-r--r--chardev/char-io.h46
-rw-r--r--chardev/char-mux.c358
-rw-r--r--chardev/char-mux.h63
-rw-r--r--chardev/char-null.c54
-rw-r--r--chardev/char-parallel.c316
-rw-r--r--chardev/char-parallel.h32
-rw-r--r--chardev/char-pipe.c191
-rw-r--r--chardev/char-pty.c300
-rw-r--r--chardev/char-ringbuf.c249
-rw-r--r--chardev/char-serial.c318
-rw-r--r--chardev/char-serial.h35
-rw-r--r--chardev/char-socket.c1017
-rw-r--r--chardev/char-stdio.c164
-rw-r--r--chardev/char-udp.c233
-rw-r--r--chardev/char-win-stdio.c266
-rw-r--r--chardev/char-win-stdio.h29
-rw-r--r--chardev/char-win.c265
-rw-r--r--chardev/char-win.h53
-rw-r--r--chardev/char.c1334
-rw-r--r--cpu-exec.c4
-rw-r--r--default-configs/arm-softmmu.mak1
-rw-r--r--default-configs/i386-softmmu.mak2
-rw-r--r--default-configs/ppc-softmmu.mak2
-rw-r--r--default-configs/ppc64-softmmu.mak1
-rw-r--r--default-configs/x86_64-softmmu.mak2
-rw-r--r--disas/ppc.c12
-rw-r--r--dma-helpers.c2
-rw-r--r--docs/replay.txt16
-rw-r--r--docs/tracing.txt55
-rw-r--r--exec.c4
-rw-r--r--fpu/softfloat-specialize.h2
-rw-r--r--gdbstub.c45
-rw-r--r--hmp-commands-info.hx6
-rw-r--r--hmp.c1
-rw-r--r--hw/arm/fsl-imx25.c2
-rw-r--r--hw/arm/fsl-imx31.c2
-rw-r--r--hw/arm/fsl-imx6.c2
-rw-r--r--hw/arm/nseries.c2
-rw-r--r--hw/arm/omap2.c2
-rw-r--r--hw/arm/pxa2xx.c2
-rw-r--r--hw/arm/virt.c2
-rw-r--r--hw/block/dataplane/trace-events5
-rw-r--r--hw/block/nvme.c2
-rw-r--r--hw/block/trace-events5
-rw-r--r--hw/bt/hci-csr.c64
-rw-r--r--hw/char/escc.c2
-rw-r--r--hw/char/exynos4210_uart.c2
-rw-r--r--hw/char/imx_serial.c2
-rw-r--r--hw/char/mcf_uart.c4
-rw-r--r--hw/char/omap_uart.c6
-rw-r--r--hw/char/parallel.c2
-rw-r--r--hw/char/serial-isa.c2
-rw-r--r--hw/char/serial.c4
-rw-r--r--hw/char/sh_serial.c2
-rw-r--r--hw/char/spapr_vty.c2
-rw-r--r--hw/char/virtio-console.c2
-rw-r--r--hw/core/qdev-properties-system.c4
-rw-r--r--hw/cpu/core.c8
-rw-r--r--hw/display/cirrus_vga.c64
-rw-r--r--hw/display/milkymist-tmu2.c2
-rw-r--r--hw/display/qxl.c9
-rw-r--r--hw/display/sm501.c2
-rw-r--r--hw/display/trace-events1
-rw-r--r--hw/gpio/mpc8xxx.c20
-rw-r--r--hw/i386/intel_iommu.c10
-rw-r--r--hw/i386/kvmvapic.c6
-rw-r--r--hw/i386/pc.c5
-rw-r--r--hw/i386/trace-events8
-rw-r--r--hw/i386/xen/trace-events6
-rw-r--r--hw/input/ps2.c8
-rw-r--r--hw/input/trace-events2
-rw-r--r--hw/intc/apic_common.c33
-rw-r--r--hw/intc/trace-events1
-rw-r--r--hw/isa/isa-bus.c2
-rw-r--r--hw/isa/lpc_ich9.c91
-rw-r--r--hw/isa/pc87312.c2
-rw-r--r--hw/lm32/lm32.h4
-rw-r--r--hw/lm32/milkymist-hw.h2
-rw-r--r--hw/mips/mips_malta.c4
-rw-r--r--hw/misc/ivshmem.c10
-rw-r--r--hw/misc/milkymist-pfpu.c2
-rw-r--r--hw/net/e1000e.c2
-rw-r--r--hw/net/fsl_etsec/etsec.c1
-rw-r--r--hw/net/rocker/rocker.c4
-rw-r--r--hw/net/trace-events8
-rw-r--r--hw/net/vmxnet3.c2
-rw-r--r--hw/pci-bridge/Makefile.objs3
-rw-r--r--hw/pci-bridge/gen_pcie_root_port.c87
-rw-r--r--hw/pci-bridge/ioh3420.c121
-rw-r--r--hw/pci-bridge/pci_bridge_dev.c2
-rw-r--r--hw/pci-bridge/pcie_root_port.c171
-rw-r--r--hw/pci/msix.c44
-rw-r--r--hw/pci/pci.c2
-rw-r--r--hw/ppc/Makefile.objs2
-rw-r--r--hw/ppc/e500.c23
-rw-r--r--hw/ppc/pnv.c6
-rw-r--r--hw/ppc/ppc.c73
-rw-r--r--hw/ppc/ppc440_bamboo.c6
-rw-r--r--hw/ppc/ppc_booke.c8
-rw-r--r--hw/ppc/ppce500_spin.c18
-rw-r--r--hw/ppc/prep.c234
-rw-r--r--hw/ppc/prep_systemio.c303
-rw-r--r--hw/ppc/rs6000_mc.c232
-rw-r--r--hw/ppc/spapr.c214
-rw-r--r--hw/ppc/spapr_cpu_core.c38
-rw-r--r--hw/ppc/spapr_hcall.c160
-rw-r--r--hw/ppc/spapr_vio.c10
-rw-r--r--hw/ppc/trace-events13
-rw-r--r--hw/ppc/virtex_ml507.c7
-rw-r--r--hw/s390x/s390-pci-bus.h4
-rw-r--r--hw/s390x/s390-virtio.c2
-rw-r--r--hw/scsi/megasas.c4
-rw-r--r--hw/scsi/scsi-generic.c5
-rw-r--r--hw/timer/mc146818rtc.c15
-rw-r--r--hw/usb/ccid-card-passthru.c2
-rw-r--r--hw/usb/dev-serial.c6
-rw-r--r--hw/usb/hcd-xhci.c41
-rw-r--r--hw/usb/host-libusb.c29
-rw-r--r--hw/usb/host-stub.c5
-rw-r--r--hw/usb/redirect.c4
-rw-r--r--hw/usb/trace-events1
-rw-r--r--hw/vfio/pci.c8
-rw-r--r--hw/vfio/trace-events2
-rw-r--r--hw/virtio/vhost.c3
-rw-r--r--hw/virtio/virtio-pci.c4
-rw-r--r--hw/virtio/virtio.c2
-rw-r--r--hw/xen/trace-events13
-rw-r--r--include/block/block.h1
-rw-r--r--include/exec/cpu_ldst_template.h2
-rw-r--r--include/exec/cpu_ldst_useronly_template.h2
-rw-r--r--include/exec/memory.h2
-rw-r--r--include/fpu/softfloat.h20
-rw-r--r--include/hw/arm/exynos4210.h2
-rw-r--r--include/hw/arm/omap.h6
-rw-r--r--include/hw/bt.h4
-rw-r--r--include/hw/char/cadence_uart.h2
-rw-r--r--include/hw/char/escc.h2
-rw-r--r--include/hw/char/pl011.h4
-rw-r--r--include/hw/char/serial.h4
-rw-r--r--include/hw/char/xilinx_uartlite.h2
-rw-r--r--include/hw/compat.h4
-rw-r--r--include/hw/cris/etraxfs.h2
-rw-r--r--include/hw/devices.h2
-rw-r--r--include/hw/i386/apic_internal.h2
-rw-r--r--include/hw/i386/ich9.h13
-rw-r--r--include/hw/i386/pc.h12
-rw-r--r--include/hw/input/ps2.h6
-rw-r--r--include/hw/m68k/mcf.h4
-rw-r--r--include/hw/pci/msix.h5
-rw-r--r--include/hw/pci/pci.h1
-rw-r--r--include/hw/pci/pcie_port.h19
-rw-r--r--include/hw/ppc/spapr.h7
-rw-r--r--include/hw/ppc/spapr_vio.h2
-rw-r--r--include/hw/qdev-core.h1
-rw-r--r--include/hw/qdev-properties.h2
-rw-r--r--include/hw/sh4/sh.h2
-rw-r--r--include/hw/sparc/grlib.h2
-rw-r--r--include/hw/usb.h1
-rw-r--r--include/hw/virtio/virtio.h1
-rw-r--r--include/hw/xen/xen.h2
-rw-r--r--include/hw/xen/xen_common.h2
-rw-r--r--include/monitor/monitor.h2
-rw-r--r--include/qemu/compiler.h16
-rw-r--r--include/qemu/host-utils.h27
-rw-r--r--include/qemu/osdep.h9
-rw-r--r--include/qemu/typedefs.h2
-rw-r--r--include/sysemu/char.h202
-rw-r--r--include/sysemu/replay.h13
-rw-r--r--include/sysemu/sysemu.h5
-rw-r--r--include/trace.h6
-rw-r--r--include/ui/console.h2
-rw-r--r--include/ui/gtk.h2
-rw-r--r--include/ui/qemu-spice.h2
-rw-r--r--ioport.c2
-rw-r--r--kvm-all.c2
-rw-r--r--linux-user/main.c2
-rw-r--r--memory.c91
-rw-r--r--migration/savevm.c33
-rw-r--r--migration/trace-events3
-rw-r--r--monitor.c13
-rw-r--r--net/colo-compare.c4
-rw-r--r--net/filter-mirror.c4
-rw-r--r--net/slirp.c2
-rw-r--r--net/vhost-user.c13
-rw-r--r--qapi-schema.json7
-rw-r--r--qapi/qapi-visit-core.c1
-rw-r--r--qapi/trace-events2
-rw-r--r--qdev-monitor.c1
-rw-r--r--qemu-char.c4963
-rw-r--r--qemu-options.hx12
-rw-r--r--qmp.c3
-rw-r--r--qom/cpu.c3
-rw-r--r--qtest.c2
-rwxr-xr-xreplay/replay-char.c8
-rw-r--r--replay/replay-snapshot.c17
-rw-r--r--replay/replay.c5
-rw-r--r--rules.mak30
-rwxr-xr-xscripts/simpletrace.py10
-rwxr-xr-xscripts/tracetool.py31
-rw-r--r--scripts/tracetool/backend/dtrace.py7
-rw-r--r--scripts/tracetool/backend/simple.py1
-rw-r--r--scripts/tracetool/backend/ust.py7
-rw-r--r--scripts/tracetool/format/c.py7
-rw-r--r--scripts/tracetool/format/tcg_h.py6
-rw-r--r--scripts/tracetool/format/tcg_helper_c.py6
-rw-r--r--scripts/tracetool/format/ust_events_c.py2
-rw-r--r--scripts/tracetool/format/ust_events_h.py7
-rw-r--r--spice-qemu-char.c226
-rw-r--r--stubs/monitor.c2
-rw-r--r--stubs/replay.c4
-rw-r--r--target/arm/kvm-consts.h102
-rw-r--r--target/i386/cpu.c1
-rw-r--r--target/i386/cpu.h4
-rw-r--r--target/i386/kvm.c36
-rw-r--r--target/i386/seg_helper.c1
-rw-r--r--target/ppc/Makefile.objs2
-rw-r--r--target/ppc/compat.c185
-rw-r--r--target/ppc/cpu-models.c22
-rw-r--r--target/ppc/cpu-models.h2
-rw-r--r--target/ppc/cpu-qom.h3
-rw-r--r--target/ppc/cpu.h67
-rw-r--r--target/ppc/excp_helper.c11
-rw-r--r--target/ppc/fpu_helper.c756
-rw-r--r--target/ppc/helper.h50
-rw-r--r--target/ppc/int_helper.c473
-rw-r--r--target/ppc/internal.h202
-rw-r--r--target/ppc/kvm.c40
-rw-r--r--target/ppc/kvm_ppc.h4
-rw-r--r--target/ppc/mem_helper.c66
-rw-r--r--target/ppc/mmu-hash64.c4
-rw-r--r--target/ppc/mmu-hash64.h2
-rw-r--r--target/ppc/translate.c244
-rw-r--r--target/ppc/translate/fp-impl.inc.c20
-rw-r--r--target/ppc/translate/fp-ops.inc.c2
-rw-r--r--target/ppc/translate/vmx-impl.inc.c64
-rw-r--r--target/ppc/translate/vmx-ops.inc.c23
-rw-r--r--target/ppc/translate/vsx-impl.inc.c484
-rw-r--r--target/ppc/translate/vsx-ops.inc.c74
-rw-r--r--target/ppc/translate_init.c97
-rw-r--r--tcg/tci/tcg-target.inc.c2
-rw-r--r--tests/.gitignore1
-rw-r--r--tests/Makefile.include23
-rw-r--r--tests/display-vga-test.c7
-rw-r--r--tests/ivshmem-test.c46
-rw-r--r--tests/libqos/pci-spapr.c4
-rw-r--r--tests/test-char.c10
-rw-r--r--tests/test-shift128.c139
-rw-r--r--tests/vhost-user-test.c5
-rw-r--r--thread-pool.c2
-rw-r--r--trace-events11
-rw-r--r--trace/Makefile.objs93
-rw-r--r--trace/control-target.c2
-rw-r--r--trace/control.c2
-rw-r--r--trace/ftrace.c2
-rw-r--r--trace/simple.c1
-rw-r--r--translate-all.c4
-rw-r--r--ui/console.c107
-rw-r--r--ui/gtk.c113
-rw-r--r--ui/input-linux.c6
-rw-r--r--ui/spice-display.c2
-rw-r--r--ui/vnc.c67
-rw-r--r--ui/vnc.h3
-rw-r--r--util/Makefile.objs2
-rw-r--r--util/host-utils.c66
-rw-r--r--vl.c61
-rw-r--r--xen-common-stub.c2
-rw-r--r--xen-common.c4
-rw-r--r--xen-hvm.c2
-rw-r--r--xen-mapcache.c2
296 files changed, 12034 insertions, 7159 deletions
diff --git a/.gitignore b/.gitignore
index 78f180a020..c563dc175f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,18 +6,12 @@
 /config.status
 /config-temp
 /trace-events-all
-/trace/generated-tracers.h
-/trace/generated-tracers.c
-/trace/generated-tracers-dtrace.h
-/trace/generated-tracers.dtrace
 /trace/generated-events.h
 /trace/generated-events.c
 /trace/generated-helpers-wrappers.h
 /trace/generated-helpers.h
 /trace/generated-helpers.c
 /trace/generated-tcg-tracers.h
-/trace/generated-ust-provider.h
-/trace/generated-ust.c
 /ui/shader/texture-blit-frag.h
 /ui/shader/texture-blit-vert.h
 *-timestamp
@@ -120,3 +114,19 @@ tags
 TAGS
 docker-src.*
 *~
+trace.h
+trace.c
+trace-ust.h
+trace-ust.h
+trace-dtrace.h
+trace-dtrace.dtrace
+trace-root.h
+trace-root.c
+trace-ust-root.h
+trace-ust-root.h
+trace-ust-all.h
+trace-ust-all.c
+trace-dtrace-root.h
+trace-dtrace-root.dtrace
+trace-ust-all.h
+trace-ust-all.c
diff --git a/MAINTAINERS b/MAINTAINERS
index baea7c7bfb..7afbadaa15 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -671,10 +671,13 @@ F: hw/misc/macio/
 F: hw/intc/heathrow_pic.c
 
 PReP
+M: Hervé Poussineau <hpoussin@reactos.org>
 L: qemu-devel@nongnu.org
 L: qemu-ppc@nongnu.org
-S: Odd Fixes
+S: Maintained
 F: hw/ppc/prep.c
+F: hw/ppc/prep_systemio.c
+F: hw/ppc/rs6000_mc.c
 F: hw/pci-host/prep.[hc]
 F: hw/isa/pc87312.[hc]
 F: pc-bios/ppc_rom.bin
@@ -1194,8 +1197,9 @@ T: git git://github.com/jnsnow/qemu.git bitmaps
 
 Character device backends
 M: Paolo Bonzini <pbonzini@redhat.com>
+M: Marc-André Lureau <marcandre.lureau@redhat.com>
 S: Maintained
-F: qemu-char.c
+F: chardev/
 F: backends/msmouse.c
 F: backends/testdev.c
 
@@ -1642,6 +1646,7 @@ M: Peter Lieven <pl@kamp.de>
 L: qemu-block@nongnu.org
 S: Supported
 F: block/iscsi.c
+F: block/iscsi-opts.c
 
 NFS
 M: Jeff Cody <jcody@redhat.com>
diff --git a/Makefile b/Makefile
index 1e5cb1934d..4b72a4ca56 100644
--- a/Makefile
+++ b/Makefile
@@ -56,25 +56,136 @@ GENERATED_SOURCES += qmp-marshal.c qapi-types.c qapi-visit.c qapi-event.c
 GENERATED_HEADERS += qmp-introspect.h
 GENERATED_SOURCES += qmp-introspect.c
 
-GENERATED_HEADERS += trace/generated-tracers.h
-ifeq ($(findstring dtrace,$(TRACE_BACKENDS)),dtrace)
-GENERATED_HEADERS += trace/generated-tracers-dtrace.h
-endif
-GENERATED_SOURCES += trace/generated-tracers.c
-
 GENERATED_HEADERS += trace/generated-tcg-tracers.h
 
 GENERATED_HEADERS += trace/generated-helpers-wrappers.h
 GENERATED_HEADERS += trace/generated-helpers.h
 GENERATED_SOURCES += trace/generated-helpers.c
 
-ifeq ($(findstring ust,$(TRACE_BACKENDS)),ust)
-GENERATED_HEADERS += trace/generated-ust-provider.h
-GENERATED_SOURCES += trace/generated-ust.c
+ifdef CONFIG_TRACE_UST
+GENERATED_HEADERS += trace-ust-all.h
+GENERATED_SOURCES += trace-ust-all.c
 endif
 
 GENERATED_HEADERS += module_block.h
 
+TRACE_HEADERS = trace-root.h $(trace-events-subdirs:%=%/trace.h)
+TRACE_SOURCES = trace-root.c $(trace-events-subdirs:%=%/trace.c)
+TRACE_DTRACE =
+ifdef CONFIG_TRACE_DTRACE
+TRACE_HEADERS += trace-dtrace-root.h $(trace-events-subdirs:%=%/trace-dtrace.h)
+TRACE_DTRACE += trace-dtrace-root.dtrace $(trace-events-subdirs:%=%/trace-dtrace.dtrace)
+endif
+ifdef CONFIG_TRACE_UST
+TRACE_HEADERS += trace-ust-root.h $(trace-events-subdirs:%=%/trace-ust.h)
+endif
+
+GENERATED_HEADERS += $(TRACE_HEADERS)
+GENERATED_SOURCES += $(TRACE_SOURCES)
+
+trace-group-name = $(shell dirname $1 | sed -e 's/[^a-zA-Z0-9]/_/g')
+
+%/trace.h: %/trace.h-timestamp
+	@cmp $< $@ >/dev/null 2>&1 || cp $< $@
+%/trace.h-timestamp: $(SRC_PATH)/%/trace-events $(tracetool-y)
+	$(call quiet-command,$(TRACETOOL) \
+		--group=$(call trace-group-name,$@) \
+		--format=h \
+		--backends=$(TRACE_BACKENDS) \
+		$< > $@,"GEN","$(@:%-timestamp=%)")
+
+%/trace.c: %/trace.c-timestamp
+	@cmp $< $@ >/dev/null 2>&1 || cp $< $@
+%/trace.c-timestamp: $(SRC_PATH)/%/trace-events $(tracetool-y)
+	$(call quiet-command,$(TRACETOOL) \
+		--group=$(call trace-group-name,$@) \
+		--format=c \
+		--backends=$(TRACE_BACKENDS) \
+		$< > $@,"GEN","$(@:%-timestamp=%)")
+
+%/trace-ust.h: %/trace-ust.h-timestamp
+	@cmp $< $@ >/dev/null 2>&1 || cp $< $@
+%/trace-ust.h-timestamp: $(SRC_PATH)/%/trace-events $(tracetool-y)
+	$(call quiet-command,$(TRACETOOL) \
+		--group=$(call trace-group-name,$@) \
+		--format=ust-events-h \
+		--backends=$(TRACE_BACKENDS) \
+		$< > $@,"GEN","$(@:%-timestamp=%)")
+
+%/trace-dtrace.dtrace: %/trace-dtrace.dtrace-timestamp
+	@cmp $< $@ >/dev/null 2>&1 || cp $< $@
+%/trace-dtrace.dtrace-timestamp: $(SRC_PATH)/%/trace-events $(BUILD_DIR)/config-host.mak $(tracetool-y)
+	$(call quiet-command,$(TRACETOOL) \
+		--group=$(call trace-group-name,$@) \
+		--format=d \
+		--backends=$(TRACE_BACKENDS) \
+		$< > $@,"GEN","$(@:%-timestamp=%)")
+
+%/trace-dtrace.h: %/trace-dtrace.dtrace $(tracetool-y)
+	$(call quiet-command,dtrace -o $@ -h -s $<, "GEN","$@")
+
+%/trace-dtrace.o: %/trace-dtrace.dtrace $(tracetool-y)
+
+
+trace-root.h: trace-root.h-timestamp
+	@cmp $< $@ >/dev/null 2>&1 || cp $< $@
+trace-root.h-timestamp: $(SRC_PATH)/trace-events $(tracetool-y)
+	$(call quiet-command,$(TRACETOOL) \
+		--group=root \
+		--format=h \
+		--backends=$(TRACE_BACKENDS) \
+		$< > $@,"GEN","$(@:%-timestamp=%)")
+
+trace-root.c: trace-root.c-timestamp
+	@cmp $< $@ >/dev/null 2>&1 || cp $< $@
+trace-root.c-timestamp: $(SRC_PATH)/trace-events $(tracetool-y)
+	$(call quiet-command,$(TRACETOOL) \
+		--group=root \
+		--format=c \
+		--backends=$(TRACE_BACKENDS) \
+		$< > $@,"GEN","$(@:%-timestamp=%)")
+
+trace-ust-root.h: trace-ust-root.h-timestamp
+	@cmp $< $@ >/dev/null 2>&1 || cp $< $@
+trace-ust-root.h-timestamp: $(SRC_PATH)/trace-events $(tracetool-y)
+	$(call quiet-command,$(TRACETOOL) \
+		--group=root \
+		--format=ust-events-h \
+		--backends=$(TRACE_BACKENDS) \
+		$< > $@,"GEN","$(@:%-timestamp=%)")
+
+trace-ust-all.h: trace-ust-all.h-timestamp
+	@cmp $< $@ >/dev/null 2>&1 || cp $< $@
+trace-ust-all.h-timestamp: $(trace-events-files) $(tracetool-y)
+	$(call quiet-command,$(TRACETOOL) \
+		--group=all \
+		--format=ust-events-h \
+		--backends=$(TRACE_BACKENDS) \
+		$(trace-events-files) > $@,"GEN","$(@:%-timestamp=%)")
+
+trace-ust-all.c: trace-ust-all.c-timestamp
+	@cmp $< $@ >/dev/null 2>&1 || cp $< $@
+trace-ust-all.c-timestamp: $(trace-events-files) $(tracetool-y)
+	$(call quiet-command,$(TRACETOOL) \
+		--group=all \
+		--format=ust-events-c \
+		--backends=$(TRACE_BACKENDS) \
+		$(trace-events-files) > $@,"GEN","$(@:%-timestamp=%)")
+
+trace-dtrace-root.dtrace: trace-dtrace-root.dtrace-timestamp
+	@cmp $< $@ >/dev/null 2>&1 || cp $< $@
+trace-dtrace-root.dtrace-timestamp: $(SRC_PATH)/trace-events $(BUILD_DIR)/config-host.mak $(tracetool-y)
+	$(call quiet-command,$(TRACETOOL) \
+		--group=root \
+		--format=d \
+		--backends=$(TRACE_BACKENDS) \
+		$< > $@,"GEN","$(@:%-timestamp=%)")
+
+trace-dtrace-root.h: trace-dtrace-root.dtrace
+	$(call quiet-command,dtrace -o $@ -h -s $<, "GEN","$@")
+
+trace-dtrace-root.o: trace-dtrace-root.dtrace
+
 # Don't try to regenerate Makefile or configure
 # We don't generate any of them
 Makefile: ;
@@ -147,6 +258,7 @@ endif
 
 dummy := $(call unnest-vars,, \
                 stub-obj-y \
+                chardev-obj-y \
                 util-obj-y \
                 qga-obj-y \
                 ivshmem-client-obj-y \
@@ -160,7 +272,8 @@ dummy := $(call unnest-vars,, \
                 qom-obj-y \
                 io-obj-y \
                 common-obj-y \
-                common-obj-m)
+                common-obj-m \
+                trace-obj-y)
 
 ifneq ($(wildcard config-host.mak),)
 include $(SRC_PATH)/tests/Makefile.include
@@ -223,7 +336,8 @@ subdir-dtc:dtc/libfdt dtc/tests
 dtc/%:
 	mkdir -p $@
 
-$(SUBDIR_RULES): libqemuutil.a libqemustub.a $(common-obj-y) $(qom-obj-y) $(crypto-aes-obj-$(CONFIG_USER_ONLY))
+$(SUBDIR_RULES): libqemuutil.a libqemustub.a $(common-obj-y) $(chardev-obj-y) \
+	$(qom-obj-y) $(crypto-aes-obj-$(CONFIG_USER_ONLY)) $(trace-obj-y)
 
 ROMSUBDIR_RULES=$(patsubst %,romsubdir-%, $(ROMS))
 # Only keep -O and -g cflags
@@ -247,15 +361,17 @@ libqemuutil.a: $(util-obj-y)
 
 ######################################################################
 
+COMMON_LDADDS = $(trace-obj-y) libqemuutil.a libqemustub.a
+
 qemu-img.o: qemu-img-cmds.h
 
-qemu-img$(EXESUF): qemu-img.o $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) libqemuutil.a libqemustub.a
-qemu-nbd$(EXESUF): qemu-nbd.o $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) libqemuutil.a libqemustub.a
-qemu-io$(EXESUF): qemu-io.o $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) libqemuutil.a libqemustub.a
+qemu-img$(EXESUF): qemu-img.o $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS)
+qemu-nbd$(EXESUF): qemu-nbd.o $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS)
+qemu-io$(EXESUF): qemu-io.o $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS)
 
-qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o libqemuutil.a libqemustub.a
+qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o $(COMMON_LDADDS)
 
-fsdev/virtfs-proxy-helper$(EXESUF): fsdev/virtfs-proxy-helper.o fsdev/9p-marshal.o fsdev/9p-iov-marshal.o libqemuutil.a libqemustub.a
+fsdev/virtfs-proxy-helper$(EXESUF): fsdev/virtfs-proxy-helper.o fsdev/9p-marshal.o fsdev/9p-iov-marshal.o $(COMMON_LDADDS)
 fsdev/virtfs-proxy-helper$(EXESUF): LIBS += -lcap
 
 qemu-img-cmds.h: $(SRC_PATH)/qemu-img-cmds.hx $(SRC_PATH)/scripts/hxtool
@@ -320,7 +436,7 @@ $(qapi-modules) $(SRC_PATH)/scripts/qapi-introspect.py $(qapi-py)
 QGALIB_GEN=$(addprefix qga/qapi-generated/, qga-qapi-types.h qga-qapi-visit.h qga-qmp-commands.h)
 $(qga-obj-y) qemu-ga.o: $(QGALIB_GEN)
 
-qemu-ga$(EXESUF): $(qga-obj-y) libqemuutil.a libqemustub.a
+qemu-ga$(EXESUF): $(qga-obj-y) $(COMMON_LDADDS)
 	$(call LINK, $^)
 
 ifdef QEMU_GA_MSI_ENABLED
@@ -345,9 +461,9 @@ ifneq ($(EXESUF),)
 qemu-ga: qemu-ga$(EXESUF) $(QGA_VSS_PROVIDER) $(QEMU_GA_MSI)
 endif
 
-ivshmem-client$(EXESUF): $(ivshmem-client-obj-y) libqemuutil.a libqemustub.a
+ivshmem-client$(EXESUF): $(ivshmem-client-obj-y) $(COMMON_LDADDS)
 	$(call LINK, $^)
-ivshmem-server$(EXESUF): $(ivshmem-server-obj-y) libqemuutil.a libqemustub.a
+ivshmem-server$(EXESUF): $(ivshmem-server-obj-y) $(COMMON_LDADDS)
 	$(call LINK, $^)
 
 module_block.h: $(SRC_PATH)/scripts/modules/module_block.py config-host.mak
@@ -664,6 +780,10 @@ ifneq ($(filter-out $(UNCHECKED_GOALS),$(MAKECMDGOALS)),$(if $(MAKECMDGOALS),,fa
 Makefile: $(GENERATED_HEADERS)
 endif
 
+.SECONDARY: $(TRACE_HEADERS) $(TRACE_HEADERS:%=%-timestamp) \
+	$(TRACE_SOURCES) $(TRACE_SOURCES:%=%-timestamp) \
+	$(TRACE_DTRACE) $(TRACE_DTRACE:%=%-timestamp)
+
 # Include automatically generated dependency files
 # Dependencies in Makefile.objs files come from our recursive subdir rules
 -include $(wildcard *.d tests/*.d)
diff --git a/Makefile.objs b/Makefile.objs
index 01cef866e4..431fc59264 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -4,6 +4,8 @@ stub-obj-y = stubs/ crypto/
 util-obj-y = util/ qobject/ qapi/
 util-obj-y += qmp-introspect.o qapi-types.o qapi-visit.o qapi-event.o
 
+chardev-obj-y = chardev/
+
 #######################################################################
 # block-obj-y is code used by both qemu system emulation and qemu-img
 
@@ -51,8 +53,7 @@ common-obj-$(CONFIG_POSIX) += os-posix.o
 common-obj-$(CONFIG_LINUX) += fsdev/
 
 common-obj-y += migration/
-common-obj-y += qemu-char.o #aio.o
-common-obj-y += page_cache.o
+common-obj-y += page_cache.o #aio.o
 
 common-obj-$(CONFIG_SPICE) += spice-qemu-char.o
 
@@ -118,47 +119,58 @@ ivshmem-server-obj-y = contrib/ivshmem-server/
 libvhost-user-obj-y = contrib/libvhost-user/
 
 ######################################################################
-trace-events-y = trace-events
-trace-events-y += util/trace-events
-trace-events-y += crypto/trace-events
-trace-events-y += io/trace-events
-trace-events-y += migration/trace-events
-trace-events-y += block/trace-events
-trace-events-y += hw/block/trace-events
-trace-events-y += hw/char/trace-events
-trace-events-y += hw/intc/trace-events
-trace-events-y += hw/net/trace-events
-trace-events-y += hw/virtio/trace-events
-trace-events-y += hw/audio/trace-events
-trace-events-y += hw/misc/trace-events
-trace-events-y += hw/usb/trace-events
-trace-events-y += hw/scsi/trace-events
-trace-events-y += hw/nvram/trace-events
-trace-events-y += hw/display/trace-events
-trace-events-y += hw/input/trace-events
-trace-events-y += hw/timer/trace-events
-trace-events-y += hw/dma/trace-events
-trace-events-y += hw/sparc/trace-events
-trace-events-y += hw/sd/trace-events
-trace-events-y += hw/isa/trace-events
-trace-events-y += hw/mem/trace-events
-trace-events-y += hw/i386/trace-events
-trace-events-y += hw/9pfs/trace-events
-trace-events-y += hw/ppc/trace-events
-trace-events-y += hw/pci/trace-events
-trace-events-y += hw/s390x/trace-events
-trace-events-y += hw/vfio/trace-events
-trace-events-y += hw/acpi/trace-events
-trace-events-y += hw/arm/trace-events
-trace-events-y += hw/alpha/trace-events
-trace-events-y += ui/trace-events
-trace-events-y += audio/trace-events
-trace-events-y += net/trace-events
-trace-events-y += target/arm/trace-events
-trace-events-y += target/i386/trace-events
-trace-events-y += target/sparc/trace-events
-trace-events-y += target/s390x/trace-events
-trace-events-y += target/ppc/trace-events
-trace-events-y += qom/trace-events
-trace-events-y += linux-user/trace-events
-trace-events-y += qapi/trace-events
+trace-events-subdirs =
+trace-events-subdirs += util
+trace-events-subdirs += crypto
+trace-events-subdirs += io
+trace-events-subdirs += migration
+trace-events-subdirs += block
+trace-events-subdirs += hw/block
+trace-events-subdirs += hw/block/dataplane
+trace-events-subdirs += hw/char
+trace-events-subdirs += hw/intc
+trace-events-subdirs += hw/net
+trace-events-subdirs += hw/virtio
+trace-events-subdirs += hw/audio
+trace-events-subdirs += hw/misc
+trace-events-subdirs += hw/usb
+trace-events-subdirs += hw/scsi
+trace-events-subdirs += hw/nvram
+trace-events-subdirs += hw/display
+trace-events-subdirs += hw/input
+trace-events-subdirs += hw/timer
+trace-events-subdirs += hw/dma
+trace-events-subdirs += hw/sparc
+trace-events-subdirs += hw/sd
+trace-events-subdirs += hw/isa
+trace-events-subdirs += hw/mem
+trace-events-subdirs += hw/i386
+trace-events-subdirs += hw/i386/xen
+trace-events-subdirs += hw/9pfs
+trace-events-subdirs += hw/ppc
+trace-events-subdirs += hw/pci
+trace-events-subdirs += hw/s390x
+trace-events-subdirs += hw/vfio
+trace-events-subdirs += hw/acpi
+trace-events-subdirs += hw/arm
+trace-events-subdirs += hw/alpha
+trace-events-subdirs += hw/xen
+trace-events-subdirs += ui
+trace-events-subdirs += audio
+trace-events-subdirs += net
+trace-events-subdirs += target/arm
+trace-events-subdirs += target/i386
+trace-events-subdirs += target/sparc
+trace-events-subdirs += target/s390x
+trace-events-subdirs += target/ppc
+trace-events-subdirs += qom
+trace-events-subdirs += linux-user
+trace-events-subdirs += qapi
+
+trace-events-files = $(SRC_PATH)/trace-events $(trace-events-subdirs:%=$(SRC_PATH)/%/trace-events)
+
+trace-obj-y = trace-root.o
+trace-obj-y += $(trace-events-subdirs:%=%/trace.o)
+trace-obj-$(CONFIG_TRACE_UST) += trace-ust-all.o
+trace-obj-$(CONFIG_TRACE_DTRACE) += trace-dtrace-root.o
+trace-obj-$(CONFIG_TRACE_DTRACE) += $(trace-events-subdirs:%=%/trace-dtrace.o)
diff --git a/Makefile.target b/Makefile.target
index fa2b151caa..924304c9e6 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -50,6 +50,7 @@ endif
 
 $(QEMU_PROG).stp-installed: $(BUILD_DIR)/trace-events-all
 	$(call quiet-command,$(TRACETOOL) \
+		--group=all \
 		--format=stap \
 		--backends=$(TRACE_BACKENDS) \
 		--binary=$(bindir)/$(QEMU_PROG) \
@@ -59,6 +60,7 @@ $(QEMU_PROG).stp-installed: $(BUILD_DIR)/trace-events-all
 
 $(QEMU_PROG).stp: $(BUILD_DIR)/trace-events-all
 	$(call quiet-command,$(TRACETOOL) \
+		--group=all \
 		--format=stap \
 		--backends=$(TRACE_BACKENDS) \
 		--binary=$(realpath .)/$(QEMU_PROG) \
@@ -68,6 +70,7 @@ $(QEMU_PROG).stp: $(BUILD_DIR)/trace-events-all
 
 $(QEMU_PROG)-simpletrace.stp: $(BUILD_DIR)/trace-events-all
 	$(call quiet-command,$(TRACETOOL) \
+		--group=all \
 		--format=simpletrace-stap \
 		--backends=$(TRACE_BACKENDS) \
 		--probe-prefix=qemu.$(TARGET_TYPE).$(TARGET_NAME) \
@@ -172,31 +175,36 @@ all-obj-y := $(obj-y)
 target-obj-y :=
 block-obj-y :=
 common-obj-y :=
+chardev-obj-y :=
 include $(SRC_PATH)/Makefile.objs
 dummy := $(call unnest-vars,,target-obj-y)
 target-obj-y-save := $(target-obj-y)
 dummy := $(call unnest-vars,.., \
                block-obj-y \
                block-obj-m \
+               chardev-obj-y \
                crypto-obj-y \
                crypto-aes-obj-y \
                qom-obj-y \
                io-obj-y \
                common-obj-y \
-               common-obj-m)
+               common-obj-m \
+               trace-obj-y)
 target-obj-y := $(target-obj-y-save)
 all-obj-y += $(common-obj-y)
 all-obj-y += $(target-obj-y)
 all-obj-y += $(qom-obj-y)
-all-obj-$(CONFIG_SOFTMMU) += $(block-obj-y)
+all-obj-$(CONFIG_SOFTMMU) += $(block-obj-y) $(chardev-obj-y)
 all-obj-$(CONFIG_USER_ONLY) += $(crypto-aes-obj-y)
 all-obj-$(CONFIG_SOFTMMU) += $(crypto-obj-y)
 all-obj-$(CONFIG_SOFTMMU) += $(io-obj-y)
 
 $(QEMU_PROG_BUILD): config-devices.mak
 
+COMMON_LDADDS = $(trace-obj-y) ../libqemuutil.a ../libqemustub.a
+
 # build either PROG or PROGW
-$(QEMU_PROG_BUILD): $(all-obj-y) ../libqemuutil.a ../libqemustub.a
+$(QEMU_PROG_BUILD): $(all-obj-y) $(COMMON_LDADDS)
 	$(call LINK, $(filter-out %.mak, $^))
 ifdef CONFIG_DARWIN
 	$(call quiet-command,Rez -append $(SRC_PATH)/pc-bios/qemu.rsrc -o $@,"REZ","$(TARGET_DIR)$@")
diff --git a/aio-posix.c b/aio-posix.c
index a8d7090bd8..577527fda5 100644
--- a/aio-posix.c
+++ b/aio-posix.c
@@ -19,7 +19,7 @@
 #include "qemu/rcu_queue.h"
 #include "qemu/sockets.h"
 #include "qemu/cutils.h"
-#include "trace.h"
+#include "trace-root.h"
 #ifdef CONFIG_EPOLL_CREATE1
 #include <sys/epoll.h>
 #endif
diff --git a/backends/baum.c b/backends/baum.c
index b045ef49c5..2eddcae119 100644
--- a/backends/baum.c
+++ b/backends/baum.c
@@ -85,7 +85,7 @@
 #define BUF_SIZE 256
 
 typedef struct {
-    CharDriverState *chr;
+    Chardev parent;
 
     brlapi_handle_t *brlapi;
     int brlapi_fd;
@@ -98,7 +98,10 @@ typedef struct {
     uint8_t out_buf_used, out_buf_ptr;
 
     QEMUTimer *cellCount_timer;
-} BaumDriverState;
+} BaumChardev;
+
+#define TYPE_CHARDEV_BRAILLE "chardev-braille"
+#define BAUM_CHARDEV(obj) OBJECT_CHECK(BaumChardev, (obj), TYPE_CHARDEV_BRAILLE)
 
 /* Let's assume NABCC by default */
 enum way {
@@ -223,7 +226,7 @@ static const uint8_t nabcc_translation[2][256] = {
 };
 
 /* The guest OS has started discussing with us, finish initializing BrlAPI */
-static int baum_deferred_init(BaumDriverState *baum)
+static int baum_deferred_init(BaumChardev *baum)
 {
     int tty = BRLAPI_TTY_DEFAULT;
     QemuConsole *con;
@@ -253,9 +256,9 @@ static int baum_deferred_init(BaumDriverState *baum)
 }
 
 /* The serial port can receive more of our data */
-static void baum_accept_input(struct CharDriverState *chr)
+static void baum_chr_accept_input(struct Chardev *chr)
 {
-    BaumDriverState *baum = chr->opaque;
+    BaumChardev *baum = BAUM_CHARDEV(chr);
     int room, first;
 
     if (!baum->out_buf_used)
@@ -279,24 +282,25 @@ static void baum_accept_input(struct CharDriverState *chr)
 }
 
 /* We want to send a packet */
-static void baum_write_packet(BaumDriverState *baum, const uint8_t *buf, int len)
+static void baum_write_packet(BaumChardev *baum, const uint8_t *buf, int len)
 {
+    Chardev *chr = CHARDEV(baum);
     uint8_t io_buf[1 + 2 * len], *cur = io_buf;
     int room;
     *cur++ = ESC;
     while (len--)
         if ((*cur++ = *buf++) == ESC)
             *cur++ = ESC;
-    room = qemu_chr_be_can_write(baum->chr);
+    room = qemu_chr_be_can_write(chr);
     len = cur - io_buf;
     if (len <= room) {
         /* Fits */
-        qemu_chr_be_write(baum->chr, io_buf, len);
+        qemu_chr_be_write(chr, io_buf, len);
     } else {
         int first;
         uint8_t out;
         /* Can't fit all, send what can be, and store the rest. */
-        qemu_chr_be_write(baum->chr, io_buf, room);
+        qemu_chr_be_write(chr, io_buf, room);
         len -= room;
         cur = io_buf + room;
         if (len > BUF_SIZE - baum->out_buf_used) {
@@ -321,14 +325,14 @@ static void baum_write_packet(BaumDriverState *baum, const uint8_t *buf, int len
 /* Called when the other end seems to have a wrong idea of our display size */
 static void baum_cellCount_timer_cb(void *opaque)
 {
-    BaumDriverState *baum = opaque;
+    BaumChardev *baum = BAUM_CHARDEV(opaque);
     uint8_t cell_count[] = { BAUM_RSP_CellCount, baum->x * baum->y };
     DPRINTF("Timeout waiting for DisplayData, sending cell count\n");
     baum_write_packet(baum, cell_count, sizeof(cell_count));
 }
 
 /* Try to interpret a whole incoming packet */
-static int baum_eat_packet(BaumDriverState *baum, const uint8_t *buf, int len)
+static int baum_eat_packet(BaumChardev *baum, const uint8_t *buf, int len)
 {
     const uint8_t *cur = buf;
     uint8_t req = 0;
@@ -469,9 +473,9 @@ static int baum_eat_packet(BaumDriverState *baum, const uint8_t *buf, int len)
 }
 
 /* The other end is writing some data.  Store it and try to interpret */
-static int baum_write(CharDriverState *chr, const uint8_t *buf, int len)
+static int baum_chr_write(Chardev *chr, const uint8_t *buf, int len)
 {
-    BaumDriverState *baum = chr->opaque;
+    BaumChardev *baum = BAUM_CHARDEV(chr);
     int tocopy, cur, eaten, orig_len = len;
 
     if (!len)
@@ -510,14 +514,16 @@ static int baum_write(CharDriverState *chr, const uint8_t *buf, int len)
 }
 
 /* Send the key code to the other end */
-static void baum_send_key(BaumDriverState *baum, uint8_t type, uint8_t value) {
+static void baum_send_key(BaumChardev *baum, uint8_t type, uint8_t value)
+{
     uint8_t packet[] = { type, value };
     DPRINTF("writing key %x %x\n", type, value);
     baum_write_packet(baum, packet, sizeof(packet));
 }
 
-static void baum_send_key2(BaumDriverState *baum, uint8_t type, uint8_t value,
-                           uint8_t value2) {
+static void baum_send_key2(BaumChardev *baum, uint8_t type, uint8_t value,
+                           uint8_t value2)
+{
     uint8_t packet[] = { type, value, value2 };
     DPRINTF("writing key %x %x\n", type, value);
     baum_write_packet(baum, packet, sizeof(packet));
@@ -526,7 +532,7 @@ static void baum_send_key2(BaumDriverState *baum, uint8_t type, uint8_t value,
 /* We got some data on the BrlAPI socket */
 static void baum_chr_read(void *opaque)
 {
-    BaumDriverState *baum = opaque;
+    BaumChardev *baum = BAUM_CHARDEV(opaque);
     brlapi_keyCode_t code;
     int ret;
     if (!baum->brlapi)
@@ -610,41 +616,25 @@ static void baum_chr_read(void *opaque)
     }
 }
 
-static void baum_free(struct CharDriverState *chr)
+static void char_braille_finalize(Object *obj)
 {
-    BaumDriverState *baum = chr->opaque;
+    BaumChardev *baum = BAUM_CHARDEV(obj);
 
     timer_free(baum->cellCount_timer);
     if (baum->brlapi) {
         brlapi__closeConnection(baum->brlapi);
         g_free(baum->brlapi);
     }
-    g_free(baum);
 }
 
-static CharDriverState *chr_baum_init(const char *id,
-                                      ChardevBackend *backend,
-                                      ChardevReturn *ret,
-                                      bool *be_opened,
-                                      Error **errp)
+static void baum_chr_open(Chardev *chr,
+                          ChardevBackend *backend,
+                          bool *be_opened,
+                          Error **errp)
 {
-    ChardevCommon *common = backend->u.braille.data;
-    BaumDriverState *baum;
-    CharDriverState *chr;
+    BaumChardev *baum = BAUM_CHARDEV(chr);
     brlapi_handle_t *handle;
 
-    chr = qemu_chr_alloc(common, errp);
-    if (!chr) {
-        return NULL;
-    }
-    baum = g_malloc0(sizeof(BaumDriverState));
-    baum->chr = chr;
-
-    chr->opaque = baum;
-    chr->chr_write = baum_write;
-    chr->chr_accept_input = baum_accept_input;
-    chr->chr_free = baum_free;
-
     handle = g_malloc0(brlapi_getHandleSize());
     baum->brlapi = handle;
 
@@ -652,27 +642,36 @@ static CharDriverState *chr_baum_init(const char *id,
     if (baum->brlapi_fd == -1) {
         error_setg(errp, "brlapi__openConnection: %s",
                    brlapi_strerror(brlapi_error_location()));
-        goto fail_handle;
+        g_free(handle);
+        return;
     }
     baum->deferred_init = 0;
 
     baum->cellCount_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, baum_cellCount_timer_cb, baum);
 
     qemu_set_fd_handler(baum->brlapi_fd, baum_chr_read, NULL, baum);
+}
 
-    return chr;
+static void char_braille_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
 
-fail_handle:
-    g_free(handle);
-    g_free(chr);
-    g_free(baum);
-    return NULL;
+    cc->open = baum_chr_open;
+    cc->chr_write = baum_chr_write;
+    cc->chr_accept_input = baum_chr_accept_input;
 }
 
+static const TypeInfo char_braille_type_info = {
+    .name = TYPE_CHARDEV_BRAILLE,
+    .parent = TYPE_CHARDEV,
+    .instance_size = sizeof(BaumChardev),
+    .instance_finalize = char_braille_finalize,
+    .class_init = char_braille_class_init,
+};
+
 static void register_types(void)
 {
-    register_char_driver("braille", CHARDEV_BACKEND_KIND_BRAILLE, NULL,
-                         chr_baum_init);
+    type_register_static(&char_braille_type_info);
 }
 
 type_init(register_types);
diff --git a/backends/msmouse.c b/backends/msmouse.c
index 733ca80f48..d2c3162f1e 100644
--- a/backends/msmouse.c
+++ b/backends/msmouse.c
@@ -31,18 +31,23 @@
 #define MSMOUSE_HI2(n) (((n) & 0xc0) >> 6)
 
 typedef struct {
-    CharDriverState *chr;
+    Chardev parent;
+
     QemuInputHandlerState *hs;
     int axis[INPUT_AXIS__MAX];
     bool btns[INPUT_BUTTON__MAX];
     bool btnc[INPUT_BUTTON__MAX];
     uint8_t outbuf[32];
     int outlen;
-} MouseState;
+} MouseChardev;
+
+#define TYPE_CHARDEV_MSMOUSE "chardev-msmouse"
+#define MOUSE_CHARDEV(obj)                                      \
+    OBJECT_CHECK(MouseChardev, (obj), TYPE_CHARDEV_MSMOUSE)
 
-static void msmouse_chr_accept_input(CharDriverState *chr)
+static void msmouse_chr_accept_input(Chardev *chr)
 {
-    MouseState *mouse = chr->opaque;
+    MouseChardev *mouse = MOUSE_CHARDEV(chr);
     int len;
 
     len = qemu_chr_be_can_write(chr);
@@ -60,7 +65,7 @@ static void msmouse_chr_accept_input(CharDriverState *chr)
     }
 }
 
-static void msmouse_queue_event(MouseState *mouse)
+static void msmouse_queue_event(MouseChardev *mouse)
 {
     unsigned char bytes[4] = { 0x40, 0x00, 0x00, 0x00 };
     int dx, dy, count = 3;
@@ -97,7 +102,7 @@ static void msmouse_queue_event(MouseState *mouse)
 static void msmouse_input_event(DeviceState *dev, QemuConsole *src,
                                 InputEvent *evt)
 {
-    MouseState *mouse = (MouseState *)dev;
+    MouseChardev *mouse = MOUSE_CHARDEV(dev);
     InputMoveEvent *move;
     InputBtnEvent *btn;
 
@@ -121,24 +126,24 @@ static void msmouse_input_event(DeviceState *dev, QemuConsole *src,
 
 static void msmouse_input_sync(DeviceState *dev)
 {
-    MouseState *mouse = (MouseState *)dev;
+    MouseChardev *mouse = MOUSE_CHARDEV(dev);
+    Chardev *chr = CHARDEV(dev);
 
     msmouse_queue_event(mouse);
-    msmouse_chr_accept_input(mouse->chr);
+    msmouse_chr_accept_input(chr);
 }
 
-static int msmouse_chr_write (struct CharDriverState *s, const uint8_t *buf, int len)
+static int msmouse_chr_write(struct Chardev *s, const uint8_t *buf, int len)
 {
     /* Ignore writes to mouse port */
     return len;
 }
 
-static void msmouse_chr_free(struct CharDriverState *chr)
+static void char_msmouse_finalize(Object *obj)
 {
-    MouseState *mouse = chr->opaque;
+    MouseChardev *mouse = MOUSE_CHARDEV(obj);
 
     qemu_input_handler_unregister(mouse->hs);
-    g_free(mouse);
 }
 
 static QemuInputHandler msmouse_handler = {
@@ -148,39 +153,38 @@ static QemuInputHandler msmouse_handler = {
     .sync  = msmouse_input_sync,
 };
 
-static CharDriverState *qemu_chr_open_msmouse(const char *id,
-                                              ChardevBackend *backend,
-                                              ChardevReturn *ret,
-                                              bool *be_opened,
-                                              Error **errp)
+static void msmouse_chr_open(Chardev *chr,
+                             ChardevBackend *backend,
+                             bool *be_opened,
+                             Error **errp)
 {
-    ChardevCommon *common = backend->u.msmouse.data;
-    MouseState *mouse;
-    CharDriverState *chr;
+    MouseChardev *mouse = MOUSE_CHARDEV(chr);
 
-    chr = qemu_chr_alloc(common, errp);
-    if (!chr) {
-        return NULL;
-    }
-    chr->chr_write = msmouse_chr_write;
-    chr->chr_free = msmouse_chr_free;
-    chr->chr_accept_input = msmouse_chr_accept_input;
     *be_opened = false;
-
-    mouse = g_new0(MouseState, 1);
     mouse->hs = qemu_input_handler_register((DeviceState *)mouse,
                                             &msmouse_handler);
+}
 
-    mouse->chr = chr;
-    chr->opaque = mouse;
+static void char_msmouse_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
 
-    return chr;
+    cc->open = msmouse_chr_open;
+    cc->chr_write = msmouse_chr_write;
+    cc->chr_accept_input = msmouse_chr_accept_input;
 }
 
+static const TypeInfo char_msmouse_type_info = {
+    .name = TYPE_CHARDEV_MSMOUSE,
+    .parent = TYPE_CHARDEV,
+    .instance_size = sizeof(MouseChardev),
+    .instance_finalize = char_msmouse_finalize,
+    .class_init = char_msmouse_class_init,
+};
+
 static void register_types(void)
 {
-    register_char_driver("msmouse", CHARDEV_BACKEND_KIND_MSMOUSE, NULL,
-                         qemu_chr_open_msmouse);
+    type_register_static(&char_msmouse_type_info);
 }
 
 type_init(register_types);
diff --git a/backends/rng-egd.c b/backends/rng-egd.c
index 69c04b1b26..380b19a0a1 100644
--- a/backends/rng-egd.c
+++ b/backends/rng-egd.c
@@ -86,7 +86,7 @@ static void rng_egd_chr_read(void *opaque, const uint8_t *buf, int size)
 static void rng_egd_opened(RngBackend *b, Error **errp)
 {
     RngEgd *s = RNG_EGD(b);
-    CharDriverState *chr;
+    Chardev *chr;
 
     if (s->chr_name == NULL) {
         error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
@@ -125,7 +125,7 @@ static void rng_egd_set_chardev(Object *obj, const char *value, Error **errp)
 static char *rng_egd_get_chardev(Object *obj, Error **errp)
 {
     RngEgd *s = RNG_EGD(obj);
-    CharDriverState *chr = qemu_chr_fe_get_driver(&s->chr);
+    Chardev *chr = qemu_chr_fe_get_driver(&s->chr);
 
     if (chr && chr->label) {
         return g_strdup(chr->label);
diff --git a/backends/testdev.c b/backends/testdev.c
index 60156e320e..7df9248a13 100644
--- a/backends/testdev.c
+++ b/backends/testdev.c
@@ -30,13 +30,18 @@
 #define BUF_SIZE 32
 
 typedef struct {
-    CharDriverState *chr;
+    Chardev parent;
+
     uint8_t in_buf[32];
     int in_buf_used;
-} TestdevCharState;
+} TestdevChardev;
+
+#define TYPE_CHARDEV_TESTDEV "chardev-testdev"
+#define TESTDEV_CHARDEV(obj)                                    \
+    OBJECT_CHECK(TestdevChardev, (obj), TYPE_CHARDEV_TESTDEV)
 
 /* Try to interpret a whole incoming packet */
-static int testdev_eat_packet(TestdevCharState *testdev)
+static int testdev_eat_packet(TestdevChardev *testdev)
 {
     const uint8_t *cur = testdev->in_buf;
     int len = testdev->in_buf_used;
@@ -77,9 +82,9 @@ static int testdev_eat_packet(TestdevCharState *testdev)
 }
 
 /* The other end is writing some data.  Store it and try to interpret */
-static int testdev_write(CharDriverState *chr, const uint8_t *buf, int len)
+static int testdev_chr_write(Chardev *chr, const uint8_t *buf, int len)
 {
-    TestdevCharState *testdev = chr->opaque;
+    TestdevChardev *testdev = TESTDEV_CHARDEV(chr);
     int tocopy, eaten, orig_len = len;
 
     while (len) {
@@ -102,36 +107,23 @@ static int testdev_write(CharDriverState *chr, const uint8_t *buf, int len)
     return orig_len;
 }
 
-static void testdev_free(struct CharDriverState *chr)
+static void char_testdev_class_init(ObjectClass *oc, void *data)
 {
-    TestdevCharState *testdev = chr->opaque;
+    ChardevClass *cc = CHARDEV_CLASS(oc);
 
-    g_free(testdev);
+    cc->chr_write = testdev_chr_write;
 }
 
-static CharDriverState *chr_testdev_init(const char *id,
-                                         ChardevBackend *backend,
-                                         ChardevReturn *ret,
-                                         bool *be_opened,
-                                         Error **errp)
-{
-    TestdevCharState *testdev;
-    CharDriverState *chr;
-
-    testdev = g_new0(TestdevCharState, 1);
-    testdev->chr = chr = g_new0(CharDriverState, 1);
-
-    chr->opaque = testdev;
-    chr->chr_write = testdev_write;
-    chr->chr_free = testdev_free;
-
-    return chr;
-}
+static const TypeInfo char_testdev_type_info = {
+    .name = TYPE_CHARDEV_TESTDEV,
+    .parent = TYPE_CHARDEV,
+    .instance_size = sizeof(TestdevChardev),
+    .class_init = char_testdev_class_init,
+};
 
 static void register_types(void)
 {
-    register_char_driver("testdev", CHARDEV_BACKEND_KIND_TESTDEV, NULL,
-                         chr_testdev_init);
+    type_register_static(&char_testdev_type_info);
 }
 
 type_init(register_types);
diff --git a/balloon.c b/balloon.c
index f2ef50cf77..1d720fff81 100644
--- a/balloon.c
+++ b/balloon.c
@@ -29,7 +29,7 @@
 #include "exec/cpu-common.h"
 #include "sysemu/kvm.h"
 #include "sysemu/balloon.h"
-#include "trace.h"
+#include "trace-root.h"
 #include "qmp-commands.h"
 #include "qapi/qmp/qerror.h"
 #include "qapi/qmp/qjson.h"
diff --git a/block.c b/block.c
index a0346c80c6..1dbc060c3f 100644
--- a/block.c
+++ b/block.c
@@ -22,7 +22,7 @@
  * THE SOFTWARE.
  */
 #include "qemu/osdep.h"
-#include "trace.h"
+#include "block/trace.h"
 #include "block/block_int.h"
 #include "block/blockjob.h"
 #include "block/nbd.h"
diff --git a/block/Makefile.objs b/block/Makefile.objs
index 0b8fd06f27..c6bd14e883 100644
--- a/block/Makefile.objs
+++ b/block/Makefile.objs
@@ -14,6 +14,7 @@ block-obj-y += throttle-groups.o
 
 block-obj-y += nbd.o nbd-client.o sheepdog.o
 block-obj-$(CONFIG_LIBISCSI) += iscsi.o
+block-obj-$(if $(CONFIG_LIBISCSI),y,n) += iscsi-opts.o
 block-obj-$(CONFIG_LIBNFS) += nfs.o
 block-obj-$(CONFIG_CURL) += curl.o
 block-obj-$(CONFIG_RBD) += rbd.o
diff --git a/block/file-posix.c b/block/file-posix.c
index 28b47d977b..2134e0ef96 100644
--- a/block/file-posix.c
+++ b/block/file-posix.c
@@ -651,12 +651,15 @@ static void raw_reopen_abort(BDRVReopenState *state)
     state->opaque = NULL;
 }
 
-static int hdev_get_max_transfer_length(int fd)
+static int hdev_get_max_transfer_length(BlockDriverState *bs, int fd)
 {
 #ifdef BLKSECTGET
-    int max_sectors = 0;
-    if (ioctl(fd, BLKSECTGET, &max_sectors) == 0) {
-        return max_sectors;
+    int max_bytes = 0;
+    short max_sectors = 0;
+    if (bs->sg && ioctl(fd, BLKSECTGET, &max_bytes) == 0) {
+        return max_bytes;
+    } else if (!bs->sg && ioctl(fd, BLKSECTGET, &max_sectors) == 0) {
+        return max_sectors << BDRV_SECTOR_BITS;
     } else {
         return -errno;
     }
@@ -671,10 +674,10 @@ static void raw_refresh_limits(BlockDriverState *bs, Error **errp)
     struct stat st;
 
     if (!fstat(s->fd, &st)) {
-        if (S_ISBLK(st.st_mode)) {
-            int ret = hdev_get_max_transfer_length(s->fd);
-            if (ret > 0 && ret <= BDRV_REQUEST_MAX_SECTORS) {
-                bs->bl.max_transfer = pow2floor(ret << BDRV_SECTOR_BITS);
+        if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode)) {
+            int ret = hdev_get_max_transfer_length(bs, s->fd);
+            if (ret > 0 && ret <= BDRV_REQUEST_MAX_BYTES) {
+                bs->bl.max_transfer = pow2floor(ret);
             }
         }
     }
diff --git a/block/iscsi-opts.c b/block/iscsi-opts.c
new file mode 100644
index 0000000000..5335539130
--- /dev/null
+++ b/block/iscsi-opts.c
@@ -0,0 +1,69 @@
+/*
+ * QEMU Block driver for iSCSI images (static options)
+ *
+ * Copyright (c) 2017 Peter Lieven <pl@kamp.de>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "qemu/config-file.h"
+
+static QemuOptsList qemu_iscsi_opts = {
+    .name = "iscsi",
+    .head = QTAILQ_HEAD_INITIALIZER(qemu_iscsi_opts.head),
+    .desc = {
+        {
+            .name = "user",
+            .type = QEMU_OPT_STRING,
+            .help = "username for CHAP authentication to target",
+        },{
+            .name = "password",
+            .type = QEMU_OPT_STRING,
+            .help = "password for CHAP authentication to target",
+        },{
+            .name = "password-secret",
+            .type = QEMU_OPT_STRING,
+            .help = "ID of the secret providing password for CHAP "
+                    "authentication to target",
+        },{
+            .name = "header-digest",
+            .type = QEMU_OPT_STRING,
+            .help = "HeaderDigest setting. "
+                    "{CRC32C|CRC32C-NONE|NONE-CRC32C|NONE}",
+        },{
+            .name = "initiator-name",
+            .type = QEMU_OPT_STRING,
+            .help = "Initiator iqn name to use when connecting",
+        },{
+            .name = "timeout",
+            .type = QEMU_OPT_NUMBER,
+            .help = "Request timeout in seconds (default 0 = no timeout)",
+        },
+        { /* end of list */ }
+    },
+};
+
+static void iscsi_block_opts_init(void)
+{
+    qemu_add_opts(&qemu_iscsi_opts);
+}
+
+block_init(iscsi_block_opts_init);
diff --git a/block/iscsi.c b/block/iscsi.c
index 6aeeb9ec4f..1860f1bc91 100644
--- a/block/iscsi.c
+++ b/block/iscsi.c
@@ -499,14 +499,18 @@ iscsi_allocmap_update(IscsiLun *iscsilun, int64_t sector_num,
     if (allocated) {
         bitmap_set(iscsilun->allocmap, cl_num_expanded, nb_cls_expanded);
     } else {
-        bitmap_clear(iscsilun->allocmap, cl_num_shrunk, nb_cls_shrunk);
+        if (nb_cls_shrunk > 0) {
+            bitmap_clear(iscsilun->allocmap, cl_num_shrunk, nb_cls_shrunk);
+        }
     }
 
     if (iscsilun->allocmap_valid == NULL) {
         return;
     }
     if (valid) {
-        bitmap_set(iscsilun->allocmap_valid, cl_num_shrunk, nb_cls_shrunk);
+        if (nb_cls_shrunk > 0) {
+            bitmap_set(iscsilun->allocmap_valid, cl_num_shrunk, nb_cls_shrunk);
+        }
     } else {
         bitmap_clear(iscsilun->allocmap_valid, cl_num_expanded,
                      nb_cls_expanded);
diff --git a/block/sheepdog.c b/block/sheepdog.c
index 5637e0cd37..f757157cea 100644
--- a/block/sheepdog.c
+++ b/block/sheepdog.c
@@ -306,6 +306,7 @@ static inline size_t count_data_objs(const struct SheepdogInode *inode)
     } while (0)
 
 typedef struct SheepdogAIOCB SheepdogAIOCB;
+typedef struct BDRVSheepdogState BDRVSheepdogState;
 
 typedef struct AIOReq {
     SheepdogAIOCB *aiocb;
@@ -334,7 +335,7 @@ enum AIOCBState {
        || y->max_affect_data_idx < x->min_affect_data_idx))
 
 struct SheepdogAIOCB {
-    BlockAIOCB common;
+    BDRVSheepdogState *s;
 
     QEMUIOVector *qiov;
 
@@ -345,9 +346,6 @@ struct SheepdogAIOCB {
     enum AIOCBState aiocb_type;
 
     Coroutine *coroutine;
-    void (*aio_done_func)(SheepdogAIOCB *);
-
-    bool cancelable;
     int nr_pending;
 
     uint32_t min_affect_data_idx;
@@ -365,7 +363,7 @@ struct SheepdogAIOCB {
     QLIST_ENTRY(SheepdogAIOCB) aiocb_siblings;
 };
 
-typedef struct BDRVSheepdogState {
+struct BDRVSheepdogState {
     BlockDriverState *bs;
     AioContext *aio_context;
 
@@ -392,7 +390,7 @@ typedef struct BDRVSheepdogState {
 
     CoQueue overlapping_queue;
     QLIST_HEAD(inflight_aiocb_head, SheepdogAIOCB) inflight_aiocb_head;
-} BDRVSheepdogState;
+};
 
 typedef struct BDRVSheepdogReopenState {
     int fd;
@@ -450,14 +448,13 @@ static const char * sd_strerror(int err)
  *
  * 1. In sd_co_rw_vector, we send the I/O requests to the server and
  *    link the requests to the inflight_list in the
- *    BDRVSheepdogState.  The function exits without waiting for
+ *    BDRVSheepdogState.  The function yields while waiting for
  *    receiving the response.
  *
  * 2. We receive the response in aio_read_response, the fd handler to
- *    the sheepdog connection.  If metadata update is needed, we send
- *    the write request to the vdi object in sd_write_done, the write
- *    completion function.  We switch back to sd_co_readv/writev after
- *    all the requests belonging to the AIOCB are finished.
+ *    the sheepdog connection.  We switch back to sd_co_readv/sd_writev
+ *    after all the requests belonging to the AIOCB are finished.  If
+ *    needed, sd_co_writev will send another requests for the vdi object.
  */
 
 static inline AIOReq *alloc_aio_req(BDRVSheepdogState *s, SheepdogAIOCB *acb,
@@ -482,94 +479,34 @@ static inline AIOReq *alloc_aio_req(BDRVSheepdogState *s, SheepdogAIOCB *acb,
     return aio_req;
 }
 
-static inline void free_aio_req(BDRVSheepdogState *s, AIOReq *aio_req)
-{
-    SheepdogAIOCB *acb = aio_req->aiocb;
-
-    acb->cancelable = false;
-    QLIST_REMOVE(aio_req, aio_siblings);
-    g_free(aio_req);
-
-    acb->nr_pending--;
-}
-
-static void coroutine_fn sd_finish_aiocb(SheepdogAIOCB *acb)
+static void wait_for_overlapping_aiocb(BDRVSheepdogState *s, SheepdogAIOCB *acb)
 {
-    qemu_coroutine_enter(acb->coroutine);
-    qemu_aio_unref(acb);
-}
-
-/*
- * Check whether the specified acb can be canceled
- *
- * We can cancel aio when any request belonging to the acb is:
- *  - Not processed by the sheepdog server.
- *  - Not linked to the inflight queue.
- */
-static bool sd_acb_cancelable(const SheepdogAIOCB *acb)
-{
-    BDRVSheepdogState *s = acb->common.bs->opaque;
-    AIOReq *aioreq;
-
-    if (!acb->cancelable) {
-        return false;
-    }
-
-    QLIST_FOREACH(aioreq, &s->inflight_aio_head, aio_siblings) {
-        if (aioreq->aiocb == acb) {
-            return false;
-        }
-    }
-
-    return true;
-}
-
-static void sd_aio_cancel(BlockAIOCB *blockacb)
-{
-    SheepdogAIOCB *acb = (SheepdogAIOCB *)blockacb;
-    BDRVSheepdogState *s = acb->common.bs->opaque;
-    AIOReq *aioreq, *next;
-
-    if (sd_acb_cancelable(acb)) {
-        /* Remove outstanding requests from failed queue.  */
-        QLIST_FOREACH_SAFE(aioreq, &s->failed_aio_head, aio_siblings,
-                           next) {
-            if (aioreq->aiocb == acb) {
-                free_aio_req(s, aioreq);
-            }
-        }
+    SheepdogAIOCB *cb;
 
-        assert(acb->nr_pending == 0);
-        if (acb->common.cb) {
-            acb->common.cb(acb->common.opaque, -ECANCELED);
+retry:
+    QLIST_FOREACH(cb, &s->inflight_aiocb_head, aiocb_siblings) {
+        if (AIOCBOverlapping(acb, cb)) {
+            qemu_co_queue_wait(&s->overlapping_queue);
+            goto retry;
         }
-        sd_finish_aiocb(acb);
     }
 }
 
-static const AIOCBInfo sd_aiocb_info = {
-    .aiocb_size     = sizeof(SheepdogAIOCB),
-    .cancel_async   = sd_aio_cancel,
-};
-
-static SheepdogAIOCB *sd_aio_setup(BlockDriverState *bs, QEMUIOVector *qiov,
-                                   int64_t sector_num, int nb_sectors)
+static void sd_aio_setup(SheepdogAIOCB *acb, BDRVSheepdogState *s,
+                         QEMUIOVector *qiov, int64_t sector_num, int nb_sectors,
+                         int type)
 {
-    SheepdogAIOCB *acb;
     uint32_t object_size;
-    BDRVSheepdogState *s = bs->opaque;
 
     object_size = (UINT32_C(1) << s->inode.block_size_shift);
 
-    acb = qemu_aio_get(&sd_aiocb_info, bs, NULL, NULL);
+    acb->s = s;
 
     acb->qiov = qiov;
 
     acb->sector_num = sector_num;
     acb->nb_sectors = nb_sectors;
 
-    acb->aio_done_func = NULL;
-    acb->cancelable = true;
     acb->coroutine = qemu_coroutine_self();
     acb->ret = 0;
     acb->nr_pending = 0;
@@ -580,8 +517,14 @@ static SheepdogAIOCB *sd_aio_setup(BlockDriverState *bs, QEMUIOVector *qiov,
 
     acb->min_dirty_data_idx = UINT32_MAX;
     acb->max_dirty_data_idx = 0;
+    acb->aiocb_type = type;
+
+    if (type == AIOCB_FLUSH_CACHE) {
+        return;
+    }
 
-    return acb;
+    wait_for_overlapping_aiocb(s, acb);
+    QLIST_INSERT_HEAD(&s->inflight_aiocb_head, acb, aiocb_siblings);
 }
 
 /* Return -EIO in case of error, file descriptor on success */
@@ -797,7 +740,6 @@ static coroutine_fn void reconnect_to_sdog(void *opaque)
     while (!QLIST_EMPTY(&s->failed_aio_head)) {
         aio_req = QLIST_FIRST(&s->failed_aio_head);
         QLIST_REMOVE(aio_req, aio_siblings);
-        QLIST_INSERT_HEAD(&s->inflight_aio_head, aio_req, aio_siblings);
         resend_aioreq(s, aio_req);
     }
 }
@@ -840,9 +782,6 @@ static void coroutine_fn aio_read_response(void *opaque)
 
     switch (acb->aiocb_type) {
     case AIOCB_WRITE_UDATA:
-        /* this coroutine context is no longer suitable for co_recv
-         * because we may send data to update vdi objects */
-        s->co_recv = NULL;
         if (!is_data_obj(aio_req->oid)) {
             break;
         }
@@ -890,6 +829,12 @@ static void coroutine_fn aio_read_response(void *opaque)
         }
     }
 
+    /* No more data for this aio_req (reload_inode below uses its own file
+     * descriptor handler which doesn't use co_recv).
+    */
+    s->co_recv = NULL;
+
+    QLIST_REMOVE(aio_req, aio_siblings);
     switch (rsp.result) {
     case SD_RES_SUCCESS:
         break;
@@ -907,26 +852,26 @@ static void coroutine_fn aio_read_response(void *opaque)
             aio_req->oid = vid_to_vdi_oid(s->inode.vdi_id);
         }
         resend_aioreq(s, aio_req);
-        goto out;
+        return;
     default:
         acb->ret = -EIO;
         error_report("%s", sd_strerror(rsp.result));
         break;
     }
 
-    free_aio_req(s, aio_req);
-    if (!acb->nr_pending) {
+    g_free(aio_req);
+
+    if (!--acb->nr_pending) {
         /*
          * We've finished all requests which belong to the AIOCB, so
          * we can switch back to sd_co_readv/writev now.
          */
-        acb->aio_done_func(acb);
+        qemu_coroutine_enter(acb->coroutine);
     }
-out:
-    s->co_recv = NULL;
+
     return;
+
 err:
-    s->co_recv = NULL;
     reconnect_to_sdog(opaque);
 }
 
@@ -1176,6 +1121,8 @@ static void coroutine_fn add_aio_request(BDRVSheepdogState *s, AIOReq *aio_req,
     uint64_t old_oid = aio_req->base_oid;
     bool create = aio_req->create;
 
+    QLIST_INSERT_HEAD(&s->inflight_aio_head, aio_req, aio_siblings);
+
     if (!nr_copies) {
         error_report("bug");
     }
@@ -2025,11 +1972,10 @@ static int sd_truncate(BlockDriverState *bs, int64_t offset)
 /*
  * This function is called after writing data objects.  If we need to
  * update metadata, this sends a write request to the vdi object.
- * Otherwise, this switches back to sd_co_readv/writev.
  */
 static void coroutine_fn sd_write_done(SheepdogAIOCB *acb)
 {
-    BDRVSheepdogState *s = acb->common.bs->opaque;
+    BDRVSheepdogState *s = acb->s;
     struct iovec iov;
     AIOReq *aio_req;
     uint32_t offset, data_len, mn, mx;
@@ -2038,6 +1984,7 @@ static void coroutine_fn sd_write_done(SheepdogAIOCB *acb)
     mx = acb->max_dirty_data_idx;
     if (mn <= mx) {
         /* we need to update the vdi object. */
+        ++acb->nr_pending;
         offset = sizeof(s->inode) - sizeof(s->inode.data_vdi_id) +
             mn * sizeof(s->inode.data_vdi_id[0]);
         data_len = (mx - mn + 1) * sizeof(s->inode.data_vdi_id[0]);
@@ -2049,15 +1996,11 @@ static void coroutine_fn sd_write_done(SheepdogAIOCB *acb)
         iov.iov_len = sizeof(s->inode);
         aio_req = alloc_aio_req(s, acb, vid_to_vdi_oid(s->inode.vdi_id),
                                 data_len, offset, 0, false, 0, offset);
-        QLIST_INSERT_HEAD(&s->inflight_aio_head, aio_req, aio_siblings);
         add_aio_request(s, aio_req, &iov, 1, AIOCB_WRITE_UDATA);
-
-        acb->aio_done_func = sd_finish_aiocb;
-        acb->aiocb_type = AIOCB_WRITE_UDATA;
-        return;
+        if (--acb->nr_pending) {
+            qemu_coroutine_yield();
+        }
     }
-
-    sd_finish_aiocb(acb);
 }
 
 /* Delete current working VDI on the snapshot chain */
@@ -2169,16 +2112,15 @@ out:
  * Returns 1 when we need to wait a response, 0 when there is no sent
  * request and -errno in error cases.
  */
-static int coroutine_fn sd_co_rw_vector(void *p)
+static void coroutine_fn sd_co_rw_vector(SheepdogAIOCB *acb)
 {
-    SheepdogAIOCB *acb = p;
     int ret = 0;
     unsigned long len, done = 0, total = acb->nb_sectors * BDRV_SECTOR_SIZE;
     unsigned long idx;
     uint32_t object_size;
     uint64_t oid;
     uint64_t offset;
-    BDRVSheepdogState *s = acb->common.bs->opaque;
+    BDRVSheepdogState *s = acb->s;
     SheepdogInode *inode = &s->inode;
     AIOReq *aio_req;
 
@@ -2190,7 +2132,7 @@ static int coroutine_fn sd_co_rw_vector(void *p)
         ret = sd_create_branch(s);
         if (ret) {
             acb->ret = -EIO;
-            goto out;
+            return;
         }
     }
 
@@ -2255,8 +2197,6 @@ static int coroutine_fn sd_co_rw_vector(void *p)
                                 old_oid,
                                 acb->aiocb_type == AIOCB_DISCARD_OBJ ?
                                 0 : done);
-        QLIST_INSERT_HEAD(&s->inflight_aio_head, aio_req, aio_siblings);
-
         add_aio_request(s, aio_req, acb->qiov->iov, acb->qiov->niov,
                         acb->aiocb_type);
     done:
@@ -2264,31 +2204,25 @@ static int coroutine_fn sd_co_rw_vector(void *p)
         idx++;
         done += len;
     }
-out:
-    if (!--acb->nr_pending) {
-        return acb->ret;
+    if (--acb->nr_pending) {
+        qemu_coroutine_yield();
     }
-    return 1;
 }
 
-static bool check_overlapping_aiocb(BDRVSheepdogState *s, SheepdogAIOCB *aiocb)
+static void sd_aio_complete(SheepdogAIOCB *acb)
 {
-    SheepdogAIOCB *cb;
-
-    QLIST_FOREACH(cb, &s->inflight_aiocb_head, aiocb_siblings) {
-        if (AIOCBOverlapping(aiocb, cb)) {
-            return true;
-        }
+    if (acb->aiocb_type == AIOCB_FLUSH_CACHE) {
+        return;
     }
 
-    QLIST_INSERT_HEAD(&s->inflight_aiocb_head, aiocb, aiocb_siblings);
-    return false;
+    QLIST_REMOVE(acb, aiocb_siblings);
+    qemu_co_queue_restart_all(&acb->s->overlapping_queue);
 }
 
 static coroutine_fn int sd_co_writev(BlockDriverState *bs, int64_t sector_num,
                         int nb_sectors, QEMUIOVector *qiov)
 {
-    SheepdogAIOCB *acb;
+    SheepdogAIOCB acb;
     int ret;
     int64_t offset = (sector_num + nb_sectors) * BDRV_SECTOR_SIZE;
     BDRVSheepdogState *s = bs->opaque;
@@ -2300,85 +2234,50 @@ static coroutine_fn int sd_co_writev(BlockDriverState *bs, int64_t sector_num,
         }
     }
 
-    acb = sd_aio_setup(bs, qiov, sector_num, nb_sectors);
-    acb->aio_done_func = sd_write_done;
-    acb->aiocb_type = AIOCB_WRITE_UDATA;
-
-retry:
-    if (check_overlapping_aiocb(s, acb)) {
-        qemu_co_queue_wait(&s->overlapping_queue);
-        goto retry;
-    }
-
-    ret = sd_co_rw_vector(acb);
-    if (ret <= 0) {
-        QLIST_REMOVE(acb, aiocb_siblings);
-        qemu_co_queue_restart_all(&s->overlapping_queue);
-        qemu_aio_unref(acb);
-        return ret;
-    }
-
-    qemu_coroutine_yield();
-
-    QLIST_REMOVE(acb, aiocb_siblings);
-    qemu_co_queue_restart_all(&s->overlapping_queue);
+    sd_aio_setup(&acb, s, qiov, sector_num, nb_sectors, AIOCB_WRITE_UDATA);
+    sd_co_rw_vector(&acb);
+    sd_write_done(&acb);
+    sd_aio_complete(&acb);
 
-    return acb->ret;
+    return acb.ret;
 }
 
 static coroutine_fn int sd_co_readv(BlockDriverState *bs, int64_t sector_num,
                        int nb_sectors, QEMUIOVector *qiov)
 {
-    SheepdogAIOCB *acb;
-    int ret;
+    SheepdogAIOCB acb;
     BDRVSheepdogState *s = bs->opaque;
 
-    acb = sd_aio_setup(bs, qiov, sector_num, nb_sectors);
-    acb->aiocb_type = AIOCB_READ_UDATA;
-    acb->aio_done_func = sd_finish_aiocb;
+    sd_aio_setup(&acb, s, qiov, sector_num, nb_sectors, AIOCB_READ_UDATA);
+    sd_co_rw_vector(&acb);
+    sd_aio_complete(&acb);
 
-retry:
-    if (check_overlapping_aiocb(s, acb)) {
-        qemu_co_queue_wait(&s->overlapping_queue);
-        goto retry;
-    }
-
-    ret = sd_co_rw_vector(acb);
-    if (ret <= 0) {
-        QLIST_REMOVE(acb, aiocb_siblings);
-        qemu_co_queue_restart_all(&s->overlapping_queue);
-        qemu_aio_unref(acb);
-        return ret;
-    }
-
-    qemu_coroutine_yield();
-
-    QLIST_REMOVE(acb, aiocb_siblings);
-    qemu_co_queue_restart_all(&s->overlapping_queue);
-    return acb->ret;
+    return acb.ret;
 }
 
 static int coroutine_fn sd_co_flush_to_disk(BlockDriverState *bs)
 {
     BDRVSheepdogState *s = bs->opaque;
-    SheepdogAIOCB *acb;
+    SheepdogAIOCB acb;
     AIOReq *aio_req;
 
     if (s->cache_flags != SD_FLAG_CMD_CACHE) {
         return 0;
     }
 
-    acb = sd_aio_setup(bs, NULL, 0, 0);
-    acb->aiocb_type = AIOCB_FLUSH_CACHE;
-    acb->aio_done_func = sd_finish_aiocb;
+    sd_aio_setup(&acb, s, NULL, 0, 0, AIOCB_FLUSH_CACHE);
 
-    aio_req = alloc_aio_req(s, acb, vid_to_vdi_oid(s->inode.vdi_id),
+    acb.nr_pending++;
+    aio_req = alloc_aio_req(s, &acb, vid_to_vdi_oid(s->inode.vdi_id),
                             0, 0, 0, false, 0, 0);
-    QLIST_INSERT_HEAD(&s->inflight_aio_head, aio_req, aio_siblings);
-    add_aio_request(s, aio_req, NULL, 0, acb->aiocb_type);
+    add_aio_request(s, aio_req, NULL, 0, acb.aiocb_type);
 
-    qemu_coroutine_yield();
-    return acb->ret;
+    if (--acb.nr_pending) {
+        qemu_coroutine_yield();
+    }
+
+    sd_aio_complete(&acb);
+    return acb.ret;
 }
 
 static int sd_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
@@ -2812,9 +2711,8 @@ static int sd_load_vmstate(BlockDriverState *bs, QEMUIOVector *qiov,
 static coroutine_fn int sd_co_pdiscard(BlockDriverState *bs, int64_t offset,
                                       int count)
 {
-    SheepdogAIOCB *acb;
+    SheepdogAIOCB acb;
     BDRVSheepdogState *s = bs->opaque;
-    int ret;
     QEMUIOVector discard_iov;
     struct iovec iov;
     uint32_t zero = 0;
@@ -2832,31 +2730,12 @@ static coroutine_fn int sd_co_pdiscard(BlockDriverState *bs, int64_t offset,
     if (!QEMU_IS_ALIGNED(offset | count, BDRV_SECTOR_SIZE)) {
         return -ENOTSUP;
     }
-    acb = sd_aio_setup(bs, &discard_iov, offset >> BDRV_SECTOR_BITS,
-                       count >> BDRV_SECTOR_BITS);
-    acb->aiocb_type = AIOCB_DISCARD_OBJ;
-    acb->aio_done_func = sd_finish_aiocb;
-
-retry:
-    if (check_overlapping_aiocb(s, acb)) {
-        qemu_co_queue_wait(&s->overlapping_queue);
-        goto retry;
-    }
-
-    ret = sd_co_rw_vector(acb);
-    if (ret <= 0) {
-        QLIST_REMOVE(acb, aiocb_siblings);
-        qemu_co_queue_restart_all(&s->overlapping_queue);
-        qemu_aio_unref(acb);
-        return ret;
-    }
-
-    qemu_coroutine_yield();
-
-    QLIST_REMOVE(acb, aiocb_siblings);
-    qemu_co_queue_restart_all(&s->overlapping_queue);
+    sd_aio_setup(&acb, s, &discard_iov, offset >> BDRV_SECTOR_BITS,
+                 count >> BDRV_SECTOR_BITS, AIOCB_DISCARD_OBJ);
+    sd_co_rw_vector(&acb);
+    sd_aio_complete(&acb);
 
-    return acb->ret;
+    return acb.ret;
 }
 
 static coroutine_fn int64_t
diff --git a/block/trace-events b/block/trace-events
index 671a6a851c..0bc5c0adf1 100644
--- a/block/trace-events
+++ b/block/trace-events
@@ -35,8 +35,6 @@ mirror_one_iteration(void *s, int64_t sector_num, int nb_sectors) "s %p sector_n
 mirror_iteration_done(void *s, int64_t sector_num, int nb_sectors, int ret) "s %p sector_num %"PRId64" nb_sectors %d ret %d"
 mirror_yield(void *s, int64_t cnt, int buf_free_count, int in_flight) "s %p dirty count %"PRId64" free buffers %d in_flight %d"
 mirror_yield_in_flight(void *s, int64_t sector_num, int in_flight) "s %p sector_num %"PRId64" in_flight %d"
-mirror_yield_buf_busy(void *s, int nb_chunks, int in_flight) "s %p requested chunks %d in_flight %d"
-mirror_break_buf_busy(void *s, int nb_chunks, int in_flight) "s %p requested chunks %d in_flight %d"
 
 # block/backup.c
 backup_do_cow_enter(void *job, int64_t start, int64_t sector_num, int nb_sectors) "job %p start %"PRId64" sector_num %"PRId64" nb_sectors %d"
diff --git a/blockdev-nbd.c b/blockdev-nbd.c
index 81bca1760f..7ea836b46e 100644
--- a/blockdev-nbd.c
+++ b/blockdev-nbd.c
@@ -16,7 +16,6 @@
 #include "qapi/qmp/qerror.h"
 #include "sysemu/sysemu.h"
 #include "qmp-commands.h"
-#include "trace.h"
 #include "block/nbd.h"
 #include "io/channel-socket.h"
 
diff --git a/blockdev.c b/blockdev.c
index 245e1e1d17..db82ac97e5 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -48,7 +48,7 @@
 #include "sysemu/sysemu.h"
 #include "block/block_int.h"
 #include "qmp-commands.h"
-#include "trace.h"
+#include "block/trace.h"
 #include "sysemu/arch_init.h"
 #include "qemu/cutils.h"
 #include "qemu/help_option.h"
diff --git a/blockjob.c b/blockjob.c
index 513620c199..abee11bb08 100644
--- a/blockjob.c
+++ b/blockjob.c
@@ -25,7 +25,6 @@
 
 #include "qemu/osdep.h"
 #include "qemu-common.h"
-#include "trace.h"
 #include "block/block.h"
 #include "block/blockjob_int.h"
 #include "block/block_int.h"
diff --git a/chardev/Makefile.objs b/chardev/Makefile.objs
new file mode 100644
index 0000000000..1feda0f0ed
--- /dev/null
+++ b/chardev/Makefile.objs
@@ -0,0 +1,17 @@
+chardev-obj-y += char.o
+chardev-obj-$(CONFIG_WIN32) += char-console.o
+chardev-obj-$(CONFIG_POSIX) += char-fd.o
+chardev-obj-y += char-file.o
+chardev-obj-y += char-io.o
+chardev-obj-y += char-mux.o
+chardev-obj-y += char-null.o
+chardev-obj-$(CONFIG_POSIX) += char-parallel.o
+chardev-obj-y += char-pipe.o
+chardev-obj-$(CONFIG_POSIX) += char-pty.o
+chardev-obj-y += char-ringbuf.o
+chardev-obj-y += char-serial.o
+chardev-obj-y += char-socket.o
+chardev-obj-y += char-stdio.o
+chardev-obj-y += char-udp.o
+chardev-obj-$(CONFIG_WIN32) += char-win.o
+chardev-obj-$(CONFIG_WIN32) += char-win-stdio.o
diff --git a/chardev/char-console.c b/chardev/char-console.c
new file mode 100644
index 0000000000..c824937fe6
--- /dev/null
+++ b/chardev/char-console.c
@@ -0,0 +1,53 @@
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu/osdep.h"
+#include "char-win.h"
+
+static void qemu_chr_open_win_con(Chardev *chr,
+                                  ChardevBackend *backend,
+                                  bool *be_opened,
+                                  Error **errp)
+{
+    qemu_chr_open_win_file(chr, GetStdHandle(STD_OUTPUT_HANDLE));
+}
+
+static void char_console_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
+
+    cc->open = qemu_chr_open_win_con;
+}
+
+static const TypeInfo char_console_type_info = {
+    .name = TYPE_CHARDEV_CONSOLE,
+    .parent = TYPE_CHARDEV_WIN,
+    .class_init = char_console_class_init,
+};
+
+static void register_types(void)
+{
+    type_register_static(&char_console_type_info);
+}
+
+type_init(register_types);
diff --git a/chardev/char-fd.c b/chardev/char-fd.c
new file mode 100644
index 0000000000..fb51ab4234
--- /dev/null
+++ b/chardev/char-fd.c
@@ -0,0 +1,170 @@
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu/osdep.h"
+#include "qemu/sockets.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "sysemu/char.h"
+#include "io/channel-file.h"
+
+#include "char-fd.h"
+#include "char-io.h"
+
+/* Called with chr_write_lock held.  */
+static int fd_chr_write(Chardev *chr, const uint8_t *buf, int len)
+{
+    FDChardev *s = FD_CHARDEV(chr);
+
+    return io_channel_send(s->ioc_out, buf, len);
+}
+
+static gboolean fd_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
+{
+    Chardev *chr = CHARDEV(opaque);
+    FDChardev *s = FD_CHARDEV(opaque);
+    int len;
+    uint8_t buf[CHR_READ_BUF_LEN];
+    ssize_t ret;
+
+    len = sizeof(buf);
+    if (len > s->max_size) {
+        len = s->max_size;
+    }
+    if (len == 0) {
+        return TRUE;
+    }
+
+    ret = qio_channel_read(
+        chan, (gchar *)buf, len, NULL);
+    if (ret == 0) {
+        remove_fd_in_watch(chr);
+        qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
+        return FALSE;
+    }
+    if (ret > 0) {
+        qemu_chr_be_write(chr, buf, ret);
+    }
+
+    return TRUE;
+}
+
+static int fd_chr_read_poll(void *opaque)
+{
+    Chardev *chr = CHARDEV(opaque);
+    FDChardev *s = FD_CHARDEV(opaque);
+
+    s->max_size = qemu_chr_be_can_write(chr);
+    return s->max_size;
+}
+
+static GSource *fd_chr_add_watch(Chardev *chr, GIOCondition cond)
+{
+    FDChardev *s = FD_CHARDEV(chr);
+    return qio_channel_create_watch(s->ioc_out, cond);
+}
+
+static void fd_chr_update_read_handler(Chardev *chr,
+                                       GMainContext *context)
+{
+    FDChardev *s = FD_CHARDEV(chr);
+
+    remove_fd_in_watch(chr);
+    if (s->ioc_in) {
+        chr->fd_in_tag = io_add_watch_poll(chr, s->ioc_in,
+                                           fd_chr_read_poll,
+                                           fd_chr_read, chr,
+                                           context);
+    }
+}
+
+static void char_fd_finalize(Object *obj)
+{
+    Chardev *chr = CHARDEV(obj);
+    FDChardev *s = FD_CHARDEV(obj);
+
+    remove_fd_in_watch(chr);
+    if (s->ioc_in) {
+        object_unref(OBJECT(s->ioc_in));
+    }
+    if (s->ioc_out) {
+        object_unref(OBJECT(s->ioc_out));
+    }
+
+    qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
+}
+
+int qmp_chardev_open_file_source(char *src, int flags, Error **errp)
+{
+    int fd = -1;
+
+    TFR(fd = qemu_open(src, flags, 0666));
+    if (fd == -1) {
+        error_setg_file_open(errp, errno, src);
+    }
+    return fd;
+}
+
+/* open a character device to a unix fd */
+void qemu_chr_open_fd(Chardev *chr,
+                      int fd_in, int fd_out)
+{
+    FDChardev *s = FD_CHARDEV(chr);
+    char *name;
+
+    s->ioc_in = QIO_CHANNEL(qio_channel_file_new_fd(fd_in));
+    name = g_strdup_printf("chardev-file-in-%s", chr->label);
+    qio_channel_set_name(QIO_CHANNEL(s->ioc_in), name);
+    g_free(name);
+    s->ioc_out = QIO_CHANNEL(qio_channel_file_new_fd(fd_out));
+    name = g_strdup_printf("chardev-file-out-%s", chr->label);
+    qio_channel_set_name(QIO_CHANNEL(s->ioc_out), name);
+    g_free(name);
+    qemu_set_nonblock(fd_out);
+    s->chr = chr;
+}
+
+static void char_fd_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
+
+    cc->chr_add_watch = fd_chr_add_watch;
+    cc->chr_write = fd_chr_write;
+    cc->chr_update_read_handler = fd_chr_update_read_handler;
+}
+
+static const TypeInfo char_fd_type_info = {
+    .name = TYPE_CHARDEV_FD,
+    .parent = TYPE_CHARDEV,
+    .instance_size = sizeof(FDChardev),
+    .instance_finalize = char_fd_finalize,
+    .class_init = char_fd_class_init,
+    .abstract = true,
+};
+
+static void register_types(void)
+{
+    type_register_static(&char_fd_type_info);
+}
+
+type_init(register_types);
diff --git a/chardev/char-fd.h b/chardev/char-fd.h
new file mode 100644
index 0000000000..d8327982fb
--- /dev/null
+++ b/chardev/char-fd.h
@@ -0,0 +1,44 @@
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef CHAR_FD_H
+#define CHAR_FD_H
+
+#include "io/channel.h"
+#include "sysemu/char.h"
+
+typedef struct FDChardev {
+    Chardev parent;
+    Chardev *chr;
+    QIOChannel *ioc_in, *ioc_out;
+    int max_size;
+} FDChardev;
+
+#define TYPE_CHARDEV_FD "chardev-fd"
+
+#define FD_CHARDEV(obj) OBJECT_CHECK(FDChardev, (obj), TYPE_CHARDEV_FD)
+
+void qemu_chr_open_fd(Chardev *chr, int fd_in, int fd_out);
+int qmp_chardev_open_file_source(char *src, int flags, Error **errp);
+
+#endif /* CHAR_FD_H */
diff --git a/chardev/char-file.c b/chardev/char-file.c
new file mode 100644
index 0000000000..8bae25350d
--- /dev/null
+++ b/chardev/char-file.c
@@ -0,0 +1,139 @@
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "sysemu/char.h"
+
+#ifdef _WIN32
+#include "char-win.h"
+#else
+#include "char-fd.h"
+#endif
+
+static void qmp_chardev_open_file(Chardev *chr,
+                                  ChardevBackend *backend,
+                                  bool *be_opened,
+                                  Error **errp)
+{
+    ChardevFile *file = backend->u.file.data;
+#ifdef _WIN32
+    HANDLE out;
+    DWORD accessmode;
+    DWORD flags;
+
+    if (file->has_in) {
+        error_setg(errp, "input file not supported");
+        return;
+    }
+
+    if (file->has_append && file->append) {
+        /* Append to file if it already exists. */
+        accessmode = FILE_GENERIC_WRITE & ~FILE_WRITE_DATA;
+        flags = OPEN_ALWAYS;
+    } else {
+        /* Truncate file if it already exists. */
+        accessmode = GENERIC_WRITE;
+        flags = CREATE_ALWAYS;
+    }
+
+    out = CreateFile(file->out, accessmode, FILE_SHARE_READ, NULL, flags,
+                     FILE_ATTRIBUTE_NORMAL, NULL);
+    if (out == INVALID_HANDLE_VALUE) {
+        error_setg(errp, "open %s failed", file->out);
+        return;
+    }
+
+    qemu_chr_open_win_file(chr, out);
+#else
+    int flags, in = -1, out;
+
+    flags = O_WRONLY | O_CREAT | O_BINARY;
+    if (file->has_append && file->append) {
+        flags |= O_APPEND;
+    } else {
+        flags |= O_TRUNC;
+    }
+
+    out = qmp_chardev_open_file_source(file->out, flags, errp);
+    if (out < 0) {
+        return;
+    }
+
+    if (file->has_in) {
+        flags = O_RDONLY;
+        in = qmp_chardev_open_file_source(file->in, flags, errp);
+        if (in < 0) {
+            qemu_close(out);
+            return;
+        }
+    }
+
+    qemu_chr_open_fd(chr, in, out);
+#endif
+}
+
+static void qemu_chr_parse_file_out(QemuOpts *opts, ChardevBackend *backend,
+                                    Error **errp)
+{
+    const char *path = qemu_opt_get(opts, "path");
+    ChardevFile *file;
+
+    backend->type = CHARDEV_BACKEND_KIND_FILE;
+    if (path == NULL) {
+        error_setg(errp, "chardev: file: no filename given");
+        return;
+    }
+    file = backend->u.file.data = g_new0(ChardevFile, 1);
+    qemu_chr_parse_common(opts, qapi_ChardevFile_base(file));
+    file->out = g_strdup(path);
+
+    file->has_append = true;
+    file->append = qemu_opt_get_bool(opts, "append", false);
+}
+
+static void char_file_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
+
+    cc->parse = qemu_chr_parse_file_out;
+    cc->open = qmp_chardev_open_file;
+}
+
+static const TypeInfo char_file_type_info = {
+    .name = TYPE_CHARDEV_FILE,
+#ifdef _WIN32
+    .parent = TYPE_CHARDEV_WIN,
+#else
+    .parent = TYPE_CHARDEV_FD,
+#endif
+    .class_init = char_file_class_init,
+};
+
+static void register_types(void)
+{
+    type_register_static(&char_file_type_info);
+}
+
+type_init(register_types);
diff --git a/chardev/char-io.c b/chardev/char-io.c
new file mode 100644
index 0000000000..7dfc3f22ba
--- /dev/null
+++ b/chardev/char-io.c
@@ -0,0 +1,192 @@
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu/osdep.h"
+#include "char-io.h"
+
+typedef struct IOWatchPoll {
+    GSource parent;
+
+    QIOChannel *ioc;
+    GSource *src;
+
+    IOCanReadHandler *fd_can_read;
+    GSourceFunc fd_read;
+    void *opaque;
+    GMainContext *context;
+} IOWatchPoll;
+
+static IOWatchPoll *io_watch_poll_from_source(GSource *source)
+{
+    return container_of(source, IOWatchPoll, parent);
+}
+
+static gboolean io_watch_poll_prepare(GSource *source,
+                                      gint *timeout)
+{
+    IOWatchPoll *iwp = io_watch_poll_from_source(source);
+    bool now_active = iwp->fd_can_read(iwp->opaque) > 0;
+    bool was_active = iwp->src != NULL;
+    if (was_active == now_active) {
+        return FALSE;
+    }
+
+    if (now_active) {
+        iwp->src = qio_channel_create_watch(
+            iwp->ioc, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL);
+        g_source_set_callback(iwp->src, iwp->fd_read, iwp->opaque, NULL);
+        g_source_attach(iwp->src, iwp->context);
+    } else {
+        g_source_destroy(iwp->src);
+        g_source_unref(iwp->src);
+        iwp->src = NULL;
+    }
+    return FALSE;
+}
+
+static gboolean io_watch_poll_check(GSource *source)
+{
+    return FALSE;
+}
+
+static gboolean io_watch_poll_dispatch(GSource *source, GSourceFunc callback,
+                                       gpointer user_data)
+{
+    abort();
+}
+
+static void io_watch_poll_finalize(GSource *source)
+{
+    /* Due to a glib bug, removing the last reference to a source
+     * inside a finalize callback causes recursive locking (and a
+     * deadlock).  This is not a problem inside other callbacks,
+     * including dispatch callbacks, so we call io_remove_watch_poll
+     * to remove this source.  At this point, iwp->src must
+     * be NULL, or we would leak it.
+     *
+     * This would be solved much more elegantly by child sources,
+     * but we support older glib versions that do not have them.
+     */
+    IOWatchPoll *iwp = io_watch_poll_from_source(source);
+    assert(iwp->src == NULL);
+}
+
+static GSourceFuncs io_watch_poll_funcs = {
+    .prepare = io_watch_poll_prepare,
+    .check = io_watch_poll_check,
+    .dispatch = io_watch_poll_dispatch,
+    .finalize = io_watch_poll_finalize,
+};
+
+guint io_add_watch_poll(Chardev *chr,
+                        QIOChannel *ioc,
+                        IOCanReadHandler *fd_can_read,
+                        QIOChannelFunc fd_read,
+                        gpointer user_data,
+                        GMainContext *context)
+{
+    IOWatchPoll *iwp;
+    int tag;
+    char *name;
+
+    iwp = (IOWatchPoll *) g_source_new(&io_watch_poll_funcs,
+                                       sizeof(IOWatchPoll));
+    iwp->fd_can_read = fd_can_read;
+    iwp->opaque = user_data;
+    iwp->ioc = ioc;
+    iwp->fd_read = (GSourceFunc) fd_read;
+    iwp->src = NULL;
+    iwp->context = context;
+
+    name = g_strdup_printf("chardev-iowatch-%s", chr->label);
+    g_source_set_name((GSource *)iwp, name);
+    g_free(name);
+
+    tag = g_source_attach(&iwp->parent, context);
+    g_source_unref(&iwp->parent);
+    return tag;
+}
+
+static void io_remove_watch_poll(guint tag)
+{
+    GSource *source;
+    IOWatchPoll *iwp;
+
+    g_return_if_fail(tag > 0);
+
+    source = g_main_context_find_source_by_id(NULL, tag);
+    g_return_if_fail(source != NULL);
+
+    iwp = io_watch_poll_from_source(source);
+    if (iwp->src) {
+        g_source_destroy(iwp->src);
+        g_source_unref(iwp->src);
+        iwp->src = NULL;
+    }
+    g_source_destroy(&iwp->parent);
+}
+
+void remove_fd_in_watch(Chardev *chr)
+{
+    if (chr->fd_in_tag) {
+        io_remove_watch_poll(chr->fd_in_tag);
+        chr->fd_in_tag = 0;
+    }
+}
+
+int io_channel_send_full(QIOChannel *ioc,
+                         const void *buf, size_t len,
+                         int *fds, size_t nfds)
+{
+    size_t offset = 0;
+
+    while (offset < len) {
+        ssize_t ret = 0;
+        struct iovec iov = { .iov_base = (char *)buf + offset,
+                             .iov_len = len - offset };
+
+        ret = qio_channel_writev_full(
+            ioc, &iov, 1,
+            fds, nfds, NULL);
+        if (ret == QIO_CHANNEL_ERR_BLOCK) {
+            if (offset) {
+                return offset;
+            }
+
+            errno = EAGAIN;
+            return -1;
+        } else if (ret < 0) {
+            errno = EINVAL;
+            return -1;
+        }
+
+        offset += ret;
+    }
+
+    return offset;
+}
+
+int io_channel_send(QIOChannel *ioc, const void *buf, size_t len)
+{
+    return io_channel_send_full(ioc, buf, len, NULL, 0);
+}
diff --git a/chardev/char-io.h b/chardev/char-io.h
new file mode 100644
index 0000000000..d7ae5f1585
--- /dev/null
+++ b/chardev/char-io.h
@@ -0,0 +1,46 @@
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef CHAR_IO_H
+#define CHAR_IO_H
+
+#include "qemu-common.h"
+#include "io/channel.h"
+#include "sysemu/char.h"
+
+/* Can only be used for read */
+guint io_add_watch_poll(Chardev *chr,
+                        QIOChannel *ioc,
+                        IOCanReadHandler *fd_can_read,
+                        QIOChannelFunc fd_read,
+                        gpointer user_data,
+                        GMainContext *context);
+
+void remove_fd_in_watch(Chardev *chr);
+
+int io_channel_send(QIOChannel *ioc, const void *buf, size_t len);
+
+int io_channel_send_full(QIOChannel *ioc, const void *buf, size_t len,
+                         int *fds, size_t nfds);
+
+#endif /* CHAR_IO_H */
diff --git a/chardev/char-mux.c b/chardev/char-mux.c
new file mode 100644
index 0000000000..5547a36a0a
--- /dev/null
+++ b/chardev/char-mux.c
@@ -0,0 +1,358 @@
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "sysemu/char.h"
+#include "sysemu/block-backend.h"
+#include "char-mux.h"
+
+/* MUX driver for serial I/O splitting */
+
+/* Called with chr_write_lock held.  */
+static int mux_chr_write(Chardev *chr, const uint8_t *buf, int len)
+{
+    MuxChardev *d = MUX_CHARDEV(chr);
+    int ret;
+    if (!d->timestamps) {
+        ret = qemu_chr_fe_write(&d->chr, buf, len);
+    } else {
+        int i;
+
+        ret = 0;
+        for (i = 0; i < len; i++) {
+            if (d->linestart) {
+                char buf1[64];
+                int64_t ti;
+                int secs;
+
+                ti = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
+                if (d->timestamps_start == -1) {
+                    d->timestamps_start = ti;
+                }
+                ti -= d->timestamps_start;
+                secs = ti / 1000;
+                snprintf(buf1, sizeof(buf1),
+                         "[%02d:%02d:%02d.%03d] ",
+                         secs / 3600,
+                         (secs / 60) % 60,
+                         secs % 60,
+                         (int)(ti % 1000));
+                /* XXX this blocks entire thread. Rewrite to use
+                 * qemu_chr_fe_write and background I/O callbacks */
+                qemu_chr_fe_write_all(&d->chr,
+                                      (uint8_t *)buf1, strlen(buf1));
+                d->linestart = 0;
+            }
+            ret += qemu_chr_fe_write(&d->chr, buf + i, 1);
+            if (buf[i] == '\n') {
+                d->linestart = 1;
+            }
+        }
+    }
+    return ret;
+}
+
+static const char * const mux_help[] = {
+    "% h    print this help\n\r",
+    "% x    exit emulator\n\r",
+    "% s    save disk data back to file (if -snapshot)\n\r",
+    "% t    toggle console timestamps\n\r",
+    "% b    send break (magic sysrq)\n\r",
+    "% c    switch between console and monitor\n\r",
+    "% %  sends %\n\r",
+    NULL
+};
+
+int term_escape_char = 0x01; /* ctrl-a is used for escape */
+static void mux_print_help(Chardev *chr)
+{
+    int i, j;
+    char ebuf[15] = "Escape-Char";
+    char cbuf[50] = "\n\r";
+
+    if (term_escape_char > 0 && term_escape_char < 26) {
+        snprintf(cbuf, sizeof(cbuf), "\n\r");
+        snprintf(ebuf, sizeof(ebuf), "C-%c", term_escape_char - 1 + 'a');
+    } else {
+        snprintf(cbuf, sizeof(cbuf),
+                 "\n\rEscape-Char set to Ascii: 0x%02x\n\r\n\r",
+                 term_escape_char);
+    }
+    /* XXX this blocks entire thread. Rewrite to use
+     * qemu_chr_fe_write and background I/O callbacks */
+    qemu_chr_write_all(chr, (uint8_t *)cbuf, strlen(cbuf));
+    for (i = 0; mux_help[i] != NULL; i++) {
+        for (j = 0; mux_help[i][j] != '\0'; j++) {
+            if (mux_help[i][j] == '%') {
+                qemu_chr_write_all(chr, (uint8_t *)ebuf, strlen(ebuf));
+            } else {
+                qemu_chr_write_all(chr, (uint8_t *)&mux_help[i][j], 1);
+            }
+        }
+    }
+}
+
+void mux_chr_send_event(MuxChardev *d, int mux_nr, int event)
+{
+    CharBackend *be = d->backends[mux_nr];
+
+    if (be && be->chr_event) {
+        be->chr_event(be->opaque, event);
+    }
+}
+
+static int mux_proc_byte(Chardev *chr, MuxChardev *d, int ch)
+{
+    if (d->term_got_escape) {
+        d->term_got_escape = 0;
+        if (ch == term_escape_char) {
+            goto send_char;
+        }
+        switch (ch) {
+        case '?':
+        case 'h':
+            mux_print_help(chr);
+            break;
+        case 'x':
+            {
+                 const char *term =  "QEMU: Terminated\n\r";
+                 qemu_chr_write_all(chr, (uint8_t *)term, strlen(term));
+                 exit(0);
+                 break;
+            }
+        case 's':
+            blk_commit_all();
+            break;
+        case 'b':
+            qemu_chr_be_event(chr, CHR_EVENT_BREAK);
+            break;
+        case 'c':
+            assert(d->mux_cnt > 0); /* handler registered with first fe */
+            /* Switch to the next registered device */
+            mux_set_focus(chr, (d->focus + 1) % d->mux_cnt);
+            break;
+        case 't':
+            d->timestamps = !d->timestamps;
+            d->timestamps_start = -1;
+            d->linestart = 0;
+            break;
+        }
+    } else if (ch == term_escape_char) {
+        d->term_got_escape = 1;
+    } else {
+    send_char:
+        return 1;
+    }
+    return 0;
+}
+
+static void mux_chr_accept_input(Chardev *chr)
+{
+    MuxChardev *d = MUX_CHARDEV(chr);
+    int m = d->focus;
+    CharBackend *be = d->backends[m];
+
+    while (be && d->prod[m] != d->cons[m] &&
+           be->chr_can_read && be->chr_can_read(be->opaque)) {
+        be->chr_read(be->opaque,
+                     &d->buffer[m][d->cons[m]++ & MUX_BUFFER_MASK], 1);
+    }
+}
+
+static int mux_chr_can_read(void *opaque)
+{
+    MuxChardev *d = MUX_CHARDEV(opaque);
+    int m = d->focus;
+    CharBackend *be = d->backends[m];
+
+    if ((d->prod[m] - d->cons[m]) < MUX_BUFFER_SIZE) {
+        return 1;
+    }
+
+    if (be && be->chr_can_read) {
+        return be->chr_can_read(be->opaque);
+    }
+
+    return 0;
+}
+
+static void mux_chr_read(void *opaque, const uint8_t *buf, int size)
+{
+    Chardev *chr = CHARDEV(opaque);
+    MuxChardev *d = MUX_CHARDEV(opaque);
+    int m = d->focus;
+    CharBackend *be = d->backends[m];
+    int i;
+
+    mux_chr_accept_input(opaque);
+
+    for (i = 0; i < size; i++)
+        if (mux_proc_byte(chr, d, buf[i])) {
+            if (d->prod[m] == d->cons[m] &&
+                be && be->chr_can_read &&
+                be->chr_can_read(be->opaque)) {
+                be->chr_read(be->opaque, &buf[i], 1);
+            } else {
+                d->buffer[m][d->prod[m]++ & MUX_BUFFER_MASK] = buf[i];
+            }
+        }
+}
+
+bool muxes_realized;
+
+static void mux_chr_event(void *opaque, int event)
+{
+    MuxChardev *d = MUX_CHARDEV(opaque);
+    int i;
+
+    if (!muxes_realized) {
+        return;
+    }
+
+    /* Send the event to all registered listeners */
+    for (i = 0; i < d->mux_cnt; i++) {
+        mux_chr_send_event(d, i, event);
+    }
+}
+
+static GSource *mux_chr_add_watch(Chardev *s, GIOCondition cond)
+{
+    MuxChardev *d = MUX_CHARDEV(s);
+    Chardev *chr = qemu_chr_fe_get_driver(&d->chr);
+    ChardevClass *cc = CHARDEV_GET_CLASS(chr);
+
+    if (!cc->chr_add_watch) {
+        return NULL;
+    }
+
+    return cc->chr_add_watch(chr, cond);
+}
+
+static void char_mux_finalize(Object *obj)
+{
+    MuxChardev *d = MUX_CHARDEV(obj);
+    int i;
+
+    for (i = 0; i < d->mux_cnt; i++) {
+        CharBackend *be = d->backends[i];
+        if (be) {
+            be->chr = NULL;
+        }
+    }
+    qemu_chr_fe_deinit(&d->chr);
+}
+
+void mux_chr_set_handlers(Chardev *chr, GMainContext *context)
+{
+    MuxChardev *d = MUX_CHARDEV(chr);
+
+    /* Fix up the real driver with mux routines */
+    qemu_chr_fe_set_handlers(&d->chr,
+                             mux_chr_can_read,
+                             mux_chr_read,
+                             mux_chr_event,
+                             chr,
+                             context, true);
+}
+
+void mux_set_focus(Chardev *chr, int focus)
+{
+    MuxChardev *d = MUX_CHARDEV(chr);
+
+    assert(focus >= 0);
+    assert(focus < d->mux_cnt);
+
+    if (d->focus != -1) {
+        mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_OUT);
+    }
+
+    d->focus = focus;
+    mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_IN);
+}
+
+static void qemu_chr_open_mux(Chardev *chr,
+                              ChardevBackend *backend,
+                              bool *be_opened,
+                              Error **errp)
+{
+    ChardevMux *mux = backend->u.mux.data;
+    Chardev *drv;
+    MuxChardev *d = MUX_CHARDEV(chr);
+
+    drv = qemu_chr_find(mux->chardev);
+    if (drv == NULL) {
+        error_setg(errp, "mux: base chardev %s not found", mux->chardev);
+        return;
+    }
+
+    d->focus = -1;
+    /* only default to opened state if we've realized the initial
+     * set of muxes
+     */
+    *be_opened = muxes_realized;
+    qemu_chr_fe_init(&d->chr, drv, errp);
+}
+
+static void qemu_chr_parse_mux(QemuOpts *opts, ChardevBackend *backend,
+                               Error **errp)
+{
+    const char *chardev = qemu_opt_get(opts, "chardev");
+    ChardevMux *mux;
+
+    if (chardev == NULL) {
+        error_setg(errp, "chardev: mux: no chardev given");
+        return;
+    }
+    backend->type = CHARDEV_BACKEND_KIND_MUX;
+    mux = backend->u.mux.data = g_new0(ChardevMux, 1);
+    qemu_chr_parse_common(opts, qapi_ChardevMux_base(mux));
+    mux->chardev = g_strdup(chardev);
+}
+
+static void char_mux_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
+
+    cc->parse = qemu_chr_parse_mux;
+    cc->open = qemu_chr_open_mux;
+    cc->chr_write = mux_chr_write;
+    cc->chr_accept_input = mux_chr_accept_input;
+    cc->chr_add_watch = mux_chr_add_watch;
+}
+
+static const TypeInfo char_mux_type_info = {
+    .name = TYPE_CHARDEV_MUX,
+    .parent = TYPE_CHARDEV,
+    .class_init = char_mux_class_init,
+    .instance_size = sizeof(MuxChardev),
+    .instance_finalize = char_mux_finalize,
+};
+
+static void register_types(void)
+{
+    type_register_static(&char_mux_type_info);
+}
+
+type_init(register_types);
diff --git a/chardev/char-mux.h b/chardev/char-mux.h
new file mode 100644
index 0000000000..9a2fffce91
--- /dev/null
+++ b/chardev/char-mux.h
@@ -0,0 +1,63 @@
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef CHAR_MUX_H
+#define CHAR_MUX_H
+
+#include "sysemu/char.h"
+
+extern bool muxes_realized;
+
+#define MAX_MUX 4
+#define MUX_BUFFER_SIZE 32 /* Must be a power of 2.  */
+#define MUX_BUFFER_MASK (MUX_BUFFER_SIZE - 1)
+typedef struct MuxChardev {
+    Chardev parent;
+    CharBackend *backends[MAX_MUX];
+    CharBackend chr;
+    int focus;
+    int mux_cnt;
+    int term_got_escape;
+    int max_size;
+    /* Intermediate input buffer catches escape sequences even if the
+       currently active device is not accepting any input - but only until it
+       is full as well. */
+    unsigned char buffer[MAX_MUX][MUX_BUFFER_SIZE];
+    int prod[MAX_MUX];
+    int cons[MAX_MUX];
+    int timestamps;
+
+    /* Protected by the Chardev chr_write_lock.  */
+    int linestart;
+    int64_t timestamps_start;
+} MuxChardev;
+
+#define MUX_CHARDEV(obj) OBJECT_CHECK(MuxChardev, (obj), TYPE_CHARDEV_MUX)
+#define CHARDEV_IS_MUX(chr)                             \
+    object_dynamic_cast(OBJECT(chr), TYPE_CHARDEV_MUX)
+
+void mux_chr_set_handlers(Chardev *chr, GMainContext *context);
+void mux_set_focus(Chardev *chr, int focus);
+void mux_chr_send_event(MuxChardev *d, int mux_nr, int event);
+
+#endif /* CHAR_MUX_H */
diff --git a/chardev/char-null.c b/chardev/char-null.c
new file mode 100644
index 0000000000..dc0d68ab2d
--- /dev/null
+++ b/chardev/char-null.c
@@ -0,0 +1,54 @@
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu/osdep.h"
+#include "sysemu/char.h"
+
+static void null_chr_open(Chardev *chr,
+                          ChardevBackend *backend,
+                          bool *be_opened,
+                          Error **errp)
+{
+    *be_opened = false;
+}
+
+static void char_null_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
+
+    cc->open = null_chr_open;
+}
+
+static const TypeInfo char_null_type_info = {
+    .name = TYPE_CHARDEV_NULL,
+    .parent = TYPE_CHARDEV,
+    .instance_size = sizeof(Chardev),
+    .class_init = char_null_class_init,
+};
+
+static void register_types(void)
+{
+    type_register_static(&char_null_type_info);
+}
+
+type_init(register_types);
diff --git a/chardev/char-parallel.c b/chardev/char-parallel.c
new file mode 100644
index 0000000000..3fa22ce29d
--- /dev/null
+++ b/chardev/char-parallel.c
@@ -0,0 +1,316 @@
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu/osdep.h"
+#include "sysemu/char.h"
+#include "qapi/error.h"
+#include <sys/ioctl.h>
+
+#ifdef CONFIG_BSD
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+#include <dev/ppbus/ppi.h>
+#include <dev/ppbus/ppbconf.h>
+#elif defined(__DragonFly__)
+#include <dev/misc/ppi/ppi.h>
+#include <bus/ppbus/ppbconf.h>
+#endif
+#else
+#ifdef __linux__
+#include <linux/ppdev.h>
+#include <linux/parport.h>
+#endif
+#endif
+
+#include "char-fd.h"
+#include "char-parallel.h"
+
+#if defined(__linux__)
+
+typedef struct {
+    Chardev parent;
+    int fd;
+    int mode;
+} ParallelChardev;
+
+#define PARALLEL_CHARDEV(obj) \
+    OBJECT_CHECK(ParallelChardev, (obj), TYPE_CHARDEV_PARALLEL)
+
+static int pp_hw_mode(ParallelChardev *s, uint16_t mode)
+{
+    if (s->mode != mode) {
+        int m = mode;
+        if (ioctl(s->fd, PPSETMODE, &m) < 0) {
+            return 0;
+        }
+        s->mode = mode;
+    }
+    return 1;
+}
+
+static int pp_ioctl(Chardev *chr, int cmd, void *arg)
+{
+    ParallelChardev *drv = PARALLEL_CHARDEV(chr);
+    int fd = drv->fd;
+    uint8_t b;
+
+    switch (cmd) {
+    case CHR_IOCTL_PP_READ_DATA:
+        if (ioctl(fd, PPRDATA, &b) < 0) {
+            return -ENOTSUP;
+        }
+        *(uint8_t *)arg = b;
+        break;
+    case CHR_IOCTL_PP_WRITE_DATA:
+        b = *(uint8_t *)arg;
+        if (ioctl(fd, PPWDATA, &b) < 0) {
+            return -ENOTSUP;
+        }
+        break;
+    case CHR_IOCTL_PP_READ_CONTROL:
+        if (ioctl(fd, PPRCONTROL, &b) < 0) {
+            return -ENOTSUP;
+        }
+        /* Linux gives only the lowest bits, and no way to know data
+           direction! For better compatibility set the fixed upper
+           bits. */
+        *(uint8_t *)arg = b | 0xc0;
+        break;
+    case CHR_IOCTL_PP_WRITE_CONTROL:
+        b = *(uint8_t *)arg;
+        if (ioctl(fd, PPWCONTROL, &b) < 0) {
+            return -ENOTSUP;
+        }
+        break;
+    case CHR_IOCTL_PP_READ_STATUS:
+        if (ioctl(fd, PPRSTATUS, &b) < 0) {
+            return -ENOTSUP;
+        }
+        *(uint8_t *)arg = b;
+        break;
+    case CHR_IOCTL_PP_DATA_DIR:
+        if (ioctl(fd, PPDATADIR, (int *)arg) < 0) {
+            return -ENOTSUP;
+        }
+        break;
+    case CHR_IOCTL_PP_EPP_READ_ADDR:
+        if (pp_hw_mode(drv, IEEE1284_MODE_EPP | IEEE1284_ADDR)) {
+            struct ParallelIOArg *parg = arg;
+            int n = read(fd, parg->buffer, parg->count);
+            if (n != parg->count) {
+                return -EIO;
+            }
+        }
+        break;
+    case CHR_IOCTL_PP_EPP_READ:
+        if (pp_hw_mode(drv, IEEE1284_MODE_EPP)) {
+            struct ParallelIOArg *parg = arg;
+            int n = read(fd, parg->buffer, parg->count);
+            if (n != parg->count) {
+                return -EIO;
+            }
+        }
+        break;
+    case CHR_IOCTL_PP_EPP_WRITE_ADDR:
+        if (pp_hw_mode(drv, IEEE1284_MODE_EPP | IEEE1284_ADDR)) {
+            struct ParallelIOArg *parg = arg;
+            int n = write(fd, parg->buffer, parg->count);
+            if (n != parg->count) {
+                return -EIO;
+            }
+        }
+        break;
+    case CHR_IOCTL_PP_EPP_WRITE:
+        if (pp_hw_mode(drv, IEEE1284_MODE_EPP)) {
+            struct ParallelIOArg *parg = arg;
+            int n = write(fd, parg->buffer, parg->count);
+            if (n != parg->count) {
+                return -EIO;
+            }
+        }
+        break;
+    default:
+        return -ENOTSUP;
+    }
+    return 0;
+}
+
+static void qemu_chr_open_pp_fd(Chardev *chr,
+                                int fd,
+                                bool *be_opened,
+                                Error **errp)
+{
+    ParallelChardev *drv = PARALLEL_CHARDEV(chr);
+
+    if (ioctl(fd, PPCLAIM) < 0) {
+        error_setg_errno(errp, errno, "not a parallel port");
+        close(fd);
+        return;
+    }
+
+    drv->fd = fd;
+    drv->mode = IEEE1284_MODE_COMPAT;
+}
+#endif /* __linux__ */
+
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
+
+typedef struct {
+    Chardev parent;
+    int fd;
+} ParallelChardev;
+
+#define PARALLEL_CHARDEV(obj)                                   \
+    OBJECT_CHECK(ParallelChardev, (obj), TYPE_CHARDEV_PARALLEL)
+
+static int pp_ioctl(Chardev *chr, int cmd, void *arg)
+{
+    ParallelChardev *drv = PARALLEL_CHARDEV(chr);
+    uint8_t b;
+
+    switch (cmd) {
+    case CHR_IOCTL_PP_READ_DATA:
+        if (ioctl(drv->fd, PPIGDATA, &b) < 0) {
+            return -ENOTSUP;
+        }
+        *(uint8_t *)arg = b;
+        break;
+    case CHR_IOCTL_PP_WRITE_DATA:
+        b = *(uint8_t *)arg;
+        if (ioctl(drv->fd, PPISDATA, &b) < 0) {
+            return -ENOTSUP;
+        }
+        break;
+    case CHR_IOCTL_PP_READ_CONTROL:
+        if (ioctl(drv->fd, PPIGCTRL, &b) < 0) {
+            return -ENOTSUP;
+        }
+        *(uint8_t *)arg = b;
+        break;
+    case CHR_IOCTL_PP_WRITE_CONTROL:
+        b = *(uint8_t *)arg;
+        if (ioctl(drv->fd, PPISCTRL, &b) < 0) {
+            return -ENOTSUP;
+        }
+        break;
+    case CHR_IOCTL_PP_READ_STATUS:
+        if (ioctl(drv->fd, PPIGSTATUS, &b) < 0) {
+            return -ENOTSUP;
+        }
+        *(uint8_t *)arg = b;
+        break;
+    default:
+        return -ENOTSUP;
+    }
+    return 0;
+}
+
+static void qemu_chr_open_pp_fd(Chardev *chr,
+                                int fd,
+                                bool *be_opened,
+                                Error **errp)
+{
+    ParallelChardev *drv = PARALLEL_CHARDEV(chr);
+    drv->fd = fd;
+    *be_opened = false;
+}
+#endif
+
+#ifdef HAVE_CHARDEV_PARPORT
+static void qmp_chardev_open_parallel(Chardev *chr,
+                                      ChardevBackend *backend,
+                                      bool *be_opened,
+                                      Error **errp)
+{
+    ChardevHostdev *parallel = backend->u.parallel.data;
+    int fd;
+
+    fd = qmp_chardev_open_file_source(parallel->device, O_RDWR, errp);
+    if (fd < 0) {
+        return;
+    }
+    qemu_chr_open_pp_fd(chr, fd, be_opened, errp);
+}
+
+static void qemu_chr_parse_parallel(QemuOpts *opts, ChardevBackend *backend,
+                                    Error **errp)
+{
+    const char *device = qemu_opt_get(opts, "path");
+    ChardevHostdev *parallel;
+
+    if (device == NULL) {
+        error_setg(errp, "chardev: parallel: no device path given");
+        return;
+    }
+    backend->type = CHARDEV_BACKEND_KIND_PARALLEL;
+    parallel = backend->u.parallel.data = g_new0(ChardevHostdev, 1);
+    qemu_chr_parse_common(opts, qapi_ChardevHostdev_base(parallel));
+    parallel->device = g_strdup(device);
+}
+
+static void char_parallel_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
+
+    cc->parse = qemu_chr_parse_parallel;
+    cc->open = qmp_chardev_open_parallel;
+#if defined(__linux__)
+    cc->chr_ioctl = pp_ioctl;
+#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \
+    defined(__DragonFly__)
+    cc->chr_ioctl = pp_ioctl;
+#endif
+}
+
+static void char_parallel_finalize(Object *obj)
+{
+#if defined(__linux__)
+    Chardev *chr = CHARDEV(obj);
+    ParallelChardev *drv = PARALLEL_CHARDEV(chr);
+    int fd = drv->fd;
+
+    pp_hw_mode(drv, IEEE1284_MODE_COMPAT);
+    ioctl(fd, PPRELEASE);
+    close(fd);
+    qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
+#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \
+    defined(__DragonFly__)
+    /* FIXME: close fd? */
+#endif
+}
+
+static const TypeInfo char_parallel_type_info = {
+    .name = TYPE_CHARDEV_PARALLEL,
+    .parent = TYPE_CHARDEV,
+    .instance_size = sizeof(ParallelChardev),
+    .instance_finalize = char_parallel_finalize,
+    .class_init = char_parallel_class_init,
+};
+
+static void register_types(void)
+{
+    type_register_static(&char_parallel_type_info);
+}
+
+type_init(register_types);
+
+#endif
diff --git a/chardev/char-parallel.h b/chardev/char-parallel.h
new file mode 100644
index 0000000000..26742f9d5c
--- /dev/null
+++ b/chardev/char-parallel.h
@@ -0,0 +1,32 @@
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef CHAR_PARALLEL_H
+#define CHAR_PARALLEL_H
+
+#if defined(__linux__) || defined(__FreeBSD__) || \
+    defined(__FreeBSD_kernel__) || defined(__DragonFly__)
+#define HAVE_CHARDEV_PARPORT 1
+#endif
+
+#endif /* CHAR_PARALLEL_H */
diff --git a/chardev/char-pipe.c b/chardev/char-pipe.c
new file mode 100644
index 0000000000..54240c863d
--- /dev/null
+++ b/chardev/char-pipe.c
@@ -0,0 +1,191 @@
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "sysemu/char.h"
+
+#ifdef _WIN32
+#include "char-win.h"
+#else
+#include "char-fd.h"
+#endif
+
+#ifdef _WIN32
+#define MAXCONNECT 1
+#define NTIMEOUT 5000
+
+static int win_chr_pipe_init(Chardev *chr, const char *filename,
+                             Error **errp)
+{
+    WinChardev *s = WIN_CHARDEV(chr);
+    OVERLAPPED ov;
+    int ret;
+    DWORD size;
+    char *openname;
+
+    s->fpipe = TRUE;
+
+    s->hsend = CreateEvent(NULL, TRUE, FALSE, NULL);
+    if (!s->hsend) {
+        error_setg(errp, "Failed CreateEvent");
+        goto fail;
+    }
+    s->hrecv = CreateEvent(NULL, TRUE, FALSE, NULL);
+    if (!s->hrecv) {
+        error_setg(errp, "Failed CreateEvent");
+        goto fail;
+    }
+
+    openname = g_strdup_printf("\\\\.\\pipe\\%s", filename);
+    s->hcom = CreateNamedPipe(openname,
+                              PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
+                              PIPE_TYPE_BYTE | PIPE_READMODE_BYTE |
+                              PIPE_WAIT,
+                              MAXCONNECT, NSENDBUF, NRECVBUF, NTIMEOUT, NULL);
+    g_free(openname);
+    if (s->hcom == INVALID_HANDLE_VALUE) {
+        error_setg(errp, "Failed CreateNamedPipe (%lu)", GetLastError());
+        s->hcom = NULL;
+        goto fail;
+    }
+
+    ZeroMemory(&ov, sizeof(ov));
+    ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+    ret = ConnectNamedPipe(s->hcom, &ov);
+    if (ret) {
+        error_setg(errp, "Failed ConnectNamedPipe");
+        goto fail;
+    }
+
+    ret = GetOverlappedResult(s->hcom, &ov, &size, TRUE);
+    if (!ret) {
+        error_setg(errp, "Failed GetOverlappedResult");
+        if (ov.hEvent) {
+            CloseHandle(ov.hEvent);
+            ov.hEvent = NULL;
+        }
+        goto fail;
+    }
+
+    if (ov.hEvent) {
+        CloseHandle(ov.hEvent);
+        ov.hEvent = NULL;
+    }
+    qemu_add_polling_cb(win_chr_pipe_poll, chr);
+    return 0;
+
+ fail:
+    return -1;
+}
+
+static void qemu_chr_open_pipe(Chardev *chr,
+                               ChardevBackend *backend,
+                               bool *be_opened,
+                               Error **errp)
+{
+    ChardevHostdev *opts = backend->u.pipe.data;
+    const char *filename = opts->device;
+
+    if (win_chr_pipe_init(chr, filename, errp) < 0) {
+        return;
+    }
+}
+
+#else
+
+static void qemu_chr_open_pipe(Chardev *chr,
+                               ChardevBackend *backend,
+                               bool *be_opened,
+                               Error **errp)
+{
+    ChardevHostdev *opts = backend->u.pipe.data;
+    int fd_in, fd_out;
+    char *filename_in;
+    char *filename_out;
+    const char *filename = opts->device;
+
+    filename_in = g_strdup_printf("%s.in", filename);
+    filename_out = g_strdup_printf("%s.out", filename);
+    TFR(fd_in = qemu_open(filename_in, O_RDWR | O_BINARY));
+    TFR(fd_out = qemu_open(filename_out, O_RDWR | O_BINARY));
+    g_free(filename_in);
+    g_free(filename_out);
+    if (fd_in < 0 || fd_out < 0) {
+        if (fd_in >= 0) {
+            close(fd_in);
+        }
+        if (fd_out >= 0) {
+            close(fd_out);
+        }
+        TFR(fd_in = fd_out = qemu_open(filename, O_RDWR | O_BINARY));
+        if (fd_in < 0) {
+            error_setg_file_open(errp, errno, filename);
+            return;
+        }
+    }
+    qemu_chr_open_fd(chr, fd_in, fd_out);
+}
+
+#endif /* !_WIN32 */
+
+static void qemu_chr_parse_pipe(QemuOpts *opts, ChardevBackend *backend,
+                                Error **errp)
+{
+    const char *device = qemu_opt_get(opts, "path");
+    ChardevHostdev *dev;
+
+    if (device == NULL) {
+        error_setg(errp, "chardev: pipe: no device path given");
+        return;
+    }
+    backend->type = CHARDEV_BACKEND_KIND_PIPE;
+    dev = backend->u.pipe.data = g_new0(ChardevHostdev, 1);
+    qemu_chr_parse_common(opts, qapi_ChardevHostdev_base(dev));
+    dev->device = g_strdup(device);
+}
+
+static void char_pipe_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
+
+    cc->parse = qemu_chr_parse_pipe;
+    cc->open = qemu_chr_open_pipe;
+}
+
+static const TypeInfo char_pipe_type_info = {
+    .name = TYPE_CHARDEV_PIPE,
+#ifdef _WIN32
+    .parent = TYPE_CHARDEV_WIN,
+#else
+    .parent = TYPE_CHARDEV_FD,
+#endif
+    .class_init = char_pipe_class_init,
+};
+
+static void register_types(void)
+{
+    type_register_static(&char_pipe_type_info);
+}
+
+type_init(register_types);
diff --git a/chardev/char-pty.c b/chardev/char-pty.c
new file mode 100644
index 0000000000..27eb85f505
--- /dev/null
+++ b/chardev/char-pty.c
@@ -0,0 +1,300 @@
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "sysemu/char.h"
+#include "io/channel-file.h"
+#include "qemu/sockets.h"
+#include "qemu/error-report.h"
+
+#include "char-io.h"
+
+#if defined(__linux__) || defined(__sun__) || defined(__FreeBSD__)      \
+    || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) \
+    || defined(__GLIBC__)
+
+typedef struct {
+    Chardev parent;
+    QIOChannel *ioc;
+    int read_bytes;
+
+    /* Protected by the Chardev chr_write_lock.  */
+    int connected;
+    guint timer_tag;
+    guint open_tag;
+} PtyChardev;
+
+#define PTY_CHARDEV(obj) OBJECT_CHECK(PtyChardev, (obj), TYPE_CHARDEV_PTY)
+
+static void pty_chr_update_read_handler_locked(Chardev *chr);
+static void pty_chr_state(Chardev *chr, int connected);
+
+static gboolean pty_chr_timer(gpointer opaque)
+{
+    struct Chardev *chr = CHARDEV(opaque);
+    PtyChardev *s = PTY_CHARDEV(opaque);
+
+    qemu_mutex_lock(&chr->chr_write_lock);
+    s->timer_tag = 0;
+    s->open_tag = 0;
+    if (!s->connected) {
+        /* Next poll ... */
+        pty_chr_update_read_handler_locked(chr);
+    }
+    qemu_mutex_unlock(&chr->chr_write_lock);
+    return FALSE;
+}
+
+/* Called with chr_write_lock held.  */
+static void pty_chr_rearm_timer(Chardev *chr, int ms)
+{
+    PtyChardev *s = PTY_CHARDEV(chr);
+    char *name;
+
+    if (s->timer_tag) {
+        g_source_remove(s->timer_tag);
+        s->timer_tag = 0;
+    }
+
+    if (ms == 1000) {
+        name = g_strdup_printf("pty-timer-secs-%s", chr->label);
+        s->timer_tag = g_timeout_add_seconds(1, pty_chr_timer, chr);
+    } else {
+        name = g_strdup_printf("pty-timer-ms-%s", chr->label);
+        s->timer_tag = g_timeout_add(ms, pty_chr_timer, chr);
+    }
+    g_source_set_name_by_id(s->timer_tag, name);
+    g_free(name);
+}
+
+/* Called with chr_write_lock held.  */
+static void pty_chr_update_read_handler_locked(Chardev *chr)
+{
+    PtyChardev *s = PTY_CHARDEV(chr);
+    GPollFD pfd;
+    int rc;
+    QIOChannelFile *fioc = QIO_CHANNEL_FILE(s->ioc);
+
+    pfd.fd = fioc->fd;
+    pfd.events = G_IO_OUT;
+    pfd.revents = 0;
+    do {
+        rc = g_poll(&pfd, 1, 0);
+    } while (rc == -1 && errno == EINTR);
+    assert(rc >= 0);
+
+    if (pfd.revents & G_IO_HUP) {
+        pty_chr_state(chr, 0);
+    } else {
+        pty_chr_state(chr, 1);
+    }
+}
+
+static void pty_chr_update_read_handler(Chardev *chr,
+                                        GMainContext *context)
+{
+    qemu_mutex_lock(&chr->chr_write_lock);
+    pty_chr_update_read_handler_locked(chr);
+    qemu_mutex_unlock(&chr->chr_write_lock);
+}
+
+/* Called with chr_write_lock held.  */
+static int char_pty_chr_write(Chardev *chr, const uint8_t *buf, int len)
+{
+    PtyChardev *s = PTY_CHARDEV(chr);
+
+    if (!s->connected) {
+        /* guest sends data, check for (re-)connect */
+        pty_chr_update_read_handler_locked(chr);
+        if (!s->connected) {
+            return 0;
+        }
+    }
+    return io_channel_send(s->ioc, buf, len);
+}
+
+static GSource *pty_chr_add_watch(Chardev *chr, GIOCondition cond)
+{
+    PtyChardev *s = PTY_CHARDEV(chr);
+    if (!s->connected) {
+        return NULL;
+    }
+    return qio_channel_create_watch(s->ioc, cond);
+}
+
+static int pty_chr_read_poll(void *opaque)
+{
+    Chardev *chr = CHARDEV(opaque);
+    PtyChardev *s = PTY_CHARDEV(opaque);
+
+    s->read_bytes = qemu_chr_be_can_write(chr);
+    return s->read_bytes;
+}
+
+static gboolean pty_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
+{
+    Chardev *chr = CHARDEV(opaque);
+    PtyChardev *s = PTY_CHARDEV(opaque);
+    gsize len;
+    uint8_t buf[CHR_READ_BUF_LEN];
+    ssize_t ret;
+
+    len = sizeof(buf);
+    if (len > s->read_bytes) {
+        len = s->read_bytes;
+    }
+    if (len == 0) {
+        return TRUE;
+    }
+    ret = qio_channel_read(s->ioc, (char *)buf, len, NULL);
+    if (ret <= 0) {
+        pty_chr_state(chr, 0);
+        return FALSE;
+    } else {
+        pty_chr_state(chr, 1);
+        qemu_chr_be_write(chr, buf, ret);
+    }
+    return TRUE;
+}
+
+static gboolean qemu_chr_be_generic_open_func(gpointer opaque)
+{
+    Chardev *chr = CHARDEV(opaque);
+    PtyChardev *s = PTY_CHARDEV(opaque);
+
+    s->open_tag = 0;
+    qemu_chr_be_generic_open(chr);
+    return FALSE;
+}
+
+/* Called with chr_write_lock held.  */
+static void pty_chr_state(Chardev *chr, int connected)
+{
+    PtyChardev *s = PTY_CHARDEV(chr);
+
+    if (!connected) {
+        if (s->open_tag) {
+            g_source_remove(s->open_tag);
+            s->open_tag = 0;
+        }
+        remove_fd_in_watch(chr);
+        s->connected = 0;
+        /* (re-)connect poll interval for idle guests: once per second.
+         * We check more frequently in case the guests sends data to
+         * the virtual device linked to our pty. */
+        pty_chr_rearm_timer(chr, 1000);
+    } else {
+        if (s->timer_tag) {
+            g_source_remove(s->timer_tag);
+            s->timer_tag = 0;
+        }
+        if (!s->connected) {
+            g_assert(s->open_tag == 0);
+            s->connected = 1;
+            s->open_tag = g_idle_add(qemu_chr_be_generic_open_func, chr);
+        }
+        if (!chr->fd_in_tag) {
+            chr->fd_in_tag = io_add_watch_poll(chr, s->ioc,
+                                               pty_chr_read_poll,
+                                               pty_chr_read,
+                                               chr, NULL);
+        }
+    }
+}
+
+static void char_pty_finalize(Object *obj)
+{
+    Chardev *chr = CHARDEV(obj);
+    PtyChardev *s = PTY_CHARDEV(obj);
+
+    qemu_mutex_lock(&chr->chr_write_lock);
+    pty_chr_state(chr, 0);
+    object_unref(OBJECT(s->ioc));
+    if (s->timer_tag) {
+        g_source_remove(s->timer_tag);
+        s->timer_tag = 0;
+    }
+    qemu_mutex_unlock(&chr->chr_write_lock);
+    qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
+}
+
+static void char_pty_open(Chardev *chr,
+                          ChardevBackend *backend,
+                          bool *be_opened,
+                          Error **errp)
+{
+    PtyChardev *s;
+    int master_fd, slave_fd;
+    char pty_name[PATH_MAX];
+    char *name;
+
+    master_fd = qemu_openpty_raw(&slave_fd, pty_name);
+    if (master_fd < 0) {
+        error_setg_errno(errp, errno, "Failed to create PTY");
+        return;
+    }
+
+    close(slave_fd);
+    qemu_set_nonblock(master_fd);
+
+    chr->filename = g_strdup_printf("pty:%s", pty_name);
+    error_report("char device redirected to %s (label %s)",
+                 pty_name, chr->label);
+
+    s = PTY_CHARDEV(chr);
+    s->ioc = QIO_CHANNEL(qio_channel_file_new_fd(master_fd));
+    name = g_strdup_printf("chardev-pty-%s", chr->label);
+    qio_channel_set_name(QIO_CHANNEL(s->ioc), name);
+    g_free(name);
+    s->timer_tag = 0;
+    *be_opened = false;
+}
+
+static void char_pty_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
+
+    cc->open = char_pty_open;
+    cc->chr_write = char_pty_chr_write;
+    cc->chr_update_read_handler = pty_chr_update_read_handler;
+    cc->chr_add_watch = pty_chr_add_watch;
+}
+
+static const TypeInfo char_pty_type_info = {
+    .name = TYPE_CHARDEV_PTY,
+    .parent = TYPE_CHARDEV,
+    .instance_size = sizeof(PtyChardev),
+    .instance_finalize = char_pty_finalize,
+    .class_init = char_pty_class_init,
+};
+
+static void register_types(void)
+{
+    type_register_static(&char_pty_type_info);
+}
+
+type_init(register_types);
+
+#endif
diff --git a/chardev/char-ringbuf.c b/chardev/char-ringbuf.c
new file mode 100644
index 0000000000..d130069e88
--- /dev/null
+++ b/chardev/char-ringbuf.c
@@ -0,0 +1,249 @@
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu/osdep.h"
+#include "sysemu/char.h"
+#include "qmp-commands.h"
+#include "qemu/base64.h"
+
+/* Ring buffer chardev */
+
+typedef struct {
+    Chardev parent;
+    size_t size;
+    size_t prod;
+    size_t cons;
+    uint8_t *cbuf;
+} RingBufChardev;
+
+#define RINGBUF_CHARDEV(obj)                                    \
+    OBJECT_CHECK(RingBufChardev, (obj), TYPE_CHARDEV_RINGBUF)
+
+static size_t ringbuf_count(const Chardev *chr)
+{
+    const RingBufChardev *d = RINGBUF_CHARDEV(chr);
+
+    return d->prod - d->cons;
+}
+
+static int ringbuf_chr_write(Chardev *chr, const uint8_t *buf, int len)
+{
+    RingBufChardev *d = RINGBUF_CHARDEV(chr);
+    int i;
+
+    if (!buf || (len < 0)) {
+        return -1;
+    }
+
+    for (i = 0; i < len; i++) {
+        d->cbuf[d->prod++ & (d->size - 1)] = buf[i];
+        if (d->prod - d->cons > d->size) {
+            d->cons = d->prod - d->size;
+        }
+    }
+
+    return len;
+}
+
+static int ringbuf_chr_read(Chardev *chr, uint8_t *buf, int len)
+{
+    RingBufChardev *d = RINGBUF_CHARDEV(chr);
+    int i;
+
+    qemu_mutex_lock(&chr->chr_write_lock);
+    for (i = 0; i < len && d->cons != d->prod; i++) {
+        buf[i] = d->cbuf[d->cons++ & (d->size - 1)];
+    }
+    qemu_mutex_unlock(&chr->chr_write_lock);
+
+    return i;
+}
+
+static void char_ringbuf_finalize(Object *obj)
+{
+    RingBufChardev *d = RINGBUF_CHARDEV(obj);
+
+    g_free(d->cbuf);
+}
+
+static void qemu_chr_open_ringbuf(Chardev *chr,
+                                  ChardevBackend *backend,
+                                  bool *be_opened,
+                                  Error **errp)
+{
+    ChardevRingbuf *opts = backend->u.ringbuf.data;
+    RingBufChardev *d = RINGBUF_CHARDEV(chr);
+
+    d->size = opts->has_size ? opts->size : 65536;
+
+    /* The size must be power of 2 */
+    if (d->size & (d->size - 1)) {
+        error_setg(errp, "size of ringbuf chardev must be power of two");
+        return;
+    }
+
+    d->prod = 0;
+    d->cons = 0;
+    d->cbuf = g_malloc0(d->size);
+}
+
+void qmp_ringbuf_write(const char *device, const char *data,
+                       bool has_format, enum DataFormat format,
+                       Error **errp)
+{
+    Chardev *chr;
+    const uint8_t *write_data;
+    int ret;
+    gsize write_count;
+
+    chr = qemu_chr_find(device);
+    if (!chr) {
+        error_setg(errp, "Device '%s' not found", device);
+        return;
+    }
+
+    if (!CHARDEV_IS_RINGBUF(chr)) {
+        error_setg(errp, "%s is not a ringbuf device", device);
+        return;
+    }
+
+    if (has_format && (format == DATA_FORMAT_BASE64)) {
+        write_data = qbase64_decode(data, -1,
+                                    &write_count,
+                                    errp);
+        if (!write_data) {
+            return;
+        }
+    } else {
+        write_data = (uint8_t *)data;
+        write_count = strlen(data);
+    }
+
+    ret = ringbuf_chr_write(chr, write_data, write_count);
+
+    if (write_data != (uint8_t *)data) {
+        g_free((void *)write_data);
+    }
+
+    if (ret < 0) {
+        error_setg(errp, "Failed to write to device %s", device);
+        return;
+    }
+}
+
+char *qmp_ringbuf_read(const char *device, int64_t size,
+                       bool has_format, enum DataFormat format,
+                       Error **errp)
+{
+    Chardev *chr;
+    uint8_t *read_data;
+    size_t count;
+    char *data;
+
+    chr = qemu_chr_find(device);
+    if (!chr) {
+        error_setg(errp, "Device '%s' not found", device);
+        return NULL;
+    }
+
+    if (!CHARDEV_IS_RINGBUF(chr)) {
+        error_setg(errp, "%s is not a ringbuf device", device);
+        return NULL;
+    }
+
+    if (size <= 0) {
+        error_setg(errp, "size must be greater than zero");
+        return NULL;
+    }
+
+    count = ringbuf_count(chr);
+    size = size > count ? count : size;
+    read_data = g_malloc(size + 1);
+
+    ringbuf_chr_read(chr, read_data, size);
+
+    if (has_format && (format == DATA_FORMAT_BASE64)) {
+        data = g_base64_encode(read_data, size);
+        g_free(read_data);
+    } else {
+        /*
+         * FIXME should read only complete, valid UTF-8 characters up
+         * to @size bytes.  Invalid sequences should be replaced by a
+         * suitable replacement character.  Except when (and only
+         * when) ring buffer lost characters since last read, initial
+         * continuation characters should be dropped.
+         */
+        read_data[size] = 0;
+        data = (char *)read_data;
+    }
+
+    return data;
+}
+
+static void qemu_chr_parse_ringbuf(QemuOpts *opts, ChardevBackend *backend,
+                                   Error **errp)
+{
+    int val;
+    ChardevRingbuf *ringbuf;
+
+    backend->type = CHARDEV_BACKEND_KIND_RINGBUF;
+    ringbuf = backend->u.ringbuf.data = g_new0(ChardevRingbuf, 1);
+    qemu_chr_parse_common(opts, qapi_ChardevRingbuf_base(ringbuf));
+
+    val = qemu_opt_get_size(opts, "size", 0);
+    if (val != 0) {
+        ringbuf->has_size = true;
+        ringbuf->size = val;
+    }
+}
+
+static void char_ringbuf_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
+
+    cc->parse = qemu_chr_parse_ringbuf;
+    cc->open = qemu_chr_open_ringbuf;
+    cc->chr_write = ringbuf_chr_write;
+}
+
+static const TypeInfo char_ringbuf_type_info = {
+    .name = TYPE_CHARDEV_RINGBUF,
+    .parent = TYPE_CHARDEV,
+    .class_init = char_ringbuf_class_init,
+    .instance_size = sizeof(RingBufChardev),
+    .instance_finalize = char_ringbuf_finalize,
+};
+
+/* Bug-compatibility: */
+static const TypeInfo char_memory_type_info = {
+    .name = TYPE_CHARDEV_MEMORY,
+    .parent = TYPE_CHARDEV_RINGBUF,
+};
+
+static void register_types(void)
+{
+    type_register_static(&char_ringbuf_type_info);
+    type_register_static(&char_memory_type_info);
+}
+
+type_init(register_types);
diff --git a/chardev/char-serial.c b/chardev/char-serial.c
new file mode 100644
index 0000000000..094e08dca5
--- /dev/null
+++ b/chardev/char-serial.c
@@ -0,0 +1,318 @@
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu/osdep.h"
+#include "qemu/sockets.h"
+#include "io/channel-file.h"
+#include "qapi/error.h"
+
+#ifdef _WIN32
+#include "char-win.h"
+#else
+#include <sys/ioctl.h>
+#include <termios.h>
+#include "char-fd.h"
+#endif
+
+#include "char-serial.h"
+
+#ifdef _WIN32
+
+static void qmp_chardev_open_serial(Chardev *chr,
+                                    ChardevBackend *backend,
+                                    bool *be_opened,
+                                    Error **errp)
+{
+    ChardevHostdev *serial = backend->u.serial.data;
+
+    win_chr_init(chr, serial->device, errp);
+}
+
+#elif defined(__linux__) || defined(__sun__) || defined(__FreeBSD__)      \
+    || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) \
+    || defined(__GLIBC__)
+
+static void tty_serial_init(int fd, int speed,
+                            int parity, int data_bits, int stop_bits)
+{
+    struct termios tty;
+    speed_t spd;
+
+#if 0
+    printf("tty_serial_init: speed=%d parity=%c data=%d stop=%d\n",
+           speed, parity, data_bits, stop_bits);
+#endif
+    tcgetattr(fd, &tty);
+
+#define check_speed(val) if (speed <= val) { spd = B##val; break; }
+    speed = speed * 10 / 11;
+    do {
+        check_speed(50);
+        check_speed(75);
+        check_speed(110);
+        check_speed(134);
+        check_speed(150);
+        check_speed(200);
+        check_speed(300);
+        check_speed(600);
+        check_speed(1200);
+        check_speed(1800);
+        check_speed(2400);
+        check_speed(4800);
+        check_speed(9600);
+        check_speed(19200);
+        check_speed(38400);
+        /* Non-Posix values follow. They may be unsupported on some systems. */
+        check_speed(57600);
+        check_speed(115200);
+#ifdef B230400
+        check_speed(230400);
+#endif
+#ifdef B460800
+        check_speed(460800);
+#endif
+#ifdef B500000
+        check_speed(500000);
+#endif
+#ifdef B576000
+        check_speed(576000);
+#endif
+#ifdef B921600
+        check_speed(921600);
+#endif
+#ifdef B1000000
+        check_speed(1000000);
+#endif
+#ifdef B1152000
+        check_speed(1152000);
+#endif
+#ifdef B1500000
+        check_speed(1500000);
+#endif
+#ifdef B2000000
+        check_speed(2000000);
+#endif
+#ifdef B2500000
+        check_speed(2500000);
+#endif
+#ifdef B3000000
+        check_speed(3000000);
+#endif
+#ifdef B3500000
+        check_speed(3500000);
+#endif
+#ifdef B4000000
+        check_speed(4000000);
+#endif
+        spd = B115200;
+    } while (0);
+
+    cfsetispeed(&tty, spd);
+    cfsetospeed(&tty, spd);
+
+    tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
+                     | INLCR | IGNCR | ICRNL | IXON);
+    tty.c_oflag |= OPOST;
+    tty.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN | ISIG);
+    tty.c_cflag &= ~(CSIZE | PARENB | PARODD | CRTSCTS | CSTOPB);
+    switch (data_bits) {
+    default:
+    case 8:
+        tty.c_cflag |= CS8;
+        break;
+    case 7:
+        tty.c_cflag |= CS7;
+        break;
+    case 6:
+        tty.c_cflag |= CS6;
+        break;
+    case 5:
+        tty.c_cflag |= CS5;
+        break;
+    }
+    switch (parity) {
+    default:
+    case 'N':
+        break;
+    case 'E':
+        tty.c_cflag |= PARENB;
+        break;
+    case 'O':
+        tty.c_cflag |= PARENB | PARODD;
+        break;
+    }
+    if (stop_bits == 2) {
+        tty.c_cflag |= CSTOPB;
+    }
+
+    tcsetattr(fd, TCSANOW, &tty);
+}
+
+static int tty_serial_ioctl(Chardev *chr, int cmd, void *arg)
+{
+    FDChardev *s = FD_CHARDEV(chr);
+    QIOChannelFile *fioc = QIO_CHANNEL_FILE(s->ioc_in);
+
+    switch (cmd) {
+    case CHR_IOCTL_SERIAL_SET_PARAMS:
+        {
+            QEMUSerialSetParams *ssp = arg;
+            tty_serial_init(fioc->fd,
+                            ssp->speed, ssp->parity,
+                            ssp->data_bits, ssp->stop_bits);
+        }
+        break;
+    case CHR_IOCTL_SERIAL_SET_BREAK:
+        {
+            int enable = *(int *)arg;
+            if (enable) {
+                tcsendbreak(fioc->fd, 1);
+            }
+        }
+        break;
+    case CHR_IOCTL_SERIAL_GET_TIOCM:
+        {
+            int sarg = 0;
+            int *targ = (int *)arg;
+            ioctl(fioc->fd, TIOCMGET, &sarg);
+            *targ = 0;
+            if (sarg & TIOCM_CTS) {
+                *targ |= CHR_TIOCM_CTS;
+            }
+            if (sarg & TIOCM_CAR) {
+                *targ |= CHR_TIOCM_CAR;
+            }
+            if (sarg & TIOCM_DSR) {
+                *targ |= CHR_TIOCM_DSR;
+            }
+            if (sarg & TIOCM_RI) {
+                *targ |= CHR_TIOCM_RI;
+            }
+            if (sarg & TIOCM_DTR) {
+                *targ |= CHR_TIOCM_DTR;
+            }
+            if (sarg & TIOCM_RTS) {
+                *targ |= CHR_TIOCM_RTS;
+            }
+        }
+        break;
+    case CHR_IOCTL_SERIAL_SET_TIOCM:
+        {
+            int sarg = *(int *)arg;
+            int targ = 0;
+            ioctl(fioc->fd, TIOCMGET, &targ);
+            targ &= ~(CHR_TIOCM_CTS | CHR_TIOCM_CAR | CHR_TIOCM_DSR
+                     | CHR_TIOCM_RI | CHR_TIOCM_DTR | CHR_TIOCM_RTS);
+            if (sarg & CHR_TIOCM_CTS) {
+                targ |= TIOCM_CTS;
+            }
+            if (sarg & CHR_TIOCM_CAR) {
+                targ |= TIOCM_CAR;
+            }
+            if (sarg & CHR_TIOCM_DSR) {
+                targ |= TIOCM_DSR;
+            }
+            if (sarg & CHR_TIOCM_RI) {
+                targ |= TIOCM_RI;
+            }
+            if (sarg & CHR_TIOCM_DTR) {
+                targ |= TIOCM_DTR;
+            }
+            if (sarg & CHR_TIOCM_RTS) {
+                targ |= TIOCM_RTS;
+            }
+            ioctl(fioc->fd, TIOCMSET, &targ);
+        }
+        break;
+    default:
+        return -ENOTSUP;
+    }
+    return 0;
+}
+
+static void qmp_chardev_open_serial(Chardev *chr,
+                                    ChardevBackend *backend,
+                                    bool *be_opened,
+                                    Error **errp)
+{
+    ChardevHostdev *serial = backend->u.serial.data;
+    int fd;
+
+    fd = qmp_chardev_open_file_source(serial->device, O_RDWR, errp);
+    if (fd < 0) {
+        return;
+    }
+    qemu_set_nonblock(fd);
+    tty_serial_init(fd, 115200, 'N', 8, 1);
+
+    qemu_chr_open_fd(chr, fd, fd);
+}
+#endif /* __linux__ || __sun__ */
+
+#ifdef HAVE_CHARDEV_SERIAL
+static void qemu_chr_parse_serial(QemuOpts *opts, ChardevBackend *backend,
+                                  Error **errp)
+{
+    const char *device = qemu_opt_get(opts, "path");
+    ChardevHostdev *serial;
+
+    if (device == NULL) {
+        error_setg(errp, "chardev: serial/tty: no device path given");
+        return;
+    }
+    backend->type = CHARDEV_BACKEND_KIND_SERIAL;
+    serial = backend->u.serial.data = g_new0(ChardevHostdev, 1);
+    qemu_chr_parse_common(opts, qapi_ChardevHostdev_base(serial));
+    serial->device = g_strdup(device);
+}
+
+static void char_serial_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
+
+    cc->parse = qemu_chr_parse_serial;
+    cc->open = qmp_chardev_open_serial;
+#ifndef _WIN32
+    cc->chr_ioctl = tty_serial_ioctl;
+#endif
+}
+
+
+static const TypeInfo char_serial_type_info = {
+    .name = TYPE_CHARDEV_SERIAL,
+#ifdef _WIN32
+    .parent = TYPE_CHARDEV_WIN,
+#else
+    .parent = TYPE_CHARDEV_FD,
+#endif
+    .class_init = char_serial_class_init,
+};
+
+static void register_types(void)
+{
+    type_register_static(&char_serial_type_info);
+}
+
+type_init(register_types);
+
+#endif
diff --git a/chardev/char-serial.h b/chardev/char-serial.h
new file mode 100644
index 0000000000..64a27f63b1
--- /dev/null
+++ b/chardev/char-serial.h
@@ -0,0 +1,35 @@
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef CHAR_SERIAL_H
+#define CHAR_SERIAL_H
+
+#ifdef _WIN32
+#define HAVE_CHARDEV_SERIAL 1
+#elif defined(__linux__) || defined(__sun__) || defined(__FreeBSD__)    \
+    || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) \
+    || defined(__GLIBC__)
+#define HAVE_CHARDEV_SERIAL 1
+#endif
+
+#endif
diff --git a/chardev/char-socket.c b/chardev/char-socket.c
new file mode 100644
index 0000000000..4068dc5e52
--- /dev/null
+++ b/chardev/char-socket.c
@@ -0,0 +1,1017 @@
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu/osdep.h"
+#include "sysemu/char.h"
+#include "io/channel-socket.h"
+#include "io/channel-tls.h"
+#include "qemu/error-report.h"
+#include "qapi/error.h"
+#include "qapi/clone-visitor.h"
+
+#include "char-io.h"
+
+/***********************************************************/
+/* TCP Net console */
+
+#define TCP_MAX_FDS 16
+
+typedef struct {
+    Chardev parent;
+    QIOChannel *ioc; /* Client I/O channel */
+    QIOChannelSocket *sioc; /* Client master channel */
+    QIOChannelSocket *listen_ioc;
+    guint listen_tag;
+    QCryptoTLSCreds *tls_creds;
+    int connected;
+    int max_size;
+    int do_telnetopt;
+    int do_nodelay;
+    int is_unix;
+    int *read_msgfds;
+    size_t read_msgfds_num;
+    int *write_msgfds;
+    size_t write_msgfds_num;
+
+    SocketAddress *addr;
+    bool is_listen;
+    bool is_telnet;
+
+    guint reconnect_timer;
+    int64_t reconnect_time;
+    bool connect_err_reported;
+} SocketChardev;
+
+#define SOCKET_CHARDEV(obj)                                     \
+    OBJECT_CHECK(SocketChardev, (obj), TYPE_CHARDEV_SOCKET)
+
+static gboolean socket_reconnect_timeout(gpointer opaque);
+
+static void qemu_chr_socket_restart_timer(Chardev *chr)
+{
+    SocketChardev *s = SOCKET_CHARDEV(chr);
+    char *name;
+
+    assert(s->connected == 0);
+    s->reconnect_timer = g_timeout_add_seconds(s->reconnect_time,
+                                               socket_reconnect_timeout, chr);
+    name = g_strdup_printf("chardev-socket-reconnect-%s", chr->label);
+    g_source_set_name_by_id(s->reconnect_timer, name);
+    g_free(name);
+}
+
+static void check_report_connect_error(Chardev *chr,
+                                       Error *err)
+{
+    SocketChardev *s = SOCKET_CHARDEV(chr);
+
+    if (!s->connect_err_reported) {
+        error_report("Unable to connect character device %s: %s",
+                     chr->label, error_get_pretty(err));
+        s->connect_err_reported = true;
+    }
+    qemu_chr_socket_restart_timer(chr);
+}
+
+static gboolean tcp_chr_accept(QIOChannel *chan,
+                               GIOCondition cond,
+                               void *opaque);
+
+/* Called with chr_write_lock held.  */
+static int tcp_chr_write(Chardev *chr, const uint8_t *buf, int len)
+{
+    SocketChardev *s = SOCKET_CHARDEV(chr);
+
+    if (s->connected) {
+        int ret =  io_channel_send_full(s->ioc, buf, len,
+                                        s->write_msgfds,
+                                        s->write_msgfds_num);
+
+        /* free the written msgfds, no matter what */
+        if (s->write_msgfds_num) {
+            g_free(s->write_msgfds);
+            s->write_msgfds = 0;
+            s->write_msgfds_num = 0;
+        }
+
+        return ret;
+    } else {
+        /* XXX: indicate an error ? */
+        return len;
+    }
+}
+
+static int tcp_chr_read_poll(void *opaque)
+{
+    Chardev *chr = CHARDEV(opaque);
+    SocketChardev *s = SOCKET_CHARDEV(opaque);
+    if (!s->connected) {
+        return 0;
+    }
+    s->max_size = qemu_chr_be_can_write(chr);
+    return s->max_size;
+}
+
+#define IAC 255
+#define IAC_BREAK 243
+static void tcp_chr_process_IAC_bytes(Chardev *chr,
+                                      SocketChardev *s,
+                                      uint8_t *buf, int *size)
+{
+    /* Handle any telnet client's basic IAC options to satisfy char by
+     * char mode with no echo.  All IAC options will be removed from
+     * the buf and the do_telnetopt variable will be used to track the
+     * state of the width of the IAC information.
+     *
+     * IAC commands come in sets of 3 bytes with the exception of the
+     * "IAC BREAK" command and the double IAC.
+     */
+
+    int i;
+    int j = 0;
+
+    for (i = 0; i < *size; i++) {
+        if (s->do_telnetopt > 1) {
+            if ((unsigned char)buf[i] == IAC && s->do_telnetopt == 2) {
+                /* Double IAC means send an IAC */
+                if (j != i) {
+                    buf[j] = buf[i];
+                }
+                j++;
+                s->do_telnetopt = 1;
+            } else {
+                if ((unsigned char)buf[i] == IAC_BREAK
+                    && s->do_telnetopt == 2) {
+                    /* Handle IAC break commands by sending a serial break */
+                    qemu_chr_be_event(chr, CHR_EVENT_BREAK);
+                    s->do_telnetopt++;
+                }
+                s->do_telnetopt++;
+            }
+            if (s->do_telnetopt >= 4) {
+                s->do_telnetopt = 1;
+            }
+        } else {
+            if ((unsigned char)buf[i] == IAC) {
+                s->do_telnetopt = 2;
+            } else {
+                if (j != i) {
+                    buf[j] = buf[i];
+                }
+                j++;
+            }
+        }
+    }
+    *size = j;
+}
+
+static int tcp_get_msgfds(Chardev *chr, int *fds, int num)
+{
+    SocketChardev *s = SOCKET_CHARDEV(chr);
+
+    int to_copy = (s->read_msgfds_num < num) ? s->read_msgfds_num : num;
+
+    assert(num <= TCP_MAX_FDS);
+
+    if (to_copy) {
+        int i;
+
+        memcpy(fds, s->read_msgfds, to_copy * sizeof(int));
+
+        /* Close unused fds */
+        for (i = to_copy; i < s->read_msgfds_num; i++) {
+            close(s->read_msgfds[i]);
+        }
+
+        g_free(s->read_msgfds);
+        s->read_msgfds = 0;
+        s->read_msgfds_num = 0;
+    }
+
+    return to_copy;
+}
+
+static int tcp_set_msgfds(Chardev *chr, int *fds, int num)
+{
+    SocketChardev *s = SOCKET_CHARDEV(chr);
+
+    /* clear old pending fd array */
+    g_free(s->write_msgfds);
+    s->write_msgfds = NULL;
+    s->write_msgfds_num = 0;
+
+    if (!s->connected ||
+        !qio_channel_has_feature(s->ioc,
+                                 QIO_CHANNEL_FEATURE_FD_PASS)) {
+        return -1;
+    }
+
+    if (num) {
+        s->write_msgfds = g_new(int, num);
+        memcpy(s->write_msgfds, fds, num * sizeof(int));
+    }
+
+    s->write_msgfds_num = num;
+
+    return 0;
+}
+
+static ssize_t tcp_chr_recv(Chardev *chr, char *buf, size_t len)
+{
+    SocketChardev *s = SOCKET_CHARDEV(chr);
+    struct iovec iov = { .iov_base = buf, .iov_len = len };
+    int ret;
+    size_t i;
+    int *msgfds = NULL;
+    size_t msgfds_num = 0;
+
+    if (qio_channel_has_feature(s->ioc, QIO_CHANNEL_FEATURE_FD_PASS)) {
+        ret = qio_channel_readv_full(s->ioc, &iov, 1,
+                                     &msgfds, &msgfds_num,
+                                     NULL);
+    } else {
+        ret = qio_channel_readv_full(s->ioc, &iov, 1,
+                                     NULL, NULL,
+                                     NULL);
+    }
+
+    if (ret == QIO_CHANNEL_ERR_BLOCK) {
+        errno = EAGAIN;
+        ret = -1;
+    } else if (ret == -1) {
+        errno = EIO;
+    }
+
+    if (msgfds_num) {
+        /* close and clean read_msgfds */
+        for (i = 0; i < s->read_msgfds_num; i++) {
+            close(s->read_msgfds[i]);
+        }
+
+        if (s->read_msgfds_num) {
+            g_free(s->read_msgfds);
+        }
+
+        s->read_msgfds = msgfds;
+        s->read_msgfds_num = msgfds_num;
+    }
+
+    for (i = 0; i < s->read_msgfds_num; i++) {
+        int fd = s->read_msgfds[i];
+        if (fd < 0) {
+            continue;
+        }
+
+        /* O_NONBLOCK is preserved across SCM_RIGHTS so reset it */
+        qemu_set_block(fd);
+
+#ifndef MSG_CMSG_CLOEXEC
+        qemu_set_cloexec(fd);
+#endif
+    }
+
+    return ret;
+}
+
+static GSource *tcp_chr_add_watch(Chardev *chr, GIOCondition cond)
+{
+    SocketChardev *s = SOCKET_CHARDEV(chr);
+    return qio_channel_create_watch(s->ioc, cond);
+}
+
+static void tcp_chr_free_connection(Chardev *chr)
+{
+    SocketChardev *s = SOCKET_CHARDEV(chr);
+    int i;
+
+    if (!s->connected) {
+        return;
+    }
+
+    if (s->read_msgfds_num) {
+        for (i = 0; i < s->read_msgfds_num; i++) {
+            close(s->read_msgfds[i]);
+        }
+        g_free(s->read_msgfds);
+        s->read_msgfds = NULL;
+        s->read_msgfds_num = 0;
+    }
+
+    tcp_set_msgfds(chr, NULL, 0);
+    remove_fd_in_watch(chr);
+    object_unref(OBJECT(s->sioc));
+    s->sioc = NULL;
+    object_unref(OBJECT(s->ioc));
+    s->ioc = NULL;
+    g_free(chr->filename);
+    chr->filename = NULL;
+    s->connected = 0;
+}
+
+static char *SocketAddress_to_str(const char *prefix, SocketAddress *addr,
+                                  bool is_listen, bool is_telnet)
+{
+    switch (addr->type) {
+    case SOCKET_ADDRESS_KIND_INET:
+        return g_strdup_printf("%s%s:%s:%s%s", prefix,
+                               is_telnet ? "telnet" : "tcp",
+                               addr->u.inet.data->host,
+                               addr->u.inet.data->port,
+                               is_listen ? ",server" : "");
+        break;
+    case SOCKET_ADDRESS_KIND_UNIX:
+        return g_strdup_printf("%sunix:%s%s", prefix,
+                               addr->u.q_unix.data->path,
+                               is_listen ? ",server" : "");
+        break;
+    case SOCKET_ADDRESS_KIND_FD:
+        return g_strdup_printf("%sfd:%s%s", prefix, addr->u.fd.data->str,
+                               is_listen ? ",server" : "");
+        break;
+    default:
+        abort();
+    }
+}
+
+static void tcp_chr_disconnect(Chardev *chr)
+{
+    SocketChardev *s = SOCKET_CHARDEV(chr);
+
+    if (!s->connected) {
+        return;
+    }
+
+    tcp_chr_free_connection(chr);
+
+    if (s->listen_ioc) {
+        s->listen_tag = qio_channel_add_watch(
+            QIO_CHANNEL(s->listen_ioc), G_IO_IN, tcp_chr_accept, chr, NULL);
+    }
+    chr->filename = SocketAddress_to_str("disconnected:", s->addr,
+                                         s->is_listen, s->is_telnet);
+    qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
+    if (s->reconnect_time) {
+        qemu_chr_socket_restart_timer(chr);
+    }
+}
+
+static gboolean tcp_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
+{
+    Chardev *chr = CHARDEV(opaque);
+    SocketChardev *s = SOCKET_CHARDEV(opaque);
+    uint8_t buf[CHR_READ_BUF_LEN];
+    int len, size;
+
+    if (!s->connected || s->max_size <= 0) {
+        return TRUE;
+    }
+    len = sizeof(buf);
+    if (len > s->max_size) {
+        len = s->max_size;
+    }
+    size = tcp_chr_recv(chr, (void *)buf, len);
+    if (size == 0 || size == -1) {
+        /* connection closed */
+        tcp_chr_disconnect(chr);
+    } else if (size > 0) {
+        if (s->do_telnetopt) {
+            tcp_chr_process_IAC_bytes(chr, s, buf, &size);
+        }
+        if (size > 0) {
+            qemu_chr_be_write(chr, buf, size);
+        }
+    }
+
+    return TRUE;
+}
+
+static int tcp_chr_sync_read(Chardev *chr, const uint8_t *buf, int len)
+{
+    SocketChardev *s = SOCKET_CHARDEV(chr);
+    int size;
+
+    if (!s->connected) {
+        return 0;
+    }
+
+    size = tcp_chr_recv(chr, (void *) buf, len);
+    if (size == 0) {
+        /* connection closed */
+        tcp_chr_disconnect(chr);
+    }
+
+    return size;
+}
+
+static char *sockaddr_to_str(struct sockaddr_storage *ss, socklen_t ss_len,
+                             struct sockaddr_storage *ps, socklen_t ps_len,
+                             bool is_listen, bool is_telnet)
+{
+    char shost[NI_MAXHOST], sserv[NI_MAXSERV];
+    char phost[NI_MAXHOST], pserv[NI_MAXSERV];
+    const char *left = "", *right = "";
+
+    switch (ss->ss_family) {
+#ifndef _WIN32
+    case AF_UNIX:
+        return g_strdup_printf("unix:%s%s",
+                               ((struct sockaddr_un *)(ss))->sun_path,
+                               is_listen ? ",server" : "");
+#endif
+    case AF_INET6:
+        left  = "[";
+        right = "]";
+        /* fall through */
+    case AF_INET:
+        getnameinfo((struct sockaddr *) ss, ss_len, shost, sizeof(shost),
+                    sserv, sizeof(sserv), NI_NUMERICHOST | NI_NUMERICSERV);
+        getnameinfo((struct sockaddr *) ps, ps_len, phost, sizeof(phost),
+                    pserv, sizeof(pserv), NI_NUMERICHOST | NI_NUMERICSERV);
+        return g_strdup_printf("%s:%s%s%s:%s%s <-> %s%s%s:%s",
+                               is_telnet ? "telnet" : "tcp",
+                               left, shost, right, sserv,
+                               is_listen ? ",server" : "",
+                               left, phost, right, pserv);
+
+    default:
+        return g_strdup_printf("unknown");
+    }
+}
+
+static void tcp_chr_connect(void *opaque)
+{
+    Chardev *chr = CHARDEV(opaque);
+    SocketChardev *s = SOCKET_CHARDEV(opaque);
+
+    g_free(chr->filename);
+    chr->filename = sockaddr_to_str(
+        &s->sioc->localAddr, s->sioc->localAddrLen,
+        &s->sioc->remoteAddr, s->sioc->remoteAddrLen,
+        s->is_listen, s->is_telnet);
+
+    s->connected = 1;
+    if (s->ioc) {
+        chr->fd_in_tag = io_add_watch_poll(chr, s->ioc,
+                                           tcp_chr_read_poll,
+                                           tcp_chr_read,
+                                           chr, NULL);
+    }
+    qemu_chr_be_generic_open(chr);
+}
+
+static void tcp_chr_update_read_handler(Chardev *chr,
+                                        GMainContext *context)
+{
+    SocketChardev *s = SOCKET_CHARDEV(chr);
+
+    if (!s->connected) {
+        return;
+    }
+
+    remove_fd_in_watch(chr);
+    if (s->ioc) {
+        chr->fd_in_tag = io_add_watch_poll(chr, s->ioc,
+                                           tcp_chr_read_poll,
+                                           tcp_chr_read, chr,
+                                           context);
+    }
+}
+
+typedef struct {
+    Chardev *chr;
+    char buf[12];
+    size_t buflen;
+} TCPChardevTelnetInit;
+
+static gboolean tcp_chr_telnet_init_io(QIOChannel *ioc,
+                                       GIOCondition cond G_GNUC_UNUSED,
+                                       gpointer user_data)
+{
+    TCPChardevTelnetInit *init = user_data;
+    ssize_t ret;
+
+    ret = qio_channel_write(ioc, init->buf, init->buflen, NULL);
+    if (ret < 0) {
+        if (ret == QIO_CHANNEL_ERR_BLOCK) {
+            ret = 0;
+        } else {
+            tcp_chr_disconnect(init->chr);
+            return FALSE;
+        }
+    }
+    init->buflen -= ret;
+
+    if (init->buflen == 0) {
+        tcp_chr_connect(init->chr);
+        return FALSE;
+    }
+
+    memmove(init->buf, init->buf + ret, init->buflen);
+
+    return TRUE;
+}
+
+static void tcp_chr_telnet_init(Chardev *chr)
+{
+    SocketChardev *s = SOCKET_CHARDEV(chr);
+    TCPChardevTelnetInit *init = g_new0(TCPChardevTelnetInit, 1);
+    size_t n = 0;
+
+    init->chr = chr;
+    init->buflen = 12;
+
+#define IACSET(x, a, b, c)                      \
+    do {                                        \
+        x[n++] = a;                             \
+        x[n++] = b;                             \
+        x[n++] = c;                             \
+    } while (0)
+
+    /* Prep the telnet negotion to put telnet in binary,
+     * no echo, single char mode */
+    IACSET(init->buf, 0xff, 0xfb, 0x01);  /* IAC WILL ECHO */
+    IACSET(init->buf, 0xff, 0xfb, 0x03);  /* IAC WILL Suppress go ahead */
+    IACSET(init->buf, 0xff, 0xfb, 0x00);  /* IAC WILL Binary */
+    IACSET(init->buf, 0xff, 0xfd, 0x00);  /* IAC DO Binary */
+
+#undef IACSET
+
+    qio_channel_add_watch(
+        s->ioc, G_IO_OUT,
+        tcp_chr_telnet_init_io,
+        init, NULL);
+}
+
+
+static void tcp_chr_tls_handshake(QIOTask *task,
+                                  gpointer user_data)
+{
+    Chardev *chr = user_data;
+    SocketChardev *s = user_data;
+
+    if (qio_task_propagate_error(task, NULL)) {
+        tcp_chr_disconnect(chr);
+    } else {
+        if (s->do_telnetopt) {
+            tcp_chr_telnet_init(chr);
+        } else {
+            tcp_chr_connect(chr);
+        }
+    }
+}
+
+
+static void tcp_chr_tls_init(Chardev *chr)
+{
+    SocketChardev *s = SOCKET_CHARDEV(chr);
+    QIOChannelTLS *tioc;
+    Error *err = NULL;
+    gchar *name;
+
+    if (s->is_listen) {
+        tioc = qio_channel_tls_new_server(
+            s->ioc, s->tls_creds,
+            NULL, /* XXX Use an ACL */
+            &err);
+    } else {
+        tioc = qio_channel_tls_new_client(
+            s->ioc, s->tls_creds,
+            s->addr->u.inet.data->host,
+            &err);
+    }
+    if (tioc == NULL) {
+        error_free(err);
+        tcp_chr_disconnect(chr);
+        return;
+    }
+    name = g_strdup_printf("chardev-tls-%s-%s",
+                           s->is_listen ? "server" : "client",
+                           chr->label);
+    qio_channel_set_name(QIO_CHANNEL(tioc), name);
+    g_free(name);
+    object_unref(OBJECT(s->ioc));
+    s->ioc = QIO_CHANNEL(tioc);
+
+    qio_channel_tls_handshake(tioc,
+                              tcp_chr_tls_handshake,
+                              chr,
+                              NULL);
+}
+
+
+static void tcp_chr_set_client_ioc_name(Chardev *chr,
+                                        QIOChannelSocket *sioc)
+{
+    SocketChardev *s = SOCKET_CHARDEV(chr);
+    char *name;
+    name = g_strdup_printf("chardev-tcp-%s-%s",
+                           s->is_listen ? "server" : "client",
+                           chr->label);
+    qio_channel_set_name(QIO_CHANNEL(sioc), name);
+    g_free(name);
+
+}
+
+static int tcp_chr_new_client(Chardev *chr, QIOChannelSocket *sioc)
+{
+    SocketChardev *s = SOCKET_CHARDEV(chr);
+
+    if (s->ioc != NULL) {
+        return -1;
+    }
+
+    s->ioc = QIO_CHANNEL(sioc);
+    object_ref(OBJECT(sioc));
+    s->sioc = sioc;
+    object_ref(OBJECT(sioc));
+
+    qio_channel_set_blocking(s->ioc, false, NULL);
+
+    if (s->do_nodelay) {
+        qio_channel_set_delay(s->ioc, false);
+    }
+    if (s->listen_tag) {
+        g_source_remove(s->listen_tag);
+        s->listen_tag = 0;
+    }
+
+    if (s->tls_creds) {
+        tcp_chr_tls_init(chr);
+    } else {
+        if (s->do_telnetopt) {
+            tcp_chr_telnet_init(chr);
+        } else {
+            tcp_chr_connect(chr);
+        }
+    }
+
+    return 0;
+}
+
+
+static int tcp_chr_add_client(Chardev *chr, int fd)
+{
+    int ret;
+    QIOChannelSocket *sioc;
+
+    sioc = qio_channel_socket_new_fd(fd, NULL);
+    if (!sioc) {
+        return -1;
+    }
+    tcp_chr_set_client_ioc_name(chr, sioc);
+    ret = tcp_chr_new_client(chr, sioc);
+    object_unref(OBJECT(sioc));
+    return ret;
+}
+
+static gboolean tcp_chr_accept(QIOChannel *channel,
+                               GIOCondition cond,
+                               void *opaque)
+{
+    Chardev *chr = CHARDEV(opaque);
+    QIOChannelSocket *sioc;
+
+    sioc = qio_channel_socket_accept(QIO_CHANNEL_SOCKET(channel),
+                                     NULL);
+    if (!sioc) {
+        return TRUE;
+    }
+
+    tcp_chr_new_client(chr, sioc);
+
+    object_unref(OBJECT(sioc));
+
+    return TRUE;
+}
+
+static int tcp_chr_wait_connected(Chardev *chr, Error **errp)
+{
+    SocketChardev *s = SOCKET_CHARDEV(chr);
+    QIOChannelSocket *sioc;
+
+    /* It can't wait on s->connected, since it is set asynchronously
+     * in TLS and telnet cases, only wait for an accepted socket */
+    while (!s->ioc) {
+        if (s->is_listen) {
+            error_report("QEMU waiting for connection on: %s",
+                         chr->filename);
+            qio_channel_set_blocking(QIO_CHANNEL(s->listen_ioc), true, NULL);
+            tcp_chr_accept(QIO_CHANNEL(s->listen_ioc), G_IO_IN, chr);
+            qio_channel_set_blocking(QIO_CHANNEL(s->listen_ioc), false, NULL);
+        } else {
+            sioc = qio_channel_socket_new();
+            tcp_chr_set_client_ioc_name(chr, sioc);
+            if (qio_channel_socket_connect_sync(sioc, s->addr, errp) < 0) {
+                object_unref(OBJECT(sioc));
+                return -1;
+            }
+            tcp_chr_new_client(chr, sioc);
+            object_unref(OBJECT(sioc));
+        }
+    }
+
+    return 0;
+}
+
+static void char_socket_finalize(Object *obj)
+{
+    Chardev *chr = CHARDEV(obj);
+    SocketChardev *s = SOCKET_CHARDEV(obj);
+
+    tcp_chr_free_connection(chr);
+
+    if (s->reconnect_timer) {
+        g_source_remove(s->reconnect_timer);
+        s->reconnect_timer = 0;
+    }
+    qapi_free_SocketAddress(s->addr);
+    if (s->listen_tag) {
+        g_source_remove(s->listen_tag);
+        s->listen_tag = 0;
+    }
+    if (s->listen_ioc) {
+        object_unref(OBJECT(s->listen_ioc));
+    }
+    if (s->tls_creds) {
+        object_unref(OBJECT(s->tls_creds));
+    }
+
+    qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
+}
+
+static void qemu_chr_socket_connected(QIOTask *task, void *opaque)
+{
+    QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(qio_task_get_source(task));
+    Chardev *chr = CHARDEV(opaque);
+    SocketChardev *s = SOCKET_CHARDEV(chr);
+    Error *err = NULL;
+
+    if (qio_task_propagate_error(task, &err)) {
+        check_report_connect_error(chr, err);
+        error_free(err);
+        goto cleanup;
+    }
+
+    s->connect_err_reported = false;
+    tcp_chr_new_client(chr, sioc);
+
+cleanup:
+    object_unref(OBJECT(sioc));
+}
+
+static gboolean socket_reconnect_timeout(gpointer opaque)
+{
+    Chardev *chr = CHARDEV(opaque);
+    SocketChardev *s = SOCKET_CHARDEV(opaque);
+    QIOChannelSocket *sioc;
+
+    s->reconnect_timer = 0;
+
+    if (chr->be_open) {
+        return false;
+    }
+
+    sioc = qio_channel_socket_new();
+    tcp_chr_set_client_ioc_name(chr, sioc);
+    qio_channel_socket_connect_async(sioc, s->addr,
+                                     qemu_chr_socket_connected,
+                                     chr, NULL);
+
+    return false;
+}
+
+static void qmp_chardev_open_socket(Chardev *chr,
+                                    ChardevBackend *backend,
+                                    bool *be_opened,
+                                    Error **errp)
+{
+    SocketChardev *s = SOCKET_CHARDEV(chr);
+    ChardevSocket *sock = backend->u.socket.data;
+    SocketAddress *addr = sock->addr;
+    bool do_nodelay     = sock->has_nodelay ? sock->nodelay : false;
+    bool is_listen      = sock->has_server  ? sock->server  : true;
+    bool is_telnet      = sock->has_telnet  ? sock->telnet  : false;
+    bool is_waitconnect = sock->has_wait    ? sock->wait    : false;
+    int64_t reconnect   = sock->has_reconnect ? sock->reconnect : 0;
+    QIOChannelSocket *sioc = NULL;
+
+    s->is_unix = addr->type == SOCKET_ADDRESS_KIND_UNIX;
+    s->is_listen = is_listen;
+    s->is_telnet = is_telnet;
+    s->do_nodelay = do_nodelay;
+    if (sock->tls_creds) {
+        Object *creds;
+        creds = object_resolve_path_component(
+            object_get_objects_root(), sock->tls_creds);
+        if (!creds) {
+            error_setg(errp, "No TLS credentials with id '%s'",
+                       sock->tls_creds);
+            goto error;
+        }
+        s->tls_creds = (QCryptoTLSCreds *)
+            object_dynamic_cast(creds,
+                                TYPE_QCRYPTO_TLS_CREDS);
+        if (!s->tls_creds) {
+            error_setg(errp, "Object with id '%s' is not TLS credentials",
+                       sock->tls_creds);
+            goto error;
+        }
+        object_ref(OBJECT(s->tls_creds));
+        if (is_listen) {
+            if (s->tls_creds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
+                error_setg(errp, "%s",
+                           "Expected TLS credentials for server endpoint");
+                goto error;
+            }
+        } else {
+            if (s->tls_creds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT) {
+                error_setg(errp, "%s",
+                           "Expected TLS credentials for client endpoint");
+                goto error;
+            }
+        }
+    }
+
+    s->addr = QAPI_CLONE(SocketAddress, sock->addr);
+
+    qemu_chr_set_feature(chr, QEMU_CHAR_FEATURE_RECONNECTABLE);
+    if (s->is_unix) {
+        qemu_chr_set_feature(chr, QEMU_CHAR_FEATURE_FD_PASS);
+    }
+
+    /* be isn't opened until we get a connection */
+    *be_opened = false;
+
+    chr->filename = SocketAddress_to_str("disconnected:",
+                                         addr, is_listen, is_telnet);
+
+    if (is_listen) {
+        if (is_telnet) {
+            s->do_telnetopt = 1;
+        }
+    } else if (reconnect > 0) {
+        s->reconnect_time = reconnect;
+    }
+
+    if (s->reconnect_time) {
+        sioc = qio_channel_socket_new();
+        tcp_chr_set_client_ioc_name(chr, sioc);
+        qio_channel_socket_connect_async(sioc, s->addr,
+                                         qemu_chr_socket_connected,
+                                         chr, NULL);
+    } else {
+        if (s->is_listen) {
+            char *name;
+            sioc = qio_channel_socket_new();
+
+            name = g_strdup_printf("chardev-tcp-listener-%s", chr->label);
+            qio_channel_set_name(QIO_CHANNEL(sioc), name);
+            g_free(name);
+
+            if (qio_channel_socket_listen_sync(sioc, s->addr, errp) < 0) {
+                goto error;
+            }
+            s->listen_ioc = sioc;
+            if (is_waitconnect &&
+                qemu_chr_wait_connected(chr, errp) < 0) {
+                return;
+            }
+            if (!s->ioc) {
+                s->listen_tag = qio_channel_add_watch(
+                    QIO_CHANNEL(s->listen_ioc), G_IO_IN,
+                    tcp_chr_accept, chr, NULL);
+            }
+        } else if (qemu_chr_wait_connected(chr, errp) < 0) {
+            goto error;
+        }
+    }
+
+    return;
+
+error:
+    if (sioc) {
+        object_unref(OBJECT(sioc));
+    }
+}
+
+static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
+                                  Error **errp)
+{
+    bool is_listen      = qemu_opt_get_bool(opts, "server", false);
+    bool is_waitconnect = is_listen && qemu_opt_get_bool(opts, "wait", true);
+    bool is_telnet      = qemu_opt_get_bool(opts, "telnet", false);
+    bool do_nodelay     = !qemu_opt_get_bool(opts, "delay", true);
+    int64_t reconnect   = qemu_opt_get_number(opts, "reconnect", 0);
+    const char *path = qemu_opt_get(opts, "path");
+    const char *host = qemu_opt_get(opts, "host");
+    const char *port = qemu_opt_get(opts, "port");
+    const char *tls_creds = qemu_opt_get(opts, "tls-creds");
+    SocketAddress *addr;
+    ChardevSocket *sock;
+
+    backend->type = CHARDEV_BACKEND_KIND_SOCKET;
+    if (!path) {
+        if (!host) {
+            error_setg(errp, "chardev: socket: no host given");
+            return;
+        }
+        if (!port) {
+            error_setg(errp, "chardev: socket: no port given");
+            return;
+        }
+    } else {
+        if (tls_creds) {
+            error_setg(errp, "TLS can only be used over TCP socket");
+            return;
+        }
+    }
+
+    sock = backend->u.socket.data = g_new0(ChardevSocket, 1);
+    qemu_chr_parse_common(opts, qapi_ChardevSocket_base(sock));
+
+    sock->has_nodelay = true;
+    sock->nodelay = do_nodelay;
+    sock->has_server = true;
+    sock->server = is_listen;
+    sock->has_telnet = true;
+    sock->telnet = is_telnet;
+    sock->has_wait = true;
+    sock->wait = is_waitconnect;
+    sock->has_reconnect = true;
+    sock->reconnect = reconnect;
+    sock->tls_creds = g_strdup(tls_creds);
+
+    addr = g_new0(SocketAddress, 1);
+    if (path) {
+        UnixSocketAddress *q_unix;
+        addr->type = SOCKET_ADDRESS_KIND_UNIX;
+        q_unix = addr->u.q_unix.data = g_new0(UnixSocketAddress, 1);
+        q_unix->path = g_strdup(path);
+    } else {
+        addr->type = SOCKET_ADDRESS_KIND_INET;
+        addr->u.inet.data = g_new(InetSocketAddress, 1);
+        *addr->u.inet.data = (InetSocketAddress) {
+            .host = g_strdup(host),
+            .port = g_strdup(port),
+            .has_to = qemu_opt_get(opts, "to"),
+            .to = qemu_opt_get_number(opts, "to", 0),
+            .has_ipv4 = qemu_opt_get(opts, "ipv4"),
+            .ipv4 = qemu_opt_get_bool(opts, "ipv4", 0),
+            .has_ipv6 = qemu_opt_get(opts, "ipv6"),
+            .ipv6 = qemu_opt_get_bool(opts, "ipv6", 0),
+        };
+    }
+    sock->addr = addr;
+}
+
+static void char_socket_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
+
+    cc->parse = qemu_chr_parse_socket;
+    cc->open = qmp_chardev_open_socket;
+    cc->chr_wait_connected = tcp_chr_wait_connected;
+    cc->chr_write = tcp_chr_write;
+    cc->chr_sync_read = tcp_chr_sync_read;
+    cc->chr_disconnect = tcp_chr_disconnect;
+    cc->get_msgfds = tcp_get_msgfds;
+    cc->set_msgfds = tcp_set_msgfds;
+    cc->chr_add_client = tcp_chr_add_client;
+    cc->chr_add_watch = tcp_chr_add_watch;
+    cc->chr_update_read_handler = tcp_chr_update_read_handler;
+}
+
+static const TypeInfo char_socket_type_info = {
+    .name = TYPE_CHARDEV_SOCKET,
+    .parent = TYPE_CHARDEV,
+    .instance_size = sizeof(SocketChardev),
+    .instance_finalize = char_socket_finalize,
+    .class_init = char_socket_class_init,
+};
+
+static void register_types(void)
+{
+    type_register_static(&char_socket_type_info);
+}
+
+type_init(register_types);
diff --git a/chardev/char-stdio.c b/chardev/char-stdio.c
new file mode 100644
index 0000000000..be4a65962c
--- /dev/null
+++ b/chardev/char-stdio.c
@@ -0,0 +1,164 @@
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu/osdep.h"
+#include "qemu/sockets.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "sysemu/char.h"
+
+#ifdef _WIN32
+#include "char-win.h"
+#include "char-win-stdio.h"
+#else
+#include <termios.h>
+#include "char-fd.h"
+#endif
+
+#ifndef _WIN32
+/* init terminal so that we can grab keys */
+static struct termios oldtty;
+static int old_fd0_flags;
+static bool stdio_in_use;
+static bool stdio_allow_signal;
+static bool stdio_echo_state;
+
+static void term_exit(void)
+{
+    tcsetattr(0, TCSANOW, &oldtty);
+    fcntl(0, F_SETFL, old_fd0_flags);
+}
+
+static void qemu_chr_set_echo_stdio(Chardev *chr, bool echo)
+{
+    struct termios tty;
+
+    stdio_echo_state = echo;
+    tty = oldtty;
+    if (!echo) {
+        tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
+                         | INLCR | IGNCR | ICRNL | IXON);
+        tty.c_oflag |= OPOST;
+        tty.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN);
+        tty.c_cflag &= ~(CSIZE | PARENB);
+        tty.c_cflag |= CS8;
+        tty.c_cc[VMIN] = 1;
+        tty.c_cc[VTIME] = 0;
+    }
+    if (!stdio_allow_signal) {
+        tty.c_lflag &= ~ISIG;
+    }
+
+    tcsetattr(0, TCSANOW, &tty);
+}
+
+static void term_stdio_handler(int sig)
+{
+    /* restore echo after resume from suspend. */
+    qemu_chr_set_echo_stdio(NULL, stdio_echo_state);
+}
+
+static void qemu_chr_open_stdio(Chardev *chr,
+                                ChardevBackend *backend,
+                                bool *be_opened,
+                                Error **errp)
+{
+    ChardevStdio *opts = backend->u.stdio.data;
+    struct sigaction act;
+
+    if (is_daemonized()) {
+        error_setg(errp, "cannot use stdio with -daemonize");
+        return;
+    }
+
+    if (stdio_in_use) {
+        error_setg(errp, "cannot use stdio by multiple character devices");
+        return;
+    }
+
+    stdio_in_use = true;
+    old_fd0_flags = fcntl(0, F_GETFL);
+    tcgetattr(0, &oldtty);
+    qemu_set_nonblock(0);
+    atexit(term_exit);
+
+    memset(&act, 0, sizeof(act));
+    act.sa_handler = term_stdio_handler;
+    sigaction(SIGCONT, &act, NULL);
+
+    qemu_chr_open_fd(chr, 0, 1);
+
+    if (opts->has_signal) {
+        stdio_allow_signal = opts->signal;
+    }
+    qemu_chr_set_echo_stdio(chr, false);
+}
+#endif
+
+static void qemu_chr_parse_stdio(QemuOpts *opts, ChardevBackend *backend,
+                                 Error **errp)
+{
+    ChardevStdio *stdio;
+
+    backend->type = CHARDEV_BACKEND_KIND_STDIO;
+    stdio = backend->u.stdio.data = g_new0(ChardevStdio, 1);
+    qemu_chr_parse_common(opts, qapi_ChardevStdio_base(stdio));
+    stdio->has_signal = true;
+    stdio->signal = qemu_opt_get_bool(opts, "signal", true);
+}
+
+static void char_stdio_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
+
+    cc->parse = qemu_chr_parse_stdio;
+#ifndef _WIN32
+    cc->open = qemu_chr_open_stdio;
+    cc->chr_set_echo = qemu_chr_set_echo_stdio;
+#endif
+}
+
+static void char_stdio_finalize(Object *obj)
+{
+#ifndef _WIN32
+    term_exit();
+#endif
+}
+
+static const TypeInfo char_stdio_type_info = {
+    .name = TYPE_CHARDEV_STDIO,
+#ifdef _WIN32
+    .parent = TYPE_CHARDEV_WIN_STDIO,
+#else
+    .parent = TYPE_CHARDEV_FD,
+#endif
+    .instance_finalize = char_stdio_finalize,
+    .class_init = char_stdio_class_init,
+};
+
+static void register_types(void)
+{
+    type_register_static(&char_stdio_type_info);
+}
+
+type_init(register_types);
diff --git a/chardev/char-udp.c b/chardev/char-udp.c
new file mode 100644
index 0000000000..2c6c7ddd73
--- /dev/null
+++ b/chardev/char-udp.c
@@ -0,0 +1,233 @@
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu/osdep.h"
+#include "sysemu/char.h"
+#include "io/channel-socket.h"
+#include "qapi/error.h"
+
+#include "char-io.h"
+
+/***********************************************************/
+/* UDP Net console */
+
+typedef struct {
+    Chardev parent;
+    QIOChannel *ioc;
+    uint8_t buf[CHR_READ_BUF_LEN];
+    int bufcnt;
+    int bufptr;
+    int max_size;
+} UdpChardev;
+
+#define UDP_CHARDEV(obj) OBJECT_CHECK(UdpChardev, (obj), TYPE_CHARDEV_UDP)
+
+/* Called with chr_write_lock held.  */
+static int udp_chr_write(Chardev *chr, const uint8_t *buf, int len)
+{
+    UdpChardev *s = UDP_CHARDEV(chr);
+
+    return qio_channel_write(
+        s->ioc, (const char *)buf, len, NULL);
+}
+
+static int udp_chr_read_poll(void *opaque)
+{
+    Chardev *chr = CHARDEV(opaque);
+    UdpChardev *s = UDP_CHARDEV(opaque);
+
+    s->max_size = qemu_chr_be_can_write(chr);
+
+    /* If there were any stray characters in the queue process them
+     * first
+     */
+    while (s->max_size > 0 && s->bufptr < s->bufcnt) {
+        qemu_chr_be_write(chr, &s->buf[s->bufptr], 1);
+        s->bufptr++;
+        s->max_size = qemu_chr_be_can_write(chr);
+    }
+    return s->max_size;
+}
+
+static gboolean udp_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
+{
+    Chardev *chr = CHARDEV(opaque);
+    UdpChardev *s = UDP_CHARDEV(opaque);
+    ssize_t ret;
+
+    if (s->max_size == 0) {
+        return TRUE;
+    }
+    ret = qio_channel_read(
+        s->ioc, (char *)s->buf, sizeof(s->buf), NULL);
+    if (ret <= 0) {
+        remove_fd_in_watch(chr);
+        return FALSE;
+    }
+    s->bufcnt = ret;
+
+    s->bufptr = 0;
+    while (s->max_size > 0 && s->bufptr < s->bufcnt) {
+        qemu_chr_be_write(chr, &s->buf[s->bufptr], 1);
+        s->bufptr++;
+        s->max_size = qemu_chr_be_can_write(chr);
+    }
+
+    return TRUE;
+}
+
+static void udp_chr_update_read_handler(Chardev *chr,
+                                        GMainContext *context)
+{
+    UdpChardev *s = UDP_CHARDEV(chr);
+
+    remove_fd_in_watch(chr);
+    if (s->ioc) {
+        chr->fd_in_tag = io_add_watch_poll(chr, s->ioc,
+                                           udp_chr_read_poll,
+                                           udp_chr_read, chr,
+                                           context);
+    }
+}
+
+static void char_udp_finalize(Object *obj)
+{
+    Chardev *chr = CHARDEV(obj);
+    UdpChardev *s = UDP_CHARDEV(obj);
+
+    remove_fd_in_watch(chr);
+    if (s->ioc) {
+        object_unref(OBJECT(s->ioc));
+    }
+    qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
+}
+
+static void qemu_chr_parse_udp(QemuOpts *opts, ChardevBackend *backend,
+                               Error **errp)
+{
+    const char *host = qemu_opt_get(opts, "host");
+    const char *port = qemu_opt_get(opts, "port");
+    const char *localaddr = qemu_opt_get(opts, "localaddr");
+    const char *localport = qemu_opt_get(opts, "localport");
+    bool has_local = false;
+    SocketAddress *addr;
+    ChardevUdp *udp;
+
+    backend->type = CHARDEV_BACKEND_KIND_UDP;
+    if (host == NULL || strlen(host) == 0) {
+        host = "localhost";
+    }
+    if (port == NULL || strlen(port) == 0) {
+        error_setg(errp, "chardev: udp: remote port not specified");
+        return;
+    }
+    if (localport == NULL || strlen(localport) == 0) {
+        localport = "0";
+    } else {
+        has_local = true;
+    }
+    if (localaddr == NULL || strlen(localaddr) == 0) {
+        localaddr = "";
+    } else {
+        has_local = true;
+    }
+
+    udp = backend->u.udp.data = g_new0(ChardevUdp, 1);
+    qemu_chr_parse_common(opts, qapi_ChardevUdp_base(udp));
+
+    addr = g_new0(SocketAddress, 1);
+    addr->type = SOCKET_ADDRESS_KIND_INET;
+    addr->u.inet.data = g_new(InetSocketAddress, 1);
+    *addr->u.inet.data = (InetSocketAddress) {
+        .host = g_strdup(host),
+        .port = g_strdup(port),
+        .has_ipv4 = qemu_opt_get(opts, "ipv4"),
+        .ipv4 = qemu_opt_get_bool(opts, "ipv4", 0),
+        .has_ipv6 = qemu_opt_get(opts, "ipv6"),
+        .ipv6 = qemu_opt_get_bool(opts, "ipv6", 0),
+    };
+    udp->remote = addr;
+
+    if (has_local) {
+        udp->has_local = true;
+        addr = g_new0(SocketAddress, 1);
+        addr->type = SOCKET_ADDRESS_KIND_INET;
+        addr->u.inet.data = g_new(InetSocketAddress, 1);
+        *addr->u.inet.data = (InetSocketAddress) {
+            .host = g_strdup(localaddr),
+            .port = g_strdup(localport),
+        };
+        udp->local = addr;
+    }
+}
+
+static void qmp_chardev_open_udp(Chardev *chr,
+                                 ChardevBackend *backend,
+                                 bool *be_opened,
+                                 Error **errp)
+{
+    ChardevUdp *udp = backend->u.udp.data;
+    QIOChannelSocket *sioc = qio_channel_socket_new();
+    char *name;
+    UdpChardev *s = UDP_CHARDEV(chr);
+
+    if (qio_channel_socket_dgram_sync(sioc,
+                                      udp->local, udp->remote,
+                                      errp) < 0) {
+        object_unref(OBJECT(sioc));
+        return;
+    }
+
+    name = g_strdup_printf("chardev-udp-%s", chr->label);
+    qio_channel_set_name(QIO_CHANNEL(sioc), name);
+    g_free(name);
+
+    s->ioc = QIO_CHANNEL(sioc);
+    /* be isn't opened until we get a connection */
+    *be_opened = false;
+}
+
+static void char_udp_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
+
+    cc->parse = qemu_chr_parse_udp;
+    cc->open = qmp_chardev_open_udp;
+    cc->chr_write = udp_chr_write;
+    cc->chr_update_read_handler = udp_chr_update_read_handler;
+}
+
+static const TypeInfo char_udp_type_info = {
+    .name = TYPE_CHARDEV_UDP,
+    .parent = TYPE_CHARDEV,
+    .instance_size = sizeof(UdpChardev),
+    .instance_finalize = char_udp_finalize,
+    .class_init = char_udp_class_init,
+};
+
+static void register_types(void)
+{
+    type_register_static(&char_udp_type_info);
+}
+
+type_init(register_types);
diff --git a/chardev/char-win-stdio.c b/chardev/char-win-stdio.c
new file mode 100644
index 0000000000..eb44afc17a
--- /dev/null
+++ b/chardev/char-win-stdio.c
@@ -0,0 +1,266 @@
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "char-win.h"
+#include "char-win-stdio.h"
+
+typedef struct {
+    Chardev parent;
+    HANDLE  hStdIn;
+    HANDLE  hInputReadyEvent;
+    HANDLE  hInputDoneEvent;
+    HANDLE  hInputThread;
+    uint8_t win_stdio_buf;
+} WinStdioChardev;
+
+#define WIN_STDIO_CHARDEV(obj)                                          \
+    OBJECT_CHECK(WinStdioChardev, (obj), TYPE_CHARDEV_WIN_STDIO)
+
+static void win_stdio_wait_func(void *opaque)
+{
+    Chardev *chr = CHARDEV(opaque);
+    WinStdioChardev *stdio = WIN_STDIO_CHARDEV(opaque);
+    INPUT_RECORD       buf[4];
+    int                ret;
+    DWORD              dwSize;
+    int                i;
+
+    ret = ReadConsoleInput(stdio->hStdIn, buf, ARRAY_SIZE(buf), &dwSize);
+
+    if (!ret) {
+        /* Avoid error storm */
+        qemu_del_wait_object(stdio->hStdIn, NULL, NULL);
+        return;
+    }
+
+    for (i = 0; i < dwSize; i++) {
+        KEY_EVENT_RECORD *kev = &buf[i].Event.KeyEvent;
+
+        if (buf[i].EventType == KEY_EVENT && kev->bKeyDown) {
+            int j;
+            if (kev->uChar.AsciiChar != 0) {
+                for (j = 0; j < kev->wRepeatCount; j++) {
+                    if (qemu_chr_be_can_write(chr)) {
+                        uint8_t c = kev->uChar.AsciiChar;
+                        qemu_chr_be_write(chr, &c, 1);
+                    }
+                }
+            }
+        }
+    }
+}
+
+static DWORD WINAPI win_stdio_thread(LPVOID param)
+{
+    WinStdioChardev *stdio = WIN_STDIO_CHARDEV(param);
+    int                ret;
+    DWORD              dwSize;
+
+    while (1) {
+
+        /* Wait for one byte */
+        ret = ReadFile(stdio->hStdIn, &stdio->win_stdio_buf, 1, &dwSize, NULL);
+
+        /* Exit in case of error, continue if nothing read */
+        if (!ret) {
+            break;
+        }
+        if (!dwSize) {
+            continue;
+        }
+
+        /* Some terminal emulator returns \r\n for Enter, just pass \n */
+        if (stdio->win_stdio_buf == '\r') {
+            continue;
+        }
+
+        /* Signal the main thread and wait until the byte was eaten */
+        if (!SetEvent(stdio->hInputReadyEvent)) {
+            break;
+        }
+        if (WaitForSingleObject(stdio->hInputDoneEvent, INFINITE)
+            != WAIT_OBJECT_0) {
+            break;
+        }
+    }
+
+    qemu_del_wait_object(stdio->hInputReadyEvent, NULL, NULL);
+    return 0;
+}
+
+static void win_stdio_thread_wait_func(void *opaque)
+{
+    Chardev *chr = CHARDEV(opaque);
+    WinStdioChardev *stdio = WIN_STDIO_CHARDEV(opaque);
+
+    if (qemu_chr_be_can_write(chr)) {
+        qemu_chr_be_write(chr, &stdio->win_stdio_buf, 1);
+    }
+
+    SetEvent(stdio->hInputDoneEvent);
+}
+
+static void qemu_chr_set_echo_win_stdio(Chardev *chr, bool echo)
+{
+    WinStdioChardev *stdio = WIN_STDIO_CHARDEV(chr);
+    DWORD              dwMode = 0;
+
+    GetConsoleMode(stdio->hStdIn, &dwMode);
+
+    if (echo) {
+        SetConsoleMode(stdio->hStdIn, dwMode | ENABLE_ECHO_INPUT);
+    } else {
+        SetConsoleMode(stdio->hStdIn, dwMode & ~ENABLE_ECHO_INPUT);
+    }
+}
+
+static void qemu_chr_open_stdio(Chardev *chr,
+                                ChardevBackend *backend,
+                                bool *be_opened,
+                                Error **errp)
+{
+    WinStdioChardev *stdio = WIN_STDIO_CHARDEV(chr);
+    DWORD              dwMode;
+    int                is_console = 0;
+
+    stdio->hStdIn = GetStdHandle(STD_INPUT_HANDLE);
+    if (stdio->hStdIn == INVALID_HANDLE_VALUE) {
+        error_setg(errp, "cannot open stdio: invalid handle");
+        return;
+    }
+
+    is_console = GetConsoleMode(stdio->hStdIn, &dwMode) != 0;
+
+    if (is_console) {
+        if (qemu_add_wait_object(stdio->hStdIn,
+                                 win_stdio_wait_func, chr)) {
+            error_setg(errp, "qemu_add_wait_object: failed");
+            goto err1;
+        }
+    } else {
+        DWORD   dwId;
+
+        stdio->hInputReadyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+        stdio->hInputDoneEvent  = CreateEvent(NULL, FALSE, FALSE, NULL);
+        if (stdio->hInputReadyEvent == INVALID_HANDLE_VALUE
+            || stdio->hInputDoneEvent == INVALID_HANDLE_VALUE) {
+            error_setg(errp, "cannot create event");
+            goto err2;
+        }
+        if (qemu_add_wait_object(stdio->hInputReadyEvent,
+                                 win_stdio_thread_wait_func, chr)) {
+            error_setg(errp, "qemu_add_wait_object: failed");
+            goto err2;
+        }
+        stdio->hInputThread     = CreateThread(NULL, 0, win_stdio_thread,
+                                               chr, 0, &dwId);
+
+        if (stdio->hInputThread == INVALID_HANDLE_VALUE) {
+            error_setg(errp, "cannot create stdio thread");
+            goto err3;
+        }
+    }
+
+    dwMode |= ENABLE_LINE_INPUT;
+
+    if (is_console) {
+        /* set the terminal in raw mode */
+        /* ENABLE_QUICK_EDIT_MODE | ENABLE_EXTENDED_FLAGS */
+        dwMode |= ENABLE_PROCESSED_INPUT;
+    }
+
+    SetConsoleMode(stdio->hStdIn, dwMode);
+
+    qemu_chr_set_echo_win_stdio(chr, false);
+
+    return;
+
+err3:
+    qemu_del_wait_object(stdio->hInputReadyEvent, NULL, NULL);
+err2:
+    CloseHandle(stdio->hInputReadyEvent);
+    CloseHandle(stdio->hInputDoneEvent);
+err1:
+    qemu_del_wait_object(stdio->hStdIn, NULL, NULL);
+}
+
+static void char_win_stdio_finalize(Object *obj)
+{
+    WinStdioChardev *stdio = WIN_STDIO_CHARDEV(obj);
+
+    if (stdio->hInputReadyEvent != INVALID_HANDLE_VALUE) {
+        CloseHandle(stdio->hInputReadyEvent);
+    }
+    if (stdio->hInputDoneEvent != INVALID_HANDLE_VALUE) {
+        CloseHandle(stdio->hInputDoneEvent);
+    }
+    if (stdio->hInputThread != INVALID_HANDLE_VALUE) {
+        TerminateThread(stdio->hInputThread, 0);
+    }
+}
+
+static int win_stdio_write(Chardev *chr, const uint8_t *buf, int len)
+{
+    HANDLE  hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
+    DWORD   dwSize;
+    int     len1;
+
+    len1 = len;
+
+    while (len1 > 0) {
+        if (!WriteFile(hStdOut, buf, len1, &dwSize, NULL)) {
+            break;
+        }
+        buf  += dwSize;
+        len1 -= dwSize;
+    }
+
+    return len - len1;
+}
+
+static void char_win_stdio_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
+
+    cc->open = qemu_chr_open_stdio;
+    cc->chr_write = win_stdio_write;
+    cc->chr_set_echo = qemu_chr_set_echo_win_stdio;
+}
+
+static const TypeInfo char_win_stdio_type_info = {
+    .name = TYPE_CHARDEV_WIN_STDIO,
+    .parent = TYPE_CHARDEV,
+    .instance_size = sizeof(WinStdioChardev),
+    .instance_finalize = char_win_stdio_finalize,
+    .class_init = char_win_stdio_class_init,
+    .abstract = true,
+};
+
+static void register_types(void)
+{
+    type_register_static(&char_win_stdio_type_info);
+}
+
+type_init(register_types);
diff --git a/chardev/char-win-stdio.h b/chardev/char-win-stdio.h
new file mode 100644
index 0000000000..d7314f734d
--- /dev/null
+++ b/chardev/char-win-stdio.h
@@ -0,0 +1,29 @@
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef CHAR_WIN_STDIO_H
+#define CHAR_WIN_STDIO_H
+
+#define TYPE_CHARDEV_WIN_STDIO "chardev-win-stdio"
+
+#endif /* CHAR_WIN_STDIO_H */
diff --git a/chardev/char-win.c b/chardev/char-win.c
new file mode 100644
index 0000000000..e4b6957ded
--- /dev/null
+++ b/chardev/char-win.c
@@ -0,0 +1,265 @@
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "qapi/error.h"
+#include "char-win.h"
+
+static void win_chr_readfile(Chardev *chr)
+{
+    WinChardev *s = WIN_CHARDEV(chr);
+
+    int ret, err;
+    uint8_t buf[CHR_READ_BUF_LEN];
+    DWORD size;
+
+    ZeroMemory(&s->orecv, sizeof(s->orecv));
+    s->orecv.hEvent = s->hrecv;
+    ret = ReadFile(s->hcom, buf, s->len, &size, &s->orecv);
+    if (!ret) {
+        err = GetLastError();
+        if (err == ERROR_IO_PENDING) {
+            ret = GetOverlappedResult(s->hcom, &s->orecv, &size, TRUE);
+        }
+    }
+
+    if (size > 0) {
+        qemu_chr_be_write(chr, buf, size);
+    }
+}
+
+static void win_chr_read(Chardev *chr)
+{
+    WinChardev *s = WIN_CHARDEV(chr);
+
+    if (s->len > s->max_size) {
+        s->len = s->max_size;
+    }
+    if (s->len == 0) {
+        return;
+    }
+
+    win_chr_readfile(chr);
+}
+
+static int win_chr_read_poll(Chardev *chr)
+{
+    WinChardev *s = WIN_CHARDEV(chr);
+
+    s->max_size = qemu_chr_be_can_write(chr);
+    return s->max_size;
+}
+
+static int win_chr_poll(void *opaque)
+{
+    Chardev *chr = CHARDEV(opaque);
+    WinChardev *s = WIN_CHARDEV(opaque);
+    COMSTAT status;
+    DWORD comerr;
+
+    ClearCommError(s->hcom, &comerr, &status);
+    if (status.cbInQue > 0) {
+        s->len = status.cbInQue;
+        win_chr_read_poll(chr);
+        win_chr_read(chr);
+        return 1;
+    }
+    return 0;
+}
+
+int win_chr_init(Chardev *chr, const char *filename, Error **errp)
+{
+    WinChardev *s = WIN_CHARDEV(chr);
+    COMMCONFIG comcfg;
+    COMMTIMEOUTS cto = { 0, 0, 0, 0, 0};
+    COMSTAT comstat;
+    DWORD size;
+    DWORD err;
+
+    s->hsend = CreateEvent(NULL, TRUE, FALSE, NULL);
+    if (!s->hsend) {
+        error_setg(errp, "Failed CreateEvent");
+        goto fail;
+    }
+    s->hrecv = CreateEvent(NULL, TRUE, FALSE, NULL);
+    if (!s->hrecv) {
+        error_setg(errp, "Failed CreateEvent");
+        goto fail;
+    }
+
+    s->hcom = CreateFile(filename, GENERIC_READ | GENERIC_WRITE, 0, NULL,
+                      OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
+    if (s->hcom == INVALID_HANDLE_VALUE) {
+        error_setg(errp, "Failed CreateFile (%lu)", GetLastError());
+        s->hcom = NULL;
+        goto fail;
+    }
+
+    if (!SetupComm(s->hcom, NRECVBUF, NSENDBUF)) {
+        error_setg(errp, "Failed SetupComm");
+        goto fail;
+    }
+
+    ZeroMemory(&comcfg, sizeof(COMMCONFIG));
+    size = sizeof(COMMCONFIG);
+    GetDefaultCommConfig(filename, &comcfg, &size);
+    comcfg.dcb.DCBlength = sizeof(DCB);
+    CommConfigDialog(filename, NULL, &comcfg);
+
+    if (!SetCommState(s->hcom, &comcfg.dcb)) {
+        error_setg(errp, "Failed SetCommState");
+        goto fail;
+    }
+
+    if (!SetCommMask(s->hcom, EV_ERR)) {
+        error_setg(errp, "Failed SetCommMask");
+        goto fail;
+    }
+
+    cto.ReadIntervalTimeout = MAXDWORD;
+    if (!SetCommTimeouts(s->hcom, &cto)) {
+        error_setg(errp, "Failed SetCommTimeouts");
+        goto fail;
+    }
+
+    if (!ClearCommError(s->hcom, &err, &comstat)) {
+        error_setg(errp, "Failed ClearCommError");
+        goto fail;
+    }
+    qemu_add_polling_cb(win_chr_poll, chr);
+    return 0;
+
+ fail:
+    return -1;
+}
+
+int win_chr_pipe_poll(void *opaque)
+{
+    Chardev *chr = CHARDEV(opaque);
+    WinChardev *s = WIN_CHARDEV(opaque);
+    DWORD size;
+
+    PeekNamedPipe(s->hcom, NULL, 0, NULL, &size, NULL);
+    if (size > 0) {
+        s->len = size;
+        win_chr_read_poll(chr);
+        win_chr_read(chr);
+        return 1;
+    }
+    return 0;
+}
+
+/* Called with chr_write_lock held.  */
+static int win_chr_write(Chardev *chr, const uint8_t *buf, int len1)
+{
+    WinChardev *s = WIN_CHARDEV(chr);
+    DWORD len, ret, size, err;
+
+    len = len1;
+    ZeroMemory(&s->osend, sizeof(s->osend));
+    s->osend.hEvent = s->hsend;
+    while (len > 0) {
+        if (s->hsend) {
+            ret = WriteFile(s->hcom, buf, len, &size, &s->osend);
+        } else {
+            ret = WriteFile(s->hcom, buf, len, &size, NULL);
+        }
+        if (!ret) {
+            err = GetLastError();
+            if (err == ERROR_IO_PENDING) {
+                ret = GetOverlappedResult(s->hcom, &s->osend, &size, TRUE);
+                if (ret) {
+                    buf += size;
+                    len -= size;
+                } else {
+                    break;
+                }
+            } else {
+                break;
+            }
+        } else {
+            buf += size;
+            len -= size;
+        }
+    }
+    return len1 - len;
+}
+
+static void char_win_finalize(Object *obj)
+{
+    Chardev *chr = CHARDEV(obj);
+    WinChardev *s = WIN_CHARDEV(chr);
+
+    if (s->skip_free) {
+        return;
+    }
+
+    if (s->hsend) {
+        CloseHandle(s->hsend);
+    }
+    if (s->hrecv) {
+        CloseHandle(s->hrecv);
+    }
+    if (s->hcom) {
+        CloseHandle(s->hcom);
+    }
+    if (s->fpipe) {
+        qemu_del_polling_cb(win_chr_pipe_poll, chr);
+    } else {
+        qemu_del_polling_cb(win_chr_poll, chr);
+    }
+
+    qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
+}
+
+void qemu_chr_open_win_file(Chardev *chr, HANDLE fd_out)
+{
+    WinChardev *s = WIN_CHARDEV(chr);
+
+    s->skip_free = true;
+    s->hcom = fd_out;
+}
+
+static void char_win_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
+
+    cc->chr_write = win_chr_write;
+}
+
+static const TypeInfo char_win_type_info = {
+    .name = TYPE_CHARDEV_WIN,
+    .parent = TYPE_CHARDEV,
+    .instance_size = sizeof(WinChardev),
+    .instance_finalize = char_win_finalize,
+    .class_init = char_win_class_init,
+    .abstract = true,
+};
+
+static void register_types(void)
+{
+    type_register_static(&char_win_type_info);
+}
+
+type_init(register_types);
diff --git a/chardev/char-win.h b/chardev/char-win.h
new file mode 100644
index 0000000000..d78a7d7972
--- /dev/null
+++ b/chardev/char-win.h
@@ -0,0 +1,53 @@
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef CHAR_WIN_H
+#define CHAR_WIN_H
+
+#include "sysemu/char.h"
+
+typedef struct {
+    Chardev parent;
+    int max_size;
+    HANDLE hcom, hrecv, hsend;
+    OVERLAPPED orecv;
+    BOOL fpipe;
+    DWORD len;
+
+    /* Protected by the Chardev chr_write_lock.  */
+    OVERLAPPED osend;
+    /* FIXME: file/console do not finalize */
+    bool skip_free;
+} WinChardev;
+
+#define NSENDBUF 2048
+#define NRECVBUF 2048
+
+#define TYPE_CHARDEV_WIN "chardev-win"
+#define WIN_CHARDEV(obj) OBJECT_CHECK(WinChardev, (obj), TYPE_CHARDEV_WIN)
+
+void qemu_chr_open_win_file(Chardev *chr, HANDLE fd_out);
+int win_chr_init(Chardev *chr, const char *filename, Error **errp);
+int win_chr_pipe_poll(void *opaque);
+
+#endif /* CHAR_WIN_H */
diff --git a/chardev/char.c b/chardev/char.c
new file mode 100644
index 0000000000..abd525f75e
--- /dev/null
+++ b/chardev/char.c
@@ -0,0 +1,1334 @@
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "qemu/cutils.h"
+#include "monitor/monitor.h"
+#include "sysemu/sysemu.h"
+#include "qemu/config-file.h"
+#include "qemu/error-report.h"
+#include "sysemu/char.h"
+#include "qmp-commands.h"
+#include "qapi-visit.h"
+#include "sysemu/replay.h"
+#include "qemu/help_option.h"
+
+#include "char-mux.h"
+#include "char-io.h"
+#include "char-parallel.h"
+#include "char-serial.h"
+
+/***********************************************************/
+/* character device */
+
+static QTAILQ_HEAD(ChardevHead, Chardev) chardevs =
+    QTAILQ_HEAD_INITIALIZER(chardevs);
+
+void qemu_chr_be_event(Chardev *s, int event)
+{
+    CharBackend *be = s->be;
+
+    /* Keep track if the char device is open */
+    switch (event) {
+        case CHR_EVENT_OPENED:
+            s->be_open = 1;
+            break;
+        case CHR_EVENT_CLOSED:
+            s->be_open = 0;
+            break;
+    }
+
+    if (!be || !be->chr_event) {
+        return;
+    }
+
+    be->chr_event(be->opaque, event);
+}
+
+void qemu_chr_be_generic_open(Chardev *s)
+{
+    qemu_chr_be_event(s, CHR_EVENT_OPENED);
+}
+
+
+/* Not reporting errors from writing to logfile, as logs are
+ * defined to be "best effort" only */
+static void qemu_chr_fe_write_log(Chardev *s,
+                                  const uint8_t *buf, size_t len)
+{
+    size_t done = 0;
+    ssize_t ret;
+
+    if (s->logfd < 0) {
+        return;
+    }
+
+    while (done < len) {
+    retry:
+        ret = write(s->logfd, buf + done, len - done);
+        if (ret == -1 && errno == EAGAIN) {
+            g_usleep(100);
+            goto retry;
+        }
+
+        if (ret <= 0) {
+            return;
+        }
+        done += ret;
+    }
+}
+
+static int qemu_chr_fe_write_buffer(Chardev *s,
+                                    const uint8_t *buf, int len, int *offset)
+{
+    ChardevClass *cc = CHARDEV_GET_CLASS(s);
+    int res = 0;
+    *offset = 0;
+
+    qemu_mutex_lock(&s->chr_write_lock);
+    while (*offset < len) {
+    retry:
+        res = cc->chr_write(s, buf + *offset, len - *offset);
+        if (res < 0 && errno == EAGAIN) {
+            g_usleep(100);
+            goto retry;
+        }
+
+        if (res <= 0) {
+            break;
+        }
+
+        *offset += res;
+    }
+    if (*offset > 0) {
+        qemu_chr_fe_write_log(s, buf, *offset);
+    }
+    qemu_mutex_unlock(&s->chr_write_lock);
+
+    return res;
+}
+
+static bool qemu_chr_replay(Chardev *chr)
+{
+    return qemu_chr_has_feature(chr, QEMU_CHAR_FEATURE_REPLAY);
+}
+
+int qemu_chr_fe_write(CharBackend *be, const uint8_t *buf, int len)
+{
+    Chardev *s = be->chr;
+    ChardevClass *cc;
+    int ret;
+
+    if (!s) {
+        return 0;
+    }
+
+    if (qemu_chr_replay(s) && replay_mode == REPLAY_MODE_PLAY) {
+        int offset;
+        replay_char_write_event_load(&ret, &offset);
+        assert(offset <= len);
+        qemu_chr_fe_write_buffer(s, buf, offset, &offset);
+        return ret;
+    }
+
+    cc = CHARDEV_GET_CLASS(s);
+    qemu_mutex_lock(&s->chr_write_lock);
+    ret = cc->chr_write(s, buf, len);
+
+    if (ret > 0) {
+        qemu_chr_fe_write_log(s, buf, ret);
+    }
+
+    qemu_mutex_unlock(&s->chr_write_lock);
+    
+    if (qemu_chr_replay(s) && replay_mode == REPLAY_MODE_RECORD) {
+        replay_char_write_event_save(ret, ret < 0 ? 0 : ret);
+    }
+    
+    return ret;
+}
+
+int qemu_chr_write_all(Chardev *s, const uint8_t *buf, int len)
+{
+    int offset;
+    int res;
+
+    if (qemu_chr_replay(s) && replay_mode == REPLAY_MODE_PLAY) {
+        replay_char_write_event_load(&res, &offset);
+        assert(offset <= len);
+        qemu_chr_fe_write_buffer(s, buf, offset, &offset);
+        return res;
+    }
+
+    res = qemu_chr_fe_write_buffer(s, buf, len, &offset);
+
+    if (qemu_chr_replay(s) && replay_mode == REPLAY_MODE_RECORD) {
+        replay_char_write_event_save(res, offset);
+    }
+
+    if (res < 0) {
+        return res;
+    }
+    return offset;
+}
+
+int qemu_chr_fe_write_all(CharBackend *be, const uint8_t *buf, int len)
+{
+    Chardev *s = be->chr;
+
+    if (!s) {
+        return 0;
+    }
+
+    return qemu_chr_write_all(s, buf, len);
+}
+
+int qemu_chr_fe_read_all(CharBackend *be, uint8_t *buf, int len)
+{
+    Chardev *s = be->chr;
+    int offset = 0, counter = 10;
+    int res;
+
+    if (!s || !CHARDEV_GET_CLASS(s)->chr_sync_read) {
+        return 0;
+    }
+
+    if (qemu_chr_replay(s) && replay_mode == REPLAY_MODE_PLAY) {
+        return replay_char_read_all_load(buf);
+    }
+
+    while (offset < len) {
+    retry:
+        res = CHARDEV_GET_CLASS(s)->chr_sync_read(s, buf + offset,
+                                                  len - offset);
+        if (res == -1 && errno == EAGAIN) {
+            g_usleep(100);
+            goto retry;
+        }
+
+        if (res == 0) {
+            break;
+        }
+
+        if (res < 0) {
+            if (qemu_chr_replay(s) && replay_mode == REPLAY_MODE_RECORD) {
+                replay_char_read_all_save_error(res);
+            }
+            return res;
+        }
+
+        offset += res;
+
+        if (!counter--) {
+            break;
+        }
+    }
+
+    if (qemu_chr_replay(s) && replay_mode == REPLAY_MODE_RECORD) {
+        replay_char_read_all_save_buf(buf, offset);
+    }
+    return offset;
+}
+
+int qemu_chr_fe_ioctl(CharBackend *be, int cmd, void *arg)
+{
+    Chardev *s = be->chr;
+    int res;
+
+    if (!s || !CHARDEV_GET_CLASS(s)->chr_ioctl || qemu_chr_replay(s)) {
+        res = -ENOTSUP;
+    } else {
+        res = CHARDEV_GET_CLASS(s)->chr_ioctl(s, cmd, arg);
+    }
+
+    return res;
+}
+
+int qemu_chr_be_can_write(Chardev *s)
+{
+    CharBackend *be = s->be;
+
+    if (!be || !be->chr_can_read) {
+        return 0;
+    }
+
+    return be->chr_can_read(be->opaque);
+}
+
+void qemu_chr_be_write_impl(Chardev *s, uint8_t *buf, int len)
+{
+    CharBackend *be = s->be;
+
+    if (be && be->chr_read) {
+        be->chr_read(be->opaque, buf, len);
+    }
+}
+
+void qemu_chr_be_write(Chardev *s, uint8_t *buf, int len)
+{
+    if (qemu_chr_replay(s)) {
+        if (replay_mode == REPLAY_MODE_PLAY) {
+            return;
+        }
+        replay_chr_be_write(s, buf, len);
+    } else {
+        qemu_chr_be_write_impl(s, buf, len);
+    }
+}
+
+int qemu_chr_fe_get_msgfd(CharBackend *be)
+{
+    Chardev *s = be->chr;
+    int fd;
+    int res = (qemu_chr_fe_get_msgfds(be, &fd, 1) == 1) ? fd : -1;
+    if (s && qemu_chr_replay(s)) {
+        error_report("Replay: get msgfd is not supported "
+                     "for serial devices yet");
+        exit(1);
+    }
+    return res;
+}
+
+int qemu_chr_fe_get_msgfds(CharBackend *be, int *fds, int len)
+{
+    Chardev *s = be->chr;
+
+    if (!s) {
+        return -1;
+    }
+
+    return CHARDEV_GET_CLASS(s)->get_msgfds ?
+        CHARDEV_GET_CLASS(s)->get_msgfds(s, fds, len) : -1;
+}
+
+int qemu_chr_fe_set_msgfds(CharBackend *be, int *fds, int num)
+{
+    Chardev *s = be->chr;
+
+    if (!s) {
+        return -1;
+    }
+
+    return CHARDEV_GET_CLASS(s)->set_msgfds ?
+        CHARDEV_GET_CLASS(s)->set_msgfds(s, fds, num) : -1;
+}
+
+int qemu_chr_add_client(Chardev *s, int fd)
+{
+    return CHARDEV_GET_CLASS(s)->chr_add_client ?
+        CHARDEV_GET_CLASS(s)->chr_add_client(s, fd) : -1;
+}
+
+void qemu_chr_fe_accept_input(CharBackend *be)
+{
+    Chardev *s = be->chr;
+
+    if (!s) {
+        return;
+    }
+
+    if (CHARDEV_GET_CLASS(s)->chr_accept_input) {
+        CHARDEV_GET_CLASS(s)->chr_accept_input(s);
+    }
+    qemu_notify_event();
+}
+
+void qemu_chr_fe_printf(CharBackend *be, const char *fmt, ...)
+{
+    char buf[CHR_READ_BUF_LEN];
+    va_list ap;
+    va_start(ap, fmt);
+    vsnprintf(buf, sizeof(buf), fmt, ap);
+    /* XXX this blocks entire thread. Rewrite to use
+     * qemu_chr_fe_write and background I/O callbacks */
+    qemu_chr_fe_write_all(be, (uint8_t *)buf, strlen(buf));
+    va_end(ap);
+}
+
+static void qemu_char_open(Chardev *chr, ChardevBackend *backend,
+                           bool *be_opened, Error **errp)
+{
+    ChardevClass *cc = CHARDEV_GET_CLASS(chr);
+    /* Any ChardevCommon member would work */
+    ChardevCommon *common = backend ? backend->u.null.data : NULL;
+
+    if (common && common->has_logfile) {
+        int flags = O_WRONLY | O_CREAT;
+        if (common->has_logappend &&
+            common->logappend) {
+            flags |= O_APPEND;
+        } else {
+            flags |= O_TRUNC;
+        }
+        chr->logfd = qemu_open(common->logfile, flags, 0666);
+        if (chr->logfd < 0) {
+            error_setg_errno(errp, errno,
+                             "Unable to open logfile %s",
+                             common->logfile);
+            return;
+        }
+    }
+
+    if (cc->open) {
+        cc->open(chr, backend, be_opened, errp);
+    }
+}
+
+static void char_init(Object *obj)
+{
+    Chardev *chr = CHARDEV(obj);
+
+    chr->logfd = -1;
+    qemu_mutex_init(&chr->chr_write_lock);
+}
+
+static int null_chr_write(Chardev *chr, const uint8_t *buf, int len)
+{
+    return len;
+}
+
+static void char_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
+
+    cc->chr_write = null_chr_write;
+}
+
+static void char_finalize(Object *obj)
+{
+    Chardev *chr = CHARDEV(obj);
+
+    if (chr->be) {
+        chr->be->chr = NULL;
+    }
+    g_free(chr->filename);
+    g_free(chr->label);
+    if (chr->logfd != -1) {
+        close(chr->logfd);
+    }
+    qemu_mutex_destroy(&chr->chr_write_lock);
+}
+
+static const TypeInfo char_type_info = {
+    .name = TYPE_CHARDEV,
+    .parent = TYPE_OBJECT,
+    .instance_size = sizeof(Chardev),
+    .instance_init = char_init,
+    .instance_finalize = char_finalize,
+    .abstract = true,
+    .class_size = sizeof(ChardevClass),
+    .class_init = char_class_init,
+};
+
+/**
+ * Called after processing of default and command-line-specified
+ * chardevs to deliver CHR_EVENT_OPENED events to any FEs attached
+ * to a mux chardev. This is done here to ensure that
+ * output/prompts/banners are only displayed for the FE that has
+ * focus when initial command-line processing/machine init is
+ * completed.
+ *
+ * After this point, any new FE attached to any new or existing
+ * mux will receive CHR_EVENT_OPENED notifications for the BE
+ * immediately.
+ */
+static void muxes_realize_done(Notifier *notifier, void *unused)
+{
+    Chardev *chr;
+
+    QTAILQ_FOREACH(chr, &chardevs, next) {
+        if (CHARDEV_IS_MUX(chr)) {
+            MuxChardev *d = MUX_CHARDEV(chr);
+            int i;
+
+            /* send OPENED to all already-attached FEs */
+            for (i = 0; i < d->mux_cnt; i++) {
+                mux_chr_send_event(d, i, CHR_EVENT_OPENED);
+            }
+            /* mark mux as OPENED so any new FEs will immediately receive
+             * OPENED event
+             */
+            qemu_chr_be_generic_open(chr);
+        }
+    }
+    muxes_realized = true;
+}
+
+static Notifier muxes_realize_notify = {
+    .notify = muxes_realize_done,
+};
+
+Chardev *qemu_chr_fe_get_driver(CharBackend *be)
+{
+    return be->chr;
+}
+
+bool qemu_chr_fe_init(CharBackend *b, Chardev *s, Error **errp)
+{
+    int tag = 0;
+
+    if (CHARDEV_IS_MUX(s)) {
+        MuxChardev *d = MUX_CHARDEV(s);
+
+        if (d->mux_cnt >= MAX_MUX) {
+            goto unavailable;
+        }
+
+        d->backends[d->mux_cnt] = b;
+        tag = d->mux_cnt++;
+    } else if (s->be) {
+        goto unavailable;
+    } else {
+        s->be = b;
+    }
+
+    b->fe_open = false;
+    b->tag = tag;
+    b->chr = s;
+    return true;
+
+unavailable:
+    error_setg(errp, QERR_DEVICE_IN_USE, s->label);
+    return false;
+}
+
+static bool qemu_chr_is_busy(Chardev *s)
+{
+    if (CHARDEV_IS_MUX(s)) {
+        MuxChardev *d = MUX_CHARDEV(s);
+        return d->mux_cnt >= 0;
+    } else {
+        return s->be != NULL;
+    }
+}
+
+void qemu_chr_fe_deinit(CharBackend *b)
+{
+    assert(b);
+
+    if (b->chr) {
+        qemu_chr_fe_set_handlers(b, NULL, NULL, NULL, NULL, NULL, true);
+        if (b->chr->be == b) {
+            b->chr->be = NULL;
+        }
+        if (CHARDEV_IS_MUX(b->chr)) {
+            MuxChardev *d = MUX_CHARDEV(b->chr);
+            d->backends[b->tag] = NULL;
+        }
+        b->chr = NULL;
+    }
+}
+
+void qemu_chr_fe_set_handlers(CharBackend *b,
+                              IOCanReadHandler *fd_can_read,
+                              IOReadHandler *fd_read,
+                              IOEventHandler *fd_event,
+                              void *opaque,
+                              GMainContext *context,
+                              bool set_open)
+{
+    Chardev *s;
+    ChardevClass *cc;
+    int fe_open;
+
+    s = b->chr;
+    if (!s) {
+        return;
+    }
+
+    cc = CHARDEV_GET_CLASS(s);
+    if (!opaque && !fd_can_read && !fd_read && !fd_event) {
+        fe_open = 0;
+        remove_fd_in_watch(s);
+    } else {
+        fe_open = 1;
+    }
+    b->chr_can_read = fd_can_read;
+    b->chr_read = fd_read;
+    b->chr_event = fd_event;
+    b->opaque = opaque;
+    if (cc->chr_update_read_handler) {
+        cc->chr_update_read_handler(s, context);
+    }
+
+    if (set_open) {
+        qemu_chr_fe_set_open(b, fe_open);
+    }
+
+    if (fe_open) {
+        qemu_chr_fe_take_focus(b);
+        /* We're connecting to an already opened device, so let's make sure we
+           also get the open event */
+        if (s->be_open) {
+            qemu_chr_be_generic_open(s);
+        }
+    }
+
+    if (CHARDEV_IS_MUX(s)) {
+        mux_chr_set_handlers(s, context);
+    }
+}
+
+void qemu_chr_fe_take_focus(CharBackend *b)
+{
+    if (!b->chr) {
+        return;
+    }
+
+    if (CHARDEV_IS_MUX(b->chr)) {
+        mux_set_focus(b->chr, b->tag);
+    }
+}
+
+int qemu_chr_wait_connected(Chardev *chr, Error **errp)
+{
+    ChardevClass *cc = CHARDEV_GET_CLASS(chr);
+
+    if (cc->chr_wait_connected) {
+        return cc->chr_wait_connected(chr, errp);
+    }
+
+    return 0;
+}
+
+int qemu_chr_fe_wait_connected(CharBackend *be, Error **errp)
+{
+    if (!be->chr) {
+        error_setg(errp, "missing associated backend");
+        return -1;
+    }
+
+    return qemu_chr_wait_connected(be->chr, errp);
+}
+
+QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename)
+{
+    char host[65], port[33], width[8], height[8];
+    int pos;
+    const char *p;
+    QemuOpts *opts;
+    Error *local_err = NULL;
+
+    opts = qemu_opts_create(qemu_find_opts("chardev"), label, 1, &local_err);
+    if (local_err) {
+        error_report_err(local_err);
+        return NULL;
+    }
+
+    if (strstart(filename, "mon:", &p)) {
+        filename = p;
+        qemu_opt_set(opts, "mux", "on", &error_abort);
+        if (strcmp(filename, "stdio") == 0) {
+            /* Monitor is muxed to stdio: do not exit on Ctrl+C by default
+             * but pass it to the guest.  Handle this only for compat syntax,
+             * for -chardev syntax we have special option for this.
+             * This is what -nographic did, redirecting+muxing serial+monitor
+             * to stdio causing Ctrl+C to be passed to guest. */
+            qemu_opt_set(opts, "signal", "off", &error_abort);
+        }
+    }
+
+    if (strcmp(filename, "null")    == 0 ||
+        strcmp(filename, "pty")     == 0 ||
+        strcmp(filename, "msmouse") == 0 ||
+        strcmp(filename, "braille") == 0 ||
+        strcmp(filename, "testdev") == 0 ||
+        strcmp(filename, "stdio")   == 0) {
+        qemu_opt_set(opts, "backend", filename, &error_abort);
+        return opts;
+    }
+    if (strstart(filename, "vc", &p)) {
+        qemu_opt_set(opts, "backend", "vc", &error_abort);
+        if (*p == ':') {
+            if (sscanf(p+1, "%7[0-9]x%7[0-9]", width, height) == 2) {
+                /* pixels */
+                qemu_opt_set(opts, "width", width, &error_abort);
+                qemu_opt_set(opts, "height", height, &error_abort);
+            } else if (sscanf(p+1, "%7[0-9]Cx%7[0-9]C", width, height) == 2) {
+                /* chars */
+                qemu_opt_set(opts, "cols", width, &error_abort);
+                qemu_opt_set(opts, "rows", height, &error_abort);
+            } else {
+                goto fail;
+            }
+        }
+        return opts;
+    }
+    if (strcmp(filename, "con:") == 0) {
+        qemu_opt_set(opts, "backend", "console", &error_abort);
+        return opts;
+    }
+    if (strstart(filename, "COM", NULL)) {
+        qemu_opt_set(opts, "backend", "serial", &error_abort);
+        qemu_opt_set(opts, "path", filename, &error_abort);
+        return opts;
+    }
+    if (strstart(filename, "file:", &p)) {
+        qemu_opt_set(opts, "backend", "file", &error_abort);
+        qemu_opt_set(opts, "path", p, &error_abort);
+        return opts;
+    }
+    if (strstart(filename, "pipe:", &p)) {
+        qemu_opt_set(opts, "backend", "pipe", &error_abort);
+        qemu_opt_set(opts, "path", p, &error_abort);
+        return opts;
+    }
+    if (strstart(filename, "tcp:", &p) ||
+        strstart(filename, "telnet:", &p)) {
+        if (sscanf(p, "%64[^:]:%32[^,]%n", host, port, &pos) < 2) {
+            host[0] = 0;
+            if (sscanf(p, ":%32[^,]%n", port, &pos) < 1)
+                goto fail;
+        }
+        qemu_opt_set(opts, "backend", "socket", &error_abort);
+        qemu_opt_set(opts, "host", host, &error_abort);
+        qemu_opt_set(opts, "port", port, &error_abort);
+        if (p[pos] == ',') {
+            qemu_opts_do_parse(opts, p+pos+1, NULL, &local_err);
+            if (local_err) {
+                error_report_err(local_err);
+                goto fail;
+            }
+        }
+        if (strstart(filename, "telnet:", &p))
+            qemu_opt_set(opts, "telnet", "on", &error_abort);
+        return opts;
+    }
+    if (strstart(filename, "udp:", &p)) {
+        qemu_opt_set(opts, "backend", "udp", &error_abort);
+        if (sscanf(p, "%64[^:]:%32[^@,]%n", host, port, &pos) < 2) {
+            host[0] = 0;
+            if (sscanf(p, ":%32[^@,]%n", port, &pos) < 1) {
+                goto fail;
+            }
+        }
+        qemu_opt_set(opts, "host", host, &error_abort);
+        qemu_opt_set(opts, "port", port, &error_abort);
+        if (p[pos] == '@') {
+            p += pos + 1;
+            if (sscanf(p, "%64[^:]:%32[^,]%n", host, port, &pos) < 2) {
+                host[0] = 0;
+                if (sscanf(p, ":%32[^,]%n", port, &pos) < 1) {
+                    goto fail;
+                }
+            }
+            qemu_opt_set(opts, "localaddr", host, &error_abort);
+            qemu_opt_set(opts, "localport", port, &error_abort);
+        }
+        return opts;
+    }
+    if (strstart(filename, "unix:", &p)) {
+        qemu_opt_set(opts, "backend", "socket", &error_abort);
+        qemu_opts_do_parse(opts, p, "path", &local_err);
+        if (local_err) {
+            error_report_err(local_err);
+            goto fail;
+        }
+        return opts;
+    }
+    if (strstart(filename, "/dev/parport", NULL) ||
+        strstart(filename, "/dev/ppi", NULL)) {
+        qemu_opt_set(opts, "backend", "parport", &error_abort);
+        qemu_opt_set(opts, "path", filename, &error_abort);
+        return opts;
+    }
+    if (strstart(filename, "/dev/", NULL)) {
+        qemu_opt_set(opts, "backend", "tty", &error_abort);
+        qemu_opt_set(opts, "path", filename, &error_abort);
+        return opts;
+    }
+
+fail:
+    qemu_opts_del(opts);
+    return NULL;
+}
+
+void qemu_chr_parse_common(QemuOpts *opts, ChardevCommon *backend)
+{
+    const char *logfile = qemu_opt_get(opts, "logfile");
+
+    backend->has_logfile = logfile != NULL;
+    backend->logfile = logfile ? g_strdup(logfile) : NULL;
+
+    backend->has_logappend = true;
+    backend->logappend = qemu_opt_get_bool(opts, "logappend", false);
+}
+
+static const ChardevClass *char_get_class(const char *driver, Error **errp)
+{
+    ObjectClass *oc;
+    const ChardevClass *cc;
+    char *typename = g_strdup_printf("chardev-%s", driver);
+
+    oc = object_class_by_name(typename);
+    g_free(typename);
+
+    if (!object_class_dynamic_cast(oc, TYPE_CHARDEV)) {
+        error_setg(errp, "'%s' is not a valid char driver name", driver);
+        return NULL;
+    }
+
+    if (object_class_is_abstract(oc)) {
+        error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "driver",
+                   "abstract device type");
+        return NULL;
+    }
+
+    cc = CHARDEV_CLASS(oc);
+    if (cc->internal) {
+        error_setg(errp, "'%s' is not a valid char driver name", driver);
+        return NULL;
+    }
+
+    return cc;
+}
+
+static Chardev *qemu_chardev_add(const char *id, const char *typename,
+                                 ChardevBackend *backend, Error **errp)
+{
+    Chardev *chr;
+
+    chr = qemu_chr_find(id);
+    if (chr) {
+        error_setg(errp, "Chardev '%s' already exists", id);
+        return NULL;
+    }
+
+    chr = qemu_chardev_new(id, typename, backend, errp);
+    if (!chr) {
+        return NULL;
+    }
+
+    QTAILQ_INSERT_TAIL(&chardevs, chr, next);
+    return chr;
+}
+
+static const struct ChardevAlias {
+    const char *typename;
+    const char *alias;
+} chardev_alias_table[] = {
+#ifdef HAVE_CHARDEV_PARPORT
+    { "parallel", "parport" },
+#endif
+#ifdef HAVE_CHARDEV_SERIAL
+    { "serial", "tty" },
+#endif
+};
+
+typedef struct ChadevClassFE {
+    void (*fn)(const char *name, void *opaque);
+    void *opaque;
+} ChadevClassFE;
+
+static void
+chardev_class_foreach(ObjectClass *klass, void *opaque)
+{
+    ChadevClassFE *fe = opaque;
+
+    assert(g_str_has_prefix(object_class_get_name(klass), "chardev-"));
+    if (CHARDEV_CLASS(klass)->internal) {
+        return;
+    }
+
+    fe->fn(object_class_get_name(klass) + 8, fe->opaque);
+}
+
+static void
+chardev_name_foreach(void (*fn)(const char *name, void *opaque), void *opaque)
+{
+    ChadevClassFE fe = { .fn = fn, .opaque = opaque };
+    int i;
+
+    object_class_foreach(chardev_class_foreach, TYPE_CHARDEV, false, &fe);
+
+    for (i = 0; i < ARRAY_SIZE(chardev_alias_table); i++) {
+        fn(chardev_alias_table[i].alias, opaque);
+    }
+}
+
+static void
+help_string_append(const char *name, void *opaque)
+{
+    GString *str = opaque;
+
+    g_string_append_printf(str, "\n%s", name);
+}
+
+Chardev *qemu_chr_new_from_opts(QemuOpts *opts,
+                                Error **errp)
+{
+    Error *local_err = NULL;
+    const ChardevClass *cc;
+    Chardev *chr;
+    int i;
+    ChardevBackend *backend = NULL;
+    const char *name = qemu_opt_get(opts, "backend");
+    const char *id = qemu_opts_id(opts);
+    char *bid = NULL;
+
+    if (name == NULL) {
+        error_setg(errp, "chardev: \"%s\" missing backend",
+                   qemu_opts_id(opts));
+        return NULL;
+    }
+
+    if (is_help_option(name)) {
+        GString *str = g_string_new("");
+
+        chardev_name_foreach(help_string_append, str);
+
+        error_report("Available chardev backend types: %s", str->str);
+        g_string_free(str, true);
+        exit(0);
+    }
+
+    if (id == NULL) {
+        error_setg(errp, "chardev: no id specified");
+        return NULL;
+    }
+
+    for (i = 0; i < ARRAY_SIZE(chardev_alias_table); i++) {
+        if (g_strcmp0(chardev_alias_table[i].alias, name) == 0) {
+            name = chardev_alias_table[i].typename;
+            break;
+        }
+    }
+
+    cc = char_get_class(name, errp);
+    if (cc == NULL) {
+        return NULL;
+    }
+
+    backend = g_new0(ChardevBackend, 1);
+    backend->type = CHARDEV_BACKEND_KIND_NULL;
+
+    if (qemu_opt_get_bool(opts, "mux", 0)) {
+        bid = g_strdup_printf("%s-base", id);
+    }
+
+    chr = NULL;
+    if (cc->parse) {
+        cc->parse(opts, backend, &local_err);
+        if (local_err) {
+            error_propagate(errp, local_err);
+            goto out;
+        }
+    } else {
+        ChardevCommon *ccom = g_new0(ChardevCommon, 1);
+        qemu_chr_parse_common(opts, ccom);
+        backend->u.null.data = ccom; /* Any ChardevCommon member would work */
+    }
+
+    chr = qemu_chardev_add(bid ? bid : id,
+                           object_class_get_name(OBJECT_CLASS(cc)),
+                           backend, errp);
+    if (chr == NULL) {
+        goto out;
+    }
+
+    if (bid) {
+        Chardev *mux;
+        qapi_free_ChardevBackend(backend);
+        backend = g_new0(ChardevBackend, 1);
+        backend->type = CHARDEV_BACKEND_KIND_MUX;
+        backend->u.mux.data = g_new0(ChardevMux, 1);
+        backend->u.mux.data->chardev = g_strdup(bid);
+        mux = qemu_chardev_add(id, TYPE_CHARDEV_MUX, backend, errp);
+        if (mux == NULL) {
+            qemu_chr_delete(chr);
+            chr = NULL;
+            goto out;
+        }
+        chr = mux;
+    }
+
+out:
+    qapi_free_ChardevBackend(backend);
+    g_free(bid);
+    return chr;
+}
+
+Chardev *qemu_chr_new_noreplay(const char *label, const char *filename)
+{
+    const char *p;
+    Chardev *chr;
+    QemuOpts *opts;
+    Error *err = NULL;
+
+    if (strstart(filename, "chardev:", &p)) {
+        return qemu_chr_find(p);
+    }
+
+    opts = qemu_chr_parse_compat(label, filename);
+    if (!opts)
+        return NULL;
+
+    chr = qemu_chr_new_from_opts(opts, &err);
+    if (err) {
+        error_report_err(err);
+    }
+    if (chr && qemu_opt_get_bool(opts, "mux", 0)) {
+        monitor_init(chr, MONITOR_USE_READLINE);
+    }
+    qemu_opts_del(opts);
+    return chr;
+}
+
+Chardev *qemu_chr_new(const char *label, const char *filename)
+{
+    Chardev *chr;
+    chr = qemu_chr_new_noreplay(label, filename);
+    if (chr) {
+        if (replay_mode != REPLAY_MODE_NONE) {
+            qemu_chr_set_feature(chr, QEMU_CHAR_FEATURE_REPLAY);
+        }
+        if (qemu_chr_replay(chr) && CHARDEV_GET_CLASS(chr)->chr_ioctl) {
+            error_report("Replay: ioctl is not supported "
+                         "for serial devices yet");
+        }
+        replay_register_char_driver(chr);
+    }
+    return chr;
+}
+
+void qemu_chr_fe_set_echo(CharBackend *be, bool echo)
+{
+    Chardev *chr = be->chr;
+
+    if (chr && CHARDEV_GET_CLASS(chr)->chr_set_echo) {
+        CHARDEV_GET_CLASS(chr)->chr_set_echo(chr, echo);
+    }
+}
+
+void qemu_chr_fe_set_open(CharBackend *be, int fe_open)
+{
+    Chardev *chr = be->chr;
+
+    if (!chr) {
+        return;
+    }
+
+    if (be->fe_open == fe_open) {
+        return;
+    }
+    be->fe_open = fe_open;
+    if (CHARDEV_GET_CLASS(chr)->chr_set_fe_open) {
+        CHARDEV_GET_CLASS(chr)->chr_set_fe_open(chr, fe_open);
+    }
+}
+
+guint qemu_chr_fe_add_watch(CharBackend *be, GIOCondition cond,
+                            GIOFunc func, void *user_data)
+{
+    Chardev *s = be->chr;
+    GSource *src;
+    guint tag;
+
+    if (!s || CHARDEV_GET_CLASS(s)->chr_add_watch == NULL) {
+        return 0;
+    }
+
+    src = CHARDEV_GET_CLASS(s)->chr_add_watch(s, cond);
+    if (!src) {
+        return 0;
+    }
+
+    g_source_set_callback(src, (GSourceFunc)func, user_data, NULL);
+    tag = g_source_attach(src, NULL);
+    g_source_unref(src);
+
+    return tag;
+}
+
+void qemu_chr_fe_disconnect(CharBackend *be)
+{
+    Chardev *chr = be->chr;
+
+    if (chr && CHARDEV_GET_CLASS(chr)->chr_disconnect) {
+        CHARDEV_GET_CLASS(chr)->chr_disconnect(chr);
+    }
+}
+
+void qemu_chr_delete(Chardev *chr)
+{
+    QTAILQ_REMOVE(&chardevs, chr, next);
+    object_unref(OBJECT(chr));
+}
+
+ChardevInfoList *qmp_query_chardev(Error **errp)
+{
+    ChardevInfoList *chr_list = NULL;
+    Chardev *chr;
+
+    QTAILQ_FOREACH(chr, &chardevs, next) {
+        ChardevInfoList *info = g_malloc0(sizeof(*info));
+        info->value = g_malloc0(sizeof(*info->value));
+        info->value->label = g_strdup(chr->label);
+        info->value->filename = g_strdup(chr->filename);
+        info->value->frontend_open = chr->be && chr->be->fe_open;
+
+        info->next = chr_list;
+        chr_list = info;
+    }
+
+    return chr_list;
+}
+
+static void
+qmp_prepend_backend(const char *name, void *opaque)
+{
+    ChardevBackendInfoList **list = opaque;
+    ChardevBackendInfoList *info = g_malloc0(sizeof(*info));
+
+    info->value = g_malloc0(sizeof(*info->value));
+    info->value->name = g_strdup(name);
+    info->next = *list;
+    *list = info;
+}
+
+ChardevBackendInfoList *qmp_query_chardev_backends(Error **errp)
+{
+    ChardevBackendInfoList *backend_list = NULL;
+
+    chardev_name_foreach(qmp_prepend_backend, &backend_list);
+
+    return backend_list;
+}
+
+Chardev *qemu_chr_find(const char *name)
+{
+    Chardev *chr;
+
+    QTAILQ_FOREACH(chr, &chardevs, next) {
+        if (strcmp(chr->label, name) != 0)
+            continue;
+        return chr;
+    }
+    return NULL;
+}
+
+QemuOptsList qemu_chardev_opts = {
+    .name = "chardev",
+    .implied_opt_name = "backend",
+    .head = QTAILQ_HEAD_INITIALIZER(qemu_chardev_opts.head),
+    .desc = {
+        {
+            .name = "backend",
+            .type = QEMU_OPT_STRING,
+        },{
+            .name = "path",
+            .type = QEMU_OPT_STRING,
+        },{
+            .name = "host",
+            .type = QEMU_OPT_STRING,
+        },{
+            .name = "port",
+            .type = QEMU_OPT_STRING,
+        },{
+            .name = "localaddr",
+            .type = QEMU_OPT_STRING,
+        },{
+            .name = "localport",
+            .type = QEMU_OPT_STRING,
+        },{
+            .name = "to",
+            .type = QEMU_OPT_NUMBER,
+        },{
+            .name = "ipv4",
+            .type = QEMU_OPT_BOOL,
+        },{
+            .name = "ipv6",
+            .type = QEMU_OPT_BOOL,
+        },{
+            .name = "wait",
+            .type = QEMU_OPT_BOOL,
+        },{
+            .name = "server",
+            .type = QEMU_OPT_BOOL,
+        },{
+            .name = "delay",
+            .type = QEMU_OPT_BOOL,
+        },{
+            .name = "reconnect",
+            .type = QEMU_OPT_NUMBER,
+        },{
+            .name = "telnet",
+            .type = QEMU_OPT_BOOL,
+        },{
+            .name = "tls-creds",
+            .type = QEMU_OPT_STRING,
+        },{
+            .name = "width",
+            .type = QEMU_OPT_NUMBER,
+        },{
+            .name = "height",
+            .type = QEMU_OPT_NUMBER,
+        },{
+            .name = "cols",
+            .type = QEMU_OPT_NUMBER,
+        },{
+            .name = "rows",
+            .type = QEMU_OPT_NUMBER,
+        },{
+            .name = "mux",
+            .type = QEMU_OPT_BOOL,
+        },{
+            .name = "signal",
+            .type = QEMU_OPT_BOOL,
+        },{
+            .name = "name",
+            .type = QEMU_OPT_STRING,
+        },{
+            .name = "debug",
+            .type = QEMU_OPT_NUMBER,
+        },{
+            .name = "size",
+            .type = QEMU_OPT_SIZE,
+        },{
+            .name = "chardev",
+            .type = QEMU_OPT_STRING,
+        },{
+            .name = "append",
+            .type = QEMU_OPT_BOOL,
+        },{
+            .name = "logfile",
+            .type = QEMU_OPT_STRING,
+        },{
+            .name = "logappend",
+            .type = QEMU_OPT_BOOL,
+        },
+        { /* end of list */ }
+    },
+};
+
+bool qemu_chr_has_feature(Chardev *chr,
+                          ChardevFeature feature)
+{
+    return test_bit(feature, chr->features);
+}
+
+void qemu_chr_set_feature(Chardev *chr,
+                           ChardevFeature feature)
+{
+    return set_bit(feature, chr->features);
+}
+
+Chardev *qemu_chardev_new(const char *id, const char *typename,
+                          ChardevBackend *backend, Error **errp)
+{
+    Chardev *chr = NULL;
+    Error *local_err = NULL;
+    bool be_opened = true;
+
+    assert(g_str_has_prefix(typename, "chardev-"));
+
+    chr = CHARDEV(object_new(typename));
+    chr->label = g_strdup(id);
+
+    qemu_char_open(chr, backend, &be_opened, &local_err);
+    if (local_err) {
+        error_propagate(errp, local_err);
+        object_unref(OBJECT(chr));
+        return NULL;
+    }
+
+    if (!chr->filename) {
+        chr->filename = g_strdup(typename + 8);
+    }
+    if (be_opened) {
+        qemu_chr_be_event(chr, CHR_EVENT_OPENED);
+    }
+
+    return chr;
+}
+
+ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend,
+                               Error **errp)
+{
+    const ChardevClass *cc;
+    ChardevReturn *ret;
+    Chardev *chr;
+
+    cc = char_get_class(ChardevBackendKind_lookup[backend->type], errp);
+    if (!cc) {
+        return NULL;
+    }
+
+    chr = qemu_chardev_add(id, object_class_get_name(OBJECT_CLASS(cc)),
+                           backend, errp);
+    if (!chr) {
+        return NULL;
+    }
+
+    ret = g_new0(ChardevReturn, 1);
+    if (CHARDEV_IS_PTY(chr)) {
+        ret->pty = g_strdup(chr->filename + 4);
+        ret->has_pty = true;
+    }
+
+    return ret;
+}
+
+void qmp_chardev_remove(const char *id, Error **errp)
+{
+    Chardev *chr;
+
+    chr = qemu_chr_find(id);
+    if (chr == NULL) {
+        error_setg(errp, "Chardev '%s' not found", id);
+        return;
+    }
+    if (qemu_chr_is_busy(chr)) {
+        error_setg(errp, "Chardev '%s' is busy", id);
+        return;
+    }
+    if (qemu_chr_replay(chr)) {
+        error_setg(errp,
+            "Chardev '%s' cannot be unplugged in record/replay mode", id);
+        return;
+    }
+    qemu_chr_delete(chr);
+}
+
+void qemu_chr_cleanup(void)
+{
+    Chardev *chr, *tmp;
+
+    QTAILQ_FOREACH_SAFE(chr, &chardevs, next, tmp) {
+        qemu_chr_delete(chr);
+    }
+}
+
+static void register_types(void)
+{
+    type_register_static(&char_type_info);
+
+    /* this must be done after machine init, since we register FEs with muxes
+     * as part of realize functions like serial_isa_realizefn when -nographic
+     * is specified
+     */
+    qemu_add_machine_init_done_notifier(&muxes_realize_notify);
+}
+
+type_init(register_types);
diff --git a/cpu-exec.c b/cpu-exec.c
index 4188fed3c6..57583f16a0 100644
--- a/cpu-exec.c
+++ b/cpu-exec.c
@@ -18,7 +18,7 @@
  */
 #include "qemu/osdep.h"
 #include "cpu.h"
-#include "trace.h"
+#include "trace-root.h"
 #include "disas/disas.h"
 #include "exec/exec-all.h"
 #include "tcg.h"
@@ -508,8 +508,8 @@ static inline void cpu_handle_interrupt(CPUState *cpu,
            True when it is, and we should restart on a new TB,
            and via longjmp via cpu_loop_exit.  */
         else {
-            replay_interrupt();
             if (cc->cpu_exec_interrupt(cpu, interrupt_request)) {
+                replay_interrupt();
                 *last_tb = NULL;
             }
             /* The target hook may have updated the 'cpu->interrupt_request';
diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak
index 6de3e16a3e..6f2a180985 100644
--- a/default-configs/arm-softmmu.mak
+++ b/default-configs/arm-softmmu.mak
@@ -108,6 +108,7 @@ CONFIG_FSL_IMX25=y
 
 CONFIG_IMX_I2C=y
 
+CONFIG_PCIE_PORT=y
 CONFIG_XIO3130=y
 CONFIG_IOH3420=y
 CONFIG_I82801B11=y
diff --git a/default-configs/i386-softmmu.mak b/default-configs/i386-softmmu.mak
index 0b513602c8..384cefb612 100644
--- a/default-configs/i386-softmmu.mak
+++ b/default-configs/i386-softmmu.mak
@@ -51,8 +51,10 @@ CONFIG_PVPANIC=y
 CONFIG_MEM_HOTPLUG=y
 CONFIG_NVDIMM=y
 CONFIG_ACPI_NVDIMM=y
+CONFIG_PCIE_PORT=y
 CONFIG_XIO3130=y
 CONFIG_IOH3420=y
 CONFIG_I82801B11=y
 CONFIG_SMBIOS=y
 CONFIG_HYPERV_TESTDEV=$(CONFIG_KVM)
+CONFIG_PXB=y
diff --git a/default-configs/ppc-softmmu.mak b/default-configs/ppc-softmmu.mak
index d4d0f9b192..7dd004e36e 100644
--- a/default-configs/ppc-softmmu.mak
+++ b/default-configs/ppc-softmmu.mak
@@ -18,6 +18,7 @@ CONFIG_I82378=y
 CONFIG_PC87312=y
 CONFIG_MACIO=y
 CONFIG_PCSPK=y
+CONFIG_CS4231A=y
 CONFIG_CUDA=y
 CONFIG_ADB=y
 CONFIG_MAC_NVRAM=y
@@ -47,3 +48,4 @@ CONFIG_LIBDECNUMBER=y
 # For PReP
 CONFIG_MC146818RTC=y
 CONFIG_ISA_TESTDEV=y
+CONFIG_RS6000_MC=y
diff --git a/default-configs/ppc64-softmmu.mak b/default-configs/ppc64-softmmu.mak
index 67a9bcaa67..9ae6563c82 100644
--- a/default-configs/ppc64-softmmu.mak
+++ b/default-configs/ppc64-softmmu.mak
@@ -55,3 +55,4 @@ CONFIG_XICS_KVM=$(and $(CONFIG_PSERIES),$(CONFIG_KVM))
 CONFIG_MC146818RTC=y
 CONFIG_ISA_TESTDEV=y
 CONFIG_MEM_HOTPLUG=y
+CONFIG_RS6000_MC=y
diff --git a/default-configs/x86_64-softmmu.mak b/default-configs/x86_64-softmmu.mak
index 7f89503f95..491a191b9d 100644
--- a/default-configs/x86_64-softmmu.mak
+++ b/default-configs/x86_64-softmmu.mak
@@ -51,8 +51,10 @@ CONFIG_PVPANIC=y
 CONFIG_MEM_HOTPLUG=y
 CONFIG_NVDIMM=y
 CONFIG_ACPI_NVDIMM=y
+CONFIG_PCIE_PORT=y
 CONFIG_XIO3130=y
 CONFIG_IOH3420=y
 CONFIG_I82801B11=y
 CONFIG_SMBIOS=y
 CONFIG_HYPERV_TESTDEV=$(CONFIG_KVM)
+CONFIG_PXB=y
diff --git a/disas/ppc.c b/disas/ppc.c
index ed7e0d0b9c..5ab9c35a84 100644
--- a/disas/ppc.c
+++ b/disas/ppc.c
@@ -1653,11 +1653,11 @@ extract_tbr (unsigned long insn,
 #define BBOYBI_MASK (BBOYCB_MASK | BI_MASK)
 #define BBOATBI_MASK (BBOAT2CB_MASK | BI_MASK)
 
-/* An Context form instruction.  */
+/* A Context form instruction.  */
 #define CTX(op, xop)   (OP (op) | (((unsigned long)(xop)) & 0x7))
 #define CTX_MASK CTX(0x3f, 0x7)
 
-/* An User Context form instruction.  */
+/* A User Context form instruction.  */
 #define UCTX(op, xop)  (OP (op) | (((unsigned long)(xop)) & 0x1f))
 #define UCTX_MASK UCTX(0x3f, 0x1f)
 
@@ -1710,19 +1710,19 @@ extract_tbr (unsigned long insn,
 #define SC(op, sa, lk) (OP (op) | ((((unsigned long)(sa)) & 1) << 1) | ((lk) & 1))
 #define SC_MASK (OP_MASK | (((unsigned long)0x3ff) << 16) | (((unsigned long)1) << 1) | 1)
 
-/* An VX form instruction.  */
+/* A VX form instruction.  */
 #define VX(op, xop) (OP (op) | (((unsigned long)(xop)) & 0x7ff))
 
 /* The mask for an VX form instruction.  */
 #define VX_MASK	VX(0x3f, 0x7ff)
 
-/* An VA form instruction.  */
+/* A VA form instruction.  */
 #define VXA(op, xop) (OP (op) | (((unsigned long)(xop)) & 0x03f))
 
-/* The mask for an VA form instruction.  */
+/* The mask for a VA form instruction.  */
 #define VXA_MASK VXA(0x3f, 0x3f)
 
-/* An VXR form instruction.  */
+/* A VXR form instruction.  */
 #define VXR(op, xop, rc) (OP (op) | (((rc) & 1) << 10) | (((unsigned long)(xop)) & 0x3ff))
 
 /* The mask for a VXR form instruction.  */
diff --git a/dma-helpers.c b/dma-helpers.c
index 6f9d47ca50..97157cc2ec 100644
--- a/dma-helpers.c
+++ b/dma-helpers.c
@@ -10,7 +10,7 @@
 #include "qemu/osdep.h"
 #include "sysemu/block-backend.h"
 #include "sysemu/dma.h"
-#include "trace.h"
+#include "trace-root.h"
 #include "qemu/thread.h"
 #include "qemu/main-loop.h"
 
diff --git a/docs/replay.txt b/docs/replay.txt
index 347b2ff055..03e193193f 100644
--- a/docs/replay.txt
+++ b/docs/replay.txt
@@ -196,6 +196,22 @@ is recorded to the log. In replay phase the queue is matched with
 events read from the log. Therefore block devices requests are processed
 deterministically.
 
+Snapshotting
+------------
+
+New VM snapshots may be created in replay mode. They can be used later
+to recover the desired VM state. All VM states created in replay mode
+are associated with the moment of time in the replay scenario.
+After recovering the VM state replay will start from that position.
+
+Default starting snapshot name may be specified with icount field
+rrsnapshot as follows:
+ -icount shift=7,rr=record,rrfile=replay.bin,rrsnapshot=snapshot_name
+
+This snapshot is created at start of recording and restored at start
+of replaying. It also can be loaded while replaying to roll back
+the execution.
+
 Network devices
 ---------------
 
diff --git a/docs/tracing.txt b/docs/tracing.txt
index f351998a4e..e14bb6dccc 100644
--- a/docs/tracing.txt
+++ b/docs/tracing.txt
@@ -27,18 +27,44 @@ for debugging, profiling, and observing execution.
 
 == Trace events ==
 
-Each directory in the source tree can declare a set of static trace events
-in a "trace-events" file. Each trace event declaration names the event, its
-arguments, and the format string which can be used for pretty-printing:
+=== Sub-directory setup ===
 
-    qemu_vmalloc(size_t size, void *ptr) "size %zu ptr %p"
-    qemu_vfree(void *ptr) "ptr %p"
-
-All "trace-events" files must be listed in the "trace-event-y" make variable
-in the top level Makefile.objs. During build the individual files are combined
-to create a "trace-events-all" file, which is processed by the "tracetool"
-script during build to generate code for the trace events. The
-"trace-events-all" file is also installed into "/usr/share/qemu".
+Each directory in the source tree can declare a set of static trace events
+in a local "trace-events" file. All directories which contain "trace-events"
+files must be listed in the "trace-events-subdirs" make variable in the top
+level Makefile.objs. During build, the "trace-events" file in each listed
+subdirectory will be processed by the "tracetool" script to generate code for
+the trace events.
+
+The individual "trace-events" files are merged into a "trace-events-all" file,
+which is also installed into "/usr/share/qemu" with the name "trace-events".
+This merged file is to be used by the "simpletrace.py" script to later analyse
+traces in the simpletrace data format.
+
+In the sub-directory the following files will be automatically generated
+
+ - trace.c - the trace event state declarations
+ - trace.h - the trace event enums and probe functions
+ - trace-dtrace.h - DTrace event probe specification
+ - trace-dtrace.dtrace - DTrace event probe helper declaration
+ - trace-dtrace.o - binary DTrace provider (generated by dtrace)
+ - trace-ust.h - UST event probe helper declarations
+
+Source files in the sub-directory should #include the local 'trace.h' file,
+without any sub-directory path prefix. eg io/channel-buffer.c would do
+
+  #include "trace.h"
+
+To access the 'io/trace.h' file. While it is possible to include a trace.h
+file from outside a source files' own sub-directory, this is discouraged in
+general. It is strongly preferred that all events be declared directly in
+the sub-directory that uses them. The only exception is where there are some
+shared trace events defined in the top level directory trace-events file.
+The top level directory generates trace files with a filename prefix of
+"trace-root" instead of just "trace". This is to avoid ambiguity between
+a trace.h in the current directory, vs the top level directory.
+
+=== Using trace events ===
 
 Trace events are invoked directly from source code like this:
 
@@ -83,6 +109,13 @@ Format strings should reflect the types defined in the trace event.  Take
 special care to use PRId64 and PRIu64 for int64_t and uint64_t types,
 respectively.  This ensures portability between 32- and 64-bit platforms.
 
+Each event declaration will start with the event name, then its arguments,
+finally a format string for pretty-printing. For example:
+
+    qemu_vmalloc(size_t size, void *ptr) "size %zu ptr %p"
+    qemu_vfree(void *ptr) "ptr %p"
+
+
 === Hints for adding new trace events ===
 
 1. Trace state changes in the code.  Interesting points in the code usually
diff --git a/exec.c b/exec.c
index f2bed92b64..8b9ed73b15 100644
--- a/exec.c
+++ b/exec.c
@@ -44,7 +44,7 @@
 #include "sysemu/dma.h"
 #include "exec/address-spaces.h"
 #include "sysemu/xen-mapcache.h"
-#include "trace.h"
+#include "trace-root.h"
 #endif
 #include "exec/cpu-all.h"
 #include "qemu/rcu_queue.h"
@@ -2630,7 +2630,7 @@ static MemTxResult address_space_write_continue(AddressSpace *as, hwaddr addr,
                 break;
             case 4:
                 /* 32 bit write access */
-                val = ldl_p(buf);
+                val = (uint32_t)ldl_p(buf);
                 result |= memory_region_dispatch_write(mr, addr1, val, 4,
                                                        attrs);
                 break;
diff --git a/fpu/softfloat-specialize.h b/fpu/softfloat-specialize.h
index f05c8658c1..100c8a98bf 100644
--- a/fpu/softfloat-specialize.h
+++ b/fpu/softfloat-specialize.h
@@ -185,7 +185,7 @@ float128 float128_default_nan(float_status *status)
         r.high = LIT64(0x7FFF7FFFFFFFFFFF);
     } else {
         r.low = LIT64(0x0000000000000000);
-#if defined(TARGET_S390X)
+#if defined(TARGET_S390X) || defined(TARGET_PPC)
         r.high = LIT64(0x7FFF800000000000);
 #else
         r.high = LIT64(0xFFFF800000000000);
diff --git a/gdbstub.c b/gdbstub.c
index 2d18ed73be..755a8e378d 100644
--- a/gdbstub.c
+++ b/gdbstub.c
@@ -305,7 +305,7 @@ typedef struct GDBState {
     int running_state;
 #else
     CharBackend chr;
-    CharDriverState *mon_chr;
+    Chardev *mon_chr;
 #endif
     char syscall_buf[256];
     gdb_syscall_complete_cb current_syscall_cb;
@@ -1473,7 +1473,7 @@ void gdb_exit(CPUArchState *env, int code)
   GDBState *s;
   char buf[4];
 #ifndef CONFIG_USER_ONLY
-  CharDriverState *chr;
+  Chardev *chr;
 #endif
 
   s = gdbserver_state;
@@ -1698,7 +1698,7 @@ static void gdb_monitor_output(GDBState *s, const char *msg, int len)
     put_packet(s, buf);
 }
 
-static int gdb_monitor_write(CharDriverState *chr, const uint8_t *buf, int len)
+static int gdb_monitor_write(Chardev *chr, const uint8_t *buf, int len)
 {
     const char *p = (const char *)buf;
     int max_sz;
@@ -1725,13 +1725,35 @@ static void gdb_sigterm_handler(int signal)
 }
 #endif
 
+static void gdb_monitor_open(Chardev *chr, ChardevBackend *backend,
+                             bool *be_opened, Error **errp)
+{
+    *be_opened = false;
+}
+
+static void char_gdb_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
+
+    cc->internal = true;
+    cc->open = gdb_monitor_open;
+    cc->chr_write = gdb_monitor_write;
+}
+
+#define TYPE_CHARDEV_GDB "chardev-gdb"
+
+static const TypeInfo char_gdb_type_info = {
+    .name = TYPE_CHARDEV_GDB,
+    .parent = TYPE_CHARDEV,
+    .class_init = char_gdb_class_init,
+};
+
 int gdbserver_start(const char *device)
 {
     GDBState *s;
     char gdbstub_device_name[128];
-    CharDriverState *chr = NULL;
-    CharDriverState *mon_chr;
-    ChardevCommon common = { 0 };
+    Chardev *chr = NULL;
+    Chardev *mon_chr;
 
     if (!first_cpu) {
         error_report("gdbstub: meaningless to attach gdb to a "
@@ -1770,8 +1792,8 @@ int gdbserver_start(const char *device)
         qemu_add_vm_change_state_handler(gdb_vm_state_change, NULL);
 
         /* Initialize a monitor terminal for gdb */
-        mon_chr = qemu_chr_alloc(&common, &error_abort);
-        mon_chr->chr_write = gdb_monitor_write;
+        mon_chr = qemu_chardev_new(NULL, TYPE_CHARDEV_GDB,
+                                   NULL, &error_abort);
         monitor_init(mon_chr, 0);
     } else {
         if (qemu_chr_fe_get_driver(&s->chr)) {
@@ -1794,4 +1816,11 @@ int gdbserver_start(const char *device)
 
     return 0;
 }
+
+static void register_types(void)
+{
+    type_register_static(&char_gdb_type_info);
+}
+
+type_init(register_types);
 #endif
diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx
index 55d50c42c6..b0f35e6829 100644
--- a/hmp-commands-info.hx
+++ b/hmp-commands-info.hx
@@ -249,9 +249,9 @@ ETEXI
 
     {
         .name       = "mtree",
-        .args_type  = "",
-        .params     = "",
-        .help       = "show memory tree",
+        .args_type  = "flatview:-f",
+        .params     = "[-f]",
+        .help       = "show memory tree (-f: dump flat view for address spaces)",
         .cmd        = hmp_info_mtree,
     },
 
diff --git a/hmp.c b/hmp.c
index 8522efea26..2bc4f062bb 100644
--- a/hmp.c
+++ b/hmp.c
@@ -19,6 +19,7 @@
 #include "net/eth.h"
 #include "sysemu/char.h"
 #include "sysemu/block-backend.h"
+#include "qemu/config-file.h"
 #include "qemu/option.h"
 #include "qemu/timer.h"
 #include "qmp-commands.h"
diff --git a/hw/arm/fsl-imx25.c b/hw/arm/fsl-imx25.c
index 7bb7be76b6..2126f73ca0 100644
--- a/hw/arm/fsl-imx25.c
+++ b/hw/arm/fsl-imx25.c
@@ -118,7 +118,7 @@ static void fsl_imx25_realize(DeviceState *dev, Error **errp)
         };
 
         if (i < MAX_SERIAL_PORTS) {
-            CharDriverState *chr;
+            Chardev *chr;
 
             chr = serial_hds[i];
 
diff --git a/hw/arm/fsl-imx31.c b/hw/arm/fsl-imx31.c
index f23672b222..dd1c713ae3 100644
--- a/hw/arm/fsl-imx31.c
+++ b/hw/arm/fsl-imx31.c
@@ -107,7 +107,7 @@ static void fsl_imx31_realize(DeviceState *dev, Error **errp)
         };
 
         if (i < MAX_SERIAL_PORTS) {
-            CharDriverState *chr;
+            Chardev *chr;
 
             chr = serial_hds[i];
 
diff --git a/hw/arm/fsl-imx6.c b/hw/arm/fsl-imx6.c
index e93532fb57..76dd8a48ca 100644
--- a/hw/arm/fsl-imx6.c
+++ b/hw/arm/fsl-imx6.c
@@ -187,7 +187,7 @@ static void fsl_imx6_realize(DeviceState *dev, Error **errp)
         };
 
         if (i < MAX_SERIAL_PORTS) {
-            CharDriverState *chr;
+            Chardev *chr;
 
             chr = serial_hds[i];
 
diff --git a/hw/arm/nseries.c b/hw/arm/nseries.c
index c86cf80514..503a3b6d24 100644
--- a/hw/arm/nseries.c
+++ b/hw/arm/nseries.c
@@ -786,7 +786,7 @@ static void n8x0_cbus_setup(struct n800_s *s)
 
 static void n8x0_uart_setup(struct n800_s *s)
 {
-    CharDriverState *radio = uart_hci_init();
+    Chardev *radio = uart_hci_init();
 
     qdev_connect_gpio_out(s->mpu->gpio, N8X0_BT_RESET_GPIO,
                     csrhci_pins_get(radio)[csrhci_pin_reset]);
diff --git a/hw/arm/omap2.c b/hw/arm/omap2.c
index 6f05c98d3e..cf1b4ba58f 100644
--- a/hw/arm/omap2.c
+++ b/hw/arm/omap2.c
@@ -792,7 +792,7 @@ static const MemoryRegionOps omap_sti_fifo_ops = {
 static struct omap_sti_s *omap_sti_init(struct omap_target_agent_s *ta,
                 MemoryRegion *sysmem,
                 hwaddr channel_base, qemu_irq irq, omap_clk clk,
-                CharDriverState *chr)
+                Chardev *chr)
 {
     struct omap_sti_s *s = g_new0(struct omap_sti_s, 1);
 
diff --git a/hw/arm/pxa2xx.c b/hw/arm/pxa2xx.c
index d31b4577f0..cfee3929d9 100644
--- a/hw/arm/pxa2xx.c
+++ b/hw/arm/pxa2xx.c
@@ -2024,7 +2024,7 @@ static PXA2xxFIrState *pxa2xx_fir_init(MemoryRegion *sysmem,
                                        hwaddr base,
                                        qemu_irq irq, qemu_irq rx_dma,
                                        qemu_irq tx_dma,
-                                       CharDriverState *chr)
+                                       Chardev *chr)
 {
     DeviceState *dev;
     SysBusDevice *sbd;
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index 6c9e8985bf..1f216cf3b1 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -613,7 +613,7 @@ static void create_gic(VirtMachineState *vms, qemu_irq *pic)
 }
 
 static void create_uart(const VirtMachineState *vms, qemu_irq *pic, int uart,
-                        MemoryRegion *mem, CharDriverState *chr)
+                        MemoryRegion *mem, Chardev *chr)
 {
     char *nodename;
     hwaddr base = vms->memmap[uart].base;
diff --git a/hw/block/dataplane/trace-events b/hw/block/dataplane/trace-events
new file mode 100644
index 0000000000..e07673ab1f
--- /dev/null
+++ b/hw/block/dataplane/trace-events
@@ -0,0 +1,5 @@
+# See docs/tracing.txt for syntax documentation.
+
+# hw/block/dataplane/virtio-blk.c
+virtio_blk_data_plane_start(void *s) "dataplane %p"
+virtio_blk_data_plane_stop(void *s) "dataplane %p"
diff --git a/hw/block/nvme.c b/hw/block/nvme.c
index d479fd22f5..ae91a18f17 100644
--- a/hw/block/nvme.c
+++ b/hw/block/nvme.c
@@ -872,7 +872,7 @@ static int nvme_init(PCIDevice *pci_dev)
     pci_register_bar(&n->parent_obj, 0,
         PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_64,
         &n->iomem);
-    msix_init_exclusive_bar(&n->parent_obj, n->num_queues, 4);
+    msix_init_exclusive_bar(&n->parent_obj, n->num_queues, 4, NULL);
 
     id->vid = cpu_to_le16(pci_get_word(pci_conf + PCI_VENDOR_ID));
     id->ssvid = cpu_to_le16(pci_get_word(pci_conf + PCI_SUBSYSTEM_VENDOR_ID));
diff --git a/hw/block/trace-events b/hw/block/trace-events
index d0dd94ff05..65e83dc258 100644
--- a/hw/block/trace-events
+++ b/hw/block/trace-events
@@ -7,11 +7,6 @@ virtio_blk_handle_write(void *req, uint64_t sector, size_t nsectors) "req %p sec
 virtio_blk_handle_read(void *req, uint64_t sector, size_t nsectors) "req %p sector %"PRIu64" nsectors %zu"
 virtio_blk_submit_multireq(void *mrb, int start, int num_reqs, uint64_t offset, size_t size, bool is_write) "mrb %p start %d num_reqs %d offset %"PRIu64" size %zu is_write %d"
 
-# hw/block/dataplane/virtio-blk.c
-virtio_blk_data_plane_start(void *s) "dataplane %p"
-virtio_blk_data_plane_stop(void *s) "dataplane %p"
-virtio_blk_data_plane_process_request(void *s, unsigned int out_num, unsigned int in_num, unsigned int head) "dataplane %p out_num %u in_num %u head %u"
-
 # hw/block/hd-geometry.c
 hd_geometry_lchs_guess(void *blk, int cyls, int heads, int secs) "blk %p LCHS %d %d %d"
 hd_geometry_guess(void *blk, uint32_t cyls, uint32_t heads, uint32_t secs, int trans) "blk %p CHS %u %u %u trans %d"
diff --git a/hw/bt/hci-csr.c b/hw/bt/hci-csr.c
index fbb3109cc1..3c193848fc 100644
--- a/hw/bt/hci-csr.c
+++ b/hw/bt/hci-csr.c
@@ -26,13 +26,14 @@
 #include "hw/irq.h"
 #include "sysemu/bt.h"
 #include "hw/bt.h"
+#include "qapi/error.h"
 
 struct csrhci_s {
+    Chardev parent;
     int enable;
     qemu_irq *pins;
     int pin_state;
     int modem_state;
-    CharDriverState chr;
 #define FIFO_LEN	4096
     int out_start;
     int out_len;
@@ -54,6 +55,9 @@ struct csrhci_s {
     struct HCIInfo *hci;
 };
 
+#define TYPE_CHARDEV_HCI "chardev-hci"
+#define HCI_CHARDEV(obj) OBJECT_CHECK(struct csrhci_s, (obj), TYPE_CHARDEV_HCI)
+
 /* H4+ packet types */
 enum {
     H4_CMD_PKT   = 1,
@@ -78,7 +82,8 @@ enum {
 
 static inline void csrhci_fifo_wake(struct csrhci_s *s)
 {
-    CharBackend *be = s->chr.be;
+    Chardev *chr = (Chardev *)s;
+    CharBackend *be = chr->be;
 
     if (!s->enable || !s->out_len)
         return;
@@ -311,10 +316,10 @@ static void csrhci_ready_for_next_inpkt(struct csrhci_s *s)
     s->in_hdr = INT_MAX;
 }
 
-static int csrhci_write(struct CharDriverState *chr,
+static int csrhci_write(struct Chardev *chr,
                 const uint8_t *buf, int len)
 {
-    struct csrhci_s *s = (struct csrhci_s *) chr->opaque;
+    struct csrhci_s *s = (struct csrhci_s *)chr;
     int total = 0;
 
     if (!s->enable)
@@ -384,10 +389,10 @@ static void csrhci_out_hci_packet_acl(void *opaque,
     csrhci_fifo_wake(s);
 }
 
-static int csrhci_ioctl(struct CharDriverState *chr, int cmd, void *arg)
+static int csrhci_ioctl(struct Chardev *chr, int cmd, void *arg)
 {
     QEMUSerialSetParams *ssp;
-    struct csrhci_s *s = (struct csrhci_s *) chr->opaque;
+    struct csrhci_s *s = (struct csrhci_s *) chr;
     int prev_state = s->modem_state;
 
     switch (cmd) {
@@ -453,21 +458,19 @@ static void csrhci_pins(void *opaque, int line, int level)
     }
 }
 
-qemu_irq *csrhci_pins_get(CharDriverState *chr)
+qemu_irq *csrhci_pins_get(Chardev *chr)
 {
-    struct csrhci_s *s = (struct csrhci_s *) chr->opaque;
+    struct csrhci_s *s = (struct csrhci_s *) chr;
 
     return s->pins;
 }
 
-CharDriverState *uart_hci_init(void)
+static void csrhci_open(Chardev *chr,
+                        ChardevBackend *backend,
+                        bool *be_opened,
+                        Error **errp)
 {
-    struct csrhci_s *s = (struct csrhci_s *)
-            g_malloc0(sizeof(struct csrhci_s));
-
-    s->chr.opaque = s;
-    s->chr.chr_write = csrhci_write;
-    s->chr.chr_ioctl = csrhci_ioctl;
+    struct csrhci_s *s = HCI_CHARDEV(chr);
 
     s->hci = qemu_next_hci();
     s->hci->opaque = s;
@@ -477,6 +480,35 @@ CharDriverState *uart_hci_init(void)
     s->out_tm = timer_new_ns(QEMU_CLOCK_VIRTUAL, csrhci_out_tick, s);
     s->pins = qemu_allocate_irqs(csrhci_pins, s, __csrhci_pins);
     csrhci_reset(s);
+    *be_opened = false;
+}
+
+static void char_hci_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
+
+    cc->internal = true;
+    cc->open = csrhci_open;
+    cc->chr_write = csrhci_write;
+    cc->chr_ioctl = csrhci_ioctl;
+}
 
-    return &s->chr;
+static const TypeInfo char_hci_type_info = {
+    .name = TYPE_CHARDEV_HCI,
+    .parent = TYPE_CHARDEV,
+    .instance_size = sizeof(struct csrhci_s),
+    .class_init = char_hci_class_init,
+};
+
+Chardev *uart_hci_init(void)
+{
+    return qemu_chardev_new(NULL, TYPE_CHARDEV_HCI,
+                            NULL, &error_abort);
 }
+
+static void register_types(void)
+{
+    type_register_static(&char_hci_type_info);
+}
+
+type_init(register_types);
diff --git a/hw/char/escc.c b/hw/char/escc.c
index d6662dc77d..9228091cec 100644
--- a/hw/char/escc.c
+++ b/hw/char/escc.c
@@ -689,7 +689,7 @@ static const VMStateDescription vmstate_escc = {
 };
 
 MemoryRegion *escc_init(hwaddr base, qemu_irq irqA, qemu_irq irqB,
-              CharDriverState *chrA, CharDriverState *chrB,
+              Chardev *chrA, Chardev *chrB,
               int clock, int it_shift)
 {
     DeviceState *dev;
diff --git a/hw/char/exynos4210_uart.c b/hw/char/exynos4210_uart.c
index 0cd3dd3958..7c16e894e2 100644
--- a/hw/char/exynos4210_uart.c
+++ b/hw/char/exynos4210_uart.c
@@ -582,7 +582,7 @@ static const VMStateDescription vmstate_exynos4210_uart = {
 DeviceState *exynos4210_uart_create(hwaddr addr,
                                     int fifo_size,
                                     int channel,
-                                    CharDriverState *chr,
+                                    Chardev *chr,
                                     qemu_irq irq)
 {
     DeviceState  *dev;
diff --git a/hw/char/imx_serial.c b/hw/char/imx_serial.c
index 99545fc359..52e67f8dc9 100644
--- a/hw/char/imx_serial.c
+++ b/hw/char/imx_serial.c
@@ -170,7 +170,7 @@ static void imx_serial_write(void *opaque, hwaddr offset,
                              uint64_t value, unsigned size)
 {
     IMXSerialState *s = (IMXSerialState *)opaque;
-    CharDriverState *chr = qemu_chr_fe_get_driver(&s->chr);
+    Chardev *chr = qemu_chr_fe_get_driver(&s->chr);
     unsigned char ch;
 
     DPRINTF("write(offset=0x%" HWADDR_PRIx ", value = 0x%x) to %s\n",
diff --git a/hw/char/mcf_uart.c b/hw/char/mcf_uart.c
index ecaa091190..80c380e077 100644
--- a/hw/char/mcf_uart.c
+++ b/hw/char/mcf_uart.c
@@ -275,7 +275,7 @@ static void mcf_uart_receive(void *opaque, const uint8_t *buf, int size)
     mcf_uart_push_byte(s, buf[0]);
 }
 
-void *mcf_uart_init(qemu_irq irq, CharDriverState *chr)
+void *mcf_uart_init(qemu_irq irq, Chardev *chr)
 {
     mcf_uart_state *s;
 
@@ -300,7 +300,7 @@ static const MemoryRegionOps mcf_uart_ops = {
 void mcf_uart_mm_init(MemoryRegion *sysmem,
                       hwaddr base,
                       qemu_irq irq,
-                      CharDriverState *chr)
+                      Chardev *chr)
 {
     mcf_uart_state *s;
 
diff --git a/hw/char/omap_uart.c b/hw/char/omap_uart.c
index 893ab108bc..31ebb1592c 100644
--- a/hw/char/omap_uart.c
+++ b/hw/char/omap_uart.c
@@ -54,7 +54,7 @@ void omap_uart_reset(struct omap_uart_s *s)
 struct omap_uart_s *omap_uart_init(hwaddr base,
                 qemu_irq irq, omap_clk fclk, omap_clk iclk,
                 qemu_irq txdma, qemu_irq rxdma,
-                const char *label, CharDriverState *chr)
+                const char *label, Chardev *chr)
 {
     struct omap_uart_s *s = g_new0(struct omap_uart_s, 1);
 
@@ -163,7 +163,7 @@ struct omap_uart_s *omap2_uart_init(MemoryRegion *sysmem,
                 struct omap_target_agent_s *ta,
                 qemu_irq irq, omap_clk fclk, omap_clk iclk,
                 qemu_irq txdma, qemu_irq rxdma,
-                const char *label, CharDriverState *chr)
+                const char *label, Chardev *chr)
 {
     hwaddr base = omap_l4_attach(ta, 0, NULL);
     struct omap_uart_s *s = omap_uart_init(base, irq,
@@ -178,7 +178,7 @@ struct omap_uart_s *omap2_uart_init(MemoryRegion *sysmem,
     return s;
 }
 
-void omap_uart_attach(struct omap_uart_s *s, CharDriverState *chr)
+void omap_uart_attach(struct omap_uart_s *s, Chardev *chr)
 {
     /* TODO: Should reuse or destroy current s->serial */
     s->serial = serial_mm_init(get_system_memory(), s->base, 2, s->irq,
diff --git a/hw/char/parallel.c b/hw/char/parallel.c
index f2d56666b7..c71a4a0f4f 100644
--- a/hw/char/parallel.c
+++ b/hw/char/parallel.c
@@ -603,7 +603,7 @@ static const MemoryRegionOps parallel_mm_ops = {
 /* If fd is zero, it means that the parallel device uses the console */
 bool parallel_mm_init(MemoryRegion *address_space,
                       hwaddr base, int it_shift, qemu_irq irq,
-                      CharDriverState *chr)
+                      Chardev *chr)
 {
     ParallelState *s;
 
diff --git a/hw/char/serial-isa.c b/hw/char/serial-isa.c
index 54d3a12f51..d7c5cc11fe 100644
--- a/hw/char/serial-isa.c
+++ b/hw/char/serial-isa.c
@@ -121,7 +121,7 @@ static void serial_register_types(void)
 
 type_init(serial_register_types)
 
-static void serial_isa_init(ISABus *bus, int index, CharDriverState *chr)
+static void serial_isa_init(ISABus *bus, int index, Chardev *chr)
 {
     DeviceState *dev;
     ISADevice *isadev;
diff --git a/hw/char/serial.c b/hw/char/serial.c
index 67b18eda12..03d890ca24 100644
--- a/hw/char/serial.c
+++ b/hw/char/serial.c
@@ -937,7 +937,7 @@ const MemoryRegionOps serial_io_ops = {
 };
 
 SerialState *serial_init(int base, qemu_irq irq, int baudbase,
-                         CharDriverState *chr, MemoryRegion *system_io)
+                         Chardev *chr, MemoryRegion *system_io)
 {
     SerialState *s;
 
@@ -993,7 +993,7 @@ static const MemoryRegionOps serial_mm_ops[3] = {
 SerialState *serial_mm_init(MemoryRegion *address_space,
                             hwaddr base, int it_shift,
                             qemu_irq irq, int baudbase,
-                            CharDriverState *chr, enum device_endian end)
+                            Chardev *chr, enum device_endian end)
 {
     SerialState *s;
 
diff --git a/hw/char/sh_serial.c b/hw/char/sh_serial.c
index 9d35564bcf..303eb0a678 100644
--- a/hw/char/sh_serial.c
+++ b/hw/char/sh_serial.c
@@ -356,7 +356,7 @@ static const MemoryRegionOps sh_serial_ops = {
 
 void sh_serial_init(MemoryRegion *sysmem,
                     hwaddr base, int feat,
-                    uint32_t freq, CharDriverState *chr,
+                    uint32_t freq, Chardev *chr,
                     qemu_irq eri_source,
                     qemu_irq rxi_source,
                     qemu_irq txi_source,
diff --git a/hw/char/spapr_vty.c b/hw/char/spapr_vty.c
index 7c22b8bd0e..e30c8da57c 100644
--- a/hw/char/spapr_vty.c
+++ b/hw/char/spapr_vty.c
@@ -141,7 +141,7 @@ static target_ulong h_get_term_char(PowerPCCPU *cpu, sPAPRMachineState *spapr,
     return H_SUCCESS;
 }
 
-void spapr_vty_create(VIOsPAPRBus *bus, CharDriverState *chardev)
+void spapr_vty_create(VIOsPAPRBus *bus, Chardev *chardev)
 {
     DeviceState *dev;
 
diff --git a/hw/char/virtio-console.c b/hw/char/virtio-console.c
index 776205b4a9..798d9b69fd 100644
--- a/hw/char/virtio-console.c
+++ b/hw/char/virtio-console.c
@@ -168,7 +168,7 @@ static void virtconsole_realize(DeviceState *dev, Error **errp)
     VirtIOSerialPort *port = VIRTIO_SERIAL_PORT(dev);
     VirtConsole *vcon = VIRTIO_CONSOLE(dev);
     VirtIOSerialPortClass *k = VIRTIO_SERIAL_PORT_GET_CLASS(dev);
-    CharDriverState *chr = qemu_chr_fe_get_driver(&vcon->chr);
+    Chardev *chr = qemu_chr_fe_get_driver(&vcon->chr);
 
     if (port->id == 0 && !k->is_console) {
         error_setg(errp, "Port number 0 on virtio-serial devices reserved "
diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c
index 1b7ea50e9f..94f4d8bde4 100644
--- a/hw/core/qdev-properties-system.c
+++ b/hw/core/qdev-properties-system.c
@@ -179,7 +179,7 @@ static void set_chr(Object *obj, Visitor *v, const char *name, void *opaque,
     Error *local_err = NULL;
     Property *prop = opaque;
     CharBackend *be = qdev_get_prop_ptr(dev, prop);
-    CharDriverState *s;
+    Chardev *s;
     char *str;
 
     if (dev->realized) {
@@ -411,7 +411,7 @@ void qdev_prop_set_drive(DeviceState *dev, const char *name,
 }
 
 void qdev_prop_set_chr(DeviceState *dev, const char *name,
-                       CharDriverState *value)
+                       Chardev *value)
 {
     assert(!value || value->label);
     object_property_set_str(OBJECT(dev),
diff --git a/hw/cpu/core.c b/hw/cpu/core.c
index eff90c12be..2bf960d6a8 100644
--- a/hw/cpu/core.c
+++ b/hw/cpu/core.c
@@ -72,10 +72,18 @@ static void cpu_core_instance_init(Object *obj)
     core->nr_threads = smp_threads;
 }
 
+static void cpu_core_class_init(ObjectClass *oc, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(oc);
+
+    set_bit(DEVICE_CATEGORY_CPU, dc->categories);
+}
+
 static const TypeInfo cpu_core_type_info = {
     .name = TYPE_CPU_CORE,
     .parent = TYPE_DEVICE,
     .abstract = true,
+    .class_init = cpu_core_class_init,
     .instance_size = sizeof(CPUCore),
     .instance_init = cpu_core_instance_init,
 };
diff --git a/hw/display/cirrus_vga.c b/hw/display/cirrus_vga.c
index 379910db2d..16f27e8ac5 100644
--- a/hw/display/cirrus_vga.c
+++ b/hw/display/cirrus_vga.c
@@ -272,15 +272,11 @@ static void cirrus_update_memory_access(CirrusVGAState *s);
 static bool blit_region_is_unsafe(struct CirrusVGAState *s,
                                   int32_t pitch, int32_t addr)
 {
-    if (!pitch) {
-        return true;
-    }
     if (pitch < 0) {
         int64_t min = addr
-            + ((int64_t)s->cirrus_blt_height-1) * pitch;
-        int32_t max = addr
-            + s->cirrus_blt_width;
-        if (min < 0 || max > s->vga.vram_size) {
+            + ((int64_t)s->cirrus_blt_height - 1) * pitch
+            - s->cirrus_blt_width;
+        if (min < -1 || addr >= s->vga.vram_size) {
             return true;
         }
     } else {
@@ -294,8 +290,11 @@ static bool blit_region_is_unsafe(struct CirrusVGAState *s,
     return false;
 }
 
-static bool blit_is_unsafe(struct CirrusVGAState *s, bool dst_only)
+static bool blit_is_unsafe(struct CirrusVGAState *s, bool dst_only,
+                           bool zero_src_pitch_ok)
 {
+    int32_t check_pitch;
+
     /* should be the case, see cirrus_bitblt_start */
     assert(s->cirrus_blt_width > 0);
     assert(s->cirrus_blt_height > 0);
@@ -304,15 +303,25 @@ static bool blit_is_unsafe(struct CirrusVGAState *s, bool dst_only)
         return true;
     }
 
+    if (!s->cirrus_blt_dstpitch) {
+        return true;
+    }
+
     if (blit_region_is_unsafe(s, s->cirrus_blt_dstpitch,
-                              s->cirrus_blt_dstaddr & s->cirrus_addr_mask)) {
+                              s->cirrus_blt_dstaddr)) {
         return true;
     }
     if (dst_only) {
         return false;
     }
-    if (blit_region_is_unsafe(s, s->cirrus_blt_srcpitch,
-                              s->cirrus_blt_srcaddr & s->cirrus_addr_mask)) {
+
+    check_pitch = s->cirrus_blt_srcpitch;
+    if (!zero_src_pitch_ok && !check_pitch) {
+        check_pitch = s->cirrus_blt_width;
+    }
+
+    if (blit_region_is_unsafe(s, check_pitch,
+                              s->cirrus_blt_srcaddr)) {
         return true;
     }
 
@@ -661,9 +670,14 @@ static void cirrus_invalidate_region(CirrusVGAState * s, int off_begin,
     int off_cur;
     int off_cur_end;
 
+    if (off_pitch < 0) {
+        off_begin -= bytesperline - 1;
+    }
+
     for (y = 0; y < lines; y++) {
 	off_cur = off_begin;
 	off_cur_end = (off_cur + bytesperline) & s->cirrus_addr_mask;
+        assert(off_cur_end >= off_cur);
         memory_region_set_dirty(&s->vga.vram, off_cur, off_cur_end - off_cur);
 	off_begin += off_pitch;
     }
@@ -674,10 +688,11 @@ static int cirrus_bitblt_common_patterncopy(CirrusVGAState * s,
 {
     uint8_t *dst;
 
-    dst = s->vga.vram_ptr + (s->cirrus_blt_dstaddr & s->cirrus_addr_mask);
+    dst = s->vga.vram_ptr + s->cirrus_blt_dstaddr;
 
-    if (blit_is_unsafe(s, false))
+    if (blit_is_unsafe(s, false, true)) {
         return 0;
+    }
 
     (*s->cirrus_rop) (s, dst, src,
                       s->cirrus_blt_dstpitch, 0,
@@ -694,11 +709,11 @@ static int cirrus_bitblt_solidfill(CirrusVGAState *s, int blt_rop)
 {
     cirrus_fill_t rop_func;
 
-    if (blit_is_unsafe(s, true)) {
+    if (blit_is_unsafe(s, true, true)) {
         return 0;
     }
     rop_func = cirrus_fill[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1];
-    rop_func(s, s->vga.vram_ptr + (s->cirrus_blt_dstaddr & s->cirrus_addr_mask),
+    rop_func(s, s->vga.vram_ptr + s->cirrus_blt_dstaddr,
              s->cirrus_blt_dstpitch,
              s->cirrus_blt_width, s->cirrus_blt_height);
     cirrus_invalidate_region(s, s->cirrus_blt_dstaddr,
@@ -716,9 +731,8 @@ static int cirrus_bitblt_solidfill(CirrusVGAState *s, int blt_rop)
 
 static int cirrus_bitblt_videotovideo_patterncopy(CirrusVGAState * s)
 {
-    return cirrus_bitblt_common_patterncopy(s,
-					    s->vga.vram_ptr + ((s->cirrus_blt_srcaddr & ~7) &
-                                            s->cirrus_addr_mask));
+    return cirrus_bitblt_common_patterncopy(s, s->vga.vram_ptr +
+                                            (s->cirrus_blt_srcaddr & ~7));
 }
 
 static int cirrus_do_copy(CirrusVGAState *s, int dst, int src, int w, int h)
@@ -772,10 +786,8 @@ static int cirrus_do_copy(CirrusVGAState *s, int dst, int src, int w, int h)
     if (notify)
         graphic_hw_update(s->vga.con);
 
-    (*s->cirrus_rop) (s, s->vga.vram_ptr +
-		      (s->cirrus_blt_dstaddr & s->cirrus_addr_mask),
-		      s->vga.vram_ptr +
-		      (s->cirrus_blt_srcaddr & s->cirrus_addr_mask),
+    (*s->cirrus_rop) (s, s->vga.vram_ptr + s->cirrus_blt_dstaddr,
+                      s->vga.vram_ptr + s->cirrus_blt_srcaddr,
 		      s->cirrus_blt_dstpitch, s->cirrus_blt_srcpitch,
 		      s->cirrus_blt_width, s->cirrus_blt_height);
 
@@ -798,7 +810,7 @@ static int cirrus_do_copy(CirrusVGAState *s, int dst, int src, int w, int h)
 
 static int cirrus_bitblt_videotovideo_copy(CirrusVGAState * s)
 {
-    if (blit_is_unsafe(s, false))
+    if (blit_is_unsafe(s, false, false))
         return 0;
 
     return cirrus_do_copy(s, s->cirrus_blt_dstaddr - s->vga.start_addr,
@@ -826,8 +838,7 @@ static void cirrus_bitblt_cputovideo_next(CirrusVGAState * s)
         } else {
             /* at least one scan line */
             do {
-                (*s->cirrus_rop)(s, s->vga.vram_ptr +
-                                 (s->cirrus_blt_dstaddr & s->cirrus_addr_mask),
+                (*s->cirrus_rop)(s, s->vga.vram_ptr + s->cirrus_blt_dstaddr,
                                   s->cirrus_bltbuf, 0, 0, s->cirrus_blt_width, 1);
                 cirrus_invalidate_region(s, s->cirrus_blt_dstaddr, 0,
                                          s->cirrus_blt_width, 1);
@@ -946,6 +957,9 @@ static void cirrus_bitblt_start(CirrusVGAState * s)
     s->cirrus_blt_modeext = s->vga.gr[0x33];
     blt_rop = s->vga.gr[0x32];
 
+    s->cirrus_blt_dstaddr &= s->cirrus_addr_mask;
+    s->cirrus_blt_srcaddr &= s->cirrus_addr_mask;
+
 #ifdef DEBUG_BITBLT
     printf("rop=0x%02x mode=0x%02x modeext=0x%02x w=%d h=%d dpitch=%d spitch=%d daddr=0x%08x saddr=0x%08x writemask=0x%02x\n",
            blt_rop,
diff --git a/hw/display/milkymist-tmu2.c b/hw/display/milkymist-tmu2.c
index 920374b985..7528665510 100644
--- a/hw/display/milkymist-tmu2.c
+++ b/hw/display/milkymist-tmu2.c
@@ -85,7 +85,7 @@ struct MilkymistTMU2State {
     SysBusDevice parent_obj;
 
     MemoryRegion regs_region;
-    CharDriverState *chr;
+    Chardev *chr;
     qemu_irq irq;
 
     uint32_t regs[R_MAX];
diff --git a/hw/display/qxl.c b/hw/display/qxl.c
index 62d0c80dcf..af4c0ca002 100644
--- a/hw/display/qxl.c
+++ b/hw/display/qxl.c
@@ -306,12 +306,11 @@ void qxl_spice_reset_cursor(PCIQXLDevice *qxl)
 
 static ram_addr_t qxl_rom_size(void)
 {
-    uint32_t required_rom_size = sizeof(QXLRom) + sizeof(QXLModes) +
-                                 sizeof(qxl_modes);
-    uint32_t rom_size = 8192; /* two pages */
+#define QXL_REQUIRED_SZ (sizeof(QXLRom) + sizeof(QXLModes) + sizeof(qxl_modes))
+#define QXL_ROM_SZ 8192
 
-    QEMU_BUILD_BUG_ON(required_rom_size > rom_size);
-    return rom_size;
+    QEMU_BUILD_BUG_ON(QXL_REQUIRED_SZ > QXL_ROM_SZ);
+    return QXL_ROM_SZ;
 }
 
 static void init_qxl_rom(PCIQXLDevice *d)
diff --git a/hw/display/sm501.c b/hw/display/sm501.c
index 5f71012108..040a0b93f2 100644
--- a/hw/display/sm501.c
+++ b/hw/display/sm501.c
@@ -1392,7 +1392,7 @@ static const GraphicHwOps sm501_ops = {
 };
 
 void sm501_init(MemoryRegion *address_space_mem, uint32_t base,
-                uint32_t local_mem_bytes, qemu_irq irq, CharDriverState *chr)
+                uint32_t local_mem_bytes, qemu_irq irq, Chardev *chr)
 {
     SM501State * s;
     DeviceState *dev;
diff --git a/hw/display/trace-events b/hw/display/trace-events
index 332ababd8e..aadb612dcb 100644
--- a/hw/display/trace-events
+++ b/hw/display/trace-events
@@ -34,7 +34,6 @@ vmware_setmode(uint32_t w, uint32_t h, uint32_t bpp) "%dx%d @ %d bpp"
 # hw/display/virtio-gpu.c
 virtio_gpu_features(bool virgl) "virgl %d"
 virtio_gpu_cmd_get_display_info(void) ""
-virtio_gpu_cmd_get_caps(void) ""
 virtio_gpu_cmd_set_scanout(uint32_t id, uint32_t res, uint32_t w, uint32_t h, uint32_t x, uint32_t y) "id %d, res 0x%x, w %d, h %d, x %d, y %d"
 virtio_gpu_cmd_res_create_2d(uint32_t res, uint32_t fmt, uint32_t w, uint32_t h) "res 0x%x, fmt 0x%x, w %d, h %d"
 virtio_gpu_cmd_res_create_3d(uint32_t res, uint32_t fmt, uint32_t w, uint32_t h, uint32_t d) "res 0x%x, fmt 0x%x, w %d, h %d, d %d"
diff --git a/hw/gpio/mpc8xxx.c b/hw/gpio/mpc8xxx.c
index d149719469..e12edb4933 100644
--- a/hw/gpio/mpc8xxx.c
+++ b/hw/gpio/mpc8xxx.c
@@ -143,8 +143,10 @@ static void mpc8xxx_gpio_write(void *opaque, hwaddr offset,
     mpc8xxx_gpio_update(s);
 }
 
-static void mpc8xxx_gpio_reset(MPC8XXXGPIOState *s)
+static void mpc8xxx_gpio_reset(DeviceState *dev)
 {
+    MPC8XXXGPIOState *s = MPC8XXX_GPIO(dev);
+
     s->dir = 0;
     s->odr = 0;
     s->dat = 0;
@@ -180,33 +182,33 @@ static const MemoryRegionOps mpc8xxx_gpio_ops = {
     .endianness = DEVICE_BIG_ENDIAN,
 };
 
-static int mpc8xxx_gpio_initfn(SysBusDevice *sbd)
+static void mpc8xxx_gpio_initfn(Object *obj)
 {
-    DeviceState *dev = DEVICE(sbd);
-    MPC8XXXGPIOState *s = MPC8XXX_GPIO(dev);
+    DeviceState *dev = DEVICE(obj);
+    MPC8XXXGPIOState *s = MPC8XXX_GPIO(obj);
+    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
 
-    memory_region_init_io(&s->iomem, OBJECT(s), &mpc8xxx_gpio_ops, s, "mpc8xxx_gpio", 0x1000);
+    memory_region_init_io(&s->iomem, obj, &mpc8xxx_gpio_ops,
+                          s, "mpc8xxx_gpio", 0x1000);
     sysbus_init_mmio(sbd, &s->iomem);
     sysbus_init_irq(sbd, &s->irq);
     qdev_init_gpio_in(dev, mpc8xxx_gpio_set_irq, 32);
     qdev_init_gpio_out(dev, s->out, 32);
-    mpc8xxx_gpio_reset(s);
-    return 0;
 }
 
 static void mpc8xxx_gpio_class_init(ObjectClass *klass, void *data)
 {
     DeviceClass *dc = DEVICE_CLASS(klass);
-    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
 
-    k->init = mpc8xxx_gpio_initfn;
     dc->vmsd = &vmstate_mpc8xxx_gpio;
+    dc->reset = mpc8xxx_gpio_reset;
 }
 
 static const TypeInfo mpc8xxx_gpio_info = {
     .name          = TYPE_MPC8XXX_GPIO,
     .parent        = TYPE_SYS_BUS_DEVICE,
     .instance_size = sizeof(MPC8XXXGPIOState),
+    .instance_init = mpc8xxx_gpio_initfn,
     .class_init    = mpc8xxx_gpio_class_init,
 };
 
diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c
index ec62239aba..3270fb9162 100644
--- a/hw/i386/intel_iommu.c
+++ b/hw/i386/intel_iommu.c
@@ -1485,8 +1485,16 @@ static bool vtd_process_device_iotlb_desc(IntelIOMMUState *s,
         goto done;
     }
 
+    /* According to ATS spec table 2.4:
+     * S = 0, bits 15:12 = xxxx     range size: 4K
+     * S = 1, bits 15:12 = xxx0     range size: 8K
+     * S = 1, bits 15:12 = xx01     range size: 16K
+     * S = 1, bits 15:12 = x011     range size: 32K
+     * S = 1, bits 15:12 = 0111     range size: 64K
+     * ...
+     */
     if (size) {
-        sz = 1 << (ctz64(~(addr | (VTD_PAGE_MASK_4K - 1))) + 1);
+        sz = (VTD_PAGE_SIZE * 2) << cto64(addr >> VTD_PAGE_SHIFT);
         addr &= ~(sz - 1);
     } else {
         sz = VTD_PAGE_SIZE;
diff --git a/hw/i386/kvmvapic.c b/hw/i386/kvmvapic.c
index 702e281dc8..7135633863 100644
--- a/hw/i386/kvmvapic.c
+++ b/hw/i386/kvmvapic.c
@@ -413,6 +413,12 @@ static void patch_instruction(VAPICROMState *s, X86CPU *cpu, target_ulong ip)
     if (!kvm_enabled()) {
         cpu_get_tb_cpu_state(env, &current_pc, &current_cs_base,
                              &current_flags);
+        /* Account this instruction, because we will exit the tb.
+           This is the first instruction in the block. Therefore
+           there is no need in restoring CPU state. */
+        if (use_icount) {
+            --cs->icount_decr.u16.low;
+        }
     }
 
     pause_all_vcpus();
diff --git a/hw/i386/pc.c b/hw/i386/pc.c
index 706e2330ac..e3fcd514dd 100644
--- a/hw/i386/pc.c
+++ b/hw/i386/pc.c
@@ -1708,6 +1708,11 @@ static void pc_dimm_plug(HotplugHandler *hotplug_dev,
     }
 
     if (object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM)) {
+        if (!pcms->acpi_nvdimm_state.is_enabled) {
+            error_setg(&local_err,
+                       "nvdimm is not enabled: missing 'nvdimm' in '-M'");
+            goto out;
+        }
         nvdimm_plug(&pcms->acpi_nvdimm_state);
     }
 
diff --git a/hw/i386/trace-events b/hw/i386/trace-events
index d2b497327e..1cc4a10a07 100644
--- a/hw/i386/trace-events
+++ b/hw/i386/trace-events
@@ -1,12 +1,5 @@
 # See docs/tracing.txt for syntax documentation.
 
-# hw/i386/xen/xen_platform.c
-xen_platform_log(char *s) "xen platform: %s"
-
-# hw/i386/xen/xen_pvdevice.c
-xen_pv_mmio_read(uint64_t addr) "WARNING: read from Xen PV Device MMIO space (address %"PRIx64")"
-xen_pv_mmio_write(uint64_t addr) "WARNING: write to Xen PV Device MMIO space (address %"PRIx64")"
-
 # hw/i386/x86-iommu.c
 x86_iommu_iec_notify(bool global, uint32_t index, uint32_t mask) "Notify IEC invalidation: global=%d index=%" PRIu32 " mask=%" PRIu32
 
@@ -30,7 +23,6 @@ amdvi_devtab_inval(uint8_t bus, uint8_t slot, uint8_t func) "device table entry
 amdvi_completion_wait(uint64_t addr, uint64_t data) "completion wait requested with store address 0x%"PRIx64" and store data 0x%"PRIx64
 amdvi_control_status(uint64_t val) "MMIO_STATUS state 0x%"PRIx64
 amdvi_iotlb_reset(void) "IOTLB exceed size limit - reset "
-amdvi_completion_wait_exec(uint64_t addr, uint64_t data) "completion wait requested with store address 0x%"PRIx64" and store data 0x%"PRIx64
 amdvi_dte_get_fail(uint64_t addr, uint32_t offset) "error: failed to access Device Entry devtab 0x%"PRIx64" offset 0x%"PRIx32
 amdvi_invalid_dte(uint64_t addr) "PTE entry at 0x%"PRIx64" is invalid "
 amdvi_get_pte_hwerror(uint64_t addr) "hardware error eccessing PTE at addr 0x%"PRIx64
diff --git a/hw/i386/xen/trace-events b/hw/i386/xen/trace-events
new file mode 100644
index 0000000000..321fe60fed
--- /dev/null
+++ b/hw/i386/xen/trace-events
@@ -0,0 +1,6 @@
+# hw/i386/xen/xen_platform.c
+xen_platform_log(char *s) "xen platform: %s"
+
+# hw/i386/xen/xen_pvdevice.c
+xen_pv_mmio_read(uint64_t addr) "WARNING: read from Xen PV Device MMIO space (address %"PRIx64")"
+xen_pv_mmio_write(uint64_t addr) "WARNING: write to Xen PV Device MMIO space (address %"PRIx64")"
diff --git a/hw/input/ps2.c b/hw/input/ps2.c
index 8485a4edaf..1d3a440bbd 100644
--- a/hw/input/ps2.c
+++ b/hw/input/ps2.c
@@ -881,9 +881,11 @@ static void ps2_mouse_event(DeviceState *dev, QemuConsole *src,
                             InputEvent *evt)
 {
     static const int bmap[INPUT_BUTTON__MAX] = {
-        [INPUT_BUTTON_LEFT]   = MOUSE_EVENT_LBUTTON,
-        [INPUT_BUTTON_MIDDLE] = MOUSE_EVENT_MBUTTON,
-        [INPUT_BUTTON_RIGHT]  = MOUSE_EVENT_RBUTTON,
+        [INPUT_BUTTON_LEFT]   = PS2_MOUSE_BUTTON_LEFT,
+        [INPUT_BUTTON_MIDDLE] = PS2_MOUSE_BUTTON_MIDDLE,
+        [INPUT_BUTTON_RIGHT]  = PS2_MOUSE_BUTTON_RIGHT,
+        [INPUT_BUTTON_SIDE]   = PS2_MOUSE_BUTTON_SIDE,
+        [INPUT_BUTTON_EXTRA]  = PS2_MOUSE_BUTTON_EXTRA,
     };
     PS2MouseState *s = (PS2MouseState *)dev;
     InputMoveEvent *move;
diff --git a/hw/input/trace-events b/hw/input/trace-events
index 8c4003f361..f3bfbede5c 100644
--- a/hw/input/trace-events
+++ b/hw/input/trace-events
@@ -8,8 +8,6 @@ ps2_reset_keyboard(void *s) "%p"
 ps2_write_keyboard(void *opaque, int val) "%p val %d"
 ps2_keyboard_set_translation(void *opaque, int mode) "%p mode %d"
 ps2_mouse_send_packet(void *s, int dx1, int dy1, int dz1, int b) "%p x %d y %d z %d bs %#x"
-ps2_mouse_event_disabled(void *opaque, int dx, int dy, int dz, int buttons_state, int mouse_dx, int mouse_dy, int mouse_dz) "%p x %d y %d z %d bs %#x mx %d my %d mz %d "
-ps2_mouse_event(void *opaque, int dx, int dy, int dz, int buttons_state, int mouse_dx, int mouse_dy, int mouse_dz) "%p x %d y %d z %d bs %#x mx %d my %d mz %d "
 ps2_mouse_fake_event(void *opaque) "%p"
 ps2_write_mouse(void *opaque, int val) "%p val %d"
 ps2_kbd_reset(void *opaque) "%p"
diff --git a/hw/intc/apic_common.c b/hw/intc/apic_common.c
index 3945dfd7b9..17df24c9d0 100644
--- a/hw/intc/apic_common.c
+++ b/hw/intc/apic_common.c
@@ -385,6 +385,25 @@ static bool apic_common_sipi_needed(void *opaque)
     return s->wait_for_sipi != 0;
 }
 
+static bool apic_irq_delivered_needed(void *opaque)
+{
+    APICCommonState *s = APIC_COMMON(opaque);
+    return s->cpu == X86_CPU(first_cpu) && apic_irq_delivered != 0;
+}
+
+static void apic_irq_delivered_pre_save(void *opaque)
+{
+    APICCommonState *s = APIC_COMMON(opaque);
+    s->apic_irq_delivered = apic_irq_delivered;
+}
+
+static int apic_irq_delivered_post_load(void *opaque, int version_id)
+{
+    APICCommonState *s = APIC_COMMON(opaque);
+    apic_irq_delivered = s->apic_irq_delivered;
+    return 0;
+}
+
 static const VMStateDescription vmstate_apic_common_sipi = {
     .name = "apic_sipi",
     .version_id = 1,
@@ -397,6 +416,19 @@ static const VMStateDescription vmstate_apic_common_sipi = {
     }
 };
 
+static const VMStateDescription vmstate_apic_irq_delivered = {
+    .name = "apic_irq_delivered",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .needed = apic_irq_delivered_needed,
+    .pre_save = apic_irq_delivered_pre_save,
+    .post_load = apic_irq_delivered_post_load,
+    .fields = (VMStateField[]) {
+        VMSTATE_INT32(apic_irq_delivered, APICCommonState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
 static const VMStateDescription vmstate_apic_common = {
     .name = "apic",
     .version_id = 3,
@@ -431,6 +463,7 @@ static const VMStateDescription vmstate_apic_common = {
     },
     .subsections = (const VMStateDescription*[]) {
         &vmstate_apic_common_sipi,
+        &vmstate_apic_irq_delivered,
         NULL
     }
 };
diff --git a/hw/intc/trace-events b/hw/intc/trace-events
index 92a6171692..39a538d048 100644
--- a/hw/intc/trace-events
+++ b/hw/intc/trace-events
@@ -67,7 +67,6 @@ xics_alloc(int irq) "irq %d"
 xics_alloc_block(int first, int num, bool lsi, int align) "first irq %d, %d irqs, lsi=%d, alignnum %d"
 xics_ics_free(int src, int irq, int num) "Source#%d, first irq %d, %d irqs"
 xics_ics_free_warn(int src, int irq) "Source#%d, irq %d is already free"
-xics_icp_post_load(uint32_t server_no, uint32_t xirr, uint64_t addr, uint8_t pend) "server_no %d, xirr %#x, xirr_owner 0x%" PRIx64 ", pending %d"
 
 # hw/intc/s390_flic_kvm.c
 flic_create_device(int err) "flic: create device failed %d"
diff --git a/hw/isa/isa-bus.c b/hw/isa/isa-bus.c
index 0ffbc8dd28..348e0eab9d 100644
--- a/hw/isa/isa-bus.c
+++ b/hw/isa/isa-bus.c
@@ -288,7 +288,7 @@ MemoryRegion *isa_address_space_io(ISADevice *dev)
 
 type_init(isabus_register_types)
 
-static void parallel_init(ISABus *bus, int index, CharDriverState *chr)
+static void parallel_init(ISABus *bus, int index, Chardev *chr)
 {
     DeviceState *dev;
     ISADevice *isadev;
diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c
index 10d1ee8b93..59930dd9d0 100644
--- a/hw/isa/lpc_ich9.c
+++ b/hw/isa/lpc_ich9.c
@@ -48,6 +48,8 @@
 #include "exec/address-spaces.h"
 #include "sysemu/sysemu.h"
 #include "qom/cpu.h"
+#include "hw/nvram/fw_cfg.h"
+#include "qemu/cutils.h"
 
 /*****************************************************************************/
 /* ICH9 LPC PCI to ISA bridge */
@@ -360,13 +362,62 @@ static void ich9_set_sci(void *opaque, int irq_num, int level)
     }
 }
 
+static void smi_features_ok_callback(void *opaque)
+{
+    ICH9LPCState *lpc = opaque;
+    uint64_t guest_features;
+
+    if (lpc->smi_features_ok) {
+        /* negotiation already complete, features locked */
+        return;
+    }
+
+    memcpy(&guest_features, lpc->smi_guest_features_le, sizeof guest_features);
+    le64_to_cpus(&guest_features);
+    if (guest_features & ~lpc->smi_host_features) {
+        /* guest requests invalid features, leave @features_ok at zero */
+        return;
+    }
+
+    /* valid feature subset requested, lock it down, report success */
+    lpc->smi_negotiated_features = guest_features;
+    lpc->smi_features_ok = 1;
+}
+
 void ich9_lpc_pm_init(PCIDevice *lpc_pci, bool smm_enabled)
 {
     ICH9LPCState *lpc = ICH9_LPC_DEVICE(lpc_pci);
     qemu_irq sci_irq;
+    FWCfgState *fw_cfg = fw_cfg_find();
 
     sci_irq = qemu_allocate_irq(ich9_set_sci, lpc, 0);
     ich9_pm_init(lpc_pci, &lpc->pm, smm_enabled, sci_irq);
+
+    if (lpc->smi_host_features && fw_cfg) {
+        uint64_t host_features_le;
+
+        host_features_le = cpu_to_le64(lpc->smi_host_features);
+        memcpy(lpc->smi_host_features_le, &host_features_le,
+               sizeof host_features_le);
+        fw_cfg_add_file(fw_cfg, "etc/smi/supported-features",
+                        lpc->smi_host_features_le,
+                        sizeof lpc->smi_host_features_le);
+
+        /* The other two guest-visible fields are cleared on device reset, we
+         * just link them into fw_cfg here.
+         */
+        fw_cfg_add_file_callback(fw_cfg, "etc/smi/requested-features",
+                                 NULL, NULL,
+                                 lpc->smi_guest_features_le,
+                                 sizeof lpc->smi_guest_features_le,
+                                 false);
+        fw_cfg_add_file_callback(fw_cfg, "etc/smi/features-ok",
+                                 smi_features_ok_callback, lpc,
+                                 &lpc->smi_features_ok,
+                                 sizeof lpc->smi_features_ok,
+                                 true);
+    }
+
     ich9_lpc_reset(&lpc->d.qdev);
 }
 
@@ -386,7 +437,15 @@ static void ich9_apm_ctrl_changed(uint32_t val, void *arg)
 
     /* SMI_EN = PMBASE + 30. SMI control and enable register */
     if (lpc->pm.smi_en & ICH9_PMIO_SMI_EN_APMC_EN) {
-        cpu_interrupt(current_cpu, CPU_INTERRUPT_SMI);
+        if (lpc->smi_negotiated_features &
+            (UINT64_C(1) << ICH9_LPC_SMI_F_BROADCAST_BIT)) {
+            CPUState *cs;
+            CPU_FOREACH(cs) {
+                cpu_interrupt(cs, CPU_INTERRUPT_SMI);
+            }
+        } else {
+            cpu_interrupt(current_cpu, CPU_INTERRUPT_SMI);
+        }
     }
 }
 
@@ -507,6 +566,10 @@ static void ich9_lpc_reset(DeviceState *qdev)
 
     lpc->sci_level = 0;
     lpc->rst_cnt = 0;
+
+    memset(lpc->smi_guest_features_le, 0, sizeof lpc->smi_guest_features_le);
+    lpc->smi_features_ok = 0;
+    lpc->smi_negotiated_features = 0;
 }
 
 /* root complex register block is mapped into memory space */
@@ -668,6 +731,29 @@ static const VMStateDescription vmstate_ich9_rst_cnt = {
     }
 };
 
+static bool ich9_smi_feat_needed(void *opaque)
+{
+    ICH9LPCState *lpc = opaque;
+
+    return !buffer_is_zero(lpc->smi_guest_features_le,
+                           sizeof lpc->smi_guest_features_le) ||
+           lpc->smi_features_ok;
+}
+
+static const VMStateDescription vmstate_ich9_smi_feat = {
+    .name = "ICH9LPC/smi_feat",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .needed = ich9_smi_feat_needed,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT8_ARRAY(smi_guest_features_le, ICH9LPCState,
+                            sizeof(uint64_t)),
+        VMSTATE_UINT8(smi_features_ok, ICH9LPCState),
+        VMSTATE_UINT64(smi_negotiated_features, ICH9LPCState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
 static const VMStateDescription vmstate_ich9_lpc = {
     .name = "ICH9LPC",
     .version_id = 1,
@@ -683,12 +769,15 @@ static const VMStateDescription vmstate_ich9_lpc = {
     },
     .subsections = (const VMStateDescription*[]) {
         &vmstate_ich9_rst_cnt,
+        &vmstate_ich9_smi_feat,
         NULL
     }
 };
 
 static Property ich9_lpc_properties[] = {
     DEFINE_PROP_BOOL("noreboot", ICH9LPCState, pin_strap.spkr_hi, true),
+    DEFINE_PROP_BIT64("x-smi-broadcast", ICH9LPCState, smi_host_features,
+                      ICH9_LPC_SMI_F_BROADCAST_BIT, true),
     DEFINE_PROP_END_OF_LIST(),
 };
 
diff --git a/hw/isa/pc87312.c b/hw/isa/pc87312.c
index b1c1a0acb1..c707d24db4 100644
--- a/hw/isa/pc87312.c
+++ b/hw/isa/pc87312.c
@@ -268,7 +268,7 @@ static void pc87312_realize(DeviceState *dev, Error **errp)
     DeviceState *d;
     ISADevice *isa;
     ISABus *bus;
-    CharDriverState *chr;
+    Chardev *chr;
     DriveInfo *drive;
     char name[5];
     int i;
diff --git a/hw/lm32/lm32.h b/hw/lm32/lm32.h
index db9eb29ea4..d1514a61b3 100644
--- a/hw/lm32/lm32.h
+++ b/hw/lm32/lm32.h
@@ -16,7 +16,7 @@ static inline DeviceState *lm32_pic_init(qemu_irq cpu_irq)
     return dev;
 }
 
-static inline DeviceState *lm32_juart_init(CharDriverState *chr)
+static inline DeviceState *lm32_juart_init(Chardev *chr)
 {
     DeviceState *dev;
 
@@ -29,7 +29,7 @@ static inline DeviceState *lm32_juart_init(CharDriverState *chr)
 
 static inline DeviceState *lm32_uart_create(hwaddr addr,
                                             qemu_irq irq,
-                                            CharDriverState *chr)
+                                            Chardev *chr)
 {
     DeviceState *dev;
     SysBusDevice *s;
diff --git a/hw/lm32/milkymist-hw.h b/hw/lm32/milkymist-hw.h
index 4418b44ca9..d3be0cfb3a 100644
--- a/hw/lm32/milkymist-hw.h
+++ b/hw/lm32/milkymist-hw.h
@@ -6,7 +6,7 @@
 
 static inline DeviceState *milkymist_uart_create(hwaddr base,
                                                  qemu_irq irq,
-                                                 CharDriverState *chr)
+                                                 Chardev *chr)
 {
     DeviceState *dev;
 
diff --git a/hw/mips/mips_malta.c b/hw/mips/mips_malta.c
index cf48f420cc..75877de11c 100644
--- a/hw/mips/mips_malta.c
+++ b/hw/mips/mips_malta.c
@@ -551,10 +551,10 @@ static void malta_fgpa_display_event(void *opaque, int event)
 }
 
 static MaltaFPGAState *malta_fpga_init(MemoryRegion *address_space,
-         hwaddr base, qemu_irq uart_irq, CharDriverState *uart_chr)
+         hwaddr base, qemu_irq uart_irq, Chardev *uart_chr)
 {
     MaltaFPGAState *s;
-    CharDriverState *chr;
+    Chardev *chr;
 
     s = (MaltaFPGAState *)g_malloc0(sizeof(MaltaFPGAState));
 
diff --git a/hw/misc/ivshmem.c b/hw/misc/ivshmem.c
index fd14d7a07e..bf57e635d6 100644
--- a/hw/misc/ivshmem.c
+++ b/hw/misc/ivshmem.c
@@ -749,13 +749,13 @@ static void ivshmem_reset(DeviceState *d)
     }
 }
 
-static int ivshmem_setup_interrupts(IVShmemState *s)
+static int ivshmem_setup_interrupts(IVShmemState *s, Error **errp)
 {
     /* allocate QEMU callback data for receiving interrupts */
     s->msi_vectors = g_malloc0(s->vectors * sizeof(MSIVector));
 
     if (ivshmem_has_feature(s, IVSHMEM_MSI)) {
-        if (msix_init_exclusive_bar(PCI_DEVICE(s), s->vectors, 1)) {
+        if (msix_init_exclusive_bar(PCI_DEVICE(s), s->vectors, 1, errp)) {
             return -1;
         }
 
@@ -869,7 +869,7 @@ static void ivshmem_common_realize(PCIDevice *dev, Error **errp)
         s->ivshmem_bar2 = host_memory_backend_get_memory(s->hostmem,
                                                          &error_abort);
     } else {
-        CharDriverState *chr = qemu_chr_fe_get_driver(&s->server_chr);
+        Chardev *chr = qemu_chr_fe_get_driver(&s->server_chr);
         assert(chr);
 
         IVSHMEM_DPRINTF("using shared memory server (socket = %s)\n",
@@ -898,8 +898,8 @@ static void ivshmem_common_realize(PCIDevice *dev, Error **errp)
         qemu_chr_fe_set_handlers(&s->server_chr, ivshmem_can_receive,
                                  ivshmem_read, NULL, s, NULL, true);
 
-        if (ivshmem_setup_interrupts(s) < 0) {
-            error_setg(errp, "failed to initialize interrupts");
+        if (ivshmem_setup_interrupts(s, errp) < 0) {
+            error_prepend(errp, "Failed to initialize interrupts: ");
             return;
         }
     }
diff --git a/hw/misc/milkymist-pfpu.c b/hw/misc/milkymist-pfpu.c
index 3ca25894f1..86f5e383b0 100644
--- a/hw/misc/milkymist-pfpu.c
+++ b/hw/misc/milkymist-pfpu.c
@@ -125,7 +125,7 @@ struct MilkymistPFPUState {
     SysBusDevice parent_obj;
 
     MemoryRegion regs_region;
-    CharDriverState *chr;
+    Chardev *chr;
     qemu_irq irq;
 
     uint32_t regs[R_MAX];
diff --git a/hw/net/e1000e.c b/hw/net/e1000e.c
index 0e9a25b7ab..b0f429b8e5 100644
--- a/hw/net/e1000e.c
+++ b/hw/net/e1000e.c
@@ -292,7 +292,7 @@ e1000e_init_msix(E1000EState *s)
                         E1000E_MSIX_IDX, E1000E_MSIX_TABLE,
                         &s->msix,
                         E1000E_MSIX_IDX, E1000E_MSIX_PBA,
-                        0xA0);
+                        0xA0, NULL);
 
     if (res < 0) {
         trace_e1000e_msix_init_fail(res);
diff --git a/hw/net/fsl_etsec/etsec.c b/hw/net/fsl_etsec/etsec.c
index fadf9c8faf..aa2b0d5a85 100644
--- a/hw/net/fsl_etsec/etsec.c
+++ b/hw/net/fsl_etsec/etsec.c
@@ -29,7 +29,6 @@
 #include "qemu/osdep.h"
 #include "sysemu/sysemu.h"
 #include "hw/sysbus.h"
-#include "trace.h"
 #include "hw/ptimer.h"
 #include "etsec.h"
 #include "registers.h"
diff --git a/hw/net/rocker/rocker.c b/hw/net/rocker/rocker.c
index e9d215aa4d..6e70fddee3 100644
--- a/hw/net/rocker/rocker.c
+++ b/hw/net/rocker/rocker.c
@@ -1256,14 +1256,16 @@ static int rocker_msix_init(Rocker *r)
 {
     PCIDevice *dev = PCI_DEVICE(r);
     int err;
+    Error *local_err = NULL;
 
     err = msix_init(dev, ROCKER_MSIX_VEC_COUNT(r->fp_ports),
                     &r->msix_bar,
                     ROCKER_PCI_MSIX_BAR_IDX, ROCKER_PCI_MSIX_TABLE_OFFSET,
                     &r->msix_bar,
                     ROCKER_PCI_MSIX_BAR_IDX, ROCKER_PCI_MSIX_PBA_OFFSET,
-                    0);
+                    0, &local_err);
     if (err) {
+        error_report_err(local_err);
         return err;
     }
 
diff --git a/hw/net/trace-events b/hw/net/trace-events
index 1a5c909939..c71480535e 100644
--- a/hw/net/trace-events
+++ b/hw/net/trace-events
@@ -63,10 +63,6 @@ net_rx_pkt_l4_csum_validate_entry(void) "Starting L4 checksum validation"
 net_rx_pkt_l4_csum_validate_not_xxp(void) "Not a TCP/UDP packet"
 net_rx_pkt_l4_csum_validate_udp_with_no_checksum(void) "UDP packet without checksum"
 net_rx_pkt_l4_csum_validate_ip4_fragment(void) "IP4 fragment"
-net_rx_pkt_l4_csum_validate_ip4_udp(void) "IP4/UDP packet"
-net_rx_pkt_l4_csum_validate_ip4_tcp(void) "IP4/TCP packet"
-net_rx_pkt_l4_csum_validate_ip6_udp(void) "IP6/UDP packet"
-net_rx_pkt_l4_csum_validate_ip6_tcp(void) "IP6/TCP packet"
 net_rx_pkt_l4_csum_validate_csum(bool csum_valid) "Checksum valid: %d"
 
 net_rx_pkt_l4_csum_calc_entry(void) "Starting L4 checksum calculation"
@@ -117,7 +113,6 @@ e1000e_core_mdic_read(uint8_t page, uint32_t addr, uint32_t data) "MDIC READ: PH
 e1000e_core_mdic_read_unhandled(uint8_t page, uint32_t addr) "MDIC READ: PHY[%u][%u] UNHANDLED"
 e1000e_core_mdic_write(uint8_t page, uint32_t addr, uint32_t data) "MDIC WRITE: PHY[%u][%u] = 0x%x"
 e1000e_core_mdic_write_unhandled(uint8_t page, uint32_t addr) "MDIC WRITE: PHY[%u][%u] UNHANDLED"
-e1000e_core_eeeprom_write(uint16_t bit_in, uint16_t bit_out, uint16_t reading) "eeprom bitnum in %d out %d, reading %d"
 e1000e_core_ctrl_write(uint64_t index, uint32_t val) "Write CTRL register 0x%"PRIx64", value: 0x%X"
 e1000e_core_ctrl_sw_reset(void) "Doing SW reset"
 e1000e_core_ctrl_phy_reset(void) "Doing PHY reset"
@@ -159,7 +154,6 @@ e1000e_rx_desc_buff_write(uint8_t idx, uint64_t addr, uint16_t offset, const voi
 e1000e_rx_descr(int ridx, uint64_t base, uint8_t len) "Next RX descriptor: ring #%d, PA: 0x%"PRIx64", length: %u"
 e1000e_rx_set_rctl(uint32_t rctl) "RCTL = 0x%x"
 e1000e_rx_receive_iov(int iovcnt) "Received vector of %d fragments"
-e1000e_rx_packet_size(size_t full, size_t vhdr, size_t data) "Received packet of %zu bytes total, %zu virt header, %zu data"
 e1000e_rx_flt_dropped(void) "Received packet dropped by RX filter"
 e1000e_rx_written_to_guest(uint32_t causes) "Received packet written to guest (ICR causes %u)"
 e1000e_rx_not_written_to_guest(uint32_t causes) "Received packet NOT written to guest (ICR causes %u)"
@@ -196,14 +190,12 @@ e1000e_rx_metadata_ipv6_filtering_disabled(void) "IPv6 RX filtering disabled by
 
 e1000e_vlan_vet(uint16_t vet) "Setting VLAN ethernet type 0x%X"
 
-e1000e_irq_set_cause(uint32_t cause) "IRQ cause set 0x%x"
 e1000e_irq_msi_notify(uint32_t cause) "MSI notify 0x%x"
 e1000e_irq_throttling_no_pending_interrupts(void) "No pending interrupts to notify"
 e1000e_irq_msi_notify_postponed(void) "Sending MSI postponed by ITR"
 e1000e_irq_legacy_notify_postponed(void) "Raising legacy IRQ postponed by ITR"
 e1000e_irq_throttling_no_pending_vec(int idx) "No pending interrupts for vector %d"
 e1000e_irq_msix_notify_postponed_vec(int idx) "Sending MSI-X postponed by EITR[%d]"
-e1000e_irq_msix_notify(uint32_t cause) "MSI-X notify 0x%x"
 e1000e_irq_legacy_notify(bool level) "IRQ line state: %d"
 e1000e_irq_msix_notify_vec(uint32_t vector) "MSI-X notify vector 0x%x"
 e1000e_irq_postponed_by_xitr(uint32_t reg) "Interrupt postponed by [E]ITR register 0x%x"
diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c
index 2cb2731e29..7dd456551c 100644
--- a/hw/net/vmxnet3.c
+++ b/hw/net/vmxnet3.c
@@ -2191,7 +2191,7 @@ vmxnet3_init_msix(VMXNET3State *s)
                         VMXNET3_MSIX_BAR_IDX, VMXNET3_OFF_MSIX_TABLE,
                         &s->msix_bar,
                         VMXNET3_MSIX_BAR_IDX, VMXNET3_OFF_MSIX_PBA(s),
-                        VMXNET3_MSIX_OFFSET(s));
+                        VMXNET3_MSIX_OFFSET(s), NULL);
 
     if (0 > res) {
         VMW_WRPRN("Failed to initialize MSI-X, error %d", res);
diff --git a/hw/pci-bridge/Makefile.objs b/hw/pci-bridge/Makefile.objs
index f2adfe348c..c4683cf5c1 100644
--- a/hw/pci-bridge/Makefile.objs
+++ b/hw/pci-bridge/Makefile.objs
@@ -1,5 +1,6 @@
 common-obj-y += pci_bridge_dev.o
-common-obj-y += pci_expander_bridge.o
+common-obj-$(CONFIG_PCIE_PORT) += pcie_root_port.o gen_pcie_root_port.o
+common-obj-$(CONFIG_PXB) += pci_expander_bridge.o
 common-obj-$(CONFIG_XIO3130) += xio3130_upstream.o xio3130_downstream.o
 common-obj-$(CONFIG_IOH3420) += ioh3420.o
 common-obj-$(CONFIG_I82801B11) += i82801b11.o
diff --git a/hw/pci-bridge/gen_pcie_root_port.c b/hw/pci-bridge/gen_pcie_root_port.c
new file mode 100644
index 0000000000..8ebffa8bb0
--- /dev/null
+++ b/hw/pci-bridge/gen_pcie_root_port.c
@@ -0,0 +1,87 @@
+/*
+ * Generic PCI Express Root Port emulation
+ *
+ * Copyright (C) 2017 Red Hat Inc
+ *
+ * Authors:
+ *   Marcel Apfelbaum <marcel@redhat.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 "qemu/osdep.h"
+#include "qapi/error.h"
+#include "hw/pci/msix.h"
+#include "hw/pci/pcie_port.h"
+
+#define TYPE_GEN_PCIE_ROOT_PORT                "pcie-root-port"
+
+#define GEN_PCIE_ROOT_PORT_AER_OFFSET           0x100
+#define GEN_PCIE_ROOT_PORT_MSIX_NR_VECTOR       1
+
+static uint8_t gen_rp_aer_vector(const PCIDevice *d)
+{
+    return 0;
+}
+
+static int gen_rp_interrupts_init(PCIDevice *d, Error **errp)
+{
+    int rc;
+
+    rc = msix_init_exclusive_bar(d, GEN_PCIE_ROOT_PORT_MSIX_NR_VECTOR, 0, errp);
+
+    if (rc < 0) {
+        assert(rc == -ENOTSUP);
+    } else {
+        msix_vector_use(d, 0);
+    }
+
+    return rc;
+}
+
+static void gen_rp_interrupts_uninit(PCIDevice *d)
+{
+    msix_uninit_exclusive_bar(d);
+}
+
+static const VMStateDescription vmstate_rp_dev = {
+    .name = "pcie-root-port",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .post_load = pcie_cap_slot_post_load,
+    .fields = (VMStateField[]) {
+        VMSTATE_PCI_DEVICE(parent_obj.parent_obj.parent_obj, PCIESlot),
+        VMSTATE_STRUCT(parent_obj.parent_obj.parent_obj.exp.aer_log,
+                       PCIESlot, 0, vmstate_pcie_aer_log, PCIEAERLog),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void gen_rp_dev_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+    PCIERootPortClass *rpc = PCIE_ROOT_PORT_CLASS(klass);
+
+    k->vendor_id = PCI_VENDOR_ID_REDHAT;
+    k->device_id = PCI_DEVICE_ID_REDHAT_PCIE_RP;
+    dc->desc = "PCI Express Root Port";
+    dc->vmsd = &vmstate_rp_dev;
+    rpc->aer_vector = gen_rp_aer_vector;
+    rpc->interrupts_init = gen_rp_interrupts_init;
+    rpc->interrupts_uninit = gen_rp_interrupts_uninit;
+    rpc->aer_offset = GEN_PCIE_ROOT_PORT_AER_OFFSET;
+}
+
+static const TypeInfo gen_rp_dev_info = {
+    .name          = TYPE_GEN_PCIE_ROOT_PORT,
+    .parent        = TYPE_PCIE_ROOT_PORT,
+    .class_init    = gen_rp_dev_class_init,
+};
+
+ static void gen_rp_register_types(void)
+ {
+    type_register_static(&gen_rp_dev_info);
+ }
+ type_init(gen_rp_register_types)
diff --git a/hw/pci-bridge/ioh3420.c b/hw/pci-bridge/ioh3420.c
index 0eef87a4f8..da4e5bdf04 100644
--- a/hw/pci-bridge/ioh3420.c
+++ b/hw/pci-bridge/ioh3420.c
@@ -61,119 +61,28 @@ static uint8_t ioh3420_aer_vector(const PCIDevice *d)
     return 0;
 }
 
-static void ioh3420_aer_vector_update(PCIDevice *d)
+static int ioh3420_interrupts_init(PCIDevice *d, Error **errp)
 {
-    pcie_aer_root_set_vector(d, ioh3420_aer_vector(d));
-}
-
-static void ioh3420_write_config(PCIDevice *d,
-                                   uint32_t address, uint32_t val, int len)
-{
-    uint32_t root_cmd =
-        pci_get_long(d->config + d->exp.aer_cap + PCI_ERR_ROOT_COMMAND);
-
-    pci_bridge_write_config(d, address, val, len);
-    ioh3420_aer_vector_update(d);
-    pcie_cap_slot_write_config(d, address, val, len);
-    pcie_aer_write_config(d, address, val, len);
-    pcie_aer_root_write_config(d, address, val, len, root_cmd);
-}
-
-static void ioh3420_reset(DeviceState *qdev)
-{
-    PCIDevice *d = PCI_DEVICE(qdev);
-
-    ioh3420_aer_vector_update(d);
-    pcie_cap_root_reset(d);
-    pcie_cap_deverr_reset(d);
-    pcie_cap_slot_reset(d);
-    pcie_cap_arifwd_reset(d);
-    pcie_aer_root_reset(d);
-    pci_bridge_reset(qdev);
-    pci_bridge_disable_base_limit(d);
-}
-
-static int ioh3420_initfn(PCIDevice *d)
-{
-    PCIEPort *p = PCIE_PORT(d);
-    PCIESlot *s = PCIE_SLOT(d);
     int rc;
-    Error *err = NULL;
-
-    pci_config_set_interrupt_pin(d->config, 1);
-    pci_bridge_initfn(d, TYPE_PCIE_BUS);
-    pcie_port_init_reg(d);
-
-    rc = pci_bridge_ssvid_init(d, IOH_EP_SSVID_OFFSET,
-                               IOH_EP_SSVID_SVID, IOH_EP_SSVID_SSID);
-    if (rc < 0) {
-        goto err_bridge;
-    }
+    Error *local_err = NULL;
 
     rc = msi_init(d, IOH_EP_MSI_OFFSET, IOH_EP_MSI_NR_VECTOR,
                   IOH_EP_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_64BIT,
-                  IOH_EP_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT, &err);
+                  IOH_EP_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT,
+                  &local_err);
     if (rc < 0) {
         assert(rc == -ENOTSUP);
-        error_report_err(err);
-        goto err_bridge;
+        error_propagate(errp, local_err);
     }
 
-    rc = pcie_cap_init(d, IOH_EP_EXP_OFFSET, PCI_EXP_TYPE_ROOT_PORT, p->port);
-    if (rc < 0) {
-        goto err_msi;
-    }
-
-    pcie_cap_arifwd_init(d);
-    pcie_cap_deverr_init(d);
-    pcie_cap_slot_init(d, s->slot);
-    pcie_cap_root_init(d);
-
-    pcie_chassis_create(s->chassis);
-    rc = pcie_chassis_add_slot(s);
-    if (rc < 0) {
-        goto err_pcie_cap;
-    }
-
-    rc = pcie_aer_init(d, PCI_ERR_VER, IOH_EP_AER_OFFSET,
-                       PCI_ERR_SIZEOF, &err);
-    if (rc < 0) {
-        error_report_err(err);
-        goto err;
-    }
-    pcie_aer_root_init(d);
-    ioh3420_aer_vector_update(d);
-
-    return 0;
-
-err:
-    pcie_chassis_del_slot(s);
-err_pcie_cap:
-    pcie_cap_exit(d);
-err_msi:
-    msi_uninit(d);
-err_bridge:
-    pci_bridge_exitfn(d);
     return rc;
 }
 
-static void ioh3420_exitfn(PCIDevice *d)
+static void ioh3420_interrupts_uninit(PCIDevice *d)
 {
-    PCIESlot *s = PCIE_SLOT(d);
-
-    pcie_aer_exit(d);
-    pcie_chassis_del_slot(s);
-    pcie_cap_exit(d);
     msi_uninit(d);
-    pci_bridge_exitfn(d);
 }
 
-static Property ioh3420_props[] = {
-    DEFINE_PROP_BIT(COMPAT_PROP_PCP, PCIDevice, cap_present,
-                    QEMU_PCIE_SLTCAP_PCP_BITNR, true),
-    DEFINE_PROP_END_OF_LIST()
-};
-
 static const VMStateDescription vmstate_ioh3420 = {
     .name = "ioh-3240-express-root-port",
     .version_id = 1,
@@ -191,25 +100,25 @@ static void ioh3420_class_init(ObjectClass *klass, void *data)
 {
     DeviceClass *dc = DEVICE_CLASS(klass);
     PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+    PCIERootPortClass *rpc = PCIE_ROOT_PORT_CLASS(klass);
 
-    k->is_express = 1;
-    k->is_bridge = 1;
-    k->config_write = ioh3420_write_config;
-    k->init = ioh3420_initfn;
-    k->exit = ioh3420_exitfn;
     k->vendor_id = PCI_VENDOR_ID_INTEL;
     k->device_id = PCI_DEVICE_ID_IOH_EPORT;
     k->revision = PCI_DEVICE_ID_IOH_REV;
-    set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
     dc->desc = "Intel IOH device id 3420 PCIE Root Port";
-    dc->reset = ioh3420_reset;
     dc->vmsd = &vmstate_ioh3420;
-    dc->props = ioh3420_props;
+    rpc->aer_vector = ioh3420_aer_vector;
+    rpc->interrupts_init = ioh3420_interrupts_init;
+    rpc->interrupts_uninit = ioh3420_interrupts_uninit;
+    rpc->exp_offset = IOH_EP_EXP_OFFSET;
+    rpc->aer_offset = IOH_EP_AER_OFFSET;
+    rpc->ssvid_offset = IOH_EP_SSVID_OFFSET;
+    rpc->ssid = IOH_EP_SSVID_SSID;
 }
 
 static const TypeInfo ioh3420_info = {
     .name          = "ioh3420",
-    .parent        = TYPE_PCIE_SLOT,
+    .parent        = TYPE_PCIE_ROOT_PORT,
     .class_init    = ioh3420_class_init,
 };
 
diff --git a/hw/pci-bridge/pci_bridge_dev.c b/hw/pci-bridge/pci_bridge_dev.c
index 5dbd933cc1..647ad80155 100644
--- a/hw/pci-bridge/pci_bridge_dev.c
+++ b/hw/pci-bridge/pci_bridge_dev.c
@@ -163,7 +163,7 @@ static Property pci_bridge_dev_properties[] = {
     DEFINE_PROP_ON_OFF_AUTO(PCI_BRIDGE_DEV_PROP_MSI, PCIBridgeDev, msi,
                             ON_OFF_AUTO_AUTO),
     DEFINE_PROP_BIT(PCI_BRIDGE_DEV_PROP_SHPC, PCIBridgeDev, flags,
-                    PCI_BRIDGE_DEV_F_SHPC_REQ, true),
+                    PCI_BRIDGE_DEV_F_SHPC_REQ, false),
     DEFINE_PROP_END_OF_LIST(),
 };
 
diff --git a/hw/pci-bridge/pcie_root_port.c b/hw/pci-bridge/pcie_root_port.c
new file mode 100644
index 0000000000..cf3631806f
--- /dev/null
+++ b/hw/pci-bridge/pcie_root_port.c
@@ -0,0 +1,171 @@
+/*
+ * Base class for PCI Express Root Ports
+ *
+ * Copyright (C) 2017 Red Hat Inc
+ *
+ * Authors:
+ *   Marcel Apfelbaum <marcel@redhat.com>
+ *
+ * Most of the code was migrated from hw/pci-bridge/ioh3420.
+ *
+ * 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 "qemu/osdep.h"
+#include "qapi/error.h"
+#include "hw/pci/pcie_port.h"
+
+static void rp_aer_vector_update(PCIDevice *d)
+{
+    PCIERootPortClass *rpc = PCIE_ROOT_PORT_GET_CLASS(d);
+
+    if (rpc->aer_vector) {
+        pcie_aer_root_set_vector(d, rpc->aer_vector(d));
+    }
+}
+
+static void rp_write_config(PCIDevice *d, uint32_t address,
+                            uint32_t val, int len)
+{
+    uint32_t root_cmd =
+        pci_get_long(d->config + d->exp.aer_cap + PCI_ERR_ROOT_COMMAND);
+
+    pci_bridge_write_config(d, address, val, len);
+    rp_aer_vector_update(d);
+    pcie_cap_slot_write_config(d, address, val, len);
+    pcie_aer_write_config(d, address, val, len);
+    pcie_aer_root_write_config(d, address, val, len, root_cmd);
+}
+
+static void rp_reset(DeviceState *qdev)
+{
+    PCIDevice *d = PCI_DEVICE(qdev);
+
+    rp_aer_vector_update(d);
+    pcie_cap_root_reset(d);
+    pcie_cap_deverr_reset(d);
+    pcie_cap_slot_reset(d);
+    pcie_cap_arifwd_reset(d);
+    pcie_aer_root_reset(d);
+    pci_bridge_reset(qdev);
+    pci_bridge_disable_base_limit(d);
+}
+
+static void rp_realize(PCIDevice *d, Error **errp)
+{
+    PCIEPort *p = PCIE_PORT(d);
+    PCIESlot *s = PCIE_SLOT(d);
+    PCIDeviceClass *dc = PCI_DEVICE_GET_CLASS(d);
+    PCIERootPortClass *rpc = PCIE_ROOT_PORT_GET_CLASS(d);
+    int rc;
+    Error *local_err = NULL;
+
+    pci_config_set_interrupt_pin(d->config, 1);
+    pci_bridge_initfn(d, TYPE_PCIE_BUS);
+    pcie_port_init_reg(d);
+
+    rc = pci_bridge_ssvid_init(d, rpc->ssvid_offset, dc->vendor_id, rpc->ssid);
+    if (rc < 0) {
+        error_setg(errp, "Can't init SSV ID, error %d", rc);
+        goto err_bridge;
+    }
+
+    if (rpc->interrupts_init) {
+        rc = rpc->interrupts_init(d, &local_err);
+        if (rc < 0) {
+            error_propagate(errp, local_err);
+            goto err_bridge;
+        }
+    }
+
+    rc = pcie_cap_init(d, rpc->exp_offset, PCI_EXP_TYPE_ROOT_PORT, p->port);
+    if (rc < 0) {
+        error_setg(errp, "Can't add Root Port capability, error %d", rc);
+        goto err_int;
+    }
+
+    pcie_cap_arifwd_init(d);
+    pcie_cap_deverr_init(d);
+    pcie_cap_slot_init(d, s->slot);
+    pcie_cap_root_init(d);
+
+    pcie_chassis_create(s->chassis);
+    rc = pcie_chassis_add_slot(s);
+    if (rc < 0) {
+        error_setg(errp, "Can't add chassis slot, error %d", rc);
+        goto err_pcie_cap;
+    }
+
+    rc = pcie_aer_init(d, PCI_ERR_VER, rpc->aer_offset,
+                       PCI_ERR_SIZEOF, &local_err);
+    if (rc < 0) {
+        error_propagate(errp, local_err);
+        goto err;
+    }
+    pcie_aer_root_init(d);
+    rp_aer_vector_update(d);
+
+    return;
+
+err:
+    pcie_chassis_del_slot(s);
+err_pcie_cap:
+    pcie_cap_exit(d);
+err_int:
+    if (rpc->interrupts_uninit) {
+        rpc->interrupts_uninit(d);
+    }
+err_bridge:
+    pci_bridge_exitfn(d);
+}
+
+static void rp_exit(PCIDevice *d)
+{
+    PCIERootPortClass *rpc = PCIE_ROOT_PORT_GET_CLASS(d);
+    PCIESlot *s = PCIE_SLOT(d);
+
+    pcie_aer_exit(d);
+    pcie_chassis_del_slot(s);
+    pcie_cap_exit(d);
+    if (rpc->interrupts_uninit) {
+        rpc->interrupts_uninit(d);
+    }
+    pci_bridge_exitfn(d);
+}
+
+static Property rp_props[] = {
+    DEFINE_PROP_BIT(COMPAT_PROP_PCP, PCIDevice, cap_present,
+                    QEMU_PCIE_SLTCAP_PCP_BITNR, true),
+    DEFINE_PROP_END_OF_LIST()
+};
+
+static void rp_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+    k->is_express = 1;
+    k->is_bridge = 1;
+    k->config_write = rp_write_config;
+    k->realize = rp_realize;
+    k->exit = rp_exit;
+    set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
+    dc->reset = rp_reset;
+    dc->props = rp_props;
+}
+
+static const TypeInfo rp_info = {
+    .name          = TYPE_PCIE_ROOT_PORT,
+    .parent        = TYPE_PCIE_SLOT,
+    .class_init    = rp_class_init,
+    .abstract      = true,
+    .class_size = sizeof(PCIERootPortClass),
+};
+
+static void rp_register_types(void)
+{
+    type_register_static(&rp_info);
+}
+
+type_init(rp_register_types)
diff --git a/hw/pci/msix.c b/hw/pci/msix.c
index ee1714d2cf..bb54e8b0ac 100644
--- a/hw/pci/msix.c
+++ b/hw/pci/msix.c
@@ -21,6 +21,7 @@
 #include "hw/pci/pci.h"
 #include "hw/xen/xen.h"
 #include "qemu/range.h"
+#include "qapi/error.h"
 
 #define MSIX_CAP_LENGTH 12
 
@@ -238,11 +239,31 @@ static void msix_mask_all(struct PCIDevice *dev, unsigned nentries)
     }
 }
 
-/* Initialize the MSI-X structures */
+/*
+ * Make PCI device @dev MSI-X capable
+ * @nentries is the max number of MSI-X vectors that the device support.
+ * @table_bar is the MemoryRegion that MSI-X table structure resides.
+ * @table_bar_nr is number of base address register corresponding to @table_bar.
+ * @table_offset indicates the offset that the MSI-X table structure starts with
+ * in @table_bar.
+ * @pba_bar is the MemoryRegion that the Pending Bit Array structure resides.
+ * @pba_bar_nr is number of base address register corresponding to @pba_bar.
+ * @pba_offset indicates the offset that the Pending Bit Array structure
+ * starts with in @pba_bar.
+ * Non-zero @cap_pos puts capability MSI-X at that offset in PCI config space.
+ * @errp is for returning errors.
+ *
+ * Return 0 on success; set @errp and return -errno on error:
+ * -ENOTSUP means lacking msi support for a msi-capable platform.
+ * -EINVAL means capability overlap, happens when @cap_pos is non-zero,
+ * also means a programming error, except device assignment, which can check
+ * if a real HW is broken.
+ */
 int msix_init(struct PCIDevice *dev, unsigned short nentries,
               MemoryRegion *table_bar, uint8_t table_bar_nr,
               unsigned table_offset, MemoryRegion *pba_bar,
-              uint8_t pba_bar_nr, unsigned pba_offset, uint8_t cap_pos)
+              uint8_t pba_bar_nr, unsigned pba_offset, uint8_t cap_pos,
+              Error **errp)
 {
     int cap;
     unsigned table_size, pba_size;
@@ -250,10 +271,12 @@ int msix_init(struct PCIDevice *dev, unsigned short nentries,
 
     /* Nothing to do if MSI is not supported by interrupt controller */
     if (!msi_nonbroken) {
+        error_setg(errp, "MSI-X is not supported by interrupt controller");
         return -ENOTSUP;
     }
 
     if (nentries < 1 || nentries > PCI_MSIX_FLAGS_QSIZE + 1) {
+        error_setg(errp, "The number of MSI-X vectors is invalid");
         return -EINVAL;
     }
 
@@ -266,10 +289,13 @@ int msix_init(struct PCIDevice *dev, unsigned short nentries,
         table_offset + table_size > memory_region_size(table_bar) ||
         pba_offset + pba_size > memory_region_size(pba_bar) ||
         (table_offset | pba_offset) & PCI_MSIX_FLAGS_BIRMASK) {
+        error_setg(errp, "table & pba overlap, or they don't fit in BARs,"
+                   " or don't align");
         return -EINVAL;
     }
 
-    cap = pci_add_capability(dev, PCI_CAP_ID_MSIX, cap_pos, MSIX_CAP_LENGTH);
+    cap = pci_add_capability2(dev, PCI_CAP_ID_MSIX,
+                              cap_pos, MSIX_CAP_LENGTH, errp);
     if (cap < 0) {
         return cap;
     }
@@ -306,7 +332,7 @@ int msix_init(struct PCIDevice *dev, unsigned short nentries,
 }
 
 int msix_init_exclusive_bar(PCIDevice *dev, unsigned short nentries,
-                            uint8_t bar_nr)
+                            uint8_t bar_nr, Error **errp)
 {
     int ret;
     char *name;
@@ -338,7 +364,7 @@ int msix_init_exclusive_bar(PCIDevice *dev, unsigned short nentries,
     ret = msix_init(dev, nentries, &dev->msix_exclusive_bar, bar_nr,
                     0, &dev->msix_exclusive_bar,
                     bar_nr, bar_pba_offset,
-                    0);
+                    0, errp);
     if (ret) {
         return ret;
     }
@@ -447,8 +473,10 @@ void msix_notify(PCIDevice *dev, unsigned vector)
 {
     MSIMessage msg;
 
-    if (vector >= dev->msix_entries_nr || !dev->msix_entry_used[vector])
+    if (vector >= dev->msix_entries_nr || !dev->msix_entry_used[vector]) {
         return;
+    }
+
     if (msix_is_masked(dev, vector)) {
         msix_set_pending(dev, vector);
         return;
@@ -483,8 +511,10 @@ void msix_reset(PCIDevice *dev)
 /* Mark vector as used. */
 int msix_vector_use(PCIDevice *dev, unsigned vector)
 {
-    if (vector >= dev->msix_entries_nr)
+    if (vector >= dev->msix_entries_nr) {
         return -EINVAL;
+    }
+
     dev->msix_entry_used[vector]++;
     return 0;
 }
diff --git a/hw/pci/pci.c b/hw/pci/pci.c
index 47ca3af69a..a563555e7d 100644
--- a/hw/pci/pci.c
+++ b/hw/pci/pci.c
@@ -2195,7 +2195,7 @@ static void pci_add_option_rom(PCIDevice *pdev, bool is_default_rom,
         snprintf(name, sizeof(name), "%s.rom", object_get_typename(OBJECT(pdev)));
     }
     pdev->has_rom = true;
-    memory_region_init_ram(&pdev->rom, OBJECT(pdev), name, size, &error_fatal);
+    memory_region_init_rom(&pdev->rom, OBJECT(pdev), name, size, &error_fatal);
     vmstate_register_ram(&pdev->rom, &pdev->qdev);
     ptr = memory_region_get_ram_ptr(&pdev->rom);
     load_image(path, ptr);
diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs
index 8025129377..001293423c 100644
--- a/hw/ppc/Makefile.objs
+++ b/hw/ppc/Makefile.objs
@@ -16,6 +16,8 @@ obj-y += ppc405_boards.o ppc4xx_devs.o ppc405_uc.o ppc440_bamboo.o
 obj-y += ppc4xx_pci.o
 # PReP
 obj-$(CONFIG_PREP) += prep.o
+obj-$(CONFIG_PREP) += prep_systemio.o
+obj-${CONFIG_RS6000_MC} += rs6000_mc.o
 # OldWorld PowerMac
 obj-$(CONFIG_MAC) += mac_oldworld.o
 # NewWorld PowerMac
diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c
index cf8b122afe..f7df2388c1 100644
--- a/hw/ppc/e500.c
+++ b/hw/ppc/e500.c
@@ -827,6 +827,12 @@ void ppce500_init(MachineState *machine, PPCE500Params *params)
         env = &cpu->env;
         cs = CPU(cpu);
 
+        if (env->mmu_model != POWERPC_MMU_BOOKE206) {
+            fprintf(stderr, "MMU model %i not supported by this machine.\n",
+                env->mmu_model);
+            exit(1);
+        }
+
         if (!firstenv) {
             firstenv = env;
         }
@@ -1049,27 +1055,18 @@ void ppce500_init(MachineState *machine, PPCE500Params *params)
     boot_info->dt_size = dt_size;
 }
 
-static int e500_ccsr_initfn(SysBusDevice *dev)
+static void e500_ccsr_initfn(Object *obj)
 {
-    PPCE500CCSRState *ccsr;
-
-    ccsr = CCSR(dev);
-    memory_region_init(&ccsr->ccsr_space, OBJECT(ccsr), "e500-ccsr",
+    PPCE500CCSRState *ccsr = CCSR(obj);
+    memory_region_init(&ccsr->ccsr_space, obj, "e500-ccsr",
                        MPC8544_CCSRBAR_SIZE);
-    return 0;
-}
-
-static void e500_ccsr_class_init(ObjectClass *klass, void *data)
-{
-    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
-    k->init = e500_ccsr_initfn;
 }
 
 static const TypeInfo e500_ccsr_info = {
     .name          = TYPE_CCSR,
     .parent        = TYPE_SYS_BUS_DEVICE,
     .instance_size = sizeof(PPCE500CCSRState),
-    .class_init    = e500_ccsr_class_init,
+    .instance_init = e500_ccsr_initfn,
 };
 
 static void e500_register_types(void)
diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index 83597fe92b..4fab5c0ae7 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -381,7 +381,7 @@ static void ppc_powernv_init(MachineState *machine)
 
     fw_size = load_image_targphys(fw_filename, FW_LOAD_ADDR, FW_MAX_SIZE);
     if (fw_size < 0) {
-        hw_error("qemu: could not load OPAL '%s'\n", fw_filename);
+        error_report("qemu: could not load OPAL '%s'", fw_filename);
         exit(1);
     }
     g_free(fw_filename);
@@ -393,8 +393,8 @@ static void ppc_powernv_init(MachineState *machine)
         kernel_size = load_image_targphys(machine->kernel_filename,
                                           KERNEL_LOAD_ADDR, 0x2000000);
         if (kernel_size < 0) {
-            hw_error("qemu: could not load kernel'%s'\n",
-                     machine->kernel_filename);
+            error_report("qemu: could not load kernel'%s'",
+                         machine->kernel_filename);
             exit(1);
         }
     }
diff --git a/hw/ppc/ppc.c b/hw/ppc/ppc.c
index 8945869009..d171e60b5c 100644
--- a/hw/ppc/ppc.c
+++ b/hw/ppc/ppc.c
@@ -847,9 +847,8 @@ static void cpu_ppc_set_tb_clk (void *opaque, uint32_t freq)
     cpu_ppc_store_purr(cpu, 0x0000000000000000ULL);
 }
 
-static void timebase_pre_save(void *opaque)
+static void timebase_save(PPCTimebase *tb)
 {
-    PPCTimebase *tb = opaque;
     uint64_t ticks = cpu_get_host_ticks();
     PowerPCCPU *first_ppc_cpu = POWERPC_CPU(first_cpu);
 
@@ -858,43 +857,30 @@ static void timebase_pre_save(void *opaque)
         return;
     }
 
+    /* not used anymore, we keep it for compatibility */
     tb->time_of_the_day_ns = qemu_clock_get_ns(QEMU_CLOCK_HOST);
     /*
-     * tb_offset is only expected to be changed by migration so
+     * tb_offset is only expected to be changed by QEMU so
      * there is no need to update it from KVM here
      */
     tb->guest_timebase = ticks + first_ppc_cpu->env.tb_env->tb_offset;
 }
 
-static int timebase_post_load(void *opaque, int version_id)
+static void timebase_load(PPCTimebase *tb)
 {
-    PPCTimebase *tb_remote = opaque;
     CPUState *cpu;
     PowerPCCPU *first_ppc_cpu = POWERPC_CPU(first_cpu);
-    int64_t tb_off_adj, tb_off, ns_diff;
-    int64_t migration_duration_ns, migration_duration_tb, guest_tb, host_ns;
+    int64_t tb_off_adj, tb_off;
     unsigned long freq;
 
     if (!first_ppc_cpu->env.tb_env) {
         error_report("No timebase object");
-        return -1;
+        return;
     }
 
     freq = first_ppc_cpu->env.tb_env->tb_freq;
-    /*
-     * Calculate timebase on the destination side of migration.
-     * The destination timebase must be not less than the source timebase.
-     * We try to adjust timebase by downtime if host clocks are not
-     * too much out of sync (1 second for now).
-     */
-    host_ns = qemu_clock_get_ns(QEMU_CLOCK_HOST);
-    ns_diff = MAX(0, host_ns - tb_remote->time_of_the_day_ns);
-    migration_duration_ns = MIN(NANOSECONDS_PER_SECOND, ns_diff);
-    migration_duration_tb = muldiv64(freq, migration_duration_ns,
-                                     NANOSECONDS_PER_SECOND);
-    guest_tb = tb_remote->guest_timebase + MIN(0, migration_duration_tb);
 
-    tb_off_adj = guest_tb - cpu_get_host_ticks();
+    tb_off_adj = tb->guest_timebase - cpu_get_host_ticks();
 
     tb_off = first_ppc_cpu->env.tb_env->tb_offset;
     trace_ppc_tb_adjust(tb_off, tb_off_adj, tb_off_adj - tb_off,
@@ -904,9 +890,44 @@ static int timebase_post_load(void *opaque, int version_id)
     CPU_FOREACH(cpu) {
         PowerPCCPU *pcpu = POWERPC_CPU(cpu);
         pcpu->env.tb_env->tb_offset = tb_off_adj;
+#if defined(CONFIG_KVM)
+        kvm_set_one_reg(cpu, KVM_REG_PPC_TB_OFFSET,
+                        &pcpu->env.tb_env->tb_offset);
+#endif
     }
+}
 
-    return 0;
+void cpu_ppc_clock_vm_state_change(void *opaque, int running,
+                                   RunState state)
+{
+    PPCTimebase *tb = opaque;
+
+    if (running) {
+        timebase_load(tb);
+    } else {
+        timebase_save(tb);
+    }
+}
+
+/*
+ * When migrating, read the clock just before migration,
+ * so that the guest clock counts during the events
+ * between:
+ *
+ *  * vm_stop()
+ *  *
+ *  * pre_save()
+ *
+ *  This reduces clock difference on migration from 5s
+ *  to 0.1s (when max_downtime == 5s), because sending the
+ *  final pages of memory (which happens between vm_stop()
+ *  and pre_save()) takes max_downtime.
+ */
+static void timebase_pre_save(void *opaque)
+{
+    PPCTimebase *tb = opaque;
+
+    timebase_save(tb);
 }
 
 const VMStateDescription vmstate_ppc_timebase = {
@@ -915,7 +936,6 @@ const VMStateDescription vmstate_ppc_timebase = {
     .minimum_version_id = 1,
     .minimum_version_id_old = 1,
     .pre_save = timebase_pre_save,
-    .post_load = timebase_post_load,
     .fields      = (VMStateField []) {
         VMSTATE_UINT64(guest_timebase, PPCTimebase),
         VMSTATE_INT64(time_of_the_day_ns, PPCTimebase),
@@ -950,13 +970,6 @@ clk_setup_cb cpu_ppc_tb_init (CPUPPCState *env, uint32_t freq)
 }
 
 /* Specific helpers for POWER & PowerPC 601 RTC */
-#if 0
-static clk_setup_cb cpu_ppc601_rtc_init (CPUPPCState *env)
-{
-    return cpu_ppc_tb_init(env, 7812500);
-}
-#endif
-
 void cpu_ppc601_store_rtcu (CPUPPCState *env, uint32_t value)
 {
     _cpu_ppc_store_tbu(env, value);
diff --git a/hw/ppc/ppc440_bamboo.c b/hw/ppc/ppc440_bamboo.c
index 5c535b18a2..9d997bf743 100644
--- a/hw/ppc/ppc440_bamboo.c
+++ b/hw/ppc/ppc440_bamboo.c
@@ -193,6 +193,12 @@ static void bamboo_init(MachineState *machine)
     }
     env = &cpu->env;
 
+    if (env->mmu_model != POWERPC_MMU_BOOKE) {
+        fprintf(stderr, "MMU model %i not supported by this machine.\n",
+            env->mmu_model);
+        exit(1);
+    }
+
     qemu_register_reset(main_cpu_reset, cpu);
     ppc_booke_timers_init(cpu, 400000000, 0);
     ppc_dcr_init(env, NULL, NULL);
diff --git a/hw/ppc/ppc_booke.c b/hw/ppc/ppc_booke.c
index ab8d026c32..60baffaf1d 100644
--- a/hw/ppc/ppc_booke.c
+++ b/hw/ppc/ppc_booke.c
@@ -198,8 +198,12 @@ static void booke_decr_cb(void *opaque)
     booke_update_irq(cpu);
 
     if (env->spr[SPR_BOOKE_TCR] & TCR_ARE) {
-        /* Auto Reload */
-        cpu_ppc_store_decr(env, env->spr[SPR_BOOKE_DECAR]);
+        /* Do not reload 0, it is already there. It would just trigger
+         * the timer again and lead to infinite loop */
+        if (env->spr[SPR_BOOKE_DECAR] != 0) {
+            /* Auto Reload */
+            cpu_ppc_store_decr(env, env->spr[SPR_BOOKE_DECAR]);
+        }
     }
 }
 
diff --git a/hw/ppc/ppce500_spin.c b/hw/ppc/ppce500_spin.c
index eb219abdff..69ca2d0e42 100644
--- a/hw/ppc/ppce500_spin.c
+++ b/hw/ppc/ppce500_spin.c
@@ -54,9 +54,9 @@ typedef struct SpinState {
     SpinInfo spin[MAX_CPUS];
 } SpinState;
 
-static void spin_reset(void *opaque)
+static void spin_reset(DeviceState *dev)
 {
-    SpinState *s = opaque;
+    SpinState *s = E500_SPIN(dev);
     int i;
 
     for (i = 0; i < MAX_CPUS; i++) {
@@ -174,30 +174,28 @@ static const MemoryRegionOps spin_rw_ops = {
     .endianness = DEVICE_BIG_ENDIAN,
 };
 
-static int ppce500_spin_initfn(SysBusDevice *dev)
+static void ppce500_spin_initfn(Object *obj)
 {
+    SysBusDevice *dev = SYS_BUS_DEVICE(obj);
     SpinState *s = E500_SPIN(dev);
 
-    memory_region_init_io(&s->iomem, OBJECT(s), &spin_rw_ops, s,
+    memory_region_init_io(&s->iomem, obj, &spin_rw_ops, s,
                           "e500 spin pv device", sizeof(SpinInfo) * MAX_CPUS);
     sysbus_init_mmio(dev, &s->iomem);
-
-    qemu_register_reset(spin_reset, s);
-
-    return 0;
 }
 
 static void ppce500_spin_class_init(ObjectClass *klass, void *data)
 {
-    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+    DeviceClass *dc = DEVICE_CLASS(klass);
 
-    k->init = ppce500_spin_initfn;
+    dc->reset = spin_reset;
 }
 
 static const TypeInfo ppce500_spin_info = {
     .name          = TYPE_E500_SPIN,
     .parent        = TYPE_SYS_BUS_DEVICE,
     .instance_size = sizeof(SpinState),
+    .instance_init = ppce500_spin_initfn,
     .class_init    = ppce500_spin_class_init,
 };
 
diff --git a/hw/ppc/prep.c b/hw/ppc/prep.c
index 054af1e8b4..ca7959c126 100644
--- a/hw/ppc/prep.c
+++ b/hw/ppc/prep.c
@@ -2,6 +2,7 @@
  * QEMU PPC PREP hardware System Emulator
  *
  * Copyright (c) 2003-2007 Jocelyn Mayer
+ * Copyright (c) 2017 Hervé Poussineau
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to deal
@@ -43,17 +44,21 @@
 #include "hw/isa/pc87312.h"
 #include "sysemu/block-backend.h"
 #include "sysemu/arch_init.h"
+#include "sysemu/kvm.h"
 #include "sysemu/qtest.h"
 #include "exec/address-spaces.h"
 #include "trace.h"
 #include "elf.h"
 #include "qemu/cutils.h"
+#include "kvm_ppc.h"
 
 /* SMP is not enabled, for now */
 #define MAX_CPUS 1
 
 #define MAX_IDE_BUS 2
 
+#define CFG_ADDR 0xf0000510
+
 #define BIOS_SIZE (1024 * 1024)
 #define BIOS_FILENAME "ppc_rom.bin"
 #define KERNEL_LOAD_ADDR 0x01000000
@@ -316,6 +321,12 @@ static uint32_t PREP_io_800_readb (void *opaque, uint32_t addr)
 
 #define NVRAM_SIZE        0x2000
 
+static void fw_cfg_boot_set(void *opaque, const char *boot_device,
+                            Error **errp)
+{
+    fw_cfg_modify_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]);
+}
+
 static void ppc_prep_reset(void *opaque)
 {
     PowerPCCPU *cpu = opaque;
@@ -339,13 +350,13 @@ static PortioList prep_port_list;
 /* NVRAM helpers */
 static inline uint32_t nvram_read(Nvram *nvram, uint32_t addr)
 {
-    NvramClass *k = NVRAM_GET_CLASS(sysctrl->nvram);
+    NvramClass *k = NVRAM_GET_CLASS(nvram);
     return (k->read)(nvram, addr);
 }
 
 static inline void nvram_write(Nvram *nvram, uint32_t addr, uint32_t val)
 {
-    NvramClass *k = NVRAM_GET_CLASS(sysctrl->nvram);
+    NvramClass *k = NVRAM_GET_CLASS(nvram);
     (k->write)(nvram, addr, val);
 }
 
@@ -677,4 +688,223 @@ static void prep_machine_init(MachineClass *mc)
     mc->default_boot_order = "cad";
 }
 
+static int prep_set_cmos_checksum(DeviceState *dev, void *opaque)
+{
+    uint16_t checksum = *(uint16_t *)opaque;
+    ISADevice *rtc;
+
+    if (object_dynamic_cast(OBJECT(dev), "mc146818rtc")) {
+        rtc = ISA_DEVICE(dev);
+        rtc_set_memory(rtc, 0x2e, checksum & 0xff);
+        rtc_set_memory(rtc, 0x3e, checksum & 0xff);
+        rtc_set_memory(rtc, 0x2f, checksum >> 8);
+        rtc_set_memory(rtc, 0x3f, checksum >> 8);
+    }
+    return 0;
+}
+
+static void ibm_40p_init(MachineState *machine)
+{
+    CPUPPCState *env = NULL;
+    uint16_t cmos_checksum;
+    PowerPCCPU *cpu;
+    DeviceState *dev;
+    SysBusDevice *pcihost;
+    Nvram *m48t59 = NULL;
+    PCIBus *pci_bus;
+    ISABus *isa_bus;
+    void *fw_cfg;
+    int i;
+    uint32_t kernel_base = 0, initrd_base = 0;
+    long kernel_size = 0, initrd_size = 0;
+    char boot_device;
+
+    /* init CPU */
+    if (!machine->cpu_model) {
+        machine->cpu_model = "604";
+    }
+    cpu = cpu_ppc_init(machine->cpu_model);
+    if (!cpu) {
+        error_report("could not initialize CPU '%s'",
+                     machine->cpu_model);
+        exit(1);
+    }
+    env = &cpu->env;
+    if (PPC_INPUT(env) != PPC_FLAGS_INPUT_6xx) {
+        error_report("only 6xx bus is supported on this machine");
+        exit(1);
+    }
+
+    if (env->flags & POWERPC_FLAG_RTC_CLK) {
+        /* POWER / PowerPC 601 RTC clock frequency is 7.8125 MHz */
+        cpu_ppc_tb_init(env, 7812500UL);
+    } else {
+        /* Set time-base frequency to 100 Mhz */
+        cpu_ppc_tb_init(env, 100UL * 1000UL * 1000UL);
+    }
+    qemu_register_reset(ppc_prep_reset, cpu);
+
+    /* PCI host */
+    dev = qdev_create(NULL, "raven-pcihost");
+    if (!bios_name) {
+        bios_name = BIOS_FILENAME;
+    }
+    qdev_prop_set_string(dev, "bios-name", bios_name);
+    qdev_prop_set_uint32(dev, "elf-machine", PPC_ELF_MACHINE);
+    pcihost = SYS_BUS_DEVICE(dev);
+    object_property_add_child(qdev_get_machine(), "raven", OBJECT(dev), NULL);
+    qdev_init_nofail(dev);
+    pci_bus = PCI_BUS(qdev_get_child_bus(dev, "pci.0"));
+    if (!pci_bus) {
+        error_report("could not create PCI host controller");
+        exit(1);
+    }
+
+    /* PCI -> ISA bridge */
+    dev = DEVICE(pci_create_simple(pci_bus, PCI_DEVFN(11, 0), "i82378"));
+    qdev_connect_gpio_out(dev, 0,
+                          cpu->env.irq_inputs[PPC6xx_INPUT_INT]);
+    sysbus_connect_irq(pcihost, 0, qdev_get_gpio_in(dev, 15));
+    sysbus_connect_irq(pcihost, 1, qdev_get_gpio_in(dev, 13));
+    sysbus_connect_irq(pcihost, 2, qdev_get_gpio_in(dev, 15));
+    sysbus_connect_irq(pcihost, 3, qdev_get_gpio_in(dev, 13));
+    isa_bus = ISA_BUS(qdev_get_child_bus(dev, "isa.0"));
+
+    /* Memory controller */
+    dev = DEVICE(isa_create(isa_bus, "rs6000-mc"));
+    qdev_prop_set_uint32(dev, "ram-size", machine->ram_size);
+    qdev_init_nofail(dev);
+
+    /* initialize CMOS checksums */
+    cmos_checksum = 0x6aa9;
+    qbus_walk_children(BUS(isa_bus), prep_set_cmos_checksum, NULL, NULL, NULL,
+                       &cmos_checksum);
+
+    /* initialize audio subsystem */
+    audio_init();
+
+    /* add some more devices */
+    if (defaults_enabled()) {
+        isa_create_simple(isa_bus, "i8042");
+        m48t59 = NVRAM(isa_create_simple(isa_bus, "isa-m48t59"));
+
+        dev = DEVICE(isa_create(isa_bus, "cs4231a"));
+        qdev_prop_set_uint32(dev, "iobase", 0x830);
+        qdev_prop_set_uint32(dev, "irq", 10);
+        qdev_init_nofail(dev);
+
+        dev = DEVICE(isa_create(isa_bus, "pc87312"));
+        qdev_prop_set_uint32(dev, "config", 12);
+        qdev_init_nofail(dev);
+
+        dev = DEVICE(isa_create(isa_bus, "prep-systemio"));
+        qdev_prop_set_uint32(dev, "ibm-planar-id", 0xfc);
+        qdev_prop_set_uint32(dev, "equipment", 0xc0);
+        qdev_init_nofail(dev);
+
+        pci_create_simple(pci_bus, PCI_DEVFN(1, 0), "lsi53c810");
+
+        /* XXX: s3-trio at PCI_DEVFN(2, 0) */
+        pci_vga_init(pci_bus);
+
+        for (i = 0; i < nb_nics; i++) {
+            pci_nic_init_nofail(&nd_table[i], pci_bus, "pcnet",
+                                i == 0 ? "3" : NULL);
+        }
+    }
+
+    /* Prepare firmware configuration for OpenBIOS */
+    fw_cfg = fw_cfg_init_mem(CFG_ADDR, CFG_ADDR + 2);
+
+    if (machine->kernel_filename) {
+        /* load kernel */
+        kernel_base = KERNEL_LOAD_ADDR;
+        kernel_size = load_image_targphys(machine->kernel_filename,
+                                          kernel_base,
+                                          machine->ram_size - kernel_base);
+        if (kernel_size < 0) {
+            error_report("could not load kernel '%s'",
+                         machine->kernel_filename);
+            exit(1);
+        }
+        fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, kernel_base);
+        fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_SIZE, kernel_size);
+        /* load initrd */
+        if (machine->initrd_filename) {
+            initrd_base = INITRD_LOAD_ADDR;
+            initrd_size = load_image_targphys(machine->initrd_filename,
+                                              initrd_base,
+                                              machine->ram_size - initrd_base);
+            if (initrd_size < 0) {
+                error_report("could not load initial ram disk '%s'",
+                             machine->initrd_filename);
+                exit(1);
+            }
+            fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_ADDR, initrd_base);
+            fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_SIZE, initrd_size);
+        }
+        if (machine->kernel_cmdline && *machine->kernel_cmdline) {
+            fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_CMDLINE, CMDLINE_ADDR);
+            pstrcpy_targphys("cmdline", CMDLINE_ADDR, TARGET_PAGE_SIZE,
+                             machine->kernel_cmdline);
+            fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA,
+                              machine->kernel_cmdline);
+            fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE,
+                           strlen(machine->kernel_cmdline) + 1);
+        }
+        boot_device = 'm';
+    } else {
+        boot_device = machine->boot_order[0];
+    }
+
+    fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, (uint16_t)max_cpus);
+    fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)machine->ram_size);
+    fw_cfg_add_i16(fw_cfg, FW_CFG_MACHINE_ID, ARCH_PREP);
+
+    fw_cfg_add_i16(fw_cfg, FW_CFG_PPC_WIDTH, graphic_width);
+    fw_cfg_add_i16(fw_cfg, FW_CFG_PPC_HEIGHT, graphic_height);
+    fw_cfg_add_i16(fw_cfg, FW_CFG_PPC_DEPTH, graphic_depth);
+
+    fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_IS_KVM, kvm_enabled());
+    if (kvm_enabled()) {
+#ifdef CONFIG_KVM
+        uint8_t *hypercall;
+
+        fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_TBFREQ, kvmppc_get_tbfreq());
+        hypercall = g_malloc(16);
+        kvmppc_get_hypercall(env, hypercall, 16);
+        fw_cfg_add_bytes(fw_cfg, FW_CFG_PPC_KVM_HC, hypercall, 16);
+        fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_KVM_PID, getpid());
+#endif
+    } else {
+        fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_TBFREQ, NANOSECONDS_PER_SECOND);
+    }
+    fw_cfg_add_i16(fw_cfg, FW_CFG_BOOT_DEVICE, boot_device);
+    qemu_register_boot_set(fw_cfg_boot_set, fw_cfg);
+
+    /* Prepare firmware configuration for Open Hack'Ware */
+    if (m48t59) {
+        PPC_NVRAM_set_params(m48t59, NVRAM_SIZE, "PREP", ram_size,
+                             boot_device,
+                             kernel_base, kernel_size,
+                             machine->kernel_cmdline,
+                             initrd_base, initrd_size,
+                             /* XXX: need an option to load a NVRAM image */
+                             0,
+                             graphic_width, graphic_height, graphic_depth);
+    }
+}
+
+static void ibm_40p_machine_init(MachineClass *mc)
+{
+    mc->desc = "IBM RS/6000 7020 (40p)",
+    mc->init = ibm_40p_init;
+    mc->max_cpus = 1;
+    mc->pci_allow_0_address = true;
+    mc->default_ram_size = 128 * M_BYTE;
+    mc->block_default_type = IF_SCSI;
+    mc->default_boot_order = "c";
+}
+
+DEFINE_MACHINE("40p", ibm_40p_machine_init)
 DEFINE_MACHINE("prep", prep_machine_init)
diff --git a/hw/ppc/prep_systemio.c b/hw/ppc/prep_systemio.c
new file mode 100644
index 0000000000..50893ec529
--- /dev/null
+++ b/hw/ppc/prep_systemio.c
@@ -0,0 +1,303 @@
+/*
+ * QEMU PReP System I/O emulation
+ *
+ * Copyright (c) 2017 Hervé Poussineau
+ *
+ * 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/osdep.h"
+#include "hw/isa/isa.h"
+#include "exec/address-spaces.h"
+#include "qemu/error-report.h" /* for error_report() */
+#include "sysemu/sysemu.h" /* for vm_stop() */
+#include "cpu.h"
+#include "trace.h"
+
+#define TYPE_PREP_SYSTEMIO "prep-systemio"
+#define PREP_SYSTEMIO(obj) \
+    OBJECT_CHECK(PrepSystemIoState, (obj), TYPE_PREP_SYSTEMIO)
+
+/* Bit as defined in PowerPC Reference Plaform v1.1, sect. 6.1.5, p. 132 */
+#define PREP_BIT(n) (1 << (7 - (n)))
+
+typedef struct PrepSystemIoState {
+    ISADevice parent_obj;
+    MemoryRegion ppc_parity_mem;
+
+    qemu_irq non_contiguous_io_map_irq;
+    uint8_t sreset; /* 0x0092 */
+    uint8_t equipment; /* 0x080c */
+    uint8_t system_control; /* 0x081c */
+    uint8_t iomap_type; /* 0x0850 */
+    uint8_t ibm_planar_id; /* 0x0852 */
+    qemu_irq softreset_irq;
+    PortioList portio;
+} PrepSystemIoState;
+
+/* PORT 0092 -- Special Port 92 (Read/Write) */
+
+enum {
+    PORT0092_SOFTRESET  = PREP_BIT(7),
+    PORT0092_LE_MODE    = PREP_BIT(6),
+};
+
+static void prep_port0092_write(void *opaque, uint32_t addr, uint32_t val)
+{
+    PrepSystemIoState *s = opaque;
+
+    trace_prep_systemio_write(addr, val);
+
+    s->sreset = val & PORT0092_SOFTRESET;
+    qemu_set_irq(s->softreset_irq, s->sreset);
+
+    if ((val & PORT0092_LE_MODE) != 0) {
+        /* XXX Not supported yet */
+        error_report("little-endian mode not supported");
+        vm_stop(RUN_STATE_PAUSED);
+    } else {
+        /* Nothing to do */
+    }
+}
+
+static uint32_t prep_port0092_read(void *opaque, uint32_t addr)
+{
+    PrepSystemIoState *s = opaque;
+    trace_prep_systemio_read(addr, s->sreset);
+    return s->sreset;
+}
+
+/* PORT 0808 -- Hardfile Light Register (Write Only) */
+
+enum {
+    PORT0808_HARDFILE_LIGHT_ON  = PREP_BIT(7),
+};
+
+static void prep_port0808_write(void *opaque, uint32_t addr, uint32_t val)
+{
+    trace_prep_systemio_write(addr, val);
+}
+
+/* PORT 0810 -- Password Protect 1 Register (Write Only) */
+
+/* reset by port 0x4D in the SIO */
+static void prep_port0810_write(void *opaque, uint32_t addr, uint32_t val)
+{
+    trace_prep_systemio_write(addr, val);
+}
+
+/* PORT 0812 -- Password Protect 2 Register (Write Only) */
+
+/* reset by port 0x4D in the SIO */
+static void prep_port0812_write(void *opaque, uint32_t addr, uint32_t val)
+{
+    trace_prep_systemio_write(addr, val);
+}
+
+/* PORT 0814 -- L2 Invalidate Register (Write Only) */
+
+static void prep_port0814_write(void *opaque, uint32_t addr, uint32_t val)
+{
+    trace_prep_systemio_write(addr, val);
+}
+
+/* PORT 0818 -- Reserved for Keylock (Read Only) */
+
+enum {
+    PORT0818_KEYLOCK_SIGNAL_HIGH    = PREP_BIT(7),
+};
+
+static uint32_t prep_port0818_read(void *opaque, uint32_t addr)
+{
+    uint32_t val = 0;
+    trace_prep_systemio_read(addr, val);
+    return val;
+}
+
+/* PORT 080C -- Equipment */
+
+enum {
+    PORT080C_SCSIFUSE               = PREP_BIT(1),
+    PORT080C_L2_COPYBACK            = PREP_BIT(4),
+    PORT080C_L2_256                 = PREP_BIT(5),
+    PORT080C_UPGRADE_CPU            = PREP_BIT(6),
+    PORT080C_L2                     = PREP_BIT(7),
+};
+
+static uint32_t prep_port080c_read(void *opaque, uint32_t addr)
+{
+    PrepSystemIoState *s = opaque;
+    trace_prep_systemio_read(addr, s->equipment);
+    return s->equipment;
+}
+
+/* PORT 081C -- System Control Register (Read/Write) */
+
+enum {
+    PORT081C_FLOPPY_MOTOR_INHIBIT   = PREP_BIT(3),
+    PORT081C_MASK_TEA               = PREP_BIT(2),
+    PORT081C_L2_UPDATE_INHIBIT      = PREP_BIT(1),
+    PORT081C_L2_CACHEMISS_INHIBIT   = PREP_BIT(0),
+};
+
+static void prep_port081c_write(void *opaque, uint32_t addr, uint32_t val)
+{
+    static const uint8_t mask = PORT081C_FLOPPY_MOTOR_INHIBIT |
+                                PORT081C_MASK_TEA |
+                                PORT081C_L2_UPDATE_INHIBIT |
+                                PORT081C_L2_CACHEMISS_INHIBIT;
+    PrepSystemIoState *s = opaque;
+    trace_prep_systemio_write(addr, val);
+    s->system_control = val & mask;
+}
+
+static uint32_t prep_port081c_read(void *opaque, uint32_t addr)
+{
+    PrepSystemIoState *s = opaque;
+    trace_prep_systemio_read(addr, s->system_control);
+    return s->system_control;
+}
+
+/* System Board Identification */
+
+static uint32_t prep_port0852_read(void *opaque, uint32_t addr)
+{
+    PrepSystemIoState *s = opaque;
+    trace_prep_systemio_read(addr, s->ibm_planar_id);
+    return s->ibm_planar_id;
+}
+
+/* PORT 0850 -- I/O Map Type Register (Read/Write) */
+
+enum {
+    PORT0850_IOMAP_NONCONTIGUOUS    = PREP_BIT(7),
+};
+
+static uint32_t prep_port0850_read(void *opaque, uint32_t addr)
+{
+    PrepSystemIoState *s = opaque;
+    trace_prep_systemio_read(addr, s->iomap_type);
+    return s->iomap_type;
+}
+
+static void prep_port0850_write(void *opaque, uint32_t addr, uint32_t val)
+{
+    PrepSystemIoState *s = opaque;
+
+    trace_prep_systemio_write(addr, val);
+    qemu_set_irq(s->non_contiguous_io_map_irq,
+                 val & PORT0850_IOMAP_NONCONTIGUOUS);
+    s->iomap_type = val & PORT0850_IOMAP_NONCONTIGUOUS;
+}
+
+static const MemoryRegionPortio ppc_io800_port_list[] = {
+    { 0x092, 1, 1, .read = prep_port0092_read,
+                   .write = prep_port0092_write, },
+    { 0x808, 1, 1, .write = prep_port0808_write, },
+    { 0x80c, 1, 1, .read = prep_port080c_read, },
+    { 0x810, 1, 1, .write = prep_port0810_write, },
+    { 0x812, 1, 1, .write = prep_port0812_write, },
+    { 0x814, 1, 1, .write = prep_port0814_write, },
+    { 0x818, 1, 1, .read = prep_port0818_read },
+    { 0x81c, 1, 1, .read = prep_port081c_read,
+                   .write = prep_port081c_write, },
+    { 0x850, 1, 1, .read = prep_port0850_read,
+                   .write = prep_port0850_write, },
+    { 0x852, 1, 1, .read = prep_port0852_read, },
+    PORTIO_END_OF_LIST()
+};
+
+static uint64_t ppc_parity_error_readl(void *opaque, hwaddr addr,
+                                       unsigned int size)
+{
+    uint32_t val = 0;
+    trace_prep_systemio_read((unsigned int)addr, val);
+    return val;
+}
+
+static const MemoryRegionOps ppc_parity_error_ops = {
+    .read = ppc_parity_error_readl,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    },
+};
+
+static void prep_systemio_realize(DeviceState *dev, Error **errp)
+{
+    ISADevice *isa = ISA_DEVICE(dev);
+    PrepSystemIoState *s = PREP_SYSTEMIO(dev);
+    PowerPCCPU *cpu;
+
+    qdev_init_gpio_out(dev, &s->non_contiguous_io_map_irq, 1);
+    s->iomap_type = PORT0850_IOMAP_NONCONTIGUOUS;
+    qemu_set_irq(s->non_contiguous_io_map_irq,
+                 s->iomap_type & PORT0850_IOMAP_NONCONTIGUOUS);
+    cpu = POWERPC_CPU(first_cpu);
+    s->softreset_irq = cpu->env.irq_inputs[PPC6xx_INPUT_HRESET];
+
+    isa_register_portio_list(isa, &s->portio, 0x0, ppc_io800_port_list, s,
+                             "systemio800");
+
+    memory_region_init_io(&s->ppc_parity_mem, OBJECT(dev),
+                          &ppc_parity_error_ops, s, "ppc-parity", 0x4);
+    memory_region_add_subregion(get_system_memory(), 0xbfffeff0,
+                                &s->ppc_parity_mem);
+}
+
+static const VMStateDescription vmstate_prep_systemio = {
+    .name = "prep_systemio",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT8(sreset, PrepSystemIoState),
+        VMSTATE_UINT8(system_control, PrepSystemIoState),
+        VMSTATE_UINT8(iomap_type, PrepSystemIoState),
+        VMSTATE_END_OF_LIST()
+    },
+};
+
+static Property prep_systemio_properties[] = {
+    DEFINE_PROP_UINT8("ibm-planar-id", PrepSystemIoState, ibm_planar_id, 0),
+    DEFINE_PROP_UINT8("equipment", PrepSystemIoState, equipment, 0),
+    DEFINE_PROP_END_OF_LIST()
+};
+
+static void prep_systemio_class_initfn(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->realize = prep_systemio_realize;
+    dc->vmsd = &vmstate_prep_systemio;
+    dc->props = prep_systemio_properties;
+}
+
+static TypeInfo prep_systemio800_info = {
+    .name          = TYPE_PREP_SYSTEMIO,
+    .parent        = TYPE_ISA_DEVICE,
+    .instance_size = sizeof(PrepSystemIoState),
+    .class_init    = prep_systemio_class_initfn,
+};
+
+static void prep_systemio_register_types(void)
+{
+    type_register_static(&prep_systemio800_info);
+}
+
+type_init(prep_systemio_register_types)
diff --git a/hw/ppc/rs6000_mc.c b/hw/ppc/rs6000_mc.c
new file mode 100644
index 0000000000..b6135650bd
--- /dev/null
+++ b/hw/ppc/rs6000_mc.c
@@ -0,0 +1,232 @@
+/*
+ * QEMU RS/6000 memory controller
+ *
+ * Copyright (c) 2017 Hervé Poussineau
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) version 3 or any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/isa/isa.h"
+#include "exec/address-spaces.h"
+#include "hw/boards.h"
+#include "qapi/error.h"
+#include "trace.h"
+
+#define TYPE_RS6000MC "rs6000-mc"
+#define RS6000MC_DEVICE(obj) \
+    OBJECT_CHECK(RS6000MCState, (obj), TYPE_RS6000MC)
+
+typedef struct RS6000MCState {
+    ISADevice parent_obj;
+    /* see US patent 5,684,979 for details (expired 2001-11-04) */
+    uint32_t ram_size;
+    bool autoconfigure;
+    MemoryRegion simm[6];
+    unsigned int simm_size[6];
+    uint32_t end_address[8];
+    uint8_t port0820_index;
+    PortioList portio;
+} RS6000MCState;
+
+/* P0RT 0803 -- SIMM ID Register (32/8 MB) (Read Only) */
+
+static uint32_t rs6000mc_port0803_read(void *opaque, uint32_t addr)
+{
+    RS6000MCState *s = opaque;
+    uint32_t val = 0;
+    int socket;
+
+    /* (1 << socket) indicates 32 MB SIMM at given socket */
+    for (socket = 0; socket < 6; socket++) {
+        if (s->simm_size[socket] == 32) {
+            val |= (1 << socket);
+        }
+    }
+
+    trace_rs6000mc_id_read(addr, val);
+    return val;
+}
+
+/* PORT 0804 -- SIMM Presence Register (Read Only) */
+
+static uint32_t rs6000mc_port0804_read(void *opaque, uint32_t addr)
+{
+    RS6000MCState *s = opaque;
+    uint32_t val = 0xff;
+    int socket;
+
+    /* (1 << socket) indicates SIMM absence at given socket */
+    for (socket = 0; socket < 6; socket++) {
+        if (s->simm_size[socket]) {
+            val &= ~(1 << socket);
+        }
+    }
+    s->port0820_index = 0;
+
+    trace_rs6000mc_presence_read(addr, val);
+    return val;
+}
+
+/* Memory Controller Size Programming Register */
+
+static uint32_t rs6000mc_port0820_read(void *opaque, uint32_t addr)
+{
+    RS6000MCState *s = opaque;
+    uint32_t val = s->end_address[s->port0820_index] & 0x1f;
+    s->port0820_index = (s->port0820_index + 1) & 7;
+    trace_rs6000mc_size_read(addr, val);
+    return val;
+}
+
+static void rs6000mc_port0820_write(void *opaque, uint32_t addr, uint32_t val)
+{
+    RS6000MCState *s = opaque;
+    uint8_t socket = val >> 5;
+    uint32_t end_address = val & 0x1f;
+
+    trace_rs6000mc_size_write(addr, val);
+    s->end_address[socket] = end_address;
+    if (socket > 0 && socket < 7) {
+        if (s->simm_size[socket - 1]) {
+            uint32_t size;
+            uint32_t start_address = 0;
+            if (socket > 1) {
+                start_address = s->end_address[socket - 1];
+            }
+
+            size = end_address - start_address;
+            memory_region_set_enabled(&s->simm[socket - 1], size != 0);
+            memory_region_set_address(&s->simm[socket - 1],
+                                      start_address * 8 * 1024 * 1024);
+        }
+    }
+}
+
+/* Read Memory Parity Error */
+
+enum {
+    PORT0841_NO_ERROR_DETECTED = 0x01,
+};
+
+static uint32_t rs6000mc_port0841_read(void *opaque, uint32_t addr)
+{
+    uint32_t val = PORT0841_NO_ERROR_DETECTED;
+    trace_rs6000mc_parity_read(addr, val);
+    return val;
+}
+
+static const MemoryRegionPortio rs6000mc_port_list[] = {
+    { 0x803, 1, 1, .read = rs6000mc_port0803_read },
+    { 0x804, 1, 1, .read = rs6000mc_port0804_read },
+    { 0x820, 1, 1, .read = rs6000mc_port0820_read,
+                   .write = rs6000mc_port0820_write, },
+    { 0x841, 1, 1, .read = rs6000mc_port0841_read },
+    PORTIO_END_OF_LIST()
+};
+
+static void rs6000mc_realize(DeviceState *dev, Error **errp)
+{
+    RS6000MCState *s = RS6000MC_DEVICE(dev);
+    int socket = 0;
+    unsigned int ram_size = s->ram_size / (1024 * 1024);
+
+    while (socket < 6) {
+        if (ram_size >= 64) {
+            s->simm_size[socket] = 32;
+            s->simm_size[socket + 1] = 32;
+            ram_size -= 64;
+        } else if (ram_size >= 16) {
+            s->simm_size[socket] = 8;
+            s->simm_size[socket + 1] = 8;
+            ram_size -= 16;
+        } else {
+            /* Not enough memory */
+            break;
+        }
+        socket += 2;
+    }
+
+    for (socket = 0; socket < 6; socket++) {
+        if (s->simm_size[socket]) {
+            char name[] = "simm.?";
+            name[5] = socket + '0';
+            memory_region_allocate_system_memory(&s->simm[socket], OBJECT(dev),
+                                                 name, s->simm_size[socket]
+                                                 * 1024 * 1024);
+            memory_region_add_subregion_overlap(get_system_memory(), 0,
+                                                &s->simm[socket], socket);
+        }
+    }
+    if (ram_size) {
+        /* unable to push all requested RAM in SIMMs */
+        error_setg(errp, "RAM size incompatible with this board. "
+                   "Try again with something else, like %d MB",
+                   s->ram_size / 1024 / 1024 - ram_size);
+        return;
+    }
+
+    if (s->autoconfigure) {
+        uint32_t start_address = 0;
+        for (socket = 0; socket < 6; socket++) {
+            if (s->simm_size[socket]) {
+                memory_region_set_enabled(&s->simm[socket], true);
+                memory_region_set_address(&s->simm[socket], start_address);
+                start_address += memory_region_size(&s->simm[socket]);
+            }
+        }
+    }
+
+    isa_register_portio_list(ISA_DEVICE(dev), &s->portio, 0x0,
+                             rs6000mc_port_list, s, "rs6000mc");
+}
+
+static const VMStateDescription vmstate_rs6000mc = {
+    .name = "rs6000-mc",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT8(port0820_index, RS6000MCState),
+        VMSTATE_END_OF_LIST()
+    },
+};
+
+static Property rs6000mc_properties[] = {
+    DEFINE_PROP_UINT32("ram-size", RS6000MCState, ram_size, 0),
+    DEFINE_PROP_BOOL("auto-configure", RS6000MCState, autoconfigure, true),
+    DEFINE_PROP_END_OF_LIST()
+};
+
+static void rs6000mc_class_initfn(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->realize = rs6000mc_realize;
+    dc->vmsd = &vmstate_rs6000mc;
+    dc->props = rs6000mc_properties;
+}
+
+static const TypeInfo rs6000mc_info = {
+    .name          = TYPE_RS6000MC,
+    .parent        = TYPE_ISA_DEVICE,
+    .instance_size = sizeof(RS6000MCState),
+    .class_init    = rs6000mc_class_initfn,
+};
+
+static void rs6000mc_types(void)
+{
+    type_register_static(&rs6000mc_info);
+}
+
+type_init(rs6000mc_types)
diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c
index a642e663d4..e465d7ac98 100644
--- a/hw/ppc/spapr.c
+++ b/hw/ppc/spapr.c
@@ -148,8 +148,8 @@ static int spapr_fixup_cpu_smt_dt(void *fdt, int offset, PowerPCCPU *cpu,
     uint32_t gservers_prop[smt_threads * 2];
     int index = ppc_get_vcpu_dt_id(cpu);
 
-    if (cpu->cpu_version) {
-        ret = fdt_setprop_cell(fdt, offset, "cpu-version", cpu->cpu_version);
+    if (cpu->compat_pvr) {
+        ret = fdt_setprop_cell(fdt, offset, "cpu-version", cpu->compat_pvr);
         if (ret < 0) {
             return ret;
         }
@@ -206,6 +206,7 @@ static int spapr_fixup_cpu_dt(void *fdt, sPAPRMachineState *spapr)
         PowerPCCPU *cpu = POWERPC_CPU(cs);
         DeviceClass *dc = DEVICE_GET_CLASS(cs);
         int index = ppc_get_vcpu_dt_id(cpu);
+        int compat_smt = MIN(smp_threads, ppc_compat_max_threads(cpu));
 
         if ((index % smt) != 0) {
             continue;
@@ -240,8 +241,7 @@ static int spapr_fixup_cpu_dt(void *fdt, sPAPRMachineState *spapr)
             return ret;
         }
 
-        ret = spapr_fixup_cpu_smt_dt(fdt, offset, cpu,
-                                     ppc_get_compat_smt_threads(cpu));
+        ret = spapr_fixup_cpu_smt_dt(fdt, offset, cpu, compat_smt);
         if (ret < 0) {
             return ret;
         }
@@ -407,6 +407,7 @@ static void spapr_populate_cpu_dt(CPUState *cs, void *fdt, int offset,
     size_t page_sizes_prop_size;
     uint32_t vcpus_per_socket = smp_threads * smp_cores;
     uint32_t pft_size_prop[] = {0, cpu_to_be32(spapr->htab_shift)};
+    int compat_smt = MIN(smp_threads, ppc_compat_max_threads(cpu));
     sPAPRDRConnector *drc;
     sPAPRDRConnectorClass *drck;
     int drc_index;
@@ -494,8 +495,7 @@ static void spapr_populate_cpu_dt(CPUState *cs, void *fdt, int offset,
 
     _FDT(spapr_fixup_cpu_numa_dt(fdt, offset, cs));
 
-    _FDT(spapr_fixup_cpu_smt_dt(fdt, offset, cpu,
-                                ppc_get_compat_smt_threads(cpu)));
+    _FDT(spapr_fixup_cpu_smt_dt(fdt, offset, cpu, compat_smt));
 }
 
 static void spapr_populate_cpus_dt_node(void *fdt, sPAPRMachineState *spapr)
@@ -685,7 +685,6 @@ out:
 
 int spapr_h_cas_compose_response(sPAPRMachineState *spapr,
                                  target_ulong addr, target_ulong size,
-                                 bool cpu_update,
                                  sPAPROptionVector *ov5_updates)
 {
     void *fdt, *fdt_skel;
@@ -704,9 +703,7 @@ int spapr_h_cas_compose_response(sPAPRMachineState *spapr,
     g_free(fdt_skel);
 
     /* Fixup cpu nodes */
-    if (cpu_update) {
-        _FDT((spapr_fixup_cpu_dt(fdt, spapr)));
-    }
+    _FDT((spapr_fixup_cpu_dt(fdt, spapr)));
 
     if (spapr_dt_cas_updates(spapr, fdt, ov5_updates)) {
         return -1;
@@ -1008,7 +1005,8 @@ static uint64_t translate_kernel_address(void *opaque, uint64_t addr)
     return (addr & 0x0fffffff) + KERNEL_LOAD_ADDR;
 }
 
-static void emulate_spapr_hypercall(PowerPCCPU *cpu)
+static void emulate_spapr_hypercall(PPCVirtualHypervisor *vhyp,
+                                    PowerPCCPU *cpu)
 {
     CPUPPCState *env = &cpu->env;
 
@@ -1753,11 +1751,80 @@ static void spapr_validate_node_memory(MachineState *machine, Error **errp)
     }
 }
 
+static void spapr_init_cpus(sPAPRMachineState *spapr)
+{
+    MachineState *machine = MACHINE(spapr);
+    MachineClass *mc = MACHINE_GET_CLASS(machine);
+    char *type = spapr_get_cpu_core_type(machine->cpu_model);
+    int smt = kvmppc_smt_threads();
+    int spapr_max_cores, spapr_cores;
+    int i;
+
+    if (!type) {
+        error_report("Unable to find sPAPR CPU Core definition");
+        exit(1);
+    }
+
+    if (mc->query_hotpluggable_cpus) {
+        if (smp_cpus % smp_threads) {
+            error_report("smp_cpus (%u) must be multiple of threads (%u)",
+                         smp_cpus, smp_threads);
+            exit(1);
+        }
+        if (max_cpus % smp_threads) {
+            error_report("max_cpus (%u) must be multiple of threads (%u)",
+                         max_cpus, smp_threads);
+            exit(1);
+        }
+
+        spapr_max_cores = max_cpus / smp_threads;
+        spapr_cores = smp_cpus / smp_threads;
+    } else {
+        if (max_cpus != smp_cpus) {
+            error_report("This machine version does not support CPU hotplug");
+            exit(1);
+        }
+
+        spapr_max_cores = QEMU_ALIGN_UP(smp_cpus, smp_threads) / smp_threads;
+        spapr_cores = spapr_max_cores;
+    }
+
+    spapr->cores = g_new0(Object *, spapr_max_cores);
+    for (i = 0; i < spapr_max_cores; i++) {
+        int core_id = i * smp_threads;
+
+        if (mc->query_hotpluggable_cpus) {
+            sPAPRDRConnector *drc =
+                spapr_dr_connector_new(OBJECT(spapr),
+                                       SPAPR_DR_CONNECTOR_TYPE_CPU,
+                                       (core_id / smp_threads) * smt);
+
+            qemu_register_reset(spapr_drc_reset, drc);
+        }
+
+        if (i < spapr_cores) {
+            Object *core  = object_new(type);
+            int nr_threads = smp_threads;
+
+            /* Handle the partially filled core for older machine types */
+            if ((i + 1) * smp_threads >= smp_cpus) {
+                nr_threads = smp_cpus - i * smp_threads;
+            }
+
+            object_property_set_int(core, nr_threads, "nr-threads",
+                                    &error_fatal);
+            object_property_set_int(core, core_id, CPU_CORE_PROP_CORE_ID,
+                                    &error_fatal);
+            object_property_set_bool(core, true, "realized", &error_fatal);
+        }
+    }
+    g_free(type);
+}
+
 /* pSeries LPAR / sPAPR hardware init */
 static void ppc_spapr_init(MachineState *machine)
 {
     sPAPRMachineState *spapr = SPAPR_MACHINE(machine);
-    MachineClass *mc = MACHINE_GET_CLASS(machine);
     sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(machine);
     const char *kernel_filename = machine->kernel_filename;
     const char *initrd_filename = machine->initrd_filename;
@@ -1772,28 +1839,11 @@ static void ppc_spapr_init(MachineState *machine)
     long load_limit, fw_size;
     char *filename;
     int smt = kvmppc_smt_threads();
-    int spapr_cores = smp_cpus / smp_threads;
-    int spapr_max_cores = max_cpus / smp_threads;
-
-    if (mc->query_hotpluggable_cpus) {
-        if (smp_cpus % smp_threads) {
-            error_report("smp_cpus (%u) must be multiple of threads (%u)",
-                         smp_cpus, smp_threads);
-            exit(1);
-        }
-        if (max_cpus % smp_threads) {
-            error_report("max_cpus (%u) must be multiple of threads (%u)",
-                         max_cpus, smp_threads);
-            exit(1);
-        }
-    }
 
     msi_nonbroken = true;
 
     QLIST_INIT(&spapr->phbs);
 
-    cpu_ppc_hypercall = emulate_spapr_hypercall;
-
     /* Allocate RMA if necessary */
     rma_alloc_size = kvmppc_alloc_rma(&rma);
 
@@ -1866,44 +1916,7 @@ static void ppc_spapr_init(MachineState *machine)
 
     ppc_cpu_parse_features(machine->cpu_model);
 
-    if (mc->query_hotpluggable_cpus) {
-        char *type = spapr_get_cpu_core_type(machine->cpu_model);
-
-        if (type == NULL) {
-            error_report("Unable to find sPAPR CPU Core definition");
-            exit(1);
-        }
-
-        spapr->cores = g_new0(Object *, spapr_max_cores);
-        for (i = 0; i < spapr_max_cores; i++) {
-            int core_id = i * smp_threads;
-            sPAPRDRConnector *drc =
-                spapr_dr_connector_new(OBJECT(spapr),
-                                       SPAPR_DR_CONNECTOR_TYPE_CPU,
-                                       (core_id / smp_threads) * smt);
-
-            qemu_register_reset(spapr_drc_reset, drc);
-
-            if (i < spapr_cores) {
-                Object *core  = object_new(type);
-                object_property_set_int(core, smp_threads, "nr-threads",
-                                        &error_fatal);
-                object_property_set_int(core, core_id, CPU_CORE_PROP_CORE_ID,
-                                        &error_fatal);
-                object_property_set_bool(core, true, "realized", &error_fatal);
-            }
-        }
-        g_free(type);
-    } else {
-        for (i = 0; i < smp_cpus; i++) {
-            PowerPCCPU *cpu = cpu_ppc_init(machine->cpu_model);
-            if (cpu == NULL) {
-                error_report("Unable to find PowerPC CPU definition");
-                exit(1);
-            }
-            spapr_cpu_init(spapr, cpu, &error_fatal);
-       }
-    }
+    spapr_init_cpus(spapr);
 
     if (kvm_enabled()) {
         /* Enable H_LOGICAL_CI_* so SLOF can talk to in-kernel devices */
@@ -2116,6 +2129,12 @@ static void ppc_spapr_init(MachineState *machine)
     qemu_register_reset(spapr_ccs_reset_hook, spapr);
 
     qemu_register_boot_set(spapr_boot_set, spapr);
+
+    /* to stop and start vmclock */
+    if (kvm_enabled()) {
+        qemu_add_vm_change_state_handler(cpu_ppc_clock_vm_state_change,
+                                         &spapr->tb);
+    }
 }
 
 static int spapr_kvm_type(const char *vm_type)
@@ -2185,6 +2204,19 @@ static char *spapr_get_fw_dev_path(FWPathProvider *p, BusState *bus,
         }
     }
 
+    /*
+     * SLOF probes the USB devices, and if it recognizes that the device is a
+     * storage device, it changes its name to "storage" instead of "usb-host",
+     * and additionally adds a child node for the SCSI LUN, so the correct
+     * boot path in SLOF is something like .../storage@1/disk@xxx" instead.
+     */
+    if (strcmp("usb-host", qdev_fw_name(dev)) == 0) {
+        USBDevice *usbdev = CAST(USBDevice, dev, TYPE_USB_DEVICE);
+        if (usb_host_dev_is_scsi_storage(usbdev)) {
+            return g_strdup_printf("storage@%s/disk", usbdev->port->path);
+        }
+    }
+
     if (phb) {
         /* Replace "pci" with "pci@800000020000000" */
         return g_strdup_printf("pci@%"PRIX64, phb->buid);
@@ -2252,7 +2284,7 @@ static void spapr_machine_finalizefn(Object *obj)
     g_free(spapr->kvm_type);
 }
 
-static void ppc_cpu_do_nmi_on_cpu(CPUState *cs, run_on_cpu_data arg)
+void spapr_do_system_reset_on_cpu(CPUState *cs, run_on_cpu_data arg)
 {
     cpu_synchronize_state(cs);
     ppc_cpu_do_system_reset(cs);
@@ -2263,7 +2295,7 @@ static void spapr_nmi(NMIState *n, int cpu_index, Error **errp)
     CPUState *cs;
 
     CPU_FOREACH(cs) {
-        async_run_on_cpu(cs, ppc_cpu_do_nmi_on_cpu, RUN_ON_CPU_NULL);
+        async_run_on_cpu(cs, spapr_do_system_reset_on_cpu, RUN_ON_CPU_NULL);
     }
 }
 
@@ -2630,8 +2662,8 @@ static void spapr_phb_placement(sPAPRMachineState *spapr, uint32_t index,
      * 1TiB 64-bit MMIO windows for each PHB.
      */
     const uint64_t base_buid = 0x800000020000000ULL;
-    const int max_phbs =
-        (SPAPR_PCI_LIMIT - SPAPR_PCI_BASE) / SPAPR_PCI_MEM64_WIN_SIZE - 1;
+#define SPAPR_MAX_PHBS ((SPAPR_PCI_LIMIT - SPAPR_PCI_BASE) / \
+                        SPAPR_PCI_MEM64_WIN_SIZE - 1)
     int i;
 
     /* Sanity check natural alignments */
@@ -2640,12 +2672,14 @@ static void spapr_phb_placement(sPAPRMachineState *spapr, uint32_t index,
     QEMU_BUILD_BUG_ON((SPAPR_PCI_MEM64_WIN_SIZE % SPAPR_PCI_MEM32_WIN_SIZE) != 0);
     QEMU_BUILD_BUG_ON((SPAPR_PCI_MEM32_WIN_SIZE % SPAPR_PCI_IO_WIN_SIZE) != 0);
     /* Sanity check bounds */
-    QEMU_BUILD_BUG_ON((max_phbs * SPAPR_PCI_IO_WIN_SIZE) > SPAPR_PCI_MEM32_WIN_SIZE);
-    QEMU_BUILD_BUG_ON((max_phbs * SPAPR_PCI_MEM32_WIN_SIZE) > SPAPR_PCI_MEM64_WIN_SIZE);
-
-    if (index >= max_phbs) {
-        error_setg(errp, "\"index\" for PAPR PHB is too large (max %u)",
-                   max_phbs - 1);
+    QEMU_BUILD_BUG_ON((SPAPR_MAX_PHBS * SPAPR_PCI_IO_WIN_SIZE) >
+                      SPAPR_PCI_MEM32_WIN_SIZE);
+    QEMU_BUILD_BUG_ON((SPAPR_MAX_PHBS * SPAPR_PCI_MEM32_WIN_SIZE) >
+                      SPAPR_PCI_MEM64_WIN_SIZE);
+
+    if (index >= SPAPR_MAX_PHBS) {
+        error_setg(errp, "\"index\" for PAPR PHB is too large (max %llu)",
+                   SPAPR_MAX_PHBS - 1);
         return;
     }
 
@@ -2666,6 +2700,7 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data)
     FWPathProviderClass *fwc = FW_PATH_PROVIDER_CLASS(oc);
     NMIClass *nc = NMI_CLASS(oc);
     HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc);
+    PPCVirtualHypervisorClass *vhc = PPC_VIRTUAL_HYPERVISOR_CLASS(oc);
 
     mc->desc = "pSeries Logical Partition (PAPR compliant)";
 
@@ -2697,6 +2732,7 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data)
     fwc->get_dev_path = spapr_get_fw_dev_path;
     nc->nmi_monitor_handler = spapr_nmi;
     smc->phb_placement = spapr_phb_placement;
+    vhc->hypercall = emulate_spapr_hypercall;
 }
 
 static const TypeInfo spapr_machine_info = {
@@ -2712,6 +2748,7 @@ static const TypeInfo spapr_machine_info = {
         { TYPE_FW_PATH_PROVIDER },
         { TYPE_NMI },
         { TYPE_HOTPLUG_HANDLER },
+        { TYPE_PPC_VIRTUAL_HYPERVISOR },
         { }
     },
 };
@@ -2745,18 +2782,37 @@ static const TypeInfo spapr_machine_info = {
     type_init(spapr_machine_register_##suffix)
 
 /*
+ * pseries-2.9
+ */
+static void spapr_machine_2_9_instance_options(MachineState *machine)
+{
+}
+
+static void spapr_machine_2_9_class_options(MachineClass *mc)
+{
+    /* Defaults for the latest behaviour inherited from the base class */
+}
+
+DEFINE_SPAPR_MACHINE(2_9, "2.9", true);
+
+/*
  * pseries-2.8
  */
+#define SPAPR_COMPAT_2_8                            \
+    HW_COMPAT_2_8
+
 static void spapr_machine_2_8_instance_options(MachineState *machine)
 {
+    spapr_machine_2_9_instance_options(machine);
 }
 
 static void spapr_machine_2_8_class_options(MachineClass *mc)
 {
-    /* Defaults for the latest behaviour inherited from the base class */
+    spapr_machine_2_9_class_options(mc);
+    SET_MACHINE_COMPAT(mc, SPAPR_COMPAT_2_8);
 }
 
-DEFINE_SPAPR_MACHINE(2_8, "2.8", true);
+DEFINE_SPAPR_MACHINE(2_8, "2.8", false);
 
 /*
  * pseries-2.7
diff --git a/hw/ppc/spapr_cpu_core.c b/hw/ppc/spapr_cpu_core.c
index c18632bbff..9dddaeb3fa 100644
--- a/hw/ppc/spapr_cpu_core.c
+++ b/hw/ppc/spapr_cpu_core.c
@@ -46,7 +46,8 @@ static void spapr_cpu_destroy(PowerPCCPU *cpu)
     qemu_unregister_reset(spapr_cpu_reset, cpu);
 }
 
-void spapr_cpu_init(sPAPRMachineState *spapr, PowerPCCPU *cpu, Error **errp)
+static void spapr_cpu_init(sPAPRMachineState *spapr, PowerPCCPU *cpu,
+                           Error **errp)
 {
     CPUPPCState *env = &cpu->env;
     CPUState *cs = CPU(cpu);
@@ -56,6 +57,7 @@ void spapr_cpu_init(sPAPRMachineState *spapr, PowerPCCPU *cpu, Error **errp)
     cpu_ppc_tb_init(env, SPAPR_TIMEBASE_FREQ);
 
     /* Enable PAPR mode in TCG or KVM */
+    cpu_ppc_set_vhyp(cpu, PPC_VIRTUAL_HYPERVISOR(spapr));
     cpu_ppc_set_papr(cpu);
 
     if (cpu->max_compat) {
@@ -166,11 +168,11 @@ void spapr_core_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
                      Error **errp)
 {
     sPAPRMachineState *spapr = SPAPR_MACHINE(OBJECT(hotplug_dev));
+    MachineClass *mc = MACHINE_GET_CLASS(spapr);
     sPAPRCPUCore *core = SPAPR_CPU_CORE(OBJECT(dev));
     CPUCore *cc = CPU_CORE(dev);
     CPUState *cs = CPU(core->threads);
     sPAPRDRConnector *drc;
-    sPAPRDRConnectorClass *drck;
     Error *local_err = NULL;
     void *fdt = NULL;
     int fdt_offset = 0;
@@ -180,7 +182,7 @@ void spapr_core_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
     drc = spapr_dr_connector_by_id(SPAPR_DR_CONNECTOR_TYPE_CPU, index * smt);
     spapr->cores[index] = OBJECT(dev);
 
-    g_assert(drc);
+    g_assert(drc || !mc->query_hotpluggable_cpus);
 
     /*
      * Setup CPU DT entries only for hotplugged CPUs. For boot time or
@@ -190,13 +192,15 @@ void spapr_core_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
         fdt = spapr_populate_hotplug_cpu_dt(cs, &fdt_offset, spapr);
     }
 
-    drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
-    drck->attach(drc, dev, fdt, fdt_offset, !dev->hotplugged, &local_err);
-    if (local_err) {
-        g_free(fdt);
-        spapr->cores[index] = NULL;
-        error_propagate(errp, local_err);
-        return;
+    if (drc) {
+        sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
+        drck->attach(drc, dev, fdt, fdt_offset, !dev->hotplugged, &local_err);
+        if (local_err) {
+            g_free(fdt);
+            spapr->cores[index] = NULL;
+            error_propagate(errp, local_err);
+            return;
+        }
     }
 
     if (dev->hotplugged) {
@@ -209,8 +213,11 @@ void spapr_core_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
         /*
          * Set the right DRC states for cold plugged CPU.
          */
-        drck->set_allocation_state(drc, SPAPR_DR_ALLOCATION_STATE_USABLE);
-        drck->set_isolation_state(drc, SPAPR_DR_ISOLATION_STATE_UNISOLATED);
+        if (drc) {
+            sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
+            drck->set_allocation_state(drc, SPAPR_DR_ALLOCATION_STATE_USABLE);
+            drck->set_isolation_state(drc, SPAPR_DR_ISOLATION_STATE_UNISOLATED);
+        }
     }
 }
 
@@ -227,7 +234,7 @@ void spapr_core_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
     char *base_core_type = spapr_get_cpu_core_type(machine->cpu_model);
     const char *type = object_get_typename(OBJECT(dev));
 
-    if (!mc->query_hotpluggable_cpus) {
+    if (dev->hotplugged && !mc->query_hotpluggable_cpus) {
         error_setg(&local_err, "CPU hotplug not supported for this machine");
         goto out;
     }
@@ -237,11 +244,6 @@ void spapr_core_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
         goto out;
     }
 
-    if (cc->nr_threads != smp_threads) {
-        error_setg(&local_err, "threads must be %d", smp_threads);
-        goto out;
-    }
-
     if (cc->core_id % smp_threads) {
         error_setg(&local_err, "invalid core id %d", cc->core_id);
         goto out;
diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c
index b2a8e48569..42d20e0b92 100644
--- a/hw/ppc/spapr_hcall.c
+++ b/hw/ppc/spapr_hcall.c
@@ -881,128 +881,105 @@ static target_ulong h_set_mode(PowerPCCPU *cpu, sPAPRMachineState *spapr,
     return ret;
 }
 
-typedef struct {
-    uint32_t cpu_version;
-    Error *err;
-} SetCompatState;
+#define H_SIGNAL_SYS_RESET_ALL         -1
+#define H_SIGNAL_SYS_RESET_ALLBUTSELF  -2
 
-static void do_set_compat(CPUState *cs, run_on_cpu_data arg)
+static target_ulong h_signal_sys_reset(PowerPCCPU *cpu,
+                                       sPAPRMachineState *spapr,
+                                       target_ulong opcode, target_ulong *args)
 {
-    PowerPCCPU *cpu = POWERPC_CPU(cs);
-    SetCompatState *s = arg.host_ptr;
+    target_long target = args[0];
+    CPUState *cs;
 
-    cpu_synchronize_state(cs);
-    ppc_set_compat(cpu, s->cpu_version, &s->err);
-}
+    if (target < 0) {
+        /* Broadcast */
+        if (target < H_SIGNAL_SYS_RESET_ALLBUTSELF) {
+            return H_PARAMETER;
+        }
 
-#define get_compat_level(cpuver) ( \
-    ((cpuver) == CPU_POWERPC_LOGICAL_2_05) ? 2050 : \
-    ((cpuver) == CPU_POWERPC_LOGICAL_2_06) ? 2060 : \
-    ((cpuver) == CPU_POWERPC_LOGICAL_2_06_PLUS) ? 2061 : \
-    ((cpuver) == CPU_POWERPC_LOGICAL_2_07) ? 2070 : 0)
+        CPU_FOREACH(cs) {
+            PowerPCCPU *c = POWERPC_CPU(cs);
 
-static void cas_handle_compat_cpu(PowerPCCPUClass *pcc, uint32_t pvr,
-                                  unsigned max_lvl, unsigned *compat_lvl,
-                                  unsigned *cpu_version)
-{
-    unsigned lvl = get_compat_level(pvr);
-    bool is205, is206, is207;
-
-    if (!lvl) {
-        return;
-    }
-
-    /* If it is a logical PVR, try to determine the highest level */
-    is205 = (pcc->pcr_supported & PCR_COMPAT_2_05) &&
-            (lvl == get_compat_level(CPU_POWERPC_LOGICAL_2_05));
-    is206 = (pcc->pcr_supported & PCR_COMPAT_2_06) &&
-            ((lvl == get_compat_level(CPU_POWERPC_LOGICAL_2_06)) ||
-             (lvl == get_compat_level(CPU_POWERPC_LOGICAL_2_06_PLUS)));
-    is207 = (pcc->pcr_supported & PCR_COMPAT_2_07) &&
-            (lvl == get_compat_level(CPU_POWERPC_LOGICAL_2_07));
-
-    if (is205 || is206 || is207) {
-        if (!max_lvl) {
-            /* User did not set the level, choose the highest */
-            if (*compat_lvl <= lvl) {
-                *compat_lvl = lvl;
-                *cpu_version = pvr;
+            if (target == H_SIGNAL_SYS_RESET_ALLBUTSELF) {
+                if (c == cpu) {
+                    continue;
+                }
             }
-        } else if (max_lvl >= lvl) {
-            /* User chose the level, don't set higher than this */
-            *compat_lvl = lvl;
-            *cpu_version = pvr;
+            run_on_cpu(cs, spapr_do_system_reset_on_cpu, RUN_ON_CPU_NULL);
         }
+        return H_SUCCESS;
+
+    } else {
+        /* Unicast */
+        CPU_FOREACH(cs) {
+            if (cpu->cpu_dt_id == target) {
+                run_on_cpu(cs, spapr_do_system_reset_on_cpu, RUN_ON_CPU_NULL);
+                return H_SUCCESS;
+            }
+        }
+        return H_PARAMETER;
     }
 }
 
-static target_ulong h_client_architecture_support(PowerPCCPU *cpu_,
+static target_ulong h_client_architecture_support(PowerPCCPU *cpu,
                                                   sPAPRMachineState *spapr,
                                                   target_ulong opcode,
                                                   target_ulong *args)
 {
     target_ulong list = ppc64_phys_to_real(args[0]);
     target_ulong ov_table;
-    PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu_);
-    CPUState *cs;
-    bool cpu_match = false, cpu_update = true;
-    unsigned old_cpu_version = cpu_->cpu_version;
-    unsigned compat_lvl = 0, cpu_version = 0;
-    unsigned max_lvl = get_compat_level(cpu_->max_compat);
-    int counter;
+    bool explicit_match = false; /* Matched the CPU's real PVR */
+    uint32_t max_compat = cpu->max_compat;
+    uint32_t best_compat = 0;
+    int i;
     sPAPROptionVector *ov5_guest, *ov5_cas_old, *ov5_updates;
 
-    /* Parse PVR list */
-    for (counter = 0; counter < 512; ++counter) {
+    /*
+     * We scan the supplied table of PVRs looking for two things
+     *   1. Is our real CPU PVR in the list?
+     *   2. What's the "best" listed logical PVR
+     */
+    for (i = 0; i < 512; ++i) {
         uint32_t pvr, pvr_mask;
 
         pvr_mask = ldl_be_phys(&address_space_memory, list);
-        list += 4;
-        pvr = ldl_be_phys(&address_space_memory, list);
-        list += 4;
-
-        trace_spapr_cas_pvr_try(pvr);
-        if (!max_lvl &&
-            ((cpu_->env.spr[SPR_PVR] & pvr_mask) == (pvr & pvr_mask))) {
-            cpu_match = true;
-            cpu_version = 0;
-        } else if (pvr == cpu_->cpu_version) {
-            cpu_match = true;
-            cpu_version = cpu_->cpu_version;
-        } else if (!cpu_match) {
-            cas_handle_compat_cpu(pcc, pvr, max_lvl, &compat_lvl, &cpu_version);
-        }
-        /* Terminator record */
+        pvr = ldl_be_phys(&address_space_memory, list + 4);
+        list += 8;
+
         if (~pvr_mask & pvr) {
-            break;
+            break; /* Terminator record */
+        }
+
+        if ((cpu->env.spr[SPR_PVR] & pvr_mask) == (pvr & pvr_mask)) {
+            explicit_match = true;
+        } else {
+            if (ppc_check_compat(cpu, pvr, best_compat, max_compat)) {
+                best_compat = pvr;
+            }
         }
     }
 
+    if ((best_compat == 0) && (!explicit_match || max_compat)) {
+        /* We couldn't find a suitable compatibility mode, and either
+         * the guest doesn't support "raw" mode for this CPU, or raw
+         * mode is disabled because a maximum compat mode is set */
+        return H_HARDWARE;
+    }
+
     /* Parsing finished */
-    trace_spapr_cas_pvr(cpu_->cpu_version, cpu_match,
-                        cpu_version, pcc->pcr_mask);
+    trace_spapr_cas_pvr(cpu->compat_pvr, explicit_match, best_compat);
 
     /* Update CPUs */
-    if (old_cpu_version != cpu_version) {
-        CPU_FOREACH(cs) {
-            SetCompatState s = {
-                .cpu_version = cpu_version,
-                .err = NULL,
-            };
+    if (cpu->compat_pvr != best_compat) {
+        Error *local_err = NULL;
 
-            run_on_cpu(cs, do_set_compat, RUN_ON_CPU_HOST_PTR(&s));
-
-            if (s.err) {
-                error_report_err(s.err);
-                return H_HARDWARE;
-            }
+        ppc_set_compat_all(best_compat, &local_err);
+        if (local_err) {
+            error_report_err(local_err);
+            return H_HARDWARE;
         }
     }
 
-    if (!cpu_version) {
-        cpu_update = false;
-    }
-
     /* For the future use: here @ov_table points to the first option vector */
     ov_table = list;
 
@@ -1028,7 +1005,7 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu_,
 
     if (!spapr->cas_reboot) {
         spapr->cas_reboot =
-            (spapr_h_cas_compose_response(spapr, args[1], args[2], cpu_update,
+            (spapr_h_cas_compose_response(spapr, args[1], args[2],
                                           ov5_updates) != 0);
     }
     spapr_ovec_cleanup(ov5_updates);
@@ -1101,6 +1078,7 @@ static void hypercall_register_types(void)
     /* hcall-splpar */
     spapr_register_hypercall(H_REGISTER_VPA, h_register_vpa);
     spapr_register_hypercall(H_CEDE, h_cede);
+    spapr_register_hypercall(H_SIGNAL_SYS_RESET, h_signal_sys_reset);
 
     /* processor register resource access h-calls */
     spapr_register_hypercall(H_SET_SPRG0, h_set_sprg0);
diff --git a/hw/ppc/spapr_vio.c b/hw/ppc/spapr_vio.c
index cc1e09c568..8bfc5f971f 100644
--- a/hw/ppc/spapr_vio.c
+++ b/hw/ppc/spapr_vio.c
@@ -538,21 +538,11 @@ VIOsPAPRBus *spapr_vio_bus_init(void)
     return bus;
 }
 
-/* Represents sPAPR hcall VIO devices */
-
-static int spapr_vio_bridge_init(SysBusDevice *dev)
-{
-    /* nothing */
-    return 0;
-}
-
 static void spapr_vio_bridge_class_init(ObjectClass *klass, void *data)
 {
-    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
     DeviceClass *dc = DEVICE_CLASS(klass);
 
     dc->fw_name = "vdevice";
-    k->init = spapr_vio_bridge_init;
 }
 
 static const TypeInfo spapr_vio_bridge_info = {
diff --git a/hw/ppc/trace-events b/hw/ppc/trace-events
index 2297ead11e..f46995cdb2 100644
--- a/hw/ppc/trace-events
+++ b/hw/ppc/trace-events
@@ -15,7 +15,7 @@ spapr_cas_continue(unsigned long n) "Copy changes to the guest: %ld bytes"
 
 # hw/ppc/spapr_hcall.c
 spapr_cas_pvr_try(uint32_t pvr) "%x"
-spapr_cas_pvr(uint32_t cur_pvr, bool cpu_match, uint32_t new_pvr, uint64_t pcr) "current=%x, cpu_match=%u, new=%x, compat flags=%"PRIx64
+spapr_cas_pvr(uint32_t cur_pvr, bool explicit_match, uint32_t new_pvr) "current=%x, explicit_match=%u, new=%x"
 
 # hw/ppc/spapr_iommu.c
 spapr_iommu_put(uint64_t liobn, uint64_t ioba, uint64_t tce, uint64_t ret) "liobn=%"PRIx64" ioba=0x%"PRIx64" tce=0x%"PRIx64" ret=%"PRId64
@@ -74,3 +74,14 @@ ppc_tb_adjust(uint64_t offs1, uint64_t offs2, int64_t diff, int64_t seconds) "ad
 # hw/ppc/prep.c
 prep_io_800_writeb(uint32_t addr, uint32_t val) "0x%08" PRIx32 " => 0x%02" PRIx32
 prep_io_800_readb(uint32_t addr, uint32_t retval) "0x%08" PRIx32 " <= 0x%02" PRIx32
+
+# hw/ppc/prep_systemio.c
+prep_systemio_read(uint32_t addr, uint32_t val) "read addr=%x val=%x"
+prep_systemio_write(uint32_t addr, uint32_t val) "write addr=%x val=%x"
+
+# hw/ppc/rs6000_mc.c
+rs6000mc_id_read(uint32_t addr, uint32_t val) "read addr=%x val=%x"
+rs6000mc_presence_read(uint32_t addr, uint32_t val) "read addr=%x val=%x"
+rs6000mc_size_read(uint32_t addr, uint32_t val) "read addr=%x val=%x"
+rs6000mc_size_write(uint32_t addr, uint32_t val) "write addr=%x val=%x"
+rs6000mc_parity_read(uint32_t addr, uint32_t val) "read addr=%x val=%x"
diff --git a/hw/ppc/virtex_ml507.c b/hw/ppc/virtex_ml507.c
index b97d96685c..fdbcf22a0c 100644
--- a/hw/ppc/virtex_ml507.c
+++ b/hw/ppc/virtex_ml507.c
@@ -221,6 +221,13 @@ static void virtex_init(MachineState *machine)
 
     cpu = ppc440_init_xilinx(&ram_size, 1, machine->cpu_model, 400000000);
     env = &cpu->env;
+
+    if (env->mmu_model != POWERPC_MMU_BOOKE) {
+        fprintf(stderr, "MMU model %i not supported by this machine.\n",
+            env->mmu_model);
+        exit(1);
+    }
+
     qemu_register_reset(main_cpu_reset, cpu);
 
     memory_region_allocate_system_memory(phys_ram, NULL, "ram", ram_size);
diff --git a/hw/s390x/s390-pci-bus.h b/hw/s390x/s390-pci-bus.h
index 0aad9cc272..dcbf4820c9 100644
--- a/hw/s390x/s390-pci-bus.h
+++ b/hw/s390x/s390-pci-bus.h
@@ -279,7 +279,7 @@ typedef struct S390PCIIOMMUTable {
     S390PCIIOMMU *iommu[PCI_SLOT_MAX];
 } S390PCIIOMMUTable;
 
-typedef struct S390PCIBusDevice {
+struct S390PCIBusDevice {
     DeviceState qdev;
     PCIDevice *pdev;
     ZpciState state;
@@ -301,7 +301,7 @@ typedef struct S390PCIBusDevice {
     IndAddr *indicator;
     QEMUTimer *release_timer;
     QTAILQ_ENTRY(S390PCIBusDevice) link;
-} S390PCIBusDevice;
+};
 
 typedef struct S390PCIBus {
     BusState qbus;
diff --git a/hw/s390x/s390-virtio.c b/hw/s390x/s390-virtio.c
index 0a963473ad..7a3a7fe5fd 100644
--- a/hw/s390x/s390-virtio.c
+++ b/hw/s390x/s390-virtio.c
@@ -204,8 +204,8 @@ void s390_machine_reset(void)
 {
     S390CPU *ipl_cpu = S390_CPU(qemu_get_cpu(0));
 
-    qemu_devices_reset();
     s390_cmma_reset();
+    qemu_devices_reset();
     s390_crypto_reset();
 
     /* all cpus are stopped - configure and start the ipl cpu only */
diff --git a/hw/scsi/megasas.c b/hw/scsi/megasas.c
index 6aad7c9a06..1a8b04c6d7 100644
--- a/hw/scsi/megasas.c
+++ b/hw/scsi/megasas.c
@@ -2367,9 +2367,11 @@ static void megasas_scsi_realize(PCIDevice *dev, Error **errp)
 
     if (megasas_use_msix(s) &&
         msix_init(dev, 15, &s->mmio_io, b->mmio_bar, 0x2000,
-                  &s->mmio_io, b->mmio_bar, 0x3800, 0x68)) {
+                  &s->mmio_io, b->mmio_bar, 0x3800, 0x68, NULL)) {
+        /* TODO: check msix_init's error, and should fail on msix=on */
         s->msix = ON_OFF_AUTO_OFF;
     }
+
     if (pci_is_express(dev)) {
         pcie_endpoint_cap_init(dev, 0xa0);
     }
diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c
index 7a588a7ad4..92f091a613 100644
--- a/hw/scsi/scsi-generic.c
+++ b/hw/scsi/scsi-generic.c
@@ -246,7 +246,7 @@ static void scsi_read_data(SCSIRequest *req)
     SCSIDevice *s = r->req.dev;
     int ret;
 
-    DPRINTF("scsi_read_data 0x%x\n", req->tag);
+    DPRINTF("scsi_read_data tag=0x%x\n", req->tag);
 
     /* The request is used as the AIO opaque value, so add a ref.  */
     scsi_req_ref(&r->req);
@@ -294,7 +294,7 @@ static void scsi_write_data(SCSIRequest *req)
     SCSIDevice *s = r->req.dev;
     int ret;
 
-    DPRINTF("scsi_write_data 0x%x\n", req->tag);
+    DPRINTF("scsi_write_data tag=0x%x\n", req->tag);
     if (r->len == 0) {
         r->len = r->buflen;
         scsi_req_data(&r->req, r->len);
@@ -329,6 +329,7 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *cmd)
     int ret;
 
 #ifdef DEBUG_SCSI
+    DPRINTF("Command: data=0x%02x", cmd[0]);
     {
         int i;
         for (i = 1; i < r->req.cmd.len; i++) {
diff --git a/hw/timer/mc146818rtc.c b/hw/timer/mc146818rtc.c
index 637f8722a7..4165450250 100644
--- a/hw/timer/mc146818rtc.c
+++ b/hw/timer/mc146818rtc.c
@@ -27,6 +27,7 @@
 #include "hw/hw.h"
 #include "qemu/timer.h"
 #include "sysemu/sysemu.h"
+#include "sysemu/replay.h"
 #include "hw/timer/mc146818rtc.h"
 #include "qapi/visitor.h"
 #include "qapi-event.h"
@@ -734,10 +735,16 @@ static int rtc_post_load(void *opaque, int version_id)
         check_update_timer(s);
     }
 
-    uint64_t now = qemu_clock_get_ns(rtc_clock);
-    if (now < s->next_periodic_time ||
-        now > (s->next_periodic_time + get_max_clock_jump())) {
-        periodic_timer_update(s, qemu_clock_get_ns(rtc_clock));
+    /* The periodic timer is deterministic in record/replay mode,
+     * so there is no need to update it after loading the vmstate.
+     * Reading RTC here would misalign record and replay.
+     */
+    if (replay_mode == REPLAY_MODE_NONE) {
+        uint64_t now = qemu_clock_get_ns(rtc_clock);
+        if (now < s->next_periodic_time ||
+            now > (s->next_periodic_time + get_max_clock_jump())) {
+            periodic_timer_update(s, qemu_clock_get_ns(rtc_clock));
+        }
     }
 
 #ifdef TARGET_I386
diff --git a/hw/usb/ccid-card-passthru.c b/hw/usb/ccid-card-passthru.c
index 88cb6d8978..daab0d56cf 100644
--- a/hw/usb/ccid-card-passthru.c
+++ b/hw/usb/ccid-card-passthru.c
@@ -264,7 +264,7 @@ static void ccid_card_vscard_handle_message(PassthruState *card,
 
 static void ccid_card_vscard_drop_connection(PassthruState *card)
 {
-    CharDriverState *chr = qemu_chr_fe_get_driver(&card->cs);
+    Chardev *chr = qemu_chr_fe_get_driver(&card->cs);
 
     qemu_chr_fe_deinit(&card->cs);
     qemu_chr_delete(chr);
diff --git a/hw/usb/dev-serial.c b/hw/usb/dev-serial.c
index 6066d9b0f7..6d5137383b 100644
--- a/hw/usb/dev-serial.c
+++ b/hw/usb/dev-serial.c
@@ -483,7 +483,7 @@ static void usb_serial_realize(USBDevice *dev, Error **errp)
 {
     USBSerialState *s = USB_SERIAL_DEV(dev);
     Error *local_err = NULL;
-    CharDriverState *chr = qemu_chr_fe_get_driver(&s->cs);
+    Chardev *chr = qemu_chr_fe_get_driver(&s->cs);
 
     usb_desc_create_serial(dev);
     usb_desc_init(dev);
@@ -512,7 +512,7 @@ static void usb_serial_realize(USBDevice *dev, Error **errp)
 static USBDevice *usb_serial_init(USBBus *bus, const char *filename)
 {
     USBDevice *dev;
-    CharDriverState *cdrv;
+    Chardev *cdrv;
     uint32_t vendorid = 0, productid = 0;
     char label[32];
     static int index;
@@ -564,7 +564,7 @@ static USBDevice *usb_serial_init(USBBus *bus, const char *filename)
 static USBDevice *usb_braille_init(USBBus *bus, const char *unused)
 {
     USBDevice *dev;
-    CharDriverState *cdrv;
+    Chardev *cdrv;
 
     cdrv = qemu_chr_new("braille", "braille");
     if (!cdrv)
diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
index e0b516987f..f8106789d8 100644
--- a/hw/usb/hcd-xhci.c
+++ b/hw/usb/hcd-xhci.c
@@ -3627,25 +3627,6 @@ static void usb_xhci_realize(struct PCIDevice *dev, Error **errp)
     dev->config[PCI_CACHE_LINE_SIZE] = 0x10;
     dev->config[0x60] = 0x30; /* release number */
 
-    usb_xhci_init(xhci);
-
-    if (xhci->msi != ON_OFF_AUTO_OFF) {
-        ret = msi_init(dev, 0x70, xhci->numintrs, true, false, &err);
-        /* Any error other than -ENOTSUP(board's MSI support is broken)
-         * is a programming error */
-        assert(!ret || ret == -ENOTSUP);
-        if (ret && xhci->msi == ON_OFF_AUTO_ON) {
-            /* Can't satisfy user's explicit msi=on request, fail */
-            error_append_hint(&err, "You have to use msi=auto (default) or "
-                    "msi=off with this machine type.\n");
-            error_propagate(errp, err);
-            return;
-        }
-        assert(!err || xhci->msi == ON_OFF_AUTO_AUTO);
-        /* With msi=auto, we fall back to MSI off silently */
-        error_free(err);
-    }
-
     if (xhci->numintrs > MAXINTRS) {
         xhci->numintrs = MAXINTRS;
     }
@@ -3667,6 +3648,24 @@ static void usb_xhci_realize(struct PCIDevice *dev, Error **errp)
         xhci->max_pstreams_mask = 0;
     }
 
+    if (xhci->msi != ON_OFF_AUTO_OFF) {
+        ret = msi_init(dev, 0x70, xhci->numintrs, true, false, &err);
+        /* Any error other than -ENOTSUP(board's MSI support is broken)
+         * is a programming error */
+        assert(!ret || ret == -ENOTSUP);
+        if (ret && xhci->msi == ON_OFF_AUTO_ON) {
+            /* Can't satisfy user's explicit msi=on request, fail */
+            error_append_hint(&err, "You have to use msi=auto (default) or "
+                    "msi=off with this machine type.\n");
+            error_propagate(errp, err);
+            return;
+        }
+        assert(!err || xhci->msi == ON_OFF_AUTO_AUTO);
+        /* With msi=auto, we fall back to MSI off silently */
+        error_free(err);
+    }
+
+    usb_xhci_init(xhci);
     xhci->mfwrap_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, xhci_mfwrap_timer, xhci);
 
     memory_region_init(&xhci->mem, OBJECT(xhci), "xhci", LEN_REGS);
@@ -3704,11 +3703,11 @@ static void usb_xhci_realize(struct PCIDevice *dev, Error **errp)
     }
 
     if (xhci->msix != ON_OFF_AUTO_OFF) {
-        /* TODO check for errors */
+        /* TODO check for errors, and should fail when msix=on */
         msix_init(dev, xhci->numintrs,
                   &xhci->mem, 0, OFF_MSIX_TABLE,
                   &xhci->mem, 0, OFF_MSIX_PBA,
-                  0x90);
+                  0x90, NULL);
     }
 }
 
diff --git a/hw/usb/host-libusb.c b/hw/usb/host-libusb.c
index bd81d71a98..7791c6d520 100644
--- a/hw/usb/host-libusb.c
+++ b/hw/usb/host-libusb.c
@@ -1707,6 +1707,35 @@ static void usb_host_auto_check(void *unused)
     timer_mod(usb_auto_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + 2000);
 }
 
+/**
+ * Check whether USB host device has a USB mass storage SCSI interface
+ */
+bool usb_host_dev_is_scsi_storage(USBDevice *ud)
+{
+    USBHostDevice *uhd = USB_HOST_DEVICE(ud);
+    struct libusb_config_descriptor *conf;
+    const struct libusb_interface_descriptor *intf;
+    bool is_scsi_storage = false;
+    int i;
+
+    if (!uhd || libusb_get_active_config_descriptor(uhd->dev, &conf) != 0) {
+        return false;
+    }
+
+    for (i = 0; i < conf->bNumInterfaces; i++) {
+        intf = &conf->interface[i].altsetting[ud->altsetting[i]];
+        if (intf->bInterfaceClass == LIBUSB_CLASS_MASS_STORAGE &&
+            intf->bInterfaceSubClass == 6) {                 /* 6 means SCSI */
+            is_scsi_storage = true;
+            break;
+        }
+    }
+
+    libusb_free_config_descriptor(conf);
+
+    return is_scsi_storage;
+}
+
 void hmp_info_usbhost(Monitor *mon, const QDict *qdict)
 {
     libusb_device **devs = NULL;
diff --git a/hw/usb/host-stub.c b/hw/usb/host-stub.c
index 6ba65a1f6d..d0268baaa0 100644
--- a/hw/usb/host-stub.c
+++ b/hw/usb/host-stub.c
@@ -46,3 +46,8 @@ USBDevice *usb_host_device_open(USBBus *bus, const char *devname)
 {
     return NULL;
 }
+
+bool usb_host_dev_is_scsi_storage(USBDevice *ud)
+{
+    return false;
+}
diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c
index 4a0ebbfb32..860f5c35eb 100644
--- a/hw/usb/redirect.c
+++ b/hw/usb/redirect.c
@@ -284,7 +284,7 @@ static gboolean usbredir_write_unblocked(GIOChannel *chan, GIOCondition cond,
 static int usbredir_write(void *priv, uint8_t *data, int count)
 {
     USBRedirDevice *dev = priv;
-    CharDriverState *chr = qemu_chr_fe_get_driver(&dev->cs);
+    Chardev *chr = qemu_chr_fe_get_driver(&dev->cs);
     int r;
 
     if (!chr->be_open) {
@@ -1430,7 +1430,7 @@ static void usbredir_cleanup_device_queues(USBRedirDevice *dev)
 static void usbredir_handle_destroy(USBDevice *udev)
 {
     USBRedirDevice *dev = USB_REDIRECT(udev);
-    CharDriverState *chr = qemu_chr_fe_get_driver(&dev->cs);
+    Chardev *chr = qemu_chr_fe_get_driver(&dev->cs);
 
     qemu_chr_fe_deinit(&dev->cs);
     qemu_chr_delete(chr);
diff --git a/hw/usb/trace-events b/hw/usb/trace-events
index 2d42fd45da..fdd1d29030 100644
--- a/hw/usb/trace-events
+++ b/hw/usb/trace-events
@@ -60,7 +60,6 @@ usb_ohci_mem_read_bad_offset(uint32_t addr) "%x"
 usb_ohci_mem_write_unaligned(uint32_t addr) "at %x"
 usb_ohci_mem_write_bad_offset(uint32_t addr) "%x"
 usb_ohci_process_lists(uint32_t head, uint32_t cur) "head %x, cur %x"
-usb_ohci_bus_eof_timer_failed(const char *name) "%s: timer_new_ns failed"
 usb_ohci_set_frame_interval(const char *name, uint16_t fi_x, uint16_t fi_u) "%s: FrameInterval = 0x%x (%u)"
 usb_ohci_hub_power_up(void) "powered up all ports"
 usb_ohci_hub_power_down(void) "powered down all ports"
diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c
index 882d3a91b6..332f41d662 100644
--- a/hw/vfio/pci.c
+++ b/hw/vfio/pci.c
@@ -1432,6 +1432,7 @@ static void vfio_msix_early_setup(VFIOPCIDevice *vdev, Error **errp)
 static int vfio_msix_setup(VFIOPCIDevice *vdev, int pos, Error **errp)
 {
     int ret;
+    Error *err = NULL;
 
     vdev->msix->pending = g_malloc0(BITS_TO_LONGS(vdev->msix->entries) *
                                     sizeof(unsigned long));
@@ -1439,12 +1440,15 @@ static int vfio_msix_setup(VFIOPCIDevice *vdev, int pos, Error **errp)
                     vdev->bars[vdev->msix->table_bar].region.mem,
                     vdev->msix->table_bar, vdev->msix->table_offset,
                     vdev->bars[vdev->msix->pba_bar].region.mem,
-                    vdev->msix->pba_bar, vdev->msix->pba_offset, pos);
+                    vdev->msix->pba_bar, vdev->msix->pba_offset, pos,
+                    &err);
     if (ret < 0) {
         if (ret == -ENOTSUP) {
+            error_report_err(err);
             return 0;
         }
-        error_setg(errp, "msix_init failed");
+
+        error_propagate(errp, err);
         return ret;
     }
 
diff --git a/hw/vfio/trace-events b/hw/vfio/trace-events
index ef81609b98..8de8281357 100644
--- a/hw/vfio/trace-events
+++ b/hw/vfio/trace-events
@@ -46,7 +46,7 @@ vfio_pci_emulated_device_id(const char *name, uint16_t val) "%s %04x"
 vfio_pci_emulated_sub_vendor_id(const char *name, uint16_t val) "%s %04x"
 vfio_pci_emulated_sub_device_id(const char *name, uint16_t val) "%s %04x"
 
-# hw/vfio/pci-quirks.
+# hw/vfio/pci-quirks.c
 vfio_quirk_rom_blacklisted(const char *name, uint16_t vid, uint16_t did) "%s %04x:%04x"
 vfio_quirk_generic_window_address_write(const char *name, const char * region_name, uint64_t data) "%s %s 0x%"PRIx64
 vfio_quirk_generic_window_data_read(const char *name, const char * region_name, uint64_t data) "%s %s 0x%"PRIx64
diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c
index b124d97d7c..febe519bbd 100644
--- a/hw/virtio/vhost.c
+++ b/hw/virtio/vhost.c
@@ -612,7 +612,8 @@ static void vhost_set_memory(MemoryListener *listener,
 
 static bool vhost_section(MemoryRegionSection *section)
 {
-    return memory_region_is_ram(section->mr);
+    return memory_region_is_ram(section->mr) &&
+        !memory_region_is_rom(section->mr);
 }
 
 static void vhost_begin(MemoryListener *listener)
diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c
index b5af2a00f3..5ce42af9d4 100644
--- a/hw/virtio/virtio-pci.c
+++ b/hw/virtio/virtio-pci.c
@@ -1686,9 +1686,9 @@ static void virtio_pci_device_plugged(DeviceState *d, Error **errp)
 
     if (proxy->nvectors) {
         int err = msix_init_exclusive_bar(&proxy->pci_dev, proxy->nvectors,
-                                          proxy->msix_bar_idx);
+                                          proxy->msix_bar_idx, NULL);
         if (err) {
-            /* Notice when a system that supports MSIx can't initialize it.  */
+            /* Notice when a system that supports MSIx can't initialize it */
             if (err != -ENOTSUP) {
                 error_report("unable to init msix vectors to %" PRIu32,
                              proxy->nvectors);
diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c
index f292a53940..63657066e7 100644
--- a/hw/virtio/virtio.c
+++ b/hw/virtio/virtio.c
@@ -1383,7 +1383,7 @@ static void virtio_set_isr(VirtIODevice *vdev, int value)
     }
 }
 
-bool virtio_should_notify(VirtIODevice *vdev, VirtQueue *vq)
+static bool virtio_should_notify(VirtIODevice *vdev, VirtQueue *vq)
 {
     uint16_t old, new;
     bool v;
diff --git a/hw/xen/trace-events b/hw/xen/trace-events
new file mode 100644
index 0000000000..c4fb6f1aea
--- /dev/null
+++ b/hw/xen/trace-events
@@ -0,0 +1,13 @@
+# See docs/tracing.txt for syntax documentation.
+
+# include/hw/xen/xen_common.h
+xen_default_ioreq_server(void) ""
+xen_ioreq_server_create(uint32_t id) "id: %u"
+xen_ioreq_server_destroy(uint32_t id) "id: %u"
+xen_ioreq_server_state(uint32_t id, bool enable) "id: %u: enable: %i"
+xen_map_mmio_range(uint32_t id, uint64_t start_addr, uint64_t end_addr) "id: %u start: %#"PRIx64" end: %#"PRIx64
+xen_unmap_mmio_range(uint32_t id, uint64_t start_addr, uint64_t end_addr) "id: %u start: %#"PRIx64" end: %#"PRIx64
+xen_map_portio_range(uint32_t id, uint64_t start_addr, uint64_t end_addr) "id: %u start: %#"PRIx64" end: %#"PRIx64
+xen_unmap_portio_range(uint32_t id, uint64_t start_addr, uint64_t end_addr) "id: %u start: %#"PRIx64" end: %#"PRIx64
+xen_map_pcidev(uint32_t id, uint8_t bus, uint8_t dev, uint8_t func) "id: %u bdf: %02x.%02x.%02x"
+xen_unmap_pcidev(uint32_t id, uint8_t bus, uint8_t dev, uint8_t func) "id: %u bdf: %02x.%02x.%02x"
diff --git a/include/block/block.h b/include/block/block.h
index 8b0dcdaa70..4e81f2069b 100644
--- a/include/block/block.h
+++ b/include/block/block.h
@@ -116,6 +116,7 @@ typedef struct HDGeometry {
 
 #define BDRV_REQUEST_MAX_SECTORS MIN(SIZE_MAX >> BDRV_SECTOR_BITS, \
                                      INT_MAX >> BDRV_SECTOR_BITS)
+#define BDRV_REQUEST_MAX_BYTES (BDRV_REQUEST_MAX_SECTORS << BDRV_SECTOR_BITS)
 
 /*
  * Allocation status flags
diff --git a/include/exec/cpu_ldst_template.h b/include/exec/cpu_ldst_template.h
index eaf69a1ad4..4db2302962 100644
--- a/include/exec/cpu_ldst_template.h
+++ b/include/exec/cpu_ldst_template.h
@@ -25,7 +25,7 @@
  */
 
 #if !defined(SOFTMMU_CODE_ACCESS)
-#include "trace.h"
+#include "trace-root.h"
 #endif
 
 #include "trace/mem.h"
diff --git a/include/exec/cpu_ldst_useronly_template.h b/include/exec/cpu_ldst_useronly_template.h
index b1378bfae8..7b8c7c506e 100644
--- a/include/exec/cpu_ldst_useronly_template.h
+++ b/include/exec/cpu_ldst_useronly_template.h
@@ -24,7 +24,7 @@
  */
 
 #if !defined(CODE_ACCESS)
-#include "trace.h"
+#include "trace-root.h"
 #endif
 
 #include "trace/mem.h"
diff --git a/include/exec/memory.h b/include/exec/memory.h
index a10044f08f..987f9251c6 100644
--- a/include/exec/memory.h
+++ b/include/exec/memory.h
@@ -1250,7 +1250,7 @@ void memory_global_dirty_log_start(void);
  */
 void memory_global_dirty_log_stop(void);
 
-void mtree_info(fprintf_function mon_printf, void *f);
+void mtree_info(fprintf_function mon_printf, void *f, bool flatview);
 
 /**
  * memory_region_dispatch_read: perform a read directly to the specified
diff --git a/include/fpu/softfloat.h b/include/fpu/softfloat.h
index 14f8383686..842ec6b22a 100644
--- a/include/fpu/softfloat.h
+++ b/include/fpu/softfloat.h
@@ -356,6 +356,26 @@ static inline int float16_is_any_nan(float16 a)
     return ((float16_val(a) & ~0x8000) > 0x7c00);
 }
 
+static inline int float16_is_neg(float16 a)
+{
+    return float16_val(a) >> 15;
+}
+
+static inline int float16_is_infinity(float16 a)
+{
+    return (float16_val(a) & 0x7fff) == 0x7c00;
+}
+
+static inline int float16_is_zero(float16 a)
+{
+    return (float16_val(a) & 0x7fff) == 0;
+}
+
+static inline int float16_is_zero_or_denormal(float16 a)
+{
+    return (float16_val(a) & 0x7c00) == 0;
+}
+
 /*----------------------------------------------------------------------------
 | The pattern for a default generated half-precision NaN.
 *----------------------------------------------------------------------------*/
diff --git a/include/hw/arm/exynos4210.h b/include/hw/arm/exynos4210.h
index 76bb6d4203..d9e08014d6 100644
--- a/include/hw/arm/exynos4210.h
+++ b/include/hw/arm/exynos4210.h
@@ -131,7 +131,7 @@ void exynos4210_combiner_get_gpioin(Exynos4210Irq *irqs, DeviceState *dev,
 DeviceState *exynos4210_uart_create(hwaddr addr,
                                     int fifo_size,
                                     int channel,
-                                    CharDriverState *chr,
+                                    Chardev *chr,
                                     qemu_irq irq);
 
 #endif /* EXYNOS4210_H */
diff --git a/include/hw/arm/omap.h b/include/hw/arm/omap.h
index f25870b718..cac1b2ba43 100644
--- a/include/hw/arm/omap.h
+++ b/include/hw/arm/omap.h
@@ -664,14 +664,14 @@ struct omap_uart_s;
 struct omap_uart_s *omap_uart_init(hwaddr base,
                 qemu_irq irq, omap_clk fclk, omap_clk iclk,
                 qemu_irq txdma, qemu_irq rxdma,
-                const char *label, CharDriverState *chr);
+                const char *label, Chardev *chr);
 struct omap_uart_s *omap2_uart_init(MemoryRegion *sysmem,
                 struct omap_target_agent_s *ta,
                 qemu_irq irq, omap_clk fclk, omap_clk iclk,
                 qemu_irq txdma, qemu_irq rxdma,
-                const char *label, CharDriverState *chr);
+                const char *label, Chardev *chr);
 void omap_uart_reset(struct omap_uart_s *s);
-void omap_uart_attach(struct omap_uart_s *s, CharDriverState *chr);
+void omap_uart_attach(struct omap_uart_s *s, Chardev *chr);
 
 struct omap_mpuio_s;
 qemu_irq *omap_mpuio_in_get(struct omap_mpuio_s *s);
diff --git a/include/hw/bt.h b/include/hw/bt.h
index 2fa22bdab6..b5e11d4d43 100644
--- a/include/hw/bt.h
+++ b/include/hw/bt.h
@@ -127,8 +127,8 @@ enum {
     csrhci_pin_wakeup,
     __csrhci_pins,
 };
-qemu_irq *csrhci_pins_get(CharDriverState *chr);
-CharDriverState *uart_hci_init(void);
+qemu_irq *csrhci_pins_get(Chardev *chr);
+Chardev *uart_hci_init(void);
 
 /* bt-l2cap.c */
 struct bt_l2cap_device_s;
diff --git a/include/hw/char/cadence_uart.h b/include/hw/char/cadence_uart.h
index ca75eb5e32..c836db4b74 100644
--- a/include/hw/char/cadence_uart.h
+++ b/include/hw/char/cadence_uart.h
@@ -51,7 +51,7 @@ typedef struct {
 
 static inline DeviceState *cadence_uart_create(hwaddr addr,
                                         qemu_irq irq,
-                                        CharDriverState *chr)
+                                        Chardev *chr)
 {
     DeviceState *dev;
     SysBusDevice *s;
diff --git a/include/hw/char/escc.h b/include/hw/char/escc.h
index 297e2ebcda..08ae122386 100644
--- a/include/hw/char/escc.h
+++ b/include/hw/char/escc.h
@@ -5,7 +5,7 @@
 #define TYPE_ESCC "escc"
 #define ESCC_SIZE 4
 MemoryRegion *escc_init(hwaddr base, qemu_irq irqA, qemu_irq irqB,
-              CharDriverState *chrA, CharDriverState *chrB,
+              Chardev *chrA, Chardev *chrB,
               int clock, int it_shift);
 
 void slavio_serial_ms_kbd_init(hwaddr base, qemu_irq irq,
diff --git a/include/hw/char/pl011.h b/include/hw/char/pl011.h
index 0ca7c19410..83649324b6 100644
--- a/include/hw/char/pl011.h
+++ b/include/hw/char/pl011.h
@@ -17,7 +17,7 @@
 
 static inline DeviceState *pl011_create(hwaddr addr,
                                         qemu_irq irq,
-                                        CharDriverState *chr)
+                                        Chardev *chr)
 {
     DeviceState *dev;
     SysBusDevice *s;
@@ -34,7 +34,7 @@ static inline DeviceState *pl011_create(hwaddr addr,
 
 static inline DeviceState *pl011_luminary_create(hwaddr addr,
                                                  qemu_irq irq,
-                                                 CharDriverState *chr)
+                                                 Chardev *chr)
 {
     DeviceState *dev;
     SysBusDevice *s;
diff --git a/include/hw/char/serial.h b/include/hw/char/serial.h
index c928d7d907..daebb076c2 100644
--- a/include/hw/char/serial.h
+++ b/include/hw/char/serial.h
@@ -88,11 +88,11 @@ void serial_set_frequency(SerialState *s, uint32_t frequency);
 
 /* legacy pre qom */
 SerialState *serial_init(int base, qemu_irq irq, int baudbase,
-                         CharDriverState *chr, MemoryRegion *system_io);
+                         Chardev *chr, MemoryRegion *system_io);
 SerialState *serial_mm_init(MemoryRegion *address_space,
                             hwaddr base, int it_shift,
                             qemu_irq irq, int baudbase,
-                            CharDriverState *chr, enum device_endian end);
+                            Chardev *chr, enum device_endian end);
 
 /* serial-isa.c */
 #define TYPE_ISA_SERIAL "isa-serial"
diff --git a/include/hw/char/xilinx_uartlite.h b/include/hw/char/xilinx_uartlite.h
index 8b4fc54953..634086b657 100644
--- a/include/hw/char/xilinx_uartlite.h
+++ b/include/hw/char/xilinx_uartlite.h
@@ -17,7 +17,7 @@
 
 static inline DeviceState *xilinx_uartlite_create(hwaddr addr,
                                         qemu_irq irq,
-                                        CharDriverState *chr)
+                                        Chardev *chr)
 {
     DeviceState *dev;
     SysBusDevice *s;
diff --git a/include/hw/compat.h b/include/hw/compat.h
index ee0dd1b5df..b7db43803c 100644
--- a/include/hw/compat.h
+++ b/include/hw/compat.h
@@ -14,6 +14,10 @@
         .driver   = "pflash_cfi01",\
         .property = "old-multiple-chip-handling",\
         .value    = "on",\
+    },{\
+        .driver   = "pci-bridge",\
+        .property = "shpc",\
+        .value    = "on",\
     },
 
 #define HW_COMPAT_2_7 \
diff --git a/include/hw/cris/etraxfs.h b/include/hw/cris/etraxfs.h
index 723a2753c8..8da965addb 100644
--- a/include/hw/cris/etraxfs.h
+++ b/include/hw/cris/etraxfs.h
@@ -48,7 +48,7 @@ etraxfs_eth_init(NICInfo *nd, hwaddr base, int phyaddr,
 
 static inline DeviceState *etraxfs_ser_create(hwaddr addr,
                                               qemu_irq irq,
-                                              CharDriverState *chr)
+                                              Chardev *chr)
 {
     DeviceState *dev;
     SysBusDevice *s;
diff --git a/include/hw/devices.h b/include/hw/devices.h
index c60bcabae3..7475b714de 100644
--- a/include/hw/devices.h
+++ b/include/hw/devices.h
@@ -65,6 +65,6 @@ qemu_irq tc6393xb_l3v_get(TC6393xbState *s);
 /* sm501.c */
 void sm501_init(struct MemoryRegion *address_space_mem, uint32_t base,
                 uint32_t local_mem_bytes, qemu_irq irq,
-                CharDriverState *chr);
+                Chardev *chr);
 
 #endif
diff --git a/include/hw/i386/apic_internal.h b/include/hw/i386/apic_internal.h
index 1209eb483a..20ad28c95b 100644
--- a/include/hw/i386/apic_internal.h
+++ b/include/hw/i386/apic_internal.h
@@ -189,6 +189,8 @@ struct APICCommonState {
     DeviceState *vapic;
     hwaddr vapic_paddr; /* note: persistence via kvmvapic */
     bool legacy_instance_id;
+
+    int apic_irq_delivered; /* for saving static variable */
 };
 
 typedef struct VAPICState {
diff --git a/include/hw/i386/ich9.h b/include/hw/i386/ich9.h
index 5fd7e97d23..18dcca7ebc 100644
--- a/include/hw/i386/ich9.h
+++ b/include/hw/i386/ich9.h
@@ -64,6 +64,16 @@ typedef struct ICH9LPCState {
     uint8_t rst_cnt;
     MemoryRegion rst_cnt_mem;
 
+    /* SMI feature negotiation via fw_cfg */
+    uint64_t smi_host_features;       /* guest-invisible, host endian */
+    uint8_t smi_host_features_le[8];  /* guest-visible, read-only, little
+                                       * endian uint64_t */
+    uint8_t smi_guest_features_le[8]; /* guest-visible, read-write, little
+                                       * endian uint64_t */
+    uint8_t smi_features_ok;          /* guest-visible, read-only; selecting it
+                                       * triggers feature lockdown */
+    uint64_t smi_negotiated_features; /* guest-invisible, host endian */
+
     /* isa bus */
     ISABus *isa_bus;
     MemoryRegion rcrb_mem; /* root complex register block */
@@ -240,4 +250,7 @@ Object *ich9_lpc_find(void);
 #define ICH9_SMB_HST_D1                         0x06
 #define ICH9_SMB_HOST_BLOCK_DB                  0x07
 
+/* bit positions used in fw_cfg SMI feature negotiation */
+#define ICH9_LPC_SMI_F_BROADCAST_BIT            0
+
 #endif /* HW_ICH9_H */
diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h
index 5a20c5e38e..079e8d9393 100644
--- a/include/hw/i386/pc.h
+++ b/include/hw/i386/pc.h
@@ -181,7 +181,7 @@ void parallel_hds_isa_init(ISABus *bus, int n);
 
 bool parallel_mm_init(MemoryRegion *address_space,
                       hwaddr base, int it_shift, qemu_irq irq,
-                      CharDriverState *chr);
+                      Chardev *chr);
 
 /* i8259.c */
 
@@ -381,6 +381,16 @@ bool e820_get_entry(int, uint32_t, uint64_t *, uint64_t *);
         .property = "x-mach-use-reliable-get-clock",\
         .value    = "off",\
     },\
+    {\
+        .driver   = "ICH9-LPC",\
+        .property = "x-smi-broadcast",\
+        .value    = "off",\
+    },\
+    {\
+        .driver   = TYPE_X86_CPU,\
+        .property = "vmware-cpuid-freq",\
+        .value    = "off",\
+    },
 
 #define PC_COMPAT_2_7 \
     HW_COMPAT_2_7 \
diff --git a/include/hw/input/ps2.h b/include/hw/input/ps2.h
index b9ceee4154..0fec91cdb0 100644
--- a/include/hw/input/ps2.h
+++ b/include/hw/input/ps2.h
@@ -25,6 +25,12 @@
 #ifndef HW_PS2_H
 #define HW_PS2_H
 
+#define PS2_MOUSE_BUTTON_LEFT   0x01
+#define PS2_MOUSE_BUTTON_MIDDLE 0x02
+#define PS2_MOUSE_BUTTON_RIGHT  0x04
+#define PS2_MOUSE_BUTTON_SIDE   0x08
+#define PS2_MOUSE_BUTTON_EXTRA  0x10
+
 /* ps2.c */
 void *ps2_kbd_init(void (*update_irq)(void *, int), void *update_arg);
 void *ps2_mouse_init(void (*update_irq)(void *, int), void *update_arg);
diff --git a/include/hw/m68k/mcf.h b/include/hw/m68k/mcf.h
index bf43998d9b..9a0bcfa0f4 100644
--- a/include/hw/m68k/mcf.h
+++ b/include/hw/m68k/mcf.h
@@ -11,10 +11,10 @@ uint64_t mcf_uart_read(void *opaque, hwaddr addr,
                        unsigned size);
 void mcf_uart_write(void *opaque, hwaddr addr,
                     uint64_t val, unsigned size);
-void *mcf_uart_init(qemu_irq irq, CharDriverState *chr);
+void *mcf_uart_init(qemu_irq irq, Chardev *chr);
 void mcf_uart_mm_init(struct MemoryRegion *sysmem,
                       hwaddr base,
-                      qemu_irq irq, CharDriverState *chr);
+                      qemu_irq irq, Chardev *chr);
 
 /* mcf_intc.c */
 qemu_irq *mcf_intc_init(struct MemoryRegion *sysmem,
diff --git a/include/hw/pci/msix.h b/include/hw/pci/msix.h
index 048a29dd2f..1f27658d35 100644
--- a/include/hw/pci/msix.h
+++ b/include/hw/pci/msix.h
@@ -9,9 +9,10 @@ MSIMessage msix_get_message(PCIDevice *dev, unsigned int vector);
 int msix_init(PCIDevice *dev, unsigned short nentries,
               MemoryRegion *table_bar, uint8_t table_bar_nr,
               unsigned table_offset, MemoryRegion *pba_bar,
-              uint8_t pba_bar_nr, unsigned pba_offset, uint8_t cap_pos);
+              uint8_t pba_bar_nr, unsigned pba_offset, uint8_t cap_pos,
+              Error **errp);
 int msix_init_exclusive_bar(PCIDevice *dev, unsigned short nentries,
-                            uint8_t bar_nr);
+                            uint8_t bar_nr, Error **errp);
 
 void msix_write_config(PCIDevice *dev, uint32_t address, uint32_t val, int len);
 
diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h
index 772692f1b2..cbc1fdfb5b 100644
--- a/include/hw/pci/pci.h
+++ b/include/hw/pci/pci.h
@@ -96,6 +96,7 @@
 #define PCI_DEVICE_ID_REDHAT_PXB         0x0009
 #define PCI_DEVICE_ID_REDHAT_BRIDGE_SEAT 0x000a
 #define PCI_DEVICE_ID_REDHAT_PXB_PCIE    0x000b
+#define PCI_DEVICE_ID_REDHAT_PCIE_RP     0x000c
 #define PCI_DEVICE_ID_REDHAT_QXL         0x0100
 
 #define FMT_PCIBUS                      PRIx64
diff --git a/include/hw/pci/pcie_port.h b/include/hw/pci/pcie_port.h
index f7b64db00c..13332668e8 100644
--- a/include/hw/pci/pcie_port.h
+++ b/include/hw/pci/pcie_port.h
@@ -57,4 +57,23 @@ PCIESlot *pcie_chassis_find_slot(uint8_t chassis, uint16_t slot);
 int pcie_chassis_add_slot(struct PCIESlot *slot);
 void pcie_chassis_del_slot(PCIESlot *s);
 
+#define TYPE_PCIE_ROOT_PORT         "pcie-root-port-base"
+#define PCIE_ROOT_PORT_CLASS(klass) \
+     OBJECT_CLASS_CHECK(PCIERootPortClass, (klass), TYPE_PCIE_ROOT_PORT)
+#define PCIE_ROOT_PORT_GET_CLASS(obj) \
+     OBJECT_GET_CLASS(PCIERootPortClass, (obj), TYPE_PCIE_ROOT_PORT)
+
+typedef struct PCIERootPortClass {
+    PCIDeviceClass parent_class;
+
+    uint8_t (*aer_vector)(const PCIDevice *dev);
+    int (*interrupts_init)(PCIDevice *dev, Error **errp);
+    void (*interrupts_uninit)(PCIDevice *dev);
+
+    int exp_offset;
+    int aer_offset;
+    int ssvid_offset;
+    int ssid;
+} PCIERootPortClass;
+
 #endif /* QEMU_PCIE_PORT_H */
diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h
index bd5bcf70de..a2d8964f7e 100644
--- a/include/hw/ppc/spapr.h
+++ b/include/hw/ppc/spapr.h
@@ -347,7 +347,8 @@ struct sPAPRMachineState {
 #define H_XIRR_X                0x2FC
 #define H_RANDOM                0x300
 #define H_SET_MODE              0x31C
-#define MAX_HCALL_OPCODE        H_SET_MODE
+#define H_SIGNAL_SYS_RESET      0x380
+#define MAX_HCALL_OPCODE        H_SIGNAL_SYS_RESET
 
 /* The hcalls above are standardized in PAPR and implemented by pHyp
  * as well.
@@ -589,7 +590,6 @@ void spapr_events_init(sPAPRMachineState *sm);
 void spapr_dt_events(sPAPRMachineState *sm, void *fdt);
 int spapr_h_cas_compose_response(sPAPRMachineState *sm,
                                  target_ulong addr, target_ulong size,
-                                 bool cpu_update,
                                  sPAPROptionVector *ov5_updates);
 sPAPRTCETable *spapr_tce_new_table(DeviceState *owner, uint32_t liobn);
 void spapr_tce_table_enable(sPAPRTCETable *tcet,
@@ -614,7 +614,6 @@ void spapr_hotplug_req_add_by_count_indexed(sPAPRDRConnectorType drc_type,
                                             uint32_t count, uint32_t index);
 void spapr_hotplug_req_remove_by_count_indexed(sPAPRDRConnectorType drc_type,
                                                uint32_t count, uint32_t index);
-void spapr_cpu_init(sPAPRMachineState *spapr, PowerPCCPU *cpu, Error **errp);
 void *spapr_populate_hotplug_cpu_dt(CPUState *cs, int *fdt_offset,
                                     sPAPRMachineState *spapr);
 
@@ -662,4 +661,6 @@ int spapr_rng_populate_dt(void *fdt);
 #define SPAPR_LMB_FLAGS_DRC_INVALID 0x00000020
 #define SPAPR_LMB_FLAGS_RESERVED 0x00000080
 
+void spapr_do_system_reset_on_cpu(CPUState *cs, run_on_cpu_data arg);
+
 #endif /* HW_SPAPR_H */
diff --git a/include/hw/ppc/spapr_vio.h b/include/hw/ppc/spapr_vio.h
index 14f502240e..fc6f673ea0 100644
--- a/include/hw/ppc/spapr_vio.h
+++ b/include/hw/ppc/spapr_vio.h
@@ -127,7 +127,7 @@ int spapr_vio_send_crq(VIOsPAPRDevice *dev, uint8_t *crq);
 
 VIOsPAPRDevice *vty_lookup(sPAPRMachineState *spapr, target_ulong reg);
 void vty_putchars(VIOsPAPRDevice *sdev, uint8_t *buf, int len);
-void spapr_vty_create(VIOsPAPRBus *bus, CharDriverState *chardev);
+void spapr_vty_create(VIOsPAPRBus *bus, Chardev *chardev);
 void spapr_vlan_create(VIOsPAPRBus *bus, NICInfo *nd);
 void spapr_vscsi_create(VIOsPAPRBus *bus);
 
diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h
index 2c973473f7..b44b476765 100644
--- a/include/hw/qdev-core.h
+++ b/include/hw/qdev-core.h
@@ -26,6 +26,7 @@ typedef enum DeviceCategory {
     DEVICE_CATEGORY_DISPLAY,
     DEVICE_CATEGORY_SOUND,
     DEVICE_CATEGORY_MISC,
+    DEVICE_CATEGORY_CPU,
     DEVICE_CATEGORY_MAX
 } DeviceCategory;
 
diff --git a/include/hw/qdev-properties.h b/include/hw/qdev-properties.h
index 306bbab088..7ac315331a 100644
--- a/include/hw/qdev-properties.h
+++ b/include/hw/qdev-properties.h
@@ -184,7 +184,7 @@ void qdev_prop_set_uint32(DeviceState *dev, const char *name, uint32_t value);
 void qdev_prop_set_int32(DeviceState *dev, const char *name, int32_t value);
 void qdev_prop_set_uint64(DeviceState *dev, const char *name, uint64_t value);
 void qdev_prop_set_string(DeviceState *dev, const char *name, const char *value);
-void qdev_prop_set_chr(DeviceState *dev, const char *name, CharDriverState *value);
+void qdev_prop_set_chr(DeviceState *dev, const char *name, Chardev *value);
 void qdev_prop_set_netdev(DeviceState *dev, const char *name, NetClientState *value);
 void qdev_prop_set_drive(DeviceState *dev, const char *name,
                          BlockBackend *value, Error **errp);
diff --git a/include/hw/sh4/sh.h b/include/hw/sh4/sh.h
index e59b9e7c45..767a2df7e2 100644
--- a/include/hw/sh4/sh.h
+++ b/include/hw/sh4/sh.h
@@ -42,7 +42,7 @@ void tmu012_init(struct MemoryRegion *sysmem, hwaddr base,
 #define SH_SERIAL_FEAT_SCIF (1 << 0)
 void sh_serial_init(MemoryRegion *sysmem,
                     hwaddr base, int feat,
-		     uint32_t freq, CharDriverState *chr,
+                    uint32_t freq, Chardev *chr,
 		     qemu_irq eri_source,
 		     qemu_irq rxi_source,
 		     qemu_irq txi_source,
diff --git a/include/hw/sparc/grlib.h b/include/hw/sparc/grlib.h
index afbb9bc07c..61a345c269 100644
--- a/include/hw/sparc/grlib.h
+++ b/include/hw/sparc/grlib.h
@@ -100,7 +100,7 @@ DeviceState *grlib_gptimer_create(hwaddr  base,
 
 static inline
 DeviceState *grlib_apbuart_create(hwaddr  base,
-                                  CharDriverState    *serial,
+                                  Chardev    *serial,
                                   qemu_irq            irq)
 {
     DeviceState *dev;
diff --git a/include/hw/usb.h b/include/hw/usb.h
index 847c9dec7f..43838c9f5d 100644
--- a/include/hw/usb.h
+++ b/include/hw/usb.h
@@ -471,6 +471,7 @@ void usb_generic_async_ctrl_complete(USBDevice *s, USBPacket *p);
 /* usb-linux.c */
 USBDevice *usb_host_device_open(USBBus *bus, const char *devname);
 void hmp_info_usbhost(Monitor *mon, const QDict *qdict);
+bool usb_host_dev_is_scsi_storage(USBDevice *usbdev);
 
 /* usb ports of the VM */
 
diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h
index 6523bacd2f..525da24222 100644
--- a/include/hw/virtio/virtio.h
+++ b/include/hw/virtio/virtio.h
@@ -182,7 +182,6 @@ void virtqueue_get_avail_bytes(VirtQueue *vq, unsigned int *in_bytes,
                                unsigned int *out_bytes,
                                unsigned max_in_bytes, unsigned max_out_bytes);
 
-bool virtio_should_notify(VirtIODevice *vdev, VirtQueue *vq);
 void virtio_notify_irqfd(VirtIODevice *vdev, VirtQueue *vq);
 void virtio_notify(VirtIODevice *vdev, VirtQueue *vq);
 
diff --git a/include/hw/xen/xen.h b/include/hw/xen/xen.h
index a8f3afb03b..09c2ce5170 100644
--- a/include/hw/xen/xen.h
+++ b/include/hw/xen/xen.h
@@ -37,7 +37,7 @@ int xen_is_pirq_msi(uint32_t msi_data);
 
 qemu_irq *xen_interrupt_controller_init(void);
 
-void xenstore_store_pv_console_info(int i, struct CharDriverState *chr);
+void xenstore_store_pv_console_info(int i, struct Chardev *chr);
 
 void xen_hvm_init(PCMachineState *pcms, MemoryRegion **ram_memory);
 
diff --git a/include/hw/xen/xen_common.h b/include/hw/xen/xen_common.h
index 8e1580d526..dce76ee162 100644
--- a/include/hw/xen/xen_common.h
+++ b/include/hw/xen/xen_common.h
@@ -18,7 +18,7 @@
 #include "hw/xen/xen.h"
 #include "hw/pci/pci.h"
 #include "qemu/queue.h"
-#include "trace.h"
+#include "hw/xen/trace.h"
 
 /*
  * We don't support Xen prior to 4.2.0.
diff --git a/include/monitor/monitor.h b/include/monitor/monitor.h
index 8cc532ec0e..e64b944d7c 100644
--- a/include/monitor/monitor.h
+++ b/include/monitor/monitor.h
@@ -16,7 +16,7 @@ extern Monitor *cur_mon;
 
 bool monitor_cur_is_qmp(void);
 
-void monitor_init(CharDriverState *chr, int flags);
+void monitor_init(Chardev *chr, int flags);
 void monitor_cleanup(void);
 
 int monitor_suspend(Monitor *mon);
diff --git a/include/qemu/compiler.h b/include/qemu/compiler.h
index 157698bfa9..e0ce9ffb28 100644
--- a/include/qemu/compiler.h
+++ b/include/qemu/compiler.h
@@ -85,8 +85,20 @@
 #define typeof_field(type, field) typeof(((type *)0)->field)
 #define type_check(t1,t2) ((t1*)0 - (t2*)0)
 
-#define QEMU_BUILD_BUG_ON(x) \
-    typedef char glue(qemu_build_bug_on__,__LINE__)[(x)?-1:1] __attribute__((unused));
+#define QEMU_BUILD_BUG_ON_STRUCT(x) \
+    struct { \
+        int:(x) ? -1 : 1; \
+    }
+
+#ifdef __COUNTER__
+#define QEMU_BUILD_BUG_ON(x) typedef QEMU_BUILD_BUG_ON_STRUCT(x) \
+    glue(qemu_build_bug_on__, __COUNTER__) __attribute__((unused))
+#else
+#define QEMU_BUILD_BUG_ON(x)
+#endif
+
+#define QEMU_BUILD_BUG_ON_ZERO(x) (sizeof(QEMU_BUILD_BUG_ON_STRUCT(x)) - \
+                                   sizeof(QEMU_BUILD_BUG_ON_STRUCT(x)))
 
 #if defined __GNUC__
 # if !QEMU_GNUC_PREREQ(4, 4)
diff --git a/include/qemu/host-utils.h b/include/qemu/host-utils.h
index 96288d0bce..a38be42253 100644
--- a/include/qemu/host-utils.h
+++ b/include/qemu/host-utils.h
@@ -513,4 +513,31 @@ static inline uint64_t pow2ceil(uint64_t value)
     return 1ULL << (64 - nlz);
 }
 
+/**
+ * urshift - 128-bit Unsigned Right Shift.
+ * @plow: in/out - lower 64-bit integer.
+ * @phigh: in/out - higher 64-bit integer.
+ * @shift: in - bytes to shift, between 0 and 127.
+ *
+ * Result is zero-extended and stored in plow/phigh, which are
+ * input/output variables. Shift values outside the range will
+ * be mod to 128. In other words, the caller is responsible to
+ * verify/assert both the shift range and plow/phigh pointers.
+ */
+void urshift(uint64_t *plow, uint64_t *phigh, int32_t shift);
+
+/**
+ * ulshift - 128-bit Unsigned Left Shift.
+ * @plow: in/out - lower 64-bit integer.
+ * @phigh: in/out - higher 64-bit integer.
+ * @shift: in - bytes to shift, between 0 and 127.
+ * @overflow: out - true if any 1-bit is shifted out.
+ *
+ * Result is zero-extended and stored in plow/phigh, which are
+ * input/output variables. Shift values outside the range will
+ * be mod to 128. In other words, the caller is responsible to
+ * verify/assert both the shift range and plow/phigh pointers.
+ */
+void ulshift(uint64_t *plow, uint64_t *phigh, int32_t shift, bool *overflow);
+
 #endif
diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h
index 689f253ea7..56c9e22405 100644
--- a/include/qemu/osdep.h
+++ b/include/qemu/osdep.h
@@ -198,8 +198,15 @@ extern int daemon(int, int);
 #define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))
 #endif
 
+/*
+ * &(x)[0] is always a pointer - if it's same type as x then the argument is a
+ * pointer, not an array.
+ */
+#define QEMU_IS_ARRAY(x) (!__builtin_types_compatible_p(typeof(x), \
+                                                        typeof(&(x)[0])))
 #ifndef ARRAY_SIZE
-#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+#define ARRAY_SIZE(x) ((sizeof(x) / sizeof((x)[0])) + \
+                       QEMU_BUILD_BUG_ON_ZERO(!QEMU_IS_ARRAY(x)))
 #endif
 
 int qemu_daemon(int nochdir, int noclose);
diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h
index 9a8bcbde36..e95f28cfec 100644
--- a/include/qemu/typedefs.h
+++ b/include/qemu/typedefs.h
@@ -17,7 +17,7 @@ typedef struct BlockBackendRootState BlockBackendRootState;
 typedef struct BlockDriverState BlockDriverState;
 typedef struct BusClass BusClass;
 typedef struct BusState BusState;
-typedef struct CharDriverState CharDriverState;
+typedef struct Chardev Chardev;
 typedef struct CompatProperty CompatProperty;
 typedef struct CPUAddressSpace CPUAddressSpace;
 typedef struct CPUState CPUState;
diff --git a/include/sysemu/char.h b/include/sysemu/char.h
index 0a149428cf..450881d42c 100644
--- a/include/sysemu/char.h
+++ b/include/sysemu/char.h
@@ -2,14 +2,10 @@
 #define QEMU_CHAR_H
 
 #include "qemu-common.h"
-#include "qemu/queue.h"
 #include "qemu/option.h"
-#include "qemu/config-file.h"
-#include "block/aio.h"
-#include "qapi/qmp/qobject.h"
-#include "qapi/qmp/qstring.h"
 #include "qemu/main-loop.h"
 #include "qemu/bitmap.h"
+#include "qom/object.h"
 
 /* character device */
 
@@ -21,6 +17,7 @@ typedef enum {
     CHR_EVENT_CLOSED /* connection closed */
 } QEMUChrEvent;
 
+#define CHR_READ_BUF_LEN 4096
 
 #define CHR_IOCTL_SERIAL_SET_PARAMS   1
 typedef struct {
@@ -69,14 +66,16 @@ typedef enum {
     /* Whether it is possible to send/recv file descriptors
      * over the data channel */
     QEMU_CHAR_FEATURE_FD_PASS,
+    /* Whether replay or record mode is enabled */
+    QEMU_CHAR_FEATURE_REPLAY,
 
     QEMU_CHAR_FEATURE_LAST,
-} CharDriverFeature;
+} ChardevFeature;
 
 /* This is the backend as seen by frontend, the actual backend is
- * CharDriverState */
+ * Chardev */
 typedef struct CharBackend {
-    CharDriverState *chr;
+    Chardev *chr;
     IOEventHandler *chr_event;
     IOCanReadHandler *chr_can_read;
     IOReadHandler *chr_read;
@@ -85,49 +84,21 @@ typedef struct CharBackend {
     int fe_open;
 } CharBackend;
 
-struct CharDriverState {
+struct Chardev {
+    Object parent_obj;
+
     QemuMutex chr_write_lock;
-    int (*chr_write)(struct CharDriverState *s, const uint8_t *buf, int len);
-    int (*chr_sync_read)(struct CharDriverState *s,
-                         const uint8_t *buf, int len);
-    GSource *(*chr_add_watch)(struct CharDriverState *s, GIOCondition cond);
-    void (*chr_update_read_handler)(struct CharDriverState *s,
-                                    GMainContext *context);
-    int (*chr_ioctl)(struct CharDriverState *s, int cmd, void *arg);
-    int (*get_msgfds)(struct CharDriverState *s, int* fds, int num);
-    int (*set_msgfds)(struct CharDriverState *s, int *fds, int num);
-    int (*chr_add_client)(struct CharDriverState *chr, int fd);
-    int (*chr_wait_connected)(struct CharDriverState *chr, Error **errp);
-    void (*chr_free)(struct CharDriverState *chr);
-    void (*chr_disconnect)(struct CharDriverState *chr);
-    void (*chr_accept_input)(struct CharDriverState *chr);
-    void (*chr_set_echo)(struct CharDriverState *chr, bool echo);
-    void (*chr_set_fe_open)(struct CharDriverState *chr, int fe_open);
     CharBackend *be;
-    void *opaque;
     char *label;
     char *filename;
     int logfd;
     int be_open;
-    int is_mux;
     guint fd_in_tag;
-    bool replay;
     DECLARE_BITMAP(features, QEMU_CHAR_FEATURE_LAST);
-    QTAILQ_ENTRY(CharDriverState) next;
+    QTAILQ_ENTRY(Chardev) next;
 };
 
 /**
- * qemu_chr_alloc:
- * @backend: the common backend config
- * @errp: pointer to a NULL-initialized error object
- *
- * Allocate and initialize a new CharDriverState.
- *
- * Returns: a newly allocated CharDriverState, or NULL on error.
- */
-CharDriverState *qemu_chr_alloc(ChardevCommon *backend, Error **errp);
-
-/**
  * @qemu_chr_new_from_opts:
  *
  * Create a new character backend from a QemuOpts list.
@@ -136,8 +107,8 @@ CharDriverState *qemu_chr_alloc(ChardevCommon *backend, Error **errp);
  *
  * Returns: a new character backend
  */
-CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts,
-                                        Error **errp);
+Chardev *qemu_chr_new_from_opts(QemuOpts *opts,
+                                Error **errp);
 
 /**
  * @qemu_chr_parse_common:
@@ -159,14 +130,14 @@ void qemu_chr_parse_common(QemuOpts *opts, ChardevCommon *backend);
  *
  * Returns: a new character backend
  */
-CharDriverState *qemu_chr_new(const char *label, const char *filename);
+Chardev *qemu_chr_new(const char *label, const char *filename);
 
 
 /**
  * @qemu_chr_fe_disconnect:
  *
  * Close a fd accpeted by character backend.
- * Without associated CharDriver, do nothing.
+ * Without associated Chardev, do nothing.
  */
 void qemu_chr_fe_disconnect(CharBackend *be);
 
@@ -181,7 +152,7 @@ void qemu_chr_cleanup(void);
  * @qemu_chr_fe_wait_connected:
  *
  * Wait for characted backend to be connected, return < 0 on error or
- * if no assicated CharDriver.
+ * if no assicated Chardev.
  */
 int qemu_chr_fe_wait_connected(CharBackend *be, Error **errp);
 
@@ -197,7 +168,7 @@ int qemu_chr_fe_wait_connected(CharBackend *be, Error **errp);
  *
  * Returns: a new character backend
  */
-CharDriverState *qemu_chr_new_noreplay(const char *label, const char *filename);
+Chardev *qemu_chr_new_noreplay(const char *label, const char *filename);
 
 /**
  * @qemu_chr_delete:
@@ -205,14 +176,7 @@ CharDriverState *qemu_chr_new_noreplay(const char *label, const char *filename);
  * Destroy a character backend and remove it from the list of
  * identified character backends.
  */
-void qemu_chr_delete(CharDriverState *chr);
-
-/**
- * @qemu_chr_free:
- *
- * Destroy a character backend.
- */
-void qemu_chr_free(CharDriverState *chr);
+void qemu_chr_delete(Chardev *chr);
 
 /**
  * @qemu_chr_fe_set_echo:
@@ -220,7 +184,7 @@ void qemu_chr_free(CharDriverState *chr);
  * Ask the backend to override its normal echo setting.  This only really
  * applies to the stdio backend and is used by the QMP server such that you
  * can see what you type if you try to type QMP commands.
- * Without associated CharDriver, do nothing.
+ * Without associated Chardev, do nothing.
  *
  * @echo true to enable echo, false to disable echo
  */
@@ -231,7 +195,7 @@ void qemu_chr_fe_set_echo(CharBackend *be, bool echo);
  *
  * Set character frontend open status.  This is an indication that the
  * front end is ready (or not) to begin doing I/O.
- * Without associated CharDriver, do nothing.
+ * Without associated Chardev, do nothing.
  */
 void qemu_chr_fe_set_open(CharBackend *be, int fe_open);
 
@@ -240,7 +204,7 @@ void qemu_chr_fe_set_open(CharBackend *be, int fe_open);
  *
  * Write to a character backend using a printf style interface.  This
  * function is thread-safe. It does nothing without associated
- * CharDriver.
+ * Chardev.
  *
  * @fmt see #printf
  */
@@ -253,11 +217,13 @@ void qemu_chr_fe_printf(CharBackend *be, const char *fmt, ...)
  * If the backend is connected, create and add a #GSource that fires
  * when the given condition (typically G_IO_OUT|G_IO_HUP or G_IO_HUP)
  * is active; return the #GSource's tag.  If it is disconnected,
- * or without associated CharDriver, return 0.
+ * or without associated Chardev, return 0.
  *
  * @cond the condition to poll for
  * @func the function to call when the condition happens
  * @user_data the opaque pointer to pass to @func
+ *
+ * Returns: the source tag
  */
 guint qemu_chr_fe_add_watch(CharBackend *be, GIOCondition cond,
                             GIOFunc func, void *user_data);
@@ -272,7 +238,7 @@ guint qemu_chr_fe_add_watch(CharBackend *be, GIOCondition cond,
  * @buf the data
  * @len the number of bytes to send
  *
- * Returns: the number of bytes consumed (0 if no assicated CharDriver)
+ * Returns: the number of bytes consumed (0 if no assicated Chardev)
  */
 int qemu_chr_fe_write(CharBackend *be, const uint8_t *buf, int len);
 
@@ -287,7 +253,7 @@ int qemu_chr_fe_write(CharBackend *be, const uint8_t *buf, int len);
  * @buf the data
  * @len the number of bytes to send
  *
- * Returns: the number of bytes consumed (0 if no assicated CharDriver)
+ * Returns: the number of bytes consumed (0 if no assicated Chardev)
  */
 int qemu_chr_fe_write_all(CharBackend *be, const uint8_t *buf, int len);
 
@@ -299,7 +265,7 @@ int qemu_chr_fe_write_all(CharBackend *be, const uint8_t *buf, int len);
  * @buf the data buffer
  * @len the number of bytes to read
  *
- * Returns: the number of bytes read (0 if no assicated CharDriver)
+ * Returns: the number of bytes read (0 if no assicated Chardev)
  */
 int qemu_chr_fe_read_all(CharBackend *be, uint8_t *buf, int len);
 
@@ -312,7 +278,7 @@ int qemu_chr_fe_read_all(CharBackend *be, uint8_t *buf, int len);
  * @arg the data associated with @cmd
  *
  * Returns: if @cmd is not supported by the backend or there is no
- *          associated CharDriver, -ENOTSUP, otherwise the return
+ *          associated Chardev, -ENOTSUP, otherwise the return
  *          value depends on the semantics of @cmd
  */
 int qemu_chr_fe_ioctl(CharBackend *be, int cmd, void *arg);
@@ -352,7 +318,7 @@ int qemu_chr_fe_get_msgfds(CharBackend *be, int *fds, int num);
  * result in overwriting the fd array with the new value without being send.
  * Upon writing the message the fd array is freed.
  *
- * Returns: -1 if fd passing isn't supported or no associated CharDriver.
+ * Returns: -1 if fd passing isn't supported or no associated Chardev.
  */
 int qemu_chr_fe_set_msgfds(CharBackend *be, int *fds, int num);
 
@@ -366,7 +332,7 @@ int qemu_chr_fe_set_msgfds(CharBackend *be, int *fds, int num);
  *
  * Returns: the number of bytes the front end can receive via @qemu_chr_be_write
  */
-int qemu_chr_be_can_write(CharDriverState *s);
+int qemu_chr_be_can_write(Chardev *s);
 
 /**
  * @qemu_chr_be_write:
@@ -378,7 +344,7 @@ int qemu_chr_be_can_write(CharDriverState *s);
  * @buf a buffer to receive data from the front end
  * @len the number of bytes to receive from the front end
  */
-void qemu_chr_be_write(CharDriverState *s, uint8_t *buf, int len);
+void qemu_chr_be_write(Chardev *s, uint8_t *buf, int len);
 
 /**
  * @qemu_chr_be_write_impl:
@@ -388,7 +354,7 @@ void qemu_chr_be_write(CharDriverState *s, uint8_t *buf, int len);
  * @buf a buffer to receive data from the front end
  * @len the number of bytes to receive from the front end
  */
-void qemu_chr_be_write_impl(CharDriverState *s, uint8_t *buf, int len);
+void qemu_chr_be_write_impl(Chardev *s, uint8_t *buf, int len);
 
 /**
  * @qemu_chr_be_event:
@@ -397,33 +363,33 @@ void qemu_chr_be_write_impl(CharDriverState *s, uint8_t *buf, int len);
  *
  * @event the event to send
  */
-void qemu_chr_be_event(CharDriverState *s, int event);
+void qemu_chr_be_event(Chardev *s, int event);
 
 /**
  * @qemu_chr_fe_init:
  *
  * Initializes a front end for the given CharBackend and
- * CharDriver. Call qemu_chr_fe_deinit() to remove the association and
+ * Chardev. Call qemu_chr_fe_deinit() to remove the association and
  * release the driver.
  *
  * Returns: false on error.
  */
-bool qemu_chr_fe_init(CharBackend *b, CharDriverState *s, Error **errp);
+bool qemu_chr_fe_init(CharBackend *b, Chardev *s, Error **errp);
 
 /**
  * @qemu_chr_fe_get_driver:
  *
  * Returns the driver associated with a CharBackend or NULL if no
- * associated CharDriver.
+ * associated Chardev.
  */
-CharDriverState *qemu_chr_fe_get_driver(CharBackend *be);
+Chardev *qemu_chr_fe_get_driver(CharBackend *be);
 
 /**
  * @qemu_chr_fe_deinit:
  *
- * Dissociate the CharBackend from the CharDriver.
+ * Dissociate the CharBackend from the Chardev.
  *
- * Safe to call without associated CharDriver.
+ * Safe to call without associated Chardev.
  */
 void qemu_chr_fe_deinit(CharBackend *b);
 
@@ -442,7 +408,7 @@ void qemu_chr_fe_deinit(CharBackend *b);
  * Set the front end char handlers. The front end takes the focus if
  * any of the handler is non-NULL.
  *
- * Without associated CharDriver, nothing is changed.
+ * Without associated Chardev, nothing is changed.
  */
 void qemu_chr_fe_set_handlers(CharBackend *b,
                               IOCanReadHandler *fd_can_read,
@@ -457,37 +423,79 @@ void qemu_chr_fe_set_handlers(CharBackend *b,
  *
  * Take the focus (if the front end is muxed).
  *
- * Without associated CharDriver, nothing is changed.
+ * Without associated Chardev, nothing is changed.
  */
 void qemu_chr_fe_take_focus(CharBackend *b);
 
-void qemu_chr_be_generic_open(CharDriverState *s);
+void qemu_chr_be_generic_open(Chardev *s);
 void qemu_chr_fe_accept_input(CharBackend *be);
-int qemu_chr_add_client(CharDriverState *s, int fd);
-CharDriverState *qemu_chr_find(const char *name);
-bool chr_is_ringbuf(const CharDriverState *chr);
-
-bool qemu_chr_has_feature(CharDriverState *chr,
-                          CharDriverFeature feature);
-void qemu_chr_set_feature(CharDriverState *chr,
-                          CharDriverFeature feature);
-QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename);
-
-typedef void CharDriverParse(QemuOpts *opts, ChardevBackend *backend,
-                             Error **errp);
-typedef CharDriverState *CharDriverCreate(const char *id,
-                                          ChardevBackend *backend,
-                                          ChardevReturn *ret, bool *be_opened,
-                                          Error **errp);
+int qemu_chr_add_client(Chardev *s, int fd);
+Chardev *qemu_chr_find(const char *name);
 
-void register_char_driver(const char *name, ChardevBackendKind kind,
-                          CharDriverParse *parse, CharDriverCreate *create);
+bool qemu_chr_has_feature(Chardev *chr,
+                          ChardevFeature feature);
+void qemu_chr_set_feature(Chardev *chr,
+                          ChardevFeature feature);
+QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename);
+int qemu_chr_write_all(Chardev *s, const uint8_t *buf, int len);
+int qemu_chr_wait_connected(Chardev *chr, Error **errp);
+
+#define TYPE_CHARDEV "chardev"
+#define CHARDEV(obj) OBJECT_CHECK(Chardev, (obj), TYPE_CHARDEV)
+#define CHARDEV_CLASS(klass) \
+    OBJECT_CLASS_CHECK(ChardevClass, (klass), TYPE_CHARDEV)
+#define CHARDEV_GET_CLASS(obj) \
+    OBJECT_GET_CLASS(ChardevClass, (obj), TYPE_CHARDEV)
+
+#define TYPE_CHARDEV_NULL "chardev-null"
+#define TYPE_CHARDEV_MUX "chardev-mux"
+#define TYPE_CHARDEV_RINGBUF "chardev-ringbuf"
+#define TYPE_CHARDEV_PTY "chardev-pty"
+#define TYPE_CHARDEV_CONSOLE "chardev-console"
+#define TYPE_CHARDEV_STDIO "chardev-stdio"
+#define TYPE_CHARDEV_PIPE "chardev-pipe"
+#define TYPE_CHARDEV_MEMORY "chardev-memory"
+#define TYPE_CHARDEV_PARALLEL "chardev-parallel"
+#define TYPE_CHARDEV_FILE "chardev-file"
+#define TYPE_CHARDEV_SERIAL "chardev-serial"
+#define TYPE_CHARDEV_SOCKET "chardev-socket"
+#define TYPE_CHARDEV_UDP "chardev-udp"
+
+#define CHARDEV_IS_RINGBUF(chr) \
+    object_dynamic_cast(OBJECT(chr), TYPE_CHARDEV_RINGBUF)
+#define CHARDEV_IS_PTY(chr) \
+    object_dynamic_cast(OBJECT(chr), TYPE_CHARDEV_PTY)
+
+typedef struct ChardevClass {
+    ObjectClass parent_class;
+
+    bool internal; /* TODO: eventually use TYPE_USER_CREATABLE */
+    void (*parse)(QemuOpts *opts, ChardevBackend *backend, Error **errp);
+
+    void (*open)(Chardev *chr, ChardevBackend *backend,
+                 bool *be_opened, Error **errp);
+
+    int (*chr_write)(Chardev *s, const uint8_t *buf, int len);
+    int (*chr_sync_read)(Chardev *s, const uint8_t *buf, int len);
+    GSource *(*chr_add_watch)(Chardev *s, GIOCondition cond);
+    void (*chr_update_read_handler)(Chardev *s, GMainContext *context);
+    int (*chr_ioctl)(Chardev *s, int cmd, void *arg);
+    int (*get_msgfds)(Chardev *s, int* fds, int num);
+    int (*set_msgfds)(Chardev *s, int *fds, int num);
+    int (*chr_add_client)(Chardev *chr, int fd);
+    int (*chr_wait_connected)(Chardev *chr, Error **errp);
+    void (*chr_disconnect)(Chardev *chr);
+    void (*chr_accept_input)(Chardev *chr);
+    void (*chr_set_echo)(Chardev *chr, bool echo);
+    void (*chr_set_fe_open)(Chardev *chr, int fe_open);
+} ChardevClass;
+
+Chardev *qemu_chardev_new(const char *id, const char *typename,
+                          ChardevBackend *backend, Error **errp);
 
 extern int term_escape_char;
 
-
 /* console.c */
-typedef CharDriverState *(VcHandler)(ChardevVC *vc, Error **errp);
-void register_vc_handler(VcHandler *handler);
+void qemu_chr_parse_vc(QemuOpts *opts, ChardevBackend *backend, Error **errp);
 
 #endif
diff --git a/include/sysemu/replay.h b/include/sysemu/replay.h
index abb35ca8c9..7aad20b07f 100644
--- a/include/sysemu/replay.h
+++ b/include/sysemu/replay.h
@@ -43,6 +43,9 @@ typedef struct ReplayNetState ReplayNetState;
 
 extern ReplayMode replay_mode;
 
+/* Name of the initial VM snapshot */
+extern char *replay_snapshot;
+
 /* Replay process control functions */
 
 /*! Enables recording or saving event log with specified parameters */
@@ -125,9 +128,9 @@ uint64_t blkreplay_next_id(void);
 /* Character device */
 
 /*! Registers char driver to save it's events */
-void replay_register_char_driver(struct CharDriverState *chr);
+void replay_register_char_driver(struct Chardev *chr);
 /*! Saves write to char device event to the log */
-void replay_chr_be_write(struct CharDriverState *s, uint8_t *buf, int len);
+void replay_chr_be_write(struct Chardev *s, uint8_t *buf, int len);
 /*! Writes char write return value to the replay log. */
 void replay_char_write_event_save(int res, int offset);
 /*! Reads char write return value from the replay log. */
@@ -149,4 +152,10 @@ void replay_unregister_net(ReplayNetState *rns);
 void replay_net_packet_event(ReplayNetState *rns, unsigned flags,
                              const struct iovec *iov, int iovcnt);
 
+/* VM state operations */
+
+/*! Called at the start of execution.
+    Loads or saves initial vmstate depending on execution mode. */
+void replay_vmstate_init(void);
+
 #endif
diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h
index ff8ffb5e47..4d50694930 100644
--- a/include/sysemu/sysemu.h
+++ b/include/sysemu/sysemu.h
@@ -74,6 +74,7 @@ void qemu_add_machine_init_done_notifier(Notifier *notify);
 void qemu_remove_machine_init_done_notifier(Notifier *notify);
 
 void hmp_savevm(Monitor *mon, const QDict *qdict);
+int save_vmstate(Monitor *mon, const char *name);
 int load_vmstate(const char *name);
 void hmp_delvm(Monitor *mon, const QDict *qdict);
 void hmp_info_snapshots(Monitor *mon, const QDict *qdict);
@@ -189,13 +190,13 @@ void hmp_pcie_aer_inject_error(Monitor *mon, const QDict *qdict);
 
 #define MAX_SERIAL_PORTS 4
 
-extern CharDriverState *serial_hds[MAX_SERIAL_PORTS];
+extern Chardev *serial_hds[MAX_SERIAL_PORTS];
 
 /* parallel ports */
 
 #define MAX_PARALLEL_PORTS 3
 
-extern CharDriverState *parallel_hds[MAX_PARALLEL_PORTS];
+extern Chardev *parallel_hds[MAX_PARALLEL_PORTS];
 
 void hmp_usb_add(Monitor *mon, const QDict *qdict);
 void hmp_usb_del(Monitor *mon, const QDict *qdict);
diff --git a/include/trace.h b/include/trace.h
deleted file mode 100644
index ac9ff3dddd..0000000000
--- a/include/trace.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef TRACE_H
-#define TRACE_H
-
-#include "trace/generated-tracers.h"
-
-#endif /* TRACE_H */
diff --git a/include/ui/console.h b/include/ui/console.h
index b59e7b8c15..af6350e96f 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -383,6 +383,8 @@ void graphic_hw_invalidate(QemuConsole *con);
 void graphic_hw_text_update(QemuConsole *con, console_ch_t *chardata);
 void graphic_hw_gl_block(QemuConsole *con, bool block);
 
+void qemu_console_early_init(void);
+
 QemuConsole *qemu_console_lookup_by_index(unsigned int index);
 QemuConsole *qemu_console_lookup_by_device(DeviceState *dev, uint32_t head);
 QemuConsole *qemu_console_lookup_by_device_name(const char *device_id,
diff --git a/include/ui/gtk.h b/include/ui/gtk.h
index b3b50059c7..47ffddb5b4 100644
--- a/include/ui/gtk.h
+++ b/include/ui/gtk.h
@@ -64,7 +64,7 @@ typedef struct VirtualVteConsole {
     GtkWidget *box;
     GtkWidget *scrollbar;
     GtkWidget *terminal;
-    CharDriverState *chr;
+    Chardev *chr;
     bool echo;
 } VirtualVteConsole;
 #endif
diff --git a/include/ui/qemu-spice.h b/include/ui/qemu-spice.h
index 75e12396bb..52a9f8808b 100644
--- a/include/ui/qemu-spice.h
+++ b/include/ui/qemu-spice.h
@@ -51,7 +51,7 @@ int qemu_spice_migrate_info(const char *hostname, int port, int tls_port,
 #if SPICE_SERVER_VERSION >= 0x000c02
 void qemu_spice_register_ports(void);
 #else
-static inline CharDriverState *qemu_chr_open_spice_port(const char *name)
+static inline Chardev *qemu_chr_open_spice_port(const char *name)
 { return NULL; }
 #endif
 
diff --git a/ioport.c b/ioport.c
index 94e08ab657..1a65addb66 100644
--- a/ioport.c
+++ b/ioport.c
@@ -29,7 +29,7 @@
 #include "qemu-common.h"
 #include "cpu.h"
 #include "exec/ioport.h"
-#include "trace.h"
+#include "trace-root.h"
 #include "exec/memory.h"
 #include "exec/address-spaces.h"
 
diff --git a/kvm-all.c b/kvm-all.c
index 330219e9dc..a27c880c05 100644
--- a/kvm-all.c
+++ b/kvm-all.c
@@ -34,7 +34,7 @@
 #include "exec/ram_addr.h"
 #include "exec/address-spaces.h"
 #include "qemu/event_notifier.h"
-#include "trace.h"
+#include "trace-root.h"
 #include "hw/irq.h"
 
 #include "hw/boards.h"
diff --git a/linux-user/main.c b/linux-user/main.c
index 30049581ef..e588f58f2a 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -4322,6 +4322,8 @@ int main(int argc, char **argv, char **envp)
 # endif
 #elif defined TARGET_SH4
         cpu_model = TYPE_SH7785_CPU;
+#elif defined TARGET_S390X
+        cpu_model = "qemu";
 #else
         cpu_model = "any";
 #endif
diff --git a/memory.c b/memory.c
index 2bfc37f65c..6c58373422 100644
--- a/memory.c
+++ b/memory.c
@@ -24,7 +24,7 @@
 #include "qemu/bitops.h"
 #include "qemu/error-report.h"
 #include "qom/object.h"
-#include "trace.h"
+#include "trace-root.h"
 
 #include "exec/memory-internal.h"
 #include "exec/ram_addr.h"
@@ -2450,6 +2450,21 @@ void address_space_destroy(AddressSpace *as)
     call_rcu(as, do_address_space_destroy, rcu);
 }
 
+static const char *memory_region_type(MemoryRegion *mr)
+{
+    if (memory_region_is_ram_device(mr)) {
+        return "ramd";
+    } else if (memory_region_is_romd(mr)) {
+        return "romd";
+    } else if (memory_region_is_rom(mr)) {
+        return "rom";
+    } else if (memory_region_is_ram(mr)) {
+        return "ram";
+    } else {
+        return "i/o";
+    }
+}
+
 typedef struct MemoryRegionList MemoryRegionList;
 
 struct MemoryRegionList {
@@ -2459,6 +2474,10 @@ struct MemoryRegionList {
 
 typedef QTAILQ_HEAD(queue, MemoryRegionList) MemoryRegionListHead;
 
+#define MR_SIZE(size) (int128_nz(size) ? (hwaddr)int128_get64( \
+                           int128_sub((size), int128_one())) : 0)
+#define MTREE_INDENT "  "
+
 static void mtree_print_mr(fprintf_function mon_printf, void *f,
                            const MemoryRegion *mr, unsigned int level,
                            hwaddr base,
@@ -2474,7 +2493,7 @@ static void mtree_print_mr(fprintf_function mon_printf, void *f,
     }
 
     for (i = 0; i < level; i++) {
-        mon_printf(f, "  ");
+        mon_printf(f, MTREE_INDENT);
     }
 
     if (mr->alias) {
@@ -2494,37 +2513,24 @@ static void mtree_print_mr(fprintf_function mon_printf, void *f,
             QTAILQ_INSERT_TAIL(alias_print_queue, ml, queue);
         }
         mon_printf(f, TARGET_FMT_plx "-" TARGET_FMT_plx
-                   " (prio %d, %c%c): alias %s @%s " TARGET_FMT_plx
+                   " (prio %d, %s): alias %s @%s " TARGET_FMT_plx
                    "-" TARGET_FMT_plx "%s\n",
                    base + mr->addr,
-                   base + mr->addr
-                   + (int128_nz(mr->size) ?
-                      (hwaddr)int128_get64(int128_sub(mr->size,
-                                                      int128_one())) : 0),
+                   base + mr->addr + MR_SIZE(mr->size),
                    mr->priority,
-                   mr->romd_mode ? 'R' : '-',
-                   !mr->readonly && !(mr->rom_device && mr->romd_mode) ? 'W'
-                                                                       : '-',
+                   memory_region_type((MemoryRegion *)mr),
                    memory_region_name(mr),
                    memory_region_name(mr->alias),
                    mr->alias_offset,
-                   mr->alias_offset
-                   + (int128_nz(mr->size) ?
-                      (hwaddr)int128_get64(int128_sub(mr->size,
-                                                      int128_one())) : 0),
+                   mr->alias_offset + MR_SIZE(mr->size),
                    mr->enabled ? "" : " [disabled]");
     } else {
         mon_printf(f,
-                   TARGET_FMT_plx "-" TARGET_FMT_plx " (prio %d, %c%c): %s%s\n",
+                   TARGET_FMT_plx "-" TARGET_FMT_plx " (prio %d, %s): %s%s\n",
                    base + mr->addr,
-                   base + mr->addr
-                   + (int128_nz(mr->size) ?
-                      (hwaddr)int128_get64(int128_sub(mr->size,
-                                                      int128_one())) : 0),
+                   base + mr->addr + MR_SIZE(mr->size),
                    mr->priority,
-                   mr->romd_mode ? 'R' : '-',
-                   !mr->readonly && !(mr->rom_device && mr->romd_mode) ? 'W'
-                                                                       : '-',
+                   memory_region_type((MemoryRegion *)mr),
                    memory_region_name(mr),
                    mr->enabled ? "" : " [disabled]");
     }
@@ -2558,12 +2564,51 @@ static void mtree_print_mr(fprintf_function mon_printf, void *f,
     }
 }
 
-void mtree_info(fprintf_function mon_printf, void *f)
+static void mtree_print_flatview(fprintf_function p, void *f,
+                                 AddressSpace *as)
+{
+    FlatView *view = address_space_get_flatview(as);
+    FlatRange *range = &view->ranges[0];
+    MemoryRegion *mr;
+    int n = view->nr;
+
+    if (n <= 0) {
+        p(f, MTREE_INDENT "No rendered FlatView for "
+          "address space '%s'\n", as->name);
+        flatview_unref(view);
+        return;
+    }
+
+    while (n--) {
+        mr = range->mr;
+        p(f, MTREE_INDENT TARGET_FMT_plx "-"
+          TARGET_FMT_plx " (prio %d, %s): %s\n",
+          int128_get64(range->addr.start),
+          int128_get64(range->addr.start) + MR_SIZE(range->addr.size),
+          mr->priority,
+          memory_region_type(mr),
+          memory_region_name(mr));
+        range++;
+    }
+
+    flatview_unref(view);
+}
+
+void mtree_info(fprintf_function mon_printf, void *f, bool flatview)
 {
     MemoryRegionListHead ml_head;
     MemoryRegionList *ml, *ml2;
     AddressSpace *as;
 
+    if (flatview) {
+        QTAILQ_FOREACH(as, &address_spaces, address_spaces_link) {
+            mon_printf(f, "address-space (flat view): %s\n", as->name);
+            mtree_print_flatview(mon_printf, f, as);
+            mon_printf(f, "\n");
+        }
+        return;
+    }
+
     QTAILQ_INIT(&ml_head);
 
     QTAILQ_FOREACH(as, &address_spaces, address_spaces_link) {
diff --git a/migration/savevm.c b/migration/savevm.c
index 455d5bac1e..204012ecef 100644
--- a/migration/savevm.c
+++ b/migration/savevm.c
@@ -2042,38 +2042,40 @@ int qemu_loadvm_state(QEMUFile *f)
     return ret;
 }
 
-void hmp_savevm(Monitor *mon, const QDict *qdict)
+int save_vmstate(Monitor *mon, const char *name)
 {
     BlockDriverState *bs, *bs1;
     QEMUSnapshotInfo sn1, *sn = &sn1, old_sn1, *old_sn = &old_sn1;
-    int ret;
+    int ret = -1;
     QEMUFile *f;
     int saved_vm_running;
     uint64_t vm_state_size;
     qemu_timeval tv;
     struct tm tm;
-    const char *name = qdict_get_try_str(qdict, "name");
     Error *local_err = NULL;
     AioContext *aio_context;
 
     if (!bdrv_all_can_snapshot(&bs)) {
         monitor_printf(mon, "Device '%s' is writable but does not "
                        "support snapshots.\n", bdrv_get_device_name(bs));
-        return;
+        return ret;
     }
 
     /* Delete old snapshots of the same name */
-    if (name && bdrv_all_delete_snapshot(name, &bs1, &local_err) < 0) {
-        error_reportf_err(local_err,
-                          "Error while deleting snapshot on device '%s': ",
-                          bdrv_get_device_name(bs1));
-        return;
+    if (name) {
+        ret = bdrv_all_delete_snapshot(name, &bs1, &local_err);
+        if (ret < 0) {
+            error_reportf_err(local_err,
+                              "Error while deleting snapshot on device '%s': ",
+                              bdrv_get_device_name(bs1));
+            return ret;
+        }
     }
 
     bs = bdrv_all_find_vmstate_bs();
     if (bs == NULL) {
         monitor_printf(mon, "No block device can accept snapshots\n");
-        return;
+        return ret;
     }
     aio_context = bdrv_get_aio_context(bs);
 
@@ -2082,7 +2084,7 @@ void hmp_savevm(Monitor *mon, const QDict *qdict)
     ret = global_state_store();
     if (ret) {
         monitor_printf(mon, "Error saving global state\n");
-        return;
+        return ret;
     }
     vm_stop(RUN_STATE_SAVE_VM);
 
@@ -2128,13 +2130,22 @@ void hmp_savevm(Monitor *mon, const QDict *qdict)
     if (ret < 0) {
         monitor_printf(mon, "Error while creating snapshot on '%s'\n",
                        bdrv_get_device_name(bs));
+        goto the_end;
     }
 
+    ret = 0;
+
  the_end:
     aio_context_release(aio_context);
     if (saved_vm_running) {
         vm_start();
     }
+    return ret;
+}
+
+void hmp_savevm(Monitor *mon, const QDict *qdict)
+{
+    save_vmstate(mon, qdict_get_try_str(qdict, "name"));
 }
 
 void qmp_xen_save_devices_state(const char *filename, Error **errp)
diff --git a/migration/trace-events b/migration/trace-events
index 48e531d3b8..fa660e35b1 100644
--- a/migration/trace-events
+++ b/migration/trace-events
@@ -4,8 +4,6 @@
 qemu_loadvm_state_section(unsigned int section_type) "%d"
 qemu_loadvm_state_section_command(int ret) "%d"
 qemu_loadvm_state_section_partend(uint32_t section_id) "%u"
-qemu_loadvm_state_main(void) ""
-qemu_loadvm_state_main_quit_parent(void) ""
 qemu_loadvm_state_post_main(int ret) "%d"
 qemu_loadvm_state_section_startfull(uint32_t section_id, const char *idstr, uint32_t instance_id, uint32_t version_id) "%u(%s) %u %u"
 qemu_savevm_send_packaged(void) ""
@@ -118,7 +116,6 @@ qemu_rdma_accept_incoming_migration_accepted(void) ""
 qemu_rdma_accept_pin_state(bool pin) "%d"
 qemu_rdma_accept_pin_verbsc(void *verbs) "Verbs context after listen: %p"
 qemu_rdma_block_for_wrid_miss(const char *wcompstr, int wcomp, const char *gcompstr, uint64_t req) "A Wanted wrid %s (%d) but got %s (%" PRIu64 ")"
-qemu_rdma_block_for_wrid_miss_b(const char *wcompstr, int wcomp, const char *gcompstr, uint64_t req) "B Wanted wrid %s (%d) but got %s (%" PRIu64 ")"
 qemu_rdma_cleanup_disconnect(void) ""
 qemu_rdma_cleanup_waiting_for_disconnect(void) ""
 qemu_rdma_close(void) ""
diff --git a/monitor.c b/monitor.c
index 8b06b63729..3cd72a9bab 100644
--- a/monitor.c
+++ b/monitor.c
@@ -40,6 +40,7 @@
 #include "sysemu/sysemu.h"
 #include "sysemu/numa.h"
 #include "monitor/monitor.h"
+#include "qemu/config-file.h"
 #include "qemu/readline.h"
 #include "ui/console.h"
 #include "ui/input.h"
@@ -59,7 +60,7 @@
 #include "qapi/qmp/json-streamer.h"
 #include "qapi/qmp/json-parser.h"
 #include "qom/object_interfaces.h"
-#include "trace.h"
+#include "trace-root.h"
 #include "trace/control.h"
 #include "monitor/hmp-target.h"
 #ifdef CONFIG_TRACE_SIMPLE
@@ -1529,7 +1530,9 @@ static void hmp_boot_set(Monitor *mon, const QDict *qdict)
 
 static void hmp_info_mtree(Monitor *mon, const QDict *qdict)
 {
-    mtree_info((fprintf_function)monitor_printf, mon);
+    bool flatview = qdict_get_try_bool(qdict, "flatview", false);
+
+    mtree_info((fprintf_function)monitor_printf, mon, flatview);
 }
 
 static void hmp_info_numa(Monitor *mon, const QDict *qdict)
@@ -3193,8 +3196,8 @@ static void ringbuf_completion(ReadLineState *rs, const char *str)
         ChardevInfo *chr_info = list->value;
 
         if (!strncmp(chr_info->label, str, len)) {
-            CharDriverState *chr = qemu_chr_find(chr_info->label);
-            if (chr && chr_is_ringbuf(chr)) {
+            Chardev *chr = qemu_chr_find(chr_info->label);
+            if (chr && CHARDEV_IS_RINGBUF(chr)) {
                 readline_add_completion(rs, chr_info->label);
             }
         }
@@ -3983,7 +3986,7 @@ static void __attribute__((constructor)) monitor_lock_init(void)
     qemu_mutex_init(&monitor_lock);
 }
 
-void monitor_init(CharDriverState *chr, int flags)
+void monitor_init(Chardev *chr, int flags)
 {
     static int is_first_init = 1;
     Monitor *mon;
diff --git a/net/colo-compare.c b/net/colo-compare.c
index 9bfc736f55..4962976c22 100644
--- a/net/colo-compare.c
+++ b/net/colo-compare.c
@@ -564,7 +564,7 @@ static void compare_sec_rs_finalize(SocketReadState *sec_rs)
  * Return 0 is success.
  * Return 1 is failed.
  */
-static int find_and_check_chardev(CharDriverState **chr,
+static int find_and_check_chardev(Chardev **chr,
                                   char *chr_name,
                                   Error **errp)
 {
@@ -611,7 +611,7 @@ static void check_old_packet_regular(void *opaque)
 static void colo_compare_complete(UserCreatable *uc, Error **errp)
 {
     CompareState *s = COLO_COMPARE(uc);
-    CharDriverState *chr;
+    Chardev *chr;
     char thread_name[64];
     static int compare_id;
 
diff --git a/net/filter-mirror.c b/net/filter-mirror.c
index b7d645617c..aa0aa98fa5 100644
--- a/net/filter-mirror.c
+++ b/net/filter-mirror.c
@@ -191,7 +191,7 @@ static void filter_redirector_cleanup(NetFilterState *nf)
 static void filter_mirror_setup(NetFilterState *nf, Error **errp)
 {
     MirrorState *s = FILTER_MIRROR(nf);
-    CharDriverState *chr;
+    Chardev *chr;
 
     if (!s->outdev) {
         error_setg(errp, "filter mirror needs 'outdev' "
@@ -220,7 +220,7 @@ static void redirector_rs_finalize(SocketReadState *rs)
 static void filter_redirector_setup(NetFilterState *nf, Error **errp)
 {
     MirrorState *s = FILTER_REDIRECTOR(nf);
-    CharDriverState *chr;
+    Chardev *chr;
 
     if (!s->indev && !s->outdev) {
         error_setg(errp, "filter redirector needs 'indev' or "
diff --git a/net/slirp.c b/net/slirp.c
index bcd1c5f57d..f97ec23345 100644
--- a/net/slirp.c
+++ b/net/slirp.c
@@ -748,7 +748,7 @@ static int slirp_guestfwd(SlirpState *s, const char *config_str,
         }
     } else {
         Error *err = NULL;
-        CharDriverState *chr = qemu_chr_new(buf, p);
+        Chardev *chr = qemu_chr_new(buf, p);
 
         if (!chr) {
             error_report("could not open guest forwarding device '%s'", buf);
diff --git a/net/vhost-user.c b/net/vhost-user.c
index 7aff77ee4a..77b8110f8c 100644
--- a/net/vhost-user.c
+++ b/net/vhost-user.c
@@ -151,7 +151,10 @@ static void vhost_user_cleanup(NetClientState *nc)
         s->vhost_net = NULL;
     }
     if (nc->queue_index == 0) {
+        Chardev *chr = qemu_chr_fe_get_driver(&s->chr);
+
         qemu_chr_fe_deinit(&s->chr);
+        qemu_chr_delete(chr);
     }
 
     qemu_purge_queued_packets(nc);
@@ -195,7 +198,7 @@ static void net_vhost_user_event(void *opaque, int event)
     const char *name = opaque;
     NetClientState *ncs[MAX_QUEUE_NUM];
     VhostUserState *s;
-    CharDriverState *chr;
+    Chardev *chr;
     Error *err = NULL;
     int queues;
 
@@ -232,7 +235,7 @@ static void net_vhost_user_event(void *opaque, int event)
 }
 
 static int net_vhost_user_init(NetClientState *peer, const char *device,
-                               const char *name, CharDriverState *chr,
+                               const char *name, Chardev *chr,
                                int queues)
 {
     Error *err = NULL;
@@ -274,10 +277,10 @@ static int net_vhost_user_init(NetClientState *peer, const char *device,
     return 0;
 }
 
-static CharDriverState *net_vhost_claim_chardev(
+static Chardev *net_vhost_claim_chardev(
     const NetdevVhostUserOptions *opts, Error **errp)
 {
-    CharDriverState *chr = qemu_chr_find(opts->chardev);
+    Chardev *chr = qemu_chr_find(opts->chardev);
 
     if (chr == NULL) {
         error_setg(errp, "chardev \"%s\" not found", opts->chardev);
@@ -324,7 +327,7 @@ int net_init_vhost_user(const Netdev *netdev, const char *name,
 {
     int queues;
     const NetdevVhostUserOptions *vhost_user_opts;
-    CharDriverState *chr;
+    Chardev *chr;
 
     assert(netdev->type == NET_CLIENT_DRIVER_VHOST_USER);
     vhost_user_opts = &netdev->u.vhost_user;
diff --git a/qapi-schema.json b/qapi-schema.json
index 82fabc6e24..cbdffddbc6 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -5390,10 +5390,15 @@
 #
 # Button of a pointer input device (mouse, tablet).
 #
+# @side: front side button of a 5-button mouse (since 2.9)
+#
+# @extra: rear side button of a 5-button mouse (since 2.9)
+#
 # Since: 2.0
 ##
 { 'enum'  : 'InputButton',
-  'data'  : [ 'left', 'middle', 'right', 'wheel-up', 'wheel-down' ] }
+  'data'  : [ 'left', 'middle', 'right', 'wheel-up', 'wheel-down', 'side',
+  'extra' ] }
 
 ##
 # @InputAxis:
diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
index 63bd97b341..e6e93f02e6 100644
--- a/qapi/qapi-visit-core.c
+++ b/qapi/qapi-visit-core.c
@@ -374,6 +374,7 @@ void visit_type_enum(Visitor *v, const char *name, int *obj,
                      const char *const strings[], Error **errp)
 {
     assert(obj && strings);
+    trace_visit_type_enum(v, name, obj);
     switch (v->type) {
     case VISITOR_INPUT:
         input_type_enum(v, name, obj, strings, errp);
diff --git a/qapi/trace-events b/qapi/trace-events
index 2c5d3bc7d7..9cbb61b2bd 100644
--- a/qapi/trace-events
+++ b/qapi/trace-events
@@ -1,4 +1,4 @@
-# qapi-visit-core.c
+# qapi/qapi-visit-core.c
 visit_free(void *v) "v=%p"
 visit_complete(void *v, void *opaque) "v=%p opaque=%p"
 
diff --git a/qdev-monitor.c b/qdev-monitor.c
index 81d01df928..549f45f066 100644
--- a/qdev-monitor.c
+++ b/qdev-monitor.c
@@ -137,6 +137,7 @@ static void qdev_print_devinfos(bool show_no_user)
         [DEVICE_CATEGORY_DISPLAY] = "Display",
         [DEVICE_CATEGORY_SOUND]   = "Sound",
         [DEVICE_CATEGORY_MISC]    = "Misc",
+        [DEVICE_CATEGORY_CPU]     = "CPU",
         [DEVICE_CATEGORY_MAX]     = "Uncategorized",
     };
     GSList *list, *elt;
diff --git a/qemu-char.c b/qemu-char.c
deleted file mode 100644
index d8da1677ff..0000000000
--- a/qemu-char.c
+++ /dev/null
@@ -1,4963 +0,0 @@
-/*
- * QEMU System Emulator
- *
- * Copyright (c) 2003-2008 Fabrice Bellard
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-#include "qemu/osdep.h"
-#include "qemu-common.h"
-#include "qemu/cutils.h"
-#include "monitor/monitor.h"
-#include "sysemu/sysemu.h"
-#include "sysemu/block-backend.h"
-#include "qemu/error-report.h"
-#include "qemu/timer.h"
-#include "sysemu/char.h"
-#include "hw/usb.h"
-#include "qmp-commands.h"
-#include "qapi/clone-visitor.h"
-#include "qapi-visit.h"
-#include "qemu/base64.h"
-#include "io/channel-socket.h"
-#include "io/channel-file.h"
-#include "io/channel-tls.h"
-#include "sysemu/replay.h"
-#include "qemu/help_option.h"
-
-#include <zlib.h>
-
-#ifndef _WIN32
-#include <sys/times.h>
-#include <sys/wait.h>
-#include <termios.h>
-#include <sys/ioctl.h>
-#include <sys/resource.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <net/if.h>
-#include <arpa/inet.h>
-#include <netdb.h>
-#include <sys/select.h>
-#ifdef CONFIG_BSD
-#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
-#include <dev/ppbus/ppi.h>
-#include <dev/ppbus/ppbconf.h>
-#elif defined(__DragonFly__)
-#include <dev/misc/ppi/ppi.h>
-#include <bus/ppbus/ppbconf.h>
-#endif
-#else
-#ifdef __linux__
-#include <linux/ppdev.h>
-#include <linux/parport.h>
-#endif
-#ifdef __sun__
-#include <sys/ethernet.h>
-#include <sys/sockio.h>
-#include <netinet/arp.h>
-#include <netinet/in.h>
-#include <netinet/in_systm.h>
-#include <netinet/ip.h>
-#include <netinet/ip_icmp.h> // must come after ip.h
-#include <netinet/udp.h>
-#include <netinet/tcp.h>
-#endif
-#endif
-#endif
-
-#include "qemu/sockets.h"
-#include "ui/qemu-spice.h"
-
-#define READ_BUF_LEN 4096
-#define READ_RETRIES 10
-#define TCP_MAX_FDS 16
-
-typedef struct MuxDriver MuxDriver;
-
-/***********************************************************/
-/* Socket address helpers */
-
-static char *SocketAddress_to_str(const char *prefix, SocketAddress *addr,
-                                  bool is_listen, bool is_telnet)
-{
-    switch (addr->type) {
-    case SOCKET_ADDRESS_KIND_INET:
-        return g_strdup_printf("%s%s:%s:%s%s", prefix,
-                               is_telnet ? "telnet" : "tcp",
-                               addr->u.inet.data->host,
-                               addr->u.inet.data->port,
-                               is_listen ? ",server" : "");
-        break;
-    case SOCKET_ADDRESS_KIND_UNIX:
-        return g_strdup_printf("%sunix:%s%s", prefix,
-                               addr->u.q_unix.data->path,
-                               is_listen ? ",server" : "");
-        break;
-    case SOCKET_ADDRESS_KIND_FD:
-        return g_strdup_printf("%sfd:%s%s", prefix, addr->u.fd.data->str,
-                               is_listen ? ",server" : "");
-        break;
-    default:
-        abort();
-    }
-}
-
-static char *sockaddr_to_str(struct sockaddr_storage *ss, socklen_t ss_len,
-                             struct sockaddr_storage *ps, socklen_t ps_len,
-                             bool is_listen, bool is_telnet)
-{
-    char shost[NI_MAXHOST], sserv[NI_MAXSERV];
-    char phost[NI_MAXHOST], pserv[NI_MAXSERV];
-    const char *left = "", *right = "";
-
-    switch (ss->ss_family) {
-#ifndef _WIN32
-    case AF_UNIX:
-        return g_strdup_printf("unix:%s%s",
-                               ((struct sockaddr_un *)(ss))->sun_path,
-                               is_listen ? ",server" : "");
-#endif
-    case AF_INET6:
-        left  = "[";
-        right = "]";
-        /* fall through */
-    case AF_INET:
-        getnameinfo((struct sockaddr *) ss, ss_len, shost, sizeof(shost),
-                    sserv, sizeof(sserv), NI_NUMERICHOST | NI_NUMERICSERV);
-        getnameinfo((struct sockaddr *) ps, ps_len, phost, sizeof(phost),
-                    pserv, sizeof(pserv), NI_NUMERICHOST | NI_NUMERICSERV);
-        return g_strdup_printf("%s:%s%s%s:%s%s <-> %s%s%s:%s",
-                               is_telnet ? "telnet" : "tcp",
-                               left, shost, right, sserv,
-                               is_listen ? ",server" : "",
-                               left, phost, right, pserv);
-
-    default:
-        return g_strdup_printf("unknown");
-    }
-}
-
-/***********************************************************/
-/* character device */
-
-static QTAILQ_HEAD(CharDriverStateHead, CharDriverState) chardevs =
-    QTAILQ_HEAD_INITIALIZER(chardevs);
-
-static void qemu_chr_free_common(CharDriverState *chr);
-
-CharDriverState *qemu_chr_alloc(ChardevCommon *backend, Error **errp)
-{
-    CharDriverState *chr = g_malloc0(sizeof(CharDriverState));
-    qemu_mutex_init(&chr->chr_write_lock);
-
-    if (backend->has_logfile) {
-        int flags = O_WRONLY | O_CREAT;
-        if (backend->has_logappend &&
-            backend->logappend) {
-            flags |= O_APPEND;
-        } else {
-            flags |= O_TRUNC;
-        }
-        chr->logfd = qemu_open(backend->logfile, flags, 0666);
-        if (chr->logfd < 0) {
-            error_setg_errno(errp, errno,
-                             "Unable to open logfile %s",
-                             backend->logfile);
-            g_free(chr);
-            return NULL;
-        }
-    } else {
-        chr->logfd = -1;
-    }
-
-    return chr;
-}
-
-void qemu_chr_be_event(CharDriverState *s, int event)
-{
-    CharBackend *be = s->be;
-
-    /* Keep track if the char device is open */
-    switch (event) {
-        case CHR_EVENT_OPENED:
-            s->be_open = 1;
-            break;
-        case CHR_EVENT_CLOSED:
-            s->be_open = 0;
-            break;
-    }
-
-    if (!be || !be->chr_event) {
-        return;
-    }
-
-    be->chr_event(be->opaque, event);
-}
-
-void qemu_chr_be_generic_open(CharDriverState *s)
-{
-    qemu_chr_be_event(s, CHR_EVENT_OPENED);
-}
-
-
-/* Not reporting errors from writing to logfile, as logs are
- * defined to be "best effort" only */
-static void qemu_chr_fe_write_log(CharDriverState *s,
-                                  const uint8_t *buf, size_t len)
-{
-    size_t done = 0;
-    ssize_t ret;
-
-    if (s->logfd < 0) {
-        return;
-    }
-
-    while (done < len) {
-    retry:
-        ret = write(s->logfd, buf + done, len - done);
-        if (ret == -1 && errno == EAGAIN) {
-            g_usleep(100);
-            goto retry;
-        }
-
-        if (ret <= 0) {
-            return;
-        }
-        done += ret;
-    }
-}
-
-static int qemu_chr_fe_write_buffer(CharDriverState *s, const uint8_t *buf, int len, int *offset)
-{
-    int res = 0;
-    *offset = 0;
-
-    qemu_mutex_lock(&s->chr_write_lock);
-    while (*offset < len) {
-    retry:
-        res = s->chr_write(s, buf + *offset, len - *offset);
-        if (res < 0 && errno == EAGAIN) {
-            g_usleep(100);
-            goto retry;
-        }
-
-        if (res <= 0) {
-            break;
-        }
-
-        *offset += res;
-    }
-    if (*offset > 0) {
-        qemu_chr_fe_write_log(s, buf, *offset);
-    }
-    qemu_mutex_unlock(&s->chr_write_lock);
-
-    return res;
-}
-
-int qemu_chr_fe_write(CharBackend *be, const uint8_t *buf, int len)
-{
-    CharDriverState *s = be->chr;
-    int ret;
-
-    if (!s) {
-        return 0;
-    }
-
-    if (s->replay && replay_mode == REPLAY_MODE_PLAY) {
-        int offset;
-        replay_char_write_event_load(&ret, &offset);
-        assert(offset <= len);
-        qemu_chr_fe_write_buffer(s, buf, offset, &offset);
-        return ret;
-    }
-
-    qemu_mutex_lock(&s->chr_write_lock);
-    ret = s->chr_write(s, buf, len);
-
-    if (ret > 0) {
-        qemu_chr_fe_write_log(s, buf, ret);
-    }
-
-    qemu_mutex_unlock(&s->chr_write_lock);
-    
-    if (s->replay && replay_mode == REPLAY_MODE_RECORD) {
-        replay_char_write_event_save(ret, ret < 0 ? 0 : ret);
-    }
-    
-    return ret;
-}
-
-static int qemu_chr_write_all(CharDriverState *s, const uint8_t *buf, int len)
-{
-    int offset;
-    int res;
-
-    if (s->replay && replay_mode == REPLAY_MODE_PLAY) {
-        replay_char_write_event_load(&res, &offset);
-        assert(offset <= len);
-        qemu_chr_fe_write_buffer(s, buf, offset, &offset);
-        return res;
-    }
-
-    res = qemu_chr_fe_write_buffer(s, buf, len, &offset);
-
-    if (s->replay && replay_mode == REPLAY_MODE_RECORD) {
-        replay_char_write_event_save(res, offset);
-    }
-
-    if (res < 0) {
-        return res;
-    }
-    return offset;
-}
-
-int qemu_chr_fe_write_all(CharBackend *be, const uint8_t *buf, int len)
-{
-    CharDriverState *s = be->chr;
-
-    if (!s) {
-        return 0;
-    }
-
-    return qemu_chr_write_all(s, buf, len);
-}
-
-int qemu_chr_fe_read_all(CharBackend *be, uint8_t *buf, int len)
-{
-    CharDriverState *s = be->chr;
-    int offset = 0, counter = 10;
-    int res;
-
-    if (!s || !s->chr_sync_read) {
-        return 0;
-    }
-
-    if (s->replay && replay_mode == REPLAY_MODE_PLAY) {
-        return replay_char_read_all_load(buf);
-    }
-
-    while (offset < len) {
-    retry:
-        res = s->chr_sync_read(s, buf + offset, len - offset);
-        if (res == -1 && errno == EAGAIN) {
-            g_usleep(100);
-            goto retry;
-        }
-
-        if (res == 0) {
-            break;
-        }
-
-        if (res < 0) {
-            if (s->replay && replay_mode == REPLAY_MODE_RECORD) {
-                replay_char_read_all_save_error(res);
-            }
-            return res;
-        }
-
-        offset += res;
-
-        if (!counter--) {
-            break;
-        }
-    }
-
-    if (s->replay && replay_mode == REPLAY_MODE_RECORD) {
-        replay_char_read_all_save_buf(buf, offset);
-    }
-    return offset;
-}
-
-int qemu_chr_fe_ioctl(CharBackend *be, int cmd, void *arg)
-{
-    CharDriverState *s = be->chr;
-    int res;
-
-    if (!s || !s->chr_ioctl || s->replay) {
-        res = -ENOTSUP;
-    } else {
-        res = s->chr_ioctl(s, cmd, arg);
-    }
-
-    return res;
-}
-
-int qemu_chr_be_can_write(CharDriverState *s)
-{
-    CharBackend *be = s->be;
-
-    if (!be || !be->chr_can_read) {
-        return 0;
-    }
-
-    return be->chr_can_read(be->opaque);
-}
-
-void qemu_chr_be_write_impl(CharDriverState *s, uint8_t *buf, int len)
-{
-    CharBackend *be = s->be;
-
-    if (be && be->chr_read) {
-        be->chr_read(be->opaque, buf, len);
-    }
-}
-
-void qemu_chr_be_write(CharDriverState *s, uint8_t *buf, int len)
-{
-    if (s->replay) {
-        if (replay_mode == REPLAY_MODE_PLAY) {
-            return;
-        }
-        replay_chr_be_write(s, buf, len);
-    } else {
-        qemu_chr_be_write_impl(s, buf, len);
-    }
-}
-
-int qemu_chr_fe_get_msgfd(CharBackend *be)
-{
-    CharDriverState *s = be->chr;
-    int fd;
-    int res = (qemu_chr_fe_get_msgfds(be, &fd, 1) == 1) ? fd : -1;
-    if (s && s->replay) {
-        fprintf(stderr,
-                "Replay: get msgfd is not supported for serial devices yet\n");
-        exit(1);
-    }
-    return res;
-}
-
-int qemu_chr_fe_get_msgfds(CharBackend *be, int *fds, int len)
-{
-    CharDriverState *s = be->chr;
-
-    if (!s) {
-        return -1;
-    }
-
-    return s->get_msgfds ? s->get_msgfds(s, fds, len) : -1;
-}
-
-int qemu_chr_fe_set_msgfds(CharBackend *be, int *fds, int num)
-{
-    CharDriverState *s = be->chr;
-
-    if (!s) {
-        return -1;
-    }
-
-    return s->set_msgfds ? s->set_msgfds(s, fds, num) : -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_fe_accept_input(CharBackend *be)
-{
-    CharDriverState *s = be->chr;
-
-    if (!s) {
-        return;
-    }
-
-    if (s->chr_accept_input)
-        s->chr_accept_input(s);
-    qemu_notify_event();
-}
-
-void qemu_chr_fe_printf(CharBackend *be, const char *fmt, ...)
-{
-    char buf[READ_BUF_LEN];
-    va_list ap;
-    va_start(ap, fmt);
-    vsnprintf(buf, sizeof(buf), fmt, ap);
-    /* XXX this blocks entire thread. Rewrite to use
-     * qemu_chr_fe_write and background I/O callbacks */
-    qemu_chr_fe_write_all(be, (uint8_t *)buf, strlen(buf));
-    va_end(ap);
-}
-
-static void remove_fd_in_watch(CharDriverState *chr);
-static void mux_chr_set_handlers(CharDriverState *chr, GMainContext *context);
-static void mux_set_focus(CharDriverState *chr, int focus);
-
-static int null_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
-{
-    return len;
-}
-
-static CharDriverState *qemu_chr_open_null(const char *id,
-                                           ChardevBackend *backend,
-                                           ChardevReturn *ret,
-                                           bool *be_opened,
-                                           Error **errp)
-{
-    CharDriverState *chr;
-    ChardevCommon *common = backend->u.null.data;
-
-    chr = qemu_chr_alloc(common, errp);
-    if (!chr) {
-        return NULL;
-    }
-    chr->chr_write = null_chr_write;
-    *be_opened = false;
-    return chr;
-}
-
-/* MUX driver for serial I/O splitting */
-#define MAX_MUX 4
-#define MUX_BUFFER_SIZE 32	/* Must be a power of 2.  */
-#define MUX_BUFFER_MASK (MUX_BUFFER_SIZE - 1)
-struct MuxDriver {
-    CharBackend *backends[MAX_MUX];
-    CharBackend chr;
-    int focus;
-    int mux_cnt;
-    int term_got_escape;
-    int max_size;
-    /* Intermediate input buffer allows to catch escape sequences even if the
-       currently active device is not accepting any input - but only until it
-       is full as well. */
-    unsigned char buffer[MAX_MUX][MUX_BUFFER_SIZE];
-    int prod[MAX_MUX];
-    int cons[MAX_MUX];
-    int timestamps;
-
-    /* Protected by the CharDriverState chr_write_lock.  */
-    int linestart;
-    int64_t timestamps_start;
-};
-
-/* Called with chr_write_lock held.  */
-static int mux_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
-{
-    MuxDriver *d = chr->opaque;
-    int ret;
-    if (!d->timestamps) {
-        ret = qemu_chr_fe_write(&d->chr, buf, len);
-    } else {
-        int i;
-
-        ret = 0;
-        for (i = 0; i < len; i++) {
-            if (d->linestart) {
-                char buf1[64];
-                int64_t ti;
-                int secs;
-
-                ti = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
-                if (d->timestamps_start == -1)
-                    d->timestamps_start = ti;
-                ti -= d->timestamps_start;
-                secs = ti / 1000;
-                snprintf(buf1, sizeof(buf1),
-                         "[%02d:%02d:%02d.%03d] ",
-                         secs / 3600,
-                         (secs / 60) % 60,
-                         secs % 60,
-                         (int)(ti % 1000));
-                /* XXX this blocks entire thread. Rewrite to use
-                 * qemu_chr_fe_write and background I/O callbacks */
-                qemu_chr_fe_write_all(&d->chr,
-                                      (uint8_t *)buf1, strlen(buf1));
-                d->linestart = 0;
-            }
-            ret += qemu_chr_fe_write(&d->chr, buf + i, 1);
-            if (buf[i] == '\n') {
-                d->linestart = 1;
-            }
-        }
-    }
-    return ret;
-}
-
-static const char * const mux_help[] = {
-    "% h    print this help\n\r",
-    "% x    exit emulator\n\r",
-    "% s    save disk data back to file (if -snapshot)\n\r",
-    "% t    toggle console timestamps\n\r",
-    "% b    send break (magic sysrq)\n\r",
-    "% c    switch between console and monitor\n\r",
-    "% %  sends %\n\r",
-    NULL
-};
-
-int term_escape_char = 0x01; /* ctrl-a is used for escape */
-static void mux_print_help(CharDriverState *chr)
-{
-    int i, j;
-    char ebuf[15] = "Escape-Char";
-    char cbuf[50] = "\n\r";
-
-    if (term_escape_char > 0 && term_escape_char < 26) {
-        snprintf(cbuf, sizeof(cbuf), "\n\r");
-        snprintf(ebuf, sizeof(ebuf), "C-%c", term_escape_char - 1 + 'a');
-    } else {
-        snprintf(cbuf, sizeof(cbuf),
-                 "\n\rEscape-Char set to Ascii: 0x%02x\n\r\n\r",
-                 term_escape_char);
-    }
-    /* XXX this blocks entire thread. Rewrite to use
-     * qemu_chr_fe_write and background I/O callbacks */
-    qemu_chr_write_all(chr, (uint8_t *)cbuf, strlen(cbuf));
-    for (i = 0; mux_help[i] != NULL; i++) {
-        for (j=0; mux_help[i][j] != '\0'; j++) {
-            if (mux_help[i][j] == '%')
-                qemu_chr_write_all(chr, (uint8_t *)ebuf, strlen(ebuf));
-            else
-                qemu_chr_write_all(chr, (uint8_t *)&mux_help[i][j], 1);
-        }
-    }
-}
-
-static void mux_chr_send_event(MuxDriver *d, int mux_nr, int event)
-{
-    CharBackend *be = d->backends[mux_nr];
-
-    if (be && be->chr_event) {
-        be->chr_event(be->opaque, event);
-    }
-}
-
-static int mux_proc_byte(CharDriverState *chr, MuxDriver *d, int ch)
-{
-    if (d->term_got_escape) {
-        d->term_got_escape = 0;
-        if (ch == term_escape_char)
-            goto send_char;
-        switch(ch) {
-        case '?':
-        case 'h':
-            mux_print_help(chr);
-            break;
-        case 'x':
-            {
-                 const char *term =  "QEMU: Terminated\n\r";
-                 qemu_chr_write_all(chr, (uint8_t *)term, strlen(term));
-                 exit(0);
-                 break;
-            }
-        case 's':
-            blk_commit_all();
-            break;
-        case 'b':
-            qemu_chr_be_event(chr, CHR_EVENT_BREAK);
-            break;
-        case 'c':
-            assert(d->mux_cnt > 0); /* handler registered with first fe */
-            /* Switch to the next registered device */
-            mux_set_focus(chr, (d->focus + 1) % d->mux_cnt);
-            break;
-        case 't':
-            d->timestamps = !d->timestamps;
-            d->timestamps_start = -1;
-            d->linestart = 0;
-            break;
-        }
-    } else if (ch == term_escape_char) {
-        d->term_got_escape = 1;
-    } else {
-    send_char:
-        return 1;
-    }
-    return 0;
-}
-
-static void mux_chr_accept_input(CharDriverState *chr)
-{
-    MuxDriver *d = chr->opaque;
-    int m = d->focus;
-    CharBackend *be = d->backends[m];
-
-    while (be && d->prod[m] != d->cons[m] &&
-           be->chr_can_read && be->chr_can_read(be->opaque)) {
-        be->chr_read(be->opaque,
-                     &d->buffer[m][d->cons[m]++ & MUX_BUFFER_MASK], 1);
-    }
-}
-
-static int mux_chr_can_read(void *opaque)
-{
-    CharDriverState *chr = opaque;
-    MuxDriver *d = chr->opaque;
-    int m = d->focus;
-    CharBackend *be = d->backends[m];
-
-    if ((d->prod[m] - d->cons[m]) < MUX_BUFFER_SIZE) {
-        return 1;
-    }
-
-    if (be && be->chr_can_read) {
-        return be->chr_can_read(be->opaque);
-    }
-
-    return 0;
-}
-
-static void mux_chr_read(void *opaque, const uint8_t *buf, int size)
-{
-    CharDriverState *chr = opaque;
-    MuxDriver *d = chr->opaque;
-    int m = d->focus;
-    CharBackend *be = d->backends[m];
-    int i;
-
-    mux_chr_accept_input(opaque);
-
-    for (i = 0; i < size; i++)
-        if (mux_proc_byte(chr, d, buf[i])) {
-            if (d->prod[m] == d->cons[m] &&
-                be && be->chr_can_read &&
-                be->chr_can_read(be->opaque))
-                be->chr_read(be->opaque, &buf[i], 1);
-            else
-                d->buffer[m][d->prod[m]++ & MUX_BUFFER_MASK] = buf[i];
-        }
-}
-
-static bool muxes_realized;
-
-static void mux_chr_event(void *opaque, int event)
-{
-    CharDriverState *chr = opaque;
-    MuxDriver *d = chr->opaque;
-    int i;
-
-    if (!muxes_realized) {
-        return;
-    }
-
-    /* Send the event to all registered listeners */
-    for (i = 0; i < d->mux_cnt; i++)
-        mux_chr_send_event(d, i, event);
-}
-
-/**
- * Called after processing of default and command-line-specified
- * chardevs to deliver CHR_EVENT_OPENED events to any FEs attached
- * to a mux chardev. This is done here to ensure that
- * output/prompts/banners are only displayed for the FE that has
- * focus when initial command-line processing/machine init is
- * completed.
- *
- * After this point, any new FE attached to any new or existing
- * mux will receive CHR_EVENT_OPENED notifications for the BE
- * immediately.
- */
-static void muxes_realize_done(Notifier *notifier, void *unused)
-{
-    CharDriverState *chr;
-
-    QTAILQ_FOREACH(chr, &chardevs, next) {
-        if (chr->is_mux) {
-            MuxDriver *d = chr->opaque;
-            int i;
-
-            /* send OPENED to all already-attached FEs */
-            for (i = 0; i < d->mux_cnt; i++) {
-                mux_chr_send_event(d, i, CHR_EVENT_OPENED);
-            }
-            /* mark mux as OPENED so any new FEs will immediately receive
-             * OPENED event
-             */
-            qemu_chr_be_generic_open(chr);
-        }
-    }
-    muxes_realized = true;
-}
-
-static Notifier muxes_realize_notify = {
-    .notify = muxes_realize_done,
-};
-
-static GSource *mux_chr_add_watch(CharDriverState *s, GIOCondition cond)
-{
-    MuxDriver *d = s->opaque;
-    CharDriverState *chr = qemu_chr_fe_get_driver(&d->chr);
-
-    return chr->chr_add_watch(chr, cond);
-}
-
-static void mux_chr_free(struct CharDriverState *chr)
-{
-    MuxDriver *d = chr->opaque;
-    int i;
-
-    for (i = 0; i < d->mux_cnt; i++) {
-        CharBackend *be = d->backends[i];
-        if (be) {
-            be->chr = NULL;
-        }
-    }
-    qemu_chr_fe_deinit(&d->chr);
-    g_free(d);
-}
-
-static void mux_chr_set_handlers(CharDriverState *chr, GMainContext *context)
-{
-    MuxDriver *d = chr->opaque;
-
-    /* Fix up the real driver with mux routines */
-    qemu_chr_fe_set_handlers(&d->chr,
-                             mux_chr_can_read,
-                             mux_chr_read,
-                             mux_chr_event,
-                             chr,
-                             context, true);
-}
-
-static void mux_set_focus(CharDriverState *chr, int focus)
-{
-    MuxDriver *d = chr->opaque;
-
-    assert(focus >= 0);
-    assert(focus < d->mux_cnt);
-
-    if (d->focus != -1) {
-        mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_OUT);
-    }
-
-    d->focus = focus;
-    chr->be = d->backends[focus];
-    mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_IN);
-}
-
-static CharDriverState *qemu_chr_open_mux(const char *id,
-                                          ChardevBackend *backend,
-                                          ChardevReturn *ret,
-                                          bool *be_opened,
-                                          Error **errp)
-{
-    ChardevMux *mux = backend->u.mux.data;
-    CharDriverState *chr, *drv;
-    MuxDriver *d;
-    ChardevCommon *common = qapi_ChardevMux_base(mux);
-
-    drv = qemu_chr_find(mux->chardev);
-    if (drv == NULL) {
-        error_setg(errp, "mux: base chardev %s not found", mux->chardev);
-        return NULL;
-    }
-
-    chr = qemu_chr_alloc(common, errp);
-    if (!chr) {
-        return NULL;
-    }
-    d = g_new0(MuxDriver, 1);
-
-    chr->opaque = d;
-    d->focus = -1;
-    chr->chr_free = mux_chr_free;
-    chr->chr_write = mux_chr_write;
-    chr->chr_accept_input = mux_chr_accept_input;
-    /* Frontend guest-open / -close notification is not support with muxes */
-    chr->chr_set_fe_open = NULL;
-    if (drv->chr_add_watch) {
-        chr->chr_add_watch = mux_chr_add_watch;
-    }
-    /* only default to opened state if we've realized the initial
-     * set of muxes
-     */
-    *be_opened = muxes_realized;
-    chr->is_mux = 1;
-    if (!qemu_chr_fe_init(&d->chr, drv, errp)) {
-        qemu_chr_free(chr);
-        return NULL;
-    }
-
-    return chr;
-}
-
-CharDriverState *qemu_chr_fe_get_driver(CharBackend *be)
-{
-    return be->chr;
-}
-
-bool qemu_chr_fe_init(CharBackend *b, CharDriverState *s, Error **errp)
-{
-    int tag = 0;
-
-    if (s->is_mux) {
-        MuxDriver *d = s->opaque;
-
-        if (d->mux_cnt >= MAX_MUX) {
-            goto unavailable;
-        }
-
-        d->backends[d->mux_cnt] = b;
-        tag = d->mux_cnt++;
-    } else if (s->be) {
-        goto unavailable;
-    } else {
-        s->be = b;
-    }
-
-    b->fe_open = false;
-    b->tag = tag;
-    b->chr = s;
-    return true;
-
-unavailable:
-    error_setg(errp, QERR_DEVICE_IN_USE, s->label);
-    return false;
-}
-
-static bool qemu_chr_is_busy(CharDriverState *s)
-{
-    if (s->is_mux) {
-        MuxDriver *d = s->opaque;
-        return d->mux_cnt >= 0;
-    } else {
-        return s->be != NULL;
-    }
-}
-
-void qemu_chr_fe_deinit(CharBackend *b)
-{
-    assert(b);
-
-    if (b->chr) {
-        qemu_chr_fe_set_handlers(b, NULL, NULL, NULL, NULL, NULL, true);
-        if (b->chr->be == b) {
-            b->chr->be = NULL;
-        }
-        if (b->chr->is_mux) {
-            MuxDriver *d = b->chr->opaque;
-            d->backends[b->tag] = NULL;
-        }
-        b->chr = NULL;
-    }
-}
-
-void qemu_chr_fe_set_handlers(CharBackend *b,
-                              IOCanReadHandler *fd_can_read,
-                              IOReadHandler *fd_read,
-                              IOEventHandler *fd_event,
-                              void *opaque,
-                              GMainContext *context,
-                              bool set_open)
-{
-    CharDriverState *s;
-    int fe_open;
-
-    s = b->chr;
-    if (!s) {
-        return;
-    }
-
-    if (!opaque && !fd_can_read && !fd_read && !fd_event) {
-        fe_open = 0;
-        remove_fd_in_watch(s);
-    } else {
-        fe_open = 1;
-    }
-    b->chr_can_read = fd_can_read;
-    b->chr_read = fd_read;
-    b->chr_event = fd_event;
-    b->opaque = opaque;
-    if (s->chr_update_read_handler) {
-        s->chr_update_read_handler(s, context);
-    }
-
-    if (set_open) {
-        qemu_chr_fe_set_open(b, fe_open);
-    }
-
-    if (fe_open) {
-        qemu_chr_fe_take_focus(b);
-        /* We're connecting to an already opened device, so let's make sure we
-           also get the open event */
-        if (s->be_open) {
-            qemu_chr_be_generic_open(s);
-        }
-    }
-
-    if (s->is_mux) {
-        mux_chr_set_handlers(s, context);
-    }
-}
-
-void qemu_chr_fe_take_focus(CharBackend *b)
-{
-    if (!b->chr) {
-        return;
-    }
-
-    if (b->chr->is_mux) {
-        mux_set_focus(b->chr, b->tag);
-    }
-}
-
-typedef struct IOWatchPoll
-{
-    GSource parent;
-
-    QIOChannel *ioc;
-    GSource *src;
-
-    IOCanReadHandler *fd_can_read;
-    GSourceFunc fd_read;
-    void *opaque;
-    GMainContext *context;
-} IOWatchPoll;
-
-static IOWatchPoll *io_watch_poll_from_source(GSource *source)
-{
-    return container_of(source, IOWatchPoll, parent);
-}
-
-static gboolean io_watch_poll_prepare(GSource *source,
-                                      gint *timeout_)
-{
-    IOWatchPoll *iwp = io_watch_poll_from_source(source);
-    bool now_active = iwp->fd_can_read(iwp->opaque) > 0;
-    bool was_active = iwp->src != NULL;
-    if (was_active == now_active) {
-        return FALSE;
-    }
-
-    if (now_active) {
-        iwp->src = qio_channel_create_watch(
-            iwp->ioc, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL);
-        g_source_set_callback(iwp->src, iwp->fd_read, iwp->opaque, NULL);
-        g_source_attach(iwp->src, iwp->context);
-    } else {
-        g_source_destroy(iwp->src);
-        g_source_unref(iwp->src);
-        iwp->src = NULL;
-    }
-    return FALSE;
-}
-
-static gboolean io_watch_poll_check(GSource *source)
-{
-    return FALSE;
-}
-
-static gboolean io_watch_poll_dispatch(GSource *source, GSourceFunc callback,
-                                       gpointer user_data)
-{
-    abort();
-}
-
-static void io_watch_poll_finalize(GSource *source)
-{
-    /* Due to a glib bug, removing the last reference to a source
-     * inside a finalize callback causes recursive locking (and a
-     * deadlock).  This is not a problem inside other callbacks,
-     * including dispatch callbacks, so we call io_remove_watch_poll
-     * to remove this source.  At this point, iwp->src must
-     * be NULL, or we would leak it.
-     *
-     * This would be solved much more elegantly by child sources,
-     * but we support older glib versions that do not have them.
-     */
-    IOWatchPoll *iwp = io_watch_poll_from_source(source);
-    assert(iwp->src == NULL);
-}
-
-static GSourceFuncs io_watch_poll_funcs = {
-    .prepare = io_watch_poll_prepare,
-    .check = io_watch_poll_check,
-    .dispatch = io_watch_poll_dispatch,
-    .finalize = io_watch_poll_finalize,
-};
-
-/* Can only be used for read */
-static guint io_add_watch_poll(CharDriverState *chr,
-                               QIOChannel *ioc,
-                               IOCanReadHandler *fd_can_read,
-                               QIOChannelFunc fd_read,
-                               gpointer user_data,
-                               GMainContext *context)
-{
-    IOWatchPoll *iwp;
-    int tag;
-    char *name;
-
-    iwp = (IOWatchPoll *) g_source_new(&io_watch_poll_funcs,
-                                       sizeof(IOWatchPoll));
-    iwp->fd_can_read = fd_can_read;
-    iwp->opaque = user_data;
-    iwp->ioc = ioc;
-    iwp->fd_read = (GSourceFunc) fd_read;
-    iwp->src = NULL;
-    iwp->context = context;
-
-    name = g_strdup_printf("chardev-iowatch-%s", chr->label);
-    g_source_set_name((GSource *)iwp, name);
-    g_free(name);
-
-    tag = g_source_attach(&iwp->parent, context);
-    g_source_unref(&iwp->parent);
-    return tag;
-}
-
-static void io_remove_watch_poll(guint tag)
-{
-    GSource *source;
-    IOWatchPoll *iwp;
-
-    g_return_if_fail (tag > 0);
-
-    source = g_main_context_find_source_by_id(NULL, tag);
-    g_return_if_fail (source != NULL);
-
-    iwp = io_watch_poll_from_source(source);
-    if (iwp->src) {
-        g_source_destroy(iwp->src);
-        g_source_unref(iwp->src);
-        iwp->src = NULL;
-    }
-    g_source_destroy(&iwp->parent);
-}
-
-static void remove_fd_in_watch(CharDriverState *chr)
-{
-    if (chr->fd_in_tag) {
-        io_remove_watch_poll(chr->fd_in_tag);
-        chr->fd_in_tag = 0;
-    }
-}
-
-
-static int io_channel_send_full(QIOChannel *ioc,
-                                const void *buf, size_t len,
-                                int *fds, size_t nfds)
-{
-    size_t offset = 0;
-
-    while (offset < len) {
-        ssize_t ret = 0;
-        struct iovec iov = { .iov_base = (char *)buf + offset,
-                             .iov_len = len - offset };
-
-        ret = qio_channel_writev_full(
-            ioc, &iov, 1,
-            fds, nfds, NULL);
-        if (ret == QIO_CHANNEL_ERR_BLOCK) {
-            if (offset) {
-                return offset;
-            }
-
-            errno = EAGAIN;
-            return -1;
-        } else if (ret < 0) {
-            errno = EINVAL;
-            return -1;
-        }
-
-        offset += ret;
-    }
-
-    return offset;
-}
-
-
-#ifndef _WIN32
-static int io_channel_send(QIOChannel *ioc, const void *buf, size_t len)
-{
-    return io_channel_send_full(ioc, buf, len, NULL, 0);
-}
-
-
-typedef struct FDCharDriver {
-    CharDriverState *chr;
-    QIOChannel *ioc_in, *ioc_out;
-    int max_size;
-} FDCharDriver;
-
-/* Called with chr_write_lock held.  */
-static int fd_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
-{
-    FDCharDriver *s = chr->opaque;
-    
-    return io_channel_send(s->ioc_out, buf, len);
-}
-
-static gboolean fd_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
-{
-    CharDriverState *chr = opaque;
-    FDCharDriver *s = chr->opaque;
-    int len;
-    uint8_t buf[READ_BUF_LEN];
-    ssize_t ret;
-
-    len = sizeof(buf);
-    if (len > s->max_size) {
-        len = s->max_size;
-    }
-    if (len == 0) {
-        return TRUE;
-    }
-
-    ret = qio_channel_read(
-        chan, (gchar *)buf, len, NULL);
-    if (ret == 0) {
-        remove_fd_in_watch(chr);
-        qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
-        return FALSE;
-    }
-    if (ret > 0) {
-        qemu_chr_be_write(chr, buf, ret);
-    }
-
-    return TRUE;
-}
-
-static int fd_chr_read_poll(void *opaque)
-{
-    CharDriverState *chr = opaque;
-    FDCharDriver *s = chr->opaque;
-
-    s->max_size = qemu_chr_be_can_write(chr);
-    return s->max_size;
-}
-
-static GSource *fd_chr_add_watch(CharDriverState *chr, GIOCondition cond)
-{
-    FDCharDriver *s = chr->opaque;
-    return qio_channel_create_watch(s->ioc_out, cond);
-}
-
-static void fd_chr_update_read_handler(CharDriverState *chr,
-                                       GMainContext *context)
-{
-    FDCharDriver *s = chr->opaque;
-
-    remove_fd_in_watch(chr);
-    if (s->ioc_in) {
-        chr->fd_in_tag = io_add_watch_poll(chr, s->ioc_in,
-                                           fd_chr_read_poll,
-                                           fd_chr_read, chr,
-                                           context);
-    }
-}
-
-static void fd_chr_free(struct CharDriverState *chr)
-{
-    FDCharDriver *s = chr->opaque;
-
-    remove_fd_in_watch(chr);
-    if (s->ioc_in) {
-        object_unref(OBJECT(s->ioc_in));
-    }
-    if (s->ioc_out) {
-        object_unref(OBJECT(s->ioc_out));
-    }
-
-    g_free(s);
-    qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
-}
-
-/* open a character device to a unix fd */
-static CharDriverState *qemu_chr_open_fd(int fd_in, int fd_out,
-                                         ChardevCommon *backend, Error **errp)
-{
-    CharDriverState *chr;
-    FDCharDriver *s;
-    char *name;
-
-    chr = qemu_chr_alloc(backend, errp);
-    if (!chr) {
-        return NULL;
-    }
-    s = g_new0(FDCharDriver, 1);
-    s->ioc_in = QIO_CHANNEL(qio_channel_file_new_fd(fd_in));
-    name = g_strdup_printf("chardev-file-in-%s", chr->label);
-    qio_channel_set_name(QIO_CHANNEL(s->ioc_in), name);
-    g_free(name);
-    s->ioc_out = QIO_CHANNEL(qio_channel_file_new_fd(fd_out));
-    name = g_strdup_printf("chardev-file-out-%s", chr->label);
-    qio_channel_set_name(QIO_CHANNEL(s->ioc_out), name);
-    g_free(name);
-    qemu_set_nonblock(fd_out);
-    s->chr = chr;
-    chr->opaque = s;
-    chr->chr_add_watch = fd_chr_add_watch;
-    chr->chr_write = fd_chr_write;
-    chr->chr_update_read_handler = fd_chr_update_read_handler;
-    chr->chr_free = fd_chr_free;
-
-    return chr;
-}
-
-static CharDriverState *qemu_chr_open_pipe(const char *id,
-                                           ChardevBackend *backend,
-                                           ChardevReturn *ret,
-                                           bool *be_opened,
-                                           Error **errp)
-{
-    ChardevHostdev *opts = backend->u.pipe.data;
-    int fd_in, fd_out;
-    char *filename_in;
-    char *filename_out;
-    const char *filename = opts->device;
-    ChardevCommon *common = qapi_ChardevHostdev_base(opts);
-
-
-    filename_in = g_strdup_printf("%s.in", filename);
-    filename_out = g_strdup_printf("%s.out", filename);
-    TFR(fd_in = qemu_open(filename_in, O_RDWR | O_BINARY));
-    TFR(fd_out = qemu_open(filename_out, O_RDWR | O_BINARY));
-    g_free(filename_in);
-    g_free(filename_out);
-    if (fd_in < 0 || fd_out < 0) {
-	if (fd_in >= 0)
-	    close(fd_in);
-	if (fd_out >= 0)
-	    close(fd_out);
-        TFR(fd_in = fd_out = qemu_open(filename, O_RDWR | O_BINARY));
-        if (fd_in < 0) {
-            error_setg_file_open(errp, errno, filename);
-            return NULL;
-        }
-    }
-    return qemu_chr_open_fd(fd_in, fd_out, common, errp);
-}
-
-/* init terminal so that we can grab keys */
-static struct termios oldtty;
-static int old_fd0_flags;
-static bool stdio_in_use;
-static bool stdio_allow_signal;
-static bool stdio_echo_state;
-
-static void qemu_chr_set_echo_stdio(CharDriverState *chr, bool echo);
-
-static void term_exit(void)
-{
-    tcsetattr (0, TCSANOW, &oldtty);
-    fcntl(0, F_SETFL, old_fd0_flags);
-}
-
-static void term_stdio_handler(int sig)
-{
-    /* restore echo after resume from suspend. */
-    qemu_chr_set_echo_stdio(NULL, stdio_echo_state);
-}
-
-static void qemu_chr_set_echo_stdio(CharDriverState *chr, bool echo)
-{
-    struct termios tty;
-
-    stdio_echo_state = echo;
-    tty = oldtty;
-    if (!echo) {
-        tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP
-                          |INLCR|IGNCR|ICRNL|IXON);
-        tty.c_oflag |= OPOST;
-        tty.c_lflag &= ~(ECHO|ECHONL|ICANON|IEXTEN);
-        tty.c_cflag &= ~(CSIZE|PARENB);
-        tty.c_cflag |= CS8;
-        tty.c_cc[VMIN] = 1;
-        tty.c_cc[VTIME] = 0;
-    }
-    if (!stdio_allow_signal)
-        tty.c_lflag &= ~ISIG;
-
-    tcsetattr (0, TCSANOW, &tty);
-}
-
-static void qemu_chr_free_stdio(struct CharDriverState *chr)
-{
-    term_exit();
-    fd_chr_free(chr);
-}
-
-static CharDriverState *qemu_chr_open_stdio(const char *id,
-                                            ChardevBackend *backend,
-                                            ChardevReturn *ret,
-                                            bool *be_opened,
-                                            Error **errp)
-{
-    ChardevStdio *opts = backend->u.stdio.data;
-    CharDriverState *chr;
-    struct sigaction act;
-    ChardevCommon *common = qapi_ChardevStdio_base(opts);
-
-    if (is_daemonized()) {
-        error_setg(errp, "cannot use stdio with -daemonize");
-        return NULL;
-    }
-
-    if (stdio_in_use) {
-        error_setg(errp, "cannot use stdio by multiple character devices");
-        return NULL;
-    }
-
-    stdio_in_use = true;
-    old_fd0_flags = fcntl(0, F_GETFL);
-    tcgetattr(0, &oldtty);
-    qemu_set_nonblock(0);
-    atexit(term_exit);
-
-    memset(&act, 0, sizeof(act));
-    act.sa_handler = term_stdio_handler;
-    sigaction(SIGCONT, &act, NULL);
-
-    chr = qemu_chr_open_fd(0, 1, common, errp);
-    if (!chr) {
-        return NULL;
-    }
-    chr->chr_free = qemu_chr_free_stdio;
-    chr->chr_set_echo = qemu_chr_set_echo_stdio;
-    if (opts->has_signal) {
-        stdio_allow_signal = opts->signal;
-    }
-    qemu_chr_set_echo_stdio(chr, false);
-
-    return chr;
-}
-
-#if defined(__linux__) || defined(__sun__) || defined(__FreeBSD__) \
-    || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) \
-    || defined(__GLIBC__)
-
-#define HAVE_CHARDEV_SERIAL 1
-#define HAVE_CHARDEV_PTY 1
-
-typedef struct {
-    QIOChannel *ioc;
-    int read_bytes;
-
-    /* Protected by the CharDriverState chr_write_lock.  */
-    int connected;
-    guint timer_tag;
-    guint open_tag;
-} PtyCharDriver;
-
-static void pty_chr_update_read_handler_locked(CharDriverState *chr);
-static void pty_chr_state(CharDriverState *chr, int connected);
-
-static gboolean pty_chr_timer(gpointer opaque)
-{
-    struct CharDriverState *chr = opaque;
-    PtyCharDriver *s = chr->opaque;
-
-    qemu_mutex_lock(&chr->chr_write_lock);
-    s->timer_tag = 0;
-    s->open_tag = 0;
-    if (!s->connected) {
-        /* Next poll ... */
-        pty_chr_update_read_handler_locked(chr);
-    }
-    qemu_mutex_unlock(&chr->chr_write_lock);
-    return FALSE;
-}
-
-/* Called with chr_write_lock held.  */
-static void pty_chr_rearm_timer(CharDriverState *chr, int ms)
-{
-    PtyCharDriver *s = chr->opaque;
-    char *name;
-
-    if (s->timer_tag) {
-        g_source_remove(s->timer_tag);
-        s->timer_tag = 0;
-    }
-
-    if (ms == 1000) {
-        name = g_strdup_printf("pty-timer-secs-%s", chr->label);
-        s->timer_tag = g_timeout_add_seconds(1, pty_chr_timer, chr);
-    } else {
-        name = g_strdup_printf("pty-timer-ms-%s", chr->label);
-        s->timer_tag = g_timeout_add(ms, pty_chr_timer, chr);
-    }
-    g_source_set_name_by_id(s->timer_tag, name);
-    g_free(name);
-}
-
-/* Called with chr_write_lock held.  */
-static void pty_chr_update_read_handler_locked(CharDriverState *chr)
-{
-    PtyCharDriver *s = chr->opaque;
-    GPollFD pfd;
-    int rc;
-    QIOChannelFile *fioc = QIO_CHANNEL_FILE(s->ioc);
-
-    pfd.fd = fioc->fd;
-    pfd.events = G_IO_OUT;
-    pfd.revents = 0;
-    do {
-        rc = g_poll(&pfd, 1, 0);
-    } while (rc == -1 && errno == EINTR);
-    assert(rc >= 0);
-
-    if (pfd.revents & G_IO_HUP) {
-        pty_chr_state(chr, 0);
-    } else {
-        pty_chr_state(chr, 1);
-    }
-}
-
-static void pty_chr_update_read_handler(CharDriverState *chr,
-                                        GMainContext *context)
-{
-    qemu_mutex_lock(&chr->chr_write_lock);
-    pty_chr_update_read_handler_locked(chr);
-    qemu_mutex_unlock(&chr->chr_write_lock);
-}
-
-/* Called with chr_write_lock held.  */
-static int pty_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
-{
-    PtyCharDriver *s = chr->opaque;
-
-    if (!s->connected) {
-        /* guest sends data, check for (re-)connect */
-        pty_chr_update_read_handler_locked(chr);
-        if (!s->connected) {
-            return 0;
-        }
-    }
-    return io_channel_send(s->ioc, buf, len);
-}
-
-static GSource *pty_chr_add_watch(CharDriverState *chr, GIOCondition cond)
-{
-    PtyCharDriver *s = chr->opaque;
-    if (!s->connected) {
-        return NULL;
-    }
-    return qio_channel_create_watch(s->ioc, cond);
-}
-
-static int pty_chr_read_poll(void *opaque)
-{
-    CharDriverState *chr = opaque;
-    PtyCharDriver *s = chr->opaque;
-
-    s->read_bytes = qemu_chr_be_can_write(chr);
-    return s->read_bytes;
-}
-
-static gboolean pty_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
-{
-    CharDriverState *chr = opaque;
-    PtyCharDriver *s = chr->opaque;
-    gsize len;
-    uint8_t buf[READ_BUF_LEN];
-    ssize_t ret;
-
-    len = sizeof(buf);
-    if (len > s->read_bytes)
-        len = s->read_bytes;
-    if (len == 0) {
-        return TRUE;
-    }
-    ret = qio_channel_read(s->ioc, (char *)buf, len, NULL);
-    if (ret <= 0) {
-        pty_chr_state(chr, 0);
-        return FALSE;
-    } else {
-        pty_chr_state(chr, 1);
-        qemu_chr_be_write(chr, buf, ret);
-    }
-    return TRUE;
-}
-
-static gboolean qemu_chr_be_generic_open_func(gpointer opaque)
-{
-    CharDriverState *chr = opaque;
-    PtyCharDriver *s = chr->opaque;
-
-    s->open_tag = 0;
-    qemu_chr_be_generic_open(chr);
-    return FALSE;
-}
-
-/* Called with chr_write_lock held.  */
-static void pty_chr_state(CharDriverState *chr, int connected)
-{
-    PtyCharDriver *s = chr->opaque;
-
-    if (!connected) {
-        if (s->open_tag) {
-            g_source_remove(s->open_tag);
-            s->open_tag = 0;
-        }
-        remove_fd_in_watch(chr);
-        s->connected = 0;
-        /* (re-)connect poll interval for idle guests: once per second.
-         * We check more frequently in case the guests sends data to
-         * the virtual device linked to our pty. */
-        pty_chr_rearm_timer(chr, 1000);
-    } else {
-        if (s->timer_tag) {
-            g_source_remove(s->timer_tag);
-            s->timer_tag = 0;
-        }
-        if (!s->connected) {
-            g_assert(s->open_tag == 0);
-            s->connected = 1;
-            s->open_tag = g_idle_add(qemu_chr_be_generic_open_func, chr);
-        }
-        if (!chr->fd_in_tag) {
-            chr->fd_in_tag = io_add_watch_poll(chr, s->ioc,
-                                               pty_chr_read_poll,
-                                               pty_chr_read,
-                                               chr, NULL);
-        }
-    }
-}
-
-static void pty_chr_free(struct CharDriverState *chr)
-{
-    PtyCharDriver *s = chr->opaque;
-
-    qemu_mutex_lock(&chr->chr_write_lock);
-    pty_chr_state(chr, 0);
-    object_unref(OBJECT(s->ioc));
-    if (s->timer_tag) {
-        g_source_remove(s->timer_tag);
-        s->timer_tag = 0;
-    }
-    qemu_mutex_unlock(&chr->chr_write_lock);
-    g_free(s);
-    qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
-}
-
-static CharDriverState *qemu_chr_open_pty(const char *id,
-                                          ChardevBackend *backend,
-                                          ChardevReturn *ret,
-                                          bool *be_opened,
-                                          Error **errp)
-{
-    CharDriverState *chr;
-    PtyCharDriver *s;
-    int master_fd, slave_fd;
-    char pty_name[PATH_MAX];
-    ChardevCommon *common = backend->u.pty.data;
-    char *name;
-
-    master_fd = qemu_openpty_raw(&slave_fd, pty_name);
-    if (master_fd < 0) {
-        error_setg_errno(errp, errno, "Failed to create PTY");
-        return NULL;
-    }
-
-    close(slave_fd);
-    qemu_set_nonblock(master_fd);
-
-    chr = qemu_chr_alloc(common, errp);
-    if (!chr) {
-        close(master_fd);
-        return NULL;
-    }
-
-    chr->filename = g_strdup_printf("pty:%s", pty_name);
-    ret->pty = g_strdup(pty_name);
-    ret->has_pty = true;
-
-    fprintf(stderr, "char device redirected to %s (label %s)\n",
-            pty_name, id);
-
-    s = g_new0(PtyCharDriver, 1);
-    chr->opaque = s;
-    chr->chr_write = pty_chr_write;
-    chr->chr_update_read_handler = pty_chr_update_read_handler;
-    chr->chr_free = pty_chr_free;
-    chr->chr_add_watch = pty_chr_add_watch;
-    *be_opened = false;
-
-    s->ioc = QIO_CHANNEL(qio_channel_file_new_fd(master_fd));
-    name = g_strdup_printf("chardev-pty-%s", chr->label);
-    qio_channel_set_name(QIO_CHANNEL(s->ioc), name);
-    g_free(name);
-    s->timer_tag = 0;
-
-    return chr;
-}
-
-static void tty_serial_init(int fd, int speed,
-                            int parity, int data_bits, int stop_bits)
-{
-    struct termios tty;
-    speed_t spd;
-
-#if 0
-    printf("tty_serial_init: speed=%d parity=%c data=%d stop=%d\n",
-           speed, parity, data_bits, stop_bits);
-#endif
-    tcgetattr (fd, &tty);
-
-#define check_speed(val) if (speed <= val) { spd = B##val; break; }
-    speed = speed * 10 / 11;
-    do {
-        check_speed(50);
-        check_speed(75);
-        check_speed(110);
-        check_speed(134);
-        check_speed(150);
-        check_speed(200);
-        check_speed(300);
-        check_speed(600);
-        check_speed(1200);
-        check_speed(1800);
-        check_speed(2400);
-        check_speed(4800);
-        check_speed(9600);
-        check_speed(19200);
-        check_speed(38400);
-        /* Non-Posix values follow. They may be unsupported on some systems. */
-        check_speed(57600);
-        check_speed(115200);
-#ifdef B230400
-        check_speed(230400);
-#endif
-#ifdef B460800
-        check_speed(460800);
-#endif
-#ifdef B500000
-        check_speed(500000);
-#endif
-#ifdef B576000
-        check_speed(576000);
-#endif
-#ifdef B921600
-        check_speed(921600);
-#endif
-#ifdef B1000000
-        check_speed(1000000);
-#endif
-#ifdef B1152000
-        check_speed(1152000);
-#endif
-#ifdef B1500000
-        check_speed(1500000);
-#endif
-#ifdef B2000000
-        check_speed(2000000);
-#endif
-#ifdef B2500000
-        check_speed(2500000);
-#endif
-#ifdef B3000000
-        check_speed(3000000);
-#endif
-#ifdef B3500000
-        check_speed(3500000);
-#endif
-#ifdef B4000000
-        check_speed(4000000);
-#endif
-        spd = B115200;
-    } while (0);
-
-    cfsetispeed(&tty, spd);
-    cfsetospeed(&tty, spd);
-
-    tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP
-                          |INLCR|IGNCR|ICRNL|IXON);
-    tty.c_oflag |= OPOST;
-    tty.c_lflag &= ~(ECHO|ECHONL|ICANON|IEXTEN|ISIG);
-    tty.c_cflag &= ~(CSIZE|PARENB|PARODD|CRTSCTS|CSTOPB);
-    switch(data_bits) {
-    default:
-    case 8:
-        tty.c_cflag |= CS8;
-        break;
-    case 7:
-        tty.c_cflag |= CS7;
-        break;
-    case 6:
-        tty.c_cflag |= CS6;
-        break;
-    case 5:
-        tty.c_cflag |= CS5;
-        break;
-    }
-    switch(parity) {
-    default:
-    case 'N':
-        break;
-    case 'E':
-        tty.c_cflag |= PARENB;
-        break;
-    case 'O':
-        tty.c_cflag |= PARENB | PARODD;
-        break;
-    }
-    if (stop_bits == 2)
-        tty.c_cflag |= CSTOPB;
-
-    tcsetattr (fd, TCSANOW, &tty);
-}
-
-static int tty_serial_ioctl(CharDriverState *chr, int cmd, void *arg)
-{
-    FDCharDriver *s = chr->opaque;
-    QIOChannelFile *fioc = QIO_CHANNEL_FILE(s->ioc_in);
-
-    switch(cmd) {
-    case CHR_IOCTL_SERIAL_SET_PARAMS:
-        {
-            QEMUSerialSetParams *ssp = arg;
-            tty_serial_init(fioc->fd,
-                            ssp->speed, ssp->parity,
-                            ssp->data_bits, ssp->stop_bits);
-        }
-        break;
-    case CHR_IOCTL_SERIAL_SET_BREAK:
-        {
-            int enable = *(int *)arg;
-            if (enable) {
-                tcsendbreak(fioc->fd, 1);
-            }
-        }
-        break;
-    case CHR_IOCTL_SERIAL_GET_TIOCM:
-        {
-            int sarg = 0;
-            int *targ = (int *)arg;
-            ioctl(fioc->fd, TIOCMGET, &sarg);
-            *targ = 0;
-            if (sarg & TIOCM_CTS)
-                *targ |= CHR_TIOCM_CTS;
-            if (sarg & TIOCM_CAR)
-                *targ |= CHR_TIOCM_CAR;
-            if (sarg & TIOCM_DSR)
-                *targ |= CHR_TIOCM_DSR;
-            if (sarg & TIOCM_RI)
-                *targ |= CHR_TIOCM_RI;
-            if (sarg & TIOCM_DTR)
-                *targ |= CHR_TIOCM_DTR;
-            if (sarg & TIOCM_RTS)
-                *targ |= CHR_TIOCM_RTS;
-        }
-        break;
-    case CHR_IOCTL_SERIAL_SET_TIOCM:
-        {
-            int sarg = *(int *)arg;
-            int targ = 0;
-            ioctl(fioc->fd, TIOCMGET, &targ);
-            targ &= ~(CHR_TIOCM_CTS | CHR_TIOCM_CAR | CHR_TIOCM_DSR
-                     | CHR_TIOCM_RI | CHR_TIOCM_DTR | CHR_TIOCM_RTS);
-            if (sarg & CHR_TIOCM_CTS)
-                targ |= TIOCM_CTS;
-            if (sarg & CHR_TIOCM_CAR)
-                targ |= TIOCM_CAR;
-            if (sarg & CHR_TIOCM_DSR)
-                targ |= TIOCM_DSR;
-            if (sarg & CHR_TIOCM_RI)
-                targ |= TIOCM_RI;
-            if (sarg & CHR_TIOCM_DTR)
-                targ |= TIOCM_DTR;
-            if (sarg & CHR_TIOCM_RTS)
-                targ |= TIOCM_RTS;
-            ioctl(fioc->fd, TIOCMSET, &targ);
-        }
-        break;
-    default:
-        return -ENOTSUP;
-    }
-    return 0;
-}
-
-static void qemu_chr_free_tty(CharDriverState *chr)
-{
-    fd_chr_free(chr);
-}
-
-static CharDriverState *qemu_chr_open_tty_fd(int fd,
-                                             ChardevCommon *backend,
-                                             bool *be_opened,
-                                             Error **errp)
-{
-    CharDriverState *chr;
-
-    tty_serial_init(fd, 115200, 'N', 8, 1);
-    chr = qemu_chr_open_fd(fd, fd, backend, errp);
-    if (!chr) {
-        return NULL;
-    }
-    chr->chr_ioctl = tty_serial_ioctl;
-    chr->chr_free = qemu_chr_free_tty;
-    return chr;
-}
-#endif /* __linux__ || __sun__ */
-
-#if defined(__linux__)
-
-#define HAVE_CHARDEV_PARPORT 1
-
-typedef struct {
-    int fd;
-    int mode;
-} ParallelCharDriver;
-
-static int pp_hw_mode(ParallelCharDriver *s, uint16_t mode)
-{
-    if (s->mode != mode) {
-	int m = mode;
-        if (ioctl(s->fd, PPSETMODE, &m) < 0)
-            return 0;
-	s->mode = mode;
-    }
-    return 1;
-}
-
-static int pp_ioctl(CharDriverState *chr, int cmd, void *arg)
-{
-    ParallelCharDriver *drv = chr->opaque;
-    int fd = drv->fd;
-    uint8_t b;
-
-    switch(cmd) {
-    case CHR_IOCTL_PP_READ_DATA:
-        if (ioctl(fd, PPRDATA, &b) < 0)
-            return -ENOTSUP;
-        *(uint8_t *)arg = b;
-        break;
-    case CHR_IOCTL_PP_WRITE_DATA:
-        b = *(uint8_t *)arg;
-        if (ioctl(fd, PPWDATA, &b) < 0)
-            return -ENOTSUP;
-        break;
-    case CHR_IOCTL_PP_READ_CONTROL:
-        if (ioctl(fd, PPRCONTROL, &b) < 0)
-            return -ENOTSUP;
-	/* Linux gives only the lowest bits, and no way to know data
-	   direction! For better compatibility set the fixed upper
-	   bits. */
-        *(uint8_t *)arg = b | 0xc0;
-        break;
-    case CHR_IOCTL_PP_WRITE_CONTROL:
-        b = *(uint8_t *)arg;
-        if (ioctl(fd, PPWCONTROL, &b) < 0)
-            return -ENOTSUP;
-        break;
-    case CHR_IOCTL_PP_READ_STATUS:
-        if (ioctl(fd, PPRSTATUS, &b) < 0)
-            return -ENOTSUP;
-        *(uint8_t *)arg = b;
-        break;
-    case CHR_IOCTL_PP_DATA_DIR:
-        if (ioctl(fd, PPDATADIR, (int *)arg) < 0)
-            return -ENOTSUP;
-        break;
-    case CHR_IOCTL_PP_EPP_READ_ADDR:
-	if (pp_hw_mode(drv, IEEE1284_MODE_EPP|IEEE1284_ADDR)) {
-	    struct ParallelIOArg *parg = arg;
-	    int n = read(fd, parg->buffer, parg->count);
-	    if (n != parg->count) {
-		return -EIO;
-	    }
-	}
-        break;
-    case CHR_IOCTL_PP_EPP_READ:
-	if (pp_hw_mode(drv, IEEE1284_MODE_EPP)) {
-	    struct ParallelIOArg *parg = arg;
-	    int n = read(fd, parg->buffer, parg->count);
-	    if (n != parg->count) {
-		return -EIO;
-	    }
-	}
-        break;
-    case CHR_IOCTL_PP_EPP_WRITE_ADDR:
-	if (pp_hw_mode(drv, IEEE1284_MODE_EPP|IEEE1284_ADDR)) {
-	    struct ParallelIOArg *parg = arg;
-	    int n = write(fd, parg->buffer, parg->count);
-	    if (n != parg->count) {
-		return -EIO;
-	    }
-	}
-        break;
-    case CHR_IOCTL_PP_EPP_WRITE:
-	if (pp_hw_mode(drv, IEEE1284_MODE_EPP)) {
-	    struct ParallelIOArg *parg = arg;
-	    int n = write(fd, parg->buffer, parg->count);
-	    if (n != parg->count) {
-		return -EIO;
-	    }
-	}
-        break;
-    default:
-        return -ENOTSUP;
-    }
-    return 0;
-}
-
-static void pp_free(CharDriverState *chr)
-{
-    ParallelCharDriver *drv = chr->opaque;
-    int fd = drv->fd;
-
-    pp_hw_mode(drv, IEEE1284_MODE_COMPAT);
-    ioctl(fd, PPRELEASE);
-    close(fd);
-    g_free(drv);
-    qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
-}
-
-static CharDriverState *qemu_chr_open_pp_fd(int fd,
-                                            ChardevCommon *backend,
-                                            bool *be_opened,
-                                            Error **errp)
-{
-    CharDriverState *chr;
-    ParallelCharDriver *drv;
-
-    if (ioctl(fd, PPCLAIM) < 0) {
-        error_setg_errno(errp, errno, "not a parallel port");
-        close(fd);
-        return NULL;
-    }
-
-    chr = qemu_chr_alloc(backend, errp);
-    if (!chr) {
-        return NULL;
-    }
-
-    drv = g_new0(ParallelCharDriver, 1);
-    chr->opaque = drv;
-    chr->chr_write = null_chr_write;
-    chr->chr_ioctl = pp_ioctl;
-    chr->chr_free = pp_free;
-
-    drv->fd = fd;
-    drv->mode = IEEE1284_MODE_COMPAT;
-
-    return chr;
-}
-#endif /* __linux__ */
-
-#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
-
-#define HAVE_CHARDEV_PARPORT 1
-
-static int pp_ioctl(CharDriverState *chr, int cmd, void *arg)
-{
-    int fd = (int)(intptr_t)chr->opaque;
-    uint8_t b;
-
-    switch(cmd) {
-    case CHR_IOCTL_PP_READ_DATA:
-        if (ioctl(fd, PPIGDATA, &b) < 0)
-            return -ENOTSUP;
-        *(uint8_t *)arg = b;
-        break;
-    case CHR_IOCTL_PP_WRITE_DATA:
-        b = *(uint8_t *)arg;
-        if (ioctl(fd, PPISDATA, &b) < 0)
-            return -ENOTSUP;
-        break;
-    case CHR_IOCTL_PP_READ_CONTROL:
-        if (ioctl(fd, PPIGCTRL, &b) < 0)
-            return -ENOTSUP;
-        *(uint8_t *)arg = b;
-        break;
-    case CHR_IOCTL_PP_WRITE_CONTROL:
-        b = *(uint8_t *)arg;
-        if (ioctl(fd, PPISCTRL, &b) < 0)
-            return -ENOTSUP;
-        break;
-    case CHR_IOCTL_PP_READ_STATUS:
-        if (ioctl(fd, PPIGSTATUS, &b) < 0)
-            return -ENOTSUP;
-        *(uint8_t *)arg = b;
-        break;
-    default:
-        return -ENOTSUP;
-    }
-    return 0;
-}
-
-static CharDriverState *qemu_chr_open_pp_fd(int fd,
-                                            ChardevCommon *backend,
-                                            bool *be_opened,
-                                            Error **errp)
-{
-    CharDriverState *chr;
-
-    chr = qemu_chr_alloc(backend, errp);
-    if (!chr) {
-        return NULL;
-    }
-    chr->opaque = (void *)(intptr_t)fd;
-    chr->chr_write = null_chr_write;
-    chr->chr_ioctl = pp_ioctl;
-    *be_opened = false;
-    return chr;
-}
-#endif
-
-#else /* _WIN32 */
-
-#define HAVE_CHARDEV_SERIAL 1
-
-typedef struct {
-    int max_size;
-    HANDLE hcom, hrecv, hsend;
-    OVERLAPPED orecv;
-    BOOL fpipe;
-    DWORD len;
-
-    /* Protected by the CharDriverState chr_write_lock.  */
-    OVERLAPPED osend;
-} WinCharState;
-
-typedef struct {
-    HANDLE  hStdIn;
-    HANDLE  hInputReadyEvent;
-    HANDLE  hInputDoneEvent;
-    HANDLE  hInputThread;
-    uint8_t win_stdio_buf;
-} WinStdioCharState;
-
-#define NSENDBUF 2048
-#define NRECVBUF 2048
-#define MAXCONNECT 1
-#define NTIMEOUT 5000
-
-static int win_chr_poll(void *opaque);
-static int win_chr_pipe_poll(void *opaque);
-
-static void win_chr_free(CharDriverState *chr)
-{
-    WinCharState *s = chr->opaque;
-
-    if (s->hsend) {
-        CloseHandle(s->hsend);
-        s->hsend = NULL;
-    }
-    if (s->hrecv) {
-        CloseHandle(s->hrecv);
-        s->hrecv = NULL;
-    }
-    if (s->hcom) {
-        CloseHandle(s->hcom);
-        s->hcom = NULL;
-    }
-    if (s->fpipe)
-        qemu_del_polling_cb(win_chr_pipe_poll, chr);
-    else
-        qemu_del_polling_cb(win_chr_poll, chr);
-
-    qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
-}
-
-static int win_chr_init(CharDriverState *chr, const char *filename, Error **errp)
-{
-    WinCharState *s = chr->opaque;
-    COMMCONFIG comcfg;
-    COMMTIMEOUTS cto = { 0, 0, 0, 0, 0};
-    COMSTAT comstat;
-    DWORD size;
-    DWORD err;
-
-    s->hsend = CreateEvent(NULL, TRUE, FALSE, NULL);
-    if (!s->hsend) {
-        error_setg(errp, "Failed CreateEvent");
-        goto fail;
-    }
-    s->hrecv = CreateEvent(NULL, TRUE, FALSE, NULL);
-    if (!s->hrecv) {
-        error_setg(errp, "Failed CreateEvent");
-        goto fail;
-    }
-
-    s->hcom = CreateFile(filename, GENERIC_READ|GENERIC_WRITE, 0, NULL,
-                      OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
-    if (s->hcom == INVALID_HANDLE_VALUE) {
-        error_setg(errp, "Failed CreateFile (%lu)", GetLastError());
-        s->hcom = NULL;
-        goto fail;
-    }
-
-    if (!SetupComm(s->hcom, NRECVBUF, NSENDBUF)) {
-        error_setg(errp, "Failed SetupComm");
-        goto fail;
-    }
-
-    ZeroMemory(&comcfg, sizeof(COMMCONFIG));
-    size = sizeof(COMMCONFIG);
-    GetDefaultCommConfig(filename, &comcfg, &size);
-    comcfg.dcb.DCBlength = sizeof(DCB);
-    CommConfigDialog(filename, NULL, &comcfg);
-
-    if (!SetCommState(s->hcom, &comcfg.dcb)) {
-        error_setg(errp, "Failed SetCommState");
-        goto fail;
-    }
-
-    if (!SetCommMask(s->hcom, EV_ERR)) {
-        error_setg(errp, "Failed SetCommMask");
-        goto fail;
-    }
-
-    cto.ReadIntervalTimeout = MAXDWORD;
-    if (!SetCommTimeouts(s->hcom, &cto)) {
-        error_setg(errp, "Failed SetCommTimeouts");
-        goto fail;
-    }
-
-    if (!ClearCommError(s->hcom, &err, &comstat)) {
-        error_setg(errp, "Failed ClearCommError");
-        goto fail;
-    }
-    qemu_add_polling_cb(win_chr_poll, chr);
-    return 0;
-
- fail:
-    win_chr_free(chr);
-    return -1;
-}
-
-/* Called with chr_write_lock held.  */
-static int win_chr_write(CharDriverState *chr, const uint8_t *buf, int len1)
-{
-    WinCharState *s = chr->opaque;
-    DWORD len, ret, size, err;
-
-    len = len1;
-    ZeroMemory(&s->osend, sizeof(s->osend));
-    s->osend.hEvent = s->hsend;
-    while (len > 0) {
-        if (s->hsend)
-            ret = WriteFile(s->hcom, buf, len, &size, &s->osend);
-        else
-            ret = WriteFile(s->hcom, buf, len, &size, NULL);
-        if (!ret) {
-            err = GetLastError();
-            if (err == ERROR_IO_PENDING) {
-                ret = GetOverlappedResult(s->hcom, &s->osend, &size, TRUE);
-                if (ret) {
-                    buf += size;
-                    len -= size;
-                } else {
-                    break;
-                }
-            } else {
-                break;
-            }
-        } else {
-            buf += size;
-            len -= size;
-        }
-    }
-    return len1 - len;
-}
-
-static int win_chr_read_poll(CharDriverState *chr)
-{
-    WinCharState *s = chr->opaque;
-
-    s->max_size = qemu_chr_be_can_write(chr);
-    return s->max_size;
-}
-
-static void win_chr_readfile(CharDriverState *chr)
-{
-    WinCharState *s = chr->opaque;
-    int ret, err;
-    uint8_t buf[READ_BUF_LEN];
-    DWORD size;
-
-    ZeroMemory(&s->orecv, sizeof(s->orecv));
-    s->orecv.hEvent = s->hrecv;
-    ret = ReadFile(s->hcom, buf, s->len, &size, &s->orecv);
-    if (!ret) {
-        err = GetLastError();
-        if (err == ERROR_IO_PENDING) {
-            ret = GetOverlappedResult(s->hcom, &s->orecv, &size, TRUE);
-        }
-    }
-
-    if (size > 0) {
-        qemu_chr_be_write(chr, buf, size);
-    }
-}
-
-static void win_chr_read(CharDriverState *chr)
-{
-    WinCharState *s = chr->opaque;
-
-    if (s->len > s->max_size)
-        s->len = s->max_size;
-    if (s->len == 0)
-        return;
-
-    win_chr_readfile(chr);
-}
-
-static int win_chr_poll(void *opaque)
-{
-    CharDriverState *chr = opaque;
-    WinCharState *s = chr->opaque;
-    COMSTAT status;
-    DWORD comerr;
-
-    ClearCommError(s->hcom, &comerr, &status);
-    if (status.cbInQue > 0) {
-        s->len = status.cbInQue;
-        win_chr_read_poll(chr);
-        win_chr_read(chr);
-        return 1;
-    }
-    return 0;
-}
-
-static CharDriverState *qemu_chr_open_win_path(const char *filename,
-                                               ChardevCommon *backend,
-                                               Error **errp)
-{
-    CharDriverState *chr;
-    WinCharState *s;
-
-    chr = qemu_chr_alloc(backend, errp);
-    if (!chr) {
-        return NULL;
-    }
-    s = g_new0(WinCharState, 1);
-    chr->opaque = s;
-    chr->chr_write = win_chr_write;
-    chr->chr_free = win_chr_free;
-
-    if (win_chr_init(chr, filename, errp) < 0) {
-        g_free(s);
-        qemu_chr_free_common(chr);
-        return NULL;
-    }
-    return chr;
-}
-
-static int win_chr_pipe_poll(void *opaque)
-{
-    CharDriverState *chr = opaque;
-    WinCharState *s = chr->opaque;
-    DWORD size;
-
-    PeekNamedPipe(s->hcom, NULL, 0, NULL, &size, NULL);
-    if (size > 0) {
-        s->len = size;
-        win_chr_read_poll(chr);
-        win_chr_read(chr);
-        return 1;
-    }
-    return 0;
-}
-
-static int win_chr_pipe_init(CharDriverState *chr, const char *filename,
-                             Error **errp)
-{
-    WinCharState *s = chr->opaque;
-    OVERLAPPED ov;
-    int ret;
-    DWORD size;
-    char *openname;
-
-    s->fpipe = TRUE;
-
-    s->hsend = CreateEvent(NULL, TRUE, FALSE, NULL);
-    if (!s->hsend) {
-        error_setg(errp, "Failed CreateEvent");
-        goto fail;
-    }
-    s->hrecv = CreateEvent(NULL, TRUE, FALSE, NULL);
-    if (!s->hrecv) {
-        error_setg(errp, "Failed CreateEvent");
-        goto fail;
-    }
-
-    openname = g_strdup_printf("\\\\.\\pipe\\%s", filename);
-    s->hcom = CreateNamedPipe(openname, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
-                              PIPE_TYPE_BYTE | PIPE_READMODE_BYTE |
-                              PIPE_WAIT,
-                              MAXCONNECT, NSENDBUF, NRECVBUF, NTIMEOUT, NULL);
-    g_free(openname);
-    if (s->hcom == INVALID_HANDLE_VALUE) {
-        error_setg(errp, "Failed CreateNamedPipe (%lu)", GetLastError());
-        s->hcom = NULL;
-        goto fail;
-    }
-
-    ZeroMemory(&ov, sizeof(ov));
-    ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
-    ret = ConnectNamedPipe(s->hcom, &ov);
-    if (ret) {
-        error_setg(errp, "Failed ConnectNamedPipe");
-        goto fail;
-    }
-
-    ret = GetOverlappedResult(s->hcom, &ov, &size, TRUE);
-    if (!ret) {
-        error_setg(errp, "Failed GetOverlappedResult");
-        if (ov.hEvent) {
-            CloseHandle(ov.hEvent);
-            ov.hEvent = NULL;
-        }
-        goto fail;
-    }
-
-    if (ov.hEvent) {
-        CloseHandle(ov.hEvent);
-        ov.hEvent = NULL;
-    }
-    qemu_add_polling_cb(win_chr_pipe_poll, chr);
-    return 0;
-
- fail:
-    win_chr_free(chr);
-    return -1;
-}
-
-
-static CharDriverState *qemu_chr_open_pipe(const char *id,
-                                           ChardevBackend *backend,
-                                           ChardevReturn *ret,
-                                           bool *be_opened,
-                                           Error **errp)
-{
-    ChardevHostdev *opts = backend->u.pipe.data;
-    const char *filename = opts->device;
-    CharDriverState *chr;
-    WinCharState *s;
-    ChardevCommon *common = qapi_ChardevHostdev_base(opts);
-
-    chr = qemu_chr_alloc(common, errp);
-    if (!chr) {
-        return NULL;
-    }
-    s = g_new0(WinCharState, 1);
-    chr->opaque = s;
-    chr->chr_write = win_chr_write;
-    chr->chr_free = win_chr_free;
-
-    if (win_chr_pipe_init(chr, filename, errp) < 0) {
-        g_free(s);
-        qemu_chr_free_common(chr);
-        return NULL;
-    }
-    return chr;
-}
-
-static CharDriverState *qemu_chr_open_win_file(HANDLE fd_out,
-                                               ChardevCommon *backend,
-                                               Error **errp)
-{
-    CharDriverState *chr;
-    WinCharState *s;
-
-    chr = qemu_chr_alloc(backend, errp);
-    if (!chr) {
-        return NULL;
-    }
-    s = g_new0(WinCharState, 1);
-    s->hcom = fd_out;
-    chr->opaque = s;
-    chr->chr_write = win_chr_write;
-    return chr;
-}
-
-static CharDriverState *qemu_chr_open_win_con(const char *id,
-                                              ChardevBackend *backend,
-                                              ChardevReturn *ret,
-                                              bool *be_opened,
-                                              Error **errp)
-{
-    ChardevCommon *common = backend->u.console.data;
-    return qemu_chr_open_win_file(GetStdHandle(STD_OUTPUT_HANDLE),
-                                  common, errp);
-}
-
-static int win_stdio_write(CharDriverState *chr, const uint8_t *buf, int len)
-{
-    HANDLE  hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
-    DWORD   dwSize;
-    int     len1;
-
-    len1 = len;
-
-    while (len1 > 0) {
-        if (!WriteFile(hStdOut, buf, len1, &dwSize, NULL)) {
-            break;
-        }
-        buf  += dwSize;
-        len1 -= dwSize;
-    }
-
-    return len - len1;
-}
-
-static void win_stdio_wait_func(void *opaque)
-{
-    CharDriverState   *chr   = opaque;
-    WinStdioCharState *stdio = chr->opaque;
-    INPUT_RECORD       buf[4];
-    int                ret;
-    DWORD              dwSize;
-    int                i;
-
-    ret = ReadConsoleInput(stdio->hStdIn, buf, ARRAY_SIZE(buf), &dwSize);
-
-    if (!ret) {
-        /* Avoid error storm */
-        qemu_del_wait_object(stdio->hStdIn, NULL, NULL);
-        return;
-    }
-
-    for (i = 0; i < dwSize; i++) {
-        KEY_EVENT_RECORD *kev = &buf[i].Event.KeyEvent;
-
-        if (buf[i].EventType == KEY_EVENT && kev->bKeyDown) {
-            int j;
-            if (kev->uChar.AsciiChar != 0) {
-                for (j = 0; j < kev->wRepeatCount; j++) {
-                    if (qemu_chr_be_can_write(chr)) {
-                        uint8_t c = kev->uChar.AsciiChar;
-                        qemu_chr_be_write(chr, &c, 1);
-                    }
-                }
-            }
-        }
-    }
-}
-
-static DWORD WINAPI win_stdio_thread(LPVOID param)
-{
-    CharDriverState   *chr   = param;
-    WinStdioCharState *stdio = chr->opaque;
-    int                ret;
-    DWORD              dwSize;
-
-    while (1) {
-
-        /* Wait for one byte */
-        ret = ReadFile(stdio->hStdIn, &stdio->win_stdio_buf, 1, &dwSize, NULL);
-
-        /* Exit in case of error, continue if nothing read */
-        if (!ret) {
-            break;
-        }
-        if (!dwSize) {
-            continue;
-        }
-
-        /* Some terminal emulator returns \r\n for Enter, just pass \n */
-        if (stdio->win_stdio_buf == '\r') {
-            continue;
-        }
-
-        /* Signal the main thread and wait until the byte was eaten */
-        if (!SetEvent(stdio->hInputReadyEvent)) {
-            break;
-        }
-        if (WaitForSingleObject(stdio->hInputDoneEvent, INFINITE)
-            != WAIT_OBJECT_0) {
-            break;
-        }
-    }
-
-    qemu_del_wait_object(stdio->hInputReadyEvent, NULL, NULL);
-    return 0;
-}
-
-static void win_stdio_thread_wait_func(void *opaque)
-{
-    CharDriverState   *chr   = opaque;
-    WinStdioCharState *stdio = chr->opaque;
-
-    if (qemu_chr_be_can_write(chr)) {
-        qemu_chr_be_write(chr, &stdio->win_stdio_buf, 1);
-    }
-
-    SetEvent(stdio->hInputDoneEvent);
-}
-
-static void qemu_chr_set_echo_win_stdio(CharDriverState *chr, bool echo)
-{
-    WinStdioCharState *stdio  = chr->opaque;
-    DWORD              dwMode = 0;
-
-    GetConsoleMode(stdio->hStdIn, &dwMode);
-
-    if (echo) {
-        SetConsoleMode(stdio->hStdIn, dwMode | ENABLE_ECHO_INPUT);
-    } else {
-        SetConsoleMode(stdio->hStdIn, dwMode & ~ENABLE_ECHO_INPUT);
-    }
-}
-
-static void win_stdio_free(CharDriverState *chr)
-{
-    WinStdioCharState *stdio = chr->opaque;
-
-    if (stdio->hInputReadyEvent != INVALID_HANDLE_VALUE) {
-        CloseHandle(stdio->hInputReadyEvent);
-    }
-    if (stdio->hInputDoneEvent != INVALID_HANDLE_VALUE) {
-        CloseHandle(stdio->hInputDoneEvent);
-    }
-    if (stdio->hInputThread != INVALID_HANDLE_VALUE) {
-        TerminateThread(stdio->hInputThread, 0);
-    }
-
-    g_free(chr->opaque);
-}
-
-static CharDriverState *qemu_chr_open_stdio(const char *id,
-                                            ChardevBackend *backend,
-                                            ChardevReturn *ret,
-                                            bool *be_opened,
-                                            Error **errp)
-{
-    CharDriverState   *chr;
-    WinStdioCharState *stdio;
-    DWORD              dwMode;
-    int                is_console = 0;
-    ChardevCommon *common = qapi_ChardevStdio_base(backend->u.stdio.data);
-
-    chr   = qemu_chr_alloc(common, errp);
-    if (!chr) {
-        return NULL;
-    }
-    stdio = g_new0(WinStdioCharState, 1);
-
-    stdio->hStdIn = GetStdHandle(STD_INPUT_HANDLE);
-    if (stdio->hStdIn == INVALID_HANDLE_VALUE) {
-        error_setg(errp, "cannot open stdio: invalid handle");
-        return NULL;
-    }
-
-    is_console = GetConsoleMode(stdio->hStdIn, &dwMode) != 0;
-
-    chr->opaque    = stdio;
-    chr->chr_write = win_stdio_write;
-    chr->chr_free = win_stdio_free;
-
-    if (is_console) {
-        if (qemu_add_wait_object(stdio->hStdIn,
-                                 win_stdio_wait_func, chr)) {
-            error_setg(errp, "qemu_add_wait_object: failed");
-            goto err1;
-        }
-    } else {
-        DWORD   dwId;
-            
-        stdio->hInputReadyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
-        stdio->hInputDoneEvent  = CreateEvent(NULL, FALSE, FALSE, NULL);
-        if (stdio->hInputReadyEvent == INVALID_HANDLE_VALUE
-            || stdio->hInputDoneEvent == INVALID_HANDLE_VALUE) {
-            error_setg(errp, "cannot create event");
-            goto err2;
-        }
-        if (qemu_add_wait_object(stdio->hInputReadyEvent,
-                                 win_stdio_thread_wait_func, chr)) {
-            error_setg(errp, "qemu_add_wait_object: failed");
-            goto err2;
-        }
-        stdio->hInputThread     = CreateThread(NULL, 0, win_stdio_thread,
-                                               chr, 0, &dwId);
-
-        if (stdio->hInputThread == INVALID_HANDLE_VALUE) {
-            error_setg(errp, "cannot create stdio thread");
-            goto err3;
-        }
-    }
-
-    dwMode |= ENABLE_LINE_INPUT;
-
-    if (is_console) {
-        /* set the terminal in raw mode */
-        /* ENABLE_QUICK_EDIT_MODE | ENABLE_EXTENDED_FLAGS */
-        dwMode |= ENABLE_PROCESSED_INPUT;
-    }
-
-    SetConsoleMode(stdio->hStdIn, dwMode);
-
-    chr->chr_set_echo = qemu_chr_set_echo_win_stdio;
-    qemu_chr_set_echo_win_stdio(chr, false);
-
-    return chr;
-
-err3:
-    qemu_del_wait_object(stdio->hInputReadyEvent, NULL, NULL);
-err2:
-    CloseHandle(stdio->hInputReadyEvent);
-    CloseHandle(stdio->hInputDoneEvent);
-err1:
-    qemu_del_wait_object(stdio->hStdIn, NULL, NULL);
-    return NULL;
-}
-#endif /* !_WIN32 */
-
-/***********************************************************/
-/* UDP Net console */
-
-typedef struct {
-    QIOChannel *ioc;
-    uint8_t buf[READ_BUF_LEN];
-    int bufcnt;
-    int bufptr;
-    int max_size;
-} NetCharDriver;
-
-/* Called with chr_write_lock held.  */
-static int udp_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
-{
-    NetCharDriver *s = chr->opaque;
-
-    return qio_channel_write(
-        s->ioc, (const char *)buf, len, NULL);
-}
-
-static int udp_chr_read_poll(void *opaque)
-{
-    CharDriverState *chr = opaque;
-    NetCharDriver *s = chr->opaque;
-
-    s->max_size = qemu_chr_be_can_write(chr);
-
-    /* If there were any stray characters in the queue process them
-     * first
-     */
-    while (s->max_size > 0 && s->bufptr < s->bufcnt) {
-        qemu_chr_be_write(chr, &s->buf[s->bufptr], 1);
-        s->bufptr++;
-        s->max_size = qemu_chr_be_can_write(chr);
-    }
-    return s->max_size;
-}
-
-static gboolean udp_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
-{
-    CharDriverState *chr = opaque;
-    NetCharDriver *s = chr->opaque;
-    ssize_t ret;
-
-    if (s->max_size == 0) {
-        return TRUE;
-    }
-    ret = qio_channel_read(
-        s->ioc, (char *)s->buf, sizeof(s->buf), NULL);
-    if (ret <= 0) {
-        remove_fd_in_watch(chr);
-        return FALSE;
-    }
-    s->bufcnt = ret;
-
-    s->bufptr = 0;
-    while (s->max_size > 0 && s->bufptr < s->bufcnt) {
-        qemu_chr_be_write(chr, &s->buf[s->bufptr], 1);
-        s->bufptr++;
-        s->max_size = qemu_chr_be_can_write(chr);
-    }
-
-    return TRUE;
-}
-
-static void udp_chr_update_read_handler(CharDriverState *chr,
-                                        GMainContext *context)
-{
-    NetCharDriver *s = chr->opaque;
-
-    remove_fd_in_watch(chr);
-    if (s->ioc) {
-        chr->fd_in_tag = io_add_watch_poll(chr, s->ioc,
-                                           udp_chr_read_poll,
-                                           udp_chr_read, chr,
-                                           context);
-    }
-}
-
-static void udp_chr_free(CharDriverState *chr)
-{
-    NetCharDriver *s = chr->opaque;
-
-    remove_fd_in_watch(chr);
-    if (s->ioc) {
-        object_unref(OBJECT(s->ioc));
-    }
-    g_free(s);
-    qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
-}
-
-static CharDriverState *qemu_chr_open_udp(QIOChannelSocket *sioc,
-                                          ChardevCommon *backend,
-                                          bool *be_opened,
-                                          Error **errp)
-{
-    CharDriverState *chr = NULL;
-    NetCharDriver *s = NULL;
-
-    chr = qemu_chr_alloc(backend, errp);
-    if (!chr) {
-        return NULL;
-    }
-    s = g_new0(NetCharDriver, 1);
-
-    s->ioc = QIO_CHANNEL(sioc);
-    s->bufcnt = 0;
-    s->bufptr = 0;
-    chr->opaque = s;
-    chr->chr_write = udp_chr_write;
-    chr->chr_update_read_handler = udp_chr_update_read_handler;
-    chr->chr_free = udp_chr_free;
-    /* be isn't opened until we get a connection */
-    *be_opened = false;
-    return chr;
-}
-
-/***********************************************************/
-/* TCP Net console */
-
-typedef struct {
-    QIOChannel *ioc; /* Client I/O channel */
-    QIOChannelSocket *sioc; /* Client master channel */
-    QIOChannelSocket *listen_ioc;
-    guint listen_tag;
-    QCryptoTLSCreds *tls_creds;
-    int connected;
-    int max_size;
-    int do_telnetopt;
-    int do_nodelay;
-    int is_unix;
-    int *read_msgfds;
-    size_t read_msgfds_num;
-    int *write_msgfds;
-    size_t write_msgfds_num;
-
-    SocketAddress *addr;
-    bool is_listen;
-    bool is_telnet;
-
-    guint reconnect_timer;
-    int64_t reconnect_time;
-    bool connect_err_reported;
-} TCPCharDriver;
-
-static gboolean socket_reconnect_timeout(gpointer opaque);
-
-static void qemu_chr_socket_restart_timer(CharDriverState *chr)
-{
-    TCPCharDriver *s = chr->opaque;
-    char *name;
-    assert(s->connected == 0);
-    s->reconnect_timer = g_timeout_add_seconds(s->reconnect_time,
-                                               socket_reconnect_timeout, chr);
-    name = g_strdup_printf("chardev-socket-reconnect-%s", chr->label);
-    g_source_set_name_by_id(s->reconnect_timer, name);
-    g_free(name);
-}
-
-static void check_report_connect_error(CharDriverState *chr,
-                                       Error *err)
-{
-    TCPCharDriver *s = chr->opaque;
-
-    if (!s->connect_err_reported) {
-        error_report("Unable to connect character device %s: %s",
-                     chr->label, error_get_pretty(err));
-        s->connect_err_reported = true;
-    }
-    qemu_chr_socket_restart_timer(chr);
-}
-
-static gboolean tcp_chr_accept(QIOChannel *chan,
-                               GIOCondition cond,
-                               void *opaque);
-
-/* Called with chr_write_lock held.  */
-static int tcp_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
-{
-    TCPCharDriver *s = chr->opaque;
-    if (s->connected) {
-        int ret =  io_channel_send_full(s->ioc, buf, len,
-                                        s->write_msgfds,
-                                        s->write_msgfds_num);
-
-        /* free the written msgfds, no matter what */
-        if (s->write_msgfds_num) {
-            g_free(s->write_msgfds);
-            s->write_msgfds = 0;
-            s->write_msgfds_num = 0;
-        }
-
-        return ret;
-    } else {
-        /* XXX: indicate an error ? */
-        return len;
-    }
-}
-
-static int tcp_chr_read_poll(void *opaque)
-{
-    CharDriverState *chr = opaque;
-    TCPCharDriver *s = chr->opaque;
-    if (!s->connected)
-        return 0;
-    s->max_size = qemu_chr_be_can_write(chr);
-    return s->max_size;
-}
-
-#define IAC 255
-#define IAC_BREAK 243
-static void tcp_chr_process_IAC_bytes(CharDriverState *chr,
-                                      TCPCharDriver *s,
-                                      uint8_t *buf, int *size)
-{
-    /* Handle any telnet client's basic IAC options to satisfy char by
-     * char mode with no echo.  All IAC options will be removed from
-     * the buf and the do_telnetopt variable will be used to track the
-     * state of the width of the IAC information.
-     *
-     * IAC commands come in sets of 3 bytes with the exception of the
-     * "IAC BREAK" command and the double IAC.
-     */
-
-    int i;
-    int j = 0;
-
-    for (i = 0; i < *size; i++) {
-        if (s->do_telnetopt > 1) {
-            if ((unsigned char)buf[i] == IAC && s->do_telnetopt == 2) {
-                /* Double IAC means send an IAC */
-                if (j != i)
-                    buf[j] = buf[i];
-                j++;
-                s->do_telnetopt = 1;
-            } else {
-                if ((unsigned char)buf[i] == IAC_BREAK && s->do_telnetopt == 2) {
-                    /* Handle IAC break commands by sending a serial break */
-                    qemu_chr_be_event(chr, CHR_EVENT_BREAK);
-                    s->do_telnetopt++;
-                }
-                s->do_telnetopt++;
-            }
-            if (s->do_telnetopt >= 4) {
-                s->do_telnetopt = 1;
-            }
-        } else {
-            if ((unsigned char)buf[i] == IAC) {
-                s->do_telnetopt = 2;
-            } else {
-                if (j != i)
-                    buf[j] = buf[i];
-                j++;
-            }
-        }
-    }
-    *size = j;
-}
-
-static int tcp_get_msgfds(CharDriverState *chr, int *fds, int num)
-{
-    TCPCharDriver *s = chr->opaque;
-    int to_copy = (s->read_msgfds_num < num) ? s->read_msgfds_num : num;
-
-    assert(num <= TCP_MAX_FDS);
-
-    if (to_copy) {
-        int i;
-
-        memcpy(fds, s->read_msgfds, to_copy * sizeof(int));
-
-        /* Close unused fds */
-        for (i = to_copy; i < s->read_msgfds_num; i++) {
-            close(s->read_msgfds[i]);
-        }
-
-        g_free(s->read_msgfds);
-        s->read_msgfds = 0;
-        s->read_msgfds_num = 0;
-    }
-
-    return to_copy;
-}
-
-static int tcp_set_msgfds(CharDriverState *chr, int *fds, int num)
-{
-    TCPCharDriver *s = chr->opaque;
-
-    /* clear old pending fd array */
-    g_free(s->write_msgfds);
-    s->write_msgfds = NULL;
-    s->write_msgfds_num = 0;
-
-    if (!s->connected ||
-        !qio_channel_has_feature(s->ioc,
-                                 QIO_CHANNEL_FEATURE_FD_PASS)) {
-        return -1;
-    }
-
-    if (num) {
-        s->write_msgfds = g_new(int, num);
-        memcpy(s->write_msgfds, fds, num * sizeof(int));
-    }
-
-    s->write_msgfds_num = num;
-
-    return 0;
-}
-
-static ssize_t tcp_chr_recv(CharDriverState *chr, char *buf, size_t len)
-{
-    TCPCharDriver *s = chr->opaque;
-    struct iovec iov = { .iov_base = buf, .iov_len = len };
-    int ret;
-    size_t i;
-    int *msgfds = NULL;
-    size_t msgfds_num = 0;
-
-    if (qio_channel_has_feature(s->ioc, QIO_CHANNEL_FEATURE_FD_PASS)) {
-        ret = qio_channel_readv_full(s->ioc, &iov, 1,
-                                     &msgfds, &msgfds_num,
-                                     NULL);
-    } else {
-        ret = qio_channel_readv_full(s->ioc, &iov, 1,
-                                     NULL, NULL,
-                                     NULL);
-    }
-
-    if (ret == QIO_CHANNEL_ERR_BLOCK) {
-        errno = EAGAIN;
-        ret = -1;
-    } else if (ret == -1) {
-        errno = EIO;
-    }
-
-    if (msgfds_num) {
-        /* close and clean read_msgfds */
-        for (i = 0; i < s->read_msgfds_num; i++) {
-            close(s->read_msgfds[i]);
-        }
-
-        if (s->read_msgfds_num) {
-            g_free(s->read_msgfds);
-        }
-
-        s->read_msgfds = msgfds;
-        s->read_msgfds_num = msgfds_num;
-    }
-
-    for (i = 0; i < s->read_msgfds_num; i++) {
-        int fd = s->read_msgfds[i];
-        if (fd < 0) {
-            continue;
-        }
-
-        /* O_NONBLOCK is preserved across SCM_RIGHTS so reset it */
-        qemu_set_block(fd);
-
-#ifndef MSG_CMSG_CLOEXEC
-        qemu_set_cloexec(fd);
-#endif
-    }
-
-    return ret;
-}
-
-static GSource *tcp_chr_add_watch(CharDriverState *chr, GIOCondition cond)
-{
-    TCPCharDriver *s = chr->opaque;
-    return qio_channel_create_watch(s->ioc, cond);
-}
-
-static void tcp_chr_free_connection(CharDriverState *chr)
-{
-    TCPCharDriver *s = chr->opaque;
-    int i;
-
-    if (!s->connected) {
-        return;
-    }
-
-    if (s->read_msgfds_num) {
-        for (i = 0; i < s->read_msgfds_num; i++) {
-            close(s->read_msgfds[i]);
-        }
-        g_free(s->read_msgfds);
-        s->read_msgfds = NULL;
-        s->read_msgfds_num = 0;
-    }
-
-    tcp_set_msgfds(chr, NULL, 0);
-    remove_fd_in_watch(chr);
-    object_unref(OBJECT(s->sioc));
-    s->sioc = NULL;
-    object_unref(OBJECT(s->ioc));
-    s->ioc = NULL;
-    g_free(chr->filename);
-    chr->filename = NULL;
-    s->connected = 0;
-}
-
-static void tcp_chr_disconnect(CharDriverState *chr)
-{
-    TCPCharDriver *s = chr->opaque;
-
-    if (!s->connected) {
-        return;
-    }
-
-    tcp_chr_free_connection(chr);
-
-    if (s->listen_ioc) {
-        s->listen_tag = qio_channel_add_watch(
-            QIO_CHANNEL(s->listen_ioc), G_IO_IN, tcp_chr_accept, chr, NULL);
-    }
-    chr->filename = SocketAddress_to_str("disconnected:", s->addr,
-                                         s->is_listen, s->is_telnet);
-    qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
-    if (s->reconnect_time) {
-        qemu_chr_socket_restart_timer(chr);
-    }
-}
-
-static gboolean tcp_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
-{
-    CharDriverState *chr = opaque;
-    TCPCharDriver *s = chr->opaque;
-    uint8_t buf[READ_BUF_LEN];
-    int len, size;
-
-    if (!s->connected || s->max_size <= 0) {
-        return TRUE;
-    }
-    len = sizeof(buf);
-    if (len > s->max_size)
-        len = s->max_size;
-    size = tcp_chr_recv(chr, (void *)buf, len);
-    if (size == 0 || size == -1) {
-        /* connection closed */
-        tcp_chr_disconnect(chr);
-    } else if (size > 0) {
-        if (s->do_telnetopt)
-            tcp_chr_process_IAC_bytes(chr, s, buf, &size);
-        if (size > 0)
-            qemu_chr_be_write(chr, buf, size);
-    }
-
-    return TRUE;
-}
-
-static int tcp_chr_sync_read(CharDriverState *chr, const uint8_t *buf, int len)
-{
-    TCPCharDriver *s = chr->opaque;
-    int size;
-
-    if (!s->connected) {
-        return 0;
-    }
-
-    size = tcp_chr_recv(chr, (void *) buf, len);
-    if (size == 0) {
-        /* connection closed */
-        tcp_chr_disconnect(chr);
-    }
-
-    return size;
-}
-
-static void tcp_chr_connect(void *opaque)
-{
-    CharDriverState *chr = opaque;
-    TCPCharDriver *s = chr->opaque;
-
-    g_free(chr->filename);
-    chr->filename = sockaddr_to_str(
-        &s->sioc->localAddr, s->sioc->localAddrLen,
-        &s->sioc->remoteAddr, s->sioc->remoteAddrLen,
-        s->is_listen, s->is_telnet);
-
-    s->connected = 1;
-    if (s->ioc) {
-        chr->fd_in_tag = io_add_watch_poll(chr, s->ioc,
-                                           tcp_chr_read_poll,
-                                           tcp_chr_read,
-                                           chr, NULL);
-    }
-    qemu_chr_be_generic_open(chr);
-}
-
-static void tcp_chr_update_read_handler(CharDriverState *chr,
-                                        GMainContext *context)
-{
-    TCPCharDriver *s = chr->opaque;
-
-    if (!s->connected) {
-        return;
-    }
-
-    remove_fd_in_watch(chr);
-    if (s->ioc) {
-        chr->fd_in_tag = io_add_watch_poll(chr, s->ioc,
-                                           tcp_chr_read_poll,
-                                           tcp_chr_read, chr,
-                                           context);
-    }
-}
-
-typedef struct {
-    CharDriverState *chr;
-    char buf[12];
-    size_t buflen;
-} TCPCharDriverTelnetInit;
-
-static gboolean tcp_chr_telnet_init_io(QIOChannel *ioc,
-                                       GIOCondition cond G_GNUC_UNUSED,
-                                       gpointer user_data)
-{
-    TCPCharDriverTelnetInit *init = user_data;
-    ssize_t ret;
-
-    ret = qio_channel_write(ioc, init->buf, init->buflen, NULL);
-    if (ret < 0) {
-        if (ret == QIO_CHANNEL_ERR_BLOCK) {
-            ret = 0;
-        } else {
-            tcp_chr_disconnect(init->chr);
-            return FALSE;
-        }
-    }
-    init->buflen -= ret;
-
-    if (init->buflen == 0) {
-        tcp_chr_connect(init->chr);
-        return FALSE;
-    }
-
-    memmove(init->buf, init->buf + ret, init->buflen);
-
-    return TRUE;
-}
-
-static void tcp_chr_telnet_init(CharDriverState *chr)
-{
-    TCPCharDriver *s = chr->opaque;
-    TCPCharDriverTelnetInit *init =
-        g_new0(TCPCharDriverTelnetInit, 1);
-    size_t n = 0;
-
-    init->chr = chr;
-    init->buflen = 12;
-
-#define IACSET(x, a, b, c)                      \
-    do {                                        \
-        x[n++] = a;                             \
-        x[n++] = b;                             \
-        x[n++] = c;                             \
-    } while (0)
-
-    /* Prep the telnet negotion to put telnet in binary,
-     * no echo, single char mode */
-    IACSET(init->buf, 0xff, 0xfb, 0x01);  /* IAC WILL ECHO */
-    IACSET(init->buf, 0xff, 0xfb, 0x03);  /* IAC WILL Suppress go ahead */
-    IACSET(init->buf, 0xff, 0xfb, 0x00);  /* IAC WILL Binary */
-    IACSET(init->buf, 0xff, 0xfd, 0x00);  /* IAC DO Binary */
-
-#undef IACSET
-
-    qio_channel_add_watch(
-        s->ioc, G_IO_OUT,
-        tcp_chr_telnet_init_io,
-        init, NULL);
-}
-
-
-static void tcp_chr_tls_handshake(QIOTask *task,
-                                  gpointer user_data)
-{
-    CharDriverState *chr = user_data;
-    TCPCharDriver *s = chr->opaque;
-
-    if (qio_task_propagate_error(task, NULL)) {
-        tcp_chr_disconnect(chr);
-    } else {
-        if (s->do_telnetopt) {
-            tcp_chr_telnet_init(chr);
-        } else {
-            tcp_chr_connect(chr);
-        }
-    }
-}
-
-
-static void tcp_chr_tls_init(CharDriverState *chr)
-{
-    TCPCharDriver *s = chr->opaque;
-    QIOChannelTLS *tioc;
-    Error *err = NULL;
-    gchar *name;
-
-    if (s->is_listen) {
-        tioc = qio_channel_tls_new_server(
-            s->ioc, s->tls_creds,
-            NULL, /* XXX Use an ACL */
-            &err);
-    } else {
-        tioc = qio_channel_tls_new_client(
-            s->ioc, s->tls_creds,
-            s->addr->u.inet.data->host,
-            &err);
-    }
-    if (tioc == NULL) {
-        error_free(err);
-        tcp_chr_disconnect(chr);
-        return;
-    }
-    name = g_strdup_printf("chardev-tls-%s-%s",
-                           s->is_listen ? "server" : "client",
-                           chr->label);
-    qio_channel_set_name(QIO_CHANNEL(tioc), name);
-    g_free(name);
-    object_unref(OBJECT(s->ioc));
-    s->ioc = QIO_CHANNEL(tioc);
-
-    qio_channel_tls_handshake(tioc,
-                              tcp_chr_tls_handshake,
-                              chr,
-                              NULL);
-}
-
-
-static void tcp_chr_set_client_ioc_name(CharDriverState *chr,
-                                        QIOChannelSocket *sioc)
-{
-    TCPCharDriver *s = chr->opaque;
-    char *name;
-    name = g_strdup_printf("chardev-tcp-%s-%s",
-                           s->is_listen ? "server" : "client",
-                           chr->label);
-    qio_channel_set_name(QIO_CHANNEL(sioc), name);
-    g_free(name);
-
-}
-
-static int tcp_chr_new_client(CharDriverState *chr, QIOChannelSocket *sioc)
-{
-    TCPCharDriver *s = chr->opaque;
-    if (s->ioc != NULL) {
-	return -1;
-    }
-
-    s->ioc = QIO_CHANNEL(sioc);
-    object_ref(OBJECT(sioc));
-    s->sioc = sioc;
-    object_ref(OBJECT(sioc));
-
-    qio_channel_set_blocking(s->ioc, false, NULL);
-
-    if (s->do_nodelay) {
-        qio_channel_set_delay(s->ioc, false);
-    }
-    if (s->listen_tag) {
-        g_source_remove(s->listen_tag);
-        s->listen_tag = 0;
-    }
-
-    if (s->tls_creds) {
-        tcp_chr_tls_init(chr);
-    } else {
-        if (s->do_telnetopt) {
-            tcp_chr_telnet_init(chr);
-        } else {
-            tcp_chr_connect(chr);
-        }
-    }
-
-    return 0;
-}
-
-
-static int tcp_chr_add_client(CharDriverState *chr, int fd)
-{
-    int ret;
-    QIOChannelSocket *sioc;
-
-    sioc = qio_channel_socket_new_fd(fd, NULL);
-    if (!sioc) {
-        return -1;
-    }
-    tcp_chr_set_client_ioc_name(chr, sioc);
-    ret = tcp_chr_new_client(chr, sioc);
-    object_unref(OBJECT(sioc));
-    return ret;
-}
-
-static gboolean tcp_chr_accept(QIOChannel *channel,
-                               GIOCondition cond,
-                               void *opaque)
-{
-    CharDriverState *chr = opaque;
-    QIOChannelSocket *sioc;
-
-    sioc = qio_channel_socket_accept(QIO_CHANNEL_SOCKET(channel),
-                                     NULL);
-    if (!sioc) {
-        return TRUE;
-    }
-
-    tcp_chr_new_client(chr, sioc);
-
-    object_unref(OBJECT(sioc));
-
-    return TRUE;
-}
-
-static int tcp_chr_wait_connected(CharDriverState *chr, Error **errp)
-{
-    TCPCharDriver *s = chr->opaque;
-    QIOChannelSocket *sioc;
-
-    /* It can't wait on s->connected, since it is set asynchronously
-     * in TLS and telnet cases, only wait for an accepted socket */
-    while (!s->ioc) {
-        if (s->is_listen) {
-            fprintf(stderr, "QEMU waiting for connection on: %s\n",
-                    chr->filename);
-            qio_channel_set_blocking(QIO_CHANNEL(s->listen_ioc), true, NULL);
-            tcp_chr_accept(QIO_CHANNEL(s->listen_ioc), G_IO_IN, chr);
-            qio_channel_set_blocking(QIO_CHANNEL(s->listen_ioc), false, NULL);
-        } else {
-            sioc = qio_channel_socket_new();
-            tcp_chr_set_client_ioc_name(chr, sioc);
-            if (qio_channel_socket_connect_sync(sioc, s->addr, errp) < 0) {
-                object_unref(OBJECT(sioc));
-                return -1;
-            }
-            tcp_chr_new_client(chr, sioc);
-            object_unref(OBJECT(sioc));
-        }
-    }
-
-    return 0;
-}
-
-static int qemu_chr_wait_connected(CharDriverState *chr, Error **errp)
-{
-    if (chr->chr_wait_connected) {
-        return chr->chr_wait_connected(chr, errp);
-    }
-
-    return 0;
-}
-
-int qemu_chr_fe_wait_connected(CharBackend *be, Error **errp)
-{
-    if (!be->chr) {
-        error_setg(errp, "missing associated backend");
-        return -1;
-    }
-
-    return qemu_chr_wait_connected(be->chr, errp);
-}
-
-static void tcp_chr_free(CharDriverState *chr)
-{
-    TCPCharDriver *s = chr->opaque;
-
-    tcp_chr_free_connection(chr);
-
-    if (s->reconnect_timer) {
-        g_source_remove(s->reconnect_timer);
-        s->reconnect_timer = 0;
-    }
-    qapi_free_SocketAddress(s->addr);
-    if (s->listen_tag) {
-        g_source_remove(s->listen_tag);
-        s->listen_tag = 0;
-    }
-    if (s->listen_ioc) {
-        object_unref(OBJECT(s->listen_ioc));
-    }
-    if (s->tls_creds) {
-        object_unref(OBJECT(s->tls_creds));
-    }
-    g_free(s);
-    qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
-}
-
-
-static void qemu_chr_socket_connected(QIOTask *task, void *opaque)
-{
-    QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(qio_task_get_source(task));
-    CharDriverState *chr = opaque;
-    TCPCharDriver *s = chr->opaque;
-    Error *err = NULL;
-
-    if (qio_task_propagate_error(task, &err)) {
-        check_report_connect_error(chr, err);
-        error_free(err);
-        goto cleanup;
-    }
-
-    s->connect_err_reported = false;
-    tcp_chr_new_client(chr, sioc);
-
- cleanup:
-    object_unref(OBJECT(sioc));
-}
-
-
-/*********************************************************/
-/* Ring buffer chardev */
-
-typedef struct {
-    size_t size;
-    size_t prod;
-    size_t cons;
-    uint8_t *cbuf;
-} RingBufCharDriver;
-
-static size_t ringbuf_count(const CharDriverState *chr)
-{
-    const RingBufCharDriver *d = chr->opaque;
-
-    return d->prod - d->cons;
-}
-
-/* Called with chr_write_lock held.  */
-static int ringbuf_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
-{
-    RingBufCharDriver *d = chr->opaque;
-    int i;
-
-    if (!buf || (len < 0)) {
-        return -1;
-    }
-
-    for (i = 0; i < len; i++ ) {
-        d->cbuf[d->prod++ & (d->size - 1)] = buf[i];
-        if (d->prod - d->cons > d->size) {
-            d->cons = d->prod - d->size;
-        }
-    }
-
-    return len;
-}
-
-static int ringbuf_chr_read(CharDriverState *chr, uint8_t *buf, int len)
-{
-    RingBufCharDriver *d = chr->opaque;
-    int i;
-
-    qemu_mutex_lock(&chr->chr_write_lock);
-    for (i = 0; i < len && d->cons != d->prod; i++) {
-        buf[i] = d->cbuf[d->cons++ & (d->size - 1)];
-    }
-    qemu_mutex_unlock(&chr->chr_write_lock);
-
-    return i;
-}
-
-static void ringbuf_chr_free(struct CharDriverState *chr)
-{
-    RingBufCharDriver *d = chr->opaque;
-
-    g_free(d->cbuf);
-    g_free(d);
-    chr->opaque = NULL;
-}
-
-static CharDriverState *qemu_chr_open_ringbuf(const char *id,
-                                              ChardevBackend *backend,
-                                              ChardevReturn *ret,
-                                              bool *be_opened,
-                                              Error **errp)
-{
-    ChardevRingbuf *opts = backend->u.ringbuf.data;
-    ChardevCommon *common = qapi_ChardevRingbuf_base(opts);
-    CharDriverState *chr;
-    RingBufCharDriver *d;
-
-    chr = qemu_chr_alloc(common, errp);
-    if (!chr) {
-        return NULL;
-    }
-    d = g_malloc(sizeof(*d));
-
-    d->size = opts->has_size ? opts->size : 65536;
-
-    /* The size must be power of 2 */
-    if (d->size & (d->size - 1)) {
-        error_setg(errp, "size of ringbuf chardev must be power of two");
-        goto fail;
-    }
-
-    d->prod = 0;
-    d->cons = 0;
-    d->cbuf = g_malloc0(d->size);
-
-    chr->opaque = d;
-    chr->chr_write = ringbuf_chr_write;
-    chr->chr_free = ringbuf_chr_free;
-
-    return chr;
-
-fail:
-    g_free(d);
-    qemu_chr_free_common(chr);
-    return NULL;
-}
-
-bool chr_is_ringbuf(const CharDriverState *chr)
-{
-    return chr->chr_write == ringbuf_chr_write;
-}
-
-void qmp_ringbuf_write(const char *device, const char *data,
-                       bool has_format, enum DataFormat format,
-                       Error **errp)
-{
-    CharDriverState *chr;
-    const uint8_t *write_data;
-    int ret;
-    gsize write_count;
-
-    chr = qemu_chr_find(device);
-    if (!chr) {
-        error_setg(errp, "Device '%s' not found", device);
-        return;
-    }
-
-    if (!chr_is_ringbuf(chr)) {
-        error_setg(errp,"%s is not a ringbuf device", device);
-        return;
-    }
-
-    if (has_format && (format == DATA_FORMAT_BASE64)) {
-        write_data = qbase64_decode(data, -1,
-                                    &write_count,
-                                    errp);
-        if (!write_data) {
-            return;
-        }
-    } else {
-        write_data = (uint8_t *)data;
-        write_count = strlen(data);
-    }
-
-    ret = ringbuf_chr_write(chr, write_data, write_count);
-
-    if (write_data != (uint8_t *)data) {
-        g_free((void *)write_data);
-    }
-
-    if (ret < 0) {
-        error_setg(errp, "Failed to write to device %s", device);
-        return;
-    }
-}
-
-char *qmp_ringbuf_read(const char *device, int64_t size,
-                       bool has_format, enum DataFormat format,
-                       Error **errp)
-{
-    CharDriverState *chr;
-    uint8_t *read_data;
-    size_t count;
-    char *data;
-
-    chr = qemu_chr_find(device);
-    if (!chr) {
-        error_setg(errp, "Device '%s' not found", device);
-        return NULL;
-    }
-
-    if (!chr_is_ringbuf(chr)) {
-        error_setg(errp,"%s is not a ringbuf device", device);
-        return NULL;
-    }
-
-    if (size <= 0) {
-        error_setg(errp, "size must be greater than zero");
-        return NULL;
-    }
-
-    count = ringbuf_count(chr);
-    size = size > count ? count : size;
-    read_data = g_malloc(size + 1);
-
-    ringbuf_chr_read(chr, read_data, size);
-
-    if (has_format && (format == DATA_FORMAT_BASE64)) {
-        data = g_base64_encode(read_data, size);
-        g_free(read_data);
-    } else {
-        /*
-         * FIXME should read only complete, valid UTF-8 characters up
-         * to @size bytes.  Invalid sequences should be replaced by a
-         * suitable replacement character.  Except when (and only
-         * when) ring buffer lost characters since last read, initial
-         * continuation characters should be dropped.
-         */
-        read_data[size] = 0;
-        data = (char *)read_data;
-    }
-
-    return data;
-}
-
-QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename)
-{
-    char host[65], port[33], width[8], height[8];
-    int pos;
-    const char *p;
-    QemuOpts *opts;
-    Error *local_err = NULL;
-
-    opts = qemu_opts_create(qemu_find_opts("chardev"), label, 1, &local_err);
-    if (local_err) {
-        error_report_err(local_err);
-        return NULL;
-    }
-
-    if (strstart(filename, "mon:", &p)) {
-        filename = p;
-        qemu_opt_set(opts, "mux", "on", &error_abort);
-        if (strcmp(filename, "stdio") == 0) {
-            /* Monitor is muxed to stdio: do not exit on Ctrl+C by default
-             * but pass it to the guest.  Handle this only for compat syntax,
-             * for -chardev syntax we have special option for this.
-             * This is what -nographic did, redirecting+muxing serial+monitor
-             * to stdio causing Ctrl+C to be passed to guest. */
-            qemu_opt_set(opts, "signal", "off", &error_abort);
-        }
-    }
-
-    if (strcmp(filename, "null")    == 0 ||
-        strcmp(filename, "pty")     == 0 ||
-        strcmp(filename, "msmouse") == 0 ||
-        strcmp(filename, "braille") == 0 ||
-        strcmp(filename, "testdev") == 0 ||
-        strcmp(filename, "stdio")   == 0) {
-        qemu_opt_set(opts, "backend", filename, &error_abort);
-        return opts;
-    }
-    if (strstart(filename, "vc", &p)) {
-        qemu_opt_set(opts, "backend", "vc", &error_abort);
-        if (*p == ':') {
-            if (sscanf(p+1, "%7[0-9]x%7[0-9]", width, height) == 2) {
-                /* pixels */
-                qemu_opt_set(opts, "width", width, &error_abort);
-                qemu_opt_set(opts, "height", height, &error_abort);
-            } else if (sscanf(p+1, "%7[0-9]Cx%7[0-9]C", width, height) == 2) {
-                /* chars */
-                qemu_opt_set(opts, "cols", width, &error_abort);
-                qemu_opt_set(opts, "rows", height, &error_abort);
-            } else {
-                goto fail;
-            }
-        }
-        return opts;
-    }
-    if (strcmp(filename, "con:") == 0) {
-        qemu_opt_set(opts, "backend", "console", &error_abort);
-        return opts;
-    }
-    if (strstart(filename, "COM", NULL)) {
-        qemu_opt_set(opts, "backend", "serial", &error_abort);
-        qemu_opt_set(opts, "path", filename, &error_abort);
-        return opts;
-    }
-    if (strstart(filename, "file:", &p)) {
-        qemu_opt_set(opts, "backend", "file", &error_abort);
-        qemu_opt_set(opts, "path", p, &error_abort);
-        return opts;
-    }
-    if (strstart(filename, "pipe:", &p)) {
-        qemu_opt_set(opts, "backend", "pipe", &error_abort);
-        qemu_opt_set(opts, "path", p, &error_abort);
-        return opts;
-    }
-    if (strstart(filename, "tcp:", &p) ||
-        strstart(filename, "telnet:", &p)) {
-        if (sscanf(p, "%64[^:]:%32[^,]%n", host, port, &pos) < 2) {
-            host[0] = 0;
-            if (sscanf(p, ":%32[^,]%n", port, &pos) < 1)
-                goto fail;
-        }
-        qemu_opt_set(opts, "backend", "socket", &error_abort);
-        qemu_opt_set(opts, "host", host, &error_abort);
-        qemu_opt_set(opts, "port", port, &error_abort);
-        if (p[pos] == ',') {
-            qemu_opts_do_parse(opts, p+pos+1, NULL, &local_err);
-            if (local_err) {
-                error_report_err(local_err);
-                goto fail;
-            }
-        }
-        if (strstart(filename, "telnet:", &p))
-            qemu_opt_set(opts, "telnet", "on", &error_abort);
-        return opts;
-    }
-    if (strstart(filename, "udp:", &p)) {
-        qemu_opt_set(opts, "backend", "udp", &error_abort);
-        if (sscanf(p, "%64[^:]:%32[^@,]%n", host, port, &pos) < 2) {
-            host[0] = 0;
-            if (sscanf(p, ":%32[^@,]%n", port, &pos) < 1) {
-                goto fail;
-            }
-        }
-        qemu_opt_set(opts, "host", host, &error_abort);
-        qemu_opt_set(opts, "port", port, &error_abort);
-        if (p[pos] == '@') {
-            p += pos + 1;
-            if (sscanf(p, "%64[^:]:%32[^,]%n", host, port, &pos) < 2) {
-                host[0] = 0;
-                if (sscanf(p, ":%32[^,]%n", port, &pos) < 1) {
-                    goto fail;
-                }
-            }
-            qemu_opt_set(opts, "localaddr", host, &error_abort);
-            qemu_opt_set(opts, "localport", port, &error_abort);
-        }
-        return opts;
-    }
-    if (strstart(filename, "unix:", &p)) {
-        qemu_opt_set(opts, "backend", "socket", &error_abort);
-        qemu_opts_do_parse(opts, p, "path", &local_err);
-        if (local_err) {
-            error_report_err(local_err);
-            goto fail;
-        }
-        return opts;
-    }
-    if (strstart(filename, "/dev/parport", NULL) ||
-        strstart(filename, "/dev/ppi", NULL)) {
-        qemu_opt_set(opts, "backend", "parport", &error_abort);
-        qemu_opt_set(opts, "path", filename, &error_abort);
-        return opts;
-    }
-    if (strstart(filename, "/dev/", NULL)) {
-        qemu_opt_set(opts, "backend", "tty", &error_abort);
-        qemu_opt_set(opts, "path", filename, &error_abort);
-        return opts;
-    }
-
-fail:
-    qemu_opts_del(opts);
-    return NULL;
-}
-
-void qemu_chr_parse_common(QemuOpts *opts, ChardevCommon *backend)
-{
-    const char *logfile = qemu_opt_get(opts, "logfile");
-
-    backend->has_logfile = logfile != NULL;
-    backend->logfile = logfile ? g_strdup(logfile) : NULL;
-
-    backend->has_logappend = true;
-    backend->logappend = qemu_opt_get_bool(opts, "logappend", false);
-}
-
-
-static void qemu_chr_parse_file_out(QemuOpts *opts, ChardevBackend *backend,
-                                    Error **errp)
-{
-    const char *path = qemu_opt_get(opts, "path");
-    ChardevFile *file;
-
-    if (path == NULL) {
-        error_setg(errp, "chardev: file: no filename given");
-        return;
-    }
-    file = backend->u.file.data = g_new0(ChardevFile, 1);
-    qemu_chr_parse_common(opts, qapi_ChardevFile_base(file));
-    file->out = g_strdup(path);
-
-    file->has_append = true;
-    file->append = qemu_opt_get_bool(opts, "append", false);
-}
-
-static void qemu_chr_parse_stdio(QemuOpts *opts, ChardevBackend *backend,
-                                 Error **errp)
-{
-    ChardevStdio *stdio;
-
-    stdio = backend->u.stdio.data = g_new0(ChardevStdio, 1);
-    qemu_chr_parse_common(opts, qapi_ChardevStdio_base(stdio));
-    stdio->has_signal = true;
-    stdio->signal = qemu_opt_get_bool(opts, "signal", true);
-}
-
-#ifdef HAVE_CHARDEV_SERIAL
-static void qemu_chr_parse_serial(QemuOpts *opts, ChardevBackend *backend,
-                                  Error **errp)
-{
-    const char *device = qemu_opt_get(opts, "path");
-    ChardevHostdev *serial;
-
-    if (device == NULL) {
-        error_setg(errp, "chardev: serial/tty: no device path given");
-        return;
-    }
-    serial = backend->u.serial.data = g_new0(ChardevHostdev, 1);
-    qemu_chr_parse_common(opts, qapi_ChardevHostdev_base(serial));
-    serial->device = g_strdup(device);
-}
-#endif
-
-#ifdef HAVE_CHARDEV_PARPORT
-static void qemu_chr_parse_parallel(QemuOpts *opts, ChardevBackend *backend,
-                                    Error **errp)
-{
-    const char *device = qemu_opt_get(opts, "path");
-    ChardevHostdev *parallel;
-
-    if (device == NULL) {
-        error_setg(errp, "chardev: parallel: no device path given");
-        return;
-    }
-    parallel = backend->u.parallel.data = g_new0(ChardevHostdev, 1);
-    qemu_chr_parse_common(opts, qapi_ChardevHostdev_base(parallel));
-    parallel->device = g_strdup(device);
-}
-#endif
-
-static void qemu_chr_parse_pipe(QemuOpts *opts, ChardevBackend *backend,
-                                Error **errp)
-{
-    const char *device = qemu_opt_get(opts, "path");
-    ChardevHostdev *dev;
-
-    if (device == NULL) {
-        error_setg(errp, "chardev: pipe: no device path given");
-        return;
-    }
-    dev = backend->u.pipe.data = g_new0(ChardevHostdev, 1);
-    qemu_chr_parse_common(opts, qapi_ChardevHostdev_base(dev));
-    dev->device = g_strdup(device);
-}
-
-static void qemu_chr_parse_ringbuf(QemuOpts *opts, ChardevBackend *backend,
-                                   Error **errp)
-{
-    int val;
-    ChardevRingbuf *ringbuf;
-
-    ringbuf = backend->u.ringbuf.data = g_new0(ChardevRingbuf, 1);
-    qemu_chr_parse_common(opts, qapi_ChardevRingbuf_base(ringbuf));
-
-    val = qemu_opt_get_size(opts, "size", 0);
-    if (val != 0) {
-        ringbuf->has_size = true;
-        ringbuf->size = val;
-    }
-}
-
-static void qemu_chr_parse_mux(QemuOpts *opts, ChardevBackend *backend,
-                               Error **errp)
-{
-    const char *chardev = qemu_opt_get(opts, "chardev");
-    ChardevMux *mux;
-
-    if (chardev == NULL) {
-        error_setg(errp, "chardev: mux: no chardev given");
-        return;
-    }
-    mux = backend->u.mux.data = g_new0(ChardevMux, 1);
-    qemu_chr_parse_common(opts, qapi_ChardevMux_base(mux));
-    mux->chardev = g_strdup(chardev);
-}
-
-static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
-                                  Error **errp)
-{
-    bool is_listen      = qemu_opt_get_bool(opts, "server", false);
-    bool is_waitconnect = is_listen && qemu_opt_get_bool(opts, "wait", true);
-    bool is_telnet      = qemu_opt_get_bool(opts, "telnet", false);
-    bool do_nodelay     = !qemu_opt_get_bool(opts, "delay", true);
-    int64_t reconnect   = qemu_opt_get_number(opts, "reconnect", 0);
-    const char *path = qemu_opt_get(opts, "path");
-    const char *host = qemu_opt_get(opts, "host");
-    const char *port = qemu_opt_get(opts, "port");
-    const char *tls_creds = qemu_opt_get(opts, "tls-creds");
-    SocketAddress *addr;
-    ChardevSocket *sock;
-
-    if (!path) {
-        if (!host) {
-            error_setg(errp, "chardev: socket: no host given");
-            return;
-        }
-        if (!port) {
-            error_setg(errp, "chardev: socket: no port given");
-            return;
-        }
-    } else {
-        if (tls_creds) {
-            error_setg(errp, "TLS can only be used over TCP socket");
-            return;
-        }
-    }
-
-    sock = backend->u.socket.data = g_new0(ChardevSocket, 1);
-    qemu_chr_parse_common(opts, qapi_ChardevSocket_base(sock));
-
-    sock->has_nodelay = true;
-    sock->nodelay = do_nodelay;
-    sock->has_server = true;
-    sock->server = is_listen;
-    sock->has_telnet = true;
-    sock->telnet = is_telnet;
-    sock->has_wait = true;
-    sock->wait = is_waitconnect;
-    sock->has_reconnect = true;
-    sock->reconnect = reconnect;
-    sock->tls_creds = g_strdup(tls_creds);
-
-    addr = g_new0(SocketAddress, 1);
-    if (path) {
-        UnixSocketAddress *q_unix;
-        addr->type = SOCKET_ADDRESS_KIND_UNIX;
-        q_unix = addr->u.q_unix.data = g_new0(UnixSocketAddress, 1);
-        q_unix->path = g_strdup(path);
-    } else {
-        addr->type = SOCKET_ADDRESS_KIND_INET;
-        addr->u.inet.data = g_new(InetSocketAddress, 1);
-        *addr->u.inet.data = (InetSocketAddress) {
-            .host = g_strdup(host),
-            .port = g_strdup(port),
-            .has_to = qemu_opt_get(opts, "to"),
-            .to = qemu_opt_get_number(opts, "to", 0),
-            .has_ipv4 = qemu_opt_get(opts, "ipv4"),
-            .ipv4 = qemu_opt_get_bool(opts, "ipv4", 0),
-            .has_ipv6 = qemu_opt_get(opts, "ipv6"),
-            .ipv6 = qemu_opt_get_bool(opts, "ipv6", 0),
-        };
-    }
-    sock->addr = addr;
-}
-
-static void qemu_chr_parse_udp(QemuOpts *opts, ChardevBackend *backend,
-                               Error **errp)
-{
-    const char *host = qemu_opt_get(opts, "host");
-    const char *port = qemu_opt_get(opts, "port");
-    const char *localaddr = qemu_opt_get(opts, "localaddr");
-    const char *localport = qemu_opt_get(opts, "localport");
-    bool has_local = false;
-    SocketAddress *addr;
-    ChardevUdp *udp;
-
-    if (host == NULL || strlen(host) == 0) {
-        host = "localhost";
-    }
-    if (port == NULL || strlen(port) == 0) {
-        error_setg(errp, "chardev: udp: remote port not specified");
-        return;
-    }
-    if (localport == NULL || strlen(localport) == 0) {
-        localport = "0";
-    } else {
-        has_local = true;
-    }
-    if (localaddr == NULL || strlen(localaddr) == 0) {
-        localaddr = "";
-    } else {
-        has_local = true;
-    }
-
-    udp = backend->u.udp.data = g_new0(ChardevUdp, 1);
-    qemu_chr_parse_common(opts, qapi_ChardevUdp_base(udp));
-
-    addr = g_new0(SocketAddress, 1);
-    addr->type = SOCKET_ADDRESS_KIND_INET;
-    addr->u.inet.data = g_new(InetSocketAddress, 1);
-    *addr->u.inet.data = (InetSocketAddress) {
-        .host = g_strdup(host),
-        .port = g_strdup(port),
-        .has_ipv4 = qemu_opt_get(opts, "ipv4"),
-        .ipv4 = qemu_opt_get_bool(opts, "ipv4", 0),
-        .has_ipv6 = qemu_opt_get(opts, "ipv6"),
-        .ipv6 = qemu_opt_get_bool(opts, "ipv6", 0),
-    };
-    udp->remote = addr;
-
-    if (has_local) {
-        udp->has_local = true;
-        addr = g_new0(SocketAddress, 1);
-        addr->type = SOCKET_ADDRESS_KIND_INET;
-        addr->u.inet.data = g_new(InetSocketAddress, 1);
-        *addr->u.inet.data = (InetSocketAddress) {
-            .host = g_strdup(localaddr),
-            .port = g_strdup(localport),
-        };
-        udp->local = addr;
-    }
-}
-
-typedef struct CharDriver {
-    const char *name;
-    ChardevBackendKind kind;
-    CharDriverParse *parse;
-    CharDriverCreate *create;
-} CharDriver;
-
-static GSList *backends;
-
-void register_char_driver(const char *name, ChardevBackendKind kind,
-                          CharDriverParse *parse, CharDriverCreate *create)
-{
-    CharDriver *s;
-
-    s = g_malloc0(sizeof(*s));
-    s->name = g_strdup(name);
-    s->kind = kind;
-    s->parse = parse;
-    s->create = create;
-
-    backends = g_slist_append(backends, s);
-}
-
-CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts,
-                                        Error **errp)
-{
-    Error *local_err = NULL;
-    CharDriver *cd;
-    CharDriverState *chr;
-    GSList *i;
-    ChardevReturn *ret = NULL;
-    ChardevBackend *backend;
-    const char *id = qemu_opts_id(opts);
-    char *bid = NULL;
-
-    if (qemu_opt_get(opts, "backend") == NULL) {
-        error_setg(errp, "chardev: \"%s\" missing backend",
-                   qemu_opts_id(opts));
-        goto err;
-    }
-
-    if (is_help_option(qemu_opt_get(opts, "backend"))) {
-        fprintf(stderr, "Available chardev backend types:\n");
-        for (i = backends; i; i = i->next) {
-            cd = i->data;
-            fprintf(stderr, "%s\n", cd->name);
-        }
-        exit(!is_help_option(qemu_opt_get(opts, "backend")));
-    }
-
-    if (id == NULL) {
-        error_setg(errp, "chardev: no id specified");
-        goto err;
-    }
-
-    for (i = backends; i; i = i->next) {
-        cd = i->data;
-
-        if (strcmp(cd->name, qemu_opt_get(opts, "backend")) == 0) {
-            break;
-        }
-    }
-    if (i == NULL) {
-        error_setg(errp, "chardev: backend \"%s\" not found",
-                   qemu_opt_get(opts, "backend"));
-        goto err;
-    }
-
-    backend = g_new0(ChardevBackend, 1);
-
-    if (qemu_opt_get_bool(opts, "mux", 0)) {
-        bid = g_strdup_printf("%s-base", id);
-    }
-
-    chr = NULL;
-    backend->type = cd->kind;
-    if (cd->parse) {
-        cd->parse(opts, backend, &local_err);
-        if (local_err) {
-            error_propagate(errp, local_err);
-            goto qapi_out;
-        }
-    } else {
-        ChardevCommon *cc = g_new0(ChardevCommon, 1);
-        qemu_chr_parse_common(opts, cc);
-        backend->u.null.data = cc; /* Any ChardevCommon member would work */
-    }
-
-    ret = qmp_chardev_add(bid ? bid : id, backend, errp);
-    if (!ret) {
-        goto qapi_out;
-    }
-
-    if (bid) {
-        qapi_free_ChardevBackend(backend);
-        qapi_free_ChardevReturn(ret);
-        backend = g_new0(ChardevBackend, 1);
-        backend->u.mux.data = g_new0(ChardevMux, 1);
-        backend->type = CHARDEV_BACKEND_KIND_MUX;
-        backend->u.mux.data->chardev = g_strdup(bid);
-        ret = qmp_chardev_add(id, backend, errp);
-        if (!ret) {
-            chr = qemu_chr_find(bid);
-            qemu_chr_delete(chr);
-            chr = NULL;
-            goto qapi_out;
-        }
-    }
-
-    chr = qemu_chr_find(id);
-
-qapi_out:
-    qapi_free_ChardevBackend(backend);
-    qapi_free_ChardevReturn(ret);
-    g_free(bid);
-    return chr;
-
-err:
-    return NULL;
-}
-
-CharDriverState *qemu_chr_new_noreplay(const char *label, const char *filename)
-{
-    const char *p;
-    CharDriverState *chr;
-    QemuOpts *opts;
-    Error *err = NULL;
-
-    if (strstart(filename, "chardev:", &p)) {
-        return qemu_chr_find(p);
-    }
-
-    opts = qemu_chr_parse_compat(label, filename);
-    if (!opts)
-        return NULL;
-
-    chr = qemu_chr_new_from_opts(opts, &err);
-    if (err) {
-        error_report_err(err);
-    }
-    if (chr && qemu_opt_get_bool(opts, "mux", 0)) {
-        monitor_init(chr, MONITOR_USE_READLINE);
-    }
-    qemu_opts_del(opts);
-    return chr;
-}
-
-CharDriverState *qemu_chr_new(const char *label, const char *filename)
-{
-    CharDriverState *chr;
-    chr = qemu_chr_new_noreplay(label, filename);
-    if (chr) {
-        chr->replay = replay_mode != REPLAY_MODE_NONE;
-        if (chr->replay && chr->chr_ioctl) {
-            fprintf(stderr,
-                    "Replay: ioctl is not supported for serial devices yet\n");
-        }
-        replay_register_char_driver(chr);
-    }
-    return chr;
-}
-
-void qemu_chr_fe_set_echo(CharBackend *be, bool echo)
-{
-    CharDriverState *chr = be->chr;
-
-    if (chr && chr->chr_set_echo) {
-        chr->chr_set_echo(chr, echo);
-    }
-}
-
-void qemu_chr_fe_set_open(CharBackend *be, int fe_open)
-{
-    CharDriverState *chr = be->chr;
-
-    if (!chr) {
-        return;
-    }
-
-    if (be->fe_open == fe_open) {
-        return;
-    }
-    be->fe_open = fe_open;
-    if (chr->chr_set_fe_open) {
-        chr->chr_set_fe_open(chr, fe_open);
-    }
-}
-
-guint qemu_chr_fe_add_watch(CharBackend *be, GIOCondition cond,
-                            GIOFunc func, void *user_data)
-{
-    CharDriverState *s = be->chr;
-    GSource *src;
-    guint tag;
-
-    if (!s || s->chr_add_watch == NULL) {
-        return 0;
-    }
-
-    src = s->chr_add_watch(s, cond);
-    if (!src) {
-        return 0;
-    }
-
-    g_source_set_callback(src, (GSourceFunc)func, user_data, NULL);
-    tag = g_source_attach(src, NULL);
-    g_source_unref(src);
-
-    return tag;
-}
-
-void qemu_chr_fe_disconnect(CharBackend *be)
-{
-    CharDriverState *chr = be->chr;
-
-    if (chr && chr->chr_disconnect) {
-        chr->chr_disconnect(chr);
-    }
-}
-
-static void qemu_chr_free_common(CharDriverState *chr)
-{
-    if (chr->be) {
-        chr->be->chr = NULL;
-    }
-    g_free(chr->filename);
-    g_free(chr->label);
-    if (chr->logfd != -1) {
-        close(chr->logfd);
-    }
-    qemu_mutex_destroy(&chr->chr_write_lock);
-    g_free(chr);
-}
-
-void qemu_chr_free(CharDriverState *chr)
-{
-    if (chr->chr_free) {
-        chr->chr_free(chr);
-    }
-    qemu_chr_free_common(chr);
-}
-
-void qemu_chr_delete(CharDriverState *chr)
-{
-    QTAILQ_REMOVE(&chardevs, chr, next);
-    qemu_chr_free(chr);
-}
-
-ChardevInfoList *qmp_query_chardev(Error **errp)
-{
-    ChardevInfoList *chr_list = NULL;
-    CharDriverState *chr;
-
-    QTAILQ_FOREACH(chr, &chardevs, next) {
-        ChardevInfoList *info = g_malloc0(sizeof(*info));
-        info->value = g_malloc0(sizeof(*info->value));
-        info->value->label = g_strdup(chr->label);
-        info->value->filename = g_strdup(chr->filename);
-        info->value->frontend_open = chr->be && chr->be->fe_open;
-
-        info->next = chr_list;
-        chr_list = info;
-    }
-
-    return chr_list;
-}
-
-ChardevBackendInfoList *qmp_query_chardev_backends(Error **errp)
-{
-    ChardevBackendInfoList *backend_list = NULL;
-    CharDriver *c = NULL;
-    GSList *i = NULL;
-
-    for (i = backends; i; i = i->next) {
-        ChardevBackendInfoList *info = g_malloc0(sizeof(*info));
-        c = i->data;
-        info->value = g_malloc0(sizeof(*info->value));
-        info->value->name = g_strdup(c->name);
-
-        info->next = backend_list;
-        backend_list = info;
-    }
-
-    return backend_list;
-}
-
-CharDriverState *qemu_chr_find(const char *name)
-{
-    CharDriverState *chr;
-
-    QTAILQ_FOREACH(chr, &chardevs, next) {
-        if (strcmp(chr->label, name) != 0)
-            continue;
-        return chr;
-    }
-    return NULL;
-}
-
-QemuOptsList qemu_chardev_opts = {
-    .name = "chardev",
-    .implied_opt_name = "backend",
-    .head = QTAILQ_HEAD_INITIALIZER(qemu_chardev_opts.head),
-    .desc = {
-        {
-            .name = "backend",
-            .type = QEMU_OPT_STRING,
-        },{
-            .name = "path",
-            .type = QEMU_OPT_STRING,
-        },{
-            .name = "host",
-            .type = QEMU_OPT_STRING,
-        },{
-            .name = "port",
-            .type = QEMU_OPT_STRING,
-        },{
-            .name = "localaddr",
-            .type = QEMU_OPT_STRING,
-        },{
-            .name = "localport",
-            .type = QEMU_OPT_STRING,
-        },{
-            .name = "to",
-            .type = QEMU_OPT_NUMBER,
-        },{
-            .name = "ipv4",
-            .type = QEMU_OPT_BOOL,
-        },{
-            .name = "ipv6",
-            .type = QEMU_OPT_BOOL,
-        },{
-            .name = "wait",
-            .type = QEMU_OPT_BOOL,
-        },{
-            .name = "server",
-            .type = QEMU_OPT_BOOL,
-        },{
-            .name = "delay",
-            .type = QEMU_OPT_BOOL,
-        },{
-            .name = "reconnect",
-            .type = QEMU_OPT_NUMBER,
-        },{
-            .name = "telnet",
-            .type = QEMU_OPT_BOOL,
-        },{
-            .name = "tls-creds",
-            .type = QEMU_OPT_STRING,
-        },{
-            .name = "width",
-            .type = QEMU_OPT_NUMBER,
-        },{
-            .name = "height",
-            .type = QEMU_OPT_NUMBER,
-        },{
-            .name = "cols",
-            .type = QEMU_OPT_NUMBER,
-        },{
-            .name = "rows",
-            .type = QEMU_OPT_NUMBER,
-        },{
-            .name = "mux",
-            .type = QEMU_OPT_BOOL,
-        },{
-            .name = "signal",
-            .type = QEMU_OPT_BOOL,
-        },{
-            .name = "name",
-            .type = QEMU_OPT_STRING,
-        },{
-            .name = "debug",
-            .type = QEMU_OPT_NUMBER,
-        },{
-            .name = "size",
-            .type = QEMU_OPT_SIZE,
-        },{
-            .name = "chardev",
-            .type = QEMU_OPT_STRING,
-        },{
-            .name = "append",
-            .type = QEMU_OPT_BOOL,
-        },{
-            .name = "logfile",
-            .type = QEMU_OPT_STRING,
-        },{
-            .name = "logappend",
-            .type = QEMU_OPT_BOOL,
-        },
-        { /* end of list */ }
-    },
-};
-
-#ifdef _WIN32
-
-static CharDriverState *qmp_chardev_open_file(const char *id,
-                                              ChardevBackend *backend,
-                                              ChardevReturn *ret,
-                                              bool *be_opened,
-                                              Error **errp)
-{
-    ChardevFile *file = backend->u.file.data;
-    ChardevCommon *common = qapi_ChardevFile_base(file);
-    HANDLE out;
-    DWORD accessmode;
-    DWORD flags;
-
-    if (file->has_in) {
-        error_setg(errp, "input file not supported");
-        return NULL;
-    }
-
-    if (file->has_append && file->append) {
-        /* Append to file if it already exists. */
-        accessmode = FILE_GENERIC_WRITE & ~FILE_WRITE_DATA;
-        flags = OPEN_ALWAYS;
-    } else {
-        /* Truncate file if it already exists. */
-        accessmode = GENERIC_WRITE;
-        flags = CREATE_ALWAYS;
-    }
-
-    out = CreateFile(file->out, accessmode, FILE_SHARE_READ, NULL, flags,
-                     FILE_ATTRIBUTE_NORMAL, NULL);
-    if (out == INVALID_HANDLE_VALUE) {
-        error_setg(errp, "open %s failed", file->out);
-        return NULL;
-    }
-    return qemu_chr_open_win_file(out, common, errp);
-}
-
-static CharDriverState *qmp_chardev_open_serial(const char *id,
-                                                ChardevBackend *backend,
-                                                ChardevReturn *ret,
-                                                bool *be_opened,
-                                                Error **errp)
-{
-    ChardevHostdev *serial = backend->u.serial.data;
-    ChardevCommon *common = qapi_ChardevHostdev_base(serial);
-    return qemu_chr_open_win_path(serial->device, common, errp);
-}
-
-#else /* WIN32 */
-
-static int qmp_chardev_open_file_source(char *src, int flags,
-                                        Error **errp)
-{
-    int fd = -1;
-
-    TFR(fd = qemu_open(src, flags, 0666));
-    if (fd == -1) {
-        error_setg_file_open(errp, errno, src);
-    }
-    return fd;
-}
-
-static CharDriverState *qmp_chardev_open_file(const char *id,
-                                              ChardevBackend *backend,
-                                              ChardevReturn *ret,
-                                              bool *be_opened,
-                                              Error **errp)
-{
-    ChardevFile *file = backend->u.file.data;
-    ChardevCommon *common = qapi_ChardevFile_base(file);
-    int flags, in = -1, out;
-
-    flags = O_WRONLY | O_CREAT | O_BINARY;
-    if (file->has_append && file->append) {
-        flags |= O_APPEND;
-    } else {
-        flags |= O_TRUNC;
-    }
-
-    out = qmp_chardev_open_file_source(file->out, flags, errp);
-    if (out < 0) {
-        return NULL;
-    }
-
-    if (file->has_in) {
-        flags = O_RDONLY;
-        in = qmp_chardev_open_file_source(file->in, flags, errp);
-        if (in < 0) {
-            qemu_close(out);
-            return NULL;
-        }
-    }
-
-    return qemu_chr_open_fd(in, out, common, errp);
-}
-
-#ifdef HAVE_CHARDEV_SERIAL
-static CharDriverState *qmp_chardev_open_serial(const char *id,
-                                                ChardevBackend *backend,
-                                                ChardevReturn *ret,
-                                                bool *be_opened,
-                                                Error **errp)
-{
-    ChardevHostdev *serial = backend->u.serial.data;
-    ChardevCommon *common = qapi_ChardevHostdev_base(serial);
-    int fd;
-
-    fd = qmp_chardev_open_file_source(serial->device, O_RDWR, errp);
-    if (fd < 0) {
-        return NULL;
-    }
-    qemu_set_nonblock(fd);
-    return qemu_chr_open_tty_fd(fd, common, be_opened, errp);
-}
-#endif
-
-#ifdef HAVE_CHARDEV_PARPORT
-static CharDriverState *qmp_chardev_open_parallel(const char *id,
-                                                  ChardevBackend *backend,
-                                                  ChardevReturn *ret,
-                                                  bool *be_opened,
-                                                  Error **errp)
-{
-    ChardevHostdev *parallel = backend->u.parallel.data;
-    ChardevCommon *common = qapi_ChardevHostdev_base(parallel);
-    int fd;
-
-    fd = qmp_chardev_open_file_source(parallel->device, O_RDWR, errp);
-    if (fd < 0) {
-        return NULL;
-    }
-    return qemu_chr_open_pp_fd(fd, common, be_opened, errp);
-}
-#endif
-
-#endif /* WIN32 */
-
-static gboolean socket_reconnect_timeout(gpointer opaque)
-{
-    CharDriverState *chr = opaque;
-    TCPCharDriver *s = chr->opaque;
-    QIOChannelSocket *sioc;
-
-    s->reconnect_timer = 0;
-
-    if (chr->be_open) {
-        return false;
-    }
-
-    sioc = qio_channel_socket_new();
-    tcp_chr_set_client_ioc_name(chr, sioc);
-    qio_channel_socket_connect_async(sioc, s->addr,
-                                     qemu_chr_socket_connected,
-                                     chr, NULL);
-
-    return false;
-}
-
-static CharDriverState *qmp_chardev_open_socket(const char *id,
-                                                ChardevBackend *backend,
-                                                ChardevReturn *ret,
-                                                bool *be_opened,
-                                                Error **errp)
-{
-    CharDriverState *chr;
-    TCPCharDriver *s;
-    ChardevSocket *sock = backend->u.socket.data;
-    SocketAddress *addr = sock->addr;
-    bool do_nodelay     = sock->has_nodelay ? sock->nodelay : false;
-    bool is_listen      = sock->has_server  ? sock->server  : true;
-    bool is_telnet      = sock->has_telnet  ? sock->telnet  : false;
-    bool is_waitconnect = sock->has_wait    ? sock->wait    : false;
-    int64_t reconnect   = sock->has_reconnect ? sock->reconnect : 0;
-    ChardevCommon *common = qapi_ChardevSocket_base(sock);
-    QIOChannelSocket *sioc = NULL;
-
-    chr = qemu_chr_alloc(common, errp);
-    if (!chr) {
-        return NULL;
-    }
-    s = g_new0(TCPCharDriver, 1);
-
-    s->is_unix = addr->type == SOCKET_ADDRESS_KIND_UNIX;
-    s->is_listen = is_listen;
-    s->is_telnet = is_telnet;
-    s->do_nodelay = do_nodelay;
-    if (sock->tls_creds) {
-        Object *creds;
-        creds = object_resolve_path_component(
-            object_get_objects_root(), sock->tls_creds);
-        if (!creds) {
-            error_setg(errp, "No TLS credentials with id '%s'",
-                       sock->tls_creds);
-            goto error;
-        }
-        s->tls_creds = (QCryptoTLSCreds *)
-            object_dynamic_cast(creds,
-                                TYPE_QCRYPTO_TLS_CREDS);
-        if (!s->tls_creds) {
-            error_setg(errp, "Object with id '%s' is not TLS credentials",
-                       sock->tls_creds);
-            goto error;
-        }
-        object_ref(OBJECT(s->tls_creds));
-        if (is_listen) {
-            if (s->tls_creds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
-                error_setg(errp, "%s",
-                           "Expected TLS credentials for server endpoint");
-                goto error;
-            }
-        } else {
-            if (s->tls_creds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT) {
-                error_setg(errp, "%s",
-                           "Expected TLS credentials for client endpoint");
-                goto error;
-            }
-        }
-    }
-
-    s->addr = QAPI_CLONE(SocketAddress, sock->addr);
-
-    qemu_chr_set_feature(chr, QEMU_CHAR_FEATURE_RECONNECTABLE);
-    if (s->is_unix) {
-        qemu_chr_set_feature(chr, QEMU_CHAR_FEATURE_FD_PASS);
-    }
-
-    chr->opaque = s;
-    chr->chr_wait_connected = tcp_chr_wait_connected;
-    chr->chr_write = tcp_chr_write;
-    chr->chr_sync_read = tcp_chr_sync_read;
-    chr->chr_free = tcp_chr_free;
-    chr->chr_disconnect = tcp_chr_disconnect;
-    chr->get_msgfds = tcp_get_msgfds;
-    chr->set_msgfds = tcp_set_msgfds;
-    chr->chr_add_client = tcp_chr_add_client;
-    chr->chr_add_watch = tcp_chr_add_watch;
-    chr->chr_update_read_handler = tcp_chr_update_read_handler;
-    /* be isn't opened until we get a connection */
-    *be_opened = false;
-
-    chr->filename = SocketAddress_to_str("disconnected:",
-                                         addr, is_listen, is_telnet);
-
-    if (is_listen) {
-        if (is_telnet) {
-            s->do_telnetopt = 1;
-        }
-    } else if (reconnect > 0) {
-        s->reconnect_time = reconnect;
-    }
-
-    if (s->reconnect_time) {
-        sioc = qio_channel_socket_new();
-        tcp_chr_set_client_ioc_name(chr, sioc);
-        qio_channel_socket_connect_async(sioc, s->addr,
-                                         qemu_chr_socket_connected,
-                                         chr, NULL);
-    } else {
-        if (s->is_listen) {
-            char *name;
-            sioc = qio_channel_socket_new();
-
-            name = g_strdup_printf("chardev-tcp-listener-%s", chr->label);
-            qio_channel_set_name(QIO_CHANNEL(sioc), name);
-            g_free(name);
-
-            if (qio_channel_socket_listen_sync(sioc, s->addr, errp) < 0) {
-                goto error;
-            }
-            s->listen_ioc = sioc;
-            if (is_waitconnect &&
-                qemu_chr_wait_connected(chr, errp) < 0) {
-                goto error;
-            }
-            if (!s->ioc) {
-                s->listen_tag = qio_channel_add_watch(
-                    QIO_CHANNEL(s->listen_ioc), G_IO_IN,
-                    tcp_chr_accept, chr, NULL);
-            }
-        } else if (qemu_chr_wait_connected(chr, errp) < 0) {
-            goto error;
-        }
-    }
-
-    return chr;
-
- error:
-    if (sioc) {
-        object_unref(OBJECT(sioc));
-    }
-    if (s->tls_creds) {
-        object_unref(OBJECT(s->tls_creds));
-    }
-    g_free(s);
-    qemu_chr_free_common(chr);
-    return NULL;
-}
-
-static CharDriverState *qmp_chardev_open_udp(const char *id,
-                                             ChardevBackend *backend,
-                                             ChardevReturn *ret,
-                                             bool *be_opened,
-                                             Error **errp)
-{
-    ChardevUdp *udp = backend->u.udp.data;
-    ChardevCommon *common = qapi_ChardevUdp_base(udp);
-    QIOChannelSocket *sioc = qio_channel_socket_new();
-    char *name;
-    CharDriverState *chr;
-
-    if (qio_channel_socket_dgram_sync(sioc,
-                                      udp->local, udp->remote,
-                                      errp) < 0) {
-        object_unref(OBJECT(sioc));
-        return NULL;
-    }
-    chr = qemu_chr_open_udp(sioc, common, be_opened, errp);
-
-    name = g_strdup_printf("chardev-udp-%s", chr->label);
-    qio_channel_set_name(QIO_CHANNEL(sioc), name);
-    g_free(name);
-
-    return chr;
-}
-
-
-bool qemu_chr_has_feature(CharDriverState *chr,
-                          CharDriverFeature feature)
-{
-    return test_bit(feature, chr->features);
-}
-
-void qemu_chr_set_feature(CharDriverState *chr,
-                           CharDriverFeature feature)
-{
-    return set_bit(feature, chr->features);
-}
-
-ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend,
-                               Error **errp)
-{
-    ChardevReturn *ret = g_new0(ChardevReturn, 1);
-    CharDriverState *chr = NULL;
-    Error *local_err = NULL;
-    GSList *i;
-    CharDriver *cd;
-    bool be_opened = true;
-
-    chr = qemu_chr_find(id);
-    if (chr) {
-        error_setg(errp, "Chardev '%s' already exists", id);
-        goto out_error;
-    }
-
-    for (i = backends; i; i = i->next) {
-        cd = i->data;
-
-        if (cd->kind == backend->type) {
-            chr = cd->create(id, backend, ret, &be_opened, &local_err);
-            if (local_err) {
-                error_propagate(errp, local_err);
-                goto out_error;
-            }
-            break;
-        }
-    }
-
-    if (chr == NULL) {
-        assert(!i);
-        error_setg(errp, "chardev backend not available");
-        goto out_error;
-    }
-
-    chr->label = g_strdup(id);
-    if (!chr->filename) {
-        chr->filename = g_strdup(ChardevBackendKind_lookup[backend->type]);
-    }
-    if (be_opened) {
-        qemu_chr_be_event(chr, CHR_EVENT_OPENED);
-    }
-    QTAILQ_INSERT_TAIL(&chardevs, chr, next);
-    return ret;
-
-out_error:
-    g_free(ret);
-    return NULL;
-}
-
-void qmp_chardev_remove(const char *id, Error **errp)
-{
-    CharDriverState *chr;
-
-    chr = qemu_chr_find(id);
-    if (chr == NULL) {
-        error_setg(errp, "Chardev '%s' not found", id);
-        return;
-    }
-    if (qemu_chr_is_busy(chr)) {
-        error_setg(errp, "Chardev '%s' is busy", id);
-        return;
-    }
-    if (chr->replay) {
-        error_setg(errp,
-            "Chardev '%s' cannot be unplugged in record/replay mode", id);
-        return;
-    }
-    qemu_chr_delete(chr);
-}
-
-void qemu_chr_cleanup(void)
-{
-    CharDriverState *chr, *tmp;
-
-    QTAILQ_FOREACH_SAFE(chr, &chardevs, next, tmp) {
-        qemu_chr_delete(chr);
-    }
-}
-
-static void register_types(void)
-{
-    register_char_driver("null", CHARDEV_BACKEND_KIND_NULL, NULL,
-                         qemu_chr_open_null);
-    register_char_driver("socket", CHARDEV_BACKEND_KIND_SOCKET,
-                         qemu_chr_parse_socket, qmp_chardev_open_socket);
-    register_char_driver("udp", CHARDEV_BACKEND_KIND_UDP, qemu_chr_parse_udp,
-                         qmp_chardev_open_udp);
-    register_char_driver("ringbuf", CHARDEV_BACKEND_KIND_RINGBUF,
-                         qemu_chr_parse_ringbuf, qemu_chr_open_ringbuf);
-    register_char_driver("file", CHARDEV_BACKEND_KIND_FILE,
-                         qemu_chr_parse_file_out, qmp_chardev_open_file);
-    register_char_driver("stdio", CHARDEV_BACKEND_KIND_STDIO,
-                         qemu_chr_parse_stdio, qemu_chr_open_stdio);
-#if defined HAVE_CHARDEV_SERIAL
-    register_char_driver("serial", CHARDEV_BACKEND_KIND_SERIAL,
-                         qemu_chr_parse_serial, qmp_chardev_open_serial);
-    register_char_driver("tty", CHARDEV_BACKEND_KIND_SERIAL,
-                         qemu_chr_parse_serial, qmp_chardev_open_serial);
-#endif
-#ifdef HAVE_CHARDEV_PARPORT
-    register_char_driver("parallel", CHARDEV_BACKEND_KIND_PARALLEL,
-                         qemu_chr_parse_parallel, qmp_chardev_open_parallel);
-    register_char_driver("parport", CHARDEV_BACKEND_KIND_PARALLEL,
-                         qemu_chr_parse_parallel, qmp_chardev_open_parallel);
-#endif
-#ifdef HAVE_CHARDEV_PTY
-    register_char_driver("pty", CHARDEV_BACKEND_KIND_PTY, NULL,
-                         qemu_chr_open_pty);
-#endif
-#ifdef _WIN32
-    register_char_driver("console", CHARDEV_BACKEND_KIND_CONSOLE, NULL,
-                         qemu_chr_open_win_con);
-#endif
-    register_char_driver("pipe", CHARDEV_BACKEND_KIND_PIPE,
-                         qemu_chr_parse_pipe, qemu_chr_open_pipe);
-    register_char_driver("mux", CHARDEV_BACKEND_KIND_MUX, qemu_chr_parse_mux,
-                         qemu_chr_open_mux);
-    /* Bug-compatibility: */
-    register_char_driver("memory", CHARDEV_BACKEND_KIND_MEMORY,
-                         qemu_chr_parse_ringbuf, qemu_chr_open_ringbuf);
-    /* this must be done after machine init, since we register FEs with muxes
-     * as part of realize functions like serial_isa_realizefn when -nographic
-     * is specified
-     */
-    qemu_add_machine_init_done_notifier(&muxes_realize_notify);
-}
-
-type_init(register_types);
diff --git a/qemu-options.hx b/qemu-options.hx
index 588e5beab3..ad2f8fc873 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -2430,8 +2430,6 @@ Connect to standard input and standard output of the QEMU process.
 exiting QEMU with the key sequence @key{Control-c}. This option is enabled by
 default, use @option{signal=off} to disable it.
 
-@option{stdio} is not available on Windows hosts.
-
 @item -chardev braille ,id=@var{id}
 
 Connect to a local BrlAPI server. @option{braille} does not take any options.
@@ -3017,7 +3015,7 @@ udp::4555@@:4556} to QEMU. Another approach is to use a patched
 version of netcat which can listen to a TCP port and send and receive
 characters via udp.  If you have a patched version of netcat which
 activates telnet remote echo and single char transfer, then you can
-use the following options to step up a netcat redirector to allow
+use the following options to set up a netcat redirector to allow
 telnet on port 5555 to access the QEMU port.
 @table @code
 @item QEMU Options:
@@ -3400,12 +3398,12 @@ re-inject them.
 ETEXI
 
 DEF("icount", HAS_ARG, QEMU_OPTION_icount, \
-    "-icount [shift=N|auto][,align=on|off][,sleep=on|off,rr=record|replay,rrfile=<filename>]\n" \
+    "-icount [shift=N|auto][,align=on|off][,sleep=on|off,rr=record|replay,rrfile=<filename>,rrsnapshot=<snapshot>]\n" \
     "                enable virtual instruction counter with 2^N clock ticks per\n" \
     "                instruction, enable aligning the host and virtual clocks\n" \
     "                or disable real time cpu sleeping\n", QEMU_ARCH_ALL)
 STEXI
-@item -icount [shift=@var{N}|auto][,rr=record|replay,rrfile=@var{filename}]
+@item -icount [shift=@var{N}|auto][,rr=record|replay,rrfile=@var{filename},rrsnapshot=@var{snapshot}]
 @findex -icount
 Enable virtual instruction counter.  The virtual cpu will execute one
 instruction every 2^@var{N} ns of virtual time.  If @code{auto} is specified
@@ -3438,6 +3436,10 @@ when the shift value is high (how high depends on the host machine).
 When @option{rr} option is specified deterministic record/replay is enabled.
 Replay log is written into @var{filename} file in record mode and
 read from this file in replay mode.
+
+Option rrsnapshot is used to create new vm snapshot named @var{snapshot}
+at the start of execution recording. In replay mode this option is used
+to load the initial VM state.
 ETEXI
 
 DEF("watchdog", HAS_ARG, QEMU_OPTION_watchdog, \
diff --git a/qmp.c b/qmp.c
index 886059ef94..dfaabac1a6 100644
--- a/qmp.c
+++ b/qmp.c
@@ -18,6 +18,7 @@
 #include "qemu/cutils.h"
 #include "monitor/monitor.h"
 #include "sysemu/sysemu.h"
+#include "qemu/config-file.h"
 #include "qemu/uuid.h"
 #include "qmp-commands.h"
 #include "sysemu/char.h"
@@ -616,7 +617,7 @@ void qmp_add_client(const char *protocol, const char *fdname,
                     bool has_skipauth, bool skipauth, bool has_tls, bool tls,
                     Error **errp)
 {
-    CharDriverState *s;
+    Chardev *s;
     int fd;
 
     fd = monitor_get_fd(cur_mon, fdname, errp);
diff --git a/qom/cpu.c b/qom/cpu.c
index 7f575879f6..d57faf3ddc 100644
--- a/qom/cpu.c
+++ b/qom/cpu.c
@@ -29,7 +29,7 @@
 #include "qemu/error-report.h"
 #include "sysemu/sysemu.h"
 #include "hw/qdev-properties.h"
-#include "trace.h"
+#include "trace-root.h"
 
 bool cpu_exists(int64_t id)
 {
@@ -415,6 +415,7 @@ static void cpu_class_init(ObjectClass *klass, void *data)
     k->cpu_exec_enter = cpu_common_noop;
     k->cpu_exec_exit = cpu_common_noop;
     k->cpu_exec_interrupt = cpu_common_exec_interrupt;
+    set_bit(DEVICE_CATEGORY_CPU, dc->categories);
     dc->realize = cpu_common_realizefn;
     dc->unrealize = cpu_common_unrealizefn;
     /*
diff --git a/qtest.c b/qtest.c
index bd9d417812..1446719e8d 100644
--- a/qtest.c
+++ b/qtest.c
@@ -670,7 +670,7 @@ static int qtest_init_accel(MachineState *ms)
 
 void qtest_init(const char *qtest_chrdev, const char *qtest_log, Error **errp)
 {
-    CharDriverState *chr;
+    Chardev *chr;
 
     chr = qemu_chr_new("qtest", qtest_chrdev);
 
diff --git a/replay/replay-char.c b/replay/replay-char.c
index edf46ab9df..aa65955942 100755
--- a/replay/replay-char.c
+++ b/replay/replay-char.c
@@ -18,7 +18,7 @@
 
 /* Char drivers that generate qemu_chr_be_write events
    that should be saved into the log. */
-static CharDriverState **char_drivers;
+static Chardev **char_drivers;
 static int drivers_count;
 
 /* Char event attributes. */
@@ -28,7 +28,7 @@ typedef struct CharEvent {
     size_t len;
 } CharEvent;
 
-static int find_char_driver(CharDriverState *chr)
+static int find_char_driver(Chardev *chr)
 {
     int i = 0;
     for ( ; i < drivers_count ; ++i) {
@@ -39,7 +39,7 @@ static int find_char_driver(CharDriverState *chr)
     return -1;
 }
 
-void replay_register_char_driver(CharDriverState *chr)
+void replay_register_char_driver(Chardev *chr)
 {
     if (replay_mode == REPLAY_MODE_NONE) {
         return;
@@ -49,7 +49,7 @@ void replay_register_char_driver(CharDriverState *chr)
     char_drivers[drivers_count++] = chr;
 }
 
-void replay_chr_be_write(CharDriverState *s, uint8_t *buf, int len)
+void replay_chr_be_write(Chardev *s, uint8_t *buf, int len)
 {
     CharEvent *event = g_malloc0(sizeof(CharEvent));
 
diff --git a/replay/replay-snapshot.c b/replay/replay-snapshot.c
index 498059734d..65e2d375c2 100644
--- a/replay/replay-snapshot.c
+++ b/replay/replay-snapshot.c
@@ -59,3 +59,20 @@ void replay_vmstate_register(void)
 {
     vmstate_register(NULL, 0, &vmstate_replay, &replay_state);
 }
+
+void replay_vmstate_init(void)
+{
+    if (replay_snapshot) {
+        if (replay_mode == REPLAY_MODE_RECORD) {
+            if (save_vmstate(cur_mon, replay_snapshot) != 0) {
+                error_report("Could not create snapshot for icount record");
+                exit(1);
+            }
+        } else if (replay_mode == REPLAY_MODE_PLAY) {
+            if (load_vmstate(replay_snapshot) != 0) {
+                error_report("Could not load snapshot for icount replay");
+                exit(1);
+            }
+        }
+    }
+}
diff --git a/replay/replay.c b/replay/replay.c
index 7f27cf17b0..1835b9902e 100644
--- a/replay/replay.c
+++ b/replay/replay.c
@@ -26,6 +26,7 @@
 #define HEADER_SIZE                 (sizeof(uint32_t) + sizeof(uint64_t))
 
 ReplayMode replay_mode = REPLAY_MODE_NONE;
+char *replay_snapshot;
 
 /* Name of replay file  */
 static char *replay_filename;
@@ -292,6 +293,7 @@ void replay_configure(QemuOpts *opts)
         exit(1);
     }
 
+    replay_snapshot = g_strdup(qemu_opt_get(opts, "rrsnapshot"));
     replay_vmstate_register();
     replay_enable(fname, mode);
 
@@ -346,6 +348,9 @@ void replay_finish(void)
         replay_filename = NULL;
     }
 
+    g_free(replay_snapshot);
+    replay_snapshot = NULL;
+
     replay_finish_events();
     replay_mutex_destroy();
 }
diff --git a/rules.mak b/rules.mak
index d5c516caff..575a3afdff 100644
--- a/rules.mak
+++ b/rules.mak
@@ -26,8 +26,13 @@ QEMU_CXXFLAGS = -D__STDC_LIMIT_MACROS $(filter-out -Wstrict-prototypes -Wmissing
 # Flags for dependency generation
 QEMU_DGFLAGS += -MMD -MP -MT $@ -MF $(@D)/$(*F).d
 
-# Same as -I$(SRC_PATH) -I., but for the nested source/object directories
-QEMU_INCLUDES += -I$(<D) -I$(@D)
+# Compiler searches the source file dir first, but in vpath builds
+# we need to make it search the build dir too, before any other
+# explicit search paths. There are two search locations in the build
+# dir, one absolute and the other relative to the compiler working
+# directory. These are the same for target-independent files, but
+# different for target-dependent ones.
+QEMU_LOCAL_INCLUDES = -I$(BUILD_DIR)/$(@D) -I$(@D)
 
 WL_U := -Wl,-u,
 find-symbols = $(if $1, $(sort $(shell $(NM) -P -g $1 | $2)))
@@ -61,7 +66,9 @@ expand-objs = $(strip $(sort $(filter %.o,$1)) \
                   $(filter-out %.o %.mo,$1))
 
 %.o: %.c
-	$(call quiet-command,$(CC) $(QEMU_INCLUDES) $(QEMU_CFLAGS) $(QEMU_DGFLAGS) $(CFLAGS) $($@-cflags) -c -o $@ $<,"CC","$(TARGET_DIR)$@")
+	$(call quiet-command,$(CC) $(QEMU_LOCAL_INCLUDES) $(QEMU_INCLUDES) \
+	       $(QEMU_CFLAGS) $(QEMU_DGFLAGS) $(CFLAGS) $($@-cflags) \
+	       -c -o $@ $<,"CC","$(TARGET_DIR)$@")
 %.o: %.rc
 	$(call quiet-command,$(WINDRES) -I. -o $@ $<,"RC","$(TARGET_DIR)$@")
 
@@ -74,16 +81,24 @@ LINK = $(call quiet-command, $(LINKPROG) $(QEMU_CFLAGS) $(CFLAGS) $(LDFLAGS) -o
        $(version-obj-y) $(call extract-libs,$1) $(LIBS),"LINK","$(TARGET_DIR)$@")
 
 %.o: %.S
-	$(call quiet-command,$(CCAS) $(QEMU_INCLUDES) $(QEMU_CFLAGS) $(QEMU_DGFLAGS) $(CFLAGS) -c -o $@ $<,"CCAS","$(TARGET_DIR)$@")
+	$(call quiet-command,$(CCAS) $(QEMU_LOCAL_INCLUDES) $(QEMU_INCLUDES) \
+	       $(QEMU_CFLAGS) $(QEMU_DGFLAGS) $(CFLAGS) \
+	       -c -o $@ $<,"CCAS","$(TARGET_DIR)$@")
 
 %.o: %.cc
-	$(call quiet-command,$(CXX) $(QEMU_INCLUDES) $(QEMU_CXXFLAGS) $(QEMU_DGFLAGS) $(CFLAGS) $($@-cflags) -c -o $@ $<,"CXX","$(TARGET_DIR)$@")
+	$(call quiet-command,$(CXX) $(QEMU_LOCAL_INCLUDES) $(QEMU_INCLUDES) \
+	       $(QEMU_CXXFLAGS) $(QEMU_DGFLAGS) $(CFLAGS) $($@-cflags) \
+	       -c -o $@ $<,"CXX","$(TARGET_DIR)$@")
 
 %.o: %.cpp
-	$(call quiet-command,$(CXX) $(QEMU_INCLUDES) $(QEMU_CXXFLAGS) $(QEMU_DGFLAGS) $(CFLAGS) $($@-cflags) -c -o $@ $<,"CXX","$(TARGET_DIR)$@")
+	$(call quiet-command,$(CXX) $(QEMU_LOCAL_INCLUDES) $(QEMU_INCLUDES) \
+	       $(QEMU_CXXFLAGS) $(QEMU_DGFLAGS) $(CFLAGS) $($@-cflags) \
+	       -c -o $@ $<,"CXX","$(TARGET_DIR)$@")
 
 %.o: %.m
-	$(call quiet-command,$(OBJCC) $(QEMU_INCLUDES) $(QEMU_CFLAGS) $(QEMU_DGFLAGS) $(CFLAGS) $($@-cflags) -c -o $@ $<,"OBJC","$(TARGET_DIR)$@")
+	$(call quiet-command,$(OBJCC) $(QEMU_LOCAL_INCLUDES) $(QEMU_INCLUDES) \
+	       $(QEMU_CFLAGS) $(QEMU_DGFLAGS) $(CFLAGS) $($@-cflags) \
+	       -c -o $@ $<,"OBJC","$(TARGET_DIR)$@")
 
 %.o: %.dtrace
 	$(call quiet-command,dtrace -o $@ -G -s $<,"GEN","$(TARGET_DIR)$@")
@@ -359,6 +374,7 @@ define unnest-vars
                 $(eval $(o:%.mo=%$(DSOSUF)): module-common.o $($o-objs)),
                 $(error $o added in $v but $o-objs is not set)))
         $(shell mkdir -p ./ $(sort $(dir $($v))))
+        $(shell cd $(BUILD_DIR) && mkdir -p ./ $(sort $(dir $($v))))
         # Include all the .d files
         $(eval -include $(patsubst %.o,%.d,$(patsubst %.mo,%.d,$($v))))
         $(eval $v := $(filter-out %/,$($v))))
diff --git a/scripts/simpletrace.py b/scripts/simpletrace.py
index 4ca903dc0c..4c990047b6 100755
--- a/scripts/simpletrace.py
+++ b/scripts/simpletrace.py
@@ -73,10 +73,14 @@ def read_record(edict, idtoname, fobj):
 def read_trace_header(fobj):
     """Read and verify trace file header"""
     header = read_header(fobj, log_header_fmt)
-    if header is None or \
-       header[0] != header_event_id or \
-       header[1] != header_magic:
+    if header is None:
         raise ValueError('Not a valid trace file!')
+    if header[0] != header_event_id:
+        raise ValueError('Not a valid trace file, header id %d != %d' %
+                         (header[0], header_event_id))
+    if header[1] != header_magic:
+        raise ValueError('Not a valid trace file, header magic %d != %d' %
+                         (header[1], header_magic))
 
     log_version = header[2]
     if log_version not in [0, 2, 3, 4]:
diff --git a/scripts/tracetool.py b/scripts/tracetool.py
index c9e47371d3..c55a21518b 100755
--- a/scripts/tracetool.py
+++ b/scripts/tracetool.py
@@ -49,6 +49,7 @@ Options:
     --binary <path>          Full path to QEMU binary.
     --target-type <type>     QEMU emulator target type ('system' or 'user').
     --target-name <name>     QEMU emulator target name.
+    --group <name>           Name of the event group
     --probe-prefix <prefix>  Prefix for dtrace probe names
                              (default: qemu-<target-type>-<target-name>).\
 """ % {
@@ -62,22 +63,12 @@ Options:
     else:
         sys.exit(1)
 
-def make_group_name(filename):
-    dirname = os.path.realpath(os.path.dirname(filename))
-    basedir = os.path.join(os.path.dirname(__file__), os.pardir)
-    basedir = os.path.realpath(os.path.abspath(basedir))
-    dirname = dirname[len(basedir) + 1:]
-
-    if dirname == "":
-        return "common"
-    return "_" + re.sub(r"[^A-Za-z0-9]", "_", dirname)
-
 def main(args):
     global _SCRIPT
     _SCRIPT = args[0]
 
     long_opts = ["backends=", "format=", "help", "list-backends",
-                 "check-backends"]
+                 "check-backends", "group="]
     long_opts += ["binary=", "target-type=", "target-name=", "probe-prefix="]
 
     try:
@@ -88,6 +79,7 @@ def main(args):
     check_backends = False
     arg_backends = []
     arg_format = ""
+    arg_group = None
     binary = None
     target_type = None
     target_name = None
@@ -98,6 +90,8 @@ def main(args):
 
         elif opt == "--backends":
             arg_backends = arg.split(",")
+        elif opt == "--group":
+            arg_group = arg
         elif opt == "--format":
             arg_format = arg
 
@@ -129,6 +123,9 @@ def main(args):
                 sys.exit(1)
         sys.exit(0)
 
+    if arg_group is None:
+        error_opt("group name is required")
+
     if arg_format == "stap":
         if binary is None:
             error_opt("--binary is required for SystemTAP tapset generator")
@@ -140,15 +137,15 @@ def main(args):
         if probe_prefix is None:
             probe_prefix = ".".join(["qemu", target_type, target_name])
 
-    if len(args) != 1:
+    if len(args) < 1:
         error_opt("missing trace-events filepath")
-    with open(args[0], "r") as fh:
-        events = tracetool.read_events(fh)
-
-    group = make_group_name(args[0])
+    events = []
+    for arg in args:
+        with open(arg, "r") as fh:
+            events.extend(tracetool.read_events(fh))
 
     try:
-        tracetool.generate(events, group, arg_format, arg_backends,
+        tracetool.generate(events, arg_group, arg_format, arg_backends,
                            binary=binary, probe_prefix=probe_prefix)
     except tracetool.TracetoolError as e:
         error_opt(str(e))
diff --git a/scripts/tracetool/backend/dtrace.py b/scripts/tracetool/backend/dtrace.py
index 79505c6b1a..c469cbd1a3 100644
--- a/scripts/tracetool/backend/dtrace.py
+++ b/scripts/tracetool/backend/dtrace.py
@@ -36,7 +36,12 @@ def binary():
 
 
 def generate_h_begin(events, group):
-    out('#include "trace/generated-tracers-dtrace.h"',
+    if group == "root":
+        header = "trace-dtrace-root.h"
+    else:
+        header = "trace-dtrace.h"
+
+    out('#include "%s"' % header,
         '')
 
 
diff --git a/scripts/tracetool/backend/simple.py b/scripts/tracetool/backend/simple.py
index 85f61028e2..4acc06e81c 100644
--- a/scripts/tracetool/backend/simple.py
+++ b/scripts/tracetool/backend/simple.py
@@ -44,7 +44,6 @@ def generate_h(event, group):
 
 def generate_c_begin(events, group):
     out('#include "qemu/osdep.h"',
-        '#include "trace.h"',
         '#include "trace/control.h"',
         '#include "trace/simple.h"',
         '')
diff --git a/scripts/tracetool/backend/ust.py b/scripts/tracetool/backend/ust.py
index 4594db6128..52ce892478 100644
--- a/scripts/tracetool/backend/ust.py
+++ b/scripts/tracetool/backend/ust.py
@@ -20,8 +20,13 @@ PUBLIC = True
 
 
 def generate_h_begin(events, group):
+    if group == "root":
+        header = "trace-ust-root.h"
+    else:
+        header = "trace-ust.h"
+
     out('#include <lttng/tracepoint.h>',
-        '#include "trace/generated-ust-provider.h"',
+        '#include "%s"' % header,
         '')
 
 
diff --git a/scripts/tracetool/format/c.py b/scripts/tracetool/format/c.py
index 47115ed8af..833c05a022 100644
--- a/scripts/tracetool/format/c.py
+++ b/scripts/tracetool/format/c.py
@@ -20,10 +20,15 @@ def generate(events, backend, group):
     active_events = [e for e in events
                      if "disable" not in e.properties]
 
+    if group == "root":
+        header = "trace-root.h"
+    else:
+        header = "trace.h"
+
     out('/* This file is autogenerated by tracetool, do not edit. */',
         '',
         '#include "qemu/osdep.h"',
-        '#include "trace.h"',
+        '#include "%s"' % header,
         '')
 
     for e in events:
diff --git a/scripts/tracetool/format/tcg_h.py b/scripts/tracetool/format/tcg_h.py
index 5f213f6cba..7ddc4a52ce 100644
--- a/scripts/tracetool/format/tcg_h.py
+++ b/scripts/tracetool/format/tcg_h.py
@@ -28,13 +28,17 @@ def vcpu_transform_args(args):
 
 
 def generate(events, backend, group):
+    if group == "root":
+        header = "trace-root.h"
+    else:
+        header = "trace.h"
+
     out('/* This file is autogenerated by tracetool, do not edit. */',
         '/* You must include this file after the inclusion of helper.h */',
         '',
         '#ifndef TRACE_%s_GENERATED_TCG_TRACERS_H' % group.upper(),
         '#define TRACE_%s_GENERATED_TCG_TRACERS_H' % group.upper(),
         '',
-        '#include "trace.h"',
         '#include "exec/helper-proto.h"',
         '',
         )
diff --git a/scripts/tracetool/format/tcg_helper_c.py b/scripts/tracetool/format/tcg_helper_c.py
index cc26e03008..7dccd8c5ec 100644
--- a/scripts/tracetool/format/tcg_helper_c.py
+++ b/scripts/tracetool/format/tcg_helper_c.py
@@ -41,6 +41,11 @@ def vcpu_transform_args(args, mode):
 
 
 def generate(events, backend, group):
+    if group == "root":
+        header = "trace-root.h"
+    else:
+        header = "trace.h"
+
     events = [e for e in events
               if "disable" not in e.properties]
 
@@ -49,7 +54,6 @@ def generate(events, backend, group):
         '#include "qemu/osdep.h"',
         '#include "qemu-common.h"',
         '#include "cpu.h"',
-        '#include "trace.h"',
         '#include "exec/helper-proto.h"',
         '',
         )
diff --git a/scripts/tracetool/format/ust_events_c.py b/scripts/tracetool/format/ust_events_c.py
index cd87d8ab8f..264784cdf2 100644
--- a/scripts/tracetool/format/ust_events_c.py
+++ b/scripts/tracetool/format/ust_events_c.py
@@ -32,4 +32,4 @@ def generate(events, backend, group):
         ' */',
         '#pragma GCC diagnostic ignored "-Wredundant-decls"',
         '',
-        '#include "generated-ust-provider.h"')
+        '#include "trace-ust-all.h"')
diff --git a/scripts/tracetool/format/ust_events_h.py b/scripts/tracetool/format/ust_events_h.py
index d853155d21..514294c2cc 100644
--- a/scripts/tracetool/format/ust_events_h.py
+++ b/scripts/tracetool/format/ust_events_h.py
@@ -20,13 +20,18 @@ def generate(events, backend, group):
     events = [e for e in events
               if "disabled" not in e.properties]
 
+    if group == "all":
+        include = "trace-ust-all.h"
+    else:
+        include = "trace-ust.h"
+
     out('/* This file is autogenerated by tracetool, do not edit. */',
         '',
         '#undef TRACEPOINT_PROVIDER',
         '#define TRACEPOINT_PROVIDER qemu',
         '',
         '#undef TRACEPOINT_INCLUDE_FILE',
-        '#define TRACEPOINT_INCLUDE_FILE ./generated-ust-provider.h',
+        '#define TRACEPOINT_INCLUDE_FILE ./%s' % include,
         '',
         '#if !defined (TRACE_%s_GENERATED_UST_H) || \\'  % group.upper(),
         '     defined(TRACEPOINT_HEADER_MULTI_READ)',
diff --git a/spice-qemu-char.c b/spice-qemu-char.c
index 276c4aef68..6f46f46b25 100644
--- a/spice-qemu-char.c
+++ b/spice-qemu-char.c
@@ -1,43 +1,52 @@
 #include "qemu/osdep.h"
-#include "trace.h"
+#include "trace-root.h"
 #include "ui/qemu-spice.h"
 #include "sysemu/char.h"
+#include "qemu/error-report.h"
 #include <spice.h>
 #include <spice/protocol.h>
 
 
-typedef struct SpiceCharDriver {
-    CharDriverState*      chr;
-    SpiceCharDeviceInstance     sin;
+typedef struct SpiceChardev {
+    Chardev               parent;
+
+    SpiceCharDeviceInstance sin;
     bool                  active;
     bool                  blocked;
     const uint8_t         *datapos;
     int                   datalen;
-    QLIST_ENTRY(SpiceCharDriver) next;
-} SpiceCharDriver;
+    QLIST_ENTRY(SpiceChardev) next;
+} SpiceChardev;
+
+#define TYPE_CHARDEV_SPICE "chardev-spice"
+#define TYPE_CHARDEV_SPICEVMC "chardev-spicevmc"
+#define TYPE_CHARDEV_SPICEPORT "chardev-spiceport"
+
+#define SPICE_CHARDEV(obj) OBJECT_CHECK(SpiceChardev, (obj), TYPE_CHARDEV_SPICE)
 
 typedef struct SpiceCharSource {
     GSource               source;
-    SpiceCharDriver       *scd;
+    SpiceChardev       *scd;
 } SpiceCharSource;
 
-static QLIST_HEAD(, SpiceCharDriver) spice_chars =
+static QLIST_HEAD(, SpiceChardev) spice_chars =
     QLIST_HEAD_INITIALIZER(spice_chars);
 
 static int vmc_write(SpiceCharDeviceInstance *sin, const uint8_t *buf, int len)
 {
-    SpiceCharDriver *scd = container_of(sin, SpiceCharDriver, sin);
+    SpiceChardev *scd = container_of(sin, SpiceChardev, sin);
+    Chardev *chr = CHARDEV(scd);
     ssize_t out = 0;
     ssize_t last_out;
     uint8_t* p = (uint8_t*)buf;
 
     while (len > 0) {
-        int can_write = qemu_chr_be_can_write(scd->chr);
+        int can_write = qemu_chr_be_can_write(chr);
         last_out = MIN(len, can_write);
         if (last_out <= 0) {
             break;
         }
-        qemu_chr_be_write(scd->chr, p, last_out);
+        qemu_chr_be_write(chr, p, last_out);
         out += last_out;
         len -= last_out;
         p += last_out;
@@ -49,7 +58,7 @@ static int vmc_write(SpiceCharDeviceInstance *sin, const uint8_t *buf, int len)
 
 static int vmc_read(SpiceCharDeviceInstance *sin, uint8_t *buf, int len)
 {
-    SpiceCharDriver *scd = container_of(sin, SpiceCharDriver, sin);
+    SpiceChardev *scd = container_of(sin, SpiceChardev, sin);
     int bytes = MIN(len, scd->datalen);
 
     if (bytes > 0) {
@@ -69,7 +78,8 @@ static int vmc_read(SpiceCharDeviceInstance *sin, uint8_t *buf, int len)
 #if SPICE_SERVER_VERSION >= 0x000c02
 static void vmc_event(SpiceCharDeviceInstance *sin, uint8_t event)
 {
-    SpiceCharDriver *scd = container_of(sin, SpiceCharDriver, sin);
+    SpiceChardev *scd = container_of(sin, SpiceChardev, sin);
+    Chardev *chr = CHARDEV(scd);
     int chr_event;
 
     switch (event) {
@@ -81,20 +91,21 @@ static void vmc_event(SpiceCharDeviceInstance *sin, uint8_t event)
     }
 
     trace_spice_vmc_event(chr_event);
-    qemu_chr_be_event(scd->chr, chr_event);
+    qemu_chr_be_event(chr, chr_event);
 }
 #endif
 
 static void vmc_state(SpiceCharDeviceInstance *sin, int connected)
 {
-    SpiceCharDriver *scd = container_of(sin, SpiceCharDriver, sin);
+    SpiceChardev *scd = container_of(sin, SpiceChardev, sin);
+    Chardev *chr = CHARDEV(scd);
 
-    if ((scd->chr->be_open && connected) ||
-        (!scd->chr->be_open && !connected)) {
+    if ((chr->be_open && connected) ||
+        (!chr->be_open && !connected)) {
         return;
     }
 
-    qemu_chr_be_event(scd->chr,
+    qemu_chr_be_event(chr,
                       connected ? CHR_EVENT_OPENED : CHR_EVENT_CLOSED);
 }
 
@@ -115,7 +126,7 @@ static SpiceCharDeviceInterface vmc_interface = {
 };
 
 
-static void vmc_register_interface(SpiceCharDriver *scd)
+static void vmc_register_interface(SpiceChardev *scd)
 {
     if (scd->active) {
         return;
@@ -126,7 +137,7 @@ static void vmc_register_interface(SpiceCharDriver *scd)
     trace_spice_vmc_register_interface(scd);
 }
 
-static void vmc_unregister_interface(SpiceCharDriver *scd)
+static void vmc_unregister_interface(SpiceChardev *scd)
 {
     if (!scd->active) {
         return;
@@ -166,9 +177,9 @@ static GSourceFuncs SpiceCharSourceFuncs = {
     .dispatch = spice_char_source_dispatch,
 };
 
-static GSource *spice_chr_add_watch(CharDriverState *chr, GIOCondition cond)
+static GSource *spice_chr_add_watch(Chardev *chr, GIOCondition cond)
 {
-    SpiceCharDriver *scd = chr->opaque;
+    SpiceChardev *scd = SPICE_CHARDEV(chr);
     SpiceCharSource *src;
 
     assert(cond & G_IO_OUT);
@@ -180,9 +191,9 @@ static GSource *spice_chr_add_watch(CharDriverState *chr, GIOCondition cond)
     return (GSource *)src;
 }
 
-static int spice_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
+static int spice_chr_write(Chardev *chr, const uint8_t *buf, int len)
 {
-    SpiceCharDriver *s = chr->opaque;
+    SpiceChardev *s = SPICE_CHARDEV(chr);
     int read_bytes;
 
     assert(s->datalen == 0);
@@ -199,9 +210,9 @@ static int spice_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
     return read_bytes;
 }
 
-static void spice_chr_free(struct CharDriverState *chr)
+static void char_spice_finalize(Object *obj)
 {
-    SpiceCharDriver *s = chr->opaque;
+    SpiceChardev *s = SPICE_CHARDEV(obj);
 
     vmc_unregister_interface(s);
     QLIST_REMOVE(s, next);
@@ -210,12 +221,11 @@ static void spice_chr_free(struct CharDriverState *chr)
 #if SPICE_SERVER_VERSION >= 0x000c02
     g_free((char *)s->sin.portname);
 #endif
-    g_free(s);
 }
 
-static void spice_vmc_set_fe_open(struct CharDriverState *chr, int fe_open)
+static void spice_vmc_set_fe_open(struct Chardev *chr, int fe_open)
 {
-    SpiceCharDriver *s = chr->opaque;
+    SpiceChardev *s = SPICE_CHARDEV(chr);
     if (fe_open) {
         vmc_register_interface(s);
     } else {
@@ -223,10 +233,10 @@ static void spice_vmc_set_fe_open(struct CharDriverState *chr, int fe_open)
     }
 }
 
-static void spice_port_set_fe_open(struct CharDriverState *chr, int fe_open)
+static void spice_port_set_fe_open(struct Chardev *chr, int fe_open)
 {
 #if SPICE_SERVER_VERSION >= 0x000c02
-    SpiceCharDriver *s = chr->opaque;
+    SpiceChardev *s = SPICE_CHARDEV(chr);
 
     if (fe_open) {
         spice_server_port_event(&s->sin, SPICE_PORT_EVENT_OPENED);
@@ -236,69 +246,31 @@ static void spice_port_set_fe_open(struct CharDriverState *chr, int fe_open)
 #endif
 }
 
-static void print_allowed_subtypes(void)
+static void spice_chr_accept_input(struct Chardev *chr)
 {
-    const char** psubtype;
-    int i;
-
-    fprintf(stderr, "allowed names: ");
-    for(i=0, psubtype = spice_server_char_device_recognized_subtypes();
-        *psubtype != NULL; ++psubtype, ++i) {
-        if (i == 0) {
-            fprintf(stderr, "%s", *psubtype);
-        } else {
-            fprintf(stderr, ", %s", *psubtype);
-        }
-    }
-    fprintf(stderr, "\n");
-}
-
-static void spice_chr_accept_input(struct CharDriverState *chr)
-{
-    SpiceCharDriver *s = chr->opaque;
+    SpiceChardev *s = SPICE_CHARDEV(chr);
 
     spice_server_char_device_wakeup(&s->sin);
 }
 
-static CharDriverState *chr_open(const char *subtype,
-                                 void (*set_fe_open)(struct CharDriverState *,
-                                                     int),
-                                 ChardevCommon *backend,
-                                 Error **errp)
+static void chr_open(Chardev *chr, const char *subtype)
 {
-    CharDriverState *chr;
-    SpiceCharDriver *s;
+    SpiceChardev *s = SPICE_CHARDEV(chr);
 
-    chr = qemu_chr_alloc(backend, errp);
-    if (!chr) {
-        return NULL;
-    }
-    s = g_malloc0(sizeof(SpiceCharDriver));
-    s->chr = chr;
     s->active = false;
     s->sin.subtype = g_strdup(subtype);
-    chr->opaque = s;
-    chr->chr_write = spice_chr_write;
-    chr->chr_add_watch = spice_chr_add_watch;
-    chr->chr_free = spice_chr_free;
-    chr->chr_set_fe_open = set_fe_open;
-    chr->chr_accept_input = spice_chr_accept_input;
 
     QLIST_INSERT_HEAD(&spice_chars, s, next);
-
-    return chr;
 }
 
-static CharDriverState *qemu_chr_open_spice_vmc(const char *id,
-                                                ChardevBackend *backend,
-                                                ChardevReturn *ret,
-                                                bool *be_opened,
-                                                Error **errp)
+static void qemu_chr_open_spice_vmc(Chardev *chr,
+                                    ChardevBackend *backend,
+                                    bool *be_opened,
+                                    Error **errp)
 {
     ChardevSpiceChannel *spicevmc = backend->u.spicevmc.data;
     const char *type = spicevmc->type;
     const char **psubtype = spice_server_char_device_recognized_subtypes();
-    ChardevCommon *common = qapi_ChardevSpiceChannel_base(spicevmc);
 
     for (; *psubtype != NULL; ++psubtype) {
         if (strcmp(type, *psubtype) == 0) {
@@ -306,47 +278,46 @@ static CharDriverState *qemu_chr_open_spice_vmc(const char *id,
         }
     }
     if (*psubtype == NULL) {
-        fprintf(stderr, "spice-qemu-char: unsupported type: %s\n", type);
-        print_allowed_subtypes();
-        return NULL;
+        char *subtypes = g_strjoinv(", ",
+            (gchar **)spice_server_char_device_recognized_subtypes());
+
+        error_setg(errp, "unsupported type name: %s", type);
+        error_append_hint(errp, "allowed spice char type names: %s\n",
+                          subtypes);
+
+        g_free(subtypes);
+        return;
     }
 
     *be_opened = false;
-    return chr_open(type, spice_vmc_set_fe_open, common, errp);
+    chr_open(chr, type);
 }
 
 #if SPICE_SERVER_VERSION >= 0x000c02
-static CharDriverState *qemu_chr_open_spice_port(const char *id,
-                                                 ChardevBackend *backend,
-                                                 ChardevReturn *ret,
-                                                 bool *be_opened,
-                                                 Error **errp)
+static void qemu_chr_open_spice_port(Chardev *chr,
+                                     ChardevBackend *backend,
+                                     bool *be_opened,
+                                     Error **errp)
 {
     ChardevSpicePort *spiceport = backend->u.spiceport.data;
     const char *name = spiceport->fqdn;
-    ChardevCommon *common = qapi_ChardevSpicePort_base(spiceport);
-    CharDriverState *chr;
-    SpiceCharDriver *s;
+    SpiceChardev *s;
 
     if (name == NULL) {
-        fprintf(stderr, "spice-qemu-char: missing name parameter\n");
-        return NULL;
+        error_setg(errp, "missing name parameter");
+        return;
     }
 
-    chr = chr_open("port", spice_port_set_fe_open, common, errp);
-    if (!chr) {
-        return NULL;
-    }
+    chr_open(chr, "port");
+
     *be_opened = false;
-    s = chr->opaque;
+    s = SPICE_CHARDEV(chr);
     s->sin.portname = g_strdup(name);
-
-    return chr;
 }
 
 void qemu_spice_register_ports(void)
 {
-    SpiceCharDriver *s;
+    SpiceChardev *s;
 
     QLIST_FOREACH(s, &spice_chars, next) {
         if (s->sin.portname == NULL) {
@@ -367,6 +338,7 @@ static void qemu_chr_parse_spice_vmc(QemuOpts *opts, ChardevBackend *backend,
         error_setg(errp, "chardev: spice channel: no name given");
         return;
     }
+    backend->type = CHARDEV_BACKEND_KIND_SPICEVMC;
     spicevmc = backend->u.spicevmc.data = g_new0(ChardevSpiceChannel, 1);
     qemu_chr_parse_common(opts, qapi_ChardevSpiceChannel_base(spicevmc));
     spicevmc->type = g_strdup(name);
@@ -382,17 +354,65 @@ static void qemu_chr_parse_spice_port(QemuOpts *opts, ChardevBackend *backend,
         error_setg(errp, "chardev: spice port: no name given");
         return;
     }
+    backend->type = CHARDEV_BACKEND_KIND_SPICEPORT;
     spiceport = backend->u.spiceport.data = g_new0(ChardevSpicePort, 1);
     qemu_chr_parse_common(opts, qapi_ChardevSpicePort_base(spiceport));
     spiceport->fqdn = g_strdup(name);
 }
 
+static void char_spice_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
+
+    cc->chr_write = spice_chr_write;
+    cc->chr_add_watch = spice_chr_add_watch;
+    cc->chr_accept_input = spice_chr_accept_input;
+}
+
+static const TypeInfo char_spice_type_info = {
+    .name = TYPE_CHARDEV_SPICE,
+    .parent = TYPE_CHARDEV,
+    .instance_size = sizeof(SpiceChardev),
+    .instance_finalize = char_spice_finalize,
+    .class_init = char_spice_class_init,
+    .abstract = true,
+};
+
+static void char_spicevmc_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
+
+    cc->parse = qemu_chr_parse_spice_vmc;
+    cc->open = qemu_chr_open_spice_vmc;
+    cc->chr_set_fe_open = spice_vmc_set_fe_open;
+}
+
+static const TypeInfo char_spicevmc_type_info = {
+    .name = TYPE_CHARDEV_SPICEVMC,
+    .parent = TYPE_CHARDEV_SPICE,
+    .class_init = char_spicevmc_class_init,
+};
+
+static void char_spiceport_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
+
+    cc->parse = qemu_chr_parse_spice_port;
+    cc->open = qemu_chr_open_spice_port;
+    cc->chr_set_fe_open = spice_port_set_fe_open;
+}
+
+static const TypeInfo char_spiceport_type_info = {
+    .name = TYPE_CHARDEV_SPICEPORT,
+    .parent = TYPE_CHARDEV_SPICE,
+    .class_init = char_spiceport_class_init,
+};
+
 static void register_types(void)
 {
-    register_char_driver("spicevmc", CHARDEV_BACKEND_KIND_SPICEVMC,
-                         qemu_chr_parse_spice_vmc, qemu_chr_open_spice_vmc);
-    register_char_driver("spiceport", CHARDEV_BACKEND_KIND_SPICEPORT,
-                         qemu_chr_parse_spice_port, qemu_chr_open_spice_port);
+    type_register_static(&char_spice_type_info);
+    type_register_static(&char_spicevmc_type_info);
+    type_register_static(&char_spiceport_type_info);
 }
 
 type_init(register_types);
diff --git a/stubs/monitor.c b/stubs/monitor.c
index 1d574b1c6f..e018c8f594 100644
--- a/stubs/monitor.c
+++ b/stubs/monitor.c
@@ -11,6 +11,6 @@ int monitor_get_fd(Monitor *mon, const char *name, Error **errp)
     return -1;
 }
 
-void monitor_init(CharDriverState *chr, int flags)
+void monitor_init(Chardev *chr, int flags)
 {
 }
diff --git a/stubs/replay.c b/stubs/replay.c
index d9a6da99d2..9c8aa48c9c 100644
--- a/stubs/replay.c
+++ b/stubs/replay.c
@@ -30,11 +30,11 @@ void replay_finish(void)
 {
 }
 
-void replay_register_char_driver(CharDriverState *chr)
+void replay_register_char_driver(Chardev *chr)
 {
 }
 
-void replay_chr_be_write(CharDriverState *s, uint8_t *buf, int len)
+void replay_chr_be_write(Chardev *s, uint8_t *buf, int len)
 {
     abort();
 }
diff --git a/target/arm/kvm-consts.h b/target/arm/kvm-consts.h
index a2c9518592..aad28258a3 100644
--- a/target/arm/kvm-consts.h
+++ b/target/arm/kvm-consts.h
@@ -21,7 +21,9 @@
 #define MISMATCH_CHECK(X, Y) QEMU_BUILD_BUG_ON(X != Y)
 
 #else
-#define MISMATCH_CHECK(X, Y)
+
+#define MISMATCH_CHECK(X, Y) QEMU_BUILD_BUG_ON(0)
+
 #endif
 
 #define CP_REG_SIZE_SHIFT 52
@@ -31,12 +33,12 @@
 #define CP_REG_ARM             0x4000000000000000ULL
 #define CP_REG_ARCH_MASK       0xff00000000000000ULL
 
-MISMATCH_CHECK(CP_REG_SIZE_SHIFT, KVM_REG_SIZE_SHIFT)
-MISMATCH_CHECK(CP_REG_SIZE_MASK, KVM_REG_SIZE_MASK)
-MISMATCH_CHECK(CP_REG_SIZE_U32, KVM_REG_SIZE_U32)
-MISMATCH_CHECK(CP_REG_SIZE_U64, KVM_REG_SIZE_U64)
-MISMATCH_CHECK(CP_REG_ARM, KVM_REG_ARM)
-MISMATCH_CHECK(CP_REG_ARCH_MASK, KVM_REG_ARCH_MASK)
+MISMATCH_CHECK(CP_REG_SIZE_SHIFT, KVM_REG_SIZE_SHIFT);
+MISMATCH_CHECK(CP_REG_SIZE_MASK, KVM_REG_SIZE_MASK);
+MISMATCH_CHECK(CP_REG_SIZE_U32, KVM_REG_SIZE_U32);
+MISMATCH_CHECK(CP_REG_SIZE_U64, KVM_REG_SIZE_U64);
+MISMATCH_CHECK(CP_REG_ARM, KVM_REG_ARM);
+MISMATCH_CHECK(CP_REG_ARCH_MASK, KVM_REG_ARCH_MASK);
 
 #define QEMU_PSCI_0_1_FN_BASE 0x95c1ba5e
 #define QEMU_PSCI_0_1_FN(n) (QEMU_PSCI_0_1_FN_BASE + (n))
@@ -45,10 +47,10 @@ MISMATCH_CHECK(CP_REG_ARCH_MASK, KVM_REG_ARCH_MASK)
 #define QEMU_PSCI_0_1_FN_CPU_ON QEMU_PSCI_0_1_FN(2)
 #define QEMU_PSCI_0_1_FN_MIGRATE QEMU_PSCI_0_1_FN(3)
 
-MISMATCH_CHECK(QEMU_PSCI_0_1_FN_CPU_SUSPEND, KVM_PSCI_FN_CPU_SUSPEND)
-MISMATCH_CHECK(QEMU_PSCI_0_1_FN_CPU_OFF, KVM_PSCI_FN_CPU_OFF)
-MISMATCH_CHECK(QEMU_PSCI_0_1_FN_CPU_ON, KVM_PSCI_FN_CPU_ON)
-MISMATCH_CHECK(QEMU_PSCI_0_1_FN_MIGRATE, KVM_PSCI_FN_MIGRATE)
+MISMATCH_CHECK(QEMU_PSCI_0_1_FN_CPU_SUSPEND, KVM_PSCI_FN_CPU_SUSPEND);
+MISMATCH_CHECK(QEMU_PSCI_0_1_FN_CPU_OFF, KVM_PSCI_FN_CPU_OFF);
+MISMATCH_CHECK(QEMU_PSCI_0_1_FN_CPU_ON, KVM_PSCI_FN_CPU_ON);
+MISMATCH_CHECK(QEMU_PSCI_0_1_FN_MIGRATE, KVM_PSCI_FN_MIGRATE);
 
 #define QEMU_PSCI_0_2_FN_BASE 0x84000000
 #define QEMU_PSCI_0_2_FN(n) (QEMU_PSCI_0_2_FN_BASE + (n))
@@ -75,13 +77,13 @@ MISMATCH_CHECK(QEMU_PSCI_0_1_FN_MIGRATE, KVM_PSCI_FN_MIGRATE)
 #define QEMU_PSCI_0_2_FN64_AFFINITY_INFO QEMU_PSCI_0_2_FN64(4)
 #define QEMU_PSCI_0_2_FN64_MIGRATE QEMU_PSCI_0_2_FN64(5)
 
-MISMATCH_CHECK(QEMU_PSCI_0_2_FN_CPU_SUSPEND, PSCI_0_2_FN_CPU_SUSPEND)
-MISMATCH_CHECK(QEMU_PSCI_0_2_FN_CPU_OFF, PSCI_0_2_FN_CPU_OFF)
-MISMATCH_CHECK(QEMU_PSCI_0_2_FN_CPU_ON, PSCI_0_2_FN_CPU_ON)
-MISMATCH_CHECK(QEMU_PSCI_0_2_FN_MIGRATE, PSCI_0_2_FN_MIGRATE)
-MISMATCH_CHECK(QEMU_PSCI_0_2_FN64_CPU_SUSPEND, PSCI_0_2_FN64_CPU_SUSPEND)
-MISMATCH_CHECK(QEMU_PSCI_0_2_FN64_CPU_ON, PSCI_0_2_FN64_CPU_ON)
-MISMATCH_CHECK(QEMU_PSCI_0_2_FN64_MIGRATE, PSCI_0_2_FN64_MIGRATE)
+MISMATCH_CHECK(QEMU_PSCI_0_2_FN_CPU_SUSPEND, PSCI_0_2_FN_CPU_SUSPEND);
+MISMATCH_CHECK(QEMU_PSCI_0_2_FN_CPU_OFF, PSCI_0_2_FN_CPU_OFF);
+MISMATCH_CHECK(QEMU_PSCI_0_2_FN_CPU_ON, PSCI_0_2_FN_CPU_ON);
+MISMATCH_CHECK(QEMU_PSCI_0_2_FN_MIGRATE, PSCI_0_2_FN_MIGRATE);
+MISMATCH_CHECK(QEMU_PSCI_0_2_FN64_CPU_SUSPEND, PSCI_0_2_FN64_CPU_SUSPEND);
+MISMATCH_CHECK(QEMU_PSCI_0_2_FN64_CPU_ON, PSCI_0_2_FN64_CPU_ON);
+MISMATCH_CHECK(QEMU_PSCI_0_2_FN64_MIGRATE, PSCI_0_2_FN64_MIGRATE);
 
 /* PSCI v0.2 return values used by TCG emulation of PSCI */
 
@@ -91,9 +93,9 @@ MISMATCH_CHECK(QEMU_PSCI_0_2_FN64_MIGRATE, PSCI_0_2_FN64_MIGRATE)
 /* We implement version 0.2 only */
 #define QEMU_PSCI_0_2_RET_VERSION_0_2                       2
 
-MISMATCH_CHECK(QEMU_PSCI_0_2_RET_TOS_MIGRATION_NOT_REQUIRED, PSCI_0_2_TOS_MP)
+MISMATCH_CHECK(QEMU_PSCI_0_2_RET_TOS_MIGRATION_NOT_REQUIRED, PSCI_0_2_TOS_MP);
 MISMATCH_CHECK(QEMU_PSCI_0_2_RET_VERSION_0_2,
-               (PSCI_VERSION_MAJOR(0) | PSCI_VERSION_MINOR(2)))
+               (PSCI_VERSION_MAJOR(0) | PSCI_VERSION_MINOR(2)));
 
 /* PSCI return values (inclusive of all PSCI versions) */
 #define QEMU_PSCI_RET_SUCCESS                     0
@@ -106,15 +108,15 @@ MISMATCH_CHECK(QEMU_PSCI_0_2_RET_VERSION_0_2,
 #define QEMU_PSCI_RET_NOT_PRESENT                 -7
 #define QEMU_PSCI_RET_DISABLED                    -8
 
-MISMATCH_CHECK(QEMU_PSCI_RET_SUCCESS, PSCI_RET_SUCCESS)
-MISMATCH_CHECK(QEMU_PSCI_RET_NOT_SUPPORTED, PSCI_RET_NOT_SUPPORTED)
-MISMATCH_CHECK(QEMU_PSCI_RET_INVALID_PARAMS, PSCI_RET_INVALID_PARAMS)
-MISMATCH_CHECK(QEMU_PSCI_RET_DENIED, PSCI_RET_DENIED)
-MISMATCH_CHECK(QEMU_PSCI_RET_ALREADY_ON, PSCI_RET_ALREADY_ON)
-MISMATCH_CHECK(QEMU_PSCI_RET_ON_PENDING, PSCI_RET_ON_PENDING)
-MISMATCH_CHECK(QEMU_PSCI_RET_INTERNAL_FAILURE, PSCI_RET_INTERNAL_FAILURE)
-MISMATCH_CHECK(QEMU_PSCI_RET_NOT_PRESENT, PSCI_RET_NOT_PRESENT)
-MISMATCH_CHECK(QEMU_PSCI_RET_DISABLED, PSCI_RET_DISABLED)
+MISMATCH_CHECK(QEMU_PSCI_RET_SUCCESS, PSCI_RET_SUCCESS);
+MISMATCH_CHECK(QEMU_PSCI_RET_NOT_SUPPORTED, PSCI_RET_NOT_SUPPORTED);
+MISMATCH_CHECK(QEMU_PSCI_RET_INVALID_PARAMS, PSCI_RET_INVALID_PARAMS);
+MISMATCH_CHECK(QEMU_PSCI_RET_DENIED, PSCI_RET_DENIED);
+MISMATCH_CHECK(QEMU_PSCI_RET_ALREADY_ON, PSCI_RET_ALREADY_ON);
+MISMATCH_CHECK(QEMU_PSCI_RET_ON_PENDING, PSCI_RET_ON_PENDING);
+MISMATCH_CHECK(QEMU_PSCI_RET_INTERNAL_FAILURE, PSCI_RET_INTERNAL_FAILURE);
+MISMATCH_CHECK(QEMU_PSCI_RET_NOT_PRESENT, PSCI_RET_NOT_PRESENT);
+MISMATCH_CHECK(QEMU_PSCI_RET_DISABLED, PSCI_RET_DISABLED);
 
 /* Note that KVM uses overlapping values for AArch32 and AArch64
  * target CPU numbers. AArch32 targets:
@@ -135,14 +137,14 @@ MISMATCH_CHECK(QEMU_PSCI_RET_DISABLED, PSCI_RET_DISABLED)
 #define QEMU_KVM_ARM_TARGET_NONE UINT_MAX
 
 #ifdef TARGET_AARCH64
-MISMATCH_CHECK(QEMU_KVM_ARM_TARGET_AEM_V8, KVM_ARM_TARGET_AEM_V8)
-MISMATCH_CHECK(QEMU_KVM_ARM_TARGET_FOUNDATION_V8, KVM_ARM_TARGET_FOUNDATION_V8)
-MISMATCH_CHECK(QEMU_KVM_ARM_TARGET_CORTEX_A57, KVM_ARM_TARGET_CORTEX_A57)
-MISMATCH_CHECK(QEMU_KVM_ARM_TARGET_XGENE_POTENZA, KVM_ARM_TARGET_XGENE_POTENZA)
-MISMATCH_CHECK(QEMU_KVM_ARM_TARGET_CORTEX_A53, KVM_ARM_TARGET_CORTEX_A53)
+MISMATCH_CHECK(QEMU_KVM_ARM_TARGET_AEM_V8, KVM_ARM_TARGET_AEM_V8);
+MISMATCH_CHECK(QEMU_KVM_ARM_TARGET_FOUNDATION_V8, KVM_ARM_TARGET_FOUNDATION_V8);
+MISMATCH_CHECK(QEMU_KVM_ARM_TARGET_CORTEX_A57, KVM_ARM_TARGET_CORTEX_A57);
+MISMATCH_CHECK(QEMU_KVM_ARM_TARGET_XGENE_POTENZA, KVM_ARM_TARGET_XGENE_POTENZA);
+MISMATCH_CHECK(QEMU_KVM_ARM_TARGET_CORTEX_A53, KVM_ARM_TARGET_CORTEX_A53);
 #else
-MISMATCH_CHECK(QEMU_KVM_ARM_TARGET_CORTEX_A15, KVM_ARM_TARGET_CORTEX_A15)
-MISMATCH_CHECK(QEMU_KVM_ARM_TARGET_CORTEX_A7, KVM_ARM_TARGET_CORTEX_A7)
+MISMATCH_CHECK(QEMU_KVM_ARM_TARGET_CORTEX_A15, KVM_ARM_TARGET_CORTEX_A15);
+MISMATCH_CHECK(QEMU_KVM_ARM_TARGET_CORTEX_A7, KVM_ARM_TARGET_CORTEX_A7);
 #endif
 
 #define CP_REG_ARM64                   0x6000000000000000ULL
@@ -164,20 +166,20 @@ MISMATCH_CHECK(QEMU_KVM_ARM_TARGET_CORTEX_A7, KVM_ARM_TARGET_CORTEX_A7)
 #define CP_REG_ARM64_SYSREG_CP (CP_REG_ARM64_SYSREG >> CP_REG_ARM_COPROC_SHIFT)
 
 #ifdef TARGET_AARCH64
-MISMATCH_CHECK(CP_REG_ARM64, KVM_REG_ARM64)
-MISMATCH_CHECK(CP_REG_ARM_COPROC_MASK, KVM_REG_ARM_COPROC_MASK)
-MISMATCH_CHECK(CP_REG_ARM_COPROC_SHIFT, KVM_REG_ARM_COPROC_SHIFT)
-MISMATCH_CHECK(CP_REG_ARM64_SYSREG, KVM_REG_ARM64_SYSREG)
-MISMATCH_CHECK(CP_REG_ARM64_SYSREG_OP0_MASK, KVM_REG_ARM64_SYSREG_OP0_MASK)
-MISMATCH_CHECK(CP_REG_ARM64_SYSREG_OP0_SHIFT, KVM_REG_ARM64_SYSREG_OP0_SHIFT)
-MISMATCH_CHECK(CP_REG_ARM64_SYSREG_OP1_MASK, KVM_REG_ARM64_SYSREG_OP1_MASK)
-MISMATCH_CHECK(CP_REG_ARM64_SYSREG_OP1_SHIFT, KVM_REG_ARM64_SYSREG_OP1_SHIFT)
-MISMATCH_CHECK(CP_REG_ARM64_SYSREG_CRN_MASK, KVM_REG_ARM64_SYSREG_CRN_MASK)
-MISMATCH_CHECK(CP_REG_ARM64_SYSREG_CRN_SHIFT, KVM_REG_ARM64_SYSREG_CRN_SHIFT)
-MISMATCH_CHECK(CP_REG_ARM64_SYSREG_CRM_MASK, KVM_REG_ARM64_SYSREG_CRM_MASK)
-MISMATCH_CHECK(CP_REG_ARM64_SYSREG_CRM_SHIFT, KVM_REG_ARM64_SYSREG_CRM_SHIFT)
-MISMATCH_CHECK(CP_REG_ARM64_SYSREG_OP2_MASK, KVM_REG_ARM64_SYSREG_OP2_MASK)
-MISMATCH_CHECK(CP_REG_ARM64_SYSREG_OP2_SHIFT, KVM_REG_ARM64_SYSREG_OP2_SHIFT)
+MISMATCH_CHECK(CP_REG_ARM64, KVM_REG_ARM64);
+MISMATCH_CHECK(CP_REG_ARM_COPROC_MASK, KVM_REG_ARM_COPROC_MASK);
+MISMATCH_CHECK(CP_REG_ARM_COPROC_SHIFT, KVM_REG_ARM_COPROC_SHIFT);
+MISMATCH_CHECK(CP_REG_ARM64_SYSREG, KVM_REG_ARM64_SYSREG);
+MISMATCH_CHECK(CP_REG_ARM64_SYSREG_OP0_MASK, KVM_REG_ARM64_SYSREG_OP0_MASK);
+MISMATCH_CHECK(CP_REG_ARM64_SYSREG_OP0_SHIFT, KVM_REG_ARM64_SYSREG_OP0_SHIFT);
+MISMATCH_CHECK(CP_REG_ARM64_SYSREG_OP1_MASK, KVM_REG_ARM64_SYSREG_OP1_MASK);
+MISMATCH_CHECK(CP_REG_ARM64_SYSREG_OP1_SHIFT, KVM_REG_ARM64_SYSREG_OP1_SHIFT);
+MISMATCH_CHECK(CP_REG_ARM64_SYSREG_CRN_MASK, KVM_REG_ARM64_SYSREG_CRN_MASK);
+MISMATCH_CHECK(CP_REG_ARM64_SYSREG_CRN_SHIFT, KVM_REG_ARM64_SYSREG_CRN_SHIFT);
+MISMATCH_CHECK(CP_REG_ARM64_SYSREG_CRM_MASK, KVM_REG_ARM64_SYSREG_CRM_MASK);
+MISMATCH_CHECK(CP_REG_ARM64_SYSREG_CRM_SHIFT, KVM_REG_ARM64_SYSREG_CRM_SHIFT);
+MISMATCH_CHECK(CP_REG_ARM64_SYSREG_OP2_MASK, KVM_REG_ARM64_SYSREG_OP2_MASK);
+MISMATCH_CHECK(CP_REG_ARM64_SYSREG_OP2_SHIFT, KVM_REG_ARM64_SYSREG_OP2_SHIFT);
 #endif
 
 #undef MISMATCH_CHECK
diff --git a/target/i386/cpu.c b/target/i386/cpu.c
index cff23e129d..eb49980ef1 100644
--- a/target/i386/cpu.c
+++ b/target/i386/cpu.c
@@ -3658,6 +3658,7 @@ static Property x86_cpu_properties[] = {
     DEFINE_PROP_BOOL("cpuid-0xb", X86CPU, enable_cpuid_0xb, true),
     DEFINE_PROP_BOOL("lmce", X86CPU, enable_lmce, false),
     DEFINE_PROP_BOOL("l3-cache", X86CPU, enable_l3_cache, true),
+    DEFINE_PROP_BOOL("vmware-cpuid-freq", X86CPU, vmware_cpuid_freq, true),
     DEFINE_PROP_END_OF_LIST()
 };
 
diff --git a/target/i386/cpu.h b/target/i386/cpu.h
index 10c5a3538d..4d788d56fc 100644
--- a/target/i386/cpu.h
+++ b/target/i386/cpu.h
@@ -1214,6 +1214,10 @@ struct X86CPU {
     bool host_features;
     uint32_t apic_id;
 
+    /* Enables publishing of TSC increment and Local APIC bus frequencies to
+     * the guest OS in CPUID page 0x40000010, the same way that VMWare does. */
+    bool vmware_cpuid_freq;
+
     /* if true the CPUID code directly forward host cache leaves to the guest */
     bool cache_info_passthrough;
 
diff --git a/target/i386/kvm.c b/target/i386/kvm.c
index 8e130ccf9c..27fd0505df 100644
--- a/target/i386/kvm.c
+++ b/target/i386/kvm.c
@@ -982,12 +982,6 @@ int kvm_arch_init_vcpu(CPUState *cs)
         }
     }
 
-    cpuid_data.cpuid.padding = 0;
-    r = kvm_vcpu_ioctl(cs, KVM_SET_CPUID2, &cpuid_data);
-    if (r) {
-        goto fail;
-    }
-
     r = kvm_arch_set_tsc_khz(cs);
     if (r < 0) {
         goto fail;
@@ -1007,6 +1001,36 @@ int kvm_arch_init_vcpu(CPUState *cs)
         }
     }
 
+    if (cpu->vmware_cpuid_freq
+        /* Guests depend on 0x40000000 to detect this feature, so only expose
+         * it if KVM exposes leaf 0x40000000. (Conflicts with Hyper-V) */
+        && cpu->expose_kvm
+        && kvm_base == KVM_CPUID_SIGNATURE
+        /* TSC clock must be stable and known for this feature. */
+        && ((env->features[FEAT_8000_0007_EDX] & CPUID_APM_INVTSC)
+            || env->user_tsc_khz != 0)
+        && env->tsc_khz != 0) {
+
+        c = &cpuid_data.entries[cpuid_i++];
+        c->function = KVM_CPUID_SIGNATURE | 0x10;
+        c->eax = env->tsc_khz;
+        /* LAPIC resolution of 1ns (freq: 1GHz) is hardcoded in KVM's
+         * APIC_BUS_CYCLE_NS */
+        c->ebx = 1000000;
+        c->ecx = c->edx = 0;
+
+        c = cpuid_find_entry(&cpuid_data.cpuid, kvm_base, 0);
+        c->eax = MAX(c->eax, KVM_CPUID_SIGNATURE | 0x10);
+    }
+
+    cpuid_data.cpuid.nent = cpuid_i;
+
+    cpuid_data.cpuid.padding = 0;
+    r = kvm_vcpu_ioctl(cs, KVM_SET_CPUID2, &cpuid_data);
+    if (r) {
+        goto fail;
+    }
+
     if (has_xsave) {
         env->kvm_xsave_buf = qemu_memalign(4096, sizeof(struct kvm_xsave));
     }
diff --git a/target/i386/seg_helper.c b/target/i386/seg_helper.c
index fb79f3180d..d24574da7f 100644
--- a/target/i386/seg_helper.c
+++ b/target/i386/seg_helper.c
@@ -1331,6 +1331,7 @@ bool x86_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
 #endif
     if (interrupt_request & CPU_INTERRUPT_SIPI) {
         do_cpu_sipi(cpu);
+        ret = true;
     } else if (env->hflags2 & HF2_GIF_MASK) {
         if ((interrupt_request & CPU_INTERRUPT_SMI) &&
             !(env->hflags & HF_SMM_MASK)) {
diff --git a/target/ppc/Makefile.objs b/target/ppc/Makefile.objs
index e667e69701..a8c7a30cde 100644
--- a/target/ppc/Makefile.objs
+++ b/target/ppc/Makefile.objs
@@ -2,7 +2,7 @@ obj-y += cpu-models.o
 obj-y += translate.o
 ifeq ($(CONFIG_SOFTMMU),y)
 obj-y += machine.o mmu_helper.o mmu-hash32.o monitor.o
-obj-$(TARGET_PPC64) += mmu-hash64.o arch_dump.o
+obj-$(TARGET_PPC64) += mmu-hash64.o arch_dump.o compat.o
 endif
 obj-$(CONFIG_KVM) += kvm.o
 obj-$(call lnot,$(CONFIG_KVM)) += kvm-stub.o
diff --git a/target/ppc/compat.c b/target/ppc/compat.c
new file mode 100644
index 0000000000..458da262be
--- /dev/null
+++ b/target/ppc/compat.c
@@ -0,0 +1,185 @@
+/*
+ *  PowerPC CPU initialization for qemu.
+ *
+ *  Copyright 2016, David Gibson, Red Hat Inc. <dgibson@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 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 "qemu/osdep.h"
+#include "sysemu/hw_accel.h"
+#include "sysemu/kvm.h"
+#include "kvm_ppc.h"
+#include "sysemu/cpus.h"
+#include "qemu/error-report.h"
+#include "qapi/error.h"
+#include "cpu-models.h"
+
+typedef struct {
+    uint32_t pvr;
+    uint64_t pcr;
+    uint64_t pcr_level;
+    int max_threads;
+} CompatInfo;
+
+static const CompatInfo compat_table[] = {
+    /*
+     * Ordered from oldest to newest - the code relies on this
+     */
+    { /* POWER6, ISA2.05 */
+        .pvr = CPU_POWERPC_LOGICAL_2_05,
+        .pcr = PCR_COMPAT_2_07 | PCR_COMPAT_2_06 | PCR_COMPAT_2_05
+               | PCR_TM_DIS | PCR_VSX_DIS,
+        .pcr_level = PCR_COMPAT_2_05,
+        .max_threads = 2,
+    },
+    { /* POWER7, ISA2.06 */
+        .pvr = CPU_POWERPC_LOGICAL_2_06,
+        .pcr = PCR_COMPAT_2_07 | PCR_COMPAT_2_06 | PCR_TM_DIS,
+        .pcr_level = PCR_COMPAT_2_06,
+        .max_threads = 4,
+    },
+    {
+        .pvr = CPU_POWERPC_LOGICAL_2_06_PLUS,
+        .pcr = PCR_COMPAT_2_07 | PCR_COMPAT_2_06 | PCR_TM_DIS,
+        .pcr_level = PCR_COMPAT_2_06,
+        .max_threads = 4,
+    },
+    { /* POWER8, ISA2.07 */
+        .pvr = CPU_POWERPC_LOGICAL_2_07,
+        .pcr = PCR_COMPAT_2_07,
+        .pcr_level = PCR_COMPAT_2_07,
+        .max_threads = 8,
+    },
+};
+
+static const CompatInfo *compat_by_pvr(uint32_t pvr)
+{
+    int i;
+
+    for (i = 0; i < ARRAY_SIZE(compat_table); i++) {
+        if (compat_table[i].pvr == pvr) {
+            return &compat_table[i];
+        }
+    }
+    return NULL;
+}
+
+bool ppc_check_compat(PowerPCCPU *cpu, uint32_t compat_pvr,
+                      uint32_t min_compat_pvr, uint32_t max_compat_pvr)
+{
+    PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu);
+    const CompatInfo *compat = compat_by_pvr(compat_pvr);
+    const CompatInfo *min = compat_by_pvr(min_compat_pvr);
+    const CompatInfo *max = compat_by_pvr(max_compat_pvr);
+
+#if !defined(CONFIG_USER_ONLY)
+    g_assert(cpu->vhyp);
+#endif
+    g_assert(!min_compat_pvr || min);
+    g_assert(!max_compat_pvr || max);
+
+    if (!compat) {
+        /* Not a recognized logical PVR */
+        return false;
+    }
+    if ((min && (compat < min)) || (max && (compat > max))) {
+        /* Outside specified range */
+        return false;
+    }
+    if (!(pcc->pcr_supported & compat->pcr_level)) {
+        /* Not supported by this CPU */
+        return false;
+    }
+    return true;
+}
+
+void ppc_set_compat(PowerPCCPU *cpu, uint32_t compat_pvr, Error **errp)
+{
+    const CompatInfo *compat = compat_by_pvr(compat_pvr);
+    CPUPPCState *env = &cpu->env;
+    PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu);
+    uint64_t pcr;
+
+    if (!compat_pvr) {
+        pcr = 0;
+    } else if (!compat) {
+        error_setg(errp, "Unknown compatibility PVR 0x%08"PRIx32, compat_pvr);
+        return;
+    } else if (!ppc_check_compat(cpu, compat_pvr, 0, 0)) {
+        error_setg(errp, "Compatibility PVR 0x%08"PRIx32" not valid for CPU",
+                   compat_pvr);
+        return;
+    } else {
+        pcr = compat->pcr;
+    }
+
+    cpu_synchronize_state(CPU(cpu));
+
+    cpu->compat_pvr = compat_pvr;
+    env->spr[SPR_PCR] = pcr & pcc->pcr_mask;
+
+    if (kvm_enabled()) {
+        int ret = kvmppc_set_compat(cpu, cpu->compat_pvr);
+        if (ret < 0) {
+            error_setg_errno(errp, -ret,
+                             "Unable to set CPU compatibility mode in KVM");
+        }
+    }
+}
+
+typedef struct {
+    uint32_t compat_pvr;
+    Error *err;
+} SetCompatState;
+
+static void do_set_compat(CPUState *cs, run_on_cpu_data arg)
+{
+    PowerPCCPU *cpu = POWERPC_CPU(cs);
+    SetCompatState *s = arg.host_ptr;
+
+    ppc_set_compat(cpu, s->compat_pvr, &s->err);
+}
+
+void ppc_set_compat_all(uint32_t compat_pvr, Error **errp)
+{
+    CPUState *cs;
+
+    CPU_FOREACH(cs) {
+        SetCompatState s = {
+            .compat_pvr = compat_pvr,
+            .err = NULL,
+        };
+
+        run_on_cpu(cs, do_set_compat, RUN_ON_CPU_HOST_PTR(&s));
+
+        if (s.err) {
+            error_propagate(errp, s.err);
+            return;
+        }
+    }
+}
+
+int ppc_compat_max_threads(PowerPCCPU *cpu)
+{
+    const CompatInfo *compat = compat_by_pvr(cpu->compat_pvr);
+    int n_threads = CPU(cpu)->nr_threads;
+
+    if (cpu->compat_pvr) {
+        g_assert(compat);
+        n_threads = MIN(n_threads, compat->max_threads);
+    }
+
+    return n_threads;
+}
diff --git a/target/ppc/cpu-models.c b/target/ppc/cpu-models.c
index 506dee1ee8..4d3e6354cf 100644
--- a/target/ppc/cpu-models.c
+++ b/target/ppc/cpu-models.c
@@ -1375,19 +1375,15 @@ PowerPCCPUAlias ppc_cpu_aliases[] = {
     { "7445", "7445_v3.2" },
     { "7455", "7455_v3.2" },
     { "Apollo6", "7455" },
-    { "7447", "7447_v1.2" },
+    { "7447", "7447_v1.1" },
     { "7457", "7457_v1.2" },
     { "Apollo7", "7457" },
     { "7447A", "7447A_v1.2" },
     { "7457A", "7457A_v1.2" },
     { "Apollo7PM", "7457A_v1.0" },
 #if defined(TARGET_PPC64)
-    { "Trident", "620" },
     { "POWER3", "630" },
-    { "Boxer", "POWER3" },
-    { "Dino",  "POWER3" },
     { "POWER3+", "631" },
-    { "POWER5gr", "POWER5" },
     { "POWER5+", "POWER5+_v2.1" },
     { "POWER5gs", "POWER5+_v2.1" },
     { "POWER7", "POWER7_v2.3" },
@@ -1399,21 +1395,7 @@ PowerPCCPUAlias ppc_cpu_aliases[] = {
     { "970", "970_v2.2" },
     { "970fx", "970fx_v3.1" },
     { "970mp", "970mp_v1.1" },
-    { "Apache", "RS64" },
-    { "A35",    "RS64" },
-    { "NorthStar", "RS64-II" },
-    { "A50",       "RS64-II" },
-    { "Pulsar", "RS64-III" },
-    { "IceStar", "RS64-IV" },
-    { "IStar",   "RS64-IV" },
-    { "SStar",   "RS64-IV" },
-#endif
-    { "RIOS",    "POWER" },
-    { "RSC",     "POWER" },
-    { "RSC3308", "POWER" },
-    { "RSC4608", "POWER" },
-    { "RSC2", "POWER2" },
-    { "P2SC", "POWER2" },
+#endif
 
     /* Generic PowerPCs */
 #if defined(TARGET_PPC64)
diff --git a/target/ppc/cpu-models.h b/target/ppc/cpu-models.h
index aafbbd7d5d..d587e69bbc 100644
--- a/target/ppc/cpu-models.h
+++ b/target/ppc/cpu-models.h
@@ -601,7 +601,7 @@ enum {
     CPU_POWERPC_LOGICAL_2_06       = 0x0F000003,
     CPU_POWERPC_LOGICAL_2_06_PLUS  = 0x0F100003,
     CPU_POWERPC_LOGICAL_2_07       = 0x0F000004,
-    CPU_POWERPC_LOGICAL_2_08       = 0x0F000005,
+    CPU_POWERPC_LOGICAL_3_00       = 0x0F000005,
 };
 
 /* System version register (used on MPC 8xxx)                                */
diff --git a/target/ppc/cpu-qom.h b/target/ppc/cpu-qom.h
index d46c31a15d..b7977bad18 100644
--- a/target/ppc/cpu-qom.h
+++ b/target/ppc/cpu-qom.h
@@ -214,6 +214,9 @@ extern const struct VMStateDescription vmstate_ppc_timebase;
     .flags      = VMS_STRUCT,                                         \
     .offset     = vmstate_offset_value(_state, _field, PPCTimebase),  \
 }
+
+void cpu_ppc_clock_vm_state_change(void *opaque, int running,
+                                   RunState state);
 #endif
 
 #endif
diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h
index 2a50c43689..bc2a2ce431 100644
--- a/target/ppc/cpu.h
+++ b/target/ppc/cpu.h
@@ -21,6 +21,7 @@
 #define PPC_CPU_H
 
 #include "qemu-common.h"
+#include "qemu/int128.h"
 
 //#define PPC_EMULATE_32BITS_HYPV
 
@@ -262,6 +263,7 @@ union ppc_avr_t {
 #ifdef CONFIG_INT128
     __uint128_t u128;
 #endif
+    Int128 s128;
 };
 
 #if !defined(CONFIG_USER_ONLY)
@@ -1148,12 +1150,15 @@ do {                                            \
     env->wdt_period[3] = (d_);                  \
  } while (0)
 
+typedef struct PPCVirtualHypervisor PPCVirtualHypervisor;
+typedef struct PPCVirtualHypervisorClass PPCVirtualHypervisorClass;
+
 /**
  * PowerPCCPU:
  * @env: #CPUPPCState
  * @cpu_dt_id: CPU index used in the device tree. KVM uses this index too
  * @max_compat: Maximal supported logical PVR from the command line
- * @cpu_version: Current logical PVR, zero if in "raw" mode
+ * @compat_pvr: Current logical PVR, zero if in "raw" mode
  *
  * A PowerPC CPU.
  */
@@ -1165,7 +1170,8 @@ struct PowerPCCPU {
     CPUPPCState env;
     int cpu_dt_id;
     uint32_t max_compat;
-    uint32_t cpu_version;
+    uint32_t compat_pvr;
+    PPCVirtualHypervisor *vhyp;
 
     /* Fields related to migration compatibility hacks */
     bool pre_2_8_migration;
@@ -1187,6 +1193,25 @@ static inline PowerPCCPU *ppc_env_get_cpu(CPUPPCState *env)
 PowerPCCPUClass *ppc_cpu_class_by_pvr(uint32_t pvr);
 PowerPCCPUClass *ppc_cpu_class_by_pvr_mask(uint32_t pvr);
 
+struct PPCVirtualHypervisor {
+    Object parent;
+};
+
+struct PPCVirtualHypervisorClass {
+    InterfaceClass parent;
+    void (*hypercall)(PPCVirtualHypervisor *vhyp, PowerPCCPU *cpu);
+};
+
+#define TYPE_PPC_VIRTUAL_HYPERVISOR "ppc-virtual-hypervisor"
+#define PPC_VIRTUAL_HYPERVISOR(obj)                 \
+    OBJECT_CHECK(PPCVirtualHypervisor, (obj), TYPE_PPC_VIRTUAL_HYPERVISOR)
+#define PPC_VIRTUAL_HYPERVISOR_CLASS(klass)         \
+    OBJECT_CLASS_CHECK(PPCVirtualHypervisorClass, (klass), \
+                       TYPE_PPC_VIRTUAL_HYPERVISOR)
+#define PPC_VIRTUAL_HYPERVISOR_GET_CLASS(obj) \
+    OBJECT_GET_CLASS(PPCVirtualHypervisorClass, (obj), \
+                     TYPE_PPC_VIRTUAL_HYPERVISOR)
+
 void ppc_cpu_do_interrupt(CPUState *cpu);
 bool ppc_cpu_exec_interrupt(CPUState *cpu, int int_req);
 void ppc_cpu_dump_state(CPUState *cpu, FILE *f, fprintf_function cpu_fprintf,
@@ -1225,9 +1250,7 @@ void ppc_store_sdr1 (CPUPPCState *env, target_ulong value);
 void ppc_store_msr (CPUPPCState *env, target_ulong value);
 
 void ppc_cpu_list (FILE *f, fprintf_function cpu_fprintf);
-int ppc_get_compat_smt_threads(PowerPCCPU *cpu);
 #if defined(TARGET_PPC64)
-void ppc_set_compat(PowerPCCPU *cpu, uint32_t cpu_version, Error **errp);
 #endif
 
 /* Time-base and decrementer management */
@@ -1259,6 +1282,7 @@ void store_booke_tcr (CPUPPCState *env, target_ulong val);
 void store_booke_tsr (CPUPPCState *env, target_ulong val);
 void ppc_tlb_invalidate_all (CPUPPCState *env);
 void ppc_tlb_invalidate_one (CPUPPCState *env, target_ulong addr);
+void cpu_ppc_set_vhyp(PowerPCCPU *cpu, PPCVirtualHypervisor *vhyp);
 void cpu_ppc_set_papr(PowerPCCPU *cpu);
 #endif
 #endif
@@ -1297,18 +1321,34 @@ static inline int cpu_mmu_index (CPUPPCState *env, bool ifetch)
     return ifetch ? env->immu_idx : env->dmmu_idx;
 }
 
+/* Compatibility modes */
+#if defined(TARGET_PPC64)
+bool ppc_check_compat(PowerPCCPU *cpu, uint32_t compat_pvr,
+                      uint32_t min_compat_pvr, uint32_t max_compat_pvr);
+void ppc_set_compat(PowerPCCPU *cpu, uint32_t compat_pvr, Error **errp);
+#if !defined(CONFIG_USER_ONLY)
+void ppc_set_compat_all(uint32_t compat_pvr, Error **errp);
+#endif
+int ppc_compat_max_threads(PowerPCCPU *cpu);
+#endif /* defined(TARGET_PPC64) */
+
 #include "exec/cpu-all.h"
 
 /*****************************************************************************/
 /* CRF definitions */
-#define CRF_LT        3
-#define CRF_GT        2
-#define CRF_EQ        1
-#define CRF_SO        0
-#define CRF_CH        (1 << CRF_LT)
-#define CRF_CL        (1 << CRF_GT)
-#define CRF_CH_OR_CL  (1 << CRF_EQ)
-#define CRF_CH_AND_CL (1 << CRF_SO)
+#define CRF_LT_BIT    3
+#define CRF_GT_BIT    2
+#define CRF_EQ_BIT    1
+#define CRF_SO_BIT    0
+#define CRF_LT        (1 << CRF_LT_BIT)
+#define CRF_GT        (1 << CRF_GT_BIT)
+#define CRF_EQ        (1 << CRF_EQ_BIT)
+#define CRF_SO        (1 << CRF_SO_BIT)
+/* For SPE extensions */
+#define CRF_CH        (1 << CRF_LT_BIT)
+#define CRF_CL        (1 << CRF_GT_BIT)
+#define CRF_CH_OR_CL  (1 << CRF_EQ_BIT)
+#define CRF_CH_AND_CL (1 << CRF_SO_BIT)
 
 /* XER definitions */
 #define XER_SO  31
@@ -2250,6 +2290,7 @@ enum {
     PCR_COMPAT_2_05     = 1ull << (63-62),
     PCR_COMPAT_2_06     = 1ull << (63-61),
     PCR_COMPAT_2_07     = 1ull << (63-60),
+    PCR_COMPAT_3_00     = 1ull << (63-59),
     PCR_VEC_DIS         = 1ull << (63-0), /* Vec. disable (bit NA since POWER8) */
     PCR_VSX_DIS         = 1ull << (63-1), /* VSX disable (bit NA since POWER8) */
     PCR_TM_DIS          = 1ull << (63-2), /* Trans. memory disable (POWER8) */
@@ -2428,8 +2469,6 @@ static inline bool lsw_reg_in_range(int start, int nregs, int rx)
            (start + nregs > 32 && (rx >= start || rx < start + nregs - 32));
 }
 
-extern void (*cpu_ppc_hypercall)(PowerPCCPU *);
-
 void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUPPCState *env);
 
 /**
diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c
index 93369d4fe5..f4ee7aacd2 100644
--- a/target/ppc/excp_helper.c
+++ b/target/ppc/excp_helper.c
@@ -35,11 +35,6 @@
 #endif
 
 /*****************************************************************************/
-/* PowerPC Hypercall emulation */
-
-void (*cpu_ppc_hypercall)(PowerPCCPU *);
-
-/*****************************************************************************/
 /* Exception processing */
 #if defined(CONFIG_USER_ONLY)
 void ppc_cpu_do_interrupt(CPUState *cs)
@@ -318,8 +313,10 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
         env->nip += 4;
 
         /* "PAPR mode" built-in hypercall emulation */
-        if ((lev == 1) && cpu_ppc_hypercall) {
-            cpu_ppc_hypercall(cpu);
+        if ((lev == 1) && cpu->vhyp) {
+            PPCVirtualHypervisorClass *vhc =
+                PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp);
+            vhc->hypercall(cpu->vhyp, cpu);
             return;
         }
         if (lev == 1) {
diff --git a/target/ppc/fpu_helper.c b/target/ppc/fpu_helper.c
index 8a389e19af..9f5cafd5ba 100644
--- a/target/ppc/fpu_helper.c
+++ b/target/ppc/fpu_helper.c
@@ -20,9 +20,20 @@
 #include "cpu.h"
 #include "exec/helper-proto.h"
 #include "exec/exec-all.h"
+#include "internal.h"
+
+static inline float128 float128_snan_to_qnan(float128 x)
+{
+    float128 r;
+
+    r.high = x.high | 0x0000800000000000;
+    r.low = x.low;
+    return r;
+}
 
 #define float64_snan_to_qnan(x) ((x) | 0x0008000000000000ULL)
 #define float32_snan_to_qnan(x) ((x) | 0x00400000)
+#define float16_snan_to_qnan(x) ((x) | 0x0200)
 
 /*****************************************************************************/
 /* Floating point operations helpers */
@@ -46,15 +57,6 @@ uint32_t helper_float64_to_float32(CPUPPCState *env, uint64_t arg)
     return f.l;
 }
 
-static inline int isden(float64 d)
-{
-    CPU_DoubleU u;
-
-    u.d = d;
-
-    return ((u.ll >> 52) & 0x7FF) == 0;
-}
-
 static inline int ppc_float32_get_unbiased_exp(float32 f)
 {
     return ((f >> 23) & 0xFF) - 127;
@@ -65,56 +67,60 @@ static inline int ppc_float64_get_unbiased_exp(float64 f)
     return ((f >> 52) & 0x7FF) - 1023;
 }
 
-void helper_compute_fprf(CPUPPCState *env, uint64_t arg)
-{
-    CPU_DoubleU farg;
-    int isneg;
-    int fprf;
-
-    farg.ll = arg;
-    isneg = float64_is_neg(farg.d);
-    if (unlikely(float64_is_any_nan(farg.d))) {
-        if (float64_is_signaling_nan(farg.d, &env->fp_status)) {
-            /* Signaling NaN: flags are undefined */
-            fprf = 0x00;
-        } else {
-            /* Quiet NaN */
-            fprf = 0x11;
-        }
-    } else if (unlikely(float64_is_infinity(farg.d))) {
-        /* +/- infinity */
-        if (isneg) {
-            fprf = 0x09;
-        } else {
-            fprf = 0x05;
-        }
-    } else {
-        if (float64_is_zero(farg.d)) {
-            /* +/- zero */
-            if (isneg) {
-                fprf = 0x12;
-            } else {
-                fprf = 0x02;
-            }
-        } else {
-            if (isden(farg.d)) {
-                /* Denormalized numbers */
-                fprf = 0x10;
-            } else {
-                /* Normalized numbers */
-                fprf = 0x00;
-            }
-            if (isneg) {
-                fprf |= 0x08;
-            } else {
-                fprf |= 0x04;
-            }
-        }
-    }
-    /* We update FPSCR_FPRF */
-    env->fpscr &= ~(0x1F << FPSCR_FPRF);
-    env->fpscr |= fprf << FPSCR_FPRF;
-}
+#define COMPUTE_FPRF(tp)                                       \
+void helper_compute_fprf_##tp(CPUPPCState *env, tp arg)        \
+{                                                              \
+    int isneg;                                                 \
+    int fprf;                                                  \
+                                                               \
+    isneg = tp##_is_neg(arg);                                  \
+    if (unlikely(tp##_is_any_nan(arg))) {                      \
+        if (tp##_is_signaling_nan(arg, &env->fp_status)) {     \
+            /* Signaling NaN: flags are undefined */           \
+            fprf = 0x00;                                       \
+        } else {                                               \
+            /* Quiet NaN */                                    \
+            fprf = 0x11;                                       \
+        }                                                      \
+    } else if (unlikely(tp##_is_infinity(arg))) {              \
+        /* +/- infinity */                                     \
+        if (isneg) {                                           \
+            fprf = 0x09;                                       \
+        } else {                                               \
+            fprf = 0x05;                                       \
+        }                                                      \
+    } else {                                                   \
+        if (tp##_is_zero(arg)) {                               \
+            /* +/- zero */                                     \
+            if (isneg) {                                       \
+                fprf = 0x12;                                   \
+            } else {                                           \
+                fprf = 0x02;                                   \
+            }                                                  \
+        } else {                                               \
+            if (tp##_is_zero_or_denormal(arg)) {               \
+                /* Denormalized numbers */                     \
+                fprf = 0x10;                                   \
+            } else {                                           \
+                /* Normalized numbers */                       \
+                fprf = 0x00;                                   \
+            }                                                  \
+            if (isneg) {                                       \
+                fprf |= 0x08;                                  \
+            } else {                                           \
+                fprf |= 0x04;                                  \
+            }                                                  \
+        }                                                      \
+    }                                                          \
+    /* We update FPSCR_FPRF */                                 \
+    env->fpscr &= ~(0x1F << FPSCR_FPRF);                       \
+    env->fpscr |= fprf << FPSCR_FPRF;                          \
+}
+
+COMPUTE_FPRF(float16)
+COMPUTE_FPRF(float32)
+COMPUTE_FPRF(float64)
+COMPUTE_FPRF(float128)
 
 /* Floating-point invalid operations exception */
 static inline __attribute__((__always_inline__))
@@ -1776,53 +1782,6 @@ uint32_t helper_efdcmpeq(CPUPPCState *env, uint64_t op1, uint64_t op2)
     return helper_efdtsteq(env, op1, op2);
 }
 
-#define DECODE_SPLIT(opcode, shift1, nb1, shift2, nb2) \
-    (((((opcode) >> (shift1)) & ((1 << (nb1)) - 1)) << nb2) |    \
-     (((opcode) >> (shift2)) & ((1 << (nb2)) - 1)))
-
-#define xT(opcode) DECODE_SPLIT(opcode, 0, 1, 21, 5)
-#define xA(opcode) DECODE_SPLIT(opcode, 2, 1, 16, 5)
-#define xB(opcode) DECODE_SPLIT(opcode, 1, 1, 11, 5)
-#define xC(opcode) DECODE_SPLIT(opcode, 3, 1,  6, 5)
-#define BF(opcode) (((opcode) >> (31-8)) & 7)
-
-typedef union _ppc_vsr_t {
-    uint64_t u64[2];
-    uint32_t u32[4];
-    float32 f32[4];
-    float64 f64[2];
-} ppc_vsr_t;
-
-#if defined(HOST_WORDS_BIGENDIAN)
-#define VsrW(i) u32[i]
-#define VsrD(i) u64[i]
-#else
-#define VsrW(i) u32[3-(i)]
-#define VsrD(i) u64[1-(i)]
-#endif
-
-static void getVSR(int n, ppc_vsr_t *vsr, CPUPPCState *env)
-{
-    if (n < 32) {
-        vsr->VsrD(0) = env->fpr[n];
-        vsr->VsrD(1) = env->vsr[n];
-    } else {
-        vsr->u64[0] = env->avr[n-32].u64[0];
-        vsr->u64[1] = env->avr[n-32].u64[1];
-    }
-}
-
-static void putVSR(int n, ppc_vsr_t *vsr, CPUPPCState *env)
-{
-    if (n < 32) {
-        env->fpr[n] = vsr->VsrD(0);
-        env->vsr[n] = vsr->VsrD(1);
-    } else {
-        env->avr[n-32].u64[0] = vsr->u64[0];
-        env->avr[n-32].u64[1] = vsr->u64[1];
-    }
-}
-
 #define float64_to_float64(x, env) x
 
 
@@ -1865,7 +1824,7 @@ void helper_##name(CPUPPCState *env, uint32_t opcode)                        \
         }                                                                    \
                                                                              \
         if (sfprf) {                                                         \
-            helper_compute_fprf(env, xt.fld);                                \
+            helper_compute_fprf_float64(env, xt.fld);                        \
         }                                                                    \
     }                                                                        \
     putVSR(xT(opcode), &xt, env);                                            \
@@ -1881,6 +1840,41 @@ VSX_ADD_SUB(xssubsp, sub, 1, float64, VsrD(0), 1, 1)
 VSX_ADD_SUB(xvsubdp, sub, 2, float64, VsrD(i), 0, 0)
 VSX_ADD_SUB(xvsubsp, sub, 4, float32, VsrW(i), 0, 0)
 
+void helper_xsaddqp(CPUPPCState *env, uint32_t opcode)
+{
+    ppc_vsr_t xt, xa, xb;
+    float_status tstat;
+
+    getVSR(rA(opcode) + 32, &xa, env);
+    getVSR(rB(opcode) + 32, &xb, env);
+    getVSR(rD(opcode) + 32, &xt, env);
+    helper_reset_fpstatus(env);
+
+    if (unlikely(Rc(opcode) != 0)) {
+        /* TODO: Support xsadddpo after round-to-odd is implemented */
+        abort();
+    }
+
+    tstat = env->fp_status;
+    set_float_exception_flags(0, &tstat);
+    xt.f128 = float128_add(xa.f128, xb.f128, &tstat);
+    env->fp_status.float_exception_flags |= tstat.float_exception_flags;
+
+    if (unlikely(tstat.float_exception_flags & float_flag_invalid)) {
+        if (float128_is_infinity(xa.f128) && float128_is_infinity(xb.f128)) {
+            float_invalid_op_excp(env, POWERPC_EXCP_FP_VXISI, 1);
+        } else if (float128_is_signaling_nan(xa.f128, &tstat) ||
+                   float128_is_signaling_nan(xb.f128, &tstat)) {
+            float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1);
+        }
+    }
+
+    helper_compute_fprf_float128(env, xt.f128);
+
+    putVSR(rD(opcode) + 32, &xt, env);
+    float_check_status(env);
+}
+
 /* VSX_MUL - VSX floating point multiply
  *   op    - instruction mnemonic
  *   nels  - number of elements (1, 2 or 4)
@@ -1920,7 +1914,7 @@ void helper_##op(CPUPPCState *env, uint32_t opcode)                          \
         }                                                                    \
                                                                              \
         if (sfprf) {                                                         \
-            helper_compute_fprf(env, xt.fld);                                \
+            helper_compute_fprf_float64(env, xt.fld);                        \
         }                                                                    \
     }                                                                        \
                                                                              \
@@ -1933,6 +1927,41 @@ VSX_MUL(xsmulsp, 1, float64, VsrD(0), 1, 1)
 VSX_MUL(xvmuldp, 2, float64, VsrD(i), 0, 0)
 VSX_MUL(xvmulsp, 4, float32, VsrW(i), 0, 0)
 
+void helper_xsmulqp(CPUPPCState *env, uint32_t opcode)
+{
+    ppc_vsr_t xt, xa, xb;
+
+    getVSR(rA(opcode) + 32, &xa, env);
+    getVSR(rB(opcode) + 32, &xb, env);
+    getVSR(rD(opcode) + 32, &xt, env);
+
+    if (unlikely(Rc(opcode) != 0)) {
+        /* TODO: Support xsmulpo after round-to-odd is implemented */
+        abort();
+    }
+
+    helper_reset_fpstatus(env);
+
+    float_status tstat = env->fp_status;
+    set_float_exception_flags(0, &tstat);
+    xt.f128 = float128_mul(xa.f128, xb.f128, &tstat);
+    env->fp_status.float_exception_flags |= tstat.float_exception_flags;
+
+    if (unlikely(tstat.float_exception_flags & float_flag_invalid)) {
+        if ((float128_is_infinity(xa.f128) && float128_is_zero(xb.f128)) ||
+            (float128_is_infinity(xb.f128) && float128_is_zero(xa.f128))) {
+            float_invalid_op_excp(env, POWERPC_EXCP_FP_VXIMZ, 1);
+        } else if (float128_is_signaling_nan(xa.f128, &tstat) ||
+                   float128_is_signaling_nan(xb.f128, &tstat)) {
+            float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1);
+        }
+    }
+    helper_compute_fprf_float128(env, xt.f128);
+
+    putVSR(rD(opcode) + 32, &xt, env);
+    float_check_status(env);
+}
+
 /* VSX_DIV - VSX floating point divide
  *   op    - instruction mnemonic
  *   nels  - number of elements (1, 2 or 4)
@@ -1974,7 +2003,7 @@ void helper_##op(CPUPPCState *env, uint32_t opcode)                           \
         }                                                                     \
                                                                               \
         if (sfprf) {                                                          \
-            helper_compute_fprf(env, xt.fld);                                 \
+            helper_compute_fprf_float64(env, xt.fld);                         \
         }                                                                     \
     }                                                                         \
                                                                               \
@@ -1987,6 +2016,42 @@ VSX_DIV(xsdivsp, 1, float64, VsrD(0), 1, 1)
 VSX_DIV(xvdivdp, 2, float64, VsrD(i), 0, 0)
 VSX_DIV(xvdivsp, 4, float32, VsrW(i), 0, 0)
 
+void helper_xsdivqp(CPUPPCState *env, uint32_t opcode)
+{
+    ppc_vsr_t xt, xa, xb;
+
+    getVSR(rA(opcode) + 32, &xa, env);
+    getVSR(rB(opcode) + 32, &xb, env);
+    getVSR(rD(opcode) + 32, &xt, env);
+
+    if (unlikely(Rc(opcode) != 0)) {
+        /* TODO: Support xsdivqpo after round-to-odd is implemented */
+        abort();
+    }
+
+    helper_reset_fpstatus(env);
+    float_status tstat = env->fp_status;
+    set_float_exception_flags(0, &tstat);
+    xt.f128 = float128_div(xa.f128, xb.f128, &tstat);
+    env->fp_status.float_exception_flags |= tstat.float_exception_flags;
+
+    if (unlikely(tstat.float_exception_flags & float_flag_invalid)) {
+        if (float128_is_infinity(xa.f128) && float128_is_infinity(xb.f128)) {
+            float_invalid_op_excp(env, POWERPC_EXCP_FP_VXIDI, 1);
+        } else if (float128_is_zero(xa.f128) &&
+            float128_is_zero(xb.f128)) {
+            float_invalid_op_excp(env, POWERPC_EXCP_FP_VXZDZ, 1);
+        } else if (float128_is_signaling_nan(xa.f128, &tstat) ||
+            float128_is_signaling_nan(xb.f128, &tstat)) {
+            float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1);
+        }
+    }
+
+    helper_compute_fprf_float128(env, xt.f128);
+    putVSR(rD(opcode) + 32, &xt, env);
+    float_check_status(env);
+}
+
 /* VSX_RE  - VSX floating point reciprocal estimate
  *   op    - instruction mnemonic
  *   nels  - number of elements (1, 2 or 4)
@@ -2015,7 +2080,7 @@ void helper_##op(CPUPPCState *env, uint32_t opcode)                           \
         }                                                                     \
                                                                               \
         if (sfprf) {                                                          \
-            helper_compute_fprf(env, xt.fld);                                 \
+            helper_compute_fprf_float64(env, xt.fld);                         \
         }                                                                     \
     }                                                                         \
                                                                               \
@@ -2064,7 +2129,7 @@ void helper_##op(CPUPPCState *env, uint32_t opcode)                          \
         }                                                                    \
                                                                              \
         if (sfprf) {                                                         \
-            helper_compute_fprf(env, xt.fld);                                \
+            helper_compute_fprf_float64(env, xt.fld);                        \
         }                                                                    \
     }                                                                        \
                                                                              \
@@ -2114,7 +2179,7 @@ void helper_##op(CPUPPCState *env, uint32_t opcode)                          \
         }                                                                    \
                                                                              \
         if (sfprf) {                                                         \
-            helper_compute_fprf(env, xt.fld);                                \
+            helper_compute_fprf_float64(env, xt.fld);                        \
         }                                                                    \
     }                                                                        \
                                                                              \
@@ -2314,7 +2379,7 @@ void helper_##op(CPUPPCState *env, uint32_t opcode)                           \
         }                                                                     \
                                                                               \
         if (sfprf) {                                                          \
-            helper_compute_fprf(env, xt_out.fld);                             \
+            helper_compute_fprf_float64(env, xt_out.fld);                     \
         }                                                                     \
     }                                                                         \
     putVSR(xT(opcode), &xt_out, env);                                         \
@@ -2414,34 +2479,108 @@ VSX_SCALAR_CMP_DP(xscmpgedp, le, 1, 1)
 VSX_SCALAR_CMP_DP(xscmpgtdp, lt, 1, 1)
 VSX_SCALAR_CMP_DP(xscmpnedp, eq, 0, 0)
 
+void helper_xscmpexpdp(CPUPPCState *env, uint32_t opcode)
+{
+    ppc_vsr_t xa, xb;
+    int64_t exp_a, exp_b;
+    uint32_t cc;
+
+    getVSR(xA(opcode), &xa, env);
+    getVSR(xB(opcode), &xb, env);
+
+    exp_a = extract64(xa.VsrD(0), 52, 11);
+    exp_b = extract64(xb.VsrD(0), 52, 11);
+
+    if (unlikely(float64_is_any_nan(xa.VsrD(0)) ||
+                 float64_is_any_nan(xb.VsrD(0)))) {
+        cc = CRF_SO;
+    } else {
+        if (exp_a < exp_b) {
+            cc = CRF_LT;
+        } else if (exp_a > exp_b) {
+            cc = CRF_GT;
+        } else {
+            cc = CRF_EQ;
+        }
+    }
+
+    env->fpscr &= ~(0x0F << FPSCR_FPRF);
+    env->fpscr |= cc << FPSCR_FPRF;
+    env->crf[BF(opcode)] = cc;
+
+    helper_float_check_status(env);
+}
+
+void helper_xscmpexpqp(CPUPPCState *env, uint32_t opcode)
+{
+    ppc_vsr_t xa, xb;
+    int64_t exp_a, exp_b;
+    uint32_t cc;
+
+    getVSR(rA(opcode) + 32, &xa, env);
+    getVSR(rB(opcode) + 32, &xb, env);
+
+    exp_a = extract64(xa.VsrD(0), 48, 15);
+    exp_b = extract64(xb.VsrD(0), 48, 15);
+
+    if (unlikely(float128_is_any_nan(xa.f128) ||
+                 float128_is_any_nan(xb.f128))) {
+        cc = CRF_SO;
+    } else {
+        if (exp_a < exp_b) {
+            cc = CRF_LT;
+        } else if (exp_a > exp_b) {
+            cc = CRF_GT;
+        } else {
+            cc = CRF_EQ;
+        }
+    }
+
+    env->fpscr &= ~(0x0F << FPSCR_FPRF);
+    env->fpscr |= cc << FPSCR_FPRF;
+    env->crf[BF(opcode)] = cc;
+
+    helper_float_check_status(env);
+}
+
 #define VSX_SCALAR_CMP(op, ordered)                                      \
 void helper_##op(CPUPPCState *env, uint32_t opcode)                      \
 {                                                                        \
     ppc_vsr_t xa, xb;                                                    \
     uint32_t cc = 0;                                                     \
+    bool vxsnan_flag = false, vxvc_flag = false;                         \
                                                                          \
+    helper_reset_fpstatus(env);                                          \
     getVSR(xA(opcode), &xa, env);                                        \
     getVSR(xB(opcode), &xb, env);                                        \
                                                                          \
-    if (unlikely(float64_is_any_nan(xa.VsrD(0)) ||                       \
-                 float64_is_any_nan(xb.VsrD(0)))) {                      \
-        if (float64_is_signaling_nan(xa.VsrD(0), &env->fp_status) ||     \
-            float64_is_signaling_nan(xb.VsrD(0), &env->fp_status)) {     \
-            float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 0);       \
+    if (float64_is_signaling_nan(xa.VsrD(0), &env->fp_status) ||         \
+        float64_is_signaling_nan(xb.VsrD(0), &env->fp_status)) {         \
+        vxsnan_flag = true;                                              \
+        cc = CRF_SO;                                                     \
+        if (fpscr_ve == 0 && ordered) {                                  \
+            vxvc_flag = true;                                            \
         }                                                                \
+    } else if (float64_is_quiet_nan(xa.VsrD(0), &env->fp_status) ||      \
+               float64_is_quiet_nan(xb.VsrD(0), &env->fp_status)) {      \
+        cc = CRF_SO;                                                     \
         if (ordered) {                                                   \
-            float_invalid_op_excp(env, POWERPC_EXCP_FP_VXVC, 0);         \
+            vxvc_flag = true;                                            \
         }                                                                \
-        cc = 1;                                                          \
+    }                                                                    \
+    if (vxsnan_flag) {                                                   \
+        float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 0);           \
+    }                                                                    \
+    if (vxvc_flag) {                                                     \
+        float_invalid_op_excp(env, POWERPC_EXCP_FP_VXVC, 0);             \
+    }                                                                    \
+                                                                         \
+    if (float64_lt(xa.VsrD(0), xb.VsrD(0), &env->fp_status)) {           \
+        cc |= CRF_LT;                                                    \
+    } else if (!float64_le(xa.VsrD(0), xb.VsrD(0), &env->fp_status)) {   \
+        cc |= CRF_GT;                                                    \
     } else {                                                             \
-        if (float64_lt(xa.VsrD(0), xb.VsrD(0), &env->fp_status)) {       \
-            cc = 8;                                                      \
-        } else if (!float64_le(xa.VsrD(0), xb.VsrD(0),                   \
-                               &env->fp_status)) { \
-            cc = 4;                                                      \
-        } else {                                                         \
-            cc = 2;                                                      \
-        }                                                                \
+        cc |= CRF_EQ;                                                    \
     }                                                                    \
                                                                          \
     env->fpscr &= ~(0x0F << FPSCR_FPRF);                                 \
@@ -2454,6 +2593,56 @@ void helper_##op(CPUPPCState *env, uint32_t opcode)                      \
 VSX_SCALAR_CMP(xscmpodp, 1)
 VSX_SCALAR_CMP(xscmpudp, 0)
 
+#define VSX_SCALAR_CMPQ(op, ordered)                                    \
+void helper_##op(CPUPPCState *env, uint32_t opcode)                     \
+{                                                                       \
+    ppc_vsr_t xa, xb;                                                   \
+    uint32_t cc = 0;                                                    \
+    bool vxsnan_flag = false, vxvc_flag = false;                        \
+                                                                        \
+    helper_reset_fpstatus(env);                                         \
+    getVSR(rA(opcode) + 32, &xa, env);                                  \
+    getVSR(rB(opcode) + 32, &xb, env);                                  \
+                                                                        \
+    if (float128_is_signaling_nan(xa.f128, &env->fp_status) ||          \
+        float128_is_signaling_nan(xb.f128, &env->fp_status)) {          \
+        vxsnan_flag = true;                                             \
+        cc = CRF_SO;                                                    \
+        if (fpscr_ve == 0 && ordered) {                                 \
+            vxvc_flag = true;                                           \
+        }                                                               \
+    } else if (float128_is_quiet_nan(xa.f128, &env->fp_status) ||       \
+               float128_is_quiet_nan(xb.f128, &env->fp_status)) {       \
+        cc = CRF_SO;                                                    \
+        if (ordered) {                                                  \
+            vxvc_flag = true;                                           \
+        }                                                               \
+    }                                                                   \
+    if (vxsnan_flag) {                                                  \
+        float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 0);          \
+    }                                                                   \
+    if (vxvc_flag) {                                                    \
+        float_invalid_op_excp(env, POWERPC_EXCP_FP_VXVC, 0);            \
+    }                                                                   \
+                                                                        \
+    if (float128_lt(xa.f128, xb.f128, &env->fp_status)) {               \
+        cc |= CRF_LT;                                                   \
+    } else if (!float128_le(xa.f128, xb.f128, &env->fp_status)) {       \
+        cc |= CRF_GT;                                                   \
+    } else {                                                            \
+        cc |= CRF_EQ;                                                   \
+    }                                                                   \
+                                                                        \
+    env->fpscr &= ~(0x0F << FPSCR_FPRF);                                \
+    env->fpscr |= cc << FPSCR_FPRF;                                     \
+    env->crf[BF(opcode)] = cc;                                          \
+                                                                        \
+    float_check_status(env);                                            \
+}
+
+VSX_SCALAR_CMPQ(xscmpoqp, 1)
+VSX_SCALAR_CMPQ(xscmpuqp, 0)
+
 /* VSX_MAX_MIN - VSX floating point maximum/minimum
  *   name  - instruction mnemonic
  *   op    - operation (max or min)
@@ -2576,8 +2765,7 @@ void helper_##op(CPUPPCState *env, uint32_t opcode)                \
             xt.tfld = ttp##_snan_to_qnan(xt.tfld);                 \
         }                                                          \
         if (sfprf) {                                               \
-            helper_compute_fprf(env, ttp##_to_float64(xt.tfld,     \
-                                &env->fp_status));                 \
+            helper_compute_fprf_##ttp(env, xt.tfld);               \
         }                                                          \
     }                                                              \
                                                                    \
@@ -2590,6 +2778,110 @@ VSX_CVT_FP_TO_FP(xscvspdp, 1, float32, float64, VsrW(0), VsrD(0), 1)
 VSX_CVT_FP_TO_FP(xvcvdpsp, 2, float64, float32, VsrD(i), VsrW(2*i), 0)
 VSX_CVT_FP_TO_FP(xvcvspdp, 2, float32, float64, VsrW(2*i), VsrD(i), 0)
 
+/* VSX_CVT_FP_TO_FP_VECTOR - VSX floating point/floating point conversion
+ *   op    - instruction mnemonic
+ *   nels  - number of elements (1, 2 or 4)
+ *   stp   - source type (float32 or float64)
+ *   ttp   - target type (float32 or float64)
+ *   sfld  - source vsr_t field
+ *   tfld  - target vsr_t field (f32 or f64)
+ *   sfprf - set FPRF
+ */
+#define VSX_CVT_FP_TO_FP_VECTOR(op, nels, stp, ttp, sfld, tfld, sfprf)    \
+void helper_##op(CPUPPCState *env, uint32_t opcode)                       \
+{                                                                       \
+    ppc_vsr_t xt, xb;                                                   \
+    int i;                                                              \
+                                                                        \
+    getVSR(rB(opcode) + 32, &xb, env);                                  \
+    getVSR(rD(opcode) + 32, &xt, env);                                  \
+                                                                        \
+    for (i = 0; i < nels; i++) {                                        \
+        xt.tfld = stp##_to_##ttp(xb.sfld, &env->fp_status);             \
+        if (unlikely(stp##_is_signaling_nan(xb.sfld,                    \
+                                            &env->fp_status))) {        \
+            float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 0);      \
+            xt.tfld = ttp##_snan_to_qnan(xt.tfld);                      \
+        }                                                               \
+        if (sfprf) {                                                    \
+            helper_compute_fprf_##ttp(env, xt.tfld);                    \
+        }                                                               \
+    }                                                                   \
+                                                                        \
+    putVSR(rD(opcode) + 32, &xt, env);                                  \
+    float_check_status(env);                                            \
+}
+
+VSX_CVT_FP_TO_FP_VECTOR(xscvdpqp, 1, float64, float128, VsrD(0), f128, 1)
+
+/* VSX_CVT_FP_TO_FP_HP - VSX floating point/floating point conversion
+ *                       involving one half precision value
+ *   op    - instruction mnemonic
+ *   nels  - number of elements (1, 2 or 4)
+ *   stp   - source type
+ *   ttp   - target type
+ *   sfld  - source vsr_t field
+ *   tfld  - target vsr_t field
+ *   sfprf - set FPRF
+ */
+#define VSX_CVT_FP_TO_FP_HP(op, nels, stp, ttp, sfld, tfld, sfprf) \
+void helper_##op(CPUPPCState *env, uint32_t opcode)                \
+{                                                                  \
+    ppc_vsr_t xt, xb;                                              \
+    int i;                                                         \
+                                                                   \
+    getVSR(xB(opcode), &xb, env);                                  \
+    memset(&xt, 0, sizeof(xt));                                    \
+                                                                   \
+    for (i = 0; i < nels; i++) {                                   \
+        xt.tfld = stp##_to_##ttp(xb.sfld, 1, &env->fp_status);     \
+        if (unlikely(stp##_is_signaling_nan(xb.sfld,               \
+                                            &env->fp_status))) {   \
+            float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 0); \
+            xt.tfld = ttp##_snan_to_qnan(xt.tfld);                 \
+        }                                                          \
+        if (sfprf) {                                               \
+            helper_compute_fprf_##ttp(env, xt.tfld);               \
+        }                                                          \
+    }                                                              \
+                                                                   \
+    putVSR(xT(opcode), &xt, env);                                  \
+    float_check_status(env);                                       \
+}
+
+VSX_CVT_FP_TO_FP_HP(xscvdphp, 1, float64, float16, VsrD(0), VsrH(3), 1)
+VSX_CVT_FP_TO_FP_HP(xscvhpdp, 1, float16, float64, VsrH(3), VsrD(0), 1)
+VSX_CVT_FP_TO_FP_HP(xvcvsphp, 4, float32, float16, VsrW(i), VsrH(2 * i  + 1), 0)
+VSX_CVT_FP_TO_FP_HP(xvcvhpsp, 4, float16, float32, VsrH(2 * i + 1), VsrW(i), 0)
+
+/*
+ * xscvqpdp isn't using VSX_CVT_FP_TO_FP() because xscvqpdpo will be
+ * added to this later.
+ */
+void helper_xscvqpdp(CPUPPCState *env, uint32_t opcode)
+{
+    ppc_vsr_t xt, xb;
+
+    getVSR(rB(opcode) + 32, &xb, env);
+    memset(&xt, 0, sizeof(xt));
+
+    if (unlikely(Rc(opcode) != 0)) {
+        /* TODO: Support xscvqpdpo after round-to-odd is implemented */
+        abort();
+    }
+
+    xt.VsrD(0) = float128_to_float64(xb.f128, &env->fp_status);
+    if (unlikely(float128_is_signaling_nan(xb.f128,
+                                           &env->fp_status))) {
+        float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 0);
+        xt.VsrD(0) = float64_snan_to_qnan(xt.VsrD(0));
+    }
+    helper_compute_fprf_float64(env, xt.VsrD(0));
+
+    putVSR(rD(opcode) + 32, &xt, env);
+    float_check_status(env);
+}
+
 uint64_t helper_xscvdpspn(CPUPPCState *env, uint64_t xb)
 {
     float_status tstat = env->fp_status;
@@ -2662,6 +2954,46 @@ VSX_CVT_FP_TO_INT(xvcvspsxws, 4, float32, int32, VsrW(i), VsrW(i), 0x80000000U)
 VSX_CVT_FP_TO_INT(xvcvspuxds, 2, float32, uint64, VsrW(2*i), VsrD(i), 0ULL)
 VSX_CVT_FP_TO_INT(xvcvspuxws, 4, float32, uint32, VsrW(i), VsrW(i), 0U)
 
+/* VSX_CVT_FP_TO_INT_VECTOR - VSX floating point to integer conversion
+ *   op    - instruction mnemonic
+ *   stp   - source type (float32 or float64)
+ *   ttp   - target type (int32, uint32, int64 or uint64)
+ *   sfld  - source vsr_t field
+ *   tfld  - target vsr_t field
+ *   rnan  - resulting NaN
+ */
+#define VSX_CVT_FP_TO_INT_VECTOR(op, stp, ttp, sfld, tfld, rnan)             \
+void helper_##op(CPUPPCState *env, uint32_t opcode)                          \
+{                                                                            \
+    ppc_vsr_t xt, xb;                                                        \
+                                                                             \
+    getVSR(rB(opcode) + 32, &xb, env);                                       \
+    memset(&xt, 0, sizeof(xt));                                              \
+                                                                             \
+    if (unlikely(stp##_is_any_nan(xb.sfld))) {                               \
+        if (stp##_is_signaling_nan(xb.sfld, &env->fp_status)) {              \
+            float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 0);           \
+        }                                                                    \
+        float_invalid_op_excp(env, POWERPC_EXCP_FP_VXCVI, 0);                \
+        xt.tfld = rnan;                                                      \
+    } else {                                                                 \
+        xt.tfld = stp##_to_##ttp##_round_to_zero(xb.sfld,                    \
+                      &env->fp_status);                                      \
+        if (env->fp_status.float_exception_flags & float_flag_invalid) {     \
+            float_invalid_op_excp(env, POWERPC_EXCP_FP_VXCVI, 0);            \
+        }                                                                    \
+    }                                                                        \
+                                                                             \
+    putVSR(rD(opcode) + 32, &xt, env);                                       \
+    float_check_status(env);                                                 \
+}
+
+VSX_CVT_FP_TO_INT_VECTOR(xscvqpsdz, float128, int64, f128, VsrD(0),          \
+                  0x8000000000000000ULL)
+
+VSX_CVT_FP_TO_INT_VECTOR(xscvqpswz, float128, int32, f128, VsrD(0),          \
+                  0xffffffff80000000ULL)
+
 /* VSX_CVT_INT_TO_FP - VSX integer to floating point conversion
  *   op    - instruction mnemonic
  *   nels  - number of elements (1, 2 or 4)
@@ -2687,7 +3019,7 @@ void helper_##op(CPUPPCState *env, uint32_t opcode)                     \
             xt.tfld = helper_frsp(env, xt.tfld);                        \
         }                                                               \
         if (sfprf) {                                                    \
-            helper_compute_fprf(env, xt.tfld);                          \
+            helper_compute_fprf_float64(env, xt.tfld);                  \
         }                                                               \
     }                                                                   \
                                                                         \
@@ -2708,6 +3040,31 @@ VSX_CVT_INT_TO_FP(xvcvuxdsp, 2, uint64, float32, VsrD(i), VsrW(2*i), 0, 0)
 VSX_CVT_INT_TO_FP(xvcvsxwsp, 4, int32, float32, VsrW(i), VsrW(i), 0, 0)
 VSX_CVT_INT_TO_FP(xvcvuxwsp, 4, uint32, float32, VsrW(i), VsrW(i), 0, 0)
 
+/* VSX_CVT_INT_TO_FP_VECTOR - VSX integer to floating point conversion
+ *   op    - instruction mnemonic
+ *   stp   - source type (int32, uint32, int64 or uint64)
+ *   ttp   - target type (float32 or float64)
+ *   sfld  - source vsr_t field
+ *   tfld  - target vsr_t field
+ */
+#define VSX_CVT_INT_TO_FP_VECTOR(op, stp, ttp, sfld, tfld)              \
+void helper_##op(CPUPPCState *env, uint32_t opcode)                     \
+{                                                                       \
+    ppc_vsr_t xt, xb;                                                   \
+                                                                        \
+    getVSR(rB(opcode) + 32, &xb, env);                                  \
+    getVSR(rD(opcode) + 32, &xt, env);                                  \
+                                                                        \
+    xt.tfld = stp##_to_##ttp(xb.sfld, &env->fp_status);                 \
+    helper_compute_fprf_##ttp(env, xt.tfld);                            \
+                                                                        \
+    putVSR(xT(opcode) + 32, &xt, env);                                  \
+    float_check_status(env);                                            \
+}
+
+VSX_CVT_INT_TO_FP_VECTOR(xscvsdqp, int64, float128, VsrD(0), f128)
+VSX_CVT_INT_TO_FP_VECTOR(xscvudqp, uint64, float128, VsrD(0), f128)
+
 /* For "use current rounding mode", define a value that will not be one of
  * the existing rounding model enums.
  */
@@ -2743,7 +3100,7 @@ void helper_##op(CPUPPCState *env, uint32_t opcode)                    \
             xt.fld = tp##_round_to_int(xb.fld, &env->fp_status);       \
         }                                                              \
         if (sfprf) {                                                   \
-            helper_compute_fprf(env, xt.fld);                          \
+            helper_compute_fprf_float64(env, xt.fld);                  \
         }                                                              \
     }                                                                  \
                                                                        \
@@ -2783,7 +3140,140 @@ uint64_t helper_xsrsp(CPUPPCState *env, uint64_t xb)
 
     uint64_t xt = helper_frsp(env, xb);
 
-    helper_compute_fprf(env, xt);
+    helper_compute_fprf_float64(env, xt);
     float_check_status(env);
     return xt;
 }
+
+#define VSX_XXPERM(op, indexed)                                       \
+void helper_##op(CPUPPCState *env, uint32_t opcode)                   \
+{                                                                     \
+    ppc_vsr_t xt, xa, pcv, xto;                                       \
+    int i, idx;                                                       \
+                                                                      \
+    getVSR(xA(opcode), &xa, env);                                     \
+    getVSR(xT(opcode), &xt, env);                                     \
+    getVSR(xB(opcode), &pcv, env);                                    \
+                                                                      \
+    for (i = 0; i < 16; i++) {                                        \
+        idx = pcv.VsrB(i) & 0x1F;                                     \
+        if (indexed) {                                                \
+            idx = 31 - idx;                                           \
+        }                                                             \
+        xto.VsrB(i) = (idx <= 15) ? xa.VsrB(idx) : xt.VsrB(idx - 16); \
+    }                                                                 \
+    putVSR(xT(opcode), &xto, env);                                    \
+}
+
+VSX_XXPERM(xxperm, 0)
+VSX_XXPERM(xxpermr, 1)
+
+void helper_xvxsigsp(CPUPPCState *env, uint32_t opcode)
+{
+    ppc_vsr_t xt, xb;
+    uint32_t exp, i, fraction;
+
+    getVSR(xB(opcode), &xb, env);
+    memset(&xt, 0, sizeof(xt));
+
+    for (i = 0; i < 4; i++) {
+        exp = (xb.VsrW(i) >> 23) & 0xFF;
+        fraction = xb.VsrW(i) & 0x7FFFFF;
+        if (exp != 0 && exp != 255) {
+            xt.VsrW(i) = fraction | 0x00800000;
+        } else {
+            xt.VsrW(i) = fraction;
+        }
+    }
+    putVSR(xT(opcode), &xt, env);
+}
+
+/* VSX_TEST_DC - VSX floating point test data class
+ *   op    - instruction mnemonic
+ *   nels  - number of elements (1, 2 or 4)
+ *   xbn   - VSR register number
+ *   tp    - type (float32 or float64)
+ *   fld   - vsr_t field (VsrD(*) or VsrW(*))
+ *   tfld   - target vsr_t field (VsrD(*) or VsrW(*))
+ *   fld_max - target field max
+ *   scrf - set result in CR and FPCC
+ */
+#define VSX_TEST_DC(op, nels, xbn, tp, fld, tfld, fld_max, scrf)  \
+void helper_##op(CPUPPCState *env, uint32_t opcode)         \
+{                                                           \
+    ppc_vsr_t xt, xb;                                       \
+    uint32_t i, sign, dcmx;                                 \
+    uint32_t cc, match = 0;                                 \
+                                                            \
+    getVSR(xbn, &xb, env);                                  \
+    if (!scrf) {                                            \
+        memset(&xt, 0, sizeof(xt));                         \
+        dcmx = DCMX_XV(opcode);                             \
+    } else {                                                \
+        dcmx = DCMX(opcode);                                \
+    }                                                       \
+                                                            \
+    for (i = 0; i < nels; i++) {                            \
+        sign = tp##_is_neg(xb.fld);                         \
+        if (tp##_is_any_nan(xb.fld)) {                      \
+            match = extract32(dcmx, 6, 1);                  \
+        } else if (tp##_is_infinity(xb.fld)) {              \
+            match = extract32(dcmx, 4 + !sign, 1);          \
+        } else if (tp##_is_zero(xb.fld)) {                  \
+            match = extract32(dcmx, 2 + !sign, 1);          \
+        } else if (tp##_is_zero_or_denormal(xb.fld)) {      \
+            match = extract32(dcmx, 0 + !sign, 1);          \
+        }                                                   \
+                                                            \
+        if (scrf) {                                         \
+            cc = sign << CRF_LT_BIT | match << CRF_EQ_BIT;  \
+            env->fpscr &= ~(0x0F << FPSCR_FPRF);            \
+            env->fpscr |= cc << FPSCR_FPRF;                 \
+            env->crf[BF(opcode)] = cc;                      \
+        } else {                                            \
+            xt.tfld = match ? fld_max : 0;                  \
+        }                                                   \
+        match = 0;                                          \
+    }                                                       \
+    if (!scrf) {                                            \
+        putVSR(xT(opcode), &xt, env);                       \
+    }                                                       \
+}
+
+VSX_TEST_DC(xvtstdcdp, 2, xB(opcode), float64, VsrD(i), VsrD(i), UINT64_MAX, 0)
+VSX_TEST_DC(xvtstdcsp, 4, xB(opcode), float32, VsrW(i), VsrW(i), UINT32_MAX, 0)
+VSX_TEST_DC(xststdcdp, 1, xB(opcode), float64, VsrD(0), VsrD(0), 0, 1)
+VSX_TEST_DC(xststdcqp, 1, (rB(opcode) + 32), float128, f128, VsrD(0), 0, 1)
+
+void helper_xststdcsp(CPUPPCState *env, uint32_t opcode)
+{
+    ppc_vsr_t xb;
+    uint32_t dcmx, sign, exp;
+    uint32_t cc, match = 0, not_sp = 0;
+
+    getVSR(xB(opcode), &xb, env);
+    dcmx = DCMX(opcode);
+    exp = (xb.VsrD(0) >> 52) & 0x7FF;
+
+    sign = float64_is_neg(xb.VsrD(0));
+    if (float64_is_any_nan(xb.VsrD(0))) {
+        match = extract32(dcmx, 6, 1);
+    } else if (float64_is_infinity(xb.VsrD(0))) {
+        match = extract32(dcmx, 4 + !sign, 1);
+    } else if (float64_is_zero(xb.VsrD(0))) {
+        match = extract32(dcmx, 2 + !sign, 1);
+    } else if (float64_is_zero_or_denormal(xb.VsrD(0)) ||
+               (exp > 0 && exp < 0x381)) {
+        match = extract32(dcmx, 0 + !sign, 1);
+    }
+
+    not_sp = !float64_eq(xb.VsrD(0),
+                         float32_to_float64(
+                             float64_to_float32(xb.VsrD(0), &env->fp_status),
+                             &env->fp_status), &env->fp_status);
+
+    cc = sign << CRF_LT_BIT | match << CRF_EQ_BIT | not_sp << CRF_SO_BIT;
+    env->fpscr &= ~(0x0F << FPSCR_FPRF);
+    env->fpscr |= cc << FPSCR_FPRF;
+    env->crf[BF(opcode)] = cc;
+}
diff --git a/target/ppc/helper.h b/target/ppc/helper.h
index 0a8fbba3c5..85af9df36d 100644
--- a/target/ppc/helper.h
+++ b/target/ppc/helper.h
@@ -56,7 +56,7 @@ DEF_HELPER_FLAGS_2(brinc, TCG_CALL_NO_RWG_SE, tl, tl, tl)
 
 DEF_HELPER_1(float_check_status, void, env)
 DEF_HELPER_1(reset_fpstatus, void, env)
-DEF_HELPER_2(compute_fprf, void, env, i64)
+DEF_HELPER_2(compute_fprf_float64, void, env, i64)
 DEF_HELPER_3(store_fpscr, void, env, i64, i32)
 DEF_HELPER_2(fpscr_clrbit, void, env, i32)
 DEF_HELPER_2(fpscr_setbit, void, env, i32)
@@ -312,6 +312,12 @@ DEF_HELPER_3(lvewx, void, env, avr, tl)
 DEF_HELPER_3(stvebx, void, env, avr, tl)
 DEF_HELPER_3(stvehx, void, env, avr, tl)
 DEF_HELPER_3(stvewx, void, env, avr, tl)
+#if defined(TARGET_PPC64)
+DEF_HELPER_4(lxvl, void, env, tl, tl, tl)
+DEF_HELPER_4(lxvll, void, env, tl, tl, tl)
+DEF_HELPER_4(stxvl, void, env, tl, tl, tl)
+DEF_HELPER_4(stxvll, void, env, tl, tl, tl)
+#endif
 DEF_HELPER_4(vsumsws, void, env, avr, avr, avr)
 DEF_HELPER_4(vsum2sws, void, env, avr, avr, avr)
 DEF_HELPER_4(vsum4sbs, void, env, avr, avr, avr)
@@ -361,6 +367,12 @@ DEF_HELPER_3(vpmsumb, void, avr, avr, avr)
 DEF_HELPER_3(vpmsumh, void, avr, avr, avr)
 DEF_HELPER_3(vpmsumw, void, avr, avr, avr)
 DEF_HELPER_3(vpmsumd, void, avr, avr, avr)
+DEF_HELPER_2(vextublx, tl, tl, avr)
+DEF_HELPER_2(vextuhlx, tl, tl, avr)
+DEF_HELPER_2(vextuwlx, tl, tl, avr)
+DEF_HELPER_2(vextubrx, tl, tl, avr)
+DEF_HELPER_2(vextuhrx, tl, tl, avr)
+DEF_HELPER_2(vextuwrx, tl, tl, avr)
 
 DEF_HELPER_2(vsbox, void, avr, avr)
 DEF_HELPER_3(vcipher, void, avr, avr, avr)
@@ -377,11 +389,23 @@ DEF_HELPER_3(bcdcfn, i32, avr, avr, i32)
 DEF_HELPER_3(bcdctn, i32, avr, avr, i32)
 DEF_HELPER_3(bcdcfz, i32, avr, avr, i32)
 DEF_HELPER_3(bcdctz, i32, avr, avr, i32)
+DEF_HELPER_3(bcdcfsq, i32, avr, avr, i32)
+DEF_HELPER_3(bcdctsq, i32, avr, avr, i32)
+DEF_HELPER_4(bcdcpsgn, i32, avr, avr, avr, i32)
+DEF_HELPER_3(bcdsetsgn, i32, avr, avr, i32)
+DEF_HELPER_4(bcds, i32, avr, avr, avr, i32)
+DEF_HELPER_4(bcdus, i32, avr, avr, avr, i32)
+DEF_HELPER_4(bcdsr, i32, avr, avr, avr, i32)
+DEF_HELPER_4(bcdtrunc, i32, avr, avr, avr, i32)
+DEF_HELPER_4(bcdutrunc, i32, avr, avr, avr, i32)
 
 DEF_HELPER_2(xsadddp, void, env, i32)
+DEF_HELPER_2(xsaddqp, void, env, i32)
 DEF_HELPER_2(xssubdp, void, env, i32)
 DEF_HELPER_2(xsmuldp, void, env, i32)
+DEF_HELPER_2(xsmulqp, void, env, i32)
 DEF_HELPER_2(xsdivdp, void, env, i32)
+DEF_HELPER_2(xsdivqp, void, env, i32)
 DEF_HELPER_2(xsredp, void, env, i32)
 DEF_HELPER_2(xssqrtdp, void, env, i32)
 DEF_HELPER_2(xsrsqrtedp, void, env, i32)
@@ -399,12 +423,23 @@ DEF_HELPER_2(xscmpeqdp, void, env, i32)
 DEF_HELPER_2(xscmpgtdp, void, env, i32)
 DEF_HELPER_2(xscmpgedp, void, env, i32)
 DEF_HELPER_2(xscmpnedp, void, env, i32)
+DEF_HELPER_2(xscmpexpdp, void, env, i32)
+DEF_HELPER_2(xscmpexpqp, void, env, i32)
 DEF_HELPER_2(xscmpodp, void, env, i32)
 DEF_HELPER_2(xscmpudp, void, env, i32)
+DEF_HELPER_2(xscmpoqp, void, env, i32)
+DEF_HELPER_2(xscmpuqp, void, env, i32)
 DEF_HELPER_2(xsmaxdp, void, env, i32)
 DEF_HELPER_2(xsmindp, void, env, i32)
+DEF_HELPER_2(xscvdphp, void, env, i32)
+DEF_HELPER_2(xscvdpqp, void, env, i32)
 DEF_HELPER_2(xscvdpsp, void, env, i32)
 DEF_HELPER_2(xscvdpspn, i64, env, i64)
+DEF_HELPER_2(xscvqpdp, void, env, i32)
+DEF_HELPER_2(xscvqpsdz, void, env, i32)
+DEF_HELPER_2(xscvqpswz, void, env, i32)
+DEF_HELPER_2(xscvhpdp, void, env, i32)
+DEF_HELPER_2(xscvsdqp, void, env, i32)
 DEF_HELPER_2(xscvspdp, void, env, i32)
 DEF_HELPER_2(xscvspdpn, i64, env, i64)
 DEF_HELPER_2(xscvdpsxds, void, env, i32)
@@ -414,7 +449,11 @@ DEF_HELPER_2(xscvdpuxws, void, env, i32)
 DEF_HELPER_2(xscvsxddp, void, env, i32)
 DEF_HELPER_2(xscvuxdsp, void, env, i32)
 DEF_HELPER_2(xscvsxdsp, void, env, i32)
+DEF_HELPER_2(xscvudqp, void, env, i32)
 DEF_HELPER_2(xscvuxddp, void, env, i32)
+DEF_HELPER_2(xststdcsp, void, env, i32)
+DEF_HELPER_2(xststdcdp, void, env, i32)
+DEF_HELPER_2(xststdcqp, void, env, i32)
 DEF_HELPER_2(xsrdpi, void, env, i32)
 DEF_HELPER_2(xsrdpic, void, env, i32)
 DEF_HELPER_2(xsrdpim, void, env, i32)
@@ -500,6 +539,8 @@ DEF_HELPER_2(xvcmpgesp, void, env, i32)
 DEF_HELPER_2(xvcmpgtsp, void, env, i32)
 DEF_HELPER_2(xvcmpnesp, void, env, i32)
 DEF_HELPER_2(xvcvspdp, void, env, i32)
+DEF_HELPER_2(xvcvsphp, void, env, i32)
+DEF_HELPER_2(xvcvhpsp, void, env, i32)
 DEF_HELPER_2(xvcvspsxds, void, env, i32)
 DEF_HELPER_2(xvcvspsxws, void, env, i32)
 DEF_HELPER_2(xvcvspuxds, void, env, i32)
@@ -508,11 +549,18 @@ DEF_HELPER_2(xvcvsxdsp, void, env, i32)
 DEF_HELPER_2(xvcvuxdsp, void, env, i32)
 DEF_HELPER_2(xvcvsxwsp, void, env, i32)
 DEF_HELPER_2(xvcvuxwsp, void, env, i32)
+DEF_HELPER_2(xvtstdcsp, void, env, i32)
+DEF_HELPER_2(xvtstdcdp, void, env, i32)
 DEF_HELPER_2(xvrspi, void, env, i32)
 DEF_HELPER_2(xvrspic, void, env, i32)
 DEF_HELPER_2(xvrspim, void, env, i32)
 DEF_HELPER_2(xvrspip, void, env, i32)
 DEF_HELPER_2(xvrspiz, void, env, i32)
+DEF_HELPER_2(xxperm, void, env, i32)
+DEF_HELPER_2(xxpermr, void, env, i32)
+DEF_HELPER_4(xxextractuw, void, env, tl, tl, i32)
+DEF_HELPER_4(xxinsertw, void, env, tl, tl, i32)
+DEF_HELPER_2(xvxsigsp, void, env, i32)
 
 DEF_HELPER_2(efscfsi, i32, env, i32)
 DEF_HELPER_2(efscfui, i32, env, i32)
diff --git a/target/ppc/int_helper.c b/target/ppc/int_helper.c
index 1871792ff6..dd0a8929b3 100644
--- a/target/ppc/int_helper.c
+++ b/target/ppc/int_helper.c
@@ -157,7 +157,7 @@ uint64_t helper_divde(CPUPPCState *env, uint64_t rau, uint64_t rbu, uint32_t oe)
 
 uint32_t helper_cmpeqb(target_ulong ra, target_ulong rb)
 {
-    return hasvalue(rb, ra) ? 1 << CRF_GT : 0;
+    return hasvalue(rb, ra) ? CRF_GT : 0;
 }
 
 #undef pattern
@@ -1773,6 +1773,42 @@ void helper_vlogefp(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *b)
     }
 }
 
+#if defined(HOST_WORDS_BIGENDIAN)
+#define VEXTU_X_DO(name, size, left)                                \
+    target_ulong glue(helper_, name)(target_ulong a, ppc_avr_t *b)  \
+    {                                                               \
+        int index;                                                  \
+        if (left) {                                                 \
+            index = (a & 0xf) * 8;                                  \
+        } else {                                                    \
+            index = ((15 - (a & 0xf) + 1) * 8) - size;              \
+        }                                                           \
+        return int128_getlo(int128_rshift(b->s128, index)) &        \
+            MAKE_64BIT_MASK(0, size);                               \
+    }
+#else
+#define VEXTU_X_DO(name, size, left)                                \
+    target_ulong glue(helper_, name)(target_ulong a, ppc_avr_t *b)  \
+    {                                                               \
+        int index;                                                  \
+        if (left) {                                                 \
+            index = ((15 - (a & 0xf) + 1) * 8) - size;              \
+        } else {                                                    \
+            index = (a & 0xf) * 8;                                  \
+        }                                                           \
+        return int128_getlo(int128_rshift(b->s128, index)) &        \
+            MAKE_64BIT_MASK(0, size);                               \
+    }
+#endif
+
+VEXTU_X_DO(vextublx,  8, 1)
+VEXTU_X_DO(vextuhlx, 16, 1)
+VEXTU_X_DO(vextuwlx, 32, 1)
+VEXTU_X_DO(vextubrx,  8, 0)
+VEXTU_X_DO(vextuhrx, 16, 0)
+VEXTU_X_DO(vextuwrx, 32, 0)
+#undef VEXTU_X_DO
+
 /* The specification says that the results are undefined if all of the
  * shift counts are not identical.  We check to make sure that they are
  * to conform to what real hardware appears to do.  */
@@ -1965,6 +2001,57 @@ VEXTRACT(uw, u32)
 VEXTRACT(d, u64)
 #undef VEXTRACT
 
+void helper_xxextractuw(CPUPPCState *env, target_ulong xtn,
+                        target_ulong xbn, uint32_t index)
+{
+    ppc_vsr_t xt, xb;
+    size_t es = sizeof(uint32_t);
+    uint32_t ext_index;
+    int i;
+
+    getVSR(xbn, &xb, env);
+    memset(&xt, 0, sizeof(xt));
+
+#if defined(HOST_WORDS_BIGENDIAN)
+    ext_index = index;
+    for (i = 0; i < es; i++, ext_index++) {
+        xt.u8[8 - es + i] = xb.u8[ext_index % 16];
+    }
+#else
+    ext_index = 15 - index;
+    for (i = es - 1; i >= 0; i--, ext_index--) {
+        xt.u8[8 + i] = xb.u8[ext_index % 16];
+    }
+#endif
+
+    putVSR(xtn, &xt, env);
+}
+
+void helper_xxinsertw(CPUPPCState *env, target_ulong xtn,
+                      target_ulong xbn, uint32_t index)
+{
+    ppc_vsr_t xt, xb;
+    size_t es = sizeof(uint32_t);
+    int ins_index, i = 0;
+
+    getVSR(xbn, &xb, env);
+    getVSR(xtn, &xt, env);
+
+#if defined(HOST_WORDS_BIGENDIAN)
+    ins_index = index;
+    for (i = 0; i < es && ins_index < 16; i++, ins_index++) {
+        xt.u8[ins_index] = xb.u8[8 - es + i];
+    }
+#else
+    ins_index = 15 - index;
+    for (i = es - 1; i >= 0 && ins_index >= 0; i--, ins_index--) {
+        xt.u8[ins_index] = xb.u8[8 + i];
+    }
+#endif
+
+    putVSR(xtn, &xt, env);
+}
+
 #define VEXT_SIGNED(name, element, mask, cast, recast)              \
 void helper_##name(ppc_avr_t *r, ppc_avr_t *b)                      \
 {                                                                   \
@@ -2464,9 +2551,9 @@ void helper_vsubecuq(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c)
 #define NATIONAL_NEG    0x2D
 
 #if defined(HOST_WORDS_BIGENDIAN)
-#define BCD_DIG_BYTE(n) (15 - (n/2))
+#define BCD_DIG_BYTE(n) (15 - ((n) / 2))
 #else
-#define BCD_DIG_BYTE(n) (n/2)
+#define BCD_DIG_BYTE(n) ((n) / 2)
 #endif
 
 static int bcd_get_sgn(ppc_avr_t *bcd)
@@ -2528,12 +2615,30 @@ static void bcd_put_digit(ppc_avr_t *bcd, uint8_t digit, int n)
     }
 }
 
+static bool bcd_is_valid(ppc_avr_t *bcd)
+{
+    int i;
+    int invalid = 0;
+
+    if (bcd_get_sgn(bcd) == 0) {
+        return false;
+    }
+
+    for (i = 1; i < 32; i++) {
+        bcd_get_digit(bcd, i, &invalid);
+        if (unlikely(invalid)) {
+            return false;
+        }
+    }
+    return true;
+}
+
 static int bcd_cmp_zero(ppc_avr_t *bcd)
 {
     if (bcd->u64[HI_IDX] == 0 && (bcd->u64[LO_IDX] >> 4) == 0) {
-        return 1 << CRF_EQ;
+        return CRF_EQ;
     } else {
-        return (bcd_get_sgn(bcd) == 1) ? 1 << CRF_GT : 1 << CRF_LT;
+        return (bcd_get_sgn(bcd) == 1) ? CRF_GT : CRF_LT;
     }
 }
 
@@ -2645,25 +2750,25 @@ uint32_t helper_bcdadd(ppc_avr_t *r,  ppc_avr_t *a, ppc_avr_t *b, uint32_t ps)
         if (sgna == sgnb) {
             result.u8[BCD_DIG_BYTE(0)] = bcd_preferred_sgn(sgna, ps);
             zero = bcd_add_mag(&result, a, b, &invalid, &overflow);
-            cr = (sgna > 0) ? 1 << CRF_GT : 1 << CRF_LT;
+            cr = (sgna > 0) ? CRF_GT : CRF_LT;
         } else if (bcd_cmp_mag(a, b) > 0) {
             result.u8[BCD_DIG_BYTE(0)] = bcd_preferred_sgn(sgna, ps);
             zero = bcd_sub_mag(&result, a, b, &invalid, &overflow);
-            cr = (sgna > 0) ? 1 << CRF_GT : 1 << CRF_LT;
+            cr = (sgna > 0) ? CRF_GT : CRF_LT;
         } else {
             result.u8[BCD_DIG_BYTE(0)] = bcd_preferred_sgn(sgnb, ps);
             zero = bcd_sub_mag(&result, b, a, &invalid, &overflow);
-            cr = (sgnb > 0) ? 1 << CRF_GT : 1 << CRF_LT;
+            cr = (sgnb > 0) ? CRF_GT : CRF_LT;
         }
     }
 
     if (unlikely(invalid)) {
         result.u64[HI_IDX] = result.u64[LO_IDX] = -1;
-        cr = 1 << CRF_SO;
+        cr = CRF_SO;
     } else if (overflow) {
-        cr |= 1 << CRF_SO;
+        cr |= CRF_SO;
     } else if (zero) {
-        cr = 1 << CRF_EQ;
+        cr = CRF_EQ;
     }
 
     *r = result;
@@ -2713,7 +2818,7 @@ uint32_t helper_bcdcfn(ppc_avr_t *r, ppc_avr_t *b, uint32_t ps)
     cr = bcd_cmp_zero(&ret);
 
     if (unlikely(invalid)) {
-        cr = 1 << CRF_SO;
+        cr = CRF_SO;
     }
 
     *r = ret;
@@ -2743,11 +2848,11 @@ uint32_t helper_bcdctn(ppc_avr_t *r, ppc_avr_t *b, uint32_t ps)
     cr = bcd_cmp_zero(b);
 
     if (ox_flag) {
-        cr |= 1 << CRF_SO;
+        cr |= CRF_SO;
     }
 
     if (unlikely(invalid)) {
-        cr = 1 << CRF_SO;
+        cr = CRF_SO;
     }
 
     *r = ret;
@@ -2771,7 +2876,7 @@ uint32_t helper_bcdcfz(ppc_avr_t *r, ppc_avr_t *b, uint32_t ps)
     }
 
     for (i = 0; i < 16; i++) {
-        zone_digit = (i * 2) ? b->u8[BCD_DIG_BYTE(i * 2)] >> 4 : zone_lead;
+        zone_digit = i ? b->u8[BCD_DIG_BYTE(i * 2)] >> 4 : zone_lead;
         digit = b->u8[BCD_DIG_BYTE(i * 2)] & 0xF;
         if (unlikely(zone_digit != zone_lead || digit > 0x9)) {
             invalid = 1;
@@ -2791,7 +2896,7 @@ uint32_t helper_bcdcfz(ppc_avr_t *r, ppc_avr_t *b, uint32_t ps)
     cr = bcd_cmp_zero(&ret);
 
     if (unlikely(invalid)) {
-        cr = 1 << CRF_SO;
+        cr = CRF_SO;
     }
 
     *r = ret;
@@ -2830,18 +2935,350 @@ uint32_t helper_bcdctz(ppc_avr_t *r, ppc_avr_t *b, uint32_t ps)
     cr = bcd_cmp_zero(b);
 
     if (ox_flag) {
-        cr |= 1 << CRF_SO;
+        cr |= CRF_SO;
     }
 
     if (unlikely(invalid)) {
-        cr = 1 << CRF_SO;
+        cr = CRF_SO;
+    }
+
+    *r = ret;
+
+    return cr;
+}
+
+uint32_t helper_bcdcfsq(ppc_avr_t *r, ppc_avr_t *b, uint32_t ps)
+{
+    int i;
+    int cr = 0;
+    uint64_t lo_value;
+    uint64_t hi_value;
+    ppc_avr_t ret = { .u64 = { 0, 0 } };
+
+    if (b->s64[HI_IDX] < 0) {
+        lo_value = -b->s64[LO_IDX];
+        hi_value = ~b->u64[HI_IDX] + !lo_value;
+        bcd_put_digit(&ret, 0xD, 0);
+    } else {
+        lo_value = b->u64[LO_IDX];
+        hi_value = b->u64[HI_IDX];
+        bcd_put_digit(&ret, bcd_preferred_sgn(0, ps), 0);
+    }
+
+    if (divu128(&lo_value, &hi_value, 1000000000000000ULL) ||
+            lo_value > 9999999999999999ULL) {
+        cr = CRF_SO;
+    }
+
+    for (i = 1; i < 16; hi_value /= 10, i++) {
+        bcd_put_digit(&ret, hi_value % 10, i);
+    }
+
+    for (; i < 32; lo_value /= 10, i++) {
+        bcd_put_digit(&ret, lo_value % 10, i);
     }
 
+    cr |= bcd_cmp_zero(&ret);
+
     *r = ret;
 
     return cr;
 }
 
+uint32_t helper_bcdctsq(ppc_avr_t *r, ppc_avr_t *b, uint32_t ps)
+{
+    uint8_t i;
+    int cr;
+    uint64_t carry;
+    uint64_t unused;
+    uint64_t lo_value;
+    uint64_t hi_value = 0;
+    int sgnb = bcd_get_sgn(b);
+    int invalid = (sgnb == 0);
+
+    lo_value = bcd_get_digit(b, 31, &invalid);
+    for (i = 30; i > 0; i--) {
+        mulu64(&lo_value, &carry, lo_value, 10ULL);
+        mulu64(&hi_value, &unused, hi_value, 10ULL);
+        lo_value += bcd_get_digit(b, i, &invalid);
+        hi_value += carry;
+
+        if (unlikely(invalid)) {
+            break;
+        }
+    }
+
+    if (sgnb == -1) {
+        r->s64[LO_IDX] = -lo_value;
+        r->s64[HI_IDX] = ~hi_value + !r->s64[LO_IDX];
+    } else {
+        r->s64[LO_IDX] = lo_value;
+        r->s64[HI_IDX] = hi_value;
+    }
+
+    cr = bcd_cmp_zero(b);
+
+    if (unlikely(invalid)) {
+        cr = CRF_SO;
+    }
+
+    return cr;
+}
+
+uint32_t helper_bcdcpsgn(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, uint32_t ps)
+{
+    int i;
+    int invalid = 0;
+
+    if (bcd_get_sgn(a) == 0 || bcd_get_sgn(b) == 0) {
+        return CRF_SO;
+    }
+
+    *r = *a;
+    bcd_put_digit(r, b->u8[BCD_DIG_BYTE(0)] & 0xF, 0);
+
+    for (i = 1; i < 32; i++) {
+        bcd_get_digit(a, i, &invalid);
+        bcd_get_digit(b, i, &invalid);
+        if (unlikely(invalid)) {
+            return CRF_SO;
+        }
+    }
+
+    return bcd_cmp_zero(r);
+}
+
+uint32_t helper_bcdsetsgn(ppc_avr_t *r, ppc_avr_t *b, uint32_t ps)
+{
+    int sgnb = bcd_get_sgn(b);
+
+    *r = *b;
+    bcd_put_digit(r, bcd_preferred_sgn(sgnb, ps), 0);
+
+    if (bcd_is_valid(b) == false) {
+        return CRF_SO;
+    }
+
+    return bcd_cmp_zero(r);
+}
+
+uint32_t helper_bcds(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, uint32_t ps)
+{
+    int cr;
+#if defined(HOST_WORDS_BIGENDIAN)
+    int i = a->s8[7];
+#else
+    int i = a->s8[8];
+#endif
+    bool ox_flag = false;
+    int sgnb = bcd_get_sgn(b);
+    ppc_avr_t ret = *b;
+    ret.u64[LO_IDX] &= ~0xf;
+
+    if (bcd_is_valid(b) == false) {
+        return CRF_SO;
+    }
+
+    if (unlikely(i > 31)) {
+        i = 31;
+    } else if (unlikely(i < -31)) {
+        i = -31;
+    }
+
+    if (i > 0) {
+        ulshift(&ret.u64[LO_IDX], &ret.u64[HI_IDX], i * 4, &ox_flag);
+    } else {
+        urshift(&ret.u64[LO_IDX], &ret.u64[HI_IDX], -i * 4);
+    }
+    bcd_put_digit(&ret, bcd_preferred_sgn(sgnb, ps), 0);
+
+    *r = ret;
+
+    cr = bcd_cmp_zero(r);
+    if (ox_flag) {
+        cr |= CRF_SO;
+    }
+
+    return cr;
+}
+
+uint32_t helper_bcdus(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, uint32_t ps)
+{
+    int cr;
+    int i;
+    int invalid = 0;
+    bool ox_flag = false;
+    ppc_avr_t ret = *b;
+
+    for (i = 0; i < 32; i++) {
+        bcd_get_digit(b, i, &invalid);
+
+        if (unlikely(invalid)) {
+            return CRF_SO;
+        }
+    }
+
+#if defined(HOST_WORDS_BIGENDIAN)
+    i = a->s8[7];
+#else
+    i = a->s8[8];
+#endif
+    if (i >= 32) {
+        ox_flag = true;
+        ret.u64[LO_IDX] = ret.u64[HI_IDX] = 0;
+    } else if (i <= -32) {
+        ret.u64[LO_IDX] = ret.u64[HI_IDX] = 0;
+    } else if (i > 0) {
+        ulshift(&ret.u64[LO_IDX], &ret.u64[HI_IDX], i * 4, &ox_flag);
+    } else {
+        urshift(&ret.u64[LO_IDX], &ret.u64[HI_IDX], -i * 4);
+    }
+    *r = ret;
+
+    cr = bcd_cmp_zero(r);
+    if (ox_flag) {
+        cr |= CRF_SO;
+    }
+
+    return cr;
+}
+
+uint32_t helper_bcdsr(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, uint32_t ps)
+{
+    int cr;
+    int unused = 0;
+    int invalid = 0;
+    bool ox_flag = false;
+    int sgnb = bcd_get_sgn(b);
+    ppc_avr_t ret = *b;
+    ret.u64[LO_IDX] &= ~0xf;
+
+#if defined(HOST_WORDS_BIGENDIAN)
+    int i = a->s8[7];
+    ppc_avr_t bcd_one = { .u64 = { 0, 0x10 } };
+#else
+    int i = a->s8[8];
+    ppc_avr_t bcd_one = { .u64 = { 0x10, 0 } };
+#endif
+
+    if (bcd_is_valid(b) == false) {
+        return CRF_SO;
+    }
+
+    if (unlikely(i > 31)) {
+        i = 31;
+    } else if (unlikely(i < -31)) {
+        i = -31;
+    }
+
+    if (i > 0) {
+        ulshift(&ret.u64[LO_IDX], &ret.u64[HI_IDX], i * 4, &ox_flag);
+    } else {
+        urshift(&ret.u64[LO_IDX], &ret.u64[HI_IDX], -i * 4);
+
+        if (bcd_get_digit(&ret, 0, &invalid) >= 5) {
+            bcd_add_mag(&ret, &ret, &bcd_one, &invalid, &unused);
+        }
+    }
+    bcd_put_digit(&ret, bcd_preferred_sgn(sgnb, ps), 0);
+
+    cr = bcd_cmp_zero(&ret);
+    if (ox_flag) {
+        cr |= CRF_SO;
+    }
+    *r = ret;
+
+    return cr;
+}
+
+uint32_t helper_bcdtrunc(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, uint32_t ps)
+{
+    uint64_t mask;
+    uint32_t ox_flag = 0;
+#if defined(HOST_WORDS_BIGENDIAN)
+    int i = a->s16[3] + 1;
+#else
+    int i = a->s16[4] + 1;
+#endif
+    ppc_avr_t ret = *b;
+
+    if (bcd_is_valid(b) == false) {
+        return CRF_SO;
+    }
+
+    if (i > 16 && i < 32) {
+        mask = (uint64_t)-1 >> (128 - i * 4);
+        if (ret.u64[HI_IDX] & ~mask) {
+            ox_flag = CRF_SO;
+        }
+
+        ret.u64[HI_IDX] &= mask;
+    } else if (i >= 0 && i <= 16) {
+        mask = (uint64_t)-1 >> (64 - i * 4);
+        if (ret.u64[HI_IDX] || (ret.u64[LO_IDX] & ~mask)) {
+            ox_flag = CRF_SO;
+        }
+
+        ret.u64[LO_IDX] &= mask;
+        ret.u64[HI_IDX] = 0;
+    }
+    bcd_put_digit(&ret, bcd_preferred_sgn(bcd_get_sgn(b), ps), 0);
+    *r = ret;
+
+    return bcd_cmp_zero(&ret) | ox_flag;
+}
+
+uint32_t helper_bcdutrunc(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, uint32_t ps)
+{
+    int i;
+    uint64_t mask;
+    uint32_t ox_flag = 0;
+    int invalid = 0;
+    ppc_avr_t ret = *b;
+
+    for (i = 0; i < 32; i++) {
+        bcd_get_digit(b, i, &invalid);
+
+        if (unlikely(invalid)) {
+            return CRF_SO;
+        }
+    }
+
+#if defined(HOST_WORDS_BIGENDIAN)
+    i = a->s16[3];
+#else
+    i = a->s16[4];
+#endif
+    if (i > 16 && i < 33) {
+        mask = (uint64_t)-1 >> (128 - i * 4);
+        if (ret.u64[HI_IDX] & ~mask) {
+            ox_flag = CRF_SO;
+        }
+
+        ret.u64[HI_IDX] &= mask;
+    } else if (i > 0 && i <= 16) {
+        mask = (uint64_t)-1 >> (64 - i * 4);
+        if (ret.u64[HI_IDX] || (ret.u64[LO_IDX] & ~mask)) {
+            ox_flag = CRF_SO;
+        }
+
+        ret.u64[LO_IDX] &= mask;
+        ret.u64[HI_IDX] = 0;
+    } else if (i == 0) {
+        if (ret.u64[HI_IDX] || ret.u64[LO_IDX]) {
+            ox_flag = CRF_SO;
+        }
+        ret.u64[HI_IDX] = ret.u64[LO_IDX] = 0;
+    }
+
+    *r = ret;
+    if (r->u64[HI_IDX] == 0 && r->u64[LO_IDX] == 0) {
+        return ox_flag | CRF_EQ;
+    }
+
+    return ox_flag | CRF_GT;
+}
+
 void helper_vsbox(ppc_avr_t *r, ppc_avr_t *a)
 {
     int i;
diff --git a/target/ppc/internal.h b/target/ppc/internal.h
index 1ff4896c45..5a2fd68427 100644
--- a/target/ppc/internal.h
+++ b/target/ppc/internal.h
@@ -47,4 +47,206 @@ FUNC_MASK(MASK, target_ulong, 32, UINT32_MAX);
 FUNC_MASK(mask_u32, uint32_t, 32, UINT32_MAX);
 FUNC_MASK(mask_u64, uint64_t, 64, UINT64_MAX);
 
+/*****************************************************************************/
+/***                           Instruction decoding                        ***/
+#define EXTRACT_HELPER(name, shift, nb)                                       \
+static inline uint32_t name(uint32_t opcode)                                  \
+{                                                                             \
+    return (opcode >> (shift)) & ((1 << (nb)) - 1);                           \
+}
+
+#define EXTRACT_SHELPER(name, shift, nb)                                      \
+static inline int32_t name(uint32_t opcode)                                   \
+{                                                                             \
+    return (int16_t)((opcode >> (shift)) & ((1 << (nb)) - 1));                \
+}
+
+#define EXTRACT_HELPER_SPLIT(name, shift1, nb1, shift2, nb2)                  \
+static inline uint32_t name(uint32_t opcode)                                  \
+{                                                                             \
+    return (((opcode >> (shift1)) & ((1 << (nb1)) - 1)) << nb2) |             \
+            ((opcode >> (shift2)) & ((1 << (nb2)) - 1));                      \
+}
+
+#define EXTRACT_HELPER_SPLIT_3(name,                                          \
+                              d0_bits, shift_op_d0, shift_d0,                 \
+                              d1_bits, shift_op_d1, shift_d1,                 \
+                              d2_bits, shift_op_d2, shift_d2)                 \
+static inline int16_t name(uint32_t opcode)                                   \
+{                                                                             \
+    return                                                                    \
+        (((opcode >> (shift_op_d0)) & ((1 << (d0_bits)) - 1)) << (shift_d0)) | \
+        (((opcode >> (shift_op_d1)) & ((1 << (d1_bits)) - 1)) << (shift_d1)) | \
+        (((opcode >> (shift_op_d2)) & ((1 << (d2_bits)) - 1)) << (shift_d2));  \
+}
+
+
+/* Opcode part 1 */
+EXTRACT_HELPER(opc1, 26, 6);
+/* Opcode part 2 */
+EXTRACT_HELPER(opc2, 1, 5);
+/* Opcode part 3 */
+EXTRACT_HELPER(opc3, 6, 5);
+/* Opcode part 4 */
+EXTRACT_HELPER(opc4, 16, 5);
+/* Update Cr0 flags */
+EXTRACT_HELPER(Rc, 0, 1);
+/* Update Cr6 flags (Altivec) */
+EXTRACT_HELPER(Rc21, 10, 1);
+/* Destination */
+EXTRACT_HELPER(rD, 21, 5);
+/* Source */
+EXTRACT_HELPER(rS, 21, 5);
+/* First operand */
+EXTRACT_HELPER(rA, 16, 5);
+/* Second operand */
+EXTRACT_HELPER(rB, 11, 5);
+/* Third operand */
+EXTRACT_HELPER(rC, 6, 5);
+/***                               Get CRn                                 ***/
+EXTRACT_HELPER(crfD, 23, 3);
+EXTRACT_HELPER(BF, 23, 3);
+EXTRACT_HELPER(crfS, 18, 3);
+EXTRACT_HELPER(crbD, 21, 5);
+EXTRACT_HELPER(crbA, 16, 5);
+EXTRACT_HELPER(crbB, 11, 5);
+/* SPR / TBL */
+EXTRACT_HELPER(_SPR, 11, 10);
+static inline uint32_t SPR(uint32_t opcode)
+{
+    uint32_t sprn = _SPR(opcode);
+
+    return ((sprn >> 5) & 0x1F) | ((sprn & 0x1F) << 5);
+}
+/***                              Get constants                            ***/
+/* 16 bits signed immediate value */
+EXTRACT_SHELPER(SIMM, 0, 16);
+/* 16 bits unsigned immediate value */
+EXTRACT_HELPER(UIMM, 0, 16);
+/* 5 bits signed immediate value */
+EXTRACT_HELPER(SIMM5, 16, 5);
+/* 5 bits signed immediate value */
+EXTRACT_HELPER(UIMM5, 16, 5);
+/* 4 bits unsigned immediate value */
+EXTRACT_HELPER(UIMM4, 16, 4);
+/* Bit count */
+EXTRACT_HELPER(NB, 11, 5);
+/* Shift count */
+EXTRACT_HELPER(SH, 11, 5);
+/* Vector shift count */
+EXTRACT_HELPER(VSH, 6, 4);
+/* Mask start */
+EXTRACT_HELPER(MB, 6, 5);
+/* Mask end */
+EXTRACT_HELPER(ME, 1, 5);
+/* Trap operand */
+EXTRACT_HELPER(TO, 21, 5);
+
+EXTRACT_HELPER(CRM, 12, 8);
+
+#ifndef CONFIG_USER_ONLY
+EXTRACT_HELPER(SR, 16, 4);
+#endif
+
+/* mtfsf/mtfsfi */
+EXTRACT_HELPER(FPBF, 23, 3);
+EXTRACT_HELPER(FPIMM, 12, 4);
+EXTRACT_HELPER(FPL, 25, 1);
+EXTRACT_HELPER(FPFLM, 17, 8);
+EXTRACT_HELPER(FPW, 16, 1);
+
+/* addpcis */
+EXTRACT_HELPER_SPLIT_3(DX, 10, 6, 6, 5, 16, 1, 1, 0, 0)
+#if defined(TARGET_PPC64)
+/* darn */
+EXTRACT_HELPER(L, 16, 2);
+#endif
+
+/***                            Jump target decoding                       ***/
+/* Immediate address */
+static inline target_ulong LI(uint32_t opcode)
+{
+    return (opcode >> 0) & 0x03FFFFFC;
+}
+
+static inline uint32_t BD(uint32_t opcode)
+{
+    return (opcode >> 0) & 0xFFFC;
+}
+
+EXTRACT_HELPER(BO, 21, 5);
+EXTRACT_HELPER(BI, 16, 5);
+/* Absolute/relative address */
+EXTRACT_HELPER(AA, 1, 1);
+/* Link */
+EXTRACT_HELPER(LK, 0, 1);
+
+/* DFP Z22-form */
+EXTRACT_HELPER(DCM, 10, 6)
+
+/* DFP Z23-form */
+EXTRACT_HELPER(RMC, 9, 2)
+
+EXTRACT_HELPER_SPLIT(DQxT, 3, 1, 21, 5);
+EXTRACT_HELPER_SPLIT(xT, 0, 1, 21, 5);
+EXTRACT_HELPER_SPLIT(xS, 0, 1, 21, 5);
+EXTRACT_HELPER_SPLIT(xA, 2, 1, 16, 5);
+EXTRACT_HELPER_SPLIT(xB, 1, 1, 11, 5);
+EXTRACT_HELPER_SPLIT(xC, 3, 1,  6, 5);
+EXTRACT_HELPER(DM, 8, 2);
+EXTRACT_HELPER(UIM, 16, 2);
+EXTRACT_HELPER(SHW, 8, 2);
+EXTRACT_HELPER(SP, 19, 2);
+EXTRACT_HELPER(IMM8, 11, 8);
+EXTRACT_HELPER(DCMX, 16, 7);
+EXTRACT_HELPER_SPLIT_3(DCMX_XV, 5, 16, 0, 1, 2, 5, 1, 6, 6);
+
+typedef union _ppc_vsr_t {
+    uint8_t u8[16];
+    uint16_t u16[8];
+    uint32_t u32[4];
+    uint64_t u64[2];
+    float32 f32[4];
+    float64 f64[2];
+    float128 f128;
+    Int128  s128;
+} ppc_vsr_t;
+
+#if defined(HOST_WORDS_BIGENDIAN)
+#define VsrB(i) u8[i]
+#define VsrH(i) u16[i]
+#define VsrW(i) u32[i]
+#define VsrD(i) u64[i]
+#else
+#define VsrB(i) u8[15 - (i)]
+#define VsrH(i) u16[7 - (i)]
+#define VsrW(i) u32[3 - (i)]
+#define VsrD(i) u64[1 - (i)]
+#endif
+
+static inline void getVSR(int n, ppc_vsr_t *vsr, CPUPPCState *env)
+{
+    if (n < 32) {
+        vsr->VsrD(0) = env->fpr[n];
+        vsr->VsrD(1) = env->vsr[n];
+    } else {
+        vsr->u64[0] = env->avr[n - 32].u64[0];
+        vsr->u64[1] = env->avr[n - 32].u64[1];
+    }
+}
+
+static inline void putVSR(int n, ppc_vsr_t *vsr, CPUPPCState *env)
+{
+    if (n < 32) {
+        env->fpr[n] = vsr->VsrD(0);
+        env->vsr[n] = vsr->VsrD(1);
+    } else {
+        env->avr[n - 32].u64[0] = vsr->u64[0];
+        env->avr[n - 32].u64[1] = vsr->u64[1];
+    }
+}
+
+void helper_compute_fprf_float16(CPUPPCState *env, float16 arg);
+void helper_compute_fprf_float32(CPUPPCState *env, float32 arg);
+void helper_compute_fprf_float128(CPUPPCState *env, float128 arg);
 #endif /* PPC_INTERNAL_H */
diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c
index ec92c64159..663d2e79c9 100644
--- a/target/ppc/kvm.c
+++ b/target/ppc/kvm.c
@@ -24,6 +24,7 @@
 #include "qemu-common.h"
 #include "qemu/error-report.h"
 #include "cpu.h"
+#include "cpu-models.h"
 #include "qemu/timer.h"
 #include "sysemu/sysemu.h"
 #include "sysemu/hw_accel.h"
@@ -2108,9 +2109,9 @@ void kvmppc_set_papr(PowerPCCPU *cpu)
     cap_papr = 1;
 }
 
-int kvmppc_set_compat(PowerPCCPU *cpu, uint32_t cpu_version)
+int kvmppc_set_compat(PowerPCCPU *cpu, uint32_t compat_pvr)
 {
-    return kvm_set_one_reg(CPU(cpu), KVM_REG_PPC_ARCH_COMPAT, &cpu_version);
+    return kvm_set_one_reg(CPU(cpu), KVM_REG_PPC_ARCH_COMPAT, &compat_pvr);
 }
 
 void kvmppc_set_mpic_proxy(PowerPCCPU *cpu, int mpic_proxy)
@@ -2412,6 +2413,7 @@ static int kvm_ppc_register_host_cpu_type(void)
     };
     PowerPCCPUClass *pvr_pcc;
     DeviceClass *dc;
+    int i;
 
     pvr_pcc = kvm_ppc_get_host_cpu_class();
     if (pvr_pcc == NULL) {
@@ -2420,13 +2422,6 @@ static int kvm_ppc_register_host_cpu_type(void)
     type_info.parent = object_class_get_name(OBJECT_CLASS(pvr_pcc));
     type_register(&type_info);
 
-    /* Register generic family CPU class for a family */
-    pvr_pcc = ppc_cpu_get_family_class(pvr_pcc);
-    dc = DEVICE_CLASS(pvr_pcc);
-    type_info.parent = object_class_get_name(OBJECT_CLASS(pvr_pcc));
-    type_info.name = g_strdup_printf("%s-"TYPE_POWERPC_CPU, dc->desc);
-    type_register(&type_info);
-
 #if defined(TARGET_PPC64)
     type_info.name = g_strdup_printf("%s-"TYPE_SPAPR_CPU_CORE, "host");
     type_info.parent = TYPE_SPAPR_CPU_CORE,
@@ -2436,14 +2431,29 @@ static int kvm_ppc_register_host_cpu_type(void)
     type_info.class_data = (void *) "host";
     type_register(&type_info);
     g_free((void *)type_info.name);
-
-    /* Register generic spapr CPU family class for current host CPU type */
-    type_info.name = g_strdup_printf("%s-"TYPE_SPAPR_CPU_CORE, dc->desc);
-    type_info.class_data = (void *) dc->desc;
-    type_register(&type_info);
-    g_free((void *)type_info.name);
 #endif
 
+    /*
+     * Update generic CPU family class alias (e.g. on a POWER8NVL host,
+     * we want "POWER8" to be a "family" alias that points to the current
+     * host CPU type, too)
+     */
+    dc = DEVICE_CLASS(ppc_cpu_get_family_class(pvr_pcc));
+    for (i = 0; ppc_cpu_aliases[i].alias != NULL; i++) {
+        if (strcmp(ppc_cpu_aliases[i].alias, dc->desc) == 0) {
+            ObjectClass *oc = OBJECT_CLASS(pvr_pcc);
+            char *suffix;
+
+            ppc_cpu_aliases[i].model = g_strdup(object_class_get_name(oc));
+            suffix = strstr(ppc_cpu_aliases[i].model, "-"TYPE_POWERPC_CPU);
+            if (suffix) {
+                *suffix = 0;
+            }
+            ppc_cpu_aliases[i].oc = oc;
+            break;
+        }
+    }
+
     return 0;
 }
 
diff --git a/target/ppc/kvm_ppc.h b/target/ppc/kvm_ppc.h
index 4b43283913..151c00bac7 100644
--- a/target/ppc/kvm_ppc.h
+++ b/target/ppc/kvm_ppc.h
@@ -26,7 +26,7 @@ void kvmppc_enable_logical_ci_hcalls(void);
 void kvmppc_enable_set_mode_hcall(void);
 void kvmppc_enable_clear_ref_mod_hcalls(void);
 void kvmppc_set_papr(PowerPCCPU *cpu);
-int kvmppc_set_compat(PowerPCCPU *cpu, uint32_t cpu_version);
+int kvmppc_set_compat(PowerPCCPU *cpu, uint32_t compat_pvr);
 void kvmppc_set_mpic_proxy(PowerPCCPU *cpu, int mpic_proxy);
 int kvmppc_smt_threads(void);
 int kvmppc_clear_tsr_bits(PowerPCCPU *cpu, uint32_t tsr_bits);
@@ -123,7 +123,7 @@ static inline void kvmppc_set_papr(PowerPCCPU *cpu)
 {
 }
 
-static inline int kvmppc_set_compat(PowerPCCPU *cpu, uint32_t cpu_version)
+static inline int kvmppc_set_compat(PowerPCCPU *cpu, uint32_t compat_pvr)
 {
     return 0;
 }
diff --git a/target/ppc/mem_helper.c b/target/ppc/mem_helper.c
index 1ab8a6eab4..e6383c6bfa 100644
--- a/target/ppc/mem_helper.c
+++ b/target/ppc/mem_helper.c
@@ -24,6 +24,7 @@
 
 #include "helper_regs.h"
 #include "exec/cpu_ldst.h"
+#include "internal.h"
 
 //#define DEBUG_OP
 
@@ -284,6 +285,71 @@ STVE(stvewx, cpu_stl_data_ra, bswap32, u32)
 #undef I
 #undef LVE
 
+#ifdef TARGET_PPC64
+#define GET_NB(rb) ((rb >> 56) & 0xFF)
+
+#define VSX_LXVL(name, lj)                                              \
+void helper_##name(CPUPPCState *env, target_ulong addr,                 \
+                   target_ulong xt_num, target_ulong rb)                \
+{                                                                       \
+    int i;                                                              \
+    ppc_vsr_t xt;                                                       \
+    uint64_t nb = GET_NB(rb);                                           \
+                                                                        \
+    xt.s128 = int128_zero();                                            \
+    if (nb) {                                                           \
+        nb = (nb >= 16) ? 16 : nb;                                      \
+        if (msr_le && !lj) {                                            \
+            for (i = 16; i > 16 - nb; i--) {                            \
+                xt.VsrB(i - 1) = cpu_ldub_data_ra(env, addr, GETPC());  \
+                addr = addr_add(env, addr, 1);                          \
+            }                                                           \
+        } else {                                                        \
+            for (i = 0; i < nb; i++) {                                  \
+                xt.VsrB(i) = cpu_ldub_data_ra(env, addr, GETPC());      \
+                addr = addr_add(env, addr, 1);                          \
+            }                                                           \
+        }                                                               \
+    }                                                                   \
+    putVSR(xt_num, &xt, env);                                           \
+}
+
+VSX_LXVL(lxvl, 0)
+VSX_LXVL(lxvll, 1)
+#undef VSX_LXVL
+
+#define VSX_STXVL(name, lj)                                       \
+void helper_##name(CPUPPCState *env, target_ulong addr,           \
+                   target_ulong xt_num, target_ulong rb)          \
+{                                                                 \
+    int i;                                                        \
+    ppc_vsr_t xt;                                                 \
+    target_ulong nb = GET_NB(rb);                                 \
+                                                                  \
+    if (!nb) {                                                    \
+        return;                                                   \
+    }                                                             \
+    getVSR(xt_num, &xt, env);                                     \
+    nb = (nb >= 16) ? 16 : nb;                                    \
+    if (msr_le && !lj) {                                          \
+        for (i = 16; i > 16 - nb; i--) {                          \
+            cpu_stb_data_ra(env, addr, xt.VsrB(i - 1), GETPC());  \
+            addr = addr_add(env, addr, 1);                        \
+        }                                                         \
+    } else {                                                      \
+        for (i = 0; i < nb; i++) {                                \
+            cpu_stb_data_ra(env, addr, xt.VsrB(i), GETPC());      \
+            addr = addr_add(env, addr, 1);                        \
+        }                                                         \
+    }                                                             \
+}
+
+VSX_STXVL(stxvl, 0)
+VSX_STXVL(stxvll, 1)
+#undef VSX_STXVL
+#undef GET_NB
+#endif /* TARGET_PPC64 */
+
 #undef HI_IDX
 #undef LO_IDX
 
diff --git a/target/ppc/mmu-hash64.c b/target/ppc/mmu-hash64.c
index 0efc8c63fa..bb78fb5497 100644
--- a/target/ppc/mmu-hash64.c
+++ b/target/ppc/mmu-hash64.c
@@ -181,8 +181,8 @@ int ppc_store_slb(PowerPCCPU *cpu, target_ulong slot,
     slb->vsid = vsid;
     slb->sps = sps;
 
-    LOG_SLB("%s: %d " TARGET_FMT_lx " - " TARGET_FMT_lx " => %016" PRIx64
-            " %016" PRIx64 "\n", __func__, slot, esid, vsid,
+    LOG_SLB("%s: " TARGET_FMT_lu " " TARGET_FMT_lx " - " TARGET_FMT_lx
+            " => %016" PRIx64 " %016" PRIx64 "\n", __func__, slot, esid, vsid,
             slb->esid, slb->vsid);
 
     return 0;
diff --git a/target/ppc/mmu-hash64.h b/target/ppc/mmu-hash64.h
index ab5d347a53..7a0b7fca41 100644
--- a/target/ppc/mmu-hash64.h
+++ b/target/ppc/mmu-hash64.h
@@ -85,7 +85,7 @@ void ppc_hash64_update_rmls(CPUPPCState *env);
 #define HPTE64_R_C              0x0000000000000080ULL
 #define HPTE64_R_R              0x0000000000000100ULL
 #define HPTE64_R_KEY_LO         0x0000000000000e00ULL
-#define HPTE64_R_KEY(x)         ((((x) & HPTE64_R_KEY_HI) >> 60) | \
+#define HPTE64_R_KEY(x)         ((((x) & HPTE64_R_KEY_HI) >> 57) | \
                                  (((x) & HPTE64_R_KEY_LO) >> 9))
 
 #define HPTE64_V_1TB_SEG        0x4000000000000000ULL
diff --git a/target/ppc/translate.c b/target/ppc/translate.c
index 121218087f..b48abaedfb 100644
--- a/target/ppc/translate.c
+++ b/target/ppc/translate.c
@@ -422,157 +422,6 @@ typedef struct opcode_t {
 
 #define CHK_NONE
 
-
-/*****************************************************************************/
-/***                           Instruction decoding                        ***/
-#define EXTRACT_HELPER(name, shift, nb)                                       \
-static inline uint32_t name(uint32_t opcode)                                  \
-{                                                                             \
-    return (opcode >> (shift)) & ((1 << (nb)) - 1);                           \
-}
-
-#define EXTRACT_SHELPER(name, shift, nb)                                      \
-static inline int32_t name(uint32_t opcode)                                   \
-{                                                                             \
-    return (int16_t)((opcode >> (shift)) & ((1 << (nb)) - 1));                \
-}
-
-#define EXTRACT_HELPER_SPLIT(name, shift1, nb1, shift2, nb2)                  \
-static inline uint32_t name(uint32_t opcode)                                  \
-{                                                                             \
-    return (((opcode >> (shift1)) & ((1 << (nb1)) - 1)) << nb2) |             \
-            ((opcode >> (shift2)) & ((1 << (nb2)) - 1));                      \
-}
-
-#define EXTRACT_HELPER_DXFORM(name,                                           \
-                              d0_bits, shift_op_d0, shift_d0,                 \
-                              d1_bits, shift_op_d1, shift_d1,                 \
-                              d2_bits, shift_op_d2, shift_d2)                 \
-static inline int16_t name(uint32_t opcode)                                   \
-{                                                                             \
-    return                                                                    \
-        (((opcode >> (shift_op_d0)) & ((1 << (d0_bits)) - 1)) << (shift_d0)) | \
-        (((opcode >> (shift_op_d1)) & ((1 << (d1_bits)) - 1)) << (shift_d1)) | \
-        (((opcode >> (shift_op_d2)) & ((1 << (d2_bits)) - 1)) << (shift_d2));  \
-}
-
-
-/* Opcode part 1 */
-EXTRACT_HELPER(opc1, 26, 6);
-/* Opcode part 2 */
-EXTRACT_HELPER(opc2, 1, 5);
-/* Opcode part 3 */
-EXTRACT_HELPER(opc3, 6, 5);
-/* Opcode part 4 */
-EXTRACT_HELPER(opc4, 16, 5);
-/* Update Cr0 flags */
-EXTRACT_HELPER(Rc, 0, 1);
-/* Update Cr6 flags (Altivec) */
-EXTRACT_HELPER(Rc21, 10, 1);
-/* Destination */
-EXTRACT_HELPER(rD, 21, 5);
-/* Source */
-EXTRACT_HELPER(rS, 21, 5);
-/* First operand */
-EXTRACT_HELPER(rA, 16, 5);
-/* Second operand */
-EXTRACT_HELPER(rB, 11, 5);
-/* Third operand */
-EXTRACT_HELPER(rC, 6, 5);
-/***                               Get CRn                                 ***/
-EXTRACT_HELPER(crfD, 23, 3);
-EXTRACT_HELPER(crfS, 18, 3);
-EXTRACT_HELPER(crbD, 21, 5);
-EXTRACT_HELPER(crbA, 16, 5);
-EXTRACT_HELPER(crbB, 11, 5);
-/* SPR / TBL */
-EXTRACT_HELPER(_SPR, 11, 10);
-static inline uint32_t SPR(uint32_t opcode)
-{
-    uint32_t sprn = _SPR(opcode);
-
-    return ((sprn >> 5) & 0x1F) | ((sprn & 0x1F) << 5);
-}
-/***                              Get constants                            ***/
-/* 16 bits signed immediate value */
-EXTRACT_SHELPER(SIMM, 0, 16);
-/* 16 bits unsigned immediate value */
-EXTRACT_HELPER(UIMM, 0, 16);
-/* 5 bits signed immediate value */
-EXTRACT_HELPER(SIMM5, 16, 5);
-/* 5 bits signed immediate value */
-EXTRACT_HELPER(UIMM5, 16, 5);
-/* 4 bits unsigned immediate value */
-EXTRACT_HELPER(UIMM4, 16, 4);
-/* Bit count */
-EXTRACT_HELPER(NB, 11, 5);
-/* Shift count */
-EXTRACT_HELPER(SH, 11, 5);
-/* Vector shift count */
-EXTRACT_HELPER(VSH, 6, 4);
-/* Mask start */
-EXTRACT_HELPER(MB, 6, 5);
-/* Mask end */
-EXTRACT_HELPER(ME, 1, 5);
-/* Trap operand */
-EXTRACT_HELPER(TO, 21, 5);
-
-EXTRACT_HELPER(CRM, 12, 8);
-
-#ifndef CONFIG_USER_ONLY
-EXTRACT_HELPER(SR, 16, 4);
-#endif
-
-/* mtfsf/mtfsfi */
-EXTRACT_HELPER(FPBF, 23, 3);
-EXTRACT_HELPER(FPIMM, 12, 4);
-EXTRACT_HELPER(FPL, 25, 1);
-EXTRACT_HELPER(FPFLM, 17, 8);
-EXTRACT_HELPER(FPW, 16, 1);
-
-/* addpcis */
-EXTRACT_HELPER_DXFORM(DX, 10, 6, 6, 5, 16, 1, 1, 0, 0)
-#if defined(TARGET_PPC64)
-/* darn */
-EXTRACT_HELPER(L, 16, 2);
-#endif
-
-/***                            Jump target decoding                       ***/
-/* Immediate address */
-static inline target_ulong LI(uint32_t opcode)
-{
-    return (opcode >> 0) & 0x03FFFFFC;
-}
-
-static inline uint32_t BD(uint32_t opcode)
-{
-    return (opcode >> 0) & 0xFFFC;
-}
-
-EXTRACT_HELPER(BO, 21, 5);
-EXTRACT_HELPER(BI, 16, 5);
-/* Absolute/relative address */
-EXTRACT_HELPER(AA, 1, 1);
-/* Link */
-EXTRACT_HELPER(LK, 0, 1);
-
-/* DFP Z22-form */
-EXTRACT_HELPER(DCM, 10, 6)
-
-/* DFP Z23-form */
-EXTRACT_HELPER(RMC, 9, 2)
-
-EXTRACT_HELPER_SPLIT(xT, 0, 1, 21, 5);
-EXTRACT_HELPER_SPLIT(xS, 0, 1, 21, 5);
-EXTRACT_HELPER_SPLIT(xA, 2, 1, 16, 5);
-EXTRACT_HELPER_SPLIT(xB, 1, 1, 11, 5);
-EXTRACT_HELPER_SPLIT(xC, 3, 1,  6, 5);
-EXTRACT_HELPER(DM, 8, 2);
-EXTRACT_HELPER(UIM, 16, 2);
-EXTRACT_HELPER(SHW, 8, 2);
-EXTRACT_HELPER(SP, 19, 2);
-EXTRACT_HELPER(IMM8, 11, 8);
-
 /*****************************************************************************/
 /* PowerPC instructions table                                                */
 
@@ -763,17 +612,17 @@ static inline void gen_op_cmp(TCGv arg0, TCGv arg1, int s, int crf)
 
     tcg_gen_setcond_tl((s ? TCG_COND_LT: TCG_COND_LTU), t0, arg0, arg1);
     tcg_gen_trunc_tl_i32(t1, t0);
-    tcg_gen_shli_i32(t1, t1, CRF_LT);
+    tcg_gen_shli_i32(t1, t1, CRF_LT_BIT);
     tcg_gen_or_i32(cpu_crf[crf], cpu_crf[crf], t1);
 
     tcg_gen_setcond_tl((s ? TCG_COND_GT: TCG_COND_GTU), t0, arg0, arg1);
     tcg_gen_trunc_tl_i32(t1, t0);
-    tcg_gen_shli_i32(t1, t1, CRF_GT);
+    tcg_gen_shli_i32(t1, t1, CRF_GT_BIT);
     tcg_gen_or_i32(cpu_crf[crf], cpu_crf[crf], t1);
 
     tcg_gen_setcond_tl(TCG_COND_EQ, t0, arg0, arg1);
     tcg_gen_trunc_tl_i32(t1, t0);
-    tcg_gen_shli_i32(t1, t1, CRF_EQ);
+    tcg_gen_shli_i32(t1, t1, CRF_EQ_BIT);
     tcg_gen_or_i32(cpu_crf[crf], cpu_crf[crf], t1);
 
     tcg_temp_free(t0);
@@ -899,7 +748,7 @@ static void gen_cmprb(DisasContext *ctx)
         tcg_gen_and_i32(src2lo, src2lo, src2hi);
         tcg_gen_or_i32(crf, crf, src2lo);
     }
-    tcg_gen_shli_i32(crf, crf, CRF_GT);
+    tcg_gen_shli_i32(crf, crf, CRF_GT_BIT);
     tcg_temp_free_i32(src1);
     tcg_temp_free_i32(src2);
     tcg_temp_free_i32(src2lo);
@@ -3148,7 +2997,7 @@ static void gen_conditional_store(DisasContext *ctx, TCGv EA,
     tcg_gen_trunc_tl_i32(cpu_crf[0], cpu_so);
     l1 = gen_new_label();
     tcg_gen_brcond_tl(TCG_COND_NE, EA, cpu_reserve, l1);
-    tcg_gen_ori_i32(cpu_crf[0], cpu_crf[0], 1 << CRF_EQ);
+    tcg_gen_ori_i32(cpu_crf[0], cpu_crf[0], CRF_EQ);
     tcg_gen_qemu_st_tl(cpu_gpr[reg], EA, ctx->mem_idx, memop);
     gen_set_label(l1);
     tcg_gen_movi_tl(cpu_reserve, -1);
@@ -3242,7 +3091,7 @@ static void gen_stqcx_(DisasContext *ctx)
     tcg_gen_trunc_tl_i32(cpu_crf[0], cpu_so);
     l1 = gen_new_label();
     tcg_gen_brcond_tl(TCG_COND_NE, EA, cpu_reserve, l1);
-    tcg_gen_ori_i32(cpu_crf[0], cpu_crf[0], 1 << CRF_EQ);
+    tcg_gen_ori_i32(cpu_crf[0], cpu_crf[0], CRF_EQ);
 
     if (unlikely(ctx->le_mode)) {
         gpr1 = cpu_gpr[reg + 1];
@@ -3323,6 +3172,11 @@ static void gen_nap(DisasContext *ctx)
 #endif /* defined(CONFIG_USER_ONLY) */
 }
 
+static void gen_stop(DisasContext *ctx)
+{
+    gen_nap(ctx);
+}
+
 static void gen_sleep(DisasContext *ctx)
 {
 #if defined(CONFIG_USER_ONLY)
@@ -4423,7 +4277,7 @@ static void gen_slbfee_(DisasContext *ctx)
     l2 = gen_new_label();
     tcg_gen_trunc_tl_i32(cpu_crf[0], cpu_so);
     tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_gpr[rS(ctx->opcode)], -1, l1);
-    tcg_gen_ori_i32(cpu_crf[0], cpu_crf[0], 1 << CRF_EQ);
+    tcg_gen_ori_i32(cpu_crf[0], cpu_crf[0], CRF_EQ);
     tcg_gen_br(l2);
     gen_set_label(l1);
     tcg_gen_movi_tl(cpu_gpr[rS(ctx->opcode)], 0);
@@ -6166,6 +6020,10 @@ GEN_TM_NOOP(tabortwci);
 GEN_TM_NOOP(tabortdc);
 GEN_TM_NOOP(tabortdci);
 GEN_TM_NOOP(tsr);
+static inline void gen_cp_abort(DisasContext *ctx)
+{
+    // Do Nothing
+}
 
 static void gen_tcheck(DisasContext *ctx)
 {
@@ -6223,6 +6081,67 @@ GEN_TM_PRIV_NOOP(trechkpt);
 
 #include "translate/spe-impl.inc.c"
 
+/* Handles lfdp, lxsd, lxssp */
+static void gen_dform39(DisasContext *ctx)
+{
+    switch (ctx->opcode & 0x3) {
+    case 0: /* lfdp */
+        if (ctx->insns_flags2 & PPC2_ISA205) {
+            return gen_lfdp(ctx);
+        }
+        break;
+    case 2: /* lxsd */
+        if (ctx->insns_flags2 & PPC2_ISA300) {
+            return gen_lxsd(ctx);
+        }
+        break;
+    case 3: /* lxssp */
+        if (ctx->insns_flags2 & PPC2_ISA300) {
+            return gen_lxssp(ctx);
+        }
+        break;
+    }
+    return gen_invalid(ctx);
+}
+
+/* handles stfdp, lxv, stxsd, stxssp lxvx */
+static void gen_dform3D(DisasContext *ctx)
+{
+    if ((ctx->opcode & 3) == 1) { /* DQ-FORM */
+        switch (ctx->opcode & 0x7) {
+        case 1: /* lxv */
+            if (ctx->insns_flags2 & PPC2_ISA300) {
+                return gen_lxv(ctx);
+            }
+            break;
+        case 5: /* stxv */
+            if (ctx->insns_flags2 & PPC2_ISA300) {
+                return gen_stxv(ctx);
+            }
+            break;
+        }
+    } else { /* DS-FORM */
+        switch (ctx->opcode & 0x3) {
+        case 0: /* stfdp */
+            if (ctx->insns_flags2 & PPC2_ISA205) {
+                return gen_stfdp(ctx);
+            }
+            break;
+        case 2: /* stxsd */
+            if (ctx->insns_flags2 & PPC2_ISA300) {
+                return gen_stxsd(ctx);
+            }
+            break;
+        case 3: /* stxssp */
+            if (ctx->insns_flags2 & PPC2_ISA300) {
+                return gen_stxssp(ctx);
+            }
+            break;
+        }
+    }
+    return gen_invalid(ctx);
+}
+
 static opcode_t opcodes[] = {
 GEN_HANDLER(invalid, 0x00, 0x00, 0x00, 0xFFFFFFFF, PPC_NONE),
 GEN_HANDLER(cmp, 0x1F, 0x00, 0x00, 0x00400000, PPC_INTEGER),
@@ -6255,6 +6174,7 @@ GEN_HANDLER2(andi_, "andi.", 0x1C, 0xFF, 0xFF, 0x00000000, PPC_INTEGER),
 GEN_HANDLER2(andis_, "andis.", 0x1D, 0xFF, 0xFF, 0x00000000, PPC_INTEGER),
 GEN_HANDLER(cntlzw, 0x1F, 0x1A, 0x00, 0x00000000, PPC_INTEGER),
 GEN_HANDLER_E(cnttzw, 0x1F, 0x1A, 0x10, 0x00000000, PPC_NONE, PPC2_ISA300),
+GEN_HANDLER_E(cp_abort, 0x1F, 0x06, 0x1A, 0x03FFF801, PPC_NONE, PPC2_ISA300),
 GEN_HANDLER(or, 0x1F, 0x1C, 0x0D, 0x00000000, PPC_INTEGER),
 GEN_HANDLER(xor, 0x1F, 0x1C, 0x09, 0x00000000, PPC_INTEGER),
 GEN_HANDLER(ori, 0x18, 0xFF, 0xFF, 0x00000000, PPC_INTEGER),
@@ -6295,6 +6215,10 @@ GEN_HANDLER(ld, 0x3A, 0xFF, 0xFF, 0x00000000, PPC_64B),
 GEN_HANDLER(lq, 0x38, 0xFF, 0xFF, 0x00000000, PPC_64BX),
 GEN_HANDLER(std, 0x3E, 0xFF, 0xFF, 0x00000000, PPC_64B),
 #endif
+/* handles lfdp, lxsd, lxssp */
+GEN_HANDLER_E(dform39, 0x39, 0xFF, 0xFF, 0x00000000, PPC_NONE, PPC2_ISA205),
+/* handles stfdp, lxv, stxsd, stxssp, stxv */
+GEN_HANDLER_E(dform3D, 0x3D, 0xFF, 0xFF, 0x00000000, PPC_NONE, PPC2_ISA205),
 GEN_HANDLER(lmw, 0x2E, 0xFF, 0xFF, 0x00000000, PPC_INTEGER),
 GEN_HANDLER(stmw, 0x2F, 0xFF, 0xFF, 0x00000000, PPC_INTEGER),
 GEN_HANDLER(lswi, 0x1F, 0x15, 0x12, 0x00000001, PPC_STRING),
@@ -6326,6 +6250,7 @@ GEN_HANDLER(mcrf, 0x13, 0x00, 0xFF, 0x00000001, PPC_INTEGER),
 GEN_HANDLER(rfi, 0x13, 0x12, 0x01, 0x03FF8001, PPC_FLOW),
 #if defined(TARGET_PPC64)
 GEN_HANDLER(rfid, 0x13, 0x12, 0x00, 0x03FF8001, PPC_64B),
+GEN_HANDLER_E(stop, 0x13, 0x12, 0x0b, 0x03FFF801, PPC_NONE, PPC2_ISA300),
 GEN_HANDLER_E(doze, 0x13, 0x12, 0x0c, 0x03FFF801, PPC_NONE, PPC2_PM_ISA206),
 GEN_HANDLER_E(nap, 0x13, 0x12, 0x0d, 0x03FFF801, PPC_NONE, PPC2_PM_ISA206),
 GEN_HANDLER_E(sleep, 0x13, 0x12, 0x0e, 0x03FFF801, PPC_NONE, PPC2_PM_ISA206),
@@ -6910,6 +6835,9 @@ void ppc_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf,
     }
 #endif
 
+    if (env->spr_cb[SPR_LPCR].name)
+        cpu_fprintf(f, " LPCR " TARGET_FMT_lx "\n", env->spr[SPR_LPCR]);
+
     switch (env->mmu_model) {
     case POWERPC_MMU_32B:
     case POWERPC_MMU_601:
diff --git a/target/ppc/translate/fp-impl.inc.c b/target/ppc/translate/fp-impl.inc.c
index 872af7b56f..2fbd4d4f38 100644
--- a/target/ppc/translate/fp-impl.inc.c
+++ b/target/ppc/translate/fp-impl.inc.c
@@ -9,9 +9,9 @@ static inline void gen_reset_fpstatus(void)
     gen_helper_reset_fpstatus(cpu_env);
 }
 
-static inline void gen_compute_fprf(TCGv_i64 arg)
+static inline void gen_compute_fprf_float64(TCGv_i64 arg)
 {
-    gen_helper_compute_fprf(cpu_env, arg);
+    gen_helper_compute_fprf_float64(cpu_env, arg);
     gen_helper_float_check_status(cpu_env);
 }
 
@@ -47,7 +47,7 @@ static void gen_f##name(DisasContext *ctx)                                    \
                         cpu_fpr[rD(ctx->opcode)]);                            \
     }                                                                         \
     if (set_fprf) {                                                           \
-        gen_compute_fprf(cpu_fpr[rD(ctx->opcode)]);                           \
+        gen_compute_fprf_float64(cpu_fpr[rD(ctx->opcode)]);                   \
     }                                                                         \
     if (unlikely(Rc(ctx->opcode) != 0)) {                                     \
         gen_set_cr1_from_fpscr(ctx);                                          \
@@ -74,7 +74,7 @@ static void gen_f##name(DisasContext *ctx)                                    \
                         cpu_fpr[rD(ctx->opcode)]);                            \
     }                                                                         \
     if (set_fprf) {                                                           \
-        gen_compute_fprf(cpu_fpr[rD(ctx->opcode)]);                           \
+        gen_compute_fprf_float64(cpu_fpr[rD(ctx->opcode)]);                   \
     }                                                                         \
     if (unlikely(Rc(ctx->opcode) != 0)) {                                     \
         gen_set_cr1_from_fpscr(ctx);                                          \
@@ -100,7 +100,7 @@ static void gen_f##name(DisasContext *ctx)                                    \
                         cpu_fpr[rD(ctx->opcode)]);                            \
     }                                                                         \
     if (set_fprf) {                                                           \
-        gen_compute_fprf(cpu_fpr[rD(ctx->opcode)]);                           \
+        gen_compute_fprf_float64(cpu_fpr[rD(ctx->opcode)]);                   \
     }                                                                         \
     if (unlikely(Rc(ctx->opcode) != 0)) {                                     \
         gen_set_cr1_from_fpscr(ctx);                                          \
@@ -121,7 +121,7 @@ static void gen_f##name(DisasContext *ctx)                                    \
     gen_helper_f##name(cpu_fpr[rD(ctx->opcode)], cpu_env,                     \
                        cpu_fpr[rB(ctx->opcode)]);                             \
     if (set_fprf) {                                                           \
-        gen_compute_fprf(cpu_fpr[rD(ctx->opcode)]);                           \
+        gen_compute_fprf_float64(cpu_fpr[rD(ctx->opcode)]);                   \
     }                                                                         \
     if (unlikely(Rc(ctx->opcode) != 0)) {                                     \
         gen_set_cr1_from_fpscr(ctx);                                          \
@@ -139,7 +139,7 @@ static void gen_f##name(DisasContext *ctx)                                    \
     gen_helper_f##name(cpu_fpr[rD(ctx->opcode)], cpu_env,                     \
                        cpu_fpr[rB(ctx->opcode)]);                             \
     if (set_fprf) {                                                           \
-        gen_compute_fprf(cpu_fpr[rD(ctx->opcode)]);                           \
+        gen_compute_fprf_float64(cpu_fpr[rD(ctx->opcode)]);                   \
     }                                                                         \
     if (unlikely(Rc(ctx->opcode) != 0)) {                                     \
         gen_set_cr1_from_fpscr(ctx);                                          \
@@ -174,7 +174,7 @@ static void gen_frsqrtes(DisasContext *ctx)
                        cpu_fpr[rB(ctx->opcode)]);
     gen_helper_frsp(cpu_fpr[rD(ctx->opcode)], cpu_env,
                     cpu_fpr[rD(ctx->opcode)]);
-    gen_compute_fprf(cpu_fpr[rD(ctx->opcode)]);
+    gen_compute_fprf_float64(cpu_fpr[rD(ctx->opcode)]);
     if (unlikely(Rc(ctx->opcode) != 0)) {
         gen_set_cr1_from_fpscr(ctx);
     }
@@ -196,7 +196,7 @@ static void gen_fsqrt(DisasContext *ctx)
     gen_reset_fpstatus();
     gen_helper_fsqrt(cpu_fpr[rD(ctx->opcode)], cpu_env,
                      cpu_fpr[rB(ctx->opcode)]);
-    gen_compute_fprf(cpu_fpr[rD(ctx->opcode)]);
+    gen_compute_fprf_float64(cpu_fpr[rD(ctx->opcode)]);
     if (unlikely(Rc(ctx->opcode) != 0)) {
         gen_set_cr1_from_fpscr(ctx);
     }
@@ -213,7 +213,7 @@ static void gen_fsqrts(DisasContext *ctx)
                      cpu_fpr[rB(ctx->opcode)]);
     gen_helper_frsp(cpu_fpr[rD(ctx->opcode)], cpu_env,
                     cpu_fpr[rD(ctx->opcode)]);
-    gen_compute_fprf(cpu_fpr[rD(ctx->opcode)]);
+    gen_compute_fprf_float64(cpu_fpr[rD(ctx->opcode)]);
     if (unlikely(Rc(ctx->opcode) != 0)) {
         gen_set_cr1_from_fpscr(ctx);
     }
diff --git a/target/ppc/translate/fp-ops.inc.c b/target/ppc/translate/fp-ops.inc.c
index d36ab4ecc3..3c6d05a074 100644
--- a/target/ppc/translate/fp-ops.inc.c
+++ b/target/ppc/translate/fp-ops.inc.c
@@ -68,7 +68,6 @@ GEN_LDFS(lfd, ld64, 0x12, PPC_FLOAT)
 GEN_LDFS(lfs, ld32fs, 0x10, PPC_FLOAT)
 GEN_HANDLER_E(lfiwax, 0x1f, 0x17, 0x1a, 0x00000001, PPC_NONE, PPC2_ISA205),
 GEN_HANDLER_E(lfiwzx, 0x1f, 0x17, 0x1b, 0x1, PPC_NONE, PPC2_FP_CVT_ISA206),
-GEN_HANDLER_E(lfdp, 0x39, 0xFF, 0xFF, 0x00200003, PPC_NONE, PPC2_ISA205),
 GEN_HANDLER_E(lfdpx, 0x1F, 0x17, 0x18, 0x00200001, PPC_NONE, PPC2_ISA205),
 
 #define GEN_STF(name, stop, opc, type)                                        \
@@ -88,7 +87,6 @@ GEN_STXF(name, stop, 0x17, op | 0x00, type)
 GEN_STFS(stfd, st64_i64, 0x16, PPC_FLOAT)
 GEN_STFS(stfs, st32fs, 0x14, PPC_FLOAT)
 GEN_STXF(stfiw, st32fiw, 0x17, 0x1E, PPC_FLOAT_STFIWX)
-GEN_HANDLER_E(stfdp, 0x3D, 0xFF, 0xFF, 0x00200003, PPC_NONE, PPC2_ISA205),
 GEN_HANDLER_E(stfdpx, 0x1F, 0x17, 0x1C, 0x00200001, PPC_NONE, PPC2_ISA205),
 
 GEN_HANDLER(frsqrtes, 0x3B, 0x1A, 0xFF, 0x001F07C0, PPC_FLOAT_FRSQRTES),
diff --git a/target/ppc/translate/vmx-impl.inc.c b/target/ppc/translate/vmx-impl.inc.c
index 7143eb3a39..3cb6fc2926 100644
--- a/target/ppc/translate/vmx-impl.inc.c
+++ b/target/ppc/translate/vmx-impl.inc.c
@@ -340,6 +340,19 @@ static void glue(gen_, name0##_##name1)(DisasContext *ctx)              \
     }                                                                   \
 }
 
+#define GEN_VXFORM_HETRO(name, opc2, opc3)                              \
+static void glue(gen_, name)(DisasContext *ctx)                         \
+{                                                                       \
+    TCGv_ptr rb;                                                        \
+    if (unlikely(!ctx->altivec_enabled)) {                              \
+        gen_exception(ctx, POWERPC_EXCP_VPU);                           \
+        return;                                                         \
+    }                                                                   \
+    rb = gen_avr_ptr(rB(ctx->opcode));                                  \
+    gen_helper_##name(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], rb); \
+    tcg_temp_free_ptr(rb);                                              \
+}
+
 GEN_VXFORM(vaddubm, 0, 0);
 GEN_VXFORM_DUAL_EXT(vaddubm, PPC_ALTIVEC, PPC_NONE, 0,       \
                     vmul10cuq, PPC_NONE, PPC2_ISA300, 0x0000F800)
@@ -525,6 +538,16 @@ GEN_VXFORM_ENV(vaddfp, 5, 0);
 GEN_VXFORM_ENV(vsubfp, 5, 1);
 GEN_VXFORM_ENV(vmaxfp, 5, 16);
 GEN_VXFORM_ENV(vminfp, 5, 17);
+GEN_VXFORM_HETRO(vextublx, 6, 24)
+GEN_VXFORM_HETRO(vextuhlx, 6, 25)
+GEN_VXFORM_HETRO(vextuwlx, 6, 26)
+GEN_VXFORM_DUAL(vmrgow, PPC_NONE, PPC2_ALTIVEC_207,
+                vextuwlx, PPC_NONE, PPC2_ISA300)
+GEN_VXFORM_HETRO(vextubrx, 6, 28)
+GEN_VXFORM_HETRO(vextuhrx, 6, 29)
+GEN_VXFORM_HETRO(vextuwrx, 6, 30)
+GEN_VXFORM_DUAL(vmrgew, PPC_NONE, PPC2_ALTIVEC_207, \
+                vextuwrx, PPC_NONE, PPC2_ISA300)
 
 #define GEN_VXRFORM1(opname, name, str, opc2, opc3)                     \
 static void glue(gen_, name)(DisasContext *ctx)                         \
@@ -989,10 +1012,25 @@ GEN_BCD2(bcdcfn)
 GEN_BCD2(bcdctn)
 GEN_BCD2(bcdcfz)
 GEN_BCD2(bcdctz)
+GEN_BCD2(bcdcfsq)
+GEN_BCD2(bcdctsq)
+GEN_BCD2(bcdsetsgn)
+GEN_BCD(bcdcpsgn);
+GEN_BCD(bcds);
+GEN_BCD(bcdus);
+GEN_BCD(bcdsr);
+GEN_BCD(bcdtrunc);
+GEN_BCD(bcdutrunc);
 
 static void gen_xpnd04_1(DisasContext *ctx)
 {
     switch (opc4(ctx->opcode)) {
+    case 0:
+        gen_bcdctsq(ctx);
+        break;
+    case 2:
+        gen_bcdcfsq(ctx);
+        break;
     case 4:
         gen_bcdctz(ctx);
         break;
@@ -1005,6 +1043,9 @@ static void gen_xpnd04_1(DisasContext *ctx)
     case 7:
         gen_bcdcfn(ctx);
         break;
+    case 31:
+        gen_bcdsetsgn(ctx);
+        break;
     default:
         gen_invalid(ctx);
         break;
@@ -1014,6 +1055,12 @@ static void gen_xpnd04_1(DisasContext *ctx)
 static void gen_xpnd04_2(DisasContext *ctx)
 {
     switch (opc4(ctx->opcode)) {
+    case 0:
+        gen_bcdctsq(ctx);
+        break;
+    case 2:
+        gen_bcdcfsq(ctx);
+        break;
     case 4:
         gen_bcdctz(ctx);
         break;
@@ -1023,12 +1070,16 @@ static void gen_xpnd04_2(DisasContext *ctx)
     case 7:
         gen_bcdcfn(ctx);
         break;
+    case 31:
+        gen_bcdsetsgn(ctx);
+        break;
     default:
         gen_invalid(ctx);
         break;
     }
 }
 
+
 GEN_VXFORM_DUAL(vsubcuw, PPC_ALTIVEC, PPC_NONE, \
                 xpnd04_1, PPC_NONE, PPC2_ISA300)
 GEN_VXFORM_DUAL(vsubsws, PPC_ALTIVEC, PPC_NONE, \
@@ -1042,6 +1093,19 @@ GEN_VXFORM_DUAL(vsubuhm, PPC_ALTIVEC, PPC_NONE, \
                 bcdsub, PPC_NONE, PPC2_ALTIVEC_207)
 GEN_VXFORM_DUAL(vsubuhs, PPC_ALTIVEC, PPC_NONE, \
                 bcdsub, PPC_NONE, PPC2_ALTIVEC_207)
+GEN_VXFORM_DUAL(vaddshs, PPC_ALTIVEC, PPC_NONE, \
+                bcdcpsgn, PPC_NONE, PPC2_ISA300)
+GEN_VXFORM_DUAL(vsubudm, PPC2_ALTIVEC_207, PPC_NONE, \
+                bcds, PPC_NONE, PPC2_ISA300)
+GEN_VXFORM_DUAL(vsubuwm, PPC_ALTIVEC, PPC_NONE, \
+                bcdus, PPC_NONE, PPC2_ISA300)
+GEN_VXFORM_DUAL(vsubsbs, PPC_ALTIVEC, PPC_NONE, \
+                bcdtrunc, PPC_NONE, PPC2_ISA300)
+GEN_VXFORM_DUAL(vsubuqm, PPC2_ALTIVEC_207, PPC_NONE, \
+                bcdtrunc, PPC_NONE, PPC2_ISA300)
+GEN_VXFORM_DUAL(vsubcuq, PPC2_ALTIVEC_207, PPC_NONE, \
+                bcdutrunc, PPC_NONE, PPC2_ISA300)
+
 
 static void gen_vsbox(DisasContext *ctx)
 {
diff --git a/target/ppc/translate/vmx-ops.inc.c b/target/ppc/translate/vmx-ops.inc.c
index f02b3bed50..139f80cb24 100644
--- a/target/ppc/translate/vmx-ops.inc.c
+++ b/target/ppc/translate/vmx-ops.inc.c
@@ -61,8 +61,9 @@ GEN_VXFORM(vadduwm, 0, 2),
 GEN_VXFORM_207(vaddudm, 0, 3),
 GEN_VXFORM_DUAL(vsububm, bcdadd, 0, 16, PPC_ALTIVEC, PPC_NONE),
 GEN_VXFORM_DUAL(vsubuhm, bcdsub, 0, 17, PPC_ALTIVEC, PPC_NONE),
-GEN_VXFORM(vsubuwm, 0, 18),
-GEN_VXFORM_207(vsubudm, 0, 19),
+GEN_VXFORM_DUAL(vsubuwm, bcdus, 0, 18, PPC_ALTIVEC, PPC2_ISA300),
+GEN_VXFORM_DUAL(vsubudm, bcds, 0, 19, PPC2_ALTIVEC_207, PPC2_ISA300),
+GEN_VXFORM_300(bcds, 0, 27),
 GEN_VXFORM(vmaxub, 1, 0),
 GEN_VXFORM(vmaxuh, 1, 1),
 GEN_VXFORM(vmaxuw, 1, 2),
@@ -91,8 +92,12 @@ GEN_VXFORM(vmrghw, 6, 2),
 GEN_VXFORM(vmrglb, 6, 4),
 GEN_VXFORM(vmrglh, 6, 5),
 GEN_VXFORM(vmrglw, 6, 6),
-GEN_VXFORM_207(vmrgew, 6, 30),
-GEN_VXFORM_207(vmrgow, 6, 26),
+GEN_VXFORM_300(vextublx, 6, 24),
+GEN_VXFORM_300(vextuhlx, 6, 25),
+GEN_VXFORM_DUAL(vmrgow, vextuwlx, 6, 26, PPC_NONE, PPC2_ALTIVEC_207),
+GEN_VXFORM_300(vextubrx, 6, 28),
+GEN_VXFORM_300(vextuhrx, 6, 29),
+GEN_VXFORM_DUAL(vmrgew, vextuwrx, 6, 30, PPC_NONE, PPC2_ALTIVEC_207),
 GEN_VXFORM(vmuloub, 4, 0),
 GEN_VXFORM(vmulouh, 4, 1),
 GEN_VXFORM_DUAL(vmulouw, vmuluwm, 4, 2, PPC_ALTIVEC, PPC_NONE),
@@ -127,23 +132,25 @@ GEN_HANDLER_E_2(vprtybd, 0x4, 0x1, 0x18, 9, 0, PPC_NONE, PPC2_ISA300),
 GEN_HANDLER_E_2(vprtybq, 0x4, 0x1, 0x18, 10, 0, PPC_NONE, PPC2_ISA300),
 
 GEN_VXFORM_DUAL(vsubcuw, xpnd04_1, 0, 22, PPC_ALTIVEC, PPC_NONE),
+GEN_VXFORM_300(bcdsr, 0, 23),
+GEN_VXFORM_300(bcdsr, 0, 31),
 GEN_VXFORM_DUAL(vaddubs, vmul10uq, 0, 8, PPC_ALTIVEC, PPC_NONE),
 GEN_VXFORM_DUAL(vadduhs, vmul10euq, 0, 9, PPC_ALTIVEC, PPC_NONE),
 GEN_VXFORM(vadduws, 0, 10),
 GEN_VXFORM(vaddsbs, 0, 12),
-GEN_VXFORM(vaddshs, 0, 13),
+GEN_VXFORM_DUAL(vaddshs, bcdcpsgn, 0, 13, PPC_ALTIVEC, PPC_NONE),
 GEN_VXFORM(vaddsws, 0, 14),
 GEN_VXFORM_DUAL(vsububs, bcdadd, 0, 24, PPC_ALTIVEC, PPC_NONE),
 GEN_VXFORM_DUAL(vsubuhs, bcdsub, 0, 25, PPC_ALTIVEC, PPC_NONE),
 GEN_VXFORM(vsubuws, 0, 26),
-GEN_VXFORM(vsubsbs, 0, 28),
+GEN_VXFORM_DUAL(vsubsbs, bcdtrunc, 0, 28, PPC_NONE, PPC2_ISA300),
 GEN_VXFORM(vsubshs, 0, 29),
 GEN_VXFORM_DUAL(vsubsws, xpnd04_2, 0, 30, PPC_ALTIVEC, PPC_NONE),
 GEN_VXFORM_207(vadduqm, 0, 4),
 GEN_VXFORM_207(vaddcuq, 0, 5),
 GEN_VXFORM_DUAL(vaddeuqm, vaddecuq, 30, 0xFF, PPC_NONE, PPC2_ALTIVEC_207),
-GEN_VXFORM_207(vsubuqm, 0, 20),
-GEN_VXFORM_207(vsubcuq, 0, 21),
+GEN_VXFORM_DUAL(vsubuqm, bcdtrunc, 0, 20, PPC2_ALTIVEC_207, PPC2_ISA300),
+GEN_VXFORM_DUAL(vsubcuq, bcdutrunc, 0, 21, PPC2_ALTIVEC_207, PPC2_ISA300),
 GEN_VXFORM_DUAL(vsubeuqm, vsubecuq, 31, 0xFF, PPC_NONE, PPC2_ALTIVEC_207),
 GEN_VXFORM(vrlb, 2, 0),
 GEN_VXFORM(vrlh, 2, 1),
diff --git a/target/ppc/translate/vsx-impl.inc.c b/target/ppc/translate/vsx-impl.inc.c
index 5a27be4bd4..a44c0034a8 100644
--- a/target/ppc/translate/vsx-impl.inc.c
+++ b/target/ppc/translate/vsx-impl.inc.c
@@ -190,6 +190,109 @@ static void gen_lxvb16x(DisasContext *ctx)
     tcg_temp_free(EA);
 }
 
+#define VSX_VECTOR_LOAD_STORE(name, op, indexed)            \
+static void gen_##name(DisasContext *ctx)                   \
+{                                                           \
+    int xt;                                                 \
+    TCGv EA;                                                \
+    TCGv_i64 xth, xtl;                                      \
+                                                            \
+    if (indexed) {                                          \
+        xt = xT(ctx->opcode);                               \
+    } else {                                                \
+        xt = DQxT(ctx->opcode);                             \
+    }                                                       \
+    xth = cpu_vsrh(xt);                                     \
+    xtl = cpu_vsrl(xt);                                     \
+                                                            \
+    if (xt < 32) {                                          \
+        if (unlikely(!ctx->vsx_enabled)) {                  \
+            gen_exception(ctx, POWERPC_EXCP_VSXU);          \
+            return;                                         \
+        }                                                   \
+    } else {                                                \
+        if (unlikely(!ctx->altivec_enabled)) {              \
+            gen_exception(ctx, POWERPC_EXCP_VPU);           \
+            return;                                         \
+        }                                                   \
+    }                                                       \
+    gen_set_access_type(ctx, ACCESS_INT);                   \
+    EA = tcg_temp_new();                                    \
+    if (indexed) {                                          \
+        gen_addr_reg_index(ctx, EA);                        \
+    } else {                                                \
+        gen_addr_imm_index(ctx, EA, 0x0F);                  \
+    }                                                       \
+    if (ctx->le_mode) {                                     \
+        tcg_gen_qemu_##op(xtl, EA, ctx->mem_idx, MO_LEQ);   \
+        tcg_gen_addi_tl(EA, EA, 8);                         \
+        tcg_gen_qemu_##op(xth, EA, ctx->mem_idx, MO_LEQ);   \
+    } else {                                                \
+        tcg_gen_qemu_##op(xth, EA, ctx->mem_idx, MO_BEQ);   \
+        tcg_gen_addi_tl(EA, EA, 8);                         \
+        tcg_gen_qemu_##op(xtl, EA, ctx->mem_idx, MO_BEQ);   \
+    }                                                       \
+    tcg_temp_free(EA);                                      \
+}
+
+VSX_VECTOR_LOAD_STORE(lxv, ld_i64, 0)
+VSX_VECTOR_LOAD_STORE(stxv, st_i64, 0)
+VSX_VECTOR_LOAD_STORE(lxvx, ld_i64, 1)
+VSX_VECTOR_LOAD_STORE(stxvx, st_i64, 1)
+
+#ifdef TARGET_PPC64
+#define VSX_VECTOR_LOAD_STORE_LENGTH(name)                      \
+static void gen_##name(DisasContext *ctx)                       \
+{                                                               \
+    TCGv EA, xt;                                                \
+                                                                \
+    if (xT(ctx->opcode) < 32) {                                 \
+        if (unlikely(!ctx->vsx_enabled)) {                      \
+            gen_exception(ctx, POWERPC_EXCP_VSXU);              \
+            return;                                             \
+        }                                                       \
+    } else {                                                    \
+        if (unlikely(!ctx->altivec_enabled)) {                  \
+            gen_exception(ctx, POWERPC_EXCP_VPU);               \
+            return;                                             \
+        }                                                       \
+    }                                                           \
+    EA = tcg_temp_new();                                        \
+    xt = tcg_const_tl(xT(ctx->opcode));                         \
+    gen_set_access_type(ctx, ACCESS_INT);                       \
+    gen_addr_register(ctx, EA);                                 \
+    gen_helper_##name(cpu_env, EA, xt, cpu_gpr[rB(ctx->opcode)]); \
+    tcg_temp_free(EA);                                          \
+    tcg_temp_free(xt);                                          \
+}
+
+VSX_VECTOR_LOAD_STORE_LENGTH(lxvl)
+VSX_VECTOR_LOAD_STORE_LENGTH(lxvll)
+VSX_VECTOR_LOAD_STORE_LENGTH(stxvl)
+VSX_VECTOR_LOAD_STORE_LENGTH(stxvll)
+#endif
+
+#define VSX_LOAD_SCALAR_DS(name, operation)                       \
+static void gen_##name(DisasContext *ctx)                         \
+{                                                                 \
+    TCGv EA;                                                      \
+    TCGv_i64 xth = cpu_vsrh(rD(ctx->opcode) + 32);                \
+                                                                  \
+    if (unlikely(!ctx->altivec_enabled)) {                        \
+        gen_exception(ctx, POWERPC_EXCP_VPU);                     \
+        return;                                                   \
+    }                                                             \
+    gen_set_access_type(ctx, ACCESS_INT);                         \
+    EA = tcg_temp_new();                                          \
+    gen_addr_imm_index(ctx, EA, 0x03);                            \
+    gen_qemu_##operation(ctx, xth, EA);                           \
+    /* NOTE: cpu_vsrl is undefined */                             \
+    tcg_temp_free(EA);                                            \
+}
+
+VSX_LOAD_SCALAR_DS(lxsd, ld64_i64)
+VSX_LOAD_SCALAR_DS(lxssp, ld32fs)
+
 #define VSX_STORE_SCALAR(name, operation)                     \
 static void gen_##name(DisasContext *ctx)                     \
 {                                                             \
@@ -311,6 +414,27 @@ static void gen_stxvb16x(DisasContext *ctx)
     tcg_temp_free(EA);
 }
 
+#define VSX_STORE_SCALAR_DS(name, operation)                      \
+static void gen_##name(DisasContext *ctx)                         \
+{                                                                 \
+    TCGv EA;                                                      \
+    TCGv_i64 xth = cpu_vsrh(rD(ctx->opcode) + 32);                \
+                                                                  \
+    if (unlikely(!ctx->altivec_enabled)) {                        \
+        gen_exception(ctx, POWERPC_EXCP_VPU);                     \
+        return;                                                   \
+    }                                                             \
+    gen_set_access_type(ctx, ACCESS_INT);                         \
+    EA = tcg_temp_new();                                          \
+    gen_addr_imm_index(ctx, EA, 0x03);                            \
+    gen_qemu_##operation(ctx, xth, EA);                           \
+    /* NOTE: cpu_vsrl is undefined */                             \
+    tcg_temp_free(EA);                                            \
+}
+
+VSX_LOAD_SCALAR_DS(stxsd, st64_i64)
+VSX_LOAD_SCALAR_DS(stxssp, st32fs)
+
 #define MV_VSRW(name, tcgop1, tcgop2, target, source)           \
 static void gen_##name(DisasContext *ctx)                       \
 {                                                               \
@@ -517,6 +641,55 @@ VSX_SCALAR_MOVE(xsnabsdp, OP_NABS, SGN_MASK_DP)
 VSX_SCALAR_MOVE(xsnegdp, OP_NEG, SGN_MASK_DP)
 VSX_SCALAR_MOVE(xscpsgndp, OP_CPSGN, SGN_MASK_DP)
 
+#define VSX_SCALAR_MOVE_QP(name, op, sgn_mask)                    \
+static void glue(gen_, name)(DisasContext *ctx)                   \
+{                                                                 \
+    int xa;                                                       \
+    int xt = rD(ctx->opcode) + 32;                                \
+    int xb = rB(ctx->opcode) + 32;                                \
+    TCGv_i64 xah, xbh, xbl, sgm;                                  \
+                                                                  \
+    if (unlikely(!ctx->vsx_enabled)) {                            \
+        gen_exception(ctx, POWERPC_EXCP_VSXU);                    \
+        return;                                                   \
+    }                                                             \
+    xbh = tcg_temp_new_i64();                                     \
+    xbl = tcg_temp_new_i64();                                     \
+    sgm = tcg_temp_new_i64();                                     \
+    tcg_gen_mov_i64(xbh, cpu_vsrh(xb));                           \
+    tcg_gen_mov_i64(xbl, cpu_vsrl(xb));                           \
+    tcg_gen_movi_i64(sgm, sgn_mask);                              \
+    switch (op) {                                                 \
+    case OP_ABS:                                                  \
+        tcg_gen_andc_i64(xbh, xbh, sgm);                          \
+        break;                                                    \
+    case OP_NABS:                                                 \
+        tcg_gen_or_i64(xbh, xbh, sgm);                            \
+        break;                                                    \
+    case OP_NEG:                                                  \
+        tcg_gen_xor_i64(xbh, xbh, sgm);                           \
+        break;                                                    \
+    case OP_CPSGN:                                                \
+        xah = tcg_temp_new_i64();                                 \
+        xa = rA(ctx->opcode) + 32;                                \
+        tcg_gen_and_i64(xah, cpu_vsrh(xa), sgm);                  \
+        tcg_gen_andc_i64(xbh, xbh, sgm);                          \
+        tcg_gen_or_i64(xbh, xbh, xah);                            \
+        tcg_temp_free_i64(xah);                                   \
+        break;                                                    \
+    }                                                             \
+    tcg_gen_mov_i64(cpu_vsrh(xt), xbh);                           \
+    tcg_gen_mov_i64(cpu_vsrl(xt), xbl);                           \
+    tcg_temp_free_i64(xbl);                                       \
+    tcg_temp_free_i64(xbh);                                       \
+    tcg_temp_free_i64(sgm);                                       \
+}
+
+VSX_SCALAR_MOVE_QP(xsabsqp, OP_ABS, SGN_MASK_DP)
+VSX_SCALAR_MOVE_QP(xsnabsqp, OP_NABS, SGN_MASK_DP)
+VSX_SCALAR_MOVE_QP(xsnegqp, OP_NEG, SGN_MASK_DP)
+VSX_SCALAR_MOVE_QP(xscpsgnqp, OP_CPSGN, SGN_MASK_DP)
+
 #define VSX_VECTOR_MOVE(name, op, sgn_mask)                      \
 static void glue(gen_, name)(DisasContext * ctx)                 \
     {                                                            \
@@ -604,9 +777,12 @@ static void gen_##name(DisasContext * ctx)                    \
 }
 
 GEN_VSX_HELPER_2(xsadddp, 0x00, 0x04, 0, PPC2_VSX)
+GEN_VSX_HELPER_2(xsaddqp, 0x04, 0x00, 0, PPC2_ISA300)
 GEN_VSX_HELPER_2(xssubdp, 0x00, 0x05, 0, PPC2_VSX)
 GEN_VSX_HELPER_2(xsmuldp, 0x00, 0x06, 0, PPC2_VSX)
+GEN_VSX_HELPER_2(xsmulqp, 0x04, 0x01, 0, PPC2_ISA300)
 GEN_VSX_HELPER_2(xsdivdp, 0x00, 0x07, 0, PPC2_VSX)
+GEN_VSX_HELPER_2(xsdivqp, 0x04, 0x11, 0, PPC2_ISA300)
 GEN_VSX_HELPER_2(xsredp, 0x14, 0x05, 0, PPC2_VSX)
 GEN_VSX_HELPER_2(xssqrtdp, 0x16, 0x04, 0, PPC2_VSX)
 GEN_VSX_HELPER_2(xsrsqrtedp, 0x14, 0x04, 0, PPC2_VSX)
@@ -624,12 +800,23 @@ GEN_VSX_HELPER_2(xscmpeqdp, 0x0C, 0x00, 0, PPC2_ISA300)
 GEN_VSX_HELPER_2(xscmpgtdp, 0x0C, 0x01, 0, PPC2_ISA300)
 GEN_VSX_HELPER_2(xscmpgedp, 0x0C, 0x02, 0, PPC2_ISA300)
 GEN_VSX_HELPER_2(xscmpnedp, 0x0C, 0x03, 0, PPC2_ISA300)
+GEN_VSX_HELPER_2(xscmpexpdp, 0x0C, 0x07, 0, PPC2_ISA300)
+GEN_VSX_HELPER_2(xscmpexpqp, 0x04, 0x05, 0, PPC2_ISA300)
 GEN_VSX_HELPER_2(xscmpodp, 0x0C, 0x05, 0, PPC2_VSX)
 GEN_VSX_HELPER_2(xscmpudp, 0x0C, 0x04, 0, PPC2_VSX)
+GEN_VSX_HELPER_2(xscmpoqp, 0x04, 0x04, 0, PPC2_VSX)
+GEN_VSX_HELPER_2(xscmpuqp, 0x04, 0x14, 0, PPC2_VSX)
 GEN_VSX_HELPER_2(xsmaxdp, 0x00, 0x14, 0, PPC2_VSX)
 GEN_VSX_HELPER_2(xsmindp, 0x00, 0x15, 0, PPC2_VSX)
+GEN_VSX_HELPER_2(xscvdphp, 0x16, 0x15, 0x11, PPC2_ISA300)
 GEN_VSX_HELPER_2(xscvdpsp, 0x12, 0x10, 0, PPC2_VSX)
+GEN_VSX_HELPER_2(xscvdpqp, 0x04, 0x1A, 0x16, PPC2_ISA300)
 GEN_VSX_HELPER_XT_XB_ENV(xscvdpspn, 0x16, 0x10, 0, PPC2_VSX207)
+GEN_VSX_HELPER_2(xscvqpdp, 0x04, 0x1A, 0x14, PPC2_ISA300)
+GEN_VSX_HELPER_2(xscvqpsdz, 0x04, 0x1A, 0x19, PPC2_ISA300)
+GEN_VSX_HELPER_2(xscvqpswz, 0x04, 0x1A, 0x09, PPC2_ISA300)
+GEN_VSX_HELPER_2(xscvhpdp, 0x16, 0x15, 0x10, PPC2_ISA300)
+GEN_VSX_HELPER_2(xscvsdqp, 0x04, 0x1A, 0x0A, PPC2_ISA300)
 GEN_VSX_HELPER_2(xscvspdp, 0x12, 0x14, 0, PPC2_VSX)
 GEN_VSX_HELPER_XT_XB_ENV(xscvspdpn, 0x16, 0x14, 0, PPC2_VSX207)
 GEN_VSX_HELPER_2(xscvdpsxds, 0x10, 0x15, 0, PPC2_VSX)
@@ -637,6 +824,7 @@ GEN_VSX_HELPER_2(xscvdpsxws, 0x10, 0x05, 0, PPC2_VSX)
 GEN_VSX_HELPER_2(xscvdpuxds, 0x10, 0x14, 0, PPC2_VSX)
 GEN_VSX_HELPER_2(xscvdpuxws, 0x10, 0x04, 0, PPC2_VSX)
 GEN_VSX_HELPER_2(xscvsxddp, 0x10, 0x17, 0, PPC2_VSX)
+GEN_VSX_HELPER_2(xscvudqp, 0x04, 0x1A, 0x02, PPC2_ISA300)
 GEN_VSX_HELPER_2(xscvuxddp, 0x10, 0x16, 0, PPC2_VSX)
 GEN_VSX_HELPER_2(xsrdpi, 0x12, 0x04, 0, PPC2_VSX)
 GEN_VSX_HELPER_2(xsrdpic, 0x16, 0x06, 0, PPC2_VSX)
@@ -662,6 +850,9 @@ GEN_VSX_HELPER_2(xsnmsubasp, 0x04, 0x12, 0, PPC2_VSX207)
 GEN_VSX_HELPER_2(xsnmsubmsp, 0x04, 0x13, 0, PPC2_VSX207)
 GEN_VSX_HELPER_2(xscvsxdsp, 0x10, 0x13, 0, PPC2_VSX207)
 GEN_VSX_HELPER_2(xscvuxdsp, 0x10, 0x12, 0, PPC2_VSX207)
+GEN_VSX_HELPER_2(xststdcsp, 0x14, 0x12, 0, PPC2_ISA300)
+GEN_VSX_HELPER_2(xststdcdp, 0x14, 0x16, 0, PPC2_ISA300)
+GEN_VSX_HELPER_2(xststdcqp, 0x04, 0x16, 0, PPC2_ISA300)
 
 GEN_VSX_HELPER_2(xvadddp, 0x00, 0x0C, 0, PPC2_VSX)
 GEN_VSX_HELPER_2(xvsubdp, 0x00, 0x0D, 0, PPC2_VSX)
@@ -725,6 +916,8 @@ GEN_VSX_HELPER_2(xvcmpgtsp, 0x0C, 0x09, 0, PPC2_VSX)
 GEN_VSX_HELPER_2(xvcmpgesp, 0x0C, 0x0A, 0, PPC2_VSX)
 GEN_VSX_HELPER_2(xvcmpnesp, 0x0C, 0x0B, 0, PPC2_VSX)
 GEN_VSX_HELPER_2(xvcvspdp, 0x12, 0x1C, 0, PPC2_VSX)
+GEN_VSX_HELPER_2(xvcvhpsp, 0x16, 0x1D, 0x18, PPC2_ISA300)
+GEN_VSX_HELPER_2(xvcvsphp, 0x16, 0x1D, 0x19, PPC2_ISA300)
 GEN_VSX_HELPER_2(xvcvspsxds, 0x10, 0x19, 0, PPC2_VSX)
 GEN_VSX_HELPER_2(xvcvspsxws, 0x10, 0x09, 0, PPC2_VSX)
 GEN_VSX_HELPER_2(xvcvspuxds, 0x10, 0x18, 0, PPC2_VSX)
@@ -738,6 +931,10 @@ GEN_VSX_HELPER_2(xvrspic, 0x16, 0x0A, 0, PPC2_VSX)
 GEN_VSX_HELPER_2(xvrspim, 0x12, 0x0B, 0, PPC2_VSX)
 GEN_VSX_HELPER_2(xvrspip, 0x12, 0x0A, 0, PPC2_VSX)
 GEN_VSX_HELPER_2(xvrspiz, 0x12, 0x09, 0, PPC2_VSX)
+GEN_VSX_HELPER_2(xvtstdcsp, 0x14, 0x1A, 0, PPC2_VSX)
+GEN_VSX_HELPER_2(xvtstdcdp, 0x14, 0x1E, 0, PPC2_VSX)
+GEN_VSX_HELPER_2(xxperm, 0x08, 0x03, 0, PPC2_ISA300)
+GEN_VSX_HELPER_2(xxpermr, 0x08, 0x07, 0, PPC2_ISA300)
 
 static void gen_xxbrd(DisasContext *ctx)
 {
@@ -1001,6 +1198,293 @@ static void gen_xxsldwi(DisasContext *ctx)
     tcg_temp_free_i64(xtl);
 }
 
+#define VSX_EXTRACT_INSERT(name)                                \
+static void gen_##name(DisasContext *ctx)                       \
+{                                                               \
+    TCGv xt, xb;                                                \
+    TCGv_i32 t0 = tcg_temp_new_i32();                           \
+    uint8_t uimm = UIMM4(ctx->opcode);                          \
+                                                                \
+    if (unlikely(!ctx->vsx_enabled)) {                          \
+        gen_exception(ctx, POWERPC_EXCP_VSXU);                  \
+        return;                                                 \
+    }                                                           \
+    xt = tcg_const_tl(xT(ctx->opcode));                         \
+    xb = tcg_const_tl(xB(ctx->opcode));                         \
+    /* uimm > 15 out of bound and for                           \
+     * uimm > 12 handle as per hardware in helper               \
+     */                                                         \
+    if (uimm > 15) {                                            \
+        tcg_gen_movi_i64(cpu_vsrh(xT(ctx->opcode)), 0);         \
+        tcg_gen_movi_i64(cpu_vsrl(xT(ctx->opcode)), 0);         \
+        return;                                                 \
+    }                                                           \
+    tcg_gen_movi_i32(t0, uimm);                                 \
+    gen_helper_##name(cpu_env, xt, xb, t0);                     \
+    tcg_temp_free(xb);                                          \
+    tcg_temp_free(xt);                                          \
+    tcg_temp_free_i32(t0);                                      \
+}
+
+VSX_EXTRACT_INSERT(xxextractuw)
+VSX_EXTRACT_INSERT(xxinsertw)
+
+#ifdef TARGET_PPC64
+static void gen_xsxexpdp(DisasContext *ctx)
+{
+    TCGv rt = cpu_gpr[rD(ctx->opcode)];
+    if (unlikely(!ctx->vsx_enabled)) {
+        gen_exception(ctx, POWERPC_EXCP_VSXU);
+        return;
+    }
+    tcg_gen_shri_i64(rt, cpu_vsrh(xB(ctx->opcode)), 52);
+    tcg_gen_andi_i64(rt, rt, 0x7FF);
+}
+
+static void gen_xsxexpqp(DisasContext *ctx)
+{
+    TCGv_i64 xth = cpu_vsrh(rD(ctx->opcode) + 32);
+    TCGv_i64 xtl = cpu_vsrl(rD(ctx->opcode) + 32);
+    TCGv_i64 xbh = cpu_vsrh(rB(ctx->opcode) + 32);
+
+    if (unlikely(!ctx->vsx_enabled)) {
+        gen_exception(ctx, POWERPC_EXCP_VSXU);
+        return;
+    }
+    tcg_gen_shri_i64(xth, xbh, 48);
+    tcg_gen_andi_i64(xth, xth, 0x7FFF);
+    tcg_gen_movi_i64(xtl, 0);
+}
+
+static void gen_xsiexpdp(DisasContext *ctx)
+{
+    TCGv_i64 xth = cpu_vsrh(xT(ctx->opcode));
+    TCGv ra = cpu_gpr[rA(ctx->opcode)];
+    TCGv rb = cpu_gpr[rB(ctx->opcode)];
+    TCGv_i64 t0;
+
+    if (unlikely(!ctx->vsx_enabled)) {
+        gen_exception(ctx, POWERPC_EXCP_VSXU);
+        return;
+    }
+    t0 = tcg_temp_new_i64();
+    tcg_gen_andi_i64(xth, ra, 0x800FFFFFFFFFFFFF);
+    tcg_gen_andi_i64(t0, rb, 0x7FF);
+    tcg_gen_shli_i64(t0, t0, 52);
+    tcg_gen_or_i64(xth, xth, t0);
+    /* dword[1] is undefined */
+    tcg_temp_free_i64(t0);
+}
+
+static void gen_xsiexpqp(DisasContext *ctx)
+{
+    TCGv_i64 xth = cpu_vsrh(rD(ctx->opcode) + 32);
+    TCGv_i64 xtl = cpu_vsrl(rD(ctx->opcode) + 32);
+    TCGv_i64 xah = cpu_vsrh(rA(ctx->opcode) + 32);
+    TCGv_i64 xal = cpu_vsrl(rA(ctx->opcode) + 32);
+    TCGv_i64 xbh = cpu_vsrh(rB(ctx->opcode) + 32);
+    TCGv_i64 t0;
+
+    if (unlikely(!ctx->vsx_enabled)) {
+        gen_exception(ctx, POWERPC_EXCP_VSXU);
+        return;
+    }
+    t0 = tcg_temp_new_i64();
+    tcg_gen_andi_i64(xth, xah, 0x8000FFFFFFFFFFFF);
+    tcg_gen_andi_i64(t0, xbh, 0x7FFF);
+    tcg_gen_shli_i64(t0, t0, 48);
+    tcg_gen_or_i64(xth, xth, t0);
+    tcg_gen_mov_i64(xtl, xal);
+    tcg_temp_free_i64(t0);
+}
+
+static void gen_xsxsigdp(DisasContext *ctx)
+{
+    TCGv rt = cpu_gpr[rD(ctx->opcode)];
+    TCGv_i64 t0, zr, nan, exp;
+
+    if (unlikely(!ctx->vsx_enabled)) {
+        gen_exception(ctx, POWERPC_EXCP_VSXU);
+        return;
+    }
+    exp = tcg_temp_new_i64();
+    t0 = tcg_temp_new_i64();
+    zr = tcg_const_i64(0);
+    nan = tcg_const_i64(2047);
+
+    tcg_gen_shri_i64(exp, cpu_vsrh(xB(ctx->opcode)), 52);
+    tcg_gen_andi_i64(exp, exp, 0x7FF);
+    tcg_gen_movi_i64(t0, 0x0010000000000000);
+    tcg_gen_movcond_i64(TCG_COND_EQ, t0, exp, zr, zr, t0);
+    tcg_gen_movcond_i64(TCG_COND_EQ, t0, exp, nan, zr, t0);
+    tcg_gen_andi_i64(rt, cpu_vsrh(xB(ctx->opcode)), 0x000FFFFFFFFFFFFF);
+    tcg_gen_or_i64(rt, rt, t0);
+
+    tcg_temp_free_i64(t0);
+    tcg_temp_free_i64(exp);
+    tcg_temp_free_i64(zr);
+    tcg_temp_free_i64(nan);
+}
+
+static void gen_xsxsigqp(DisasContext *ctx)
+{
+    TCGv_i64 t0, zr, nan, exp;
+    TCGv_i64 xth = cpu_vsrh(rD(ctx->opcode) + 32);
+    TCGv_i64 xtl = cpu_vsrl(rD(ctx->opcode) + 32);
+
+    if (unlikely(!ctx->vsx_enabled)) {
+        gen_exception(ctx, POWERPC_EXCP_VSXU);
+        return;
+    }
+    exp = tcg_temp_new_i64();
+    t0 = tcg_temp_new_i64();
+    zr = tcg_const_i64(0);
+    nan = tcg_const_i64(32767);
+
+    tcg_gen_shri_i64(exp, cpu_vsrh(rB(ctx->opcode) + 32), 48);
+    tcg_gen_andi_i64(exp, exp, 0x7FFF);
+    tcg_gen_movi_i64(t0, 0x0001000000000000);
+    tcg_gen_movcond_i64(TCG_COND_EQ, t0, exp, zr, zr, t0);
+    tcg_gen_movcond_i64(TCG_COND_EQ, t0, exp, nan, zr, t0);
+    tcg_gen_andi_i64(xth, cpu_vsrh(rB(ctx->opcode) + 32), 0x0000FFFFFFFFFFFF);
+    tcg_gen_or_i64(xth, xth, t0);
+    tcg_gen_mov_i64(xtl, cpu_vsrl(rB(ctx->opcode) + 32));
+
+    tcg_temp_free_i64(t0);
+    tcg_temp_free_i64(exp);
+    tcg_temp_free_i64(zr);
+    tcg_temp_free_i64(nan);
+}
+#endif
+
+static void gen_xviexpsp(DisasContext *ctx)
+{
+    TCGv_i64 xth = cpu_vsrh(xT(ctx->opcode));
+    TCGv_i64 xtl = cpu_vsrl(xT(ctx->opcode));
+    TCGv_i64 xah = cpu_vsrh(xA(ctx->opcode));
+    TCGv_i64 xal = cpu_vsrl(xA(ctx->opcode));
+    TCGv_i64 xbh = cpu_vsrh(xB(ctx->opcode));
+    TCGv_i64 xbl = cpu_vsrl(xB(ctx->opcode));
+    TCGv_i64 t0;
+
+    if (unlikely(!ctx->vsx_enabled)) {
+        gen_exception(ctx, POWERPC_EXCP_VSXU);
+        return;
+    }
+    t0 = tcg_temp_new_i64();
+    tcg_gen_andi_i64(xth, xah, 0x807FFFFF807FFFFF);
+    tcg_gen_andi_i64(t0, xbh, 0xFF000000FF);
+    tcg_gen_shli_i64(t0, t0, 23);
+    tcg_gen_or_i64(xth, xth, t0);
+    tcg_gen_andi_i64(xtl, xal, 0x807FFFFF807FFFFF);
+    tcg_gen_andi_i64(t0, xbl, 0xFF000000FF);
+    tcg_gen_shli_i64(t0, t0, 23);
+    tcg_gen_or_i64(xtl, xtl, t0);
+    tcg_temp_free_i64(t0);
+}
+
+static void gen_xviexpdp(DisasContext *ctx)
+{
+    TCGv_i64 xth = cpu_vsrh(xT(ctx->opcode));
+    TCGv_i64 xtl = cpu_vsrl(xT(ctx->opcode));
+    TCGv_i64 xah = cpu_vsrh(xA(ctx->opcode));
+    TCGv_i64 xal = cpu_vsrl(xA(ctx->opcode));
+    TCGv_i64 xbh = cpu_vsrh(xB(ctx->opcode));
+    TCGv_i64 xbl = cpu_vsrl(xB(ctx->opcode));
+    TCGv_i64 t0;
+
+    if (unlikely(!ctx->vsx_enabled)) {
+        gen_exception(ctx, POWERPC_EXCP_VSXU);
+        return;
+    }
+    t0 = tcg_temp_new_i64();
+    tcg_gen_andi_i64(xth, xah, 0x800FFFFFFFFFFFFF);
+    tcg_gen_andi_i64(t0, xbh, 0x7FF);
+    tcg_gen_shli_i64(t0, t0, 52);
+    tcg_gen_or_i64(xth, xth, t0);
+    tcg_gen_andi_i64(xtl, xal, 0x800FFFFFFFFFFFFF);
+    tcg_gen_andi_i64(t0, xbl, 0x7FF);
+    tcg_gen_shli_i64(t0, t0, 52);
+    tcg_gen_or_i64(xtl, xtl, t0);
+    tcg_temp_free_i64(t0);
+}
+
+static void gen_xvxexpsp(DisasContext *ctx)
+{
+    TCGv_i64 xth = cpu_vsrh(xT(ctx->opcode));
+    TCGv_i64 xtl = cpu_vsrl(xT(ctx->opcode));
+    TCGv_i64 xbh = cpu_vsrh(xB(ctx->opcode));
+    TCGv_i64 xbl = cpu_vsrl(xB(ctx->opcode));
+
+    if (unlikely(!ctx->vsx_enabled)) {
+        gen_exception(ctx, POWERPC_EXCP_VSXU);
+        return;
+    }
+    tcg_gen_shri_i64(xth, xbh, 23);
+    tcg_gen_andi_i64(xth, xth, 0xFF000000FF);
+    tcg_gen_shri_i64(xtl, xbl, 23);
+    tcg_gen_andi_i64(xtl, xtl, 0xFF000000FF);
+}
+
+static void gen_xvxexpdp(DisasContext *ctx)
+{
+    TCGv_i64 xth = cpu_vsrh(xT(ctx->opcode));
+    TCGv_i64 xtl = cpu_vsrl(xT(ctx->opcode));
+    TCGv_i64 xbh = cpu_vsrh(xB(ctx->opcode));
+    TCGv_i64 xbl = cpu_vsrl(xB(ctx->opcode));
+
+    if (unlikely(!ctx->vsx_enabled)) {
+        gen_exception(ctx, POWERPC_EXCP_VSXU);
+        return;
+    }
+    tcg_gen_shri_i64(xth, xbh, 52);
+    tcg_gen_andi_i64(xth, xth, 0x7FF);
+    tcg_gen_shri_i64(xtl, xbl, 52);
+    tcg_gen_andi_i64(xtl, xtl, 0x7FF);
+}
+
+GEN_VSX_HELPER_2(xvxsigsp, 0x00, 0x04, 0, PPC2_ISA300)
+
+static void gen_xvxsigdp(DisasContext *ctx)
+{
+    TCGv_i64 xth = cpu_vsrh(xT(ctx->opcode));
+    TCGv_i64 xtl = cpu_vsrl(xT(ctx->opcode));
+    TCGv_i64 xbh = cpu_vsrh(xB(ctx->opcode));
+    TCGv_i64 xbl = cpu_vsrl(xB(ctx->opcode));
+
+    TCGv_i64 t0, zr, nan, exp;
+
+    if (unlikely(!ctx->vsx_enabled)) {
+        gen_exception(ctx, POWERPC_EXCP_VSXU);
+        return;
+    }
+    exp = tcg_temp_new_i64();
+    t0 = tcg_temp_new_i64();
+    zr = tcg_const_i64(0);
+    nan = tcg_const_i64(2047);
+
+    tcg_gen_shri_i64(exp, xbh, 52);
+    tcg_gen_andi_i64(exp, exp, 0x7FF);
+    tcg_gen_movi_i64(t0, 0x0010000000000000);
+    tcg_gen_movcond_i64(TCG_COND_EQ, t0, exp, zr, zr, t0);
+    tcg_gen_movcond_i64(TCG_COND_EQ, t0, exp, nan, zr, t0);
+    tcg_gen_andi_i64(xth, xbh, 0x000FFFFFFFFFFFFF);
+    tcg_gen_or_i64(xth, xth, t0);
+
+    tcg_gen_shri_i64(exp, xbl, 52);
+    tcg_gen_andi_i64(exp, exp, 0x7FF);
+    tcg_gen_movi_i64(t0, 0x0010000000000000);
+    tcg_gen_movcond_i64(TCG_COND_EQ, t0, exp, zr, zr, t0);
+    tcg_gen_movcond_i64(TCG_COND_EQ, t0, exp, nan, zr, t0);
+    tcg_gen_andi_i64(xtl, xbl, 0x000FFFFFFFFFFFFF);
+    tcg_gen_or_i64(xtl, xtl, t0);
+
+    tcg_temp_free_i64(t0);
+    tcg_temp_free_i64(exp);
+    tcg_temp_free_i64(zr);
+    tcg_temp_free_i64(nan);
+}
+
 #undef GEN_XX2FORM
 #undef GEN_XX3FORM
 #undef GEN_XX2IFORM
diff --git a/target/ppc/translate/vsx-ops.inc.c b/target/ppc/translate/vsx-ops.inc.c
index 3d9104155a..7dc9f6f477 100644
--- a/target/ppc/translate/vsx-ops.inc.c
+++ b/target/ppc/translate/vsx-ops.inc.c
@@ -9,6 +9,11 @@ GEN_HANDLER_E(lxvdsx, 0x1F, 0x0C, 0x0A, 0, PPC_NONE, PPC2_VSX),
 GEN_HANDLER_E(lxvw4x, 0x1F, 0x0C, 0x18, 0, PPC_NONE, PPC2_VSX),
 GEN_HANDLER_E(lxvh8x, 0x1F, 0x0C, 0x19, 0, PPC_NONE,  PPC2_ISA300),
 GEN_HANDLER_E(lxvb16x, 0x1F, 0x0C, 0x1B, 0, PPC_NONE, PPC2_ISA300),
+GEN_HANDLER_E(lxvx, 0x1F, 0x0C, 0x08, 0x00000040, PPC_NONE, PPC2_ISA300),
+#if defined(TARGET_PPC64)
+GEN_HANDLER_E(lxvl, 0x1F, 0x0D, 0x08, 0, PPC_NONE, PPC2_ISA300),
+GEN_HANDLER_E(lxvll, 0x1F, 0x0D, 0x09, 0, PPC_NONE, PPC2_ISA300),
+#endif
 
 GEN_HANDLER_E(stxsdx, 0x1F, 0xC, 0x16, 0, PPC_NONE, PPC2_VSX),
 GEN_HANDLER_E(stxsibx, 0x1F, 0xD, 0x1C, 0, PPC_NONE, PPC2_ISA300),
@@ -19,6 +24,11 @@ GEN_HANDLER_E(stxvd2x, 0x1F, 0xC, 0x1E, 0, PPC_NONE, PPC2_VSX),
 GEN_HANDLER_E(stxvw4x, 0x1F, 0xC, 0x1C, 0, PPC_NONE, PPC2_VSX),
 GEN_HANDLER_E(stxvh8x, 0x1F, 0x0C, 0x1D, 0, PPC_NONE,  PPC2_ISA300),
 GEN_HANDLER_E(stxvb16x, 0x1F, 0x0C, 0x1F, 0, PPC_NONE, PPC2_ISA300),
+GEN_HANDLER_E(stxvx, 0x1F, 0x0C, 0x0C, 0, PPC_NONE, PPC2_ISA300),
+#if defined(TARGET_PPC64)
+GEN_HANDLER_E(stxvl, 0x1F, 0x0D, 0x0C, 0, PPC_NONE, PPC2_ISA300),
+GEN_HANDLER_E(stxvll, 0x1F, 0x0D, 0x0D, 0, PPC_NONE, PPC2_ISA300),
+#endif
 
 GEN_HANDLER_E(mfvsrwz, 0x1F, 0x13, 0x03, 0x0000F800, PPC_NONE, PPC2_VSX207),
 GEN_HANDLER_E(mtvsrwa, 0x1F, 0x13, 0x06, 0x0000F800, PPC_NONE, PPC2_VSX207),
@@ -39,6 +49,10 @@ GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 1, opc3, 0, PPC_NONE, fl2)
 GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 0, opc3, 0, PPC_NONE, fl2), \
 GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 1, opc3, 0, PPC_NONE, fl2)
 
+#define GEN_XX2FORM_EXT(name, opc2, opc3, fl2)                          \
+GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 0, opc3, 0x00100000, PPC_NONE, fl2), \
+GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 1, opc3, 0x00100000, PPC_NONE, fl2)
+
 #define GEN_XX2FORM_EO(name, opc2, opc3, opc4, fl2)                          \
 GEN_HANDLER2_E_2(name, #name, 0x3C, opc2 | 0, opc3, opc4, 0, PPC_NONE, fl2), \
 GEN_HANDLER2_E_2(name, #name, 0x3C, opc2 | 1, opc3, opc4, 0, PPC_NONE, fl2)
@@ -83,11 +97,54 @@ GEN_HANDLER2_E(name, #name, 0x3C, opc2|0x01, opc3|0x0C, 0, PPC_NONE, PPC2_VSX),\
 GEN_HANDLER2_E(name, #name, 0x3C, opc2|0x02, opc3|0x0C, 0, PPC_NONE, PPC2_VSX),\
 GEN_HANDLER2_E(name, #name, 0x3C, opc2|0x03, opc3|0x0C, 0, PPC_NONE, PPC2_VSX)
 
+#define GEN_VSX_XFORM_300(name, opc2, opc3, inval) \
+GEN_HANDLER_E(name, 0x3F, opc2, opc3, inval, PPC_NONE, PPC2_ISA300)
+
+#define GEN_VSX_XFORM_300_EO(name, opc2, opc3, opc4, inval)             \
+GEN_HANDLER_E_2(name, 0x3F, opc2, opc3, opc4, inval, PPC_NONE, PPC2_ISA300)
+
 GEN_XX2FORM(xsabsdp, 0x12, 0x15, PPC2_VSX),
 GEN_XX2FORM(xsnabsdp, 0x12, 0x16, PPC2_VSX),
 GEN_XX2FORM(xsnegdp, 0x12, 0x17, PPC2_VSX),
 GEN_XX3FORM(xscpsgndp, 0x00, 0x16, PPC2_VSX),
 
+GEN_VSX_XFORM_300_EO(xsabsqp, 0x04, 0x19, 0x00, 0x00000001),
+GEN_VSX_XFORM_300_EO(xsnabsqp, 0x04, 0x19, 0x08, 0x00000001),
+GEN_VSX_XFORM_300_EO(xsnegqp, 0x04, 0x19, 0x10, 0x00000001),
+GEN_VSX_XFORM_300(xscpsgnqp, 0x04, 0x03, 0x00000001),
+GEN_VSX_XFORM_300_EO(xscvdpqp, 0x04, 0x1A, 0x16, 0x00000001),
+GEN_VSX_XFORM_300_EO(xscvqpdp, 0x04, 0x1A, 0x14, 0x0),
+GEN_VSX_XFORM_300_EO(xscvqpsdz, 0x04, 0x1A, 0x19, 0x00000001),
+GEN_VSX_XFORM_300_EO(xscvqpswz, 0x04, 0x1A, 0x09, 0x00000001),
+
+#ifdef TARGET_PPC64
+GEN_XX2FORM_EO(xsxexpdp, 0x16, 0x15, 0x00, PPC2_ISA300),
+GEN_VSX_XFORM_300_EO(xsxexpqp, 0x04, 0x19, 0x02, 0x00000001),
+GEN_XX2FORM_EO(xsxsigdp, 0x16, 0x15, 0x01, PPC2_ISA300),
+GEN_VSX_XFORM_300_EO(xsxsigqp, 0x04, 0x19, 0x12, 0x00000001),
+GEN_HANDLER_E(xsiexpdp, 0x3C, 0x16, 0x1C, 0, PPC_NONE, PPC2_ISA300),
+GEN_VSX_XFORM_300(xsiexpqp, 0x4, 0x1B, 0x00000001),
+#endif
+
+GEN_XX2FORM(xststdcdp, 0x14, 0x16, PPC2_ISA300),
+GEN_XX2FORM(xststdcsp, 0x14, 0x12, PPC2_ISA300),
+GEN_VSX_XFORM_300(xststdcqp, 0x04, 0x16, 0x00000001),
+
+GEN_XX3FORM(xviexpsp, 0x00, 0x1B, PPC2_ISA300),
+GEN_XX3FORM(xviexpdp, 0x00, 0x1F, PPC2_ISA300),
+GEN_XX2FORM_EO(xvxexpdp, 0x16, 0x1D, 0x00, PPC2_ISA300),
+GEN_XX2FORM_EO(xvxsigdp, 0x16, 0x1D, 0x01, PPC2_ISA300),
+GEN_XX2FORM_EO(xvxexpsp, 0x16, 0x1D, 0x08, PPC2_ISA300),
+GEN_XX2FORM_EO(xvxsigsp, 0x16, 0x1D, 0x09, PPC2_ISA300),
+
+/* DCMX  =  bit[25] << 6 | bit[29] << 5 | bit[11:15] */
+#define GEN_XX2FORM_DCMX(name, opc2, opc3, fl2) \
+GEN_XX3FORM(name, opc2, opc3 | 0, fl2),         \
+GEN_XX3FORM(name, opc2, opc3 | 1, fl2)
+
+GEN_XX2FORM_DCMX(xvtstdcdp, 0x14, 0x1E, PPC2_ISA300),
+GEN_XX2FORM_DCMX(xvtstdcsp, 0x14, 0x1A, PPC2_ISA300),
+
 GEN_XX2FORM(xvabsdp, 0x12, 0x1D, PPC2_VSX),
 GEN_XX2FORM(xvnabsdp, 0x12, 0x1E, PPC2_VSX),
 GEN_XX2FORM(xvnegdp, 0x12, 0x1F, PPC2_VSX),
@@ -98,8 +155,10 @@ GEN_XX2FORM(xvnegsp, 0x12, 0x1B, PPC2_VSX),
 GEN_XX3FORM(xvcpsgnsp, 0x00, 0x1A, PPC2_VSX),
 
 GEN_XX3FORM(xsadddp, 0x00, 0x04, PPC2_VSX),
+GEN_VSX_XFORM_300(xsaddqp, 0x04, 0x00, 0x0),
 GEN_XX3FORM(xssubdp, 0x00, 0x05, PPC2_VSX),
 GEN_XX3FORM(xsmuldp, 0x00, 0x06, PPC2_VSX),
+GEN_VSX_XFORM_300(xsmulqp, 0x04, 0x01, 0x0),
 GEN_XX3FORM(xsdivdp, 0x00, 0x07, PPC2_VSX),
 GEN_XX2FORM(xsredp,  0x14, 0x05, PPC2_VSX),
 GEN_XX2FORM(xssqrtdp,  0x16, 0x04, PPC2_VSX),
@@ -118,12 +177,19 @@ GEN_XX3FORM(xscmpeqdp, 0x0C, 0x00, PPC2_ISA300),
 GEN_XX3FORM(xscmpgtdp, 0x0C, 0x01, PPC2_ISA300),
 GEN_XX3FORM(xscmpgedp, 0x0C, 0x02, PPC2_ISA300),
 GEN_XX3FORM(xscmpnedp, 0x0C, 0x03, PPC2_ISA300),
+GEN_XX3FORM(xscmpexpdp, 0x0C, 0x07, PPC2_ISA300),
+GEN_VSX_XFORM_300(xscmpexpqp, 0x04, 0x05, 0x00600001),
 GEN_XX2IFORM(xscmpodp,  0x0C, 0x05, PPC2_VSX),
 GEN_XX2IFORM(xscmpudp,  0x0C, 0x04, PPC2_VSX),
+GEN_VSX_XFORM_300(xscmpoqp, 0x04, 0x04, 0x00600001),
+GEN_VSX_XFORM_300(xscmpuqp, 0x04, 0x14, 0x00600001),
 GEN_XX3FORM(xsmaxdp, 0x00, 0x14, PPC2_VSX),
 GEN_XX3FORM(xsmindp, 0x00, 0x15, PPC2_VSX),
+GEN_XX2FORM_EO(xscvdphp, 0x16, 0x15, 0x11, PPC2_ISA300),
 GEN_XX2FORM(xscvdpsp, 0x12, 0x10, PPC2_VSX),
 GEN_XX2FORM(xscvdpspn, 0x16, 0x10, PPC2_VSX207),
+GEN_XX2FORM_EO(xscvhpdp, 0x16, 0x15, 0x10, PPC2_ISA300),
+GEN_VSX_XFORM_300_EO(xscvsdqp, 0x04, 0x1A, 0x0A, 0x00000001),
 GEN_XX2FORM(xscvspdp, 0x12, 0x14, PPC2_VSX),
 GEN_XX2FORM(xscvspdpn, 0x16, 0x14, PPC2_VSX207),
 GEN_XX2FORM(xscvdpsxds, 0x10, 0x15, PPC2_VSX),
@@ -131,6 +197,7 @@ GEN_XX2FORM(xscvdpsxws, 0x10, 0x05, PPC2_VSX),
 GEN_XX2FORM(xscvdpuxds, 0x10, 0x14, PPC2_VSX),
 GEN_XX2FORM(xscvdpuxws, 0x10, 0x04, PPC2_VSX),
 GEN_XX2FORM(xscvsxddp, 0x10, 0x17, PPC2_VSX),
+GEN_VSX_XFORM_300_EO(xscvudqp, 0x04, 0x1A, 0x02, 0x00000001),
 GEN_XX2FORM(xscvuxddp, 0x10, 0x16, PPC2_VSX),
 GEN_XX2FORM(xsrdpi, 0x12, 0x04, PPC2_VSX),
 GEN_XX2FORM(xsrdpic, 0x16, 0x06, PPC2_VSX),
@@ -142,6 +209,7 @@ GEN_XX3FORM(xsaddsp, 0x00, 0x00, PPC2_VSX207),
 GEN_XX3FORM(xssubsp, 0x00, 0x01, PPC2_VSX207),
 GEN_XX3FORM(xsmulsp, 0x00, 0x02, PPC2_VSX207),
 GEN_XX3FORM(xsdivsp, 0x00, 0x03, PPC2_VSX207),
+GEN_VSX_XFORM_300(xsdivqp, 0x04, 0x11, 0x0),
 GEN_XX2FORM(xsresp,  0x14, 0x01, PPC2_VSX207),
 GEN_XX2FORM(xsrsp, 0x12, 0x11, PPC2_VSX207),
 GEN_XX2FORM(xssqrtsp,  0x16, 0x00, PPC2_VSX207),
@@ -235,6 +303,8 @@ GEN_XX2FORM(xvrspiz, 0x12, 0x09, PPC2_VSX),
 GEN_XX2FORM_EO(xxbrh, 0x16, 0x1D, 0x07, PPC2_ISA300),
 GEN_XX2FORM_EO(xxbrw, 0x16, 0x1D, 0x0F, PPC2_ISA300),
 GEN_XX2FORM_EO(xxbrd, 0x16, 0x1D, 0x17, PPC2_ISA300),
+GEN_XX2FORM_EO(xvcvhpsp, 0x16, 0x1D, 0x18, PPC2_ISA300),
+GEN_XX2FORM_EO(xvcvsphp, 0x16, 0x1D, 0x19, PPC2_ISA300),
 GEN_XX2FORM_EO(xxbrq, 0x16, 0x1D, 0x1F, PPC2_ISA300),
 
 #define VSX_LOGICAL(name, opc2, opc3, fl2) \
@@ -250,9 +320,13 @@ VSX_LOGICAL(xxlnand, 0x8, 0x16, PPC2_VSX207),
 VSX_LOGICAL(xxlorc, 0x8, 0x15, PPC2_VSX207),
 GEN_XX3FORM(xxmrghw, 0x08, 0x02, PPC2_VSX),
 GEN_XX3FORM(xxmrglw, 0x08, 0x06, PPC2_VSX),
+GEN_XX3FORM(xxperm, 0x08, 0x03, PPC2_ISA300),
+GEN_XX3FORM(xxpermr, 0x08, 0x07, PPC2_ISA300),
 GEN_XX2FORM(xxspltw, 0x08, 0x0A, PPC2_VSX),
 GEN_XX1FORM(xxspltib, 0x08, 0x0B, PPC2_ISA300),
 GEN_XX3FORM_DM(xxsldwi, 0x08, 0x00),
+GEN_XX2FORM_EXT(xxextractuw, 0x0A, 0x0A, PPC2_ISA300),
+GEN_XX2FORM_EXT(xxinsertw, 0x0A, 0x0B, PPC2_ISA300),
 
 #define GEN_XXSEL_ROW(opc3) \
 GEN_HANDLER2_E(xxsel, "xxsel", 0x3C, 0x18, opc3, 0, PPC_NONE, PPC2_VSX), \
diff --git a/target/ppc/translate_init.c b/target/ppc/translate_init.c
index 94dfcd7afc..76f79fa77b 100644
--- a/target/ppc/translate_init.c
+++ b/target/ppc/translate_init.c
@@ -5217,28 +5217,6 @@ POWERPC_FAMILY(e5500)(ObjectClass *oc, void *data)
 
 /* Non-embedded PowerPC                                                      */
 
-/* POWER : same as 601, without mfmsr, mfsr                                  */
-POWERPC_FAMILY(POWER)(ObjectClass *oc, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(oc);
-    PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc);
-
-    dc->desc = "POWER";
-    /* pcc->insns_flags = XXX_TODO; */
-    /* POWER RSC (from RAD6000) */
-    pcc->msr_mask = (1ull << MSR_EE) |
-                    (1ull << MSR_PR) |
-                    (1ull << MSR_FP) |
-                    (1ull << MSR_ME) |
-                    (1ull << MSR_FE0) |
-                    (1ull << MSR_SE) |
-                    (1ull << MSR_DE) |
-                    (1ull << MSR_AL) |
-                    (1ull << MSR_EP) |
-                    (1ull << MSR_IR) |
-                    (1ull << MSR_DR);
-}
-
 #define POWERPC_MSRR_601     (0x0000000000001040ULL)
 
 static void init_proc_601 (CPUPPCState *env)
@@ -8797,6 +8775,8 @@ POWERPC_FAMILY(POWER9)(ObjectClass *oc, void *data)
     dc->props = powerpc_servercpu_properties;
     pcc->pvr_match = ppc_pvr_match_power9;
     pcc->pcr_mask = PCR_COMPAT_2_05 | PCR_COMPAT_2_06 | PCR_COMPAT_2_07;
+    pcc->pcr_supported = PCR_COMPAT_3_00 | PCR_COMPAT_2_07 | PCR_COMPAT_2_06 |
+                         PCR_COMPAT_2_05;
     pcc->init_proc = init_proc_POWER9;
     pcc->check_pow = check_pow_nocheck;
     pcc->insns_flags = PPC_INSNS_BASE | PPC_ISEL | PPC_STRING | PPC_MFTB |
@@ -8857,6 +8837,11 @@ POWERPC_FAMILY(POWER9)(ObjectClass *oc, void *data)
 
 #if !defined(CONFIG_USER_ONLY)
 
+void cpu_ppc_set_vhyp(PowerPCCPU *cpu, PPCVirtualHypervisor *vhyp)
+{
+    cpu->vhyp = vhyp;
+}
+
 void cpu_ppc_set_papr(PowerPCCPU *cpu)
 {
     CPUPPCState *env = &cpu->env;
@@ -9947,67 +9932,6 @@ static void ppc_cpu_unrealizefn(DeviceState *dev, Error **errp)
     }
 }
 
-int ppc_get_compat_smt_threads(PowerPCCPU *cpu)
-{
-    CPUState *cs = CPU(cpu);
-    int ret = MIN(cs->nr_threads, kvmppc_smt_threads());
-
-    switch (cpu->cpu_version) {
-    case CPU_POWERPC_LOGICAL_2_05:
-        ret = MIN(ret, 2);
-        break;
-    case CPU_POWERPC_LOGICAL_2_06:
-        ret = MIN(ret, 4);
-        break;
-    case CPU_POWERPC_LOGICAL_2_07:
-        ret = MIN(ret, 8);
-        break;
-    }
-
-    return ret;
-}
-
-#ifdef TARGET_PPC64
-void ppc_set_compat(PowerPCCPU *cpu, uint32_t cpu_version, Error **errp)
-{
-    int ret = 0;
-    CPUPPCState *env = &cpu->env;
-    PowerPCCPUClass *host_pcc;
-
-    cpu->cpu_version = cpu_version;
-
-    switch (cpu_version) {
-    case CPU_POWERPC_LOGICAL_2_05:
-        env->spr[SPR_PCR] = PCR_TM_DIS | PCR_VSX_DIS | PCR_COMPAT_2_07 |
-                            PCR_COMPAT_2_06 | PCR_COMPAT_2_05;
-        break;
-    case CPU_POWERPC_LOGICAL_2_06:
-    case CPU_POWERPC_LOGICAL_2_06_PLUS:
-        env->spr[SPR_PCR] = PCR_TM_DIS | PCR_COMPAT_2_07 | PCR_COMPAT_2_06;
-        break;
-    case CPU_POWERPC_LOGICAL_2_07:
-        env->spr[SPR_PCR] = PCR_COMPAT_2_07;
-        break;
-    default:
-        env->spr[SPR_PCR] = 0;
-        break;
-    }
-
-    host_pcc = kvm_ppc_get_host_cpu_class();
-    if (host_pcc) {
-        env->spr[SPR_PCR] &= host_pcc->pcr_mask;
-    }
-
-    if (kvm_enabled()) {
-        ret = kvmppc_set_compat(cpu, cpu->cpu_version);
-        if (ret < 0) {
-            error_setg_errno(errp, -ret,
-                             "Unable to set CPU compatibility mode in KVM");
-        }
-    }
-}
-#endif
-
 static gint ppc_cpu_compare_class_pvr(gconstpointer a, gconstpointer b)
 {
     ObjectClass *oc = (ObjectClass *)a;
@@ -10591,9 +10515,16 @@ static const TypeInfo ppc_cpu_type_info = {
     .class_init = ppc_cpu_class_init,
 };
 
+static const TypeInfo ppc_vhyp_type_info = {
+    .name = TYPE_PPC_VIRTUAL_HYPERVISOR,
+    .parent = TYPE_INTERFACE,
+    .class_size = sizeof(PPCVirtualHypervisorClass),
+};
+
 static void ppc_cpu_register_types(void)
 {
     type_register_static(&ppc_cpu_type_info);
+    type_register_static(&ppc_vhyp_type_info);
 }
 
 type_init(ppc_cpu_register_types)
diff --git a/tcg/tci/tcg-target.inc.c b/tcg/tci/tcg-target.inc.c
index 26ee9b1664..b6a15569f8 100644
--- a/tcg/tci/tcg-target.inc.c
+++ b/tcg/tci/tcg-target.inc.c
@@ -566,7 +566,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, const TCGArg *args,
     case INDEX_op_goto_tb:
         if (s->tb_jmp_insn_offset) {
             /* Direct jump method. */
-            tcg_debug_assert(args[0] < ARRAY_SIZE(s->tb_jmp_insn_offset));
             /* Align for atomic patching and thread safety */
             s->code_ptr = QEMU_ALIGN_PTR_UP(s->code_ptr, 4);
             s->tb_jmp_insn_offset[args[0]] = tcg_current_code_size(s);
@@ -575,7 +574,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, const TCGArg *args,
             /* Indirect jump method. */
             TODO();
         }
-        tcg_debug_assert(args[0] < ARRAY_SIZE(s->tb_jmp_reset_offset));
         s->tb_jmp_reset_offset[args[0]] = tcg_current_code_size(s);
         break;
     case INDEX_op_br:
diff --git a/tests/.gitignore b/tests/.gitignore
index 7357d0a0d4..dc37519421 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -69,6 +69,7 @@ test-qmp-marshal.c
 test-qobject-output-visitor
 test-rcu-list
 test-replication
+test-shift128
 test-string-input-visitor
 test-string-output-visitor
 test-thread-pool
diff --git a/tests/Makefile.include b/tests/Makefile.include
index 22ea256e94..634394aecf 100644
--- a/tests/Makefile.include
+++ b/tests/Makefile.include
@@ -9,7 +9,7 @@ SYSEMU_TARGET_LIST := $(subst -softmmu.mak,,$(notdir \
 check-unit-y = tests/check-qdict$(EXESUF)
 gcov-files-check-qdict-y = qobject/qdict.c
 check-unit-y += tests/test-char$(EXESUF)
-gcov-files-check-qdict-y = qemu-char.c
+gcov-files-check-qdict-y = chardev/char.c
 check-unit-y += tests/check-qfloat$(EXESUF)
 gcov-files-check-qfloat-y = qobject/qfloat.c
 check-unit-y += tests/check-qint$(EXESUF)
@@ -65,6 +65,8 @@ check-unit-$(CONFIG_POSIX) += tests/test-vmstate$(EXESUF)
 endif
 check-unit-y += tests/test-cutils$(EXESUF)
 gcov-files-test-cutils-y += util/cutils.c
+check-unit-y += tests/test-shift128$(EXESUF)
+gcov-files-test-shift128-y = util/host-utils.c
 check-unit-y += tests/test-mul64$(EXESUF)
 gcov-files-test-mul64-y = util/host-utils.c
 check-unit-y += tests/test-int128$(EXESUF)
@@ -285,6 +287,11 @@ gcov-files-ppc64-y += hw/usb/hcd-uhci.c
 check-qtest-ppc64-y += tests/usb-hcd-xhci-test$(EXESUF)
 gcov-files-ppc64-y += hw/usb/hcd-xhci.c
 check-qtest-ppc64-y += $(check-qtest-virtio-y)
+check-qtest-ppc64-y += tests/test-netfilter$(EXESUF)
+check-qtest-ppc64-y += tests/test-filter-mirror$(EXESUF)
+check-qtest-ppc64-y += tests/test-filter-redirector$(EXESUF)
+check-qtest-ppc64-y += tests/display-vga-test$(EXESUF)
+check-qtest-ppc64-$(CONFIG_EVENTFD) += tests/ivshmem-test$(EXESUF)
 
 check-qtest-sh4-y = tests/endianness-test$(EXESUF)
 
@@ -482,7 +489,7 @@ test-obj-y = tests/check-qint.o tests/check-qstring.o tests/check-qdict.o \
 	tests/test-x86-cpuid.o tests/test-mul64.o tests/test-int128.o \
 	tests/test-opts-visitor.o tests/test-qmp-event.o \
 	tests/rcutorture.o tests/test-rcu-list.o \
-	tests/test-qdist.o \
+	tests/test-qdist.o tests/test-shift128.o \
 	tests/test-qht.o tests/qht-bench.o tests/test-qht-par.o \
 	tests/atomic_add-bench.o
 
@@ -491,7 +498,7 @@ QEMU_CFLAGS += -I$(SRC_PATH)/tests
 
 
 # Deps that are common to various different sets of tests below
-test-util-obj-y = libqemuutil.a libqemustub.a
+test-util-obj-y = $(trace-obj-y) libqemuutil.a libqemustub.a
 test-qom-obj-y = $(qom-obj-y) $(test-util-obj-y)
 test-qapi-obj-y = tests/test-qapi-visit.o tests/test-qapi-types.o \
 	tests/test-qapi-event.o tests/test-qmp-introspect.o \
@@ -510,7 +517,8 @@ tests/check-qjson$(EXESUF): tests/check-qjson.o $(test-util-obj-y)
 tests/check-qom-interface$(EXESUF): tests/check-qom-interface.o $(test-qom-obj-y)
 tests/check-qom-proplist$(EXESUF): tests/check-qom-proplist.o $(test-qom-obj-y)
 
-tests/test-char$(EXESUF): tests/test-char.o qemu-char.o qemu-timer.o $(test-util-obj-y) $(qtest-obj-y) $(test-io-obj-y)
+tests/test-char$(EXESUF): tests/test-char.o qemu-timer.o \
+	$(test-util-obj-y) $(qtest-obj-y) $(test-block-obj-y) $(chardev-obj-y)
 tests/test-coroutine$(EXESUF): tests/test-coroutine.o $(test-block-obj-y)
 tests/test-aio$(EXESUF): tests/test-aio.o $(test-block-obj-y)
 tests/test-throttle$(EXESUF): tests/test-throttle.o $(test-block-obj-y)
@@ -591,6 +599,7 @@ tests/test-qmp-commands$(EXESUF): tests/test-qmp-commands.o tests/test-qmp-marsh
 tests/test-visitor-serialization$(EXESUF): tests/test-visitor-serialization.o $(test-qapi-obj-y)
 tests/test-opts-visitor$(EXESUF): tests/test-opts-visitor.o $(test-qapi-obj-y)
 
+tests/test-shift128$(EXESUF): tests/test-shift128.o $(test-util-obj-y)
 tests/test-mul64$(EXESUF): tests/test-mul64.o $(test-util-obj-y)
 tests/test-bitops$(EXESUF): tests/test-bitops.o $(test-util-obj-y)
 tests/test-bitcnt$(EXESUF): tests/test-bitcnt.o $(test-util-obj-y)
@@ -703,7 +712,9 @@ tests/usb-hcd-ehci-test$(EXESUF): tests/usb-hcd-ehci-test.o $(libqos-usb-obj-y)
 tests/usb-hcd-xhci-test$(EXESUF): tests/usb-hcd-xhci-test.o $(libqos-usb-obj-y)
 tests/pc-cpu-test$(EXESUF): tests/pc-cpu-test.o
 tests/postcopy-test$(EXESUF): tests/postcopy-test.o
-tests/vhost-user-test$(EXESUF): tests/vhost-user-test.o qemu-char.o qemu-timer.o $(qtest-obj-y) $(test-io-obj-y) $(libqos-virtio-obj-y) $(libqos-pc-obj-y)
+tests/vhost-user-test$(EXESUF): tests/vhost-user-test.o qemu-timer.o \
+	$(qtest-obj-y) $(test-io-obj-y) $(libqos-virtio-obj-y) $(libqos-pc-obj-y) \
+	$(chardev-obj-y)
 tests/qemu-iotests/socket_scm_helper$(EXESUF): tests/qemu-iotests/socket_scm_helper.o
 tests/test-qemu-opts$(EXESUF): tests/test-qemu-opts.o $(test-util-obj-y)
 tests/test-write-threshold$(EXESUF): tests/test-write-threshold.o $(test-block-obj-y)
@@ -711,7 +722,7 @@ tests/test-netfilter$(EXESUF): tests/test-netfilter.o $(qtest-obj-y)
 tests/test-filter-mirror$(EXESUF): tests/test-filter-mirror.o $(qtest-obj-y)
 tests/test-filter-redirector$(EXESUF): tests/test-filter-redirector.o $(qtest-obj-y)
 tests/test-x86-cpuid-compat$(EXESUF): tests/test-x86-cpuid-compat.o $(qtest-obj-y)
-tests/ivshmem-test$(EXESUF): tests/ivshmem-test.o contrib/ivshmem-server/ivshmem-server.o $(libqos-pc-obj-y)
+tests/ivshmem-test$(EXESUF): tests/ivshmem-test.o contrib/ivshmem-server/ivshmem-server.o $(libqos-pc-obj-y) $(libqos-spapr-obj-y)
 tests/vhost-user-bridge$(EXESUF): tests/vhost-user-bridge.o contrib/libvhost-user/libvhost-user.o $(test-util-obj-y)
 tests/test-uuid$(EXESUF): tests/test-uuid.o $(test-util-obj-y)
 tests/test-arm-mptimer$(EXESUF): tests/test-arm-mptimer.o
diff --git a/tests/display-vga-test.c b/tests/display-vga-test.c
index 91460215cc..2d7d24eee0 100644
--- a/tests/display-vga-test.c
+++ b/tests/display-vga-test.c
@@ -50,9 +50,14 @@ static void pci_virtio_vga(void)
 
 int main(int argc, char **argv)
 {
+    const char *arch = qtest_get_arch();
+
     g_test_init(&argc, &argv, NULL);
 
-    qtest_add_func("/display/pci/cirrus", pci_cirrus);
+    if (strcmp(arch, "alpha") == 0 || strcmp(arch, "i386") == 0 ||
+        strcmp(arch, "mips") == 0 || strcmp(arch, "x86_64") == 0) {
+        qtest_add_func("/display/pci/cirrus", pci_cirrus);
+    }
     qtest_add_func("/display/pci/stdvga", pci_stdvga);
     qtest_add_func("/display/pci/secondary", pci_secondary);
     qtest_add_func("/display/pci/multihead", pci_multihead);
diff --git a/tests/ivshmem-test.c b/tests/ivshmem-test.c
index 04a5c5dc7d..37763425ee 100644
--- a/tests/ivshmem-test.c
+++ b/tests/ivshmem-test.c
@@ -11,7 +11,8 @@
 #include "qemu/osdep.h"
 #include <glib/gstdio.h>
 #include "contrib/ivshmem-server/ivshmem-server.h"
-#include "libqos/pci-pc.h"
+#include "libqos/libqos-pc.h"
+#include "libqos/libqos-spapr.h"
 #include "libqtest.h"
 #include "qemu-common.h"
 
@@ -40,9 +41,8 @@ static QPCIDevice *get_device(QPCIBus *pcibus)
 }
 
 typedef struct _IVState {
-    QTestState *qtest;
+    QOSState *qs;
     QPCIBar reg_bar, mem_bar;
-    QPCIBus *pcibus;
     QPCIDevice *dev;
 } IVState;
 
@@ -74,7 +74,7 @@ static inline unsigned in_reg(IVState *s, enum Reg reg)
     QTestState *qtest = global_qtest;
     unsigned res;
 
-    global_qtest = s->qtest;
+    global_qtest = s->qs->qts;
     res = qpci_io_readl(s->dev, s->reg_bar, reg);
     g_test_message("*%s -> %x\n", name, res);
     global_qtest = qtest;
@@ -87,7 +87,7 @@ static inline void out_reg(IVState *s, enum Reg reg, unsigned v)
     const char *name = reg2str(reg);
     QTestState *qtest = global_qtest;
 
-    global_qtest = s->qtest;
+    global_qtest = s->qs->qts;
     g_test_message("%x -> *%s\n", v, name);
     qpci_io_writel(s->dev, s->reg_bar, reg, v);
     global_qtest = qtest;
@@ -97,7 +97,7 @@ static inline void read_mem(IVState *s, uint64_t off, void *buf, size_t len)
 {
     QTestState *qtest = global_qtest;
 
-    global_qtest = s->qtest;
+    global_qtest = s->qs->qts;
     qpci_memread(s->dev, s->mem_bar, off, buf, len);
     global_qtest = qtest;
 }
@@ -107,7 +107,7 @@ static inline void write_mem(IVState *s, uint64_t off,
 {
     QTestState *qtest = global_qtest;
 
-    global_qtest = s->qtest;
+    global_qtest = s->qs->qts;
     qpci_memwrite(s->dev, s->mem_bar, off, buf, len);
     global_qtest = qtest;
 }
@@ -115,17 +115,23 @@ static inline void write_mem(IVState *s, uint64_t off,
 static void cleanup_vm(IVState *s)
 {
     g_free(s->dev);
-    qpci_free_pc(s->pcibus);
-    qtest_quit(s->qtest);
+    qtest_shutdown(s->qs);
 }
 
 static void setup_vm_cmd(IVState *s, const char *cmd, bool msix)
 {
     uint64_t barsize;
+    const char *arch = qtest_get_arch();
 
-    s->qtest = qtest_start(cmd);
-    s->pcibus = qpci_init_pc(NULL);
-    s->dev = get_device(s->pcibus);
+    if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) {
+        s->qs = qtest_pc_boot(cmd);
+    } else if (strcmp(arch, "ppc64") == 0) {
+        s->qs = qtest_spapr_boot(cmd);
+    } else {
+        g_printerr("ivshmem-test tests are only available on x86 or ppc64\n");
+        exit(EXIT_FAILURE);
+    }
+    s->dev = get_device(s->qs->pcibus);
 
     s->reg_bar = qpci_iomap(s->dev, 0, &barsize);
     g_assert_cmpuint(barsize, ==, 256);
@@ -347,7 +353,7 @@ static void test_ivshmem_server(bool msi)
     g_assert_cmpint(vm1, !=, vm2);
 
     /* check number of MSI-X vectors */
-    global_qtest = s1->qtest;
+    global_qtest = s1->qs->qts;
     if (msi) {
         ret = qpci_msix_table_size(s1->dev);
         g_assert_cmpuint(ret, ==, nvectors);
@@ -370,7 +376,7 @@ static void test_ivshmem_server(bool msi)
     g_assert_cmpuint(ret, !=, 0);
 
     /* ping vm1 -> vm2 on vector 1 */
-    global_qtest = s2->qtest;
+    global_qtest = s2->qs->qts;
     if (msi) {
         ret = qpci_msix_pending(s2->dev, 1);
         g_assert_cmpuint(ret, ==, 0);
@@ -412,6 +418,7 @@ static void test_ivshmem_server_irq(void)
 
 static void test_ivshmem_hotplug(void)
 {
+    const char *arch = qtest_get_arch();
     gchar *opts;
 
     qtest_start("");
@@ -419,7 +426,9 @@ static void test_ivshmem_hotplug(void)
     opts = g_strdup_printf("'shm': '%s', 'size': '1M'", tmpshm);
 
     qpci_plug_device_test("ivshmem", "iv1", PCI_SLOT_HP, opts);
-    qpci_unplug_acpi_device_test("iv1", PCI_SLOT_HP);
+    if (strcmp(arch, "ppc64") != 0) {
+        qpci_unplug_acpi_device_test("iv1", PCI_SLOT_HP);
+    }
 
     qtest_end();
     g_free(opts);
@@ -491,6 +500,7 @@ static gchar *mktempshm(int size, int *fd)
 int main(int argc, char **argv)
 {
     int ret, fd;
+    const char *arch = qtest_get_arch();
     gchar dir[] = "/tmp/ivshmem-test.XXXXXX";
 
 #if !GLIB_CHECK_VERSION(2, 31, 0)
@@ -521,8 +531,10 @@ int main(int argc, char **argv)
     qtest_add_func("/ivshmem/memdev", test_ivshmem_memdev);
     if (g_test_slow()) {
         qtest_add_func("/ivshmem/pair", test_ivshmem_pair);
-        qtest_add_func("/ivshmem/server-msi", test_ivshmem_server_msi);
-        qtest_add_func("/ivshmem/server-irq", test_ivshmem_server_irq);
+        if (strcmp(arch, "ppc64") != 0) {
+            qtest_add_func("/ivshmem/server-msi", test_ivshmem_server_msi);
+            qtest_add_func("/ivshmem/server-irq", test_ivshmem_server_irq);
+        }
     }
 
     ret = g_test_run();
diff --git a/tests/libqos/pci-spapr.c b/tests/libqos/pci-spapr.c
index 1e5d015bd4..2043f1e123 100644
--- a/tests/libqos/pci-spapr.c
+++ b/tests/libqos/pci-spapr.c
@@ -193,8 +193,8 @@ QPCIBus *qpci_init_spapr(QGuestAllocator *alloc)
     ret->pio.size = SPAPR_PCI_IO_WIN_SIZE;
 
     /* 32-bit portion of the MMIO window is at PCI address 2..4 GiB */
-    ret->mmio32_cpu_base = SPAPR_PCI_BASE + SPAPR_PCI_MMIO32_WIN_SIZE;
-    ret->mmio32.pci_base = 0x80000000; /* 2 GiB */
+    ret->mmio32_cpu_base = SPAPR_PCI_BASE;
+    ret->mmio32.pci_base = SPAPR_PCI_MMIO32_WIN_SIZE;
     ret->mmio32.size = SPAPR_PCI_MMIO32_WIN_SIZE;
 
     ret->bus.pio_alloc_ptr = 0xc000;
diff --git a/tests/test-char.c b/tests/test-char.c
index 241685afbb..da69f110e4 100644
--- a/tests/test-char.c
+++ b/tests/test-char.c
@@ -40,7 +40,7 @@ static void fe_event(void *opaque, int event)
 #ifdef CONFIG_HAS_GLIB_SUBPROCESS_TESTS
 static void char_stdio_test_subprocess(void)
 {
-    CharDriverState *chr;
+    Chardev *chr;
     CharBackend be;
     int ret;
 
@@ -68,7 +68,7 @@ static void char_stdio_test(void)
 static void char_ringbuf_test(void)
 {
     QemuOpts *opts;
-    CharDriverState *chr;
+    Chardev *chr;
     CharBackend be;
     char *data;
     int ret;
@@ -109,7 +109,7 @@ static void char_ringbuf_test(void)
 static void char_mux_test(void)
 {
     QemuOpts *opts;
-    CharDriverState *chr, *base;
+    Chardev *chr, *base;
     char *data;
     FeHandler h1 = { 0, }, h2 = { 0, };
     CharBackend chr_be1, chr_be2;
@@ -185,7 +185,7 @@ static void char_mux_test(void)
 static void char_null_test(void)
 {
     Error *err = NULL;
-    CharDriverState *chr;
+    Chardev *chr;
     CharBackend be;
     int ret;
 
@@ -227,7 +227,7 @@ static void char_null_test(void)
 
 static void char_invalid_test(void)
 {
-    CharDriverState *chr;
+    Chardev *chr;
 
     chr = qemu_chr_new("label-invalid", "invalid");
     g_assert_null(chr);
diff --git a/tests/test-shift128.c b/tests/test-shift128.c
new file mode 100644
index 0000000000..f3ff736e5c
--- /dev/null
+++ b/tests/test-shift128.c
@@ -0,0 +1,139 @@
+/*
+ * Test unsigned left and right shift
+ *
+ * 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 "qemu/osdep.h"
+#include "qemu/host-utils.h"
+
+typedef struct {
+    uint64_t low;
+    uint64_t high;
+    uint64_t rlow;
+    uint64_t rhigh;
+    int32_t shift;
+    bool overflow;
+} test_data;
+
+static const test_data test_ltable[] = {
+    { 0x4C7ULL, 0x0ULL, 0x00000000000004C7ULL,
+      0x0000000000000000ULL,   0, false },
+    { 0x001ULL, 0x0ULL, 0x0000000000000002ULL,
+      0x0000000000000000ULL,   1, false },
+    { 0x001ULL, 0x0ULL, 0x0000000000000004ULL,
+      0x0000000000000000ULL,   2, false },
+    { 0x001ULL, 0x0ULL, 0x0000000000000010ULL,
+      0x0000000000000000ULL,   4, false },
+    { 0x001ULL, 0x0ULL, 0x0000000000000100ULL,
+      0x0000000000000000ULL,   8, false },
+    { 0x001ULL, 0x0ULL, 0x0000000000010000ULL,
+      0x0000000000000000ULL,  16, false },
+    { 0x001ULL, 0x0ULL, 0x0000000080000000ULL,
+      0x0000000000000000ULL,  31, false },
+    { 0x001ULL, 0x0ULL, 0x0000200000000000ULL,
+      0x0000000000000000ULL,  45, false },
+    { 0x001ULL, 0x0ULL, 0x1000000000000000ULL,
+      0x0000000000000000ULL,  60, false },
+    { 0x001ULL, 0x0ULL, 0x0000000000000000ULL,
+      0x0000000000000001ULL,  64, false },
+    { 0x001ULL, 0x0ULL, 0x0000000000000000ULL,
+      0x0000000000010000ULL,  80, false },
+    { 0x001ULL, 0x0ULL, 0x0000000000000000ULL,
+      0x8000000000000000ULL, 127, false },
+    { 0x000ULL, 0x1ULL, 0x0000000000000000ULL,
+      0x0000000000000000ULL,  64,  true },
+    { 0x008ULL, 0x0ULL, 0x0000000000000000ULL,
+      0x0000000000000008ULL,  64, false },
+    { 0x008ULL, 0x0ULL, 0x0000000000000000ULL,
+      0x8000000000000000ULL, 124, false },
+    { 0x001ULL, 0x0ULL, 0x0000000000000000ULL,
+      0x4000000000000000ULL, 126, false },
+    { 0x001ULL, 0x0ULL, 0x0000000000000000ULL,
+      0x8000000000000000ULL, 127, false },
+    { 0x001ULL, 0x0ULL, 0x0000000000000001ULL,
+      0x0000000000000000ULL, 128,  false },
+    { 0x000ULL, 0x0ULL, 0x0000000000000000ULL,
+      0x0000000000000000ULL, 200, false },
+    { 0x001ULL, 0x0ULL, 0x0000000000000000ULL,
+      0x0000000000000100ULL, 200,  false },
+    { 0x001ULL, 0x0ULL, 0x0000000000000000ULL,
+      0x8000000000000000ULL,  -1, false },
+    { 0x001ULL, 0x0ULL, 0x0000000000000000ULL,
+      0x8000000000000000ULL, INT32_MAX, false },
+    { 0x001ULL, 0x0ULL, 0x0000000000000000ULL,
+      0x4000000000000000ULL,  -2, false },
+    { 0x001ULL, 0x0ULL, 0x0000000000000000ULL,
+      0x4000000000000000ULL, INT32_MAX - 1, false },
+    { 0x8888888888888888ULL, 0x9999999999999999ULL,
+      0x8000000000000000ULL, 0x9888888888888888ULL, 60, true },
+    { 0x8888888888888888ULL, 0x9999999999999999ULL,
+      0x0000000000000000ULL, 0x8888888888888888ULL, 64, true },
+};
+
+static const test_data test_rtable[] = {
+    { 0x00000000000004C7ULL, 0x0ULL, 0x00000000000004C7ULL, 0x0ULL,  0, false },
+    { 0x0800000000000000ULL, 0x0ULL, 0x0400000000000000ULL, 0x0ULL,  1, false },
+    { 0x0800000000000000ULL, 0x0ULL, 0x0200000000000000ULL, 0x0ULL,  2, false },
+    { 0x0800000000000000ULL, 0x0ULL, 0x0008000000000000ULL, 0x0ULL,  8, false },
+    { 0x0800000000000000ULL, 0x0ULL, 0x0000080000000000ULL, 0x0ULL, 16, false },
+    { 0x0800000000000000ULL, 0x0ULL, 0x0000000008000000ULL, 0x0ULL, 32, false },
+    { 0x8000000000000000ULL, 0x0ULL, 0x0000000000000001ULL, 0x0ULL, 63, false },
+    { 0x8000000000000000ULL, 0x0ULL, 0x0000000000000000ULL, 0x0ULL, 64, false },
+    { 0x0000000000000000ULL, 0x8000000000000000ULL,
+      0x0000000000000000ULL, 0x8000000000000000ULL, 128, false },
+    { 0x0000000000000000ULL, 0x8000000000000000ULL,
+      0x0080000000000000ULL, 0x0000000000000000ULL, 200, false },
+    { 0x0000000000000000ULL, 0x0000000000000000ULL,
+      0x0000000000000000ULL, 0x0000000000000000ULL, 200, false },
+    { 0x0000000000000000ULL, 0x8000000000000000ULL,
+      0x0000000000000000ULL, 0x0000000000000080ULL, -200, false },
+    { 0x8000000000000000ULL, 0x8000000000000000ULL,
+      0x0000000080000000ULL, 0x0000000080000000ULL, 32, false },
+    { 0x0800000000000000ULL, 0x0800000000000000ULL,
+      0x0800000000000000ULL, 0x0000000000000000ULL, 64, false },
+    { 0x0800000000000000ULL, 0x0800000000000000ULL,
+      0x0008000000000000ULL, 0x0000000000000000ULL, 72, false },
+    { 0x8000000000000000ULL, 0x8000000000000000ULL,
+      0x0000000000000001ULL, 0x0000000000000000ULL, 127, false },
+    { 0x0000000000000000ULL, 0x8000000000000000ULL,
+      0x0000000000000001ULL, 0x0000000000000000ULL, -1, false },
+    { 0x0000000000000000ULL, 0x8000000000000000ULL,
+      0x0000000000000002ULL, 0x0000000000000000ULL, -2, false },
+};
+
+static void test_lshift(void)
+{
+    int i;
+
+    for (i = 0; i < ARRAY_SIZE(test_ltable); ++i) {
+        bool overflow = false;
+        test_data tmp = test_ltable[i];
+        ulshift(&tmp.low, &tmp.high, tmp.shift, &overflow);
+        g_assert_cmpuint(tmp.low, ==, tmp.rlow);
+        g_assert_cmpuint(tmp.high, ==, tmp.rhigh);
+        g_assert_cmpuint(tmp.overflow, ==, overflow);
+    }
+}
+
+static void test_rshift(void)
+{
+    int i;
+
+    for (i = 0; i < ARRAY_SIZE(test_rtable); ++i) {
+        test_data tmp = test_rtable[i];
+        urshift(&tmp.low, &tmp.high, tmp.shift);
+        g_assert_cmpuint(tmp.low, ==, tmp.rlow);
+        g_assert_cmpuint(tmp.high, ==, tmp.rhigh);
+    }
+}
+
+int main(int argc, char **argv)
+{
+    g_test_init(&argc, &argv, NULL);
+    g_test_add_func("/host-utils/test_lshift", test_lshift);
+    g_test_add_func("/host-utils/test_rshift", test_rshift);
+    return g_test_run();
+}
diff --git a/tests/vhost-user-test.c b/tests/vhost-user-test.c
index 96bf00eefa..2c45c7b29f 100644
--- a/tests/vhost-user-test.c
+++ b/tests/vhost-user-test.c
@@ -12,6 +12,7 @@
 
 #include "libqtest.h"
 #include "qapi/error.h"
+#include "qemu/config-file.h"
 #include "qemu/option.h"
 #include "qemu/range.h"
 #include "qemu/sockets.h"
@@ -454,7 +455,7 @@ static void chr_event(void *opaque, int event)
 static void test_server_create_chr(TestServer *server, const gchar *opt)
 {
     gchar *chr_path;
-    CharDriverState *chr;
+    Chardev *chr;
 
     chr_path = g_strdup_printf("unix:%s%s", server->socket_path, opt);
     chr = qemu_chr_new(server->chr_name, chr_path);
@@ -486,7 +487,7 @@ static inline void test_server_connect(TestServer *server)
 static gboolean _test_server_free(TestServer *server)
 {
     int i;
-    CharDriverState *chr = qemu_chr_fe_get_driver(&server->chr);
+    Chardev *chr = qemu_chr_fe_get_driver(&server->chr);
 
     qemu_chr_fe_deinit(&server->chr);
     qemu_chr_delete(chr);
diff --git a/thread-pool.c b/thread-pool.c
index 6fba913529..3847969a6a 100644
--- a/thread-pool.c
+++ b/thread-pool.c
@@ -19,7 +19,7 @@
 #include "qemu/queue.h"
 #include "qemu/thread.h"
 #include "qemu/coroutine.h"
-#include "trace.h"
+#include "trace-root.h"
 #include "block/thread-pool.h"
 #include "qemu/main-loop.h"
 
diff --git a/trace-events b/trace-events
index 839a9d0fba..756a9472dd 100644
--- a/trace-events
+++ b/trace-events
@@ -62,16 +62,6 @@ spice_vmc_event(int event) "spice vmc event %d"
 # xen-hvm.c
 xen_ram_alloc(unsigned long ram_addr, unsigned long size) "requested: %#lx, size %#lx"
 xen_client_set_memory(uint64_t start_addr, unsigned long size, bool log_dirty) "%#"PRIx64" size %#lx, log_dirty %i"
-xen_default_ioreq_server(void) ""
-xen_ioreq_server_create(uint32_t id) "id: %u"
-xen_ioreq_server_destroy(uint32_t id) "id: %u"
-xen_ioreq_server_state(uint32_t id, bool enable) "id: %u: enable: %i"
-xen_map_mmio_range(uint32_t id, uint64_t start_addr, uint64_t end_addr) "id: %u start: %#"PRIx64" end: %#"PRIx64
-xen_unmap_mmio_range(uint32_t id, uint64_t start_addr, uint64_t end_addr) "id: %u start: %#"PRIx64" end: %#"PRIx64
-xen_map_portio_range(uint32_t id, uint64_t start_addr, uint64_t end_addr) "id: %u start: %#"PRIx64" end: %#"PRIx64
-xen_unmap_portio_range(uint32_t id, uint64_t start_addr, uint64_t end_addr) "id: %u start: %#"PRIx64" end: %#"PRIx64
-xen_map_pcidev(uint32_t id, uint8_t bus, uint8_t dev, uint8_t func) "id: %u bdf: %02x.%02x.%02x"
-xen_unmap_pcidev(uint32_t id, uint8_t bus, uint8_t dev, uint8_t func) "id: %u bdf: %02x.%02x.%02x"
 handle_ioreq(void *req, uint32_t type, uint32_t dir, uint32_t df, uint32_t data_is_ptr, uint64_t addr, uint64_t data, uint32_t count, uint32_t size) "I/O=%p type=%d dir=%d df=%d ptr=%d port=%#"PRIx64" data=%#"PRIx64" count=%d size=%d"
 handle_ioreq_read(void *req, uint32_t type, uint32_t df, uint32_t data_is_ptr, uint64_t addr, uint64_t data, uint32_t count, uint32_t size) "I/O=%p read type=%d df=%d ptr=%d port=%#"PRIx64" data=%#"PRIx64" count=%d size=%d"
 handle_ioreq_write(void *req, uint32_t type, uint32_t df, uint32_t data_is_ptr, uint64_t addr, uint64_t data, uint32_t count, uint32_t size) "I/O=%p write type=%d df=%d ptr=%d port=%#"PRIx64" data=%#"PRIx64" count=%d size=%d"
@@ -90,7 +80,6 @@ handle_qmp_command(void *mon, const char *cmd_name) "mon %p cmd_name \"%s\""
 monitor_protocol_event_handler(uint32_t event, void *qdict) "event=%d data=%p"
 monitor_protocol_event_emit(uint32_t event, void *data) "event=%d data=%p"
 monitor_protocol_event_queue(uint32_t event, void *qdict, uint64_t rate) "event=%d data=%p rate=%" PRId64
-monitor_protocol_event_throttle(uint32_t event, uint64_t rate) "event=%d rate=%" PRId64
 
 # dma-helpers.c
 dma_blk_io(void *dbs, void *bs, int64_t offset, bool to_dev) "dbs=%p bs=%p offset=%" PRId64 " to_dev=%d"
diff --git a/trace/Makefile.objs b/trace/Makefile.objs
index 1e1ce7479d..7de840ad7e 100644
--- a/trace/Makefile.objs
+++ b/trace/Makefile.objs
@@ -8,110 +8,36 @@
 tracetool-y = $(SRC_PATH)/scripts/tracetool.py
 tracetool-y += $(shell find $(SRC_PATH)/scripts/tracetool -name "*.py")
 
-$(BUILD_DIR)/trace-events-all: $(trace-events-y:%=$(SRC_PATH)/%)
+$(BUILD_DIR)/trace-events-all: $(trace-events-files)
 	$(call quiet-command,cat $^ > $@)
 
-######################################################################
-# Auto-generated event descriptions for LTTng ust code
-
-ifeq ($(findstring ust,$(TRACE_BACKENDS)),ust)
-
-$(obj)/generated-ust-provider.h: $(obj)/generated-ust-provider.h-timestamp
-	@cmp $< $@ >/dev/null 2>&1 || cp $< $@
-$(obj)/generated-ust-provider.h-timestamp: $(BUILD_DIR)/trace-events-all $(tracetool-y)
-	$(call quiet-command,$(TRACETOOL) \
-		--format=ust-events-h \
-		--backends=$(TRACE_BACKENDS) \
-		$< > $@,"GEN","$(patsubst %-timestamp,%,$@)")
-
-$(obj)/generated-ust.c: $(obj)/generated-ust.c-timestamp $(BUILD_DIR)/config-host.mak
-	@cmp $< $@ >/dev/null 2>&1 || cp $< $@
-$(obj)/generated-ust.c-timestamp: $(BUILD_DIR)/trace-events-all $(tracetool-y)
-	$(call quiet-command,$(TRACETOOL) \
-		--format=ust-events-c \
-		--backends=$(TRACE_BACKENDS) \
-		$< > $@,"GEN","$(patsubst %-timestamp,%,$@)")
-
-$(obj)/generated-tracers.h: $(obj)/generated-ust-provider.h
-$(obj)/generated-tracers.c: $(obj)/generated-ust.c
-
-endif
-
-
-######################################################################
-# Auto-generated tracing routines
-
-##################################################
-# Execution level
-
-$(obj)/generated-tracers.h: $(obj)/generated-tracers.h-timestamp
-	@cmp -s $< $@ || cp $< $@
-$(obj)/generated-tracers.h-timestamp: $(BUILD_DIR)/trace-events-all $(BUILD_DIR)/config-host.mak $(tracetool-y)
-	$(call quiet-command,$(TRACETOOL) \
-		--format=h \
-		--backends=$(TRACE_BACKENDS) \
-		$< > $@,"GEN","$(patsubst %-timestamp,%,$@)")
-
-##############################
-# non-DTrace
-
-$(obj)/generated-tracers.c: $(obj)/generated-tracers.c-timestamp
-	@cmp -s $< $@ || cp $< $@
-$(obj)/generated-tracers.c-timestamp: $(BUILD_DIR)/trace-events-all $(BUILD_DIR)/config-host.mak $(tracetool-y)
-	$(call quiet-command,$(TRACETOOL) \
-		--format=c \
-		--backends=$(TRACE_BACKENDS) \
-		$< > $@,"GEN","$(patsubst %-timestamp,%,$@)")
-
-$(obj)/generated-tracers.o: $(obj)/generated-tracers.c $(obj)/generated-tracers.h
-
-##############################
-# DTrace
-
-# Normal practice is to name DTrace probe file with a '.d' extension
-# but that gets picked up by QEMU's Makefile as an external dependency
-# rule file. So we use '.dtrace' instead
-ifeq ($(findstring dtrace,$(TRACE_BACKENDS)),dtrace)
-
-$(obj)/generated-tracers-dtrace.dtrace: $(obj)/generated-tracers-dtrace.dtrace-timestamp
-	@cmp $< $@ >/dev/null 2>&1 || cp $< $@
-$(obj)/generated-tracers-dtrace.dtrace-timestamp: $(BUILD_DIR)/trace-events-all $(BUILD_DIR)/config-host.mak $(tracetool-y)
-	$(call quiet-command,$(TRACETOOL) \
-		--format=d \
-		--backends=$(TRACE_BACKENDS) \
-		$< > $@,"GEN","$(patsubst %-timestamp,%,$@)")
-
-$(obj)/generated-tracers-dtrace.h: $(obj)/generated-tracers-dtrace.dtrace
-	$(call quiet-command,dtrace -o $@ -h -s $<,"GEN","$@")
-
-$(obj)/generated-tracers-dtrace.o: $(obj)/generated-tracers-dtrace.dtrace
-
-util-obj-y += generated-tracers-dtrace.o
-endif
 
 ##################################################
 # Translation level
 
 $(obj)/generated-helpers-wrappers.h: $(obj)/generated-helpers-wrappers.h-timestamp
 	@cmp $< $@ >/dev/null 2>&1 || cp $< $@
-$(obj)/generated-helpers-wrappers.h-timestamp: $(BUILD_DIR)/trace-events-all $(BUILD_DIR)/config-host.mak $(tracetool-y)
+$(obj)/generated-helpers-wrappers.h-timestamp: $(trace-events-files) $(BUILD_DIR)/config-host.mak $(tracetool-y)
 	$(call quiet-command,$(TRACETOOL) \
+		--group=all \
 		--format=tcg-helper-wrapper-h \
 		--backend=$(TRACE_BACKENDS) \
 		$< > $@,"GEN","$(patsubst %-timestamp,%,$@)")
 
 $(obj)/generated-helpers.h: $(obj)/generated-helpers.h-timestamp
 	@cmp $< $@ >/dev/null 2>&1 || cp $< $@
-$(obj)/generated-helpers.h-timestamp: $(BUILD_DIR)/trace-events-all $(BUILD_DIR)/config-host.mak $(tracetool-y)
+$(obj)/generated-helpers.h-timestamp: $(trace-events-files) $(BUILD_DIR)/config-host.mak $(tracetool-y)
 	$(call quiet-command,$(TRACETOOL) \
+		--group=all \
 		--format=tcg-helper-h \
 		--backend=$(TRACE_BACKENDS) \
 		$< > $@,"GEN","$(patsubst %-timestamp,%,$@)")
 
 $(obj)/generated-helpers.c: $(obj)/generated-helpers.c-timestamp
 	@cmp $< $@ >/dev/null 2>&1 || cp $< $@
-$(obj)/generated-helpers.c-timestamp: $(BUILD_DIR)/trace-events-all $(BUILD_DIR)/config-host.mak $(tracetool-y)
+$(obj)/generated-helpers.c-timestamp: $(trace-events-files) $(BUILD_DIR)/config-host.mak $(tracetool-y)
 	$(call quiet-command,$(TRACETOOL) \
+		--group=all \
 		--format=tcg-helper-c \
 		--backend=$(TRACE_BACKENDS) \
 		$< > $@,"GEN","$(patsubst %-timestamp,%,$@)")
@@ -123,8 +49,9 @@ target-obj-y += generated-helpers.o
 
 $(obj)/generated-tcg-tracers.h: $(obj)/generated-tcg-tracers.h-timestamp
 	@cmp $< $@ >/dev/null 2>&1 || cp $< $@
-$(obj)/generated-tcg-tracers.h-timestamp: $(BUILD_DIR)/trace-events-all $(BUILD_DIR)/config-host.mak $(tracetool-y)
+$(obj)/generated-tcg-tracers.h-timestamp: $(trace-events-files) $(BUILD_DIR)/config-host.mak $(tracetool-y)
 	$(call quiet-command,$(TRACETOOL) \
+		--group=all \
 		--format=tcg-h \
 		--backend=$(TRACE_BACKENDS) \
 		$< > $@,"GEN","$(patsubst %-timestamp,%,$@)")
@@ -133,10 +60,8 @@ $(obj)/generated-tcg-tracers.h-timestamp: $(BUILD_DIR)/trace-events-all $(BUILD_
 ######################################################################
 # Backend code
 
-util-obj-y += generated-tracers.o
 util-obj-$(CONFIG_TRACE_SIMPLE) += simple.o
 util-obj-$(CONFIG_TRACE_FTRACE) += ftrace.o
-util-obj-$(CONFIG_TRACE_UST) += generated-ust.o
 util-obj-y += control.o
 target-obj-y += control-target.o
 util-obj-y += qmp.o
diff --git a/trace/control-target.c b/trace/control-target.c
index e2e138a3f0..6266e6380d 100644
--- a/trace/control-target.c
+++ b/trace/control-target.c
@@ -9,7 +9,7 @@
 
 #include "qemu/osdep.h"
 #include "cpu.h"
-#include "trace.h"
+#include "trace-root.h"
 #include "trace/control.h"
 #include "translate-all.h"
 
diff --git a/trace/control.c b/trace/control.c
index 56a2632584..9b157b0ca7 100644
--- a/trace/control.c
+++ b/trace/control.c
@@ -26,7 +26,7 @@
 #include "qemu/error-report.h"
 #include "qemu/config-file.h"
 #include "monitor/monitor.h"
-#include "trace.h"
+#include "trace-root.h"
 
 int trace_events_enabled_count;
 
diff --git a/trace/ftrace.c b/trace/ftrace.c
index 3588bb0eb4..7de104deba 100644
--- a/trace/ftrace.c
+++ b/trace/ftrace.c
@@ -10,8 +10,8 @@
  */
 
 #include "qemu/osdep.h"
-#include "trace.h"
 #include "trace/control.h"
+#include "trace/ftrace.h"
 
 int trace_marker_fd;
 
diff --git a/trace/simple.c b/trace/simple.c
index b263622fa9..a221a3f703 100644
--- a/trace/simple.c
+++ b/trace/simple.c
@@ -13,7 +13,6 @@
 #include <pthread.h>
 #endif
 #include "qemu/timer.h"
-#include "trace.h"
 #include "trace/control.h"
 #include "trace/simple.h"
 
diff --git a/translate-all.c b/translate-all.c
index 20262938bb..5f44ec844e 100644
--- a/translate-all.c
+++ b/translate-all.c
@@ -25,7 +25,7 @@
 #include "qemu-common.h"
 #define NO_CPU_IO_DEFS
 #include "cpu.h"
-#include "trace.h"
+#include "trace-root.h"
 #include "disas/disas.h"
 #include "exec/exec-all.h"
 #include "tcg.h"
@@ -1290,6 +1290,8 @@ TranslationBlock *tb_gen_code(CPUState *cpu,
         /* flush must be done */
         tb_flush(cpu);
         mmap_unlock();
+        /* Make the execution loop process the flush as soon as possible.  */
+        cpu->exception_index = EXCP_INTERRUPT;
         cpu_loop_exit(cpu);
     }
 
diff --git a/ui/console.c b/ui/console.c
index b9575f2ee5..49d0740b40 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -158,7 +158,7 @@ struct QemuConsole {
     int esc_params[MAX_ESC_PARAMS];
     int nb_esc_params;
 
-    CharDriverState *chr;
+    Chardev *chr;
     /* fifo for key pressed */
     QEMUFIFO out_fifo;
     uint8_t out_fifo_buf[16];
@@ -183,7 +183,7 @@ static int nb_consoles = 0;
 static bool cursor_visible_phase;
 static QEMUTimer *cursor_timer;
 
-static void text_console_do_init(CharDriverState *chr, DisplayState *ds);
+static void text_console_do_init(Chardev *chr, DisplayState *ds);
 static void dpy_refresh(DisplayState *s);
 static DisplayState *get_alloc_displaystate(void);
 static void text_console_update_cursor_timer(void);
@@ -1046,11 +1046,24 @@ void console_select(unsigned int index)
     }
 }
 
-static int console_puts(CharDriverState *chr, const uint8_t *buf, int len)
+typedef struct VCChardev {
+    Chardev parent;
+    QemuConsole *console;
+} VCChardev;
+
+#define TYPE_CHARDEV_VC "chardev-vc"
+#define VC_CHARDEV(obj) OBJECT_CHECK(VCChardev, (obj), TYPE_CHARDEV_VC)
+
+static int vc_chr_write(Chardev *chr, const uint8_t *buf, int len)
 {
-    QemuConsole *s = chr->opaque;
+    VCChardev *drv = VC_CHARDEV(chr);
+    QemuConsole *s = drv->console;
     int i;
 
+    if (!s->ds) {
+        return 0;
+    }
+
     s->update_x0 = s->width * FONT_WIDTH;
     s->update_y0 = s->height * FONT_HEIGHT;
     s->update_x1 = 0;
@@ -1129,13 +1142,13 @@ void kbd_put_keysym_console(QemuConsole *s, int keysym)
             *q++ = '[';
             *q++ = keysym & 0xff;
         } else if (s->echo && (keysym == '\r' || keysym == '\n')) {
-            console_puts(s->chr, (const uint8_t *) "\r", 1);
+            vc_chr_write(s->chr, (const uint8_t *) "\r", 1);
             *q++ = '\n';
         } else {
             *q++ = keysym;
         }
         if (s->echo) {
-            console_puts(s->chr, buf, q - buf);
+            vc_chr_write(s->chr, buf, q - buf);
         }
         be = s->chr->be;
         if (be && be->chr_read) {
@@ -1952,9 +1965,10 @@ int qemu_console_get_height(QemuConsole *con, int fallback)
     return con ? surface_height(con->surface) : fallback;
 }
 
-static void text_console_set_echo(CharDriverState *chr, bool echo)
+static void vc_chr_set_echo(Chardev *chr, bool echo)
 {
-    QemuConsole *s = chr->opaque;
+    VCChardev *drv = VC_CHARDEV(chr);
+    QemuConsole *s = drv->console;
 
     s->echo = echo;
 }
@@ -1992,16 +2006,13 @@ static const GraphicHwOps text_console_ops = {
     .text_update = text_console_update,
 };
 
-static void text_console_do_init(CharDriverState *chr, DisplayState *ds)
+static void text_console_do_init(Chardev *chr, DisplayState *ds)
 {
-    QemuConsole *s;
+    VCChardev *drv = VC_CHARDEV(chr);
+    QemuConsole *s = drv->console;
     int g_width = 80 * FONT_WIDTH;
     int g_height = 24 * FONT_HEIGHT;
 
-    s = chr->opaque;
-
-    chr->chr_write = console_puts;
-
     s->out_fifo.buf = s->out_fifo_buf;
     s->out_fifo.buf_size = sizeof(s->out_fifo_buf);
     s->kbd_timer = timer_new_ms(QEMU_CLOCK_REALTIME, kbd_send_chars, s);
@@ -2041,26 +2052,24 @@ static void text_console_do_init(CharDriverState *chr, DisplayState *ds)
 
         s->t_attrib.bgcol = QEMU_COLOR_BLUE;
         len = snprintf(msg, sizeof(msg), "%s console\r\n", chr->label);
-        console_puts(chr, (uint8_t*)msg, len);
+        vc_chr_write(chr, (uint8_t *)msg, len);
         s->t_attrib = s->t_attrib_default;
     }
 
     qemu_chr_be_generic_open(chr);
 }
 
-static CharDriverState *text_console_init(ChardevVC *vc, Error **errp)
+static void vc_chr_open(Chardev *chr,
+                        ChardevBackend *backend,
+                        bool *be_opened,
+                        Error **errp)
 {
-    ChardevCommon *common = qapi_ChardevVC_base(vc);
-    CharDriverState *chr;
+    ChardevVC *vc = backend->u.vc.data;
+    VCChardev *drv = VC_CHARDEV(chr);
     QemuConsole *s;
     unsigned width = 0;
     unsigned height = 0;
 
-    chr = qemu_chr_alloc(common, errp);
-    if (!chr) {
-        return NULL;
-    }
-
     if (vc->has_width) {
         width = vc->width;
     } else if (vc->has_cols) {
@@ -2082,37 +2091,21 @@ static CharDriverState *text_console_init(ChardevVC *vc, Error **errp)
     }
 
     if (!s) {
-        g_free(chr);
         error_setg(errp, "cannot create text console");
-        return NULL;
+        return;
     }
 
     s->chr = chr;
-    chr->opaque = s;
-    chr->chr_set_echo = text_console_set_echo;
+    drv->console = s;
 
     if (display_state) {
         text_console_do_init(chr, display_state);
     }
-    return chr;
-}
 
-static VcHandler *vc_handler = text_console_init;
-
-static CharDriverState *vc_init(const char *id, ChardevBackend *backend,
-                                ChardevReturn *ret, bool *be_opened,
-                                Error **errp)
-{
     /* console/chardev init sometimes completes elsewhere in a 2nd
      * stage, so defer OPENED events until they are fully initialized
      */
     *be_opened = false;
-    return vc_handler(backend->u.vc.data, errp);
-}
-
-void register_vc_handler(VcHandler *handler)
-{
-    vc_handler = handler;
 }
 
 void qemu_console_resize(QemuConsole *s, int width, int height)
@@ -2121,7 +2114,7 @@ void qemu_console_resize(QemuConsole *s, int width, int height)
 
     assert(s->console_type == GRAPHIC_CONSOLE);
 
-    if (s->surface &&
+    if (s->surface && (s->surface->flags & QEMU_ALLOCATED_FLAG) &&
         pixman_image_get_width(s->surface->image) == width &&
         pixman_image_get_height(s->surface->image) == height) {
         return;
@@ -2150,12 +2143,12 @@ PixelFormat qemu_default_pixelformat(int bpp)
     return pf;
 }
 
-static void qemu_chr_parse_vc(QemuOpts *opts, ChardevBackend *backend,
-                              Error **errp)
+void qemu_chr_parse_vc(QemuOpts *opts, ChardevBackend *backend, Error **errp)
 {
     int val;
     ChardevVC *vc;
 
+    backend->type = CHARDEV_BACKEND_KIND_VC;
     vc = backend->u.vc.data = g_new0(ChardevVC, 1);
     qemu_chr_parse_common(opts, qapi_ChardevVC_base(vc));
 
@@ -2191,12 +2184,34 @@ static const TypeInfo qemu_console_info = {
     .class_size = sizeof(QemuConsoleClass),
 };
 
+static void char_vc_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
+
+    cc->parse = qemu_chr_parse_vc;
+    cc->open = vc_chr_open;
+    cc->chr_write = vc_chr_write;
+    cc->chr_set_echo = vc_chr_set_echo;
+}
+
+static const TypeInfo char_vc_type_info = {
+    .name = TYPE_CHARDEV_VC,
+    .parent = TYPE_CHARDEV,
+    .instance_size = sizeof(VCChardev),
+    .class_init = char_vc_class_init,
+};
+
+void qemu_console_early_init(void)
+{
+    /* set the default vc driver */
+    if (!object_class_by_name(TYPE_CHARDEV_VC)) {
+        type_register(&char_vc_type_info);
+    }
+}
 
 static void register_types(void)
 {
     type_register_static(&qemu_console_info);
-    register_char_driver("vc", CHARDEV_BACKEND_KIND_VC, qemu_chr_parse_vc,
-                         vc_init);
 }
 
 type_init(register_types);
diff --git a/ui/gtk.c b/ui/gtk.c
index 86368e38b7..f21e9e7f7b 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -105,6 +105,7 @@
 #define GDK_KEY_g GDK_g
 #define GDK_KEY_q GDK_q
 #define GDK_KEY_plus GDK_plus
+#define GDK_KEY_equal GDK_equal
 #define GDK_KEY_minus GDK_minus
 #define GDK_KEY_Pause GDK_Pause
 #define GDK_KEY_Delete GDK_Delete
@@ -181,6 +182,15 @@ struct GtkDisplayState {
     bool ignore_keys;
 };
 
+typedef struct VCChardev {
+    Chardev parent;
+    VirtualConsole *console;
+    bool echo;
+} VCChardev;
+
+#define TYPE_CHARDEV_VC "chardev-vc"
+#define VC_CHARDEV(obj) OBJECT_CHECK(VCChardev, (obj), TYPE_CHARDEV_VC)
+
 static void gd_grab_pointer(VirtualConsole *vc, const char *reason);
 static void gd_ungrab_pointer(GtkDisplayState *s);
 static void gd_grab_keyboard(VirtualConsole *vc, const char *reason);
@@ -998,6 +1008,10 @@ static gboolean gd_button_event(GtkWidget *widget, GdkEventButton *button,
         btn = INPUT_BUTTON_MIDDLE;
     } else if (button->button == 3) {
         btn = INPUT_BUTTON_RIGHT;
+    } else if (button->button == 8) {
+        btn = INPUT_BUTTON_SIDE;
+    } else if (button->button == 9) {
+        btn = INPUT_BUTTON_EXTRA;
     } else {
         return TRUE;
     }
@@ -1018,6 +1032,19 @@ static gboolean gd_scroll_event(GtkWidget *widget, GdkEventScroll *scroll,
         btn = INPUT_BUTTON_WHEEL_UP;
     } else if (scroll->direction == GDK_SCROLL_DOWN) {
         btn = INPUT_BUTTON_WHEEL_DOWN;
+#if GTK_CHECK_VERSION(3, 4, 0)
+    } else if (scroll->direction == GDK_SCROLL_SMOOTH) {
+        gdouble delta_x, delta_y;
+        if (!gdk_event_get_scroll_deltas((GdkEvent *)scroll,
+                                         &delta_x, &delta_y)) {
+            return TRUE;
+        }
+        if (delta_y > 0) {
+            btn = INPUT_BUTTON_WHEEL_DOWN;
+        } else {
+            btn = INPUT_BUTTON_WHEEL_UP;
+        }
+#endif
     } else {
         return TRUE;
     }
@@ -1316,6 +1343,12 @@ static void gd_menu_zoom_in(GtkMenuItem *item, void *opaque)
     gd_update_windowsize(vc);
 }
 
+static void gd_accel_zoom_in(void *opaque)
+{
+    GtkDisplayState *s = opaque;
+    gtk_menu_item_activate(GTK_MENU_ITEM(s->zoom_in_item));
+}
+
 static void gd_menu_zoom_out(GtkMenuItem *item, void *opaque)
 {
     GtkDisplayState *s = opaque;
@@ -1683,50 +1716,64 @@ static void gd_vc_adjustment_changed(GtkAdjustment *adjustment, void *opaque)
     }
 }
 
-static int gd_vc_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
+static int gd_vc_chr_write(Chardev *chr, const uint8_t *buf, int len)
 {
-    VirtualConsole *vc = chr->opaque;
+    VCChardev *vcd = VC_CHARDEV(chr);
+    VirtualConsole *vc = vcd->console;
 
     vte_terminal_feed(VTE_TERMINAL(vc->vte.terminal), (const char *)buf, len);
     return len;
 }
 
-static void gd_vc_chr_set_echo(CharDriverState *chr, bool echo)
+static void gd_vc_chr_set_echo(Chardev *chr, bool echo)
 {
-    VirtualConsole *vc = chr->opaque;
+    VCChardev *vcd = VC_CHARDEV(chr);
+    VirtualConsole *vc = vcd->console;
 
-    vc->vte.echo = echo;
+    if (vc) {
+        vc->vte.echo = echo;
+    } else {
+        vcd->echo = echo;
+    }
 }
 
 static int nb_vcs;
-static CharDriverState *vcs[MAX_VCS];
-
-static CharDriverState *gd_vc_handler(ChardevVC *vc, Error **errp)
+static Chardev *vcs[MAX_VCS];
+static void gd_vc_open(Chardev *chr,
+                       ChardevBackend *backend,
+                       bool *be_opened,
+                       Error **errp)
 {
-    ChardevCommon *common = qapi_ChardevVC_base(vc);
-    CharDriverState *chr;
-
     if (nb_vcs == MAX_VCS) {
         error_setg(errp, "Maximum number of consoles reached");
-        return NULL;
-    }
-
-    chr = qemu_chr_alloc(common, errp);
-    if (!chr) {
-        return NULL;
+        return;
     }
 
-    chr->chr_write = gd_vc_chr_write;
-    chr->chr_set_echo = gd_vc_chr_set_echo;
+    vcs[nb_vcs++] = chr;
 
-    /* Temporary, until gd_vc_vte_init runs.  */
-    chr->opaque = g_new0(VirtualConsole, 1);
+    /* console/chardev init sometimes completes elsewhere in a 2nd
+     * stage, so defer OPENED events until they are fully initialized
+     */
+    *be_opened = false;
+}
 
-    vcs[nb_vcs++] = chr;
+static void char_gd_vc_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
 
-    return chr;
+    cc->parse = qemu_chr_parse_vc;
+    cc->open = gd_vc_open;
+    cc->chr_write = gd_vc_chr_write;
+    cc->chr_set_echo = gd_vc_chr_set_echo;
 }
 
+static const TypeInfo char_gd_vc_type_info = {
+    .name = TYPE_CHARDEV_VC,
+    .parent = TYPE_CHARDEV,
+    .instance_size = sizeof(VCChardev),
+    .class_init = char_gd_vc_class_init,
+};
+
 static gboolean gd_vc_in(VteTerminal *terminal, gchar *text, guint size,
                          gpointer user_data)
 {
@@ -1755,21 +1802,19 @@ static gboolean gd_vc_in(VteTerminal *terminal, gchar *text, guint size,
 }
 
 static GSList *gd_vc_vte_init(GtkDisplayState *s, VirtualConsole *vc,
-                              CharDriverState *chr, int idx,
+                              Chardev *chr, int idx,
                               GSList *group, GtkWidget *view_menu)
 {
     char buffer[32];
     GtkWidget *box;
     GtkWidget *scrollbar;
     GtkAdjustment *vadjustment;
-    VirtualConsole *tmp_vc = chr->opaque;
+    VCChardev *vcd = VC_CHARDEV(chr);
 
     vc->s = s;
-    vc->vte.echo = tmp_vc->vte.echo;
-
+    vc->vte.echo = vcd->echo;
     vc->vte.chr = chr;
-    chr->opaque = vc;
-    g_free(tmp_vc);
+    vcd->console = vc;
 
     snprintf(buffer, sizeof(buffer), "vc%d", idx);
     vc->label = g_strdup_printf("%s", vc->vte.chr->label
@@ -2065,6 +2110,8 @@ static GtkWidget *gd_create_menu_view(GtkDisplayState *s)
                                  "<QEMU>/View/Zoom In");
     gtk_accel_map_add_entry("<QEMU>/View/Zoom In", GDK_KEY_plus,
                             HOTKEY_MODIFIERS);
+    gtk_accel_group_connect(s->accel_group, GDK_KEY_equal, HOTKEY_MODIFIERS, 0,
+            g_cclosure_new_swap(G_CALLBACK(gd_accel_zoom_in), s, NULL));
     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_in_item);
 
     s->zoom_out_item = gtk_menu_item_new_with_mnemonic(_("Zoom _Out"));
@@ -2205,8 +2252,12 @@ void gtk_display_init(DisplayState *ds, bool full_screen, bool grab_on_hover)
 
     s->free_scale = FALSE;
 
-    /* LC_MESSAGES only. See early_gtk_display_init() for details */
+    /* Mostly LC_MESSAGES only. See early_gtk_display_init() for details. For
+     * LC_CTYPE, we need to make sure that non-ASCII characters are considered
+     * printable, but without changing any of the character classes to make
+     * sure that we don't accidentally break implicit assumptions.  */
     setlocale(LC_MESSAGES, "");
+    setlocale(LC_CTYPE, "C.UTF-8");
     bindtextdomain("qemu", CONFIG_QEMU_LOCALEDIR);
     textdomain("qemu");
 
@@ -2325,6 +2376,6 @@ void early_gtk_display_init(int opengl)
     }
 
 #if defined(CONFIG_VTE)
-    register_vc_handler(gd_vc_handler);
+    type_register(&char_gd_vc_type_info);
 #endif
 }
diff --git a/ui/input-linux.c b/ui/input-linux.c
index f345317794..ac31f47719 100644
--- a/ui/input-linux.c
+++ b/ui/input-linux.c
@@ -291,6 +291,12 @@ static void input_linux_handle_mouse(InputLinux *il, struct input_event *event)
             qemu_input_queue_btn(NULL, INPUT_BUTTON_WHEEL_DOWN,
                                  event->value);
             break;
+        case BTN_SIDE:
+            qemu_input_queue_btn(NULL, INPUT_BUTTON_SIDE, event->value);
+            break;
+        case BTN_EXTRA:
+            qemu_input_queue_btn(NULL, INPUT_BUTTON_EXTRA, event->value);
+            break;
         };
         break;
     case EV_REL:
diff --git a/ui/spice-display.c b/ui/spice-display.c
index 5e6f78a219..64e472eeb0 100644
--- a/ui/spice-display.c
+++ b/ui/spice-display.c
@@ -769,6 +769,7 @@ static void display_mouse_set(DisplayChangeListener *dcl,
     g_free(ssd->ptr_move);
     ssd->ptr_move = qemu_spice_create_cursor_update(ssd, NULL, on);
     qemu_mutex_unlock(&ssd->lock);
+    qemu_spice_wakeup(ssd);
 }
 
 static void display_mouse_define(DisplayChangeListener *dcl,
@@ -787,6 +788,7 @@ static void display_mouse_define(DisplayChangeListener *dcl,
     g_free(ssd->ptr_define);
     ssd->ptr_define = qemu_spice_create_cursor_update(ssd, c, 0);
     qemu_mutex_unlock(&ssd->lock);
+    qemu_spice_wakeup(ssd);
 }
 
 static const DisplayChangeListenerOps display_listener_ops = {
diff --git a/ui/vnc.c b/ui/vnc.c
index 29aa9c4c97..cdeb79c3cc 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -1231,8 +1231,6 @@ void vnc_disconnect_finish(VncState *vs)
         vnc_update_server_surface(vs->vd);
     }
 
-    if (vs->vd->lock_key_sync)
-        qemu_remove_led_event_handler(vs->led);
     vnc_unlock_output(vs);
 
     qemu_mutex_destroy(&vs->output_mutex);
@@ -1259,7 +1257,7 @@ ssize_t vnc_client_io_error(VncState *vs, ssize_t ret, Error **errp)
         if (ret == 0) {
             VNC_DEBUG("Closing down client sock: EOF\n");
         } else if (ret != QIO_CHANNEL_ERR_BLOCK) {
-            VNC_DEBUG("Closing down client sock: ret %d (%s)\n",
+            VNC_DEBUG("Closing down client sock: ret %zd (%s)\n",
                       ret, errp ? error_get_pretty(*errp) : "Unknown");
         }
 
@@ -1665,69 +1663,39 @@ static void press_key(VncState *vs, int keysym)
     qemu_input_event_send_key_delay(vs->vd->key_delay_ms);
 }
 
-static int current_led_state(VncState *vs)
-{
-    int ledstate = 0;
-
-    if (vs->modifiers_state[0x46]) {
-        ledstate |= QEMU_SCROLL_LOCK_LED;
-    }
-    if (vs->modifiers_state[0x45]) {
-        ledstate |= QEMU_NUM_LOCK_LED;
-    }
-    if (vs->modifiers_state[0x3a]) {
-        ledstate |= QEMU_CAPS_LOCK_LED;
-    }
-
-    return ledstate;
-}
-
 static void vnc_led_state_change(VncState *vs)
 {
-    int ledstate = 0;
-
     if (!vnc_has_feature(vs, VNC_FEATURE_LED_STATE)) {
         return;
     }
 
-    ledstate = current_led_state(vs);
     vnc_lock_output(vs);
     vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
     vnc_write_u8(vs, 0);
     vnc_write_u16(vs, 1);
     vnc_framebuffer_update(vs, 0, 0, 1, 1, VNC_ENCODING_LED_STATE);
-    vnc_write_u8(vs, ledstate);
+    vnc_write_u8(vs, vs->vd->ledstate);
     vnc_unlock_output(vs);
     vnc_flush(vs);
 }
 
 static void kbd_leds(void *opaque, int ledstate)
 {
-    VncState *vs = opaque;
-    int caps, num, scr;
-    bool has_changed = (ledstate != current_led_state(vs));
+    VncDisplay *vd = opaque;
+    VncState *client;
 
     trace_vnc_key_guest_leds((ledstate & QEMU_CAPS_LOCK_LED),
                              (ledstate & QEMU_NUM_LOCK_LED),
                              (ledstate & QEMU_SCROLL_LOCK_LED));
 
-    caps = ledstate & QEMU_CAPS_LOCK_LED ? 1 : 0;
-    num  = ledstate & QEMU_NUM_LOCK_LED  ? 1 : 0;
-    scr  = ledstate & QEMU_SCROLL_LOCK_LED ? 1 : 0;
-
-    if (vs->modifiers_state[0x3a] != caps) {
-        vs->modifiers_state[0x3a] = caps;
-    }
-    if (vs->modifiers_state[0x45] != num) {
-        vs->modifiers_state[0x45] = num;
-    }
-    if (vs->modifiers_state[0x46] != scr) {
-        vs->modifiers_state[0x46] = scr;
+    if (ledstate == vd->ledstate) {
+        return;
     }
 
-    /* Sending the current led state message to the client */
-    if (has_changed) {
-        vnc_led_state_change(vs);
+    vd->ledstate = ledstate;
+
+    QTAILQ_FOREACH(client, &vd->clients, next) {
+        vnc_led_state_change(client);
     }
 }
 
@@ -2756,8 +2724,10 @@ static int vnc_refresh_lossy_rect(VncDisplay *vd, int x, int y)
 
 static int vnc_update_stats(VncDisplay *vd,  struct timeval * tv)
 {
-    int width = pixman_image_get_width(vd->guest.fb);
-    int height = pixman_image_get_height(vd->guest.fb);
+    int width = MIN(pixman_image_get_width(vd->guest.fb),
+                    pixman_image_get_width(vd->server));
+    int height = MIN(pixman_image_get_height(vd->guest.fb),
+                     pixman_image_get_height(vd->server));
     int x, y;
     struct timeval res;
     int has_dirty = 0;
@@ -3087,8 +3057,6 @@ void vnc_start_protocol(VncState *vs)
     vnc_write(vs, "RFB 003.008\n", 12);
     vnc_flush(vs);
     vnc_read_when(vs, protocol_version, 12);
-    if (vs->vd->lock_key_sync)
-        vs->led = qemu_add_led_event_handler(kbd_leds, vs);
 
     vs->mouse_mode_notifier.notify = check_pointer_type_change;
     qemu_add_mouse_mode_change_notifier(&vs->mouse_mode_notifier);
@@ -3195,6 +3163,9 @@ static void vnc_display_close(VncDisplay *vd)
     }
     g_free(vd->tlsaclname);
     vd->tlsaclname = NULL;
+    if (vd->lock_key_sync) {
+        qemu_remove_led_event_handler(vd->led);
+    }
 }
 
 int vnc_display_password(const char *id, const char *password)
@@ -3762,6 +3733,10 @@ void vnc_display_open(const char *id, Error **errp)
     }
 #endif
     vd->lock_key_sync = lock_key_sync;
+    if (lock_key_sync) {
+        vd->led = qemu_add_led_event_handler(kbd_leds, vd);
+    }
+    vd->ledstate = 0;
     vd->key_delay_ms = key_delay_ms;
 
     device_id = qemu_opt_get(opts, "display");
diff --git a/ui/vnc.h b/ui/vnc.h
index d20b154a77..d8c9de5a75 100644
--- a/ui/vnc.h
+++ b/ui/vnc.h
@@ -154,6 +154,8 @@ struct VncDisplay
     DisplayChangeListener dcl;
     kbd_layout_t *kbd_layout;
     int lock_key_sync;
+    QEMUPutLEDEntry *led;
+    int ledstate;
     int key_delay_ms;
     QemuMutex mutex;
 
@@ -304,7 +306,6 @@ struct VncState
     size_t read_handler_expect;
     /* input */
     uint8_t modifiers_state[256];
-    QEMUPutLEDEntry *led;
 
     bool abort;
     QemuMutex output_mutex;
diff --git a/util/Makefile.objs b/util/Makefile.objs
index c1f247d675..56c8c23c7d 100644
--- a/util/Makefile.objs
+++ b/util/Makefile.objs
@@ -12,7 +12,7 @@ util-obj-$(CONFIG_POSIX) += memfd.o
 util-obj-$(CONFIG_WIN32) += oslib-win32.o
 util-obj-$(CONFIG_WIN32) += qemu-thread-win32.o
 util-obj-y += envlist.o path.o module.o
-util-obj-$(call lnot,$(CONFIG_INT128)) += host-utils.o
+util-obj-y += host-utils.o
 util-obj-y += bitmap.o bitops.o hbitmap.o
 util-obj-y += fifo8.o
 util-obj-y += acl.o
diff --git a/util/host-utils.c b/util/host-utils.c
index b166e57586..7b9322071d 100644
--- a/util/host-utils.c
+++ b/util/host-utils.c
@@ -26,6 +26,7 @@
 #include "qemu/osdep.h"
 #include "qemu/host-utils.h"
 
+#ifndef CONFIG_INT128
 /* Long integer helpers */
 static inline void mul64(uint64_t *plow, uint64_t *phigh,
                          uint64_t a, uint64_t b)
@@ -158,4 +159,69 @@ int divs128(int64_t *plow, int64_t *phigh, int64_t divisor)
 
     return overflow;
 }
+#endif
+
+/**
+ * urshift - 128-bit Unsigned Right Shift.
+ * @plow: in/out - lower 64-bit integer.
+ * @phigh: in/out - higher 64-bit integer.
+ * @shift: in - bytes to shift, between 0 and 127.
+ *
+ * Result is zero-extended and stored in plow/phigh, which are
+ * input/output variables. Shift values outside the range will
+ * be mod to 128. In other words, the caller is responsible to
+ * verify/assert both the shift range and plow/phigh pointers.
+ */
+void urshift(uint64_t *plow, uint64_t *phigh, int32_t shift)
+{
+    shift &= 127;
+    if (shift == 0) {
+        return;
+    }
 
+    uint64_t h = *phigh >> (shift & 63);
+    if (shift >= 64) {
+        *plow = h;
+        *phigh = 0;
+    } else {
+        *plow = (*plow >> (shift & 63)) | (*phigh << (64 - (shift & 63)));
+        *phigh = h;
+    }
+}
+
+/**
+ * ulshift - 128-bit Unsigned Left Shift.
+ * @plow: in/out - lower 64-bit integer.
+ * @phigh: in/out - higher 64-bit integer.
+ * @shift: in - bytes to shift, between 0 and 127.
+ * @overflow: out - true if any 1-bit is shifted out.
+ *
+ * Result is zero-extended and stored in plow/phigh, which are
+ * input/output variables. Shift values outside the range will
+ * be mod to 128. In other words, the caller is responsible to
+ * verify/assert both the shift range and plow/phigh pointers.
+ */
+void ulshift(uint64_t *plow, uint64_t *phigh, int32_t shift, bool *overflow)
+{
+    uint64_t low = *plow;
+    uint64_t high = *phigh;
+
+    shift &= 127;
+    if (shift == 0) {
+        return;
+    }
+
+    /* check if any bit will be shifted out */
+    urshift(&low, &high, 128 - shift);
+    if (low | high) {
+        *overflow = true;
+    }
+
+    if (shift >= 64) {
+        *phigh = *plow << (shift & 63);
+        *plow = 0;
+    } else {
+        *phigh = (*plow >> (64 - (shift & 63))) | (*phigh << (shift & 63));
+        *plow = *plow << shift;
+    }
+}
diff --git a/vl.c b/vl.c
index 68e8c003d1..b4eaf03734 100644
--- a/vl.c
+++ b/vl.c
@@ -110,7 +110,7 @@ int main(int argc, char **argv)
 
 #include "slirp/libslirp.h"
 
-#include "trace.h"
+#include "trace-root.h"
 #include "trace/control.h"
 #include "qemu/queue.h"
 #include "sysemu/arch_init.h"
@@ -151,10 +151,10 @@ static int full_screen = 0;
 static int no_frame = 0;
 int no_quit = 0;
 static bool grab_on_hover;
-CharDriverState *serial_hds[MAX_SERIAL_PORTS];
-CharDriverState *parallel_hds[MAX_PARALLEL_PORTS];
-CharDriverState *virtcon_hds[MAX_VIRTIO_CONSOLES];
-CharDriverState *sclp_hds[MAX_SCLP_CONSOLES];
+Chardev *serial_hds[MAX_SERIAL_PORTS];
+Chardev *parallel_hds[MAX_PARALLEL_PORTS];
+Chardev *virtcon_hds[MAX_VIRTIO_CONSOLES];
+Chardev *sclp_hds[MAX_SCLP_CONSOLES];
 int win2k_install_hack = 0;
 int singlestep = 0;
 int smp_cpus = 1;
@@ -465,6 +465,9 @@ static QemuOptsList qemu_icount_opts = {
         }, {
             .name = "rrfile",
             .type = QEMU_OPT_STRING,
+        }, {
+            .name = "rrsnapshot",
+            .type = QEMU_OPT_STRING,
         },
         { /* end of list */ }
     },
@@ -512,43 +515,6 @@ static QemuOptsList qemu_fw_cfg_opts = {
     },
 };
 
-#ifdef CONFIG_LIBISCSI
-static QemuOptsList qemu_iscsi_opts = {
-    .name = "iscsi",
-    .head = QTAILQ_HEAD_INITIALIZER(qemu_iscsi_opts.head),
-    .desc = {
-        {
-            .name = "user",
-            .type = QEMU_OPT_STRING,
-            .help = "username for CHAP authentication to target",
-        },{
-            .name = "password",
-            .type = QEMU_OPT_STRING,
-            .help = "password for CHAP authentication to target",
-        },{
-            .name = "password-secret",
-            .type = QEMU_OPT_STRING,
-            .help = "ID of the secret providing password for CHAP "
-                    "authentication to target",
-        },{
-            .name = "header-digest",
-            .type = QEMU_OPT_STRING,
-            .help = "HeaderDigest setting. "
-                    "{CRC32C|CRC32C-NONE|NONE-CRC32C|NONE}",
-        },{
-            .name = "initiator-name",
-            .type = QEMU_OPT_STRING,
-            .help = "Initiator iqn name to use when connecting",
-        },{
-            .name = "timeout",
-            .type = QEMU_OPT_NUMBER,
-            .help = "Request timeout in seconds (default 0 = no timeout)",
-        },
-        { /* end of list */ }
-    },
-};
-#endif
-
 /**
  * Get machine options
  *
@@ -2356,7 +2322,7 @@ static int fsdev_init_func(void *opaque, QemuOpts *opts, Error **errp)
 
 static int mon_init_func(void *opaque, QemuOpts *opts, Error **errp)
 {
-    CharDriverState *chr;
+    Chardev *chr;
     const char *chardev;
     const char *mode;
     int flags;
@@ -3042,9 +3008,6 @@ int main(int argc, char **argv, char **envp)
     qemu_add_opts(&qemu_icount_opts);
     qemu_add_opts(&qemu_semihosting_config_opts);
     qemu_add_opts(&qemu_fw_cfg_opts);
-#ifdef CONFIG_LIBISCSI
-    qemu_add_opts(&qemu_iscsi_opts);
-#endif
     module_call_init(MODULE_INIT_OPTS);
 
     runstate_init();
@@ -4289,6 +4252,8 @@ int main(int argc, char **argv, char **envp)
         sdl_display_early_init(request_opengl);
     }
 
+    qemu_console_early_init();
+
     if (request_opengl == 1 && display_opengl == 0) {
 #if defined(CONFIG_OPENGL)
         error_report("OpenGL is not supported by the display");
@@ -4634,7 +4599,9 @@ int main(int argc, char **argv, char **envp)
     replay_checkpoint(CHECKPOINT_RESET);
     qemu_system_reset(VMRESET_SILENT);
     register_global_state();
-    if (loadvm) {
+    if (replay_mode != REPLAY_MODE_NONE) {
+        replay_vmstate_init();
+    } else if (loadvm) {
         if (load_vmstate(loadvm) < 0) {
             autostart = 0;
         }
diff --git a/xen-common-stub.c b/xen-common-stub.c
index 699c3f1c64..09fce2dd36 100644
--- a/xen-common-stub.c
+++ b/xen-common-stub.c
@@ -9,6 +9,6 @@
 #include "qemu-common.h"
 #include "hw/xen/xen.h"
 
-void xenstore_store_pv_console_info(int i, CharDriverState *chr)
+void xenstore_store_pv_console_info(int i, Chardev *chr)
 {
 }
diff --git a/xen-common.c b/xen-common.c
index 909976071c..fd2c92847e 100644
--- a/xen-common.c
+++ b/xen-common.c
@@ -25,7 +25,7 @@
     do { } while (0)
 #endif
 
-static int store_dev_info(int domid, CharDriverState *cs, const char *string)
+static int store_dev_info(int domid, Chardev *cs, const char *string)
 {
     struct xs_handle *xs = NULL;
     char *path = NULL;
@@ -74,7 +74,7 @@ out:
     return ret;
 }
 
-void xenstore_store_pv_console_info(int i, CharDriverState *chr)
+void xenstore_store_pv_console_info(int i, Chardev *chr)
 {
     if (i == 0) {
         store_dev_info(xen_domid, chr, "/console");
diff --git a/xen-hvm.c b/xen-hvm.c
index 0892361cc2..5043beb98f 100644
--- a/xen-hvm.c
+++ b/xen-hvm.c
@@ -22,7 +22,7 @@
 #include "qemu/error-report.h"
 #include "qemu/range.h"
 #include "sysemu/xen-mapcache.h"
-#include "trace.h"
+#include "trace-root.h"
 #include "exec/address-spaces.h"
 
 #include <xen/hvm/ioreq.h>
diff --git a/xen-mapcache.c b/xen-mapcache.c
index 31debdfb2c..1a96d2e5db 100644
--- a/xen-mapcache.c
+++ b/xen-mapcache.c
@@ -19,7 +19,7 @@
 #include <xen/hvm/params.h>
 
 #include "sysemu/xen-mapcache.h"
-#include "trace.h"
+#include "trace-root.h"
 
 
 //#define MAPCACHE_DEBUG