diff options
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> @@ -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); @@ -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" @@ -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 @@ -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); @@ -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, }, @@ -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, ¤t_pc, ¤t_cs_base, ¤t_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 @@ -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" @@ -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 @@ -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) "" @@ -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, \ @@ -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); @@ -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; /* @@ -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(); } @@ -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); @@ -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 = { @@ -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"); @@ -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; + } +} @@ -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"); @@ -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 |