aboutsummaryrefslogtreecommitdiff
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